]> git.lizzy.rs Git - minetest.git/commitdiff
Replace C++ mainmenu by formspec powered one
authorsapier <Sapier at GMX dot net>
Sun, 23 Jun 2013 16:30:21 +0000 (18:30 +0200)
committerkwolekr <kwolekr@minetest.net>
Tue, 2 Jul 2013 23:58:20 +0000 (19:58 -0400)
38 files changed:
builtin/gamemgr.lua [new file with mode: 0644]
builtin/mainmenu.lua [new file with mode: 0644]
builtin/mainmenu_helper.lua [new file with mode: 0644]
builtin/modmgr.lua [new file with mode: 0644]
builtin/modstore.lua [new file with mode: 0644]
doc/lua_api.txt
doc/menu_lua_api.txt [new file with mode: 0644]
minetest.conf.example
src/CMakeLists.txt
src/convert_json.cpp [new file with mode: 0644]
src/convert_json.h [new file with mode: 0644]
src/defaultsettings.cpp
src/filesys.cpp
src/filesys.h
src/game.cpp
src/guiConfigureWorld.cpp [deleted file]
src/guiConfigureWorld.h [deleted file]
src/guiConfirmMenu.cpp [deleted file]
src/guiConfirmMenu.h [deleted file]
src/guiCreateWorld.cpp [deleted file]
src/guiCreateWorld.h [deleted file]
src/guiEngine.cpp [new file with mode: 0644]
src/guiEngine.h [new file with mode: 0644]
src/guiFileSelectMenu.cpp [new file with mode: 0644]
src/guiFileSelectMenu.h [new file with mode: 0644]
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/guiLuaApi.cpp [new file with mode: 0644]
src/guiLuaApi.h [new file with mode: 0644]
src/guiMainMenu.cpp [deleted file]
src/guiMainMenu.h
src/main.cpp
src/map.cpp
src/mods.cpp
src/mods.h
src/serverlist.cpp
src/test.cpp
textures/base/pack/no_screenshot.png [new file with mode: 0644]

diff --git a/builtin/gamemgr.lua b/builtin/gamemgr.lua
new file mode 100644 (file)
index 0000000..bbff513
--- /dev/null
@@ -0,0 +1,309 @@
+--Minetest
+--Copyright (C) 2013 sapier
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+gamemgr = {}
+
+--------------------------------------------------------------------------------
+function gamemgr.dialog_new_game()
+       local retval = 
+               "label[2,2;Game Name]"..
+               "field[4.5,2.4;6,0.5;te_game_name;;]" ..
+               "button[5,4.2;2.6,0.5;new_game_confirm;Create]" ..
+               "button[7.5,4.2;2.8,0.5;new_game_cancel;Cancel]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.handle_games_buttons(fields)
+       if fields["gamelist"] ~= nil then
+               local event = explode_textlist_event(fields["gamelist"])
+               gamemgr.selected_game = event.index
+       end
+       
+       if fields["btn_game_mgr_edit_game"] ~= nil then
+               return {
+                       is_dialog = true,
+                       show_buttons = false,
+                       current_tab = "dialog_edit_game"
+               }
+       end
+       
+       if fields["btn_game_mgr_new_game"] ~= nil then
+               return {
+                       is_dialog = true,
+                       show_buttons = false,
+                       current_tab = "dialog_new_game"
+               }
+       end
+       
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.handle_new_game_buttons(fields)
+
+       if fields["new_game_confirm"] and
+               fields["te_game_name"] ~= nil and
+               fields["te_game_name"] ~= "" then
+               local gamepath = engine.get_gamepath()
+               
+               if gamepath ~= nil and
+                       gamepath ~= "" then
+                       local gamefolder = cleanup_path(fields["te_game_name"])
+                       
+                       --TODO check for already existing first
+                       engine.create_dir(gamepath .. DIR_DELIM .. gamefolder)
+                       engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "mods")
+                       engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "menu")
+                       
+                       local gameconf = 
+                               io.open(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "game.conf","w")
+                       
+                       if gameconf then
+                               gameconf:write("name = " .. fields["te_game_name"])
+                               gameconf:close()
+                       end
+               end
+       end
+
+       return {
+               is_dialog = false,
+               show_buttons = true,
+               current_tab = engine.setting_get("main_menu_tab")
+               }
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.handle_edit_game_buttons(fields)
+       local current_game = gamemgr.get_game(gamemgr.selected_game)
+       
+       if fields["btn_close_edit_game"] ~= nil or
+               current_game == nil then
+               return {
+                       is_dialog = false,
+                       show_buttons = true,
+                       current_tab = engine.setting_get("main_menu_tab")
+                       }
+       end
+
+       if fields["btn_remove_mod_from_game"] ~= nil then
+               gamemgr.delete_mod(current_game,engine.get_textlist_index("mods_current"))
+       end
+       
+       if fields["btn_add_mod_to_game"] ~= nil then
+               local modindex = engine.get_textlist_index("mods_available")
+               
+               if modindex > 0 and
+                       modindex <= #modmgr.global_mods then
+                       
+                       local sourcepath = 
+                               engine.get_modpath() .. DIR_DELIM .. modmgr.global_mods[modindex]
+                       
+                       gamemgr.add_mod(current_game,sourcepath)
+               end
+       end
+       
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.add_mod(gamespec,sourcepath)
+       if gamespec.gamemods_path ~= nil and
+               gamespec.gamemods_path ~= "" then
+               
+               local modname = get_last_folder(sourcepath)
+               
+               engine.copy_dir(sourcepath,gamespec.gamemods_path .. DIR_DELIM .. modname);
+       end
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.delete_mod(gamespec,modindex)
+       if gamespec.gamemods_path ~= nil and
+               gamespec.gamemods_path ~= "" then
+               local game_mods = {}
+               get_mods(gamespec.gamemods_path,game_mods)
+               
+               if modindex > 0 and
+                       #game_mods >= modindex then
+                       
+                       local modname = game_mods[modindex]
+       
+                       if modname:find("<MODPACK>") ~= nil then
+                               modname = modname:sub(0,modname:find("<") -2)
+                       end
+
+                       local modpath = gamespec.gamemods_path .. DIR_DELIM .. modname
+                       if modpath:sub(0,gamespec.gamemods_path:len()) == gamespec.gamemods_path then
+                               engine.delete_dir(modpath)
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.get_game_mods(gamespec)
+
+       local retval = ""
+       
+       if gamespec.gamemods_path ~= nil and
+               gamespec.gamemods_path ~= "" then
+               local game_mods = {}
+               get_mods(gamespec.gamemods_path,game_mods)
+               
+               for i=1,#game_mods,1 do
+                       if retval ~= "" then
+                               retval = retval..","
+                       end
+                       retval = retval .. game_mods[i]
+               end 
+       end
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.gettab(name)
+       local retval = ""
+       
+       if name == "dialog_edit_game" then
+               retval = retval .. gamemgr.dialog_edit_game()
+       end
+       
+       if name == "dialog_new_game" then
+               retval = retval .. gamemgr.dialog_new_game()
+       end
+       
+       if name == "game_mgr" then
+               retval = retval .. gamemgr.tab()
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.tab()
+       if gamemgr.selected_game == nil then
+               gamemgr.selected_game = 1
+       end
+       
+       local retval = 
+               "vertlabel[0,-0.25;GAMES]" ..
+               "label[1,-0.25;Games:]" ..
+               "textlist[1,0.25;4.5,4.4;gamelist;" ..
+               gamemgr.gamelist() ..
+               ";" .. gamemgr.selected_game .. "]"
+       
+       local current_game = gamemgr.get_game(gamemgr.selected_game)
+       
+       if current_game ~= nil then
+               if current_game.menuicon_path ~= nil and
+                       current_game.menuicon_path ~= "" then
+                       retval = retval .. 
+                               "image[5.8,-0.25;2,2;" .. current_game.menuicon_path .. "]"
+               end
+               
+               retval = retval ..
+                       "field[8,-0.25;6,2;;" .. current_game.name .. ";]"..
+                       "label[6,1.4;Mods:]" ..
+                       "button[9.7,1.5;2,0.2;btn_game_mgr_edit_game;edit game]" ..
+                       "textlist[6,2;5.5,3.3;game_mgr_modlist;"
+                       .. gamemgr.get_game_mods(current_game) ..";0]" ..
+                       "button[1,4.75;3.2,0.5;btn_game_mgr_new_game;new game]"
+       end
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.dialog_edit_game()
+       local current_game = gamemgr.get_game(gamemgr.selected_game)
+       if current_game ~= nil then
+               local retval = 
+                       "vertlabel[0,-0.25;EDIT GAME]" ..
+                       "label[0,-0.25;" .. current_game.name .. "]" ..
+                       "button[11.55,-0.2;0.75,0.5;btn_close_edit_game;x]"
+               
+               if current_game.menuicon_path ~= nil and
+                       current_game.menuicon_path ~= "" then
+                       retval = retval .. 
+                               "image[5.25,0;2,2;" .. current_game.menuicon_path .. "]"                        
+               end
+               
+               retval = retval .. 
+                       "textlist[0.5,0.5;4.5,4.3;mods_current;"
+                       .. gamemgr.get_game_mods(current_game) ..";0]"
+                       
+                       
+               retval = retval .. 
+                       "textlist[7,0.5;4.5,4.3;mods_available;"
+                       .. modmgr.get_mods_list() .. ";0]"
+
+               retval = retval ..
+                       "button[0.55,4.95;4.7,0.5;btn_remove_mod_from_game;Remove selected mod]"
+                       
+               retval = retval ..
+                       "button[7.05,4.95;4.7,0.5;btn_add_mod_to_game;<<-- Add mod]"
+               
+               return retval
+       end
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.handle_buttons(tab,fields)
+       local retval = nil
+       
+       if tab == "dialog_edit_game" then
+               retval = gamemgr.handle_edit_game_buttons(fields)
+       end
+       
+       if tab == "dialog_new_game" then
+               retval = gamemgr.handle_new_game_buttons(fields)
+       end
+       
+       if tab == "game_mgr" then
+               retval = gamemgr.handle_games_buttons(fields)
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.get_game(index) 
+       if index > 0 and index <= #gamemgr.games then
+               return gamemgr.games[index]
+       end
+       
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.update_gamelist()
+       gamemgr.games = engine.get_games()
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.gamelist()
+       local retval = ""
+       if #gamemgr.games > 0 then
+               retval = retval .. gamemgr.games[1].id
+               
+               for i=2,#gamemgr.games,1 do
+                       retval = retval .. "," .. gamemgr.games[i].name
+               end
+       end
+       return retval
+end
diff --git a/builtin/mainmenu.lua b/builtin/mainmenu.lua
new file mode 100644 (file)
index 0000000..ef0ba72
--- /dev/null
@@ -0,0 +1,1190 @@
+local scriptpath = engine.get_scriptdir()
+
+dofile(scriptpath .. DIR_DELIM .. "modmgr.lua")
+dofile(scriptpath .. DIR_DELIM .. "modstore.lua")
+dofile(scriptpath .. DIR_DELIM .. "gamemgr.lua")
+
+local menu = {}
+local tabbuilder = {}
+local menubar = {}
+
+--------------------------------------------------------------------------------
+function render_favourite(spec)
+       local text = ""
+       
+       if spec.name ~= nil then
+               text = text .. spec.name:trim()
+               
+               if spec.description ~= nil then
+                       --TODO make sure there's no invalid chat in spec.description
+                       text = text .. " (" .. fs_escape_string(spec.description) .. ")"
+               end
+       else
+               if spec.address ~= nil then
+                       text = text .. spec.address:trim()
+               end
+       end
+       
+       local details = ""
+       if spec.password == true then
+               details = " *"
+       else
+               details = "  "
+       end
+       
+       if spec.creative then
+               details = details .. "C"
+       else
+               details = details .. " "
+       end
+       
+       if spec.damage then
+               details = details .. "D"
+       else
+               details = details .. " "
+       end
+       
+       if spec.pvp then
+               details = details .. "P"
+       else
+               details = details .. " "
+       end
+       
+       text = text .. ":" .. spec.port:trim()
+       
+       return text
+end
+
+--------------------------------------------------------------------------------
+os.tempfolder = function()
+       local filetocheck = os.tmpname()
+       os.remove(filetocheck)
+       
+       local randname = "MTTempModFolder_" .. math.random(0,10000)
+       if DIR_DELIM == "\\" then
+               local tempfolder = os.getenv("TEMP")
+               return tempfolder .. filetocheck
+       else
+               local backstring = filetocheck:reverse()
+               return filetocheck:sub(0,filetocheck:len()-backstring:find(DIR_DELIM)+1) ..randname
+       end
+
+end
+
+--------------------------------------------------------------------------------
+function cleanup_path(temppath)
+       
+       local parts = temppath:split("-")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath .. "_"
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       parts = temppath:split(".")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath .. "_"
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       parts = temppath:split("'")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath .. ""
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       parts = temppath:split(" ")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       return temppath
+end
+
+--------------------------------------------------------------------------------
+function menu.update_gametype()
+       if (menu.game_last_check == nil or
+               menu.game_last_check ~= menu.last_game) and
+               tabbuilder.current_tab == "singleplayer" then
+               
+               local gamedetails = menu.lastgame()
+               engine.set_topleft_text(gamedetails.name)
+               
+               --background
+               local path_background_texture = gamedetails.path .. DIR_DELIM .."menu" .. 
+                                                                                                DIR_DELIM .. "background.png"
+               if engine.set_background("background",path_background_texture) then
+                       engine.set_clouds(false)
+               else
+                       engine.set_clouds(true)
+               end
+               
+               --overlay
+               local path_overlay_texture = gamedetails.path .. DIR_DELIM .."menu" .. 
+                                                                                                DIR_DELIM .. "overlay.png"
+               engine.set_background("overlay",path_overlay_texture)
+               
+               --header
+               local path_overlay_texture = gamedetails.path .. DIR_DELIM .."menu" .. 
+                                                                                                DIR_DELIM .. "header.png"
+               engine.set_background("header",path_overlay_texture)
+               
+               --footer
+               local path_overlay_texture = gamedetails.path .. DIR_DELIM .."menu" .. 
+                                                                                                DIR_DELIM .. "footer.png"
+               engine.set_background("footer",path_overlay_texture)
+               
+               menu.game_last_check = menu.last_game
+       else
+               if menu.game_last_check ~= menu.last_game then
+                       menu.game_last_check = menu.last_game
+                       menu.reset_gametype()
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function menu.reset_gametype()
+       menu.game_last_check = nil
+       engine.set_clouds(true)
+       engine.set_background("background","")
+       engine.set_background("overlay",menu.basetexturedir .. "menu_overlay.png")
+       engine.set_background("header",menu.basetexturedir .. "menu_header.png")
+       engine.set_background("footer",menu.basetexturedir .. "menu_footer.png")
+       engine.set_topleft_text("")
+end
+
+--------------------------------------------------------------------------------
+function get_last_folder(text,count)
+       local parts = text:split(DIR_DELIM)
+       
+       if count == nil then
+               return parts[#parts]
+       end
+       
+       local retval = ""
+       for i=1,count,1 do
+               retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function init_globals()
+       --init gamedata
+       gamedata.worldindex = 0
+end
+
+--------------------------------------------------------------------------------
+function identify_filetype(name)
+
+       if name:sub(-3):lower() == "zip" then
+               return {
+                               name = name,
+                               type = "zip"
+                               }
+       end
+       
+       if name:sub(-6):lower() == "tar.gz" or
+               name:sub(-3):lower() == "tgz"then
+               return {
+                               name = name,
+                               type = "tgz"
+                               }
+       end
+       
+       if name:sub(-6):lower() == "tar.bz2" then
+               return {
+                               name = name,
+                               type = "tbz"
+                               }
+       end
+       
+       if name:sub(-2):lower() == "7z" then
+               return {
+                               name = name,
+                               type = "7z"
+                               }
+       end
+
+       return {
+               name = name,
+               type = "ukn"
+       }
+end
+
+--------------------------------------------------------------------------------
+function update_menu()
+
+       local formspec = "size[12,5.2]"
+       
+       -- handle errors
+       if gamedata.errormessage ~= nil then
+               formspec = formspec ..
+                       "field[1,2;10,2;;ERROR: " ..
+                       gamedata.errormessage .. 
+                       ";]"..
+                       "button[4.5,4.2;3,0.5;btn_error_confirm;Ok]"
+       else
+               formspec = formspec .. tabbuilder.gettab()
+       end
+
+       engine.update_formspec(formspec)
+end
+
+--------------------------------------------------------------------------------
+function menu.filtered_game_list()
+       local retval = ""
+
+       local current_game = menu.lastgame()
+       
+       for i=1,#menu.worldlist,1 do
+               if menu.worldlist[i].gameid == current_game.id then
+                       if retval ~= "" then
+                               retval = retval ..","
+                       end
+                       
+                       retval = retval .. menu.worldlist[i].name .. 
+                                               " [[" .. menu.worldlist[i].gameid .. "]]"
+               end
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function menu.filtered_game_list_raw()
+       local retval =  {}
+
+       local current_game = menu.lastgame()
+       
+       for i=1,#menu.worldlist,1 do
+               if menu.worldlist[i].gameid == current_game.id then
+                       table.insert(retval,menu.worldlist[i])
+               end
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function menu.filtered_index_to_plain(filtered_index)
+
+       local current_game = menu.lastgame()
+       
+       local temp_idx = 0
+       
+       for i=1,#menu.worldlist,1 do
+               if menu.worldlist[i].gameid == current_game.id then
+                       temp_idx = temp_idx +1
+               end
+               
+               if temp_idx == filtered_index then
+                       return i
+               end
+       end
+       return -1
+end
+
+--------------------------------------------------------------------------------
+function menu.init()
+       --init menu data
+       gamemgr.update_gamelist()
+
+       menu.worldlist = engine.get_worlds()
+       
+       menu.last_world = tonumber(engine.setting_get("main_menu_last_world_idx"))
+       menu.last_game  = tonumber(engine.setting_get("main_menu_last_game_idx"))
+       
+       if type(menu.last_world) ~= "number" then
+               menu.last_world = 1
+       end
+       
+       if type(menu.last_game) ~= "number" then
+               menu.last_game = 1
+       end
+
+       if engine.setting_getbool("public_serverlist") then
+               menu.favorites = engine.get_favorites("online")
+       else
+               menu.favorites = engine.get_favorites("local")
+       end
+       
+       
+       menu.basetexturedir = engine.get_gamepath() .. DIR_DELIM .. ".." ..
+                                               DIR_DELIM .. "textures" .. DIR_DELIM .. "base" .. 
+                                               DIR_DELIM .. "pack" .. DIR_DELIM
+end
+
+--------------------------------------------------------------------------------
+function menu.lastgame()
+       if menu.last_game > 0 and menu.last_game <= #gamemgr.games then
+               return gamemgr.games[menu.last_game]
+       end
+       
+       if #gamemgr.games >= 1 then
+               menu.last_game = 1
+               return gamemgr.games[menu.last_game]
+       end
+       
+       --error case!!
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function menu.lastworld()
+       if menu.last_world ~= nil and 
+               menu.last_world > 0 and 
+               menu.last_world <= #menu.worldlist then
+               return menu.worldlist[menu.last_world]
+       end
+       
+       if #menu.worldlist >= 1 then
+               menu.last_world = 1
+               return menu.worldlist[menu.last_world]
+       end
+       
+       --error case!!
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function menu.update_last_game(world_idx)
+       if gamedata.selected_world <= #menu.worldlist then
+               local world = menu.worldlist[gamedata.selected_world]
+               
+               for i=1,#gamemgr.games,1 do             
+                       if gamemgr.games[i].id == world.gameid then
+                               menu.last_game = i
+                               engine.setting_set("main_menu_last_game_idx",menu.last_game)
+                               break
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function menubar.handle_buttons(fields)
+       for i=1,#menubar.buttons,1 do
+               if fields[menubar.buttons[i].btn_name] ~= nil then
+                       menu.last_game = menubar.buttons[i].index
+                       engine.setting_set("main_menu_last_game_idx",menu.last_game)
+                       menu.update_gametype()
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function menubar.refresh()
+       menubar.formspec = "box[-2,7.625;15.75,1.75;BLK]"
+       menubar.buttons = {}
+
+       local button_base = -1.8
+       
+       local maxbuttons = #gamemgr.games
+       
+       if maxbuttons > 12 then
+               maxbuttons = 12
+       end
+       
+       for i=1,maxbuttons,1 do
+
+               local btn_name = "menubar_btn_" .. gamemgr.games[i].id
+               local buttonpos = button_base + (i-1) * 1.3
+               if gamemgr.games[i].menuicon_path ~= nil and
+                       gamemgr.games[i].menuicon_path ~= "" then
+
+                       menubar.formspec = menubar.formspec ..
+                               "image_button[" .. buttonpos ..  ",7.9;1.3,1.3;"  ..
+                               gamemgr.games[i].menuicon_path .. ";" .. btn_name .. ";;true;false]"
+               else
+               
+                       local part1 = gamemgr.games[i].id:sub(1,5)
+                       local part2 = gamemgr.games[i].id:sub(6,10)
+                       local part3 = gamemgr.games[i].id:sub(11)
+                       
+                       local text = part1 .. "\n" .. part2
+                       if part3 ~= nil and
+                               part3 ~= "" then
+                               text = text .. "\n" .. part3
+                       end
+                       menubar.formspec = menubar.formspec ..
+                               "image_button[" .. buttonpos ..  ",7.9;1.3,1.3;;" ..btn_name ..
+                               ";" .. text .. ";true;true]"
+               end
+               
+               local toadd = {
+                       btn_name = btn_name,
+                       index = i,
+               }
+               
+               table.insert(menubar.buttons,toadd)
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.dialog_create_world()
+       local retval = 
+               "label[2,0;World name]"..
+               "label[2,1;Mapgen]"..
+               "field[4.5,0.4;6,0.5;te_world_name;;]" ..
+               "label[2,2;Game]"..
+               "button[5,4.5;2.6,0.5;world_create_confirm;Create]" ..
+               "button[7.5,4.5;2.8,0.5;world_create_cancel;Cancel]" ..
+               "dropdown[4.2,1;6.3;dd_mapgen;v6,v7,indev,singlenode,math;1]" .. --TODO read from minetest
+               "textlist[4.2,1.9;5.8,2.3;games;" ..
+               gamemgr.gamelist() ..
+               ";" .. menu.last_game .. ";true]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.dialog_delete_world()
+       return  "label[2,2;Delete World \"" .. menu.lastworld().name .. "\"?]"..
+                       "button[3.5,4.2;2.6,0.5;world_delete_confirm;Yes]" ..
+                       "button[6,4.2;2.8,0.5;world_delete_cancel;No]"
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.gettab()
+       local retval = ""
+       
+       if tabbuilder.show_buttons then
+               retval = retval .. tabbuilder.tab_header()
+       end
+
+       if tabbuilder.current_tab == "singleplayer" then
+               retval = retval .. tabbuilder.tab_singleplayer()
+       end
+       
+       if tabbuilder.current_tab == "multiplayer" then
+               retval = retval .. tabbuilder.tab_multiplayer()
+       end
+
+       if tabbuilder.current_tab == "server" then
+               retval = retval .. tabbuilder.tab_server()
+       end
+       
+       if tabbuilder.current_tab == "settings" then
+               retval = retval .. tabbuilder.tab_settings()
+       end
+       
+       if tabbuilder.current_tab == "credits" then
+               retval = retval .. tabbuilder.tab_credits()
+       end
+       
+       if tabbuilder.current_tab == "dialog_create_world" then
+               retval = retval .. tabbuilder.dialog_create_world()
+       end
+       
+       if tabbuilder.current_tab == "dialog_delete_world" then
+               retval = retval .. tabbuilder.dialog_delete_world()
+       end
+       
+       retval = retval .. modmgr.gettab(tabbuilder.current_tab)
+       retval = retval .. gamemgr.gettab(tabbuilder.current_tab)
+       retval = retval .. modstore.gettab(tabbuilder.current_tab)
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_create_world_buttons(fields)
+       
+       if fields["world_create_confirm"] then
+               
+               local worldname = fields["te_world_name"]
+               local gameindex = engine.get_textlist_index("games")
+               
+               if gameindex > 0 and
+                       worldname ~= "" then
+                       engine.setting_set("mg_name",fields["dd_mapgen"])
+                       local message = engine.create_world(worldname,gameindex)
+                       
+                       menu.last_game = gameindex
+                       engine.setting_set("main_menu_last_game_idx",gameindex)
+                       
+                       if message ~= nil then
+                               gamedata.errormessage = message
+                       else
+                               menu.worldlist = engine.get_worlds()
+                               
+                               local worldlist = menu.worldlist
+                               
+                               if tabbuilder.current_tab == "singleplayer" then
+                                       worldlist = menu.filtered_game_list_raw()
+                               end
+                               
+                               local index = 0
+                               
+                               for i=1,#worldlist,1 do
+                                       if worldlist[i].name == worldname then
+                                               index = i
+                                               print("found new world index: " .. index)
+                                               break
+                                       end
+                               end
+                               
+                               if tabbuilder.current_tab == "singleplayer" then
+                                       engine.setting_set("main_menu_singleplayer_world_idx",index)
+                               else
+                                       menu.last_world = index
+                               end
+                       end
+               else
+                       gamedata.errormessage = "No worldname given or no game selected"
+               end
+       end
+       
+       if fields["games"] then
+               tabbuilder.skipformupdate = true
+               return
+       end
+       
+       tabbuilder.is_dialog = false
+       tabbuilder.show_buttons = true
+       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_delete_world_buttons(fields)
+       
+       if fields["world_delete_confirm"] then
+               if menu.last_world > 0 and 
+                       menu.last_world < #menu.worldlist then
+                       engine.delete_world(menu.last_world)
+                       menu.worldlist = engine.get_worlds()
+                       menu.last_world = 1
+               end
+       end
+       
+       tabbuilder.is_dialog = false
+       tabbuilder.show_buttons = true
+       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_multiplayer_buttons(fields)
+       if fields["favourites"] ~= nil then
+               local event = explode_textlist_event(fields["favourites"])
+               if event.typ == "DCL" then
+                       gamedata.address = menu.favorites[event.index].name
+                       if gamedata.address == nil then
+                               gamedata.address = menu.favorites[event.index].address
+                       end
+                       gamedata.port = menu.favorites[event.index].port
+                       gamedata.playername             = fields["te_name"]
+                       gamedata.password               = fields["te_pwd"]
+                       gamedata.selected_world = 0
+                       
+                       if gamedata.address ~= nil and
+                               gamedata.port ~= nil then
+                               
+                               engine.start()
+                       end
+               end
+               
+               if event.typ == "CHG" then
+                       local address = menu.favorites[event.index].name
+                       if address == nil then
+                               address = menu.favorites[event.index].address
+                       end
+                       local port = menu.favorites[event.index].port
+                       
+                       if address ~= nil and
+                               port ~= nil then
+                               engine.setting_set("address",address)
+                               engine.setting_set("port",port)
+                       end
+               end
+               return
+       end
+       
+       if fields["cb_public_serverlist"] ~= nil then
+               engine.setting_setbool("public_serverlist",
+                       tabbuilder.tobool(fields["cb_public_serverlist"]))
+                       
+               if engine.setting_getbool("public_serverlist") then
+                       menu.favorites = engine.get_favorites("online")
+               else
+                       menu.favorites = engine.get_favorites("local")
+               end
+       end
+
+       if fields["btn_delete_favorite"] ~= nil then
+               local current_favourite = engine.get_textlist_index("favourites")
+               engine.delete_favorite(current_favourite)
+               menu.favorites = engine.get_favorites()
+               
+               engine.setting_set("address","")
+               engine.setting_get("port","")
+               
+               return
+       end
+
+       if fields["btn_mp_connect"] ~= nil then
+               gamedata.playername             = fields["te_name"]
+               gamedata.password               = fields["te_pwd"]
+               gamedata.address                = fields["te_address"]
+               gamedata.port                   = fields["te_port"]
+               gamedata.selected_world = 0
+
+               engine.start()
+               return
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_server_buttons(fields)
+
+       local world_doubleclick = false
+
+       if fields["worlds"] ~= nil then
+               local event = explode_textlist_event(fields["worlds"])
+               
+               if event.typ == "DBL" then
+                       world_doubleclick = true
+               end
+       end
+       
+       if fields["cb_creative_mode"] then
+               engine.setting_setbool("creative_mode",tabbuilder.tobool(fields["cb_creative_mode"]))
+       end
+       
+       if fields["cb_enable_damage"] then
+               engine.setting_setbool("enable_damage",tabbuilder.tobool(fields["cb_enable_damage"]))
+       end
+
+       if fields["start_server"] ~= nil or
+               world_doubleclick then
+               local selected = engine.get_textlist_index("srv_worlds")
+               if selected > 0 then
+                       gamedata.playername             = fields["te_playername"]
+                       gamedata.password               = fields["te_pwd"]
+                       gamedata.address                = ""
+                       gamedata.port                   = fields["te_serverport"]
+                       gamedata.selected_world = selected
+                       
+                       engine.setting_set("main_menu_tab",tabbuilder.current_tab)
+                       engine.setting_set("main_menu_last_world_idx",gamedata.selected_world)
+                       
+                       menu.update_last_game(gamedata.selected_world)
+                       engine.start()
+               end
+       end
+       
+       if fields["world_create"] ~= nil then
+               tabbuilder.current_tab = "dialog_create_world"
+               tabbuilder.is_dialog = true
+               tabbuilder.show_buttons = false
+       end
+       
+       if fields["world_delete"] ~= nil then
+               local selected = engine.get_textlist_index("srv_worlds")
+               if selected > 0 then
+                       menu.last_world = engine.get_textlist_index("worlds")
+                       if menu.lastworld() ~= nil and
+                               menu.lastworld().name ~= nil and
+                               menu.lastworld().name ~= "" then
+                               tabbuilder.current_tab = "dialog_delete_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       else
+                               menu.last_world = 0
+                       end
+               end
+       end
+       
+       if fields["world_configure"] ~= nil then
+               selected = engine.get_textlist_index("srv_worlds")
+               if selected > 0 then
+                       modmgr.world_config_selected_world = selected
+                       if modmgr.init_worldconfig() then
+                               tabbuilder.current_tab = "dialog_configure_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tobool(text)
+       if text == "true" then
+               return true
+       else
+               return false
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_settings_buttons(fields)
+       if fields["cb_fancy_trees"] then
+               engine.setting_setbool("new_style_leaves",tabbuilder.tobool(fields["cb_fancy_trees"]))
+       end
+               
+       if fields["cb_smooth_lighting"] then
+               engine.setting_setbool("smooth_lighting",tabbuilder.tobool(fields["cb_smooth_lighting"]))
+       end
+       if fields["cb_3d_clouds"] then
+               engine.setting_setbool("enable_3d_clouds",tabbuilder.tobool(fields["cb_3d_clouds"]))
+       end
+       if fields["cb_opaque_water"] then
+               engine.setting_setbool("opaque_water",tabbuilder.tobool(fields["cb_opaque_water"]))
+       end
+                       
+       if fields["cb_mipmapping"] then
+               engine.setting_setbool("mip_map",tabbuilder.tobool(fields["cb_mipmapping"]))
+       end
+       if fields["cb_anisotrophic"] then
+               engine.setting_setbool("anisotropic_filter",tabbuilder.tobool(fields["cb_anisotrophic"]))
+       end
+       if fields["cb_bilinear"] then
+               engine.setting_setbool("bilinear_filter",tabbuilder.tobool(fields["cb_bilinear"]))
+       end
+       if fields["cb_trilinear"] then
+               engine.setting_setbool("trilinear_filter",tabbuilder.tobool(fields["cb_trilinear"]))
+       end
+                       
+       if fields["cb_shaders"] then
+               engine.setting_setbool("enable_shaders",tabbuilder.tobool(fields["cb_shaders"]))
+       end
+       if fields["cb_pre_ivis"] then
+               engine.setting_setbool("preload_item_visuals",tabbuilder.tobool(fields["cb_pre_ivis"]))
+       end
+       if fields["cb_particles"] then
+               engine.setting_setbool("enable_particles",tabbuilder.tobool(fields["cb_particles"]))
+       end
+       if fields["cb_finite_liquid"] then
+               engine.setting_setbool("liquid_finite",tabbuilder.tobool(fields["cb_finite_liquid"]))
+       end
+
+       if fields["btn_change_keys"] ~= nil then
+               engine.show_keys_menu()
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_singleplayer_buttons(fields)
+
+       local world_doubleclick = false
+
+       if fields["sp_worlds"] ~= nil then
+               local event = explode_textlist_event(fields["sp_worlds"])
+               
+               if event.typ == "DCL" then
+                       world_doubleclick = true
+               end
+       end
+       
+       if fields["cb_creative_mode"] then
+               engine.setting_setbool("creative_mode",tabbuilder.tobool(fields["cb_creative_mode"]))
+       end
+       
+       if fields["cb_enable_damage"] then
+               engine.setting_setbool("enable_damage",tabbuilder.tobool(fields["cb_enable_damage"]))
+       end
+
+       if fields["play"] ~= nil or
+               world_doubleclick then
+               local selected = engine.get_textlist_index("sp_worlds")
+               if selected > 0 then
+                       gamedata.selected_world = menu.filtered_index_to_plain(selected)
+                       gamedata.singleplayer   = true
+                       
+                       engine.setting_set("main_menu_tab",tabbuilder.current_tab)
+                       engine.setting_set("main_menu_singleplayer_world_idx",selected)
+                       
+                       menu.update_last_game(gamedata.selected_world)
+                       
+                       engine.start()
+               end
+       end
+       
+       if fields["world_create"] ~= nil then
+               tabbuilder.current_tab = "dialog_create_world"
+               tabbuilder.is_dialog = true
+               tabbuilder.show_buttons = false
+       end
+       
+       if fields["world_delete"] ~= nil then
+               local selected = engine.get_textlist_index("sp_worlds")
+               if selected > 0 then
+                       menu.last_world = menu.filtered_index_to_plain(selected)
+                       if menu.lastworld() ~= nil and
+                               menu.lastworld().name ~= nil and
+                               menu.lastworld().name ~= "" then
+                               tabbuilder.current_tab = "dialog_delete_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       else
+                               menu.last_world = 0
+                       end
+               end
+       end
+       
+       if fields["world_configure"] ~= nil then
+               selected = engine.get_textlist_index("sp_worlds")
+               if selected > 0 then
+                       modmgr.world_config_selected_world = menu.filtered_index_to_plain(selected)
+                       if modmgr.init_worldconfig() then
+                               tabbuilder.current_tab = "dialog_configure_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_header()
+
+       if tabbuilder.last_tab_index == nil then
+               tabbuilder.last_tab_index = 1
+       end
+       
+       local toadd = ""
+       
+       for i=1,#tabbuilder.current_buttons,1 do
+               
+               if toadd ~= "" then
+                       toadd = toadd .. ","
+               end
+               
+               toadd = toadd .. tabbuilder.current_buttons[i].caption
+       end
+       return "tabheader[-0.3,-0.99;main_tab;" .. toadd ..";" .. tabbuilder.last_tab_index .. ";true;false]"
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_tab_buttons(fields)
+
+       if fields["main_tab"] then
+               local index = tonumber(fields["main_tab"])
+               tabbuilder.last_tab_index = index
+               tabbuilder.current_tab = tabbuilder.current_buttons[index].name
+               
+               engine.setting_set("main_menu_tab",tabbuilder.current_tab)
+       end
+       
+       --handle tab changes
+       if tabbuilder.current_tab ~= tabbuilder.old_tab then
+               if tabbuilder.current_tab ~= "singleplayer" then
+                       menu.reset_gametype()
+               end
+       end
+       
+       if tabbuilder.current_tab == "singleplayer" then
+               menu.update_gametype()
+       end
+       
+       tabbuilder.old_tab = tabbuilder.current_tab
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.init()
+       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+       
+       if tabbuilder.current_tab == nil or
+               tabbuilder.current_tab == "" then
+               tabbuilder.current_tab = "singleplayer"
+               engine.setting_set("main_menu_tab",tabbuilder.current_tab)
+       end
+       
+       
+       --initialize tab buttons
+       tabbuilder.last_tab = nil
+       tabbuilder.show_buttons = true
+       
+       tabbuilder.current_buttons = {}
+       table.insert(tabbuilder.current_buttons,{name="singleplayer", caption="Singleplayer"})
+       table.insert(tabbuilder.current_buttons,{name="multiplayer", caption="Client"})
+       table.insert(tabbuilder.current_buttons,{name="server", caption="Server"})
+       table.insert(tabbuilder.current_buttons,{name="settings", caption="Settings"})
+       
+       if engine.setting_getbool("main_menu_game_mgr") then
+               table.insert(tabbuilder.current_buttons,{name="game_mgr", caption="Games"})
+       end
+       
+       if engine.setting_getbool("main_menu_mod_mgr") then
+               table.insert(tabbuilder.current_buttons,{name="mod_mgr", caption="Mods"})
+       end
+       table.insert(tabbuilder.current_buttons,{name="credits", caption="Credits"})
+       
+       
+       for i=1,#tabbuilder.current_buttons,1 do
+               if tabbuilder.current_buttons[i].name == tabbuilder.current_tab then
+                       tabbuilder.last_tab_index = i
+               end
+       end
+       
+       menu.update_gametype()
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_multiplayer()
+       local retval =
+               "vertlabel[0,-0.25;CLIENT]" ..
+               "label[1,-0.25;Favorites:]"..
+               "label[1,4.25;Address/Port]"..
+               "label[9,0;Name/Password]" ..
+               "field[1.25,5.25;5.5,0.5;te_address;;" ..engine.setting_get("address") .."]" ..
+               "field[6.75,5.25;2.25,0.5;te_port;;" ..engine.setting_get("port") .."]" ..
+               "button[6.45,3.95;2.25,0.5;btn_delete_favorite;Delete]" ..
+               "button[9,4.95;2.5,0.5;btn_mp_connect;Connect]" ..
+               "field[9.25,1;2.5,0.5;te_name;;" ..engine.setting_get("name") .."]" ..
+               "pwdfield[9.25,1.75;2.5,0.5;te_pwd;]" ..
+               "checkbox[1,3.6;cb_public_serverlist;Public Serverlist;" ..
+               dump(engine.setting_getbool("public_serverlist")) .. "]" ..
+               "textlist[1,0.35;7.5,3.35;favourites;"
+
+       if #menu.favorites > 0 then
+               retval = retval .. render_favourite(menu.favorites[1])
+               
+               for i=2,#menu.favorites,1 do
+                       retval = retval .. "," .. render_favourite(menu.favorites[i])
+               end
+       end
+
+       retval = retval .. ";1]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_server()
+       local retval = 
+               "button[4,4.15;2.6,0.5;world_delete;Delete]" ..
+               "button[6.5,4.15;2.8,0.5;world_create;New]" ..
+               "button[9.2,4.15;2.55,0.5;world_configure;Configure]" ..
+               "button[8.5,4.9;3.25,0.5;start_server;Start Game]" ..
+               "label[4,-0.25;Select World:]"..
+               "vertlabel[0,-0.25;START SERVER]" ..
+               "checkbox[0.5,0.25;cb_creative_mode;Creative Mode;" ..
+               dump(engine.setting_getbool("creative_mode")) .. "]"..
+               "checkbox[0.5,0.7;cb_enable_damage;Enable Damage;" ..
+               dump(engine.setting_getbool("enable_damage")) .. "]"..
+               "field[0.8,2.2;3,0.5;te_playername;Name;" ..
+               engine.setting_get("name") .. "]" ..
+               "pwdfield[0.8,3.2;3,0.5;te_passwd;Password]" ..
+               "field[0.8,5.2;3,0.5;te_serverport;Server Port;30000]" ..
+               "textlist[4,0.25;7.5,3.7;srv_worlds;"
+       
+       if #menu.worldlist > 0 then
+               retval = retval .. menu.worldlist[1].name .. 
+                                               " [[" .. menu.worldlist[1].gameid .. "]]"
+                               
+               for i=2,#menu.worldlist,1 do
+                       retval = retval .. "," .. menu.worldlist[i].name .. 
+                                               " [[" .. menu.worldlist[i].gameid .. "]]"
+               end
+       end
+                               
+       retval = retval .. ";" .. menu.last_world .. "]"
+               
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_settings()
+       return  "vertlabel[0,0;SETTINGS]" ..
+                       "checkbox[1,0.75;cb_fancy_trees;Fancy trees;"           .. dump(engine.setting_getbool("new_style_leaves"))     .. "]"..
+                       "checkbox[1,1.25;cb_smooth_lighting;Smooth Lighting;".. dump(engine.setting_getbool("smooth_lighting")) .. "]"..
+                       "checkbox[1,1.75;cb_3d_clouds;3D Clouds;"                       .. dump(engine.setting_getbool("enable_3d_clouds"))     .. "]"..
+                       "checkbox[1,2.25;cb_opaque_water;Opaque Water;"                 .. dump(engine.setting_getbool("opaque_water"))         .. "]"..
+                       
+                       "checkbox[4,0.75;cb_mipmapping;Mip-Mapping;"            .. dump(engine.setting_getbool("mip_map"))                      .. "]"..
+                       "checkbox[4,1.25;cb_anisotrophic;Anisotropic Filtering;".. dump(engine.setting_getbool("anisotropic_filter"))   .. "]"..
+                       "checkbox[4,1.75;cb_bilinear;Bi-Linear Filtering;"      .. dump(engine.setting_getbool("bilinear_filter"))      .. "]"..
+                       "checkbox[4,2.25;cb_trilinear;Tri-Linear Filtering;"    .. dump(engine.setting_getbool("trilinear_filter"))     .. "]"..
+                       
+                       "checkbox[7.5,0.75;cb_shaders;Shaders;"                         .. dump(engine.setting_getbool("enable_shaders"))               .. "]"..
+                       "checkbox[7.5,1.25;cb_pre_ivis;Preload item visuals;".. dump(engine.setting_getbool("preload_item_visuals"))    .. "]"..
+                       "checkbox[7.5,1.75;cb_particles;Enable Particles;"      .. dump(engine.setting_getbool("enable_particles"))     .. "]"..
+                       "checkbox[7.5,2.25;cb_finite_liquid;Finite Liquid;"     .. dump(engine.setting_getbool("liquid_finite"))                .. "]"..
+                       
+                       "button[1,3.75;2.25,0.5;btn_change_keys;Change keys]"
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_singleplayer()
+       local index = engine.setting_get("main_menu_singleplayer_world_idx")
+       
+       if index == nil then
+               index = 0
+       end
+
+       return  "button[4,4.15;2.6,0.5;world_delete;Delete]" ..
+                       "button[6.5,4.15;2.8,0.5;world_create;New]" ..
+                       "button[9.2,4.15;2.55,0.5;world_configure;Configure]" ..
+                       "button[8.5,4.95;3.25,0.5;play;Play]" ..
+                       "label[4,-0.25;Select World:]"..
+                       "vertlabel[0,-0.25;SINGLE PLAYER]" ..
+                       "checkbox[0.5,0.25;cb_creative_mode;Creative Mode;" ..
+                       dump(engine.setting_getbool("creative_mode")) .. "]"..
+                       "checkbox[0.5,0.7;cb_enable_damage;Enable Damage;" ..
+                       dump(engine.setting_getbool("enable_damage")) .. "]"..
+                       "textlist[4,0.25;7.5,3.7;sp_worlds;" ..
+                       menu.filtered_game_list() ..
+                       ";" .. index .. "]" ..
+                       menubar.formspec
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_credits()
+       return  "vertlabel[0,-0.5;CREDITS]" ..
+                       "label[0.5,3;Minetest " .. engine.get_version() .. "]" ..
+                       "label[0.5,3.3;http://minetest.net]" .. 
+                       "image[0.5,1;" .. menu.basetexturedir .. "logo.png]" ..
+                       "textlist[3.5,-0.25;8.5,5.8;list_credits;" ..
+                       "#YLWCore Developers," ..
+                       "Perttu Ahola (celeron55) <celeron55@gmail.com>,"..
+                       "Ryan Kwolek (kwolekr) <kwolekr@minetest.net>,"..
+                       "PilzAdam <pilzadam@minetest.net>," ..
+                       "IIya Zhuravlev (thexyz) <xyz@minetest.net>,"..
+                       "Lisa Milne (darkrose) <lisa@ltmnet.com>,"..
+                       "Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>,"..
+                       "proller <proler@gmail.com>,"..
+                       "sfan5 <sfan5@live.de>,"..
+                       "kahrl <kahrl@gmx.net>,"..
+                       ","..
+                       "#YLWActive Contributors," ..
+                       "sapier,"..
+                       "Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>,"..
+                       "Jurgen Doser (doserj) <jurgen.doser@gmail.com>,"..
+                       "Jeija <jeija@mesecons.net>,"..
+                       "MirceaKitsune <mirceakitsune@gmail.com>,"..
+                       "ShadowNinja"..
+                       "dannydark <the_skeleton_of_a_child@yahoo.co.uk>"..
+                       "0gb.us <0gb.us@0gb.us>,"..
+                       "," ..
+                       "#YLWPrevious Contributors," ..
+                       "Guiseppe Bilotta (Oblomov) <guiseppe.bilotta@gmail.com>,"..
+                       "Jonathan Neuschafer <j.neuschaefer@gmx.net>,"..
+                       "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>,"..
+                       "Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>,"..
+                       "matttpt <matttpt@gmail.com>,"..
+                       "JacobF <queatz@gmail.com>,"..
+                       ";0;true]"
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.checkretval(retval)
+
+       if retval ~= nil then
+               if retval.current_tab ~= nil then
+                       tabbuilder.current_tab = retval.current_tab
+               end
+               
+               if retval.is_dialog ~= nil then
+                       tabbuilder.is_dialog = retval.is_dialog
+               end
+               
+               if retval.show_buttons ~= nil then
+                       tabbuilder.show_buttons = retval.show_buttons
+               end
+               
+               if retval.skipformupdate ~= nil then
+                       tabbuilder.skipformupdate = retval.skipformupdate
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+-- initialize callbacks
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+engine.button_handler = function(fields)
+       --print("Buttonhandler: tab: " .. tabbuilder.current_tab .. " fields: " .. dump(fields))
+       
+       if fields["btn_error_confirm"] then
+               gamedata.errormessage = nil
+       end
+       
+       local retval = modmgr.handle_buttons(tabbuilder.current_tab,fields)
+       tabbuilder.checkretval(retval)
+       
+       retval = gamemgr.handle_buttons(tabbuilder.current_tab,fields)
+       tabbuilder.checkretval(retval)
+       
+       retval = modstore.handle_buttons(tabbuilder.current_tab,fields)
+       tabbuilder.checkretval(retval)
+       
+       if tabbuilder.current_tab == "dialog_create_world" then
+               tabbuilder.handle_create_world_buttons(fields)
+       end
+       
+       if tabbuilder.current_tab == "dialog_delete_world" then
+               tabbuilder.handle_delete_world_buttons(fields)
+       end
+       
+       if tabbuilder.current_tab == "singleplayer" then
+               tabbuilder.handle_singleplayer_buttons(fields)
+       end
+       
+       if tabbuilder.current_tab == "multiplayer" then
+               tabbuilder.handle_multiplayer_buttons(fields)
+       end
+       
+       if tabbuilder.current_tab == "settings" then
+               tabbuilder.handle_settings_buttons(fields)
+       end
+       
+       if tabbuilder.current_tab == "server" then
+               tabbuilder.handle_server_buttons(fields)
+       end
+       
+       --tab buttons
+       tabbuilder.handle_tab_buttons(fields)
+       
+       --menubar buttons
+       menubar.handle_buttons(fields)
+       
+       if not tabbuilder.skipformupdate then
+               --update menu
+               update_menu()
+       else
+               tabbuilder.skipformupdate = false
+       end
+end
+
+--------------------------------------------------------------------------------
+engine.event_handler = function(event)
+       if event == "MenuQuit" then
+               if tabbuilder.is_dialog then
+                       tabbuilder.is_dialog = false
+                       tabbuilder.show_buttons = true
+                       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+                       update_menu()
+               else
+                       engine.close()
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+-- menu startup
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+init_globals()
+menu.init()
+tabbuilder.init()
+menubar.refresh()
+modstore.init()
+update_menu()
diff --git a/builtin/mainmenu_helper.lua b/builtin/mainmenu_helper.lua
new file mode 100644 (file)
index 0000000..f5a470b
--- /dev/null
@@ -0,0 +1,105 @@
+--------------------------------------------------------------------------------
+function dump(o, dumped)
+       dumped = dumped or {}
+       if type(o) == "number" then
+               return tostring(o)
+       elseif type(o) == "string" then
+               return string.format("%q", o)
+       elseif type(o) == "table" then
+               if dumped[o] then
+                       return "<circular reference>"
+               end
+               dumped[o] = true
+               local t = {}
+               for k,v in pairs(o) do
+                       t[#t+1] = "" .. k .. " = " .. dump(v, dumped)
+               end
+               return "{" .. table.concat(t, ", ") .. "}"
+       elseif type(o) == "boolean" then
+               return tostring(o)
+       elseif type(o) == "function" then
+               return "<function>"
+       elseif type(o) == "userdata" then
+               return "<userdata>"
+       elseif type(o) == "nil" then
+               return "nil"
+       else
+               error("cannot dump a " .. type(o))
+               return nil
+       end
+end
+
+--------------------------------------------------------------------------------
+function string:split(sep)
+       local sep, fields = sep or ",", {}
+       local pattern = string.format("([^%s]+)", sep)
+       self:gsub(pattern, function(c) fields[#fields+1] = c end)
+       return fields
+end
+
+--------------------------------------------------------------------------------
+function string:trim()
+       return (self:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+--------------------------------------------------------------------------------
+engine.get_game = function(index)
+       local games = game.get_games()
+       
+       if index > 0 and index <= #games then
+               return games[index]
+       end
+       
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function fs_escape_string(text)
+       if text ~= nil then
+               while (text:find("\r\n") ~= nil) do
+                       local newtext = text:sub(1,text:find("\r\n")-1)
+                       newtext = newtext .. " " .. text:sub(text:find("\r\n")+3)
+                       
+                       text = newtext
+               end
+               
+               while (text:find("\n") ~= nil) do
+                       local newtext = text:sub(1,text:find("\n")-1)
+                       newtext = newtext .. " " .. text:sub(text:find("\n")+1)
+                       
+                       text = newtext
+               end
+               
+               while (text:find("\r") ~= nil) do
+                       local newtext = text:sub(1,text:find("\r")-1)
+                       newtext = newtext .. " " .. text:sub(text:find("\r")+1)
+                       
+                       text = newtext
+               end
+               
+               text = text:gsub("%[","%[%[")
+               text = text:gsub("]","]]")
+               text = text:gsub(";"," ")
+       end
+       return text
+end
+
+--------------------------------------------------------------------------------
+function explode_textlist_event(text)
+       
+       local retval = {}
+       retval.typ = "INV"
+       
+       local parts = text:split(":")
+                               
+       if #parts == 2 then
+               retval.typ = parts[1]:trim()
+               retval.index= tonumber(parts[2]:trim())
+               
+               if type(retval.index) ~= "number" then
+                       retval.typ = "INV"
+               end
+       end
+       
+       return retval
+end
diff --git a/builtin/modmgr.lua b/builtin/modmgr.lua
new file mode 100644 (file)
index 0000000..1cb4b39
--- /dev/null
@@ -0,0 +1,881 @@
+--Minetest
+--Copyright (C) 2013 sapier
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+--------------------------------------------------------------------------------
+function get_mods(path,retval,basefolder)
+
+       local mods = engine.get_dirlist(path,true)
+                       
+       for i=1,#mods,1 do
+               local filename = path .. DIR_DELIM .. mods[i] .. DIR_DELIM .. "modpack.txt"
+               local modpackfile,error = io.open(filename,"r")
+               
+               local name = mods[i]
+               if basefolder ~= nil and
+                       basefolder ~= "" then
+                       name = basefolder .. DIR_DELIM .. mods[i]
+               end
+                       
+               if modpackfile ~= nil then
+                       modpackfile:close()
+                       table.insert(retval,name .. " <MODPACK>")
+                       get_mods(path .. DIR_DELIM .. name,retval,name)
+               else
+
+                       table.insert(retval,name)
+               end
+       end
+end
+
+--modmanager implementation
+modmgr = {}
+
+--------------------------------------------------------------------------------
+function modmgr.extract(modfile)
+       if modfile.type == "zip" then
+               local tempfolder = os.tempfolder()
+               
+               if tempfolder ~= nil and
+                       tempfodler ~= "" then
+                       engine.create_dir(tempfolder)
+                       engine.extract_zip(modfile.name,tempfolder)
+                       return tempfolder
+               end
+       end
+end
+
+-------------------------------------------------------------------------------
+function modmgr.getbasefolder(temppath)
+
+       if temppath == nil then
+               return {
+               type = "invalid",
+               path = ""
+               }
+       end
+
+       local testfile = io.open(temppath .. DIR_DELIM .. "init.lua","r")
+       if testfile ~= nil then
+               testfile:close()
+               return {
+                               type="mod",
+                               path=temppath
+                               }
+       end
+       
+       testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r")
+       if testfile ~= nil then
+               testfile:close()
+               return {
+                               type="modpack",
+                               path=temppath
+                               }
+       end
+       
+       local subdirs = engine.get_dirlist(temppath,true)
+       
+       --only single mod or modpack allowed
+       if #subdirs ~= 1 then
+               return {
+                       type = "invalid",
+                       path = ""
+                       }
+       end
+
+       testfile = 
+       io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r")
+       if testfile ~= nil then
+               testfile:close()
+               return {
+                       type="mod",
+                       path= temppath .. DIR_DELIM .. subdirs[1]
+                       }
+       end
+       
+       testfile = 
+       io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r")
+       if testfile ~= nil then
+               testfile:close()
+               return {
+                       type="modpack",
+                       path=temppath ..  DIR_DELIM .. subdirs[1]
+                       }
+       end
+
+       return {
+               type = "invalid",
+               path = ""
+               }
+end
+
+--------------------------------------------------------------------------------
+function modmgr.isValidModname(modpath)
+       if modpath:find("-") ~= nil then
+               return false
+       end
+       
+       return true
+end
+
+--------------------------------------------------------------------------------
+function modmgr.parse_register_line(line)
+       local pos1 = line:find("\"")
+       local pos2 = nil
+       if pos1 ~= nil then
+               pos2 = line:find("\"",pos1+1)
+       end
+       
+       if pos1 ~= nil and pos2 ~= nil then
+               local item = line:sub(pos1+1,pos2-1)
+               
+               if item ~= nil and
+                       item ~= "" then
+                       local pos3 = item:find(":")
+                       
+                       if pos3 ~= nil then
+                               return item:sub(1,pos3-1)
+                       end
+               end
+       end
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function modmgr.parse_dofile_line(modpath,line)
+       local pos1 = line:find("\"")
+       local pos2 = nil
+       if pos1 ~= nil then
+               pos2 = line:find("\"",pos1+1)
+       end
+       
+       if pos1 ~= nil and pos2 ~= nil then
+               local filename = line:sub(pos1+1,pos2-1)
+               
+               if filename ~= nil and
+                       filename ~= "" and
+                       filename:find(".lua") then
+                       return modmgr.identify_modname(modpath,filename)
+               end
+       end
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function modmgr.update_global_mods()
+       local modpath = engine.get_modpath()
+       modmgr.global_mods = {}
+       if modpath ~= nil and
+               modpath ~= "" then
+               get_mods(modpath,modmgr.global_mods)
+       end
+end
+
+--------------------------------------------------------------------------------
+function modmgr.get_mods_list()
+       local toadd = ""
+       
+       modmgr.update_global_mods()
+       
+       if modmgr.global_mods ~= nil then
+               for i=1,#modmgr.global_mods,1 do
+                       if toadd ~= "" then
+                               toadd = toadd..","
+                       end
+                       toadd = toadd .. modmgr.global_mods[i]
+               end 
+       end
+       
+       return toadd
+end
+
+--------------------------------------------------------------------------------
+function modmgr.mod_exists(basename)
+       modmgr.update_global_mods()
+       
+       if modmgr.global_mods ~= nil then
+               for i=1,#modmgr.global_mods,1 do
+                       if modmgr.global_mods[i] == basename then
+                               return true
+                       end
+               end
+       end
+       
+       return false
+end
+
+--------------------------------------------------------------------------------
+function modmgr.identify_modname(modpath,filename)
+       local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
+       if testfile ~= nil then
+               local line = testfile:read()
+               
+               while line~= nil do
+                       local modname = nil
+               
+                       if line:find("minetest.register_tool") then
+                               modname = modmgr.parse_register_line(line)
+                       end
+                       
+                       if line:find("minetest.register_craftitem") then
+                               modname = modmgr.parse_register_line(line)
+                       end
+                       
+                       
+                       if line:find("minetest.register_node") then
+                               modname = modmgr.parse_register_line(line)
+                       end
+                       
+                       if line:find("dofile") then
+                               modname = modmgr.parse_dofile_line(modpath,line)
+                       end
+               
+                       if modname ~= nil then
+                               testfile:close()
+                               return modname
+                       end
+                       
+                       line = testfile:read()
+               end
+               testfile:close()
+       end
+       
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function modmgr.tab()
+       if modmgr.selected_mod == nil then
+               modmgr.selected_mod = 1
+       end
+       
+       local retval = 
+               "vertlabel[0,-0.25;MODS]" ..
+               "label[0.8,-0.25;Installed Mods:]" ..
+               "textlist[0.75,0.25;4.5,4.3;modlist;" ..
+               modmgr.get_mods_list() .. 
+               ";" .. modmgr.selected_mod .. "]"
+
+       retval = retval ..
+               "button[1,4.85;2,0.5;btn_mod_mgr_install_local;Install]" ..
+               "button[3,4.85;2,0.5;btn_mod_mgr_download;Download]"
+               
+       if #modmgr.global_mods >= modmgr.selected_mod and
+               modmgr.global_mods[modmgr.selected_mod]:find("<MODPACK>") then
+               retval = retval .. "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;Rename]"
+       end
+       
+       if #modmgr.global_mods >= modmgr.selected_mod then
+               local modpath = engine.get_modpath()
+               --show dependencys
+               if modmgr.global_mods[modmgr.selected_mod]:find("<MODPACK>") == nil then
+                       retval = retval .. 
+                               "label[6,1.9;Depends:]" ..
+                               "textlist[6,2.4;5.7,2;deplist;"
+                               
+                       toadd = modmgr.get_dependencys(modpath .. DIR_DELIM .. 
+                                               modmgr.global_mods[modmgr.selected_mod])
+                       
+                       retval = retval .. toadd .. ";0;true,false]"
+                       
+                       --TODO read modinfo
+               end
+               --show delete button
+               retval = retval .. "button[8,4.85;2,0.5;btn_mod_mgr_delete_mod;Delete]"
+       end
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.dialog_rename_modpack()
+
+       local modname = modmgr.global_mods[modmgr.selected_mod]
+       modname = modname:sub(0,modname:find("<") -2)
+       
+       local retval = 
+               "label[1.75,1;Rename Modpack:]"..
+               "field[4.5,1.4;6,0.5;te_modpack_name;;" ..
+               modname ..
+               "]" ..
+               "button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;Accept]" ..
+               "button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;Cancel]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.precheck()
+       if modmgr.global_mods == nil then
+               modmgr.update_global_mods()
+       end
+       
+       if modmgr.world_config_selected_world == nil then
+               modmgr.world_config_selected_world = 1
+       end
+       
+       if modmgr.world_config_selected_mod == nil then
+               modmgr.world_config_selected_mod = 1
+       end
+       
+       if modmgr.hide_gamemods == nil then
+               modmgr.hide_gamemods = true
+       end
+end
+
+--------------------------------------------------------------------------------
+function modmgr.get_worldmod_idx()
+       if not modmgr.hide_gamemods then
+               return modmgr.world_config_selected_mod - #modmgr.worldconfig.game_mods
+       else
+               return modmgr.world_config_selected_mod
+       end
+end
+
+--------------------------------------------------------------------------------
+function modmgr.is_gamemod()
+       if not modmgr.hide_gamemods then
+               if modmgr.world_config_selected_mod <= #modmgr.worldconfig.game_mods then
+                       return true
+               else
+                       return false
+               end
+       else
+               return false
+       end
+end
+
+--------------------------------------------------------------------------------
+function modmgr.render_worldmodlist()
+       local retval = ""
+       
+       for i=1,#modmgr.global_mods,1 do
+               local parts = modmgr.global_mods[i]:split(DIR_DELIM)
+               local shortname = parts[#parts]
+               if modmgr.worldconfig.global_mods[shortname] then
+                       retval = retval .. "#GRN" .. modmgr.global_mods[i] .. ","
+               else
+                       retval = retval .. modmgr.global_mods[i] .. ","
+               end
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.render_gamemodlist()
+       local retval = ""
+       for i=1,#modmgr.worldconfig.game_mods,1 do
+               retval = retval ..
+                       "#BLU" .. modmgr.worldconfig.game_mods[i] .. ","
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.dialog_configure_world()
+       modmgr.precheck()
+       
+       local modpack_selected = false
+       local gamemod_selected = modmgr.is_gamemod()
+       local modname = ""
+       local modfolder = ""
+       local shortname = ""
+       
+       if not gamemod_selected then
+               local worldmodidx = modmgr.get_worldmod_idx()
+               modname = modmgr.global_mods[worldmodidx]
+
+               if modname:find("<MODPACK>") ~= nil then
+                       modname = modname:sub(0,modname:find("<") -2)
+                       modpack_selected = true
+               end
+               
+               local parts = modmgr.global_mods[worldmodidx]:split(DIR_DELIM)
+               shortname = parts[#parts]
+               
+               modfolder = engine.get_modpath() .. DIR_DELIM .. modname
+       end
+
+       local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
+       
+       local retval =
+               "size[11,6.5]" ..
+               "label[1.5,-0.25;World: " .. worldspec.name .. "]"
+               
+       if modmgr.hide_gamemods then
+               retval = retval .. "checkbox[5.5,6.15;cb_hide_gamemods;Hide Game;true]"
+       else
+               retval = retval .. "checkbox[5.5,6.15;cb_hide_gamemods;Hide Game;false]"
+       end
+       retval = retval ..
+               "button[9.25,6.35;2,0.5;btn_config_world_save;Save]" ..
+               "button[7.4,6.35;2,0.5;btn_config_world_cancel;Cancel]" ..
+               "textlist[5.5,-0.25;5.5,6.5;world_config_modlist;"
+               
+
+       if not modmgr.hide_gamemods then
+               retval = retval .. modmgr.render_gamemodlist()
+       end
+       
+       retval = retval .. modmgr.render_worldmodlist() 
+
+       retval = retval .. ";" .. modmgr.world_config_selected_mod .."]"
+       
+       if not gamemod_selected then
+               retval = retval ..
+                       "label[0,0.45;Mod:]" ..
+                       "label[0.75,0.45;" .. modname .. "]" ..
+                       "label[0,1.5;depends on:]" ..
+                       "textlist[0,2;5,2;world_config_depends;" ..
+                       modmgr.get_dependencys(modfolder) .. ";0]" ..
+                       "label[0,4;depends on:]" ..
+                       "textlist[0,4.5;5,2;world_config_is_required;;0]"
+       
+               if modpack_selected then
+                       retval = retval ..
+                               "button[-0.05,1.05;2,0.5;btn_cfgw_enable_all;Enable All]" ..
+                               "button[3.25,1.05;2,0.5;btn_cfgw_disable_all;Disable All]"
+               else
+                       retval = retval .. 
+                               "checkbox[0,0.8;cb_mod_enabled;enabled;"
+                       
+                       if modmgr.worldconfig.global_mods[shortname] then
+                               print("checkbox " .. shortname .. " enabled")
+                               retval = retval .. "true"
+                       else
+                               print("checkbox " .. shortname .. " disabled")
+                               retval = retval .. "false"
+                       end
+                       
+                       retval = retval .. "]"
+               end
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.handle_buttons(tab,fields)
+
+       local retval = nil
+       
+       if tab == "mod_mgr" then
+               retval = modmgr.handle_modmgr_buttons(fields)
+       end
+       
+       if tab == "dialog_rename_modpack" then
+               retval = modmgr.handle_rename_modpack_buttons(fields)
+       end
+       
+       if tab == "dialog_delete_mod" then
+               retval = modmgr.handle_delete_mod_buttons(fields)
+       end
+       
+       if tab == "dialog_configure_world" then
+               retval = modmgr.handle_configure_world_buttons(fields)
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.get_dependencys(modfolder)
+       local filename = modfolder ..
+                               DIR_DELIM .. "depends.txt"
+
+       local dependencyfile = io.open(filename,"r")
+       
+       local toadd = ""
+       if dependencyfile then
+               local dependency = dependencyfile:read("*l")
+               while dependency do
+                       if toadd ~= "" then     
+                               toadd = toadd .. ","
+                       end
+                       toadd = toadd .. dependency
+                       dependency = dependencyfile:read()
+               end
+               dependencyfile:close()
+       else
+               print(filename .. " not found")
+       end
+
+       return toadd
+end
+
+
+--------------------------------------------------------------------------------
+function modmgr.get_worldconfig(worldpath)
+       local filename = worldpath ..
+                               DIR_DELIM .. "world.mt"
+
+       local worldfile = io.open(filename,"r")
+       
+       local worldconfig = {}
+       worldconfig.global_mods = {}
+       worldconfig.game_mods = {}
+       
+       if worldfile then
+               local dependency = worldfile:read("*l")
+               while dependency do
+                       local parts = dependency:split("=")
+
+                       local key = parts[1]:trim()
+
+                       if key == "gameid" then
+                               worldconfig.id = parts[2]:trim()
+                       else
+                               local key = parts[1]:trim():sub(10)
+                               if parts[2]:trim() == "true" then
+                                       print("found enabled mod: >" .. key .. "<")
+                                       worldconfig.global_mods[key] = true
+                               else
+                                       print("found disabled mod: >" .. key .. "<")
+                                       worldconfig.global_mods[key] = false
+                               end
+                       end
+                       dependency = worldfile:read("*l")
+               end
+               worldfile:close()
+       else
+               print(filename .. " not found")
+       end
+       
+       --read gamemods
+       local gamemodpath = engine.get_gamepath() .. DIR_DELIM .. worldconfig.id .. DIR_DELIM .. "mods"
+       
+       print("reading game mods from: " .. dump(gamemodpath))
+       get_mods(gamemodpath,worldconfig.game_mods)
+
+       return worldconfig
+end
+--------------------------------------------------------------------------------
+function modmgr.handle_modmgr_buttons(fields)
+       local retval = {
+                       tab = nil,
+                       is_dialog = nil,
+                       show_buttons = nil,
+               }
+
+       if fields["modlist"] ~= nil then
+               local event = explode_textlist_event(fields["modlist"])
+               modmgr.selected_mod = event.index
+       end
+       
+       if fields["btn_mod_mgr_install_local"] ~= nil then
+               engine.show_file_open_dialog("mod_mgt_open_dlg","Select Mod File:")
+       end
+       
+       if fields["btn_mod_mgr_download"] ~= nil then
+               retval.current_tab = "dialog_modstore_unsorted"
+               retval.is_dialog = true
+               retval.show_buttons = false
+               return retval
+       end
+       
+       if fields["btn_mod_mgr_rename_modpack"] ~= nil then
+               retval.current_tab = "dialog_rename_modpack"
+               retval.is_dialog = true
+               retval.show_buttons = false
+               return retval
+       end
+       
+       if fields["btn_mod_mgr_delete_mod"] ~= nil then
+               retval.current_tab = "dialog_delete_mod"
+               retval.is_dialog = true
+               retval.show_buttons = false
+               return retval
+       end
+       
+       if fields["mod_mgt_open_dlg_accepted"] ~= nil and
+               fields["mod_mgt_open_dlg_accepted"] ~= "" then
+               modmgr.installmod(fields["mod_mgt_open_dlg_accepted"],nil)
+       end
+       
+       return nil;
+end
+
+--------------------------------------------------------------------------------
+function modmgr.installmod(modfilename,basename)
+       local modfile = identify_filetype(modfilename)
+       
+       local modpath = modmgr.extract(modfile)
+       
+       if modpath == nil then
+               gamedata.errormessage = "Install Mod: file: " .. modfile.name ..
+                       "\nInstall Mod: unsupported filetype \"" .. modfile.type .. "\""
+               return
+       end
+       
+       
+       local basefolder = modmgr.getbasefolder(modpath)
+       
+       if basefolder.type == "modpack" then
+               local clean_path = nil
+               
+               if basename ~= nil then
+                       clean_path = "mp_" .. basename
+               end
+               
+               if clean_path == nil then
+                       clean_path = get_last_folder(cleanup_path(basefolder.path))
+               end
+               
+               if clean_path ~= nil then
+                       local targetpath = engine.get_modpath() .. DIR_DELIM .. clean_path
+                       engine.copy_dir(basefolder.path,targetpath)
+               else
+                       gamedata.errormessage = "Install Mod: unable to find suitable foldername for modpack " 
+                               .. modfilename
+               end
+       end
+       
+       if basefolder.type == "mod" then
+               local targetfolder = basename
+               
+               if targetfolder == nil then
+                       targetfolder = modmgr.identify_modname(basefolder.path,"init.lua")
+               end
+               
+               --if heuristic failed try to use current foldername
+               if targetfolder == nil then
+                       targetfolder = get_last_folder(basefolder.path)
+               end     
+               
+               if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then
+                       local targetpath = engine.get_modpath() .. DIR_DELIM .. targetfolder
+                       engine.copy_dir(basefolder.path,targetpath)
+               else
+                       gamedata.errormessage = "Install Mod: unable to find real modname for: " 
+                               .. modfilename
+               end
+       end
+       
+       engine.delete_dir(modpath)
+end
+
+--------------------------------------------------------------------------------
+function modmgr.handle_rename_modpack_buttons(fields)
+       local oldname = modmgr.global_mods[modmgr.selected_mod]
+       oldname = oldname:sub(0,oldname:find("<") -2)
+       
+       if fields["dlg_rename_modpack_confirm"] ~= nil then
+               local oldpath = engine.get_modpath() .. DIR_DELIM .. oldname
+               local targetpath = engine.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"]
+               engine.copy_dir(oldpath,targetpath,false)
+       end
+       
+       return {
+               is_dialog = false,
+               show_buttons = true,
+               current_tab = engine.setting_get("main_menu_tab")
+               }
+end
+--------------------------------------------------------------------------------
+function modmgr.handle_configure_world_buttons(fields)
+       if fields["world_config_modlist"] ~= nil then
+               local event = explode_textlist_event(fields["world_config_modlist"])
+               modmgr.world_config_selected_mod = event.index
+       end
+
+       if fields["cb_mod_enabled"] ~= nil then
+               local index = modmgr.get_worldmod_idx()
+               local modname = modmgr.global_mods[index]
+                                                               
+               local parts = modmgr.global_mods[index]:split(DIR_DELIM)
+               local shortname = parts[#parts]
+               
+               if fields["cb_mod_enabled"] == "true" then
+                       modmgr.worldconfig.global_mods[shortname] = true
+               else
+                       modmgr.worldconfig.global_mods[shortname] = false
+               end
+       end
+       
+       if fields["cb_hide_gamemods"] ~= nil then
+               if fields["cb_hide_gamemods"] == "true" then
+                       modmgr.hide_gamemods = true
+               else
+                       modmgr.hide_gamemods = false
+               end
+       end
+       
+       if fields["btn_config_world_save"] then
+               local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
+               
+               local filename = worldspec.path ..
+                               DIR_DELIM .. "world.mt"
+
+               local worldfile = io.open(filename,"w")
+               
+               if worldfile then
+                       worldfile:write("gameid = " .. modmgr.worldconfig.id .. "\n")
+                       for key,value in pairs(modmgr.worldconfig.global_mods) do
+                               if value then
+                                       worldfile:write("load_mod_" .. key .. " = true" .. "\n")
+                               else
+                                       worldfile:write("load_mod_" .. key .. " = false" .. "\n")
+                               end
+                       end
+                       
+                       worldfile:close()
+               end
+               
+               modmgr.worldconfig = nil
+       
+               return {
+                       is_dialog = false,
+                       show_buttons = true,
+                       current_tab = engine.setting_get("main_menu_tab")
+               }
+       end
+       
+       if fields["btn_config_world_cancel"] then
+       
+               modmgr.worldconfig = nil
+               
+               return {
+                       is_dialog = false,
+                       show_buttons = true,
+                       current_tab = engine.setting_get("main_menu_tab")
+               }
+       end
+       
+       if fields["btn_cfgw_enable_all"] then
+               local worldmodidx = modmgr.get_worldmod_idx()
+               modname = modmgr.global_mods[worldmodidx]
+
+               modname = modname:sub(0,modname:find("<") -2)
+
+               for i=1,#modmgr.global_mods,1 do
+               
+                       if modmgr.global_mods[i]:find("<MODPACK>") == nil then
+                               local modpackpart = modmgr.global_mods[i]:sub(0,modname:len())
+                               
+                               if modpackpart == modname then
+                                       local parts = modmgr.global_mods[i]:split(DIR_DELIM)
+                                       local shortname = parts[#parts]
+                                       modmgr.worldconfig.global_mods[shortname] = true
+                               end
+                       end
+               end
+       end
+       
+       if fields["btn_cfgw_disable_all"] then
+               local worldmodidx = modmgr.get_worldmod_idx()
+               modname = modmgr.global_mods[worldmodidx]
+
+               modname = modname:sub(0,modname:find("<") -2)
+
+               for i=1,#modmgr.global_mods,1 do
+                       local modpackpart = modmgr.global_mods[i]:sub(0,modname:len())
+                       
+                       if modpackpart == modname then
+                               local parts = modmgr.global_mods[i]:split(DIR_DELIM)
+                               local shortname = parts[#parts]
+                               modmgr.worldconfig.global_mods[shortname] = nil
+                       end
+               end
+       end
+       
+       return nil
+end
+--------------------------------------------------------------------------------
+function modmgr.handle_delete_mod_buttons(fields)
+       local modname = modmgr.global_mods[modmgr.selected_mod]
+       
+       if modname:find("<MODPACK>") ~= nil then
+               modname = modname:sub(0,modname:find("<") -2)
+       end
+       
+       if fields["dlg_delete_mod_confirm"] ~= nil then
+               local oldpath = engine.get_modpath() .. DIR_DELIM .. modname
+               
+               if oldpath ~= nil and
+                       oldpath ~= "" and
+                       oldpath ~= engine.get_modpath() then
+                       engine.delete_dir(oldpath)
+               end
+       end
+       
+       return {
+               is_dialog = false,
+               show_buttons = true,
+               current_tab = engine.setting_get("main_menu_tab")
+               }
+end
+
+--------------------------------------------------------------------------------
+function modmgr.dialog_delete_mod()
+
+       local modname = modmgr.global_mods[modmgr.selected_mod]
+
+       if modname:find("<MODPACK>") ~= nil then
+               modname = modname:sub(0,modname:find("<") -2)
+       end
+       
+       local retval = 
+               "field[1.75,1;10,3;;Are you sure you want to delete ".. modname .. "?;]"..
+               "button[4,4.2;1,0.5;dlg_delete_mod_confirm;Yes]" ..
+               "button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;No of course not!]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.init_worldconfig()
+
+       local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
+       
+       if worldspec ~= nil then
+               --read worldconfig
+               modmgr.worldconfig = modmgr.get_worldconfig(worldspec.path)
+               
+               if modmgr.worldconfig.id == nil or
+                       modmgr.worldconfig.id == "" then
+                       modmgr.worldconfig = nil
+                       return false
+               end
+               
+               return true     
+       end
+
+       return false
+end
+
+--------------------------------------------------------------------------------
+function modmgr.gettab(name)
+       local retval = ""
+       
+       if name == "mod_mgr" then
+               retval = retval .. modmgr.tab()
+       end
+       
+       if name == "dialog_rename_modpack" then
+               retval = retval .. modmgr.dialog_rename_modpack()
+       end
+       
+       if name == "dialog_delete_mod" then
+               retval = retval .. modmgr.dialog_delete_mod()
+       end
+       
+       if name == "dialog_configure_world" then
+               retval = retval .. modmgr.dialog_configure_world()
+       end
+       
+       return retval
+end
diff --git a/builtin/modstore.lua b/builtin/modstore.lua
new file mode 100644 (file)
index 0000000..73afc38
--- /dev/null
@@ -0,0 +1,275 @@
+--Minetest
+--Copyright (C) 2013 sapier
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+--------------------------------------------------------------------------------
+
+--modstore implementation
+modstore = {}
+
+--------------------------------------------------------------------------------
+function modstore.init()
+       modstore.tabnames = {}
+       
+       table.insert(modstore.tabnames,"dialog_modstore_unsorted")
+       table.insert(modstore.tabnames,"dialog_modstore_search")
+       
+       modstore.modsperpage = 5
+       
+       modstore.basetexturedir = engine.get_gamepath() .. DIR_DELIM .. ".." ..
+                                               DIR_DELIM .. "textures" .. DIR_DELIM .. "base" .. 
+                                               DIR_DELIM .. "pack" .. DIR_DELIM
+       modstore.update_modlist()
+       
+       modstore.current_list = nil
+       
+       modstore.details_cache = {}
+end
+--------------------------------------------------------------------------------
+function modstore.nametoindex(name)
+
+       for i=1,#modstore.tabnames,1 do
+               if modstore.tabnames[i] == name then
+                       return i
+               end
+       end
+
+       return 1
+end
+
+--------------------------------------------------------------------------------
+function modstore.gettab(tabname)
+       local retval = ""
+       
+       local is_modstore_tab = false
+       
+       if tabname == "dialog_modstore_unsorted" then
+               retval = modstore.getmodlist(modstore.modlist_unsorted)
+               is_modstore_tab = true
+       end
+       
+       if tabname == "dialog_modstore_search" then
+       
+       
+               is_modstore_tab = true
+       end
+       
+       if is_modstore_tab then
+               return modstore.tabheader(tabname) .. retval
+       end
+       
+       if tabname == "modstore_mod_installed" then
+               return "size[6,2]label[0.25,0.25;Mod: " .. modstore.lastmodtitle .. 
+                               " installed successfully]" ..
+                               "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;ok]"
+       end
+       
+       return ""
+end
+
+--------------------------------------------------------------------------------
+function modstore.tabheader(tabname)
+       local retval  = "size[12,9.25]"
+       retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
+                               "Unsorted,Search;" ..
+                               modstore.nametoindex(tabname) .. ";true;false]"
+                               
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modstore.handle_buttons(current_tab,fields)
+
+       modstore.lastmodtitle = ""
+       
+       if fields["modstore_tab"] then
+               local index = tonumber(fields["modstore_tab"])
+               
+               if index > 0 and
+                       index <= #modstore.tabnames then
+                       return {
+                                       current_tab = modstore.tabnames[index],
+                                       is_dialog = true,
+                                       show_buttons = false
+                       }
+               end
+               
+               modstore.modlist_page = 0
+       end
+       
+       if fields["btn_modstore_page_up"] then
+               if modstore.current_list ~= nil and modstore.current_list.page > 0 then
+                       modstore.current_list.page = modstore.current_list.page - 1
+               end
+       end
+       
+       if fields["btn_modstore_page_down"] then
+               if modstore.current_list ~= nil and 
+                       modstore.current_list.page <modstore.current_list.pagecount then
+                       modstore.current_list.page = modstore.current_list.page +1
+               end
+       end
+       
+       if fields["btn_confirm_mod_successfull"] then
+               return {
+                                       current_tab = modstore.tabnames[1],
+                                       is_dialog = true,
+                                       show_buttons = false
+                       }
+       end
+       
+       for i=1, modstore.modsperpage, 1 do
+               local installbtn = "btn_install_mod_" .. i
+               
+               if fields[installbtn] then
+                       local modlistentry = 
+                               modstore.current_list.page * modstore.modsperpage + i
+                       
+                       local moddetails = modstore.get_details(modstore.current_list.data[modlistentry].id)
+                       
+                       local fullurl = engine.setting_get("modstore_download_url") ..
+                                                               moddetails.download_url
+                       local modfilename = os.tempfolder() .. ".zip"
+                       print("Downloading mod from: " .. fullurl .. " to ".. modfilename)
+                       
+                       if engine.download_file(fullurl,modfilename) then
+                       
+                               modmgr.installmod(modfilename,moddetails.basename)
+                               
+                               os.remove(modfilename)
+                               modstore.lastmodtitle = modstore.current_list.data[modlistentry].title
+                               
+                               return {
+                                       current_tab = "modstore_mod_installed",
+                                       is_dialog = true,
+                                       show_buttons = false
+                               }
+                       else
+                               gamedata.errormessage = "Unable to download " .. 
+                                       moddetails.download_url .. " (internet connection?)"
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function modstore.update_modlist()
+       modstore.modlist_unsorted = {}
+       modstore.modlist_unsorted.data = engine.get_modstore_list()
+               
+       if modstore.modlist_unsorted.data ~= nil then
+               modstore.modlist_unsorted.pagecount = 
+                       math.floor((#modstore.modlist_unsorted.data / modstore.modsperpage))
+       else
+               modstore.modlist_unsorted.data = {}
+               modstore.modlist_unsorted.pagecount = 0
+       end
+       modstore.modlist_unsorted.page = 0
+end
+
+--------------------------------------------------------------------------------
+function modstore.getmodlist(list)
+       local retval = ""
+       retval = retval .. "label[10,-0.4;Page " .. (list.page +1) .. 
+                                                       " of " .. (list.pagecount +1) .. "]"
+       
+       retval = retval .. "button[11.6,-0.1;0.5,0.5;btn_modstore_page_up;^]"
+       retval = retval .. "box[11.6,0.35;0.28,8.6;BLK]"
+       local scrollbarpos = 0.35 + (8.1/list.pagecount) * list.page
+       retval = retval .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;LIM]"
+       retval = retval .. "button[11.6,9.0;0.5,0.5;btn_modstore_page_down;v]"
+       
+       
+       if #list.data < (list.page * modstore.modsperpage) then
+               return retval
+       end
+       
+       local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage
+       
+       if (endmod > #list.data) then
+               endmod = #list.data
+       end
+
+       for i=(list.page * modstore.modsperpage) +1, endmod, 1 do
+               --getmoddetails
+               local details = modstore.get_details(list.data[i].id)
+       
+               if details ~= nil then
+                       local screenshot_ypos = (i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
+                       
+                       retval = retval .. "box[0," .. screenshot_ypos .. ";11.4,1.75;WHT]"
+                       
+                       --screenshot
+                       if details.screenshot_url ~= nil and
+                               details.screenshot_url ~= "" then
+                               if list.data[i].texturename == nil then
+                                       print("downloading screenshot: " .. details.screenshot_url)
+                                       local filename = os.tempfolder()
+                                       
+                                       if engine.download_file(details.screenshot_url,filename) then
+                                               list.data[i].texturename = filename
+                                       end
+                               end
+                       end
+                       
+                       if list.data[i].texturename == nil then
+                               list.data[i].texturename = modstore.basetexturedir .. "no_screenshot.png"
+                       end
+                       
+                       retval = retval .. "image[0,".. screenshot_ypos .. ";3,2;" .. 
+                                       list.data[i].texturename .. "]"
+                       
+                       --title + author
+                       retval = retval .."label[2.75," .. screenshot_ypos .. ";" .. 
+                               fs_escape_string(details.title) .. " (" .. details.author .. ")]"
+                       
+                       --description
+                       local descriptiony = screenshot_ypos + 0.5
+                       retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.6;;" .. 
+                               fs_escape_string(details.description) .. ";]"
+                       --rating
+                       local ratingy = screenshot_ypos + 0.6
+                       retval = retval .."label[10.1," .. ratingy .. ";Rating: " .. details.rating .."]"
+                       
+                       --install button
+                       local buttony = screenshot_ypos + 1.2
+                       local buttonnumber = (i - (list.page * modstore.modsperpage))
+                       retval = retval .."button[9.6," .. buttony .. ";2,0.5;btn_install_mod_" .. buttonnumber .. ";"
+                       
+                       if modmgr.mod_exists(details.basename) then
+                               retval = retval .. "re-Install]"
+                       else
+                               retval = retval .. "Install]"
+                       end
+               end
+       end
+       
+       modstore.current_list = list
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modstore.get_details(modid) 
+
+       if modstore.details_cache[modid] ~= nil then
+               return modstore.details_cache[modid]
+       end
+       
+       local retval = engine.get_modstore_details(tostring(modid))
+       modstore.details_cache[modid] = retval
+       return retval
+end
\ No newline at end of file
index cd0824eb3d096455b28dc5fb4f6bcaf60ecb9e00..ebba40fbf2245fbe0e9d35a09e70ad3117ca5274 100644 (file)
@@ -883,6 +883,15 @@ background[<X>,<Y>;<W>,<H>;<texture name>]
 ^ Position and size units are inventory slots
 ^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px
 
+pwdfield[<X>,<Y>;<W>,<H>;<name>;<label>]
+^ Textual password style field; will be sent to server when a button is clicked
+^ x and y position the field relative to the top left of the menu
+^ w and h are the size of the field
+^ fields are a set height, but will be vertically centred on h
+^ Position and size units are inventory slots
+^ name is the name of the field as returned in fields to on_receive_fields
+^ label, if not blank, will be text printed on the top left above the field
+
 field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
 ^ Textual field; will be sent to server when a button is clicked
 ^ x and y position the field relative to the top left of the menu
@@ -910,6 +919,12 @@ label[<X>,<Y>;<label>]
 ^ label is the text on the label
 ^ Position and size units are inventory slots
 
+vertlabel[<X>,<Y>;<label>]
+^ Textual label drawn verticaly
+^ x and y work as per field
+^ label is the text on the label
+^ Position and size units are inventory slots
+
 button[<X>,<Y>;<W>,<H>;<name>;<label>]
 ^ Clickable button. When clicked, fields will be sent.
 ^ x, y and name work as per field
@@ -922,6 +937,13 @@ image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
 ^ image is the filename of an image
 ^ Position and size units are inventory slots
 
+image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>;<noclip>;<drawborder>]
+^ x, y, w, h, and name work as per button
+^ image is the filename of an image
+^ Position and size units are inventory slots
+^ noclip true meand imagebutton doesn't need to be within specified formsize
+^ drawborder draw button bodrer or not
+
 item_image_button[<X>,<Y>;<W>,<H>;<item name>;<name>;<label>]
 ^ x, y, w, h, name and label work as per button
 ^ item name is the registered name of an item/node,
@@ -934,6 +956,42 @@ button_exit[<X>,<Y>;<W>,<H>;<name>;<label>]
 image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
 ^ When clicked, fields will be sent and the form will quit.
 
+textlist[<X>,<Y>;<W>,<H>;<name>;<listelem 1>,<listelem 2>,...,<listelem n>]
+^Scrollabel itemlist showing arbitrary text elements
+^ x and y position the itemlist relative to the top left of the menu
+^ w and h are the size of the itemlist
+^ listelements can be prepended by #colorkey (see colorkeys), 
+^    if you want a listelement to start with # write ##
+^ name fieldname sent to server on doubleclick value is current selected element
+
+tabheader[<X>,<Y>;<name>;<caption 1>,<caption 2>;<current_tab>;<transparent>;<draw_border>]
+^ show a tabHEADER at specific position (ignores formsize)
+^ x and y position the itemlist relative to the top left of the menu
+^ name fieldname data is transfered to lua
+^ caption 1... name shown on top of tab
+^ current_tab index of selected tab 1...
+^ transparent (optional) show transparent
+^ draw_border (optional) draw border
+
+box[<X>,<Y>;<W>,<H>;<colorkey>]
+^ simple colored semitransparent box
+^ x and y position the box relative to the top left of the menu
+^ w and h are the size of box
+^ colorkey (see colorkeys)
+
+Available colorkeys:
+- YLW yellow
+- GRN green
+- LIM lime
+- ORN orange
+- RED red
+- BLU blue
+- CYN cyan
+- BLK black
+- BRN brown
+- WHT white
+- GRY grey
+
 Inventory location:
 
 - "context": Selected node metadata (deprecated: "current_name")
diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt
new file mode 100644 (file)
index 0000000..337091a
--- /dev/null
@@ -0,0 +1,163 @@
+Minetest Lua Mainmenu API Reference 0.4.6
+========================================
+
+Introduction
+-------------
+The main menu is defined as a formspec by Lua in builtin/mainmenu.lua
+Description of formspec language to show your menu is in lua_api.txt
+
+Callbacks
+---------
+engine.buttonhandler(fields): called when a button is pressed.
+^ fields = {name1 = value1, name2 = value2, ...}
+engine.event_handler(event)
+^ event: "MenuQuit", "KeyEnter", "ExitButton" or "EditBoxEnter"
+
+Gamedata
+--------
+The "gamedata" table is read when calling engine.start(). It should contain:
+{
+       playername     = <name>,
+       password       = <password>,
+       address        = <IP/adress>,
+       port           = <port>,
+       selected_world = <index>, -- 0 for client mode
+       singleplayer   = <true/false>,
+}
+
+Functions
+---------
+engine.start()
+engine.close()
+
+Filesystem:
+engine.get_scriptdir()
+^ returns directory of script
+engine.get_modpath()
+^ returns path to global modpath
+engine.get_modstore_details(modid)
+^ modid numeric id of mod in modstore
+^ returns {
+       id                      = <numeric id of mod in modstore>,
+       title           = <human readable title>,
+       basename        = <basename for mod>,
+       description = <description of mod>,
+       author          = <author of mod>,
+       download_url= <best match download url>,
+       license         = <short description of license>,
+       rating          = <float value of current rating>
+}
+engine.get_modstore_list()
+^ returns {
+       [1] = {
+               id               = <numeric id of mod in modstore>,
+               title    = <human readable title>,
+               basename = <basename for mod>
+       }
+}
+engine.get_gamepath()
+^ returns path to global gamepath
+engine.get_dirlist(path,onlydirs)
+^ path to get subdirs from
+^ onlydirs should result contain only dirs?
+^ returns list of folders within path
+engine.create_dir(absolute_path)
+^ absolute_path to directory to create (needs to be absolute)
+^ returns true/false
+engine.delete_dir(absolute_path)
+^ absolute_path to directory to delete (needs to be absolute)
+^ returns true/false
+engine.copy_dir(source,destination,keep_soure)
+^ source folder
+^ destination folder
+^ keep_source DEFAULT true --> if set to false source is deleted after copying
+^ returns true/false
+engine.extract_zip(zipfile,destination) [unzip within path required]
+^ zipfile to extract
+^ destination folder to extract to
+^ returns true/false
+engine.download_file(url,target)
+^ url to download
+^ target to store to
+^ returns true/false
+engine.get_version()
+^ returns current minetest version
+
+GUI:
+engine.update_formspec(formspec)
+- engine.set_background(type, texturepath)
+^ type: "background", "overlay", "header" or "footer"
+engine.set_clouds(<true/false>)
+engine.set_topleft_text(text)
+
+Games:
+engine.get_game(index)
+^ returns {
+       id               = <id>,
+       path             = <full path to game>,
+       gamemods_path    = <path>,
+       name             = <name of game>,
+       menuicon_path    = <full path to menuicon>,
+       DEPRECATED:
+       addon_mods_paths = {[1] = <path>,},
+}
+engine.get_games() -> table of all games in upper format
+
+Favorites:
+engine.get_favorites(location) -> list of favorites
+^ location: "local" or "online"
+^ returns {
+       [1] = {
+       clients       = <number of clients/nil>,
+       clients_max   = <maximum number of clients/nil>,
+       version       = <server version/nil>,
+       password      = <true/nil>,
+       creative      = <true/nil>,
+       damage        = <true/nil>,
+       pvp           = <true/nil>,
+       description   = <server description/nil>,
+       name          = <server name/nil>,
+       address       = <address of server/nil>,
+       port          = <port>
+       },
+}
+engine.delete_favorite(id, location) -> success
+
+Settings:
+engine.setting_set(name, value)
+engine.setting_get(name) -> string or nil
+engine.setting_setbool(name, value)
+engine.setting_getbool(name) -> bool or nil
+
+Worlds:
+engine.get_worlds() -> list of worlds
+^ returns {
+       [1] = {
+       path   = <full path to world>,
+       name   = <name of world>,
+       gameid = <gameid of world>,
+       },
+}
+engine.create_world(worldname, gameid)
+engine.delete_world(index)
+
+
+UI:
+engine.get_textlist_index(textlistname) -> index
+engine.show_keys_menu()
+engine.file_open_dialog(formname,caption)
+^ shows a file open dialog
+^ formname is base name of dialog response returned in fields
+^     -if dialog was accepted "_accepted" 
+^^       will be added to fieldname containing the path
+^     -if dialog was canceled "_cancelled" 
+^        will be added to fieldname value is set to formname itself
+^ returns nil or selected file/folder
+
+Helpers:
+dump(obj, dumped={})
+^ Return object serialized as a string
+string:split(separator)
+^ eg. string:split("a,b", ",") == {"a","b"}
+string:trim()
+^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
\ No newline at end of file
index 75e546c2f353aa9e458db434cca212c373ac3435..d7fa621a015b552d5b6582730295a496526ad6d2 100644 (file)
 # Enable/disable running an IPv6 server.  An IPv6 server may be restricted
 # to IPv6 clients, depending on system configuration.
 #ipv6_server = false
+
+#main_menu_game_mgr = 0
+#main_menu_mod_mgr = 0
+#modstore_download_url = http://forum.minetest.net/media/
+#modstore_listmods_url = http://forum.minetest.net/mmdb/mods/
+#modstore_details_url  = http://forum.minetest.net/mmdb/mod/*/
index bcd26585826921fb496ca3005c8a0bb2fc85d9f8..2b60ff0d2e60f9f7f695e4094a66c92c6a57e620 100644 (file)
@@ -271,6 +271,7 @@ set(common_SRCS
        staticobject.cpp
        serverlist.cpp
        pathfinder.cpp
+       convert_json.cpp
        ${SCRIPT_SRCS}
        ${UTIL_SRCS}
 )
@@ -313,7 +314,6 @@ set(minetest_SRCS
        clientobject.cpp
        chat.cpp
        hud.cpp
-       guiMainMenu.cpp
        guiKeyChangeMenu.cpp
        guiMessageMenu.cpp
        guiTextInputMenu.cpp
@@ -323,15 +323,16 @@ set(minetest_SRCS
        guiVolumeChange.cpp
        guiDeathScreen.cpp
        guiChatConsole.cpp
-       guiCreateWorld.cpp
-       guiConfigureWorld.cpp
-       guiConfirmMenu.cpp
        client.cpp
        filecache.cpp
        tile.cpp
        shader.cpp
        game.cpp
        main.cpp
+       guiEngine.cpp
+       guiLuaApi.cpp
+       guiFileSelectMenu.cpp
+       convert_json.cpp
 )
 
 if(USE_FREETYPE)
@@ -488,7 +489,7 @@ else()
        endif()
 
        set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} ${OTHER_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops")
-       set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}")
+       set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}")
 
        if(USE_GPROF)
                set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")
diff --git a/src/convert_json.cpp b/src/convert_json.cpp
new file mode 100644 (file)
index 0000000..7a69071
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <vector>
+#include <iostream>
+#include <sstream>
+
+#include "convert_json.h"
+#include "mods.h"
+#include "config.h"
+#include "log.h"
+
+#if USE_CURL
+#include <curl/curl.h>
+
+static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+    ((std::string*)userp)->append((char*)contents, size * nmemb);
+    return size * nmemb;
+}
+
+#endif
+
+Json::Value                 fetchJsonValue(const std::string url,
+                                                                                                       struct curl_slist *chunk) {
+#if USE_CURL
+       std::string liststring;
+       CURL *curl;
+
+       curl = curl_easy_init();
+       if (curl)
+       {
+               CURLcode res;
+
+               curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+               curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+               curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
+
+               if (chunk != 0)
+                       curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+
+
+               res = curl_easy_perform(curl);
+               if (res != CURLE_OK)
+                       errorstream<<"Jsonreader: "<< url <<" not found (internet connection?)"<<std::endl;
+               curl_easy_cleanup(curl);
+       }
+
+       Json::Value root;
+       Json::Reader reader;
+       std::istringstream stream(liststring);
+       if (!liststring.size()) {
+               return Json::Value();
+       }
+
+       if (!reader.parse( stream, root ) )
+       {
+               errorstream << "URL: " << url << std::endl;
+               errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
+               errorstream << "data: \"" << liststring << "\"" << std::endl;
+               return Json::Value();
+       }
+
+       if (root.isArray()) {
+               return root;
+       }
+       if ((root["list"].isArray())) {
+               return root["list"];
+       }
+       else {
+               return root;
+       }
+#endif
+       return Json::Value();
+}
+
+std::vector<ModStoreMod>    readModStoreList(Json::Value& modlist) {
+       std::vector<ModStoreMod> retval;
+
+       if (modlist.isArray()) {
+               for (unsigned int i = 0; i < modlist.size(); i++)
+               {
+                       ModStoreMod toadd;
+                       toadd.valid = true;
+
+                       //id
+                       if (modlist[i]["id"].asString().size()) {
+                               const char* id_raw = modlist[i]["id"].asString().c_str();
+                               char* endptr = 0;
+                               int numbervalue = strtol(id_raw,&endptr,10);
+
+                               if ((*id_raw != 0) && (*endptr == 0)) {
+                                       toadd.id = numbervalue;
+                               }
+                       }
+                       else {
+                               toadd.valid = false;
+                       }
+
+                       //title
+                       if (modlist[i]["title"].asString().size()) {
+                               toadd.title = modlist[i]["title"].asString();
+                       }
+                       else {
+                               toadd.valid = false;
+                       }
+
+                       //basename
+                       if (modlist[i]["basename"].asString().size()) {
+                               toadd.basename = modlist[i]["basename"].asString();
+                       }
+                       else {
+                               toadd.valid = false;
+                       }
+
+                       //author
+
+                       //rating
+
+                       //version
+
+                       if (toadd.valid) {
+                               retval.push_back(toadd);
+                       }
+               }
+       }
+       return retval;
+}
+
+ModStoreModDetails          readModStoreModDetails(Json::Value& details) {
+
+       ModStoreModDetails retval;
+
+       retval.valid = true;
+
+       //version set
+       if (details["version_set"].isArray()) {
+               for (unsigned int i = 0; i < details["version_set"].size(); i++)
+               {
+                       ModStoreVersionEntry toadd;
+
+                       if (details["version_set"][i]["id"].asString().size()) {
+                               const char* id_raw = details["version_set"][i]["id"].asString().c_str();
+                               char* endptr = 0;
+                               int numbervalue = strtol(id_raw,&endptr,10);
+
+                               if ((*id_raw != 0) && (*endptr == 0)) {
+                                       toadd.id = numbervalue;
+                               }
+                       }
+                       else {
+                               retval.valid = false;
+                       }
+
+                       //date
+                       if (details["version_set"][i]["date"].asString().size()) {
+                               toadd.date = details["version_set"][i]["date"].asString();
+                       }
+
+                       //file
+                       if (details["version_set"][i]["file"].asString().size()) {
+                               toadd.file = details["version_set"][i]["file"].asString();
+                       }
+                       else {
+                               retval.valid = false;
+                       }
+
+                       //approved
+
+                       //mtversion
+
+                       if( retval.valid ) {
+                               retval.versions.push_back(toadd);
+                       }
+                       else {
+                               break;
+                       }
+               }
+       }
+
+       if (retval.versions.size() < 1) {
+               retval.valid = false;
+       }
+
+       //categories
+       if (details["categories"].isObject()) {
+               for (unsigned int i = 0; i < details["categories"].size(); i++) {
+                       ModStoreCategoryInfo toadd;
+
+                       if (details["categories"][i]["id"].asString().size()) {
+
+                               const char* id_raw = details["categories"][i]["id"].asString().c_str();
+                               char* endptr = 0;
+                               int numbervalue = strtol(id_raw,&endptr,10);
+
+                               if ((*id_raw != 0) && (*endptr == 0)) {
+                                       toadd.id = numbervalue;
+                               }
+                       }
+                       else {
+                               retval.valid = false;
+                       }
+                       if (details["categories"][i]["title"].asString().size()) {
+                               toadd.name = details["categories"][i]["title"].asString();
+                       }
+                       else {
+                               retval.valid = false;
+                       }
+
+                       if( retval.valid ) {
+                               retval.categories.push_back(toadd);
+                       }
+                       else {
+                               break;
+                       }
+               }
+       }
+
+       //author
+       if (details["author"].isObject()) {
+               if (details["author"]["id"].asString().size()) {
+
+                       const char* id_raw = details["author"]["id"].asString().c_str();
+                       char* endptr = 0;
+                       int numbervalue = strtol(id_raw,&endptr,10);
+
+                       if ((*id_raw != 0) && (*endptr == 0)) {
+                               retval.author.id = numbervalue;
+                       }
+                       else {
+                               retval.valid = false;
+                       }
+               }
+               else {
+                       retval.valid = false;
+               }
+
+               if (details["author"]["username"].asString().size()) {
+                       retval.author.username = details["author"]["username"].asString();
+               }
+               else {
+                       retval.valid = false;
+               }
+       }
+       else {
+               retval.valid = false;
+       }
+
+       //license
+       if (details["license"].isObject()) {
+               if (details["license"]["id"].asString().size()) {
+
+                       const char* id_raw = details["license"]["id"].asString().c_str();
+                       char* endptr = 0;
+                       int numbervalue = strtol(id_raw,&endptr,10);
+
+                       if ((*id_raw != 0) && (*endptr == 0)) {
+                               retval.license.id = numbervalue;
+                       }
+               }
+               else {
+                       retval.valid = false;
+               }
+
+               if (details["license"]["short"].asString().size()) {
+                       retval.license.shortinfo = details["license"]["short"].asString();
+               }
+               else {
+                       retval.valid = false;
+               }
+
+               if (details["license"]["link"].asString().size()) {
+                       retval.license.url = details["license"]["link"].asString();
+               }
+
+       }
+
+       //id
+       if (details["id"].asString().size()) {
+
+               const char* id_raw = details["id"].asString().c_str();
+               char* endptr = 0;
+               int numbervalue = strtol(id_raw,&endptr,10);
+
+               if ((*id_raw != 0) && (*endptr == 0)) {
+                       retval.id = numbervalue;
+               }
+       }
+       else {
+               retval.valid = false;
+       }
+
+       //title
+       if (details["title"].asString().size()) {
+               retval.title = details["title"].asString();
+       }
+       else {
+               retval.valid = false;
+       }
+
+       //basename
+       if (details["basename"].asString().size()) {
+               retval.basename = details["basename"].asString();
+       }
+       else {
+               retval.valid = false;
+       }
+
+       //description
+       if (details["desc"].asString().size()) {
+               retval.description = details["desc"].asString();
+       }
+
+       //repository
+       if (details["replink"].asString().size()) {
+               retval.repository = details["replink"].asString();
+       }
+
+       //value
+       if (details["rating"].asString().size()) {
+
+               const char* id_raw = details["rating"].asString().c_str();
+               char* endptr = 0;
+               float numbervalue = strtof(id_raw,&endptr);
+
+               if ((*id_raw != 0) && (*endptr == 0)) {
+                       retval.rating = numbervalue;
+               }
+       }
+       else {
+               retval.rating = 0.0;
+       }
+
+       //depends
+       if (details["depends"].isArray()) {
+               //TODO
+       }
+
+       //softdepends
+       if (details["softdep"].isArray()) {
+               //TODO
+       }
+
+       //screenshot url
+       if (details["screenshot_url"].asString().size()) {
+               retval.screenshot_url = details["screenshot_url"].asString();
+       }
+
+       return retval;
+}
diff --git a/src/convert_json.h b/src/convert_json.h
new file mode 100644 (file)
index 0000000..3fa9903
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __CONVERT_JSON_H__
+#define __CONVERT_JSON_H__
+
+#include "json/json.h"
+
+struct ModStoreMod;
+struct ModStoreModDetails;
+
+std::vector<ModStoreMod>    readModStoreList(Json::Value& modlist);
+ModStoreModDetails          readModStoreModDetails(Json::Value& details);
+
+Json::Value                 fetchJsonValue(const std::string url,
+                                                                                                       struct curl_slist *chunk);
+
+#endif
index 44f5d1e867277b1194dbee16943ca2341f34f004..37be3b65146c404e75e6ff3cc6a3d7f79d3f82fb 100644 (file)
@@ -256,6 +256,11 @@ void set_default_settings(Settings *settings)
        // IPv6
        settings->setDefault("enable_ipv6", "true");
        settings->setDefault("ipv6_server", "false");
+
+       settings->setDefault("modstore_download_url", "http://forum.minetest.net/media/");
+       settings->setDefault("modstore_listmods_url", "http://forum.minetest.net/mmdb/mods/");
+       settings->setDefault("modstore_details_url", "http://forum.minetest.net/mmdb/mod/*/");
+
 }
 
 void override_default_settings(Settings *settings, Settings *from)
index 256c8f16a6dea94a29c9bd3e437fa5686c1bd4fa..21ff199a874c804a03a04c27ef75b2b9cc2b2024 100644 (file)
@@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "filesys.h"
 #include "strfnd.h"
 #include <iostream>
+#include <stdio.h>
 #include <string.h>
+#include <errno.h>
 #include "log.h"
 
 namespace fs
@@ -30,11 +32,9 @@ namespace fs
 
 #define _WIN32_WINNT 0x0501
 #include <windows.h>
-#include <stdio.h>
 #include <malloc.h>
 #include <tchar.h> 
 #include <wchar.h> 
-#include <stdio.h>
 
 #define BUFSIZE MAX_PATH
 
@@ -145,6 +145,11 @@ bool IsDir(std::string path)
                        (attr & FILE_ATTRIBUTE_DIRECTORY));
 }
 
+bool IsDirDelimiter(char c)
+{
+       return c == '/' || c == '\\';
+}
+
 bool RecursiveDelete(std::string path)
 {
        infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;
@@ -207,11 +212,26 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
        }
 }
 
+std::string TempPath()
+{
+       DWORD bufsize = GetTempPath(0, "");
+       if(bufsize == 0){
+               errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
+               return "";
+       }
+       std::vector<char> buf(bufsize);
+       DWORD len = GetTempPath(bufsize, &buf[0]);
+       if(len == 0 || len > bufsize){
+               errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
+               return "";
+       }
+       return std::string(buf.begin(), buf.begin() + len);
+}
+
 #else // POSIX
 
 #include <sys/types.h>
 #include <dirent.h>
-#include <errno.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -301,6 +321,11 @@ bool IsDir(std::string path)
        return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
 }
 
+bool IsDirDelimiter(char c)
+{
+       return c == '/';
+}
+
 bool RecursiveDelete(std::string path)
 {
        /*
@@ -364,6 +389,20 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
        }
 }
 
+std::string TempPath()
+{
+       /*
+               Should the environment variables TMPDIR, TMP and TEMP
+               and the macro P_tmpdir (if defined by stdio.h) be checked
+               before falling back on /tmp?
+
+               Probably not, because this function is intended to be
+               compatible with lua's os.tmpname which under the default
+               configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
+       */
+       return std::string(DIR_DELIM) + "tmp";
+}
+
 #endif
 
 void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst)
@@ -414,16 +453,14 @@ bool RecursiveDeleteContent(std::string path)
 bool CreateAllDirs(std::string path)
 {
 
-       size_t pos;
        std::vector<std::string> tocreate;
        std::string basepath = path;
        while(!PathExists(basepath))
        {
                tocreate.push_back(basepath);
-               pos = basepath.rfind(DIR_DELIM_C);
-               if(pos == std::string::npos)
+               basepath = RemoveLastPathComponent(basepath);
+               if(basepath.empty())
                        break;
-               basepath = basepath.substr(0,pos);
        }
        for(int i=tocreate.size()-1;i>=0;i--)
                if(!CreateDir(tocreate[i]))
@@ -431,5 +468,221 @@ bool CreateAllDirs(std::string path)
        return true;
 }
 
+bool CopyFileContents(std::string source, std::string target)
+{
+       FILE *sourcefile = fopen(source.c_str(), "rb");
+       if(sourcefile == NULL){
+               errorstream<<source<<": can't open for reading: "
+                       <<strerror(errno)<<std::endl;
+               return false;
+       }
+
+       FILE *targetfile = fopen(target.c_str(), "wb");
+       if(targetfile == NULL){
+               errorstream<<target<<": can't open for writing: "
+                       <<strerror(errno)<<std::endl;
+               fclose(sourcefile);
+               return false;
+       }
+
+       size_t total = 0;
+       bool retval = true;
+       bool done = false;
+       char readbuffer[BUFSIZ];
+       while(!done){
+               size_t readbytes = fread(readbuffer, 1,
+                               sizeof(readbuffer), sourcefile);
+               total += readbytes;
+               if(ferror(sourcefile)){
+                       errorstream<<source<<": IO error: "
+                               <<strerror(errno)<<std::endl;
+                       retval = false;
+                       done = true;
+               }
+               if(readbytes > 0){
+                       fwrite(readbuffer, 1, readbytes, targetfile);
+               }
+               if(feof(sourcefile) || ferror(sourcefile)){
+                       // flush destination file to catch write errors
+                       // (e.g. disk full)
+                       fflush(targetfile);
+                       done = true;
+               }
+               if(ferror(targetfile)){
+                       errorstream<<target<<": IO error: "
+                                       <<strerror(errno)<<std::endl;
+                       retval = false;
+                       done = true;
+               }
+       }
+       infostream<<"copied "<<total<<" bytes from "
+               <<source<<" to "<<target<<std::endl;
+       fclose(sourcefile);
+       fclose(targetfile);
+       return retval;
+}
+
+bool CopyDir(std::string source, std::string target)
+{
+       if(PathExists(source)){
+               if(!PathExists(target)){
+                       fs::CreateAllDirs(target);
+               }
+               bool retval = true;
+               std::vector<DirListNode> content = fs::GetDirListing(source);
+
+               for(unsigned int i=0; i < content.size(); i++){
+                       std::string sourcechild = source + DIR_DELIM + content[i].name;
+                       std::string targetchild = target + DIR_DELIM + content[i].name;
+                       if(content[i].dir){
+                               if(!fs::CopyDir(sourcechild, targetchild)){
+                                       retval = false;
+                               }
+                       }
+                       else {
+                               if(!fs::CopyFileContents(sourcechild, targetchild)){
+                                       retval = false;
+                               }
+                       }
+               }
+               return retval;
+       }
+       else {
+               return false;
+       }
+}
+
+bool PathStartsWith(std::string path, std::string prefix)
+{
+       size_t pathsize = path.size();
+       size_t pathpos = 0;
+       size_t prefixsize = prefix.size();
+       size_t prefixpos = 0;
+       for(;;){
+               bool delim1 = pathpos == pathsize
+                       || IsDirDelimiter(path[pathpos]);
+               bool delim2 = prefixpos == prefixsize
+                       || IsDirDelimiter(prefix[prefixpos]);
+
+               if(delim1 != delim2)
+                       return false;
+
+               if(delim1){
+                       while(pathpos < pathsize &&
+                                       IsDirDelimiter(path[pathpos]))
+                               ++pathpos;
+                       while(prefixpos < prefixsize &&
+                                       IsDirDelimiter(prefix[prefixpos]))
+                               ++prefixpos;
+                       if(prefixpos == prefixsize)
+                               return true;
+                       if(pathpos == pathsize)
+                               return false;
+               }
+               else{
+                       size_t len = 0;
+                       do{
+                               char pathchar = path[pathpos+len];
+                               char prefixchar = prefix[prefixpos+len];
+                               if(FILESYS_CASE_INSENSITIVE){
+                                       pathchar = tolower(pathchar);
+                                       prefixchar = tolower(prefixchar);
+                               }
+                               if(pathchar != prefixchar)
+                                       return false;
+                               ++len;
+                       } while(pathpos+len < pathsize
+                                       && !IsDirDelimiter(path[pathpos+len])
+                                       && prefixpos+len < prefixsize
+                                       && !IsDirDelimiter(
+                                               prefix[prefixsize+len]));
+                       pathpos += len;
+                       prefixpos += len;
+               }
+       }
+}
+
+std::string RemoveLastPathComponent(std::string path,
+               std::string *removed, int count)
+{
+       if(removed)
+               *removed = "";
+
+       size_t remaining = path.size();
+
+       for(int i = 0; i < count; ++i){
+               // strip a dir delimiter
+               while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
+                       remaining--;
+               // strip a path component
+               size_t component_end = remaining;
+               while(remaining != 0 && !IsDirDelimiter(path[remaining-1]))
+                       remaining--;
+               size_t component_start = remaining;
+               // strip a dir delimiter
+               while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
+                       remaining--;
+               if(removed){
+                       std::string component = path.substr(component_start,
+                                       component_end - component_start);
+                       if(i)
+                               *removed = component + DIR_DELIM + *removed;
+                       else
+                               *removed = component;
+               }
+       }
+       return path.substr(0, remaining);
+}
+
+std::string RemoveRelativePathComponents(std::string path)
+{
+       size_t pos = path.size();
+       size_t dotdot_count = 0;
+       while(pos != 0){
+               size_t component_with_delim_end = pos;
+               // skip a dir delimiter
+               while(pos != 0 && IsDirDelimiter(path[pos-1]))
+                       pos--;
+               // strip a path component
+               size_t component_end = pos;
+               while(pos != 0 && !IsDirDelimiter(path[pos-1]))
+                       pos--;
+               size_t component_start = pos;
+
+               std::string component = path.substr(component_start,
+                               component_end - component_start);
+               bool remove_this_component = false;
+               if(component == "."){
+                       remove_this_component = true;
+               }
+               else if(component == ".."){
+                       remove_this_component = true;
+                       dotdot_count += 1;
+               }
+               else if(dotdot_count != 0){
+                       remove_this_component = true;
+                       dotdot_count -= 1;
+               }
+
+               if(remove_this_component){
+                       while(pos != 0 && IsDirDelimiter(path[pos-1]))
+                               pos--;
+                       path = path.substr(0, pos) + DIR_DELIM +
+                               path.substr(component_with_delim_end,
+                                               std::string::npos);
+                       pos++;
+               }
+       }
+
+       if(dotdot_count > 0)
+               return "";
+
+       // remove trailing dir delimiters
+       pos = path.size();
+       while(pos != 0 && IsDirDelimiter(path[pos-1]))
+               pos--;
+       return path.substr(0, pos);
+}
+
 } // namespace fs
 
index 263eb796f9bf857731c2b075bfc106c28dcfd217..d0bf400c78921ca44d8891964a8d2e32dfd45c5d 100644 (file)
@@ -26,10 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #ifdef _WIN32 // WINDOWS
 #define DIR_DELIM "\\"
-#define DIR_DELIM_C '\\'
+#define FILESYS_CASE_INSENSITIVE 1
 #else // POSIX
 #define DIR_DELIM "/"
-#define DIR_DELIM_C '/'
+#define FILESYS_CASE_INSENSITIVE 0
 #endif
 
 namespace fs
@@ -49,12 +49,17 @@ bool PathExists(std::string path);
 
 bool IsDir(std::string path);
 
+bool IsDirDelimiter(char c);
+
 // Only pass full paths to this one. True on success.
 // NOTE: The WIN32 version returns always true.
 bool RecursiveDelete(std::string path);
 
 bool DeleteSingleFileOrEmptyDirectory(std::string path);
 
+// Returns path to temp directory, can return "" on error
+std::string TempPath();
+
 /* Multiplatform */
 
 // The path itself not included
@@ -69,6 +74,30 @@ bool RecursiveDeleteContent(std::string path);
 // Create all directories on the given path that don't already exist.
 bool CreateAllDirs(std::string path);
 
+// Copy a regular file
+bool CopyFileContents(std::string source, std::string target);
+
+// Copy directory and all subdirectories
+// Omits files and subdirectories that start with a period
+bool CopyDir(std::string source, std::string target);
+
+// Check if one path is prefix of another
+// For example, "/tmp" is a prefix of "/tmp" and "/tmp/file" but not "/tmp2"
+// Ignores case differences and '/' vs. '\\' on Windows
+bool PathStartsWith(std::string path, std::string prefix);
+
+// Remove last path component and the dir delimiter before and/or after it,
+// returns "" if there is only one path component.
+// removed: If non-NULL, receives the removed component(s).
+// count: Number of components to remove
+std::string RemoveLastPathComponent(std::string path,
+               std::string *removed = NULL, int count = 1);
+
+// Remove "." and ".." path components and for every ".." removed, remove
+// the last normal path component before it. Unlike AbsolutePath,
+// this does not resolve symlinks and check for existence of directories.
+std::string RemoveRelativePathComponents(std::string path);
+
 }//fs
 
 #endif
index bcd155a79f9b0916d20d3d8daf53eca1219abc6b..44ec9ee37f5fbade406a16aabbf843986715f888 100644 (file)
@@ -208,33 +208,6 @@ class PlayerInventoryFormSource: public IFormSource
        Client *m_client;
 };
 
-class FormspecFormSource: public IFormSource
-{
-public:
-       FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
-       {
-               m_formspec = formspec;
-               m_game_formspec = game_formspec;
-       }
-
-       ~FormspecFormSource()
-       {
-               *m_game_formspec = 0;
-       }
-
-       void setForm(std::string formspec) {
-               m_formspec = formspec;
-       }
-
-       std::string getForm()
-       {
-               return m_formspec;
-       }
-
-       std::string m_formspec;
-       FormspecFormSource** m_game_formspec;
-};
-
 /*
        Check if a node is pointable
 */
diff --git a/src/guiConfigureWorld.cpp b/src/guiConfigureWorld.cpp
deleted file mode 100644 (file)
index 8f5ef93..0000000
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-
-#include <iostream> 
-#include <string>
-#include <map>
-
-#include "guiConfigureWorld.h"
-#include "guiMessageMenu.h"
-#include <IGUIButton.h>
-#include <IGUICheckBox.h>
-#include <IGUIListBox.h>
-#include <IGUIStaticText.h>
-#include <IGUITreeView.h>
-#include "gettext.h"
-#include "util/string.h"
-#include "settings.h"
-#include "filesys.h"
-
-enum
-{
-       GUI_ID_MOD_TREEVIEW = 101,
-       GUI_ID_ENABLED_CHECKBOX,
-       GUI_ID_ENABLEALL,
-       GUI_ID_DISABLEALL,
-       GUI_ID_DEPENDS_LISTBOX,
-       GUI_ID_RDEPENDS_LISTBOX,
-       GUI_ID_CANCEL,
-       GUI_ID_SAVE
-};
-
-#define QUESTIONMARK_STR L"?"
-#define CHECKMARK_STR    L"\411"
-#define CROSS_STR        L"\403"
-
-GUIConfigureWorld::GUIConfigureWorld(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id,
-               IMenuManager *menumgr, WorldSpec wspec):
-       GUIModalMenu(env, parent, id, menumgr),
-       m_wspec(wspec),
-       m_gspec(findWorldSubgame(m_wspec.path)),
-       m_menumgr(menumgr)
-{
-       //will be initialized in regenerateGUI()
-       m_treeview=NULL;
-
-       // game mods
-       m_gamemods = flattenModTree(getModsInPath(m_gspec.gamemods_path));
-
-       // world mods
-       std::string worldmods_path = wspec.path + DIR_DELIM + "worldmods";
-       m_worldmods = flattenModTree(getModsInPath(worldmods_path));
-
-       // fill m_addontree with add-on mods
-       std::set<std::string> paths = m_gspec.addon_mods_paths;
-       for(std::set<std::string>::iterator it=paths.begin();
-               it != paths.end(); ++it)
-       {
-               std::map<std::string,ModSpec> mods = getModsInPath(*it);
-               m_addontree.insert(mods.begin(), mods.end());
-       }
-
-       // expand modpacks
-       m_addonmods = flattenModTree(m_addontree);
-
-       // collect reverse dependencies
-       for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
-               it != m_addonmods.end(); ++it)
-       {
-               std::string modname = (*it).first;
-               ModSpec mod = (*it).second;
-               for(std::set<std::string>::iterator dep_it = mod.depends.begin();
-                       dep_it != mod.depends.end(); ++dep_it)
-               {
-                       m_reverse_depends.insert(std::make_pair((*dep_it),modname));
-               }
-       }
-
-       m_settings.readConfigFile((m_wspec.path + DIR_DELIM + "world.mt").c_str());
-       std::vector<std::string> names = m_settings.getNames();
-
-       // mod_names contains the names of mods mentioned in the world.mt file
-       std::set<std::string> mod_names;
-       for(std::vector<std::string>::iterator it = names.begin(); 
-               it != names.end(); ++it)
-       {       
-               std::string name = *it;  
-               if (name.compare(0,9,"load_mod_")==0)
-                       mod_names.insert(name.substr(9));
-       }
-
-       // find new mods (installed but not mentioned in world.mt)
-       for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
-               it != m_addonmods.end(); ++it)
-       {
-               std::string modname = (*it).first;
-               ModSpec mod = (*it).second;
-               // a mod is new if it is not a modpack, and does not occur in
-               // mod_names
-               if(!mod.is_modpack &&
-                  mod_names.count(modname) == 0)
-                       m_settings.setBool("load_mod_"+modname, false);
-       }
-       // find missing mods (mentioned in world.mt, but not installed)
-       for(std::set<std::string>::iterator it = mod_names.begin();
-               it != mod_names.end(); ++it)
-       {
-               std::string modname = *it;
-               if(m_addonmods.count(modname) == 0)
-                       m_settings.remove("load_mod_"+modname);
-       }
-       std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
-       m_settings.updateConfigFile(worldmtfile.c_str());
-}
-
-void GUIConfigureWorld::drawMenu()
-{
-       gui::IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-       
-       video::SColor bgcolor(140,0,0,0);
-       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
-       gui::IGUIElement::draw();
-}
-
-
-void GUIConfigureWorld::regenerateGui(v2u32 screensize)
-{
-
-       /*
-               Remove stuff
-       */
-       removeChildren();
-       
-       /*
-               Calculate new sizes and positions
-       */
-       core::rect<s32> rect(
-                       screensize.X/2 - 580/2,
-                       screensize.Y/2 - 300/2,
-                       screensize.X/2 + 580/2,
-                       screensize.Y/2 + 300/2
-       );
-       
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-
-       v2s32 topleft = v2s32(10, 10);
-
-       /*
-               Add stuff
-       */
-       changeCtype("");
-       {
-               core::rect<s32> rect(0, 0, 200, 20);
-               rect += topleft;
-               //proper text is set below, when a mod is selected
-               m_modname_text = Environment->addStaticText(L"Mod: N/A", rect, false, 
-                                                                                                       false, this, -1);
-       }
-       {
-               core::rect<s32> rect(0, 0, 200, 20);
-               rect += v2s32(0, 25) + topleft;
-               wchar_t* text = wgettext("enabled");
-               m_enabled_checkbox = 
-                       Environment->addCheckBox(false, rect, this, GUI_ID_ENABLED_CHECKBOX, 
-                                                                        text);
-               delete[] text;
-               m_enabled_checkbox->setVisible(false);
-       }
-       {
-               core::rect<s32> rect(0, 0, 85, 30);
-               rect = rect + v2s32(0, 25) + topleft;
-               wchar_t* text = wgettext("Enable All");
-               m_enableall = Environment->addButton(rect, this, GUI_ID_ENABLEALL,
-                                                                                        text);
-               delete[] text;
-               m_enableall->setVisible(false);
-       }
-       {
-               core::rect<s32> rect(0, 0, 85, 30);
-               rect = rect + v2s32(115, 25) + topleft;
-               wchar_t* text = wgettext("Disable All");
-               m_disableall = Environment->addButton(rect, this, GUI_ID_DISABLEALL, text );
-               delete[] text;
-               m_disableall->setVisible(false);
-       }
-       {
-               core::rect<s32> rect(0, 0, 200, 20);
-               rect += v2s32(0, 60) + topleft;
-               wchar_t* text = wgettext("depends on:");
-               Environment->addStaticText(text, rect, false, false, this, -1);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 200, 85);
-               rect += v2s32(0, 80) + topleft;
-               m_dependencies_listbox = 
-                       Environment->addListBox(rect, this, GUI_ID_DEPENDS_LISTBOX, true);
-       }
-       {
-               core::rect<s32> rect(0, 0, 200, 20);
-               rect += v2s32(0, 175) + topleft;
-               wchar_t* text = wgettext("is required by:");
-               Environment->addStaticText( text, rect, false, false, this, -1);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 200, 85);
-               rect += v2s32(0, 195) + topleft;
-               m_rdependencies_listbox = 
-                       Environment->addListBox(rect,this, GUI_ID_RDEPENDS_LISTBOX,true);
-       }
-       {
-               core::rect<s32> rect(0, 0, 340, 250);
-               rect += v2s32(220, 0) + topleft;
-               m_treeview = Environment->addTreeView(rect, this,
-                                                                                         GUI_ID_MOD_TREEVIEW,true);
-               gui::IGUITreeViewNode* node 
-                       = m_treeview->getRoot()->addChildBack(L"Add-Ons");
-               buildTreeView(m_addontree, node);
-       }
-       {
-               core::rect<s32> rect(0, 0, 120, 30);
-               rect = rect + v2s32(330, 270) - topleft;
-               wchar_t* text = wgettext("Cancel");
-               Environment->addButton(rect, this, GUI_ID_CANCEL, text);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 120, 30);
-               rect = rect + v2s32(460, 270) - topleft;
-               wchar_t* text = wgettext("Save");
-               Environment->addButton(rect, this, GUI_ID_SAVE, text);
-               delete[] text;
-       }
-       changeCtype("C");
-
-       // at start, none of the treeview nodes is selected, so we select
-       // the first element in the treeview of mods manually here.
-       if(m_treeview->getRoot()->hasChilds())
-       {
-               m_treeview->getRoot()->getFirstChild()->setExpanded(true);
-               m_treeview->getRoot()->getFirstChild()->setSelected(true);
-               // Because a manual ->setSelected() doesn't cause an event, we
-               // have to do this here:
-               adjustSidebar();
-       }
-}
-
-bool GUIConfigureWorld::OnEvent(const SEvent& event)
-{
-
-       gui::IGUITreeViewNode* selected_node = NULL;
-       if(m_treeview != NULL)
-               selected_node = m_treeview->getSelected();
-
-       if(event.EventType==EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
-       {
-               switch (event.KeyInput.Key) {
-               case KEY_ESCAPE: {
-                       quitMenu();
-                       return true;
-               }
-               //      irrlicht's built-in TreeView gui has no keyboard control,
-               //      so we do it here: up/down to select prev/next node,
-               //      left/right to collapse/expand nodes, space to toggle
-               //      enabled/disabled.
-               case KEY_DOWN: {
-                       if(selected_node != NULL)
-                       {
-                               gui::IGUITreeViewNode* node = selected_node->getNextVisible();
-                               if(node != NULL)
-                               {
-                                       node->setSelected(true);
-                                       adjustSidebar();
-                               }
-                       }
-                       return true;
-               }
-               case KEY_UP: {
-                       if(selected_node != NULL)
-                       {
-                               gui::IGUITreeViewNode* node = selected_node->getPrevSibling();
-                               if(node!=NULL)
-                               {
-                                       node->setSelected(true);
-                                       adjustSidebar();
-                               }
-                               else 
-                               {
-                                       gui::IGUITreeViewNode* parent = selected_node->getParent();
-                                       if(selected_node == parent->getFirstChild() &&
-                                          parent != m_treeview->getRoot()) 
-                                       {
-                                               parent->setSelected(true);
-                                               adjustSidebar();
-                                       }
-                               }
-                       }
-                       return true;
-               }
-               case KEY_RIGHT: {
-                       if(selected_node != NULL && selected_node->hasChilds())
-                               selected_node->setExpanded(true);
-                       return true;
-               }
-               case KEY_LEFT: {
-                       if(selected_node != NULL && selected_node->hasChilds())
-                               selected_node->setExpanded(false);
-                       return true;
-               }
-               case KEY_SPACE: {
-                       if(selected_node != NULL && !selected_node->hasChilds() && 
-                          selected_node->getText() != NULL)
-                       {
-                               std::string modname = wide_to_narrow(selected_node->getText());
-                               bool checked = m_enabled_checkbox->isChecked();
-                               m_enabled_checkbox->setChecked(!checked);
-                               setEnabled(modname,!checked);
-                       }
-                       return true;
-               }
-               default: {}
-               }
-       }
-       if(event.EventType==EET_GUI_EVENT)
-       {
-               if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
-                               && isVisible())
-               {
-                       if(!canTakeFocus(event.GUIEvent.Element))
-                       {
-                               dstream<<"GUIConfigureWorld: Not allowing focus change."
-                                               <<std::endl;
-                               // Returning true disables focus change
-                               return true;
-                       }
-               }
-               if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
-                       switch(event.GUIEvent.Caller->getID()){
-                       case GUI_ID_CANCEL: {
-                               quitMenu();
-                               return true;
-                       }
-                       case GUI_ID_SAVE: {
-                               std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
-                               m_settings.updateConfigFile(worldmtfile.c_str());
-
-                               // The trailing spaces are because there seems to be a
-                               // bug in the text-size calculation. if the trailing
-                               // spaces are removed from the message text, the
-                               // message gets wrapped and parts of it are cut off:
-                               wchar_t* text = wgettext("Configuration saved.  ");
-                               GUIMessageMenu *menu = 
-                                       new GUIMessageMenu(Environment, Parent, -1, m_menumgr, 
-                                                                        text );
-                               delete[] text;
-                               menu->drop();
-
-                               try
-                               {
-                                       ModConfiguration modconf(m_wspec.path);
-                                       if(!modconf.isConsistent())
-                                       {
-                                               wchar_t* text = wgettext("Warning: Configuration not consistent.  ");
-                                               GUIMessageMenu *menu =
-                                                       new GUIMessageMenu(Environment, Parent, -1, m_menumgr, 
-                                                                                text );
-                                               delete[] text;
-                                               menu->drop();
-                                       }
-                               }
-                               catch(ModError &err)
-                               {
-                                       errorstream<<err.what()<<std::endl;
-                                       std::wstring text = narrow_to_wide(err.what()) + wgettext("\nCheck debug.txt for details.");
-                                       GUIMessageMenu *menu =
-                                               new GUIMessageMenu(Environment, Parent, -1, m_menumgr, 
-                                                                        text );
-                                       menu->drop();
-                               }
-
-                               quitMenu();     
-                               return true;
-                       }
-                       case GUI_ID_ENABLEALL: {
-                               if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
-                               {  
-                                       enableAllMods(m_addonmods,true);
-                               } 
-                               else if(selected_node != NULL && selected_node->getText() != NULL)
-                               {  
-                                       std::string modname = wide_to_narrow(selected_node->getText());
-                                       std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
-                                       if(mod_it != m_addonmods.end())
-                                               enableAllMods(mod_it->second.modpack_content,true);
-                               }
-                               return true;
-                       }
-                       case GUI_ID_DISABLEALL: {
-                               if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
-                               {
-                                       enableAllMods(m_addonmods,false);
-                               } 
-                               if(selected_node != NULL && selected_node->getText() != NULL)
-                               {
-                                       std::string modname = wide_to_narrow(selected_node->getText());
-                                       std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
-                                       if(mod_it != m_addonmods.end())
-                                               enableAllMods(mod_it->second.modpack_content,false);
-                               }
-                               return true;
-                       }
-                       }
-               }       
-               if(event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED &&
-                       event.GUIEvent.Caller->getID() == GUI_ID_ENABLED_CHECKBOX)
-               {
-                       if(selected_node != NULL && !selected_node->hasChilds() && 
-                          selected_node->getText() != NULL)
-                       {
-                               std::string modname = wide_to_narrow(selected_node->getText());
-                               setEnabled(modname, m_enabled_checkbox->isChecked());
-                       }
-                       return true;
-               }
-               if(event.GUIEvent.EventType==gui::EGET_TREEVIEW_NODE_SELECT &&
-                  event.GUIEvent.Caller->getID() == GUI_ID_MOD_TREEVIEW)
-               {
-                       selecting_dep = -1;
-                       selecting_rdep = -1;
-                       adjustSidebar();
-                       return true;
-               }
-               if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
-                  event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
-               {
-                       selecting_dep = m_dependencies_listbox->getSelected();
-                       selecting_rdep = -1;
-                       return true;
-               }
-               if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
-                  event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
-               {
-                       selecting_dep = -1;
-                       selecting_rdep = m_rdependencies_listbox->getSelected();
-                       return true;
-               }
-
-               //double click in a dependency listbox: find corresponding
-               //treeviewnode and select it:
-               if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
-               {
-                       gui::IGUIListBox* box = NULL;
-                       if(event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
-                       {
-                               box = m_dependencies_listbox;
-                               if(box->getSelected() != selecting_dep)
-                                       return true;
-                       }
-                       if(event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
-                       {
-                               box = m_rdependencies_listbox;
-                               if(box->getSelected() != selecting_rdep)
-                                       return true;
-                       }
-                       if(box != NULL && box->getSelected() != -1 &&
-                          box->getListItem(box->getSelected()) != NULL)
-                       {
-                               std::string modname = 
-                                       wide_to_narrow(box->getListItem(box->getSelected()));
-                               std::map<std::string, gui::IGUITreeViewNode*>::iterator it =
-                                       m_nodes.find(modname);
-                               if(it != m_nodes.end())
-                               {
-                                       // select node and make sure node is visible by
-                                       // expanding all parents
-                                       gui::IGUITreeViewNode* node = (*it).second;
-                                       node->setSelected(true);
-                                       while(!node->isVisible() && 
-                                                 node->getParent() != m_treeview->getRoot())
-                                       {
-                                               node = node->getParent();
-                                               node->setExpanded(true);
-                                       }
-                                       adjustSidebar();
-                               }
-                       }
-                       return true;
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
-void GUIConfigureWorld::buildTreeView(std::map<std::string, ModSpec> mods, 
-                                                                         gui::IGUITreeViewNode* node)
-{
-       for(std::map<std::string,ModSpec>::iterator it = mods.begin();
-               it != mods.end(); ++it)    
-       {
-               std::string modname = (*it).first;
-               ModSpec mod = (*it).second;
-               gui::IGUITreeViewNode* new_node = 
-                       node->addChildBack(narrow_to_wide(modname).c_str());
-               m_nodes.insert(std::make_pair(modname, new_node));
-               if(mod.is_modpack)
-                       buildTreeView(mod.modpack_content, new_node);
-               else
-               {
-                       // set icon for node: x for disabled mods, checkmark for enabled mods
-                       bool mod_enabled = false;
-                       if(m_settings.exists("load_mod_"+modname))
-                               mod_enabled = m_settings.getBool("load_mod_"+modname);
-                       if(mod_enabled)
-                               new_node->setIcon(CHECKMARK_STR);
-                       else
-                               new_node->setIcon(CROSS_STR);
-               }
-       }
-}
-
-
-void GUIConfigureWorld::adjustSidebar()
-{
-       gui::IGUITreeViewNode* node = m_treeview->getSelected();
-       std::wstring modname_w;
-       if(node->getText() != NULL)
-               modname_w = node->getText();
-       else 
-               modname_w = L"N/A";
-       std::string modname = wide_to_narrow(modname_w);
-
-       ModSpec mspec;
-       std::map<std::string, ModSpec>::iterator it = m_addonmods.find(modname);
-       if(it != m_addonmods.end())
-               mspec = it->second;
-
-       m_dependencies_listbox->clear();
-       m_rdependencies_listbox->clear();
-
-       // if no mods installed, there is nothing to enable/disable, so we
-       // don't show buttons or checkbox on the sidebar
-       if(node->getParent() == m_treeview->getRoot() && !node->hasChilds())
-       {
-               m_disableall->setVisible(false);
-               m_enableall->setVisible(false);
-               m_enabled_checkbox->setVisible(false);
-               return;
-       } 
-       
-    // a modpack is not enabled/disabled by itself, only its cotnents
-    // are. so we show show enable/disable all buttons, but hide the
-    // checkbox
-       if(node->getParent() == m_treeview->getRoot() ||
-          mspec.is_modpack)
-       {
-               m_enabled_checkbox->setVisible(false);
-               m_disableall->setVisible(true);
-               m_enableall->setVisible(true);
-               m_modname_text->setText((L"Modpack: "+modname_w).c_str());
-               return;
-       }       
-
-       // for a normal mod, we hide the enable/disable all buttons, but show the checkbox.
-       m_disableall->setVisible(false);
-       m_enableall->setVisible(false);
-       m_enabled_checkbox->setVisible(true);
-       m_modname_text->setText((L"Mod: "+modname_w).c_str());
-
-       // the mod is enabled unless it is disabled in the world.mt settings. 
-       bool mod_enabled = true;
-       if(m_settings.exists("load_mod_"+modname))
-               mod_enabled = m_settings.getBool("load_mod_"+modname);
-       m_enabled_checkbox->setChecked(mod_enabled);
-
-       for(std::set<std::string>::iterator it=mspec.depends.begin();
-               it != mspec.depends.end(); ++it)
-       {
-               // check if it is an add-on mod or a game/world mod. We only
-               // want to show add-ons
-               std::string dependency = (*it);
-               if(m_gamemods.count(dependency) > 0)
-                       dependency += " (" + m_gspec.id + ")";
-               else if(m_worldmods.count(dependency) > 0)
-                       dependency += " (" + m_wspec.name + ")";
-               else if(m_addonmods.count(dependency) == 0)
-                       dependency += " (missing)";
-               m_dependencies_listbox->addItem(narrow_to_wide(dependency).c_str());
-       }
-
-       // reverse dependencies of this mod:
-       std::pair< std::multimap<std::string, std::string>::iterator, 
-                       std::multimap<std::string, std::string>::iterator > rdep = 
-               m_reverse_depends.equal_range(modname);
-       for(std::multimap<std::string,std::string>::iterator it = rdep.first;
-               it != rdep.second; ++it)
-       {
-               // check if it is an add-on mod or a game/world mod. We only
-               // want to show add-ons
-               std::string rdependency = (*it).second;
-               if(m_addonmods.count(rdependency) > 0)
-                       m_rdependencies_listbox->addItem(narrow_to_wide(rdependency).c_str());
-       }
-}
-
-void GUIConfigureWorld::enableAllMods(std::map<std::string, ModSpec> mods,bool enable)
-{
-       for(std::map<std::string, ModSpec>::iterator it = mods.begin();
-               it != mods.end(); ++it)
-       {
-               ModSpec mod = (*it).second;
-               if(mod.is_modpack) 
-                       // a modpack, recursively enable all mods in it
-                       enableAllMods(mod.modpack_content,enable);
-               else // not a modpack
-                       setEnabled(mod.name, enable);
-
-       }
-}
-
-void GUIConfigureWorld::enableMod(std::string modname)
-{
-       std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
-       if(mod_it == m_addonmods.end()){
-               errorstream << "enableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
-               return;
-       }
-       ModSpec mspec = mod_it->second;
-       m_settings.setBool("load_mod_"+modname,true);
-       std::map<std::string,gui::IGUITreeViewNode*>::iterator it = 
-               m_nodes.find(modname);
-       if(it != m_nodes.end())
-               (*it).second->setIcon(CHECKMARK_STR);
-       //also enable all dependencies
-       for(std::set<std::string>::iterator it=mspec.depends.begin();
-               it != mspec.depends.end(); ++it)
-       {
-               std::string dependency = *it;
-               // only enable it if it is an add-on mod
-               if(m_addonmods.count(dependency) > 0)
-                       enableMod(dependency);
-       }
-}
-
-void GUIConfigureWorld::disableMod(std::string modname)
-{
-       std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
-       if(mod_it == m_addonmods.end()){
-               errorstream << "disableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
-               return;
-       }
-
-       m_settings.setBool("load_mod_"+modname,false);
-       std::map<std::string,gui::IGUITreeViewNode*>::iterator it = 
-               m_nodes.find(modname);
-       if(it != m_nodes.end())
-               (*it).second->setIcon(CROSS_STR);
-       //also disable all mods that depend on this one
-       std::pair<std::multimap<std::string, std::string>::iterator, 
-                         std::multimap<std::string, std::string>::iterator > rdep = 
-               m_reverse_depends.equal_range(modname);
-       for(std::multimap<std::string,std::string>::iterator it = rdep.first;
-               it != rdep.second; ++it)
-       {
-               std::string rdependency = (*it).second;
-               // only disable it if it is an add-on mod
-               if(m_addonmods.count(rdependency) > 0)
-                       disableMod(rdependency);
-       }       
-}
-
diff --git a/src/guiConfigureWorld.h b/src/guiConfigureWorld.h
deleted file mode 100644 (file)
index 23ebac6..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef GUICONFIGUREWORLD_HEADER
-#define GUICONFIGUREWORLD_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include "mods.h"
-#include "subgame.h"
-#include "settings.h"
-
-
-namespace irr{
-       namespace gui{
-               class IGUITreeViewNode;
-       }
-}
-
-class GUIConfigureWorld : public GUIModalMenu
-{
-public:
-       GUIConfigureWorld(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id,
-                         IMenuManager *menumgr, WorldSpec wspec);
-
-       void regenerateGui(v2u32 screensize);
-
-       void drawMenu();
-
-       bool OnEvent(const SEvent& event);
-
-private:
-       WorldSpec m_wspec;
-       SubgameSpec m_gspec;
-
-       // tree of installed add-on mods. key is the mod name, modpacks
-       // are not expanded.
-       std::map<std::string, ModSpec> m_addontree;
-
-       // like m_addontree, but modpacks are expanded.
-       std::map<std::string, ModSpec> m_addonmods;
-
-       // list of game mods (flattened)
-       std::map<std::string, ModSpec> m_gamemods;
-
-       // list of world mods (flattened)
-       std::map<std::string, ModSpec> m_worldmods;
-       
-       // for each mod, the set of mods depending on it
-       std::multimap<std::string, std::string> m_reverse_depends;
-
-       // the settings in the world.mt file
-       Settings m_settings;
-
-       // maps modnames to nodes in m_treeview
-       std::map<std::string,gui::IGUITreeViewNode*> m_nodes;
-
-       gui::IGUIStaticText* m_modname_text;
-       gui::IGUITreeView* m_treeview;
-       gui::IGUIButton* m_enableall;
-       gui::IGUIButton* m_disableall;
-       gui::IGUICheckBox* m_enabled_checkbox;
-       gui::IGUIListBox* m_dependencies_listbox;
-       gui::IGUIListBox* m_rdependencies_listbox;
-       void buildTreeView(std::map<std::string,ModSpec> mods, 
-                                          gui::IGUITreeViewNode* node);
-       void adjustSidebar();
-       void enableAllMods(std::map<std::string,ModSpec> mods, bool enable);
-       void setEnabled(std::string modname, bool enable)
-       {
-               if(enable)
-                       enableMod(modname);
-               else
-                       disableMod(modname);
-       };
-
-       void enableMod(std::string modname);
-       void disableMod(std::string modname);
-
-       // hack to work around wonky handling of double-click in
-       // irrlicht. store selected index of listbox items here so event
-       // handling can check whether it was a real double click on the
-       // same item. (irrlicht also reports a double click if you rapidly
-       // select two different items.)
-       int selecting_dep;
-       int selecting_rdep;
-
-       IMenuManager* m_menumgr;
-};
-#endif
diff --git a/src/guiConfirmMenu.cpp b/src/guiConfirmMenu.cpp
deleted file mode 100644 (file)
index 86b2305..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "guiConfirmMenu.h"
-#include "debug.h"
-#include "serialization.h"
-#include <string>
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-
-#include "gettext.h"
-
-enum
-{
-       GUI_ID_YES = 101,
-       GUI_ID_NO,
-};
-
-GUIConfirmMenu::GUIConfirmMenu(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id,
-               IMenuManager *menumgr,
-               ConfirmDest *dest,
-               std::wstring message_text
-):
-       GUIModalMenu(env, parent, id, menumgr),
-       m_dest(dest),
-       m_message_text(message_text)
-{
-}
-
-GUIConfirmMenu::~GUIConfirmMenu()
-{
-       removeChildren();
-       if(m_dest)
-               delete m_dest;
-}
-
-void GUIConfirmMenu::removeChildren()
-{
-       const core::list<gui::IGUIElement*> &children = getChildren();
-       core::list<gui::IGUIElement*> children_copy;
-       for(core::list<gui::IGUIElement*>::ConstIterator
-                       i = children.begin(); i != children.end(); i++)
-       {
-               children_copy.push_back(*i);
-       }
-       for(core::list<gui::IGUIElement*>::Iterator
-                       i = children_copy.begin();
-                       i != children_copy.end(); i++)
-       {
-               (*i)->remove();
-       }
-}
-
-void GUIConfirmMenu::regenerateGui(v2u32 screensize)
-{
-       /*
-               Remove stuff
-       */
-       removeChildren();
-       
-       /*
-               Calculate new sizes and positions
-       */
-       core::rect<s32> rect(
-                       screensize.X/2 - 580/2,
-                       screensize.Y/2 - 300/2,
-                       screensize.X/2 + 580/2,
-                       screensize.Y/2 + 300/2
-       );
-       
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-
-       v2s32 size = rect.getSize();
-
-       gui::IGUISkin *skin = Environment->getSkin();
-       gui::IGUIFont *font = skin->getFont();
-       s32 msg_h = font->getDimension(m_message_text.c_str()).Height;
-       s32 msg_w = font->getDimension(m_message_text.c_str()).Width;
-       if(msg_h > 200)
-               msg_h = 200;
-       if(msg_w > 540)
-               msg_w = 540;
-
-       /*
-               Add stuff
-       */
-       {
-               core::rect<s32> rect(0, 0, msg_w, msg_h);
-               rect += v2s32(size.X/2-msg_w/2, size.Y/2-30/2 - msg_h/2);
-               Environment->addStaticText(m_message_text.c_str(),
-                       rect, false, true, this, -1);
-       }
-       changeCtype("");
-       int bw = 100;
-       {
-               core::rect<s32> rect(0, 0, bw, 30);
-               rect = rect + v2s32(size.X/2-bw/2-(bw/2+5), size.Y/2-30/2+5 + msg_h/2);
-               wchar_t* text = wgettext("Yes");
-               Environment->addButton(rect, this, GUI_ID_YES,
-                               text);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, bw, 30);
-               rect = rect + v2s32(size.X/2-bw/2+(bw/2+5), size.Y/2-30/2+5 + msg_h/2);
-               wchar_t* text = wgettext("No");
-               Environment->addButton(rect, this, GUI_ID_NO,
-                       text);
-               delete[] text;
-       }
-       changeCtype("C");
-}
-
-void GUIConfirmMenu::drawMenu()
-{
-       gui::IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-       
-       video::SColor bgcolor(140,0,0,0);
-       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
-       gui::IGUIElement::draw();
-}
-
-void GUIConfirmMenu::acceptInput(bool answer)
-{
-       if(m_dest)
-               m_dest->answer(answer);
-}
-
-bool GUIConfirmMenu::OnEvent(const SEvent& event)
-{
-       if(event.EventType==EET_KEY_INPUT_EVENT)
-       {
-               if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
-               {
-                       acceptInput(false);
-                       quitMenu();
-                       return true;
-               }
-               if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
-               {
-                       acceptInput(true);
-                       quitMenu();
-                       return true;
-               }
-       }
-       if(event.EventType==EET_GUI_EVENT)
-       {
-               if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
-                               && isVisible())
-               {
-                       if(!canTakeFocus(event.GUIEvent.Element))
-                       {
-                               dstream<<"GUIConfirmMenu: Not allowing focus change."
-                                               <<std::endl;
-                               // Returning true disables focus change
-                               return true;
-                       }
-               }
-               if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
-               {
-                       switch(event.GUIEvent.Caller->getID())
-                       {
-                       case GUI_ID_YES:
-                               acceptInput(true);
-                               quitMenu();
-                               // quitMenu deallocates menu
-                               return true;
-                       case GUI_ID_NO:
-                               acceptInput(false);
-                               quitMenu();
-                               // quitMenu deallocates menu
-                               return true;
-                       }
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
diff --git a/src/guiConfirmMenu.h b/src/guiConfirmMenu.h
deleted file mode 100644 (file)
index 7d217d6..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef GUICONFIRMMENU_HEADER
-#define GUICONFIRMMENU_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-
-struct ConfirmDest
-{
-       virtual void answer(bool answer) = 0;
-       virtual ~ConfirmDest() {};
-};
-
-class GUIConfirmMenu : public GUIModalMenu
-{
-public:
-       GUIConfirmMenu(gui::IGUIEnvironment* env,
-                       gui::IGUIElement* parent, s32 id,
-                       IMenuManager *menumgr,
-                       ConfirmDest *dest,
-                       std::wstring message_text);
-       ~GUIConfirmMenu();
-       
-       void removeChildren();
-       // Remove and re-add (or reposition) stuff
-       void regenerateGui(v2u32 screensize);
-       void drawMenu();
-       void acceptInput(bool answer);
-       bool OnEvent(const SEvent& event);
-       
-private:
-       ConfirmDest *m_dest;
-       std::wstring m_message_text;
-};
-
-#endif
-
diff --git a/src/guiCreateWorld.cpp b/src/guiCreateWorld.cpp
deleted file mode 100644 (file)
index f9afe95..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "guiCreateWorld.h"
-#include "debug.h"
-#include "serialization.h"
-#include <string>
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IGUIListBox.h>
-#include "gettext.h"
-#include "util/string.h"
-
-enum
-{
-       GUI_ID_NAME_INPUT = 101,
-       GUI_ID_GAME_LISTBOX,
-       GUI_ID_CREATE,
-       GUI_ID_CANCEL
-};
-
-GUICreateWorld::GUICreateWorld(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id,
-               IMenuManager *menumgr,
-               CreateWorldDest *dest,
-               const std::vector<SubgameSpec> &games,
-               const std::string &initial_game
-):
-       GUIModalMenu(env, parent, id, menumgr),
-       m_dest(dest),
-       m_games(games),
-       m_initial_game_i(0)
-{
-       assert(games.size() > 0);
-
-       for(size_t i=0; i<games.size(); i++){
-               if(games[i].id == initial_game){
-                       m_initial_game_i = i;
-                       break;
-               }
-       }
-}
-
-GUICreateWorld::~GUICreateWorld()
-{
-       removeChildren();
-       if(m_dest)
-               delete m_dest;
-}
-
-void GUICreateWorld::removeChildren()
-{
-       const core::list<gui::IGUIElement*> &children = getChildren();
-       core::list<gui::IGUIElement*> children_copy;
-       for(core::list<gui::IGUIElement*>::ConstIterator
-                       i = children.begin(); i != children.end(); i++)
-       {
-               children_copy.push_back(*i);
-       }
-       for(core::list<gui::IGUIElement*>::Iterator
-                       i = children_copy.begin();
-                       i != children_copy.end(); i++)
-       {
-               (*i)->remove();
-       }
-}
-
-void GUICreateWorld::regenerateGui(v2u32 screensize)
-{
-       std::wstring name = L"";
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
-               if(e != NULL)
-                       name = e->getText();
-       }
-
-       /*
-               Remove stuff
-       */
-       removeChildren();
-       
-       /*
-               Calculate new sizes and positions
-       */
-       core::rect<s32> rect(
-                       screensize.X/2 - 580/2,
-                       screensize.Y/2 - 300/2,
-                       screensize.X/2 + 580/2,
-                       screensize.Y/2 + 300/2
-       );
-       
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-
-       v2s32 topleft = v2s32(10+80, 10+70);
-
-       /*
-               Add stuff
-       */
-       {
-               core::rect<s32> rect(0, 0, 100, 20);
-               rect += v2s32(0, 5) + topleft;
-               wchar_t* text = wgettext("World name");
-               Environment->addStaticText(text, rect, false, true, this, -1);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 300, 30);
-               rect = rect + v2s32(100, 0) + topleft;
-               gui::IGUIElement *e = 
-               Environment->addEditBox(name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
-               Environment->setFocus(e);
-
-               irr::SEvent evt;
-               evt.EventType = EET_KEY_INPUT_EVENT;
-               evt.KeyInput.Key = KEY_END;
-               evt.KeyInput.PressedDown = true;
-               evt.KeyInput.Char = 0;
-               evt.KeyInput.Control = 0;
-               evt.KeyInput.Shift = 0;
-               e->OnEvent(evt);
-       }
-       {
-               core::rect<s32> rect(0, 0, 100, 20);
-               rect += v2s32(0, 40+5) + topleft;
-               wchar_t* text = wgettext("Game");
-               Environment->addStaticText(text, rect, false, true, this, -1);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 300, 80);
-               rect += v2s32(100, 40) + topleft;
-               gui::IGUIListBox *e = Environment->addListBox(rect, this,
-                               GUI_ID_GAME_LISTBOX);
-               e->setDrawBackground(true);
-               for(u32 i=0; i<m_games.size(); i++){
-                       std::wostringstream os(std::ios::binary);
-                       os<<narrow_to_wide(m_games[i].name).c_str();
-                       os<<L" [";
-                       os<<narrow_to_wide(m_games[i].id).c_str();
-                       os<<L"]";
-                       e->addItem(os.str().c_str());
-               }
-               e->setSelected(m_initial_game_i);
-       }
-       changeCtype("");
-       {
-               core::rect<s32> rect(0, 0, 120, 30);
-               rect = rect + v2s32(170, 140) + topleft;
-               wchar_t* text = wgettext("Create");
-               Environment->addButton(rect, this, GUI_ID_CREATE,
-                       text);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 120, 30);
-               rect = rect + v2s32(300, 140) + topleft;
-               wchar_t* text = wgettext("Cancel");
-               Environment->addButton(rect, this, GUI_ID_CANCEL,
-                       text);
-               delete [] text;
-       }
-       changeCtype("C");
-}
-
-void GUICreateWorld::drawMenu()
-{
-       gui::IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-       
-       video::SColor bgcolor(140,0,0,0);
-       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
-       gui::IGUIElement::draw();
-}
-
-void GUICreateWorld::acceptInput()
-{
-       if(m_dest)
-       {
-               int selected = -1;
-               {
-                       gui::IGUIElement *e = getElementFromId(GUI_ID_GAME_LISTBOX);
-                       if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
-                               selected = ((gui::IGUIListBox*)e)->getSelected();
-               }
-               std::wstring name;
-               {
-                       gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
-                       if(e != NULL)
-                               name = e->getText();
-               }
-               if(selected != -1 && name != L"")
-                       m_dest->accepted(name, m_games[selected].id);
-               delete m_dest;
-               m_dest = NULL;
-       }
-}
-
-bool GUICreateWorld::OnEvent(const SEvent& event)
-{
-       if(event.EventType==EET_KEY_INPUT_EVENT)
-       {
-               if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
-               {
-                       quitMenu();
-                       return true;
-               }
-               if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
-               {
-                       acceptInput();
-                       quitMenu();
-                       return true;
-               }
-       }
-       if(event.EventType==EET_GUI_EVENT)
-       {
-               if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
-                               && isVisible())
-               {
-                       if(!canTakeFocus(event.GUIEvent.Element))
-                       {
-                               dstream<<"GUICreateWorld: Not allowing focus change."
-                                               <<std::endl;
-                               // Returning true disables focus change
-                               return true;
-                       }
-               }
-               bool accept_input = false;
-               if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
-                       switch(event.GUIEvent.Caller->getID()){
-                       case GUI_ID_CANCEL:
-                               quitMenu();
-                               return true;
-                               break;
-                       case GUI_ID_CREATE:
-                               accept_input = true;
-                               break;
-                       }
-               }
-               if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER){
-                       switch(event.GUIEvent.Caller->getID()){
-                       case GUI_ID_NAME_INPUT:
-                               accept_input = true;
-                               break;
-                       }
-               }
-               if(accept_input){
-                       acceptInput();
-                       quitMenu();
-                       // quitMenu deallocates menu
-                       return true;
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
diff --git a/src/guiCreateWorld.h b/src/guiCreateWorld.h
deleted file mode 100644 (file)
index 2765dc2..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef GUICREATEWORLD_HEADER
-#define GUICREATEWORLD_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-#include "subgame.h"
-
-struct CreateWorldDest
-{
-       virtual void accepted(std::wstring name, std::string gameid) = 0;
-       virtual ~CreateWorldDest() {};
-};
-
-class GUICreateWorld : public GUIModalMenu
-{
-public:
-       GUICreateWorld(gui::IGUIEnvironment* env,
-                       gui::IGUIElement* parent, s32 id,
-                       IMenuManager *menumgr,
-                       CreateWorldDest *dest,
-                       const std::vector<SubgameSpec> &games,
-                       const std::string &initial_game);
-       ~GUICreateWorld();
-       
-       void removeChildren();
-       /*
-               Remove and re-add (or reposition) stuff
-       */
-       void regenerateGui(v2u32 screensize);
-
-       void drawMenu();
-
-       void acceptInput();
-
-       bool OnEvent(const SEvent& event);
-       
-private:
-       CreateWorldDest *m_dest;
-       std::vector<SubgameSpec> m_games;
-       int m_initial_game_i;
-};
-
-#endif
-
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
new file mode 100644 (file)
index 0000000..f04f158
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+Minetest
+Copyright (C) 2013 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+extern "C" {
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include "irrlicht.h"
+
+#include "porting.h"
+#include "filesys.h"
+#include "main.h"
+#include "settings.h"
+#include "guiMainMenu.h"
+
+#include "guiEngine.h"
+
+#if USE_CURL
+#include <curl/curl.h>
+#endif
+
+/******************************************************************************/
+int menuscript_ErrorHandler(lua_State *L) {
+       lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+       if (!lua_istable(L, -1)) {
+       lua_pop(L, 1);
+       return 1;
+       }
+       lua_getfield(L, -1, "traceback");
+       if (!lua_isfunction(L, -1)) {
+       lua_pop(L, 2);
+       return 1;
+       }
+       lua_pushvalue(L, 1);
+       lua_pushinteger(L, 2);
+       lua_call(L, 2, 1);
+       return 1;
+}
+
+/******************************************************************************/
+TextDestGuiEngine::TextDestGuiEngine(GUIEngine* engine)
+{
+       m_engine = engine;
+}
+
+/******************************************************************************/
+void TextDestGuiEngine::gotText(std::map<std::string, std::string> fields)
+{
+       m_engine->handleButtons(fields);
+}
+
+/******************************************************************************/
+void TextDestGuiEngine::gotText(std::wstring text)
+{
+       m_engine->handleEvent(wide_to_narrow(text));
+}
+
+/******************************************************************************/
+GUIEngine::GUIEngine(  irr::IrrlichtDevice* dev,
+                                               gui::IGUIElement* parent,
+                                               IMenuManager *menumgr,
+                                               scene::ISceneManager* smgr,
+                                               MainMenuData* data) :
+       m_device(dev),
+       m_parent(parent),
+       m_menumanager(menumgr),
+       m_smgr(smgr),
+       m_data(data),
+       m_formspecgui(0),
+       m_buttonhandler(0),
+       m_menu(0),
+       m_startgame(false),
+       m_engineluastack(0),
+       m_luaerrorhandler(-1),
+       m_scriptdir(""),
+       m_irr_toplefttext(0),
+       m_clouds_enabled(true),
+       m_cloud()
+{
+       //initialize texture pointers
+       for (unsigned int i = 0; i < TEX_LAYER_MAX; i++) {
+               m_textures[i] = 0;
+       }
+       // is deleted by guiformspec!
+       m_buttonhandler = new TextDestGuiEngine(this);
+
+       //create luastack
+       m_engineluastack = luaL_newstate();
+
+       //load basic lua modules
+       luaL_openlibs(m_engineluastack);
+
+       //init
+       guiLuaApi::initialize(m_engineluastack,this);
+
+       //push errorstring
+       if (m_data->errormessage != "")
+       {
+               lua_getglobal(m_engineluastack, "gamedata");
+               int gamedata_idx = lua_gettop(m_engineluastack);
+               lua_pushstring(m_engineluastack, "errormessage");
+               lua_pushstring(m_engineluastack,m_data->errormessage.c_str());
+               lua_settable(m_engineluastack, gamedata_idx);
+               m_data->errormessage = "";
+       }
+
+       //create topleft header
+       core::rect<s32> rect(0, 0, 500, 40);
+       rect += v2s32(4, 0);
+       std::string t = "Minetest " VERSION_STRING;
+
+       m_irr_toplefttext =
+               m_device->getGUIEnvironment()->addStaticText(narrow_to_wide(t).c_str(),
+               rect,false,true,0,-1);
+
+       //create formspecsource
+       m_formspecgui = new FormspecFormSource("",&m_formspecgui);
+
+       /* Create menu */
+       m_menu =
+               new GUIFormSpecMenu(    m_device,
+                                                               m_parent,
+                                                               -1,
+                                                               m_menumanager,
+                                                               0 /* &client */,
+                                                               0 /* gamedef */);
+
+       m_menu->allowClose(false);
+       m_menu->lockSize(true,v2u32(800,600));
+       m_menu->setFormSource(m_formspecgui);
+       m_menu->setTextDest(m_buttonhandler);
+       m_menu->useGettext(true);
+
+       std::string builtin_helpers
+               = porting::path_share + DIR_DELIM + "builtin"
+                       + DIR_DELIM + "mainmenu_helper.lua";
+
+       if (!runScript(builtin_helpers)) {
+               errorstream
+                       << "GUIEngine::GUIEngine unable to load builtin helper script"
+                       << std::endl;
+               return;
+       }
+
+       std::string menuscript = "";
+       if (g_settings->exists("main_menu_script"))
+               menuscript = g_settings->get("main_menu_script");
+       std::string builtin_menuscript =
+                       porting::path_share + DIR_DELIM + "builtin"
+                               + DIR_DELIM + "mainmenu.lua";
+
+       lua_pushcfunction(m_engineluastack, menuscript_ErrorHandler);
+       m_luaerrorhandler = lua_gettop(m_engineluastack);
+
+       m_scriptdir = menuscript.substr(0,menuscript.find_last_of(DIR_DELIM)-1);
+       if((menuscript == "") || (!runScript(menuscript))) {
+               infostream
+                       << "GUIEngine::GUIEngine execution of custom menu failed!"
+                       << std::endl
+                       << "\tfalling back to builtin menu"
+                       << std::endl;
+               m_scriptdir = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "builtin"+ DIR_DELIM);
+               if(!runScript(builtin_menuscript)) {
+                       errorstream
+                               << "GUIEngine::GUIEngine unable to load builtin menu"
+                               << std::endl;
+                       return;
+               }
+       }
+
+       run();
+
+       m_menumanager->deletingMenu(m_menu);
+       m_menu->drop();
+       m_menu = 0;
+}
+
+/******************************************************************************/
+bool GUIEngine::runScript(std::string script) {
+
+       int ret =       luaL_loadfile(m_engineluastack, script.c_str()) ||
+                               lua_pcall(m_engineluastack, 0, 0, m_luaerrorhandler);
+       if(ret){
+               errorstream<<"========== ERROR FROM LUA WHILE CREATING MAIN MENU ==========="<<std::endl;
+               errorstream<<"Failed to load and run script from "<<std::endl;
+               errorstream<<script<<":"<<std::endl;
+               errorstream<<std::endl;
+               errorstream<<lua_tostring(m_engineluastack, -1)<<std::endl;
+               errorstream<<std::endl;
+               errorstream<<"=================== END OF ERROR FROM LUA ===================="<<std::endl;
+               lua_pop(m_engineluastack, 1); // Pop error message from stack
+               lua_pop(m_engineluastack, 1); // Pop the error handler from stack
+               return false;
+       }
+       return true;
+}
+
+/******************************************************************************/
+void GUIEngine::run()
+{
+
+       // Always create clouds because they may or may not be
+       // needed based on the game selected
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+
+       cloudInit();
+
+       while(m_device->run() && (!m_startgame)) {
+               driver->beginScene(true, true, video::SColor(255,140,186,250));
+
+               if (m_clouds_enabled)
+               {
+                       cloudPreProcess();
+                       drawOverlay(driver);
+               }
+               else
+                       drawBackground(driver);
+
+               drawHeader(driver);
+               drawFooter(driver);
+
+               m_device->getGUIEnvironment()->drawAll();
+
+               driver->endScene();
+
+               if (m_clouds_enabled)
+                       cloudPostProcess();
+               else
+                       sleep_ms(25);
+       }
+
+       m_menu->quitMenu();
+}
+
+/******************************************************************************/
+void GUIEngine::handleEvent(std::string text)
+{
+       lua_getglobal(m_engineluastack, "engine");
+
+       lua_getfield(m_engineluastack, -1, "event_handler");
+
+       if(lua_isnil(m_engineluastack, -1))
+               return;
+
+       luaL_checktype(m_engineluastack, -1, LUA_TFUNCTION);
+
+       lua_pushstring(m_engineluastack, text.c_str());
+
+       if(lua_pcall(m_engineluastack, 1, 0, m_luaerrorhandler))
+               scriptError("error: %s", lua_tostring(m_engineluastack, -1));
+}
+
+/******************************************************************************/
+void GUIEngine::handleButtons(std::map<std::string, std::string> fields)
+{
+       lua_getglobal(m_engineluastack, "engine");
+
+       lua_getfield(m_engineluastack, -1, "button_handler");
+
+       if(lua_isnil(m_engineluastack, -1))
+               return;
+
+       luaL_checktype(m_engineluastack, -1, LUA_TFUNCTION);
+
+       lua_newtable(m_engineluastack);
+       for(std::map<std::string, std::string>::const_iterator
+               i = fields.begin(); i != fields.end(); i++){
+               const std::string &name = i->first;
+               const std::string &value = i->second;
+               lua_pushstring(m_engineluastack, name.c_str());
+               lua_pushlstring(m_engineluastack, value.c_str(), value.size());
+               lua_settable(m_engineluastack, -3);
+       }
+
+       if(lua_pcall(m_engineluastack, 1, 0, m_luaerrorhandler))
+               scriptError("error: %s", lua_tostring(m_engineluastack, -1));
+}
+
+/******************************************************************************/
+GUIEngine::~GUIEngine()
+{
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver != 0);
+
+       lua_close(m_engineluastack);
+
+       m_irr_toplefttext->setText(L"");
+
+       //initialize texture pointers
+       for (unsigned int i = 0; i < TEX_LAYER_MAX; i++) {
+               if (m_textures[i] != 0)
+                       driver->removeTexture(m_textures[i]);
+       }
+
+       m_cloud.clouds->drop();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudInit()
+{
+       m_cloud.clouds = new Clouds(m_smgr->getRootSceneNode(),
+                       m_smgr, -1, rand(), 100);
+       m_cloud.clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
+
+       m_cloud.camera = m_smgr->addCameraSceneNode(0,
+                               v3f(0,0,0), v3f(0, 60, 100));
+       m_cloud.camera->setFarValue(10000);
+
+       m_cloud.lasttime = m_device->getTimer()->getTime();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudPreProcess()
+{
+       u32 time = m_device->getTimer()->getTime();
+
+       if(time > m_cloud.lasttime)
+               m_cloud.dtime = (time - m_cloud.lasttime) / 1000.0;
+       else
+               m_cloud.dtime = 0;
+
+       m_cloud.lasttime = time;
+
+       m_cloud.clouds->step(m_cloud.dtime*3);
+       m_cloud.clouds->render();
+       m_smgr->drawAll();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudPostProcess()
+{
+       float fps_max = g_settings->getFloat("fps_max");
+       // Time of frame without fps limit
+       float busytime;
+       u32 busytime_u32;
+       // not using getRealTime is necessary for wine
+       u32 time = m_device->getTimer()->getTime();
+       if(time > m_cloud.lasttime)
+               busytime_u32 = time - m_cloud.lasttime;
+       else
+               busytime_u32 = 0;
+       busytime = busytime_u32 / 1000.0;
+
+       // FPS limiter
+       u32 frametime_min = 1000./fps_max;
+
+       if(busytime_u32 < frametime_min) {
+               u32 sleeptime = frametime_min - busytime_u32;
+               m_device->sleep(sleeptime);
+       }
+}
+
+/******************************************************************************/
+void GUIEngine::drawBackground(video::IVideoDriver* driver)
+{
+       v2u32 screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_BACKGROUND];
+
+       /* If no texture, draw background of solid color */
+       if(!texture){
+               video::SColor color(255,80,58,37);
+               core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
+               driver->draw2DRectangle(color, rect, NULL);
+               return;
+       }
+
+       /* Draw background texture */
+       v2u32 sourcesize = texture->getSize();
+       driver->draw2DImage(texture,
+               core::rect<s32>(0, 0, screensize.X, screensize.Y),
+               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+               NULL, NULL, true);
+}
+
+/******************************************************************************/
+void GUIEngine::drawOverlay(video::IVideoDriver* driver)
+{
+       v2u32 screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_OVERLAY];
+
+       /* If no texture, draw background of solid color */
+       if(!texture)
+               return;
+
+       /* Draw background texture */
+       v2u32 sourcesize = texture->getSize();
+       driver->draw2DImage(texture,
+               core::rect<s32>(0, 0, screensize.X, screensize.Y),
+               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+               NULL, NULL, true);
+}
+
+/******************************************************************************/
+void GUIEngine::drawHeader(video::IVideoDriver* driver)
+{
+       core::dimension2d<u32> screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_HEADER];
+
+       /* If no texture, draw nothing */
+       if(!texture)
+               return;
+
+       f32 mult = (((f32)screensize.Width / 2)) /
+                       ((f32)texture->getOriginalSize().Width);
+
+       v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
+                       ((f32)texture->getOriginalSize().Height) * mult);
+
+       // Don't draw the header is there isn't enough room
+       s32 free_space = (((s32)screensize.Height)-320)/2;
+
+       if (free_space > splashsize.Y) {
+               core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
+               splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
+                               ((free_space/2)-splashsize.Y/2)+10);
+
+       video::SColor bgcolor(255,50,50,50);
+
+       driver->draw2DImage(texture, splashrect,
+               core::rect<s32>(core::position2d<s32>(0,0),
+               core::dimension2di(texture->getSize())),
+               NULL, NULL, true);
+       }
+}
+
+/******************************************************************************/
+void GUIEngine::drawFooter(video::IVideoDriver* driver)
+{
+       core::dimension2d<u32> screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_FOOTER];
+
+       /* If no texture, draw nothing */
+       if(!texture)
+               return;
+
+       f32 mult = (((f32)screensize.Width)) /
+                       ((f32)texture->getOriginalSize().Width);
+
+       v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
+                       ((f32)texture->getOriginalSize().Height) * mult);
+
+       // Don't draw the footer if there isn't enough room
+       s32 free_space = (((s32)screensize.Height)-320)/2;
+
+       if (free_space > footersize.Y) {
+               core::rect<s32> rect(0,0,footersize.X,footersize.Y);
+               rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
+               rect -= v2s32(footersize.X/2, 0);
+
+               driver->draw2DImage(texture, rect,
+                       core::rect<s32>(core::position2d<s32>(0,0),
+                       core::dimension2di(texture->getSize())),
+                       NULL, NULL, true);
+       }
+}
+
+/******************************************************************************/
+bool GUIEngine::setTexture(texture_layer layer,std::string texturepath) {
+
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver != 0);
+
+       if (m_textures[layer] != 0)
+       {
+               driver->removeTexture(m_textures[layer]);
+               m_textures[layer] = 0;
+       }
+
+       if ((texturepath == "") || !fs::PathExists(texturepath))
+               return false;
+
+       m_textures[layer] = driver->getTexture(texturepath.c_str());
+
+       if (m_textures[layer] == 0) return false;
+
+       return true;
+}
+
+/******************************************************************************/
+#if USE_CURL
+static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+       FILE* targetfile = (FILE*) userp;
+       fwrite(contents,size,nmemb,targetfile);
+       return size * nmemb;
+}
+#endif
+bool GUIEngine::downloadFile(std::string url,std::string target) {
+#if USE_CURL
+       //download file via curl
+       CURL *curl;
+
+       curl = curl_easy_init();
+
+       if (curl)
+       {
+               CURLcode res;
+               bool retval = true;
+
+               FILE* targetfile = fopen(target.c_str(),"wb");
+
+               if (targetfile) {
+                       curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+                       curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+                       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+                       curl_easy_setopt(curl, CURLOPT_WRITEDATA, targetfile);
+
+                       res = curl_easy_perform(curl);
+                       if (res != CURLE_OK) {
+                               errorstream << "File at url \"" << url
+                                       <<"\" not found (internet connection?)" <<std::endl;
+                               retval = false;
+                       }
+                       fclose(targetfile);
+               }
+               else {
+                       retval = false;
+               }
+
+               curl_easy_cleanup(curl);
+               return retval;
+       }
+#endif
+       return false;
+}
+
+/******************************************************************************/
+void GUIEngine::scriptError(const char *fmt, ...)
+{
+       va_list argp;
+       va_start(argp, fmt);
+       char buf[10000];
+       vsnprintf(buf, 10000, fmt, argp);
+       va_end(argp);
+       errorstream<<"MAINMENU ERROR: "<<buf;
+}
+
+/******************************************************************************/
+void GUIEngine::setTopleftText(std::string append) {
+       std::string toset = "Minetest " VERSION_STRING;
+
+       if (append != "") {
+               toset += " / ";
+               toset += append;
+       }
+
+       m_irr_toplefttext->setText(narrow_to_wide(toset).c_str());
+}
diff --git a/src/guiEngine.h b/src/guiEngine.h
new file mode 100644 (file)
index 0000000..2445ed8
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+Minetest
+Copyright (C) 2013 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef GUI_ENGINE_H_
+#define GUI_ENGINE_H_
+
+/******************************************************************************/
+/* Includes                                                                   */
+/******************************************************************************/
+#include "irrlichttypes.h"
+#include "modalMenu.h"
+#include "clouds.h"
+#include "guiLuaApi.h"
+#include "guiFormSpecMenu.h"
+
+/******************************************************************************/
+/* Typedefs and macros                                                        */
+/******************************************************************************/
+#define MAX_MENUBAR_BTN_COUNT  10
+#define MAX_MENUBAR_BTN_ID             256
+#define MIN_MENUBAR_BTN_ID             (MAX_MENUBAR_BTN_ID - MAX_MENUBAR_BTN_COUNT)
+
+/** texture layer ids */
+typedef enum {
+       TEX_LAYER_BACKGROUND = 0,
+       TEX_LAYER_OVERLAY,
+       TEX_LAYER_HEADER,
+       TEX_LAYER_FOOTER,
+       TEX_LAYER_MAX
+} texture_layer;
+
+/******************************************************************************/
+/* forward declarations                                                       */
+/******************************************************************************/
+class GUIEngine;
+struct MainMenuData;
+
+/******************************************************************************/
+/* declarations                                                               */
+/******************************************************************************/
+
+/** GUIEngine specific implementation of TextDest used within guiFormSpecMenu */
+class TextDestGuiEngine : public TextDest
+{
+public:
+       /**
+        * default constructor
+        * @param engine the engine data is transmitted for further processing
+        */
+       TextDestGuiEngine(GUIEngine* engine);
+       /**
+        * receive fields transmitted by guiFormSpecMenu
+        * @param fields map containing formspec field elements currently active
+        */
+       void gotText(std::map<std::string, std::string> fields);
+
+       /**
+        * receive text/events transmitted by guiFormSpecMenu
+        * @param text textual representation of event
+        */
+       void gotText(std::wstring text);
+private:
+       /** target to transmit data to */
+       GUIEngine* m_engine;
+};
+
+
+/** implementation of main menu based uppon formspecs */
+class GUIEngine {
+public:
+       /** TextDestGuiEngine needs to transfer data to engine */
+       friend class TextDestGuiEngine;
+       /** guiLuaApi is used to initialize contained stack */
+       friend class guiLuaApi;
+
+       /**
+        * default constructor
+        * @param dev device to draw at
+        * @param parent parent gui element
+        * @param menumgr manager to add menus to
+        * @param smgr scene manager to add scene elements to
+        * @param data struct to transfer data to main game handling
+        */
+       GUIEngine(      irr::IrrlichtDevice* dev,
+                               gui::IGUIElement* parent,
+                               IMenuManager *menumgr,
+                               scene::ISceneManager* smgr,
+                               MainMenuData* data);
+
+       /** default destructor */
+       virtual ~GUIEngine();
+
+protected:
+       /**
+        * process field data recieved from formspec
+        * @param fields data in field format
+        */
+       void handleButtons(std::map<std::string, std::string> fields);
+       /**
+        * process events received from formspec
+        * @param text events in textual form
+        */
+       void handleEvent(std::string text);
+
+       /**
+        * return dir of current menuscript
+        */
+       std::string getScriptDir() {
+               return m_scriptdir;
+       }
+
+private:
+
+       /* run main menu loop */
+       void run();
+
+       /** handler to limit frame rate within main menu */
+       void limitFrameRate();
+
+       /** device to draw at */
+       irr::IrrlichtDevice*    m_device;
+       /** parent gui element */
+       gui::IGUIElement*               m_parent;
+       /** manager to add menus to */
+       IMenuManager*                   m_menumanager;
+       /** scene manager to add scene elements to */
+       scene::ISceneManager*   m_smgr;
+       /** pointer to data beeing transfered back to main game handling */
+       MainMenuData*                   m_data;
+
+       /** representation of form source to be used in mainmenu formspec */
+       FormspecFormSource*             m_formspecgui;
+       /** formspec input receiver */
+       TextDestGuiEngine*              m_buttonhandler;
+       /** the formspec menu */
+       GUIFormSpecMenu*                m_menu;
+
+       /** variable used to abort menu and return back to main game handling */
+       bool                                    m_startgame;
+
+       /**
+        * initialize lua stack
+        * @param L stack to initialize
+        */
+       void initalize_api(lua_State * L);
+
+       /**
+        * run a lua script
+        * @param script full path to script to run
+        */
+       bool runScript(std::string script);
+
+       /**
+        * script error handler to process errors within lua
+        */
+       void scriptError(const char *fmt, ...);
+
+       /** lua stack */
+       lua_State*                              m_engineluastack;
+       /** lua internal stack number of error handler*/
+       int                                             m_luaerrorhandler;
+
+       /** script basefolder */
+       std::string                             m_scriptdir;
+
+       /**
+        * draw background layer
+        * @param driver to use for drawing
+        */
+       void drawBackground(video::IVideoDriver* driver);
+       /**
+        * draw overlay layer
+        * @param driver to use for drawing
+        */
+       void drawOverlay(video::IVideoDriver* driver);
+       /**
+        * draw header layer
+        * @param driver to use for drawing
+        */
+       void drawHeader(video::IVideoDriver* driver);
+       /**
+        * draw footer layer
+        * @param driver to use for drawing
+        */
+       void drawFooter(video::IVideoDriver* driver);
+
+       /**
+        * load a texture for a specified layer
+        * @param layer draw layer to specify texture
+        * @param texturepath full path of texture to load
+        */
+       bool setTexture(texture_layer layer,std::string texturepath);
+
+       /**
+        * download a file using curl
+        * @param url url to download
+        * @param target file to store to
+        */
+       bool downloadFile(std::string url,std::string target);
+
+       /** array containing pointers to current specified texture layers */
+       video::ITexture*                m_textures[TEX_LAYER_MAX];
+
+       /** draw version string in topleft corner */
+       void drawVersion();
+
+       /**
+        * specify text to be appended to version string
+        * @param text to set
+        */
+       void setTopleftText(std::string append);
+
+       /** pointer to gui element shown at topleft corner */
+       irr::gui::IGUIStaticText*       m_irr_toplefttext;
+
+       /** initialize cloud subsystem */
+       void cloudInit();
+       /** do preprocessing for cloud subsystem */
+       void cloudPreProcess();
+       /** do postprocessing for cloud subsystem */
+       void cloudPostProcess();
+
+       /** internam data required for drawing clouds */
+       struct clouddata {
+               /** delta time since last cloud processing */
+               f32             dtime;
+               /** absolute time of last cloud processing */
+               u32             lasttime;
+               /** pointer to cloud class */
+               Clouds* clouds;
+               /** camera required for drawing clouds */
+               scene::ICameraSceneNode* camera;
+       };
+
+       /** is drawing of clouds enabled atm */
+       bool                                    m_clouds_enabled;
+       /** data used to draw clouds */
+       clouddata                               m_cloud;
+
+};
+
+
+
+#endif /* GUI_ENGINE_H_ */
diff --git a/src/guiFileSelectMenu.cpp b/src/guiFileSelectMenu.cpp
new file mode 100644 (file)
index 0000000..93d43f7
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ Minetest
+ Copyright (C) 2013 sapier
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "guiFileSelectMenu.h"
+#include "util/string.h"
+#include <locale.h>
+
+GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
+                               gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
+                               std::string title, std::string formname) :
+GUIModalMenu(env, parent, id, menumgr)
+{
+       m_title = narrow_to_wide(title);
+       m_parent = parent;
+       m_formname = formname;
+       m_text_dst = 0;
+       m_accepted = false;
+       m_previous_locale = setlocale(LC_ALL,0);
+}
+
+GUIFileSelectMenu::~GUIFileSelectMenu()
+{
+       removeChildren();
+       setlocale(LC_ALL,m_previous_locale.c_str());
+}
+
+void GUIFileSelectMenu::removeChildren()
+{
+       const core::list<gui::IGUIElement*> &children = getChildren();
+       core::list<gui::IGUIElement*> children_copy;
+       for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
+                != children.end(); i++)
+       {
+               children_copy.push_back(*i);
+       }
+       for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
+                != children_copy.end(); i++)
+       {
+               (*i)->remove();
+       }
+}
+
+void GUIFileSelectMenu::regenerateGui(v2u32 screensize)
+{
+       removeChildren();
+       m_fileOpenDialog = 0;
+
+       core::dimension2du size(600,400);
+       core::rect < s32 > rect(0,0,screensize.X,screensize.Y);
+
+       DesiredRect = rect;
+       recalculateAbsolutePosition(false);
+
+       m_fileOpenDialog =
+                       Environment->addFileOpenDialog(m_title.c_str(),false,this,-1);
+
+       core::position2di pos = core::position2di(screensize.X/2 - size.Width/2,screensize.Y/2 -size.Height/2);
+       m_fileOpenDialog->setRelativePosition(pos);
+       m_fileOpenDialog->setMinSize(size);
+}
+
+void GUIFileSelectMenu::drawMenu()
+{
+       gui::IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+
+       gui::IGUIElement::draw();
+}
+
+void GUIFileSelectMenu::acceptInput() {
+       if ((m_text_dst != 0) && (this->m_formname != "")){
+               std::map<std::string, std::string> fields;
+
+               if (m_accepted)
+                       fields[m_formname + "_accepted"] = wide_to_narrow(m_fileOpenDialog->getFileName());
+               else
+                       fields[m_formname + "_canceled"] = m_formname;
+
+               this->m_text_dst->gotText(fields);
+       }
+}
+
+bool GUIFileSelectMenu::OnEvent(const SEvent& event)
+{
+
+       if (event.EventType == irr::EET_GUI_EVENT) {
+
+               int callerId = event.GUIEvent.Caller->getID();
+               if (callerId >= 0) {
+                       std::cout << "CallerId:" << callerId << std::endl;
+               }
+
+               switch (event.GUIEvent.EventType) {
+                       case gui::EGET_ELEMENT_CLOSED:
+                       case gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED:
+                               m_accepted=false;
+                               acceptInput();
+                               quitMenu();
+                               return true;
+                               break;
+
+                       case gui::EGET_DIRECTORY_SELECTED:
+                       case gui::EGET_FILE_SELECTED:
+                               m_accepted=true;
+                               acceptInput();
+                               quitMenu();
+                               return true;
+                               break;
+
+                       default:
+                               //ignore this event
+                               break;
+               }
+       }
+       return Parent ? Parent->OnEvent(event) : false;
+}
diff --git a/src/guiFileSelectMenu.h b/src/guiFileSelectMenu.h
new file mode 100644 (file)
index 0000000..987a9f2
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ Minetest
+ Copyright (C) 2013 sapier
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GUIFILESELECTMENU_H_
+#define GUIFILESELECTMENU_H_
+
+#include <string>
+
+#include "modalMenu.h"
+#include "IGUIFileOpenDialog.h"
+#include "guiFormSpecMenu.h" //required because of TextDest only !!!
+
+
+class GUIFileSelectMenu: public GUIModalMenu
+{
+public:
+       GUIFileSelectMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
+                       s32 id, IMenuManager *menumgr,
+                       std::string title,
+                       std::string formid);
+       ~GUIFileSelectMenu();
+
+       void removeChildren();
+
+       /*
+        Remove and re-add (or reposition) stuff
+        */
+       void regenerateGui(v2u32 screensize);
+
+       void drawMenu();
+
+       bool OnEvent(const SEvent& event);
+
+       bool isRunning() {
+               return m_running;
+       }
+
+       void setTextDest(TextDest * dest) {
+               m_text_dst = dest;
+       }
+
+private:
+       void acceptInput();
+
+       std::wstring m_title;
+       bool m_accepted;
+       gui::IGUIElement* m_parent;
+
+       std::string m_selectedPath;
+
+       gui::IGUIFileOpenDialog* m_fileOpenDialog;
+
+       std::string m_previous_locale;
+
+       bool m_running;
+
+       TextDest *m_text_dst;
+
+       std::string m_formname;
+};
+
+
+
+#endif /* GUIFILESELECTMENU_H_ */
index ee39df8b7a1c8a6b3e1e7ead6498425bb7b86b02..0aa2c2dcd8db40f00b576ab1805b5cf64ab67e76 100644 (file)
@@ -18,6 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 
+#include <cstdlib>
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+#include <limits>
 #include "guiFormSpecMenu.h"
 #include "constants.h"
 #include "gamedef.h"
@@ -28,13 +33,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <IGUIButton.h>
 #include <IGUIStaticText.h>
 #include <IGUIFont.h>
+#include <IGUIListBox.h>
+#include <IGUITabControl.h>
+#include <IGUIScrollBar.h>
+#include <IGUIComboBox.h>
 #include "log.h"
 #include "tile.h" // ITextureSource
 #include "util/string.h"
 #include "util/numeric.h"
+#include "filesys.h"
 
 #include "gettext.h"
 
+
+#define MY_CHECKPOS(a,b)                                                                                                       \
+       if (v_pos.size() != 2) {                                                                                                \
+               errorstream<< "Invalid pos for element " << a << "specified: \""        \
+                       << parts[b] << "\"" << std::endl;                                                               \
+                       return;                                                                                                                 \
+       }
+
+#define MY_CHECKGEOM(a,b)                                                                                                      \
+       if (v_geom.size() != 2) {                                                                                               \
+               errorstream<< "Invalid pos for element " << a << "specified: \""        \
+                       << parts[b] << "\"" << std::endl;                                                               \
+                       return;                                                                                                                 \
+       }
+
+
 void drawItemStack(video::IVideoDriver *driver,
                gui::IGUIFont *font,
                const ItemStack &item,
@@ -140,7 +166,10 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
        m_selected_item(NULL),
        m_selected_amount(0),
        m_selected_dragging(false),
-       m_tooltip_element(NULL)
+       m_tooltip_element(NULL),
+       m_allowclose(true),
+       m_use_gettext(false),
+       m_lock(false)
 {
 }
 
@@ -148,53 +177,1315 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
 {
        removeChildren();
 
-       delete m_selected_item;
-       delete m_form_src;
-       delete m_text_dst;
-}
+       delete m_selected_item;
+       delete m_form_src;
+       delete m_text_dst;
+}
+
+void GUIFormSpecMenu::removeChildren()
+{
+       const core::list<gui::IGUIElement*> &children = getChildren();
+       core::list<gui::IGUIElement*> children_copy;
+       for(core::list<gui::IGUIElement*>::ConstIterator
+                       i = children.begin(); i != children.end(); i++)
+       {
+               children_copy.push_back(*i);
+       }
+       for(core::list<gui::IGUIElement*>::Iterator
+                       i = children_copy.begin();
+                       i != children_copy.end(); i++)
+       {
+               (*i)->remove();
+       }
+       /*{
+               gui::IGUIElement *e = getElementFromId(256);
+               if(e != NULL)
+                       e->remove();
+       }*/
+       if(m_tooltip_element)
+       {
+               m_tooltip_element->remove();
+               m_tooltip_element = NULL;
+       }
+}
+
+int GUIFormSpecMenu::getListboxIndex(std::string listboxname) {
+
+       std::wstring wlistboxname = narrow_to_wide(listboxname.c_str());
+
+       for(unsigned int i=0; i < m_listboxes.size(); i++) {
+
+               std::wstring name(m_listboxes[i].first.fname.c_str());
+               if ( name == wlistboxname) {
+                       return m_listboxes[i].second->getSelected();
+               }
+       }
+       return -1;
+}
+
+std::vector<std::string> split(const std::string &s, char delim, bool escape=false) {
+       std::vector<std::string> tokens;
+
+       if (!escape) {
+               int startpos = 0;
+               size_t nextpos = s.find(delim);
+
+               while(nextpos != std::string::npos) {
+                       std::string toadd = s.substr(startpos,nextpos-startpos);
+                       tokens.push_back(toadd);
+                       startpos = nextpos+1;
+                       nextpos = s.find(delim,nextpos+1);
+               }
+
+               //push last element
+               tokens.push_back(s.substr(startpos));
+       }
+       else {
+               std::string current = "";
+               current += s.c_str()[0];
+               bool last_was_delim = false;
+               for(unsigned int i=1; i < s.size(); i++) {
+                       if (last_was_delim) {
+                               if (s.c_str()[i] == delim) {
+                                       current += s.c_str()[i];
+                                       last_was_delim = false;
+                               }
+                               else {
+                                       tokens.push_back(current);
+
+                                       current = "";
+                                       current += s.c_str()[i];
+                                       last_was_delim = false;
+                               }
+                       }
+                       else {
+                               if (s.c_str()[i] == delim) {
+                                       last_was_delim = true;
+                               }
+                               else {
+                                       last_was_delim = false;
+                                       current += s.c_str()[i];
+                               }
+                       }
+               }
+               //push last element
+               tokens.push_back(current);
+       }
+
+       return tokens;
+}
+
+void GUIFormSpecMenu::parseSize(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,',');
+
+       if (parts.size() == 2) {
+               v2f invsize;
+
+               if (parts[1].find(';') != std::string::npos)
+                       parts[1] = parts[1].substr(0,parts[1].find(';'));
+
+               invsize.X = stof(parts[0]);
+               invsize.Y = stof(parts[1]);
+
+               infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
+
+               if (m_lock) {
+                       v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
+                       v2u32 delta = current_screensize - m_lockscreensize;
+
+                       if (current_screensize.Y > m_lockscreensize.Y)
+                               delta.Y /= 2;
+                       else
+                               delta.Y = 0;
+
+                       if (current_screensize.X > m_lockscreensize.X)
+                               delta.X /= 2;
+                       else
+                               delta.X = 0;
+
+                       offset = v2s32(delta.X,delta.Y);
+
+                       data->screensize = m_lockscreensize;
+               }
+               else {
+                       offset = v2s32(0,0);
+               }
+
+               padding = v2s32(data->screensize.Y/40, data->screensize.Y/40);
+               spacing = v2s32(data->screensize.Y/12, data->screensize.Y/13);
+               imgsize = v2s32(data->screensize.Y/15, data->screensize.Y/15);
+               data->size = v2s32(
+                       padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
+                       padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (data->helptext_h-5)
+               );
+               data->rect = core::rect<s32>(
+                               data->screensize.X/2 - data->size.X/2 + offset.X,
+                               data->screensize.Y/2 - data->size.Y/2 + offset.Y,
+                               data->screensize.X/2 + data->size.X/2 + offset.X,
+                               data->screensize.Y/2 + data->size.Y/2 + offset.Y
+               );
+
+               DesiredRect = data->rect;
+               recalculateAbsolutePosition(false);
+               data->basepos = getBasePos();
+               data->bp_set = 2;
+               return;
+       }
+       errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseList(parserData* data,std::string element) {
+
+       if (m_gamedef == 0) {
+               errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
+               return;
+       }
+
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 4) || (parts.size() == 5)) {
+               std::string location = parts[0];
+               std::string listname = parts[1];
+               std::vector<std::string> v_pos  = split(parts[2],',');
+               std::vector<std::string> v_geom = split(parts[3],',');
+               std::string startindex = "";
+               if (parts.size() == 5)
+                       startindex = parts[4];
+
+               MY_CHECKPOS("list",2);
+               MY_CHECKGEOM("list",3);
+
+               InventoryLocation loc;
+
+               if(location == "context" || location == "current_name")
+                       loc = m_current_inventory_location;
+               else
+                       loc.deSerialize(location);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = stoi(v_geom[0]);
+               geom.Y = stoi(v_geom[1]);
+               infostream<<"list inv="<<location<<", listname="<<listname
+                               <<", pos=("<<pos.X<<","<<pos.Y<<")"
+                               <<", geom=("<<geom.X<<","<<geom.Y<<")"
+                               <<std::endl;
+
+               s32 start_i = 0;
+               if(startindex != "")
+                       start_i = stoi(startindex);
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
+               m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
+               return;
+       }
+       errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 3) || (parts.size() == 4)) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = parts[1];
+               std::string label = parts[2];
+               std::string selected = "";
+
+               if (parts.size() == 4)
+                       selected = parts[3];
+
+               MY_CHECKPOS("checkbox",0);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
+
+               bool fselected = false;
+
+               if (selected == "true")
+                       fselected = true;
+
+               wchar_t* wlabel = 0;
+
+               if (m_use_gettext)
+                       wlabel = wgettext(label.c_str());
+               else
+                       wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+               FieldSpec spec = FieldSpec(
+                               narrow_to_wide(name.c_str()),
+                               narrow_to_wide(""),
+                               wlabel,
+                               258+m_fields.size()
+                       );
+
+               spec.ftype = f_CheckBox;
+
+               gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
+                                       spec.fid, wlabel);
+
+               m_checkboxes.push_back(std::pair<FieldSpec,gui::IGUICheckBox*>(spec,e));
+               m_fields.push_back(spec);
+               if (m_use_gettext)
+                       delete[] wlabel;
+               return;
+       }
+       errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseImage(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 3) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+
+               MY_CHECKPOS("image",0);
+               MY_CHECKGEOM("image",1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               v2s32 geom;
+               geom.X = stoi(v_geom[0]) * (float)imgsize.X;
+               geom.Y = stoi(v_geom[1]) * (float)imgsize.Y;
+
+               infostream<<"image name="<<name
+                               <<", pos=("<<pos.X<<","<<pos.Y<<")"
+                               <<", geom=("<<geom.X<<","<<geom.Y<<")"
+                               <<std::endl;
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+               m_images.push_back(ImageDrawSpec(name, pos, geom));
+               return;
+       }
+
+       if (parts.size() == 2) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = parts[1];
+
+               MY_CHECKPOS("image",0);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               std::cout<<"image name="<<name
+                               <<", pos=("<<pos.X<<","<<pos.Y<<")"
+                               <<std::endl;
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+               m_images.push_back(ImageDrawSpec(name, pos));
+               return;
+       }
+       errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 3) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+
+               MY_CHECKPOS("itemimage",0);
+               MY_CHECKGEOM("itemimage",1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               v2s32 geom;
+               geom.X = stoi(v_geom[0]) * (float)imgsize.X;
+               geom.Y = stoi(v_geom[1]) * (float)imgsize.Y;
+
+               infostream<<"item name="<<name
+                               <<", pos=("<<pos.X<<","<<pos.Y<<")"
+                               <<", geom=("<<geom.X<<","<<geom.Y<<")"
+                               <<std::endl;
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
+               m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
+               return;
+       }
+       errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseButton(parserData* data,std::string element,std::string type) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 4) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+               std::string label = parts[3];
+
+               MY_CHECKPOS("button",0);
+               MY_CHECKGEOM("button",1);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
+
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
+
+               label = unescape_string(label);
+
+               wchar_t* wlabel = 0;
+
+               if (m_use_gettext)
+                       wlabel = wgettext(label.c_str());
+               else
+                       wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+               FieldSpec spec = FieldSpec(
+                       narrow_to_wide(name.c_str()),
+                       wlabel,
+                       narrow_to_wide(""),
+                       258+m_fields.size()
+               );
+               spec.ftype = f_Button;
+               if(type == "button_exit")
+                       spec.is_exit = true;
+
+               Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+               m_fields.push_back(spec);
+               if (m_use_gettext)
+                       delete[] wlabel;
+               return;
+       }
+       errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 3) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+
+               MY_CHECKPOS("background",0);
+               MY_CHECKGEOM("background",1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+               pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)spacing.X;
+               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+               infostream<<"image name="<<name
+                               <<", pos=("<<pos.X<<","<<pos.Y<<")"
+                               <<", geom=("<<geom.X<<","<<geom.Y<<")"
+                               <<std::endl;
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
+               m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
+               return;
+       }
+       errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 5) || (parts.size() == 6)) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+               std::vector<std::string> items = split(parts[3],',');
+               std::string str_initial_selection = "";
+               std::string str_transparent = "false";
+
+               if (parts.size() >= 5)
+                       str_initial_selection = parts[4];
+
+               if (parts.size() >= 6)
+                       str_transparent = parts[5];
+
+               MY_CHECKPOS("textlist",0);
+               MY_CHECKGEOM("textlist",1);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)spacing.X;
+               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               std::wstring fname_w = narrow_to_wide(name.c_str());
+
+               FieldSpec spec = FieldSpec(
+                       fname_w,
+                       narrow_to_wide(""),
+                       narrow_to_wide(""),
+                       258+m_fields.size()
+               );
+
+               spec.ftype = f_ListBox;
+
+               //now really show list
+               gui::IGUIListBox *e = Environment->addListBox(rect, this,spec.fid);
+
+               //don't reset if we already have a user specified selection
+               if (data->listbox_selections.find(fname_w) == data->listbox_selections.end()) {
+                       e->setAutoScrollEnabled(false);
+               }
+
+               if (str_transparent == "false")
+                       e->setDrawBackground(true);
+
+               for (unsigned int i=0; i < items.size(); i++) {
+                       if (items[i].c_str()[0] == '#') {
+                               if (items[i].c_str()[1] == '#') {
+                                       e->addItem(narrow_to_wide(items[i]).c_str() +1);
+                               }
+                               else {
+                                       std::wstring toadd = narrow_to_wide(items[i].c_str() + 4);
+                                       std::string color = items[i].substr(1,3);
+
+                                       e->addItem(toadd.c_str());
+
+                                       bool valid_color = true;
+
+                                       irr::video::SColor toset = getColor(color,valid_color);
+
+                                       if (valid_color)
+                                               e->setItemOverrideColor(i,toset);
+                               }
+                       }
+                       else {
+                       e->addItem(narrow_to_wide(items[i]).c_str());
+                       }
+               }
+
+               if (str_initial_selection != "")
+                       e->setSelected(stoi(str_initial_selection.c_str())-1);
+
+               if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
+                       e->setSelected(data->listbox_selections[fname_w]);
+               }
+
+               m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+
+void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 5) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = parts[2];
+               std::vector<std::string> items = split(parts[3],',');
+               std::string str_initial_selection = "";
+               str_initial_selection = parts[4];
+
+               MY_CHECKPOS("dropdown",0);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               s32 width = stof(parts[1]) * (float)spacing.Y;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+width, pos.Y+30);
+
+               std::wstring fname_w = narrow_to_wide(name.c_str());
+
+               FieldSpec spec = FieldSpec(
+                       fname_w,
+                       narrow_to_wide(""),
+                       narrow_to_wide(""),
+                       258+m_fields.size()
+               );
+
+               spec.ftype = f_DropDown;
+               spec.send = true;
+
+               //now really show list
+               gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid);
+
+               //don't reset if we already have a user specified selection
+               //if (data->combobox_selections.find(fname_w) == data->listbox_selections.end()) {
+               //      e->setAutoScrollEnabled(false);
+               //}
+
+               for (unsigned int i=0; i < items.size(); i++) {
+                       e->addItem(narrow_to_wide(items[i]).c_str());
+               }
+
+               if (str_initial_selection != "")
+                       e->setSelected(stoi(str_initial_selection.c_str())-1);
+
+               //if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
+               //      e->setSelected(data->listbox_selections[fname_w]);
+               //}
+
+               //m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream << "Invalid dropdown element(" << parts.size() << "): '"
+                               << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 4) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+               std::string label = parts[3];
+
+               MY_CHECKPOS("pwdfield",0);
+               MY_CHECKGEOM("pwdfield",1);
+
+               v2s32 pos;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+
+               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+               pos.Y -= 15;
+               geom.Y = 30;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               label = unescape_string(label);
+
+               wchar_t* wlabel = 0;
+
+               if (m_use_gettext) {
+                       if (label.length() > 1)
+                               wlabel = wgettext(label.c_str());
+                       else
+                               wlabel = (wchar_t*) narrow_to_wide("").c_str();
+               }
+               else
+                       wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+               FieldSpec spec = FieldSpec(
+                       narrow_to_wide(name.c_str()),
+                       wlabel,
+                       narrow_to_wide(""),
+                       258+m_fields.size()
+                       );
+
+               spec.send = true;
+               gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
+               Environment->setFocus(e);
+
+               if (label.length() > 1)
+               {
+                       rect.UpperLeftCorner.Y -= 15;
+                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
+                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+               }
+
+               e->setPasswordBox(true,L'*');
+
+               irr::SEvent evt;
+               evt.KeyInput.Key = KEY_END;
+               evt.EventType = EET_KEY_INPUT_EVENT;
+               evt.KeyInput.PressedDown = true;
+               e->OnEvent(evt);
+               m_fields.push_back(spec);
+               if ((m_use_gettext) && (label.length() >1))
+                       delete[] wlabel;
+               return;
+       }
+       errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseSimpleField(parserData* data,std::vector<std::string> &parts) {
+       std::string name = parts[0];
+       std::string label = parts[1];
+       std::string default_val = parts[2];
+
+       core::rect<s32> rect;
+
+       if(!data->bp_set)
+       {
+               rect = core::rect<s32>(
+                       data->screensize.X/2 - 580/2,
+                       data->screensize.Y/2 - 300/2,
+                       data->screensize.X/2 + 580/2,
+                       data->screensize.Y/2 + 300/2
+               );
+               DesiredRect = rect;
+               recalculateAbsolutePosition(false);
+               data->basepos = getBasePos();
+               data->bp_set = 1;
+       }
+       else if(data->bp_set == 2)
+               errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
+
+       v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+       pos.Y = ((m_fields.size()+2)*60);
+       v2s32 size = DesiredRect.getSize();
+
+       rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
+
+
+       if(m_form_src)
+               default_val = m_form_src->resolveText(default_val);
+
+       default_val = unescape_string(default_val);
+       label = unescape_string(label);
+
+       wchar_t* wlabel = 0;
+
+       if (m_use_gettext) {
+               if (label.length() > 1)
+                       wlabel = wgettext(label.c_str());
+               else
+                       wlabel = (wchar_t*) narrow_to_wide("").c_str();
+       }
+       else
+               wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+       FieldSpec spec = FieldSpec(
+               narrow_to_wide(name.c_str()),
+               wlabel,
+               narrow_to_wide(default_val.c_str()),
+               258+m_fields.size()
+       );
+
+       if (name == "")
+       {
+               // spec field id to 0, this stops submit searching for a value that isn't there
+               Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+       }
+       else
+       {
+               spec.send = true;
+               gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+               Environment->setFocus(e);
+
+               irr::SEvent evt;
+               evt.KeyInput.Key = KEY_END;
+               evt.EventType = EET_KEY_INPUT_EVENT;
+               evt.KeyInput.PressedDown = true;
+               e->OnEvent(evt);
+
+               if (label.length() > 1)
+               {
+                       rect.UpperLeftCorner.Y -= 15;
+                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
+                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+               }
+       }
+       if (m_use_gettext && (label.length() > 1))
+               delete[] wlabel;
+
+       m_fields.push_back(spec);
+}
+
+void GUIFormSpecMenu::parseTextArea(parserData* data,std::vector<std::string>& parts,std::string type) {
+
+       std::vector<std::string> v_pos = split(parts[0],',');
+       std::vector<std::string> v_geom = split(parts[1],',');
+       std::string name = parts[2];
+       std::string label = parts[3];
+       std::string default_val = parts[4];
+
+       MY_CHECKPOS(type,0);
+       MY_CHECKGEOM(type,1);
+
+       v2s32 pos;
+       pos.X = stof(v_pos[0]) * (float) spacing.X;
+       pos.Y = stof(v_pos[1]) * (float) spacing.Y;
+
+       v2s32 geom;
+
+       geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+
+       if (type == "textarea")
+       {
+               geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
+               pos.Y += 15;
+       }
+       else
+       {
+               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+               pos.Y -= 15;
+               geom.Y = 30;
+       }
+
+       core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+       if(data->bp_set != 2)
+               errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
+
+       if(m_form_src)
+               default_val = m_form_src->resolveText(default_val);
+
+
+       default_val = unescape_string(default_val);
+       label = unescape_string(label);
+
+       wchar_t* wlabel = 0;
+
+       if (m_use_gettext) {
+               if (label.length() > 1)
+                       wlabel = wgettext(label.c_str());
+               else
+                       wlabel = (wchar_t*) narrow_to_wide("").c_str();
+       }
+       else
+               wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str();
+
+       FieldSpec spec = FieldSpec(
+               narrow_to_wide(name.c_str()),
+               wlabel,
+               narrow_to_wide(default_val.c_str()),
+               258+m_fields.size()
+       );
+
+       if (name == "")
+       {
+               // spec field id to 0, this stops submit searching for a value that isn't there
+               Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+       }
+       else
+       {
+               spec.send = true;
+               gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+               Environment->setFocus(e);
+
+               if (type == "textarea")
+               {
+                       e->setMultiLine(true);
+                       e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
+               } else {
+                       irr::SEvent evt;
+                       evt.EventType            = EET_KEY_INPUT_EVENT;
+                       evt.KeyInput.Key         = KEY_END;
+                       evt.KeyInput.Char        = 0;
+                       evt.KeyInput.Control     = 0;
+                       evt.KeyInput.Shift       = 0;
+                       evt.KeyInput.PressedDown = true;
+                       e->OnEvent(evt);
+               }
+
+               if (label.length() > 1)
+               {
+                       rect.UpperLeftCorner.Y -= 15;
+                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
+                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+               }
+       }
+       if (m_use_gettext && (label.length() > 1))
+               delete[] wlabel;
+       m_fields.push_back(spec);
+}
+
+void GUIFormSpecMenu::parseField(parserData* data,std::string element,std::string type) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 3) {
+               parseSimpleField(data,parts);
+               return;
+       }
+
+       if (parts.size() == 5) {
+               parseTextArea(data,parts,type);
+               return;
+       }
+       errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 2) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string text = parts[1];
+
+               MY_CHECKPOS("label",0);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
+
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+
+               text = unescape_string(text);
+
+               wchar_t* wlabel = 0;
+
+               if (m_use_gettext)
+                       wlabel = wgettext(text.c_str());
+               else
+                       wlabel = (wchar_t*) narrow_to_wide(text.c_str()).c_str();
+
+               FieldSpec spec = FieldSpec(
+                       narrow_to_wide(""),
+                       wlabel,
+                       narrow_to_wide(""),
+                       258+m_fields.size()
+               );
+               Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+               m_fields.push_back(spec);
+               if (m_use_gettext)
+                       delete[] wlabel;
+               return;
+       }
+       errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 2) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string text = parts[1];
+
+               MY_CHECKPOS("vertlabel",1);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+15, pos.Y+300);
+
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+
+               text = unescape_string(text);
+               std::string label = "";
+
+               if (m_use_gettext) {
+                       const char* toset = gettext(text.c_str());
+
+                       text = std::string(toset);
+               }
+
+               for (unsigned int i=0; i < text.length(); i++) {
+                       label += text.c_str()[i];
+                       label += "\n";
+               }
+
+               FieldSpec spec = FieldSpec(
+                       narrow_to_wide(""),
+                       narrow_to_wide(label.c_str()),
+                       narrow_to_wide(""),
+                       258+m_fields.size()
+               );
+               gui::IGUIStaticText *t =
+                               Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+               t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,std::string type) {
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 5) || (parts.size() == 7)) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string image_name = parts[2];
+               std::string name = parts[3];
+               std::string label = parts[4];
+
+               MY_CHECKPOS("imagebutton",0);
+               MY_CHECKGEOM("imagebutton",1);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+               geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+
+               bool noclip = false;
+               bool drawborder = true;
+
+               if ((parts.size() == 7)) {
+                       if (parts[5] == "true")
+                               noclip = true;
+
+                       if (parts[6] == "false")
+                               drawborder = false;
+               }
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
+
+               label = unescape_string(label);
+
+               FieldSpec spec = FieldSpec(
+                       narrow_to_wide(name.c_str()),
+                       narrow_to_wide(label.c_str()),
+                       narrow_to_wide(image_name.c_str()),
+                       258+m_fields.size()
+               );
+               spec.ftype = f_Button;
+               if(type == "image_button_exit")
+                       spec.is_exit = true;
+
+               video::ITexture *texture = 0;
+               //if there's no gamedef specified try to get direct
+               //TODO check for possible texture leak
+               if (m_gamedef != 0)
+                       texture = m_gamedef->tsrc()->getTexture(image_name);
+               else {
+                       if (fs::PathExists(image_name)) {
+                               texture = Environment->getVideoDriver()->getTexture(image_name.c_str());
+                               m_Textures.push_back(texture);
+                       }
+               }
+
+               gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+               e->setUseAlphaChannel(true);
+               e->setImage(texture);
+               e->setPressedImage(texture);
+               e->setScaleImage(true);
+               e->setNotClipped(noclip);
+               e->setDrawBorder(drawborder);
+
+               m_fields.push_back(spec);
+               return;
+       }
+
+       errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 4) || (parts.size() == 6)) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = parts[1];
+               std::vector<std::string> buttons = split(parts[2],',');
+               std::string str_index = parts[3];
+               bool show_background = true;
+               bool show_border = true;
+               int tab_index = stoi(str_index) -1;
+
+               MY_CHECKPOS("tabheader",0);
+
+               if (parts.size() == 6) {
+                       if (parts[4] == "true")
+                               show_background = false;
+                       if (parts[5] == "false")
+                               show_border = false;
+               }
+
+               FieldSpec spec = FieldSpec(
+                       narrow_to_wide(name.c_str()),
+                       narrow_to_wide(""),
+                       narrow_to_wide(""),
+                       258+m_fields.size()
+               );
+
+               spec.ftype = f_TabHeader;
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+               v2s32 geom;
+               geom.X = data->screensize.Y;
+               geom.Y = 30;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               gui::IGUITabControl *e = Environment->addTabControl(rect,this,show_background,show_border,spec.fid);
+
+               e->setNotClipped(true);
+
+               for (unsigned int i=0; i< buttons.size(); i++) {
+                       wchar_t* wbutton = 0;
+
+                       if (m_use_gettext)
+                               wbutton = wgettext(buttons[i].c_str());
+                       else
+                               wbutton = (wchar_t*) narrow_to_wide(buttons[i].c_str()).c_str();
+
+                       e->addTab(wbutton,-1);
+
+                       if (m_use_gettext)
+                               delete[] wbutton;
+               }
+
+               if ((tab_index >= 0) &&
+                               (buttons.size() < INT_MAX) &&
+                               (tab_index < (int) buttons.size()))
+                       e->setActiveTab(tab_index);
+
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid TabHeader element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) {
+
+       if (m_gamedef == 0) {
+               errorstream<<"WARNING: invalid use of item_image_button with m_gamedef==0"<<std::endl;
+               return;
+       }
+
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 5) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string item_name = parts[2];
+               std::string name = parts[3];
+               std::string label = parts[4];
+
+               MY_CHECKPOS("itemimagebutton",0);
+               MY_CHECKGEOM("itemimagebutton",1);
+
+               v2s32 pos = padding;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+               geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               if(data->bp_set != 2)
+                       errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
+
+               IItemDefManager *idef = m_gamedef->idef();
+               ItemStack item;
+               item.deSerialize(item_name, idef);
+               video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
+               std::string tooltip = item.getDefinition(idef).description;
+
+               label = unescape_string(label);
+               FieldSpec spec = FieldSpec(
+                       narrow_to_wide(name.c_str()),
+                       narrow_to_wide(label.c_str()),
+                       narrow_to_wide(item_name.c_str()),
+                       258+m_fields.size()
+               );
+
+               gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+               e->setUseAlphaChannel(true);
+               e->setImage(texture);
+               e->setPressedImage(texture);
+               e->setScaleImage(true);
+               spec.ftype = f_Button;
+               rect+=data->basepos-padding;
+               spec.rect=rect;
+               if (tooltip!="")
+                       spec.tooltip=tooltip;
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseBox(parserData* data,std::string element) {
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 3) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string color_str = parts[2];
+
+               MY_CHECKPOS("box",0);
+               MY_CHECKGEOM("box",1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)spacing.X;
+               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+               bool valid_color = false;
+
+               irr::video::SColor color = getColor(color_str,valid_color);
+
+               if (valid_color) {
+                       BoxDrawSpec spec(pos,geom,color);
+
+                       m_boxes.push_back(spec);
+               }
+               else {
+                       errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'  INVALID COLOR"  << std::endl;
+               }
+               return;
+       }
+       errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseElement(parserData* data,std::string element) {
+       //some prechecks
+       if (element == "")
+               return;
+
+       std::vector<std::string> parts = split(element,'[', true);
+
+       if (parts.size() != 2)
+               return;
+
+       std::string type = trim(parts[0]);
+       std::string description = trim(parts[1]);
+
+       if ((type == "size") || (type == "invsize")){
+               parseSize(data,description);
+               return;
+       }
+
+       if (type == "list") {
+               parseList(data,description);
+               return;
+       }
+
+       if (type == "checkbox") {
+               parseCheckbox(data,description);
+               return;
+       }
+
+       if (type == "image") {
+               parseImage(data,description);
+               return;
+       }
+
+       if (type == "item_image") {
+               parseItemImage(data,description);
+               return;
+       }
+
+       if ((type == "button") || (type == "button_exit")) {
+               parseButton(data,description,type);
+               return;
+       }
+
+       if (type == "background") {
+               parseBackground(data,description);
+               return;
+       }
+
+       if (type == "textlist"){
+               parseTextList(data,description);
+               return;
+       }
+
+       if (type == "dropdown"){
+               parseDropDown(data,description);
+               return;
+       }
+
+       if (type == "pwdfield") {
+               parsePwdField(data,description);
+               return;
+       }
+
+       if ((type == "field") || (type == "textarea")){
+               parseField(data,description,type);
+               return;
+       }
+
+       if (type == "label") {
+               parseLabel(data,description);
+               return;
+       }
+
+       if (type == "vertlabel") {
+               parseVertLabel(data,description);
+               return;
+       }
 
-void GUIFormSpecMenu::removeChildren()
-{
-       const core::list<gui::IGUIElement*> &children = getChildren();
-       core::list<gui::IGUIElement*> children_copy;
-       for(core::list<gui::IGUIElement*>::ConstIterator
-                       i = children.begin(); i != children.end(); i++)
-       {
-               children_copy.push_back(*i);
+       if (type == "item_image_button") {
+               parseItemImageButton(data,description);
+               return;
        }
-       for(core::list<gui::IGUIElement*>::Iterator
-                       i = children_copy.begin();
-                       i != children_copy.end(); i++)
-       {
-               (*i)->remove();
+
+       if ((type == "image_button") || (type == "image_button_exit")) {
+               parseImageButton(data,description,type);
+               return;
        }
-       /*{
-               gui::IGUIElement *e = getElementFromId(256);
-               if(e != NULL)
-                       e->remove();
-       }*/
-       if(m_tooltip_element)
-       {
-               m_tooltip_element->remove();
-               m_tooltip_element = NULL;
+
+       if (type == "tabheader") {
+               parseTabHeader(data,description);
+               return;
+       }
+
+       if (type == "box") {
+               parseBox(data,description);
+               return;
        }
+
+       // Ignore others
+       infostream
+               << "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
+               <<std::endl;
 }
 
+
+
 void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 {
+       parserData mydata;
+
+       //preserve listboxes
+       for (unsigned int i = 0; i < m_listboxes.size(); i++) {
+               int selection = m_listboxes[i].second->getSelected();
+               if (selection != -1) {
+                       std::wstring listboxname = m_listboxes[i].first.fname;
+                       mydata.listbox_selections[listboxname] = selection;
+               }
+       }
+
        // Remove children
        removeChildren();
-       
-       v2s32 size(100,100);
-       s32 helptext_h = 15;
-       core::rect<s32> rect;
+
+       mydata.size= v2s32(100,100);
+       mydata.helptext_h = 15;
+       mydata.screensize = screensize;
 
        // Base position of contents of form
-       v2s32 basepos = getBasePos();
+       mydata.basepos = getBasePos();
+
        // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
        // Used to adjust form size automatically if needed
        // A proceed button is added if there is no size[] element
-       int bp_set = 0;
+       mydata.bp_set = 0;
+
        
        /* Convert m_init_draw_spec to m_inventorylists */
        
@@ -202,414 +1493,53 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        m_images.clear();
        m_backgrounds.clear();
        m_itemimages.clear();
+       m_listboxes.clear();
+       m_checkboxes.clear();
        m_fields.clear();
+       m_boxes.clear();
 
-       Strfnd f(m_formspec_string);
-       while(f.atend() == false)
-       {
-               std::string type = trim(f.next_esc("["));
-               if(type == "invsize" || type == "size")
-               {
-                       v2f invsize;
-                       invsize.X = stof(f.next_esc(","));
-                       if(type == "size")
-                       {
-                               invsize.Y = stof(f.next_esc("]"));
-                       }
-                       else{
-                               invsize.Y = stof(f.next_esc(";"));
-                               f.next_esc("]");
-                       }
-                       infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
-
-                       padding = v2s32(screensize.Y/40, screensize.Y/40);
-                       spacing = v2s32(screensize.Y/12, screensize.Y/13);
-                       imgsize = v2s32(screensize.Y/15, screensize.Y/15);
-                       size = v2s32(
-                               padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
-                               padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (helptext_h-5)
-                       );
-                       rect = core::rect<s32>(
-                                       screensize.X/2 - size.X/2,
-                                       screensize.Y/2 - size.Y/2,
-                                       screensize.X/2 + size.X/2,
-                                       screensize.Y/2 + size.Y/2
-                       );
-                       DesiredRect = rect;
-                       recalculateAbsolutePosition(false);
-                       basepos = getBasePos();
-                       bp_set = 2;
-               }
-               else if(type == "list")
-               {
-                       std::string name = f.next_esc(";");
-                       InventoryLocation loc;
-                       if(name == "context" || name == "current_name")
-                               loc = m_current_inventory_location;
-                       else
-                               loc.deSerialize(name);
-                       std::string listname = f.next_esc(";");
-                       v2s32 pos = basepos;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
-                       v2s32 geom;
-                       geom.X = stoi(f.next_esc(","));
-                       geom.Y = stoi(f.next_esc(";"));
-                       infostream<<"list inv="<<name<<", listname="<<listname
-                                       <<", pos=("<<pos.X<<","<<pos.Y<<")"
-                                       <<", geom=("<<geom.X<<","<<geom.Y<<")"
-                                       <<std::endl;
-                       std::string start_i_s = f.next_esc("]");
-                       s32 start_i = 0;
-                       if(start_i_s != "")
-                               start_i = stoi(start_i_s);
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
-                       m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
-               }
-               else if(type == "image")
-               {
-                       v2s32 pos = basepos;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
-                       v2s32 geom;
-                       geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
-                       geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
-                       std::string name = f.next_esc("]");
-                       infostream<<"image name="<<name
-                                       <<", pos=("<<pos.X<<","<<pos.Y<<")"
-                                       <<", geom=("<<geom.X<<","<<geom.Y<<")"
-                                       <<std::endl;
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
-                       m_images.push_back(ImageDrawSpec(name, pos, geom));
-               }
-               else if(type == "item_image")
-               {
-                       v2s32 pos = basepos;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
-                       v2s32 geom;
-                       geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
-                       geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
-                       std::string name = f.next_esc("]");
-                       infostream<<"item name="<<name
-                                       <<", pos=("<<pos.X<<","<<pos.Y<<")"
-                                       <<", geom=("<<geom.X<<","<<geom.Y<<")"
-                                       <<std::endl;
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
-                       m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
-               }
-               else if(type == "background")
-               {
-                       v2s32 pos = basepos;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
-                       v2s32 geom;
-                       geom.X = stof(f.next_esc(",")) * (float)spacing.X;
-                       geom.Y = stof(f.next_esc(";")) * (float)spacing.Y;
-                       std::string name = f.next_esc("]");
-                       infostream<<"image name="<<name
-                                       <<", pos=("<<pos.X<<","<<pos.Y<<")"
-                                       <<", geom=("<<geom.X<<","<<geom.Y<<")"
-                                       <<std::endl;
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
-                       m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
-               }
-               else if(type == "field" || type == "textarea")
-               {
-                       std::string fname = f.next_esc(";");
-                       std::string flabel = f.next_esc(";");
-
-                       if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
-                       {
-                               if (type == "textarea")
-                                       errorstream<<"WARNING: Textarea connot be unpositioned"<<std::endl;
-
-                               if(!bp_set)
-                               {
-                                       rect = core::rect<s32>(
-                                               screensize.X/2 - 580/2,
-                                               screensize.Y/2 - 300/2,
-                                               screensize.X/2 + 580/2,
-                                               screensize.Y/2 + 300/2
-                                       );
-                                       DesiredRect = rect;
-                                       recalculateAbsolutePosition(false);
-                                       basepos = getBasePos();
-                                       bp_set = 1;
-                               }
-                               else if(bp_set == 2)
-                                       errorstream<<"WARNING: invalid use of unpositioned "<<type<<" in inventory"<<std::endl;
-
-                               v2s32 pos = basepos;
-                               pos.Y = ((m_fields.size()+2)*60);
-                               v2s32 size = DesiredRect.getSize();
-                               rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
-                       }
-                       else
-                       {
-                               v2s32 pos;
-                               pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X;
-                               pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
-                               v2s32 geom;
-                               geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
-                               if (type == "textarea")
-                               {
-                                       geom.Y = (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
-                                       pos.Y += 15;
-                               }
-                               else
-                               {
-                                       pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
-                                       pos.Y -= 15;
-                                       geom.Y = 30;
-                               }
-
-                               rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
-
-                               
-                               fname = f.next_esc(";");
-                               flabel = f.next_esc(";");
-                               if(bp_set != 2)
-                                       errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
-                               
-                       }
-
-                       std::string odefault = f.next_esc("]");
-                       std::string fdefault;
-
-                       // fdefault may contain a variable reference, which
-                       // needs to be resolved from the node metadata
-                       if(m_form_src)
-                               fdefault = m_form_src->resolveText(odefault);
-                       else
-                               fdefault = odefault;
-
-                       fdefault = unescape_string(fdefault);
-                       flabel = unescape_string(flabel);
-
-                       FieldSpec spec = FieldSpec(
-                               narrow_to_wide(fname.c_str()),
-                               narrow_to_wide(flabel.c_str()),
-                               narrow_to_wide(fdefault.c_str()),
-                               258+m_fields.size()
-                       );
-
-                       // three cases: field name and no label, label and field, label name and no field
-                       gui::IGUIEditBox *e;
-                       if (fname == "")
-                       {
-                               // spec field id to 0, this stops submit searching for a value that isn't there
-                               Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
-                       }
-                       else
-                       {
-                               spec.send = true;
-                               e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
-                               Environment->setFocus(e);
-
-                               if (type == "textarea")
-                               {
-                                       e->setMultiLine(true);
-                                       e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
-                               } else {
-                                       irr::SEvent evt;
-                                       evt.EventType = EET_KEY_INPUT_EVENT;
-                                       evt.KeyInput.Key = KEY_END;
-                                       evt.KeyInput.PressedDown = true;
-                                       evt.KeyInput.Char = 0;
-                                       evt.KeyInput.Control = 0;
-                                       evt.KeyInput.Shift = 0;
-                                       e->OnEvent(evt);
-                               }
-
-                               if (flabel != "")
-                               {
-                                       rect.UpperLeftCorner.Y -= 15;
-                                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
-                                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
-                               }
-                       }
-
-                       m_fields.push_back(spec);
-               }
-               else if(type == "label")
-               {
-                       v2s32 pos = padding;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 
-                       rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
-                       
-                       std::string flabel = f.next_esc("]");
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+       std::vector<std::string> elements = split(m_formspec_string,']',true);
 
-                       flabel = unescape_string(flabel);
-
-                       FieldSpec spec = FieldSpec(
-                               narrow_to_wide(""),
-                               narrow_to_wide(flabel.c_str()),
-                               narrow_to_wide(""),
-                               258+m_fields.size()
-                       );
-                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
-                       m_fields.push_back(spec);
-               }
-               else if(type == "button" || type == "button_exit")
-               {
-                       v2s32 pos = padding;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
-                       v2s32 geom;
-                       geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-                       pos.Y += (stof(f.next_esc(";")) * (float)imgsize.Y)/2;
-
-                       rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
-                       
-                       std::string fname = f.next_esc(";");
-                       std::string flabel = f.next_esc("]");
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
-
-                       flabel = unescape_string(flabel);
-
-                       FieldSpec spec = FieldSpec(
-                               narrow_to_wide(fname.c_str()),
-                               narrow_to_wide(flabel.c_str()),
-                               narrow_to_wide(""),
-                               258+m_fields.size()
-                       );
-                       spec.is_button = true;
-                       if(type == "button_exit")
-                               spec.is_exit = true;
-                       Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
-                       m_fields.push_back(spec);
-               }
-               else if(type == "image_button" || type == "image_button_exit")
-               {
-                       v2s32 pos = padding;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
-                       v2s32 geom;
-                       geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-                       geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
-
-                       rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-                       
-                       std::string fimage = f.next_esc(";");
-                       std::string fname = f.next_esc(";");
-                       std::string flabel = f.next_esc("]");
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
-
-                       flabel = unescape_string(flabel);
-
-                       FieldSpec spec = FieldSpec(
-                               narrow_to_wide(fname.c_str()),
-                               narrow_to_wide(flabel.c_str()),
-                               narrow_to_wide(fimage.c_str()),
-                               258+m_fields.size()
-                       );
-                       spec.is_button = true;
-                       if(type == "image_button_exit")
-                               spec.is_exit = true;
-                       
-                       video::ITexture *texture = m_gamedef->tsrc()->getTexture(fimage);
-                       gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
-                       e->setUseAlphaChannel(true);
-                       e->setImage(texture);
-                       e->setPressedImage(texture);
-                       e->setScaleImage(true);
-                       
-                       m_fields.push_back(spec);
-               }
-               else if(type == "item_image_button")
-               {
-                       v2s32 pos = padding;
-                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
-                       v2s32 geom;
-                       geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-                       geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
-                       rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);               
-                       std::string fimage = f.next_esc(";");
-                       std::string fname = f.next_esc(";");
-                       std::string flabel = f.next_esc("]");
-                       if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;           
-                       IItemDefManager *idef = m_gamedef->idef();
-                       ItemStack item;
-                       item.deSerialize(fimage, idef);
-                       video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
-                       std::string tooltip = item.getDefinition(idef).description;
-                       flabel = unescape_string(flabel);
-                       FieldSpec spec = FieldSpec(
-                               narrow_to_wide(fname.c_str()),
-                               narrow_to_wide(flabel.c_str()),
-                               narrow_to_wide(fimage.c_str()),
-                               258+m_fields.size()
-                       );
-                       gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
-                       e->setUseAlphaChannel(true);
-                       e->setImage(texture);
-                       e->setPressedImage(texture);
-                       e->setScaleImage(true);
-                       spec.is_button = true;
-                       rect+=basepos-padding;
-                       spec.rect=rect;         
-                       if (tooltip!="")
-                               spec.tooltip=tooltip;
-                       m_fields.push_back(spec);
-               }
-               else
-               {
-                       // Ignore others
-                       std::string ts = f.next_esc("]");
-                       infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
-                                       <<std::endl;
-               }
+       for (unsigned int i=0;i< elements.size();i++) {
+               parseElement(&mydata,elements[i]);
        }
 
        // If there's inventory, put the usage string at the bottom
        if (m_inventorylists.size())
        {
                changeCtype("");
-               core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
-               rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
-                               size.Y-rect.getHeight()-5);
+               core::rect<s32> rect(0, 0, mydata.size.X-padding.X*2, mydata.helptext_h);
+               rect = rect + v2s32((mydata.size.X/2 - mydata.rect.getWidth()/2) +5,
+                               mydata.size.Y-5-mydata.helptext_h);
                const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
                Environment->addStaticText(text, rect, false, true, this, 256);
                delete[] text;
                changeCtype("C");
        }
        // If there's fields, add a Proceed button
-       if (m_fields.size() && bp_set != 2) 
+       if (m_fields.size() && mydata.bp_set != 2)
        {
                // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
-               rect = core::rect<s32>(
-                       screensize.X/2 - 580/2,
-                       screensize.Y/2 - 300/2,
-                       screensize.X/2 + 580/2,
-                       screensize.Y/2 + 240/2+(m_fields.size()*60)
+               mydata.rect = core::rect<s32>(
+                               mydata.screensize.X/2 - 580/2,
+                               mydata.screensize.Y/2 - 300/2,
+                               mydata.screensize.X/2 + 580/2,
+                               mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
                );
-               DesiredRect = rect;
+               DesiredRect = mydata.rect;
                recalculateAbsolutePosition(false);
-               basepos = getBasePos();
+               mydata.basepos = getBasePos();
 
                changeCtype("");
                {
-                       v2s32 pos = basepos;
+                       v2s32 pos = mydata.basepos;
                        pos.Y = ((m_fields.size()+2)*60);
 
                        v2s32 size = DesiredRect.getSize();
-                       rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
+                       mydata.rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
                        wchar_t* text = wgettext("Proceed");
-                       Environment->addButton(rect, this, 257, text);
+                       Environment->addButton(mydata.rect, this, 257, text);
                        delete[] text;
                }
                changeCtype("C");
@@ -684,8 +1614,8 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
        
        for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
        {
-               u32 item_i = i + s.start_item_i;
-               if(item_i >= ilist->getSize())
+               s32 item_i = i + s.start_item_i;
+               if(item_i >= (s32) ilist->getSize())
                        break;
                s32 x = (i%s.geom.X) * spacing.X;
                s32 y = (i/s.geom.X) * spacing.Y;
@@ -804,38 +1734,88 @@ void GUIFormSpecMenu::drawMenu()
        for(u32 i=0; i<m_backgrounds.size(); i++)
        {
                const ImageDrawSpec &spec = m_backgrounds[i];
-               video::ITexture *texture =
-                               m_gamedef->tsrc()->getTexture(spec.name);
-               // Image size on screen
-               core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
-               // Image rectangle on screen
-               core::rect<s32> rect = imgrect + spec.pos;
-               const video::SColor color(255,255,255,255);
-               const video::SColor colors[] = {color,color,color,color};
-               driver->draw2DImage(texture, rect,
-                       core::rect<s32>(core::position2d<s32>(0,0),
-                                       core::dimension2di(texture->getOriginalSize())),
-                       NULL/*&AbsoluteClippingRect*/, colors, true);
+               video::ITexture *texture = 0;
+
+               if (m_gamedef != 0)
+                       texture = m_gamedef->tsrc()->getTexture(spec.name);
+               else
+               {
+                       texture = driver->getTexture(spec.name.c_str());
+                       m_Textures.push_back(texture);
+               }
+
+               if (texture != 0) {
+                       // Image size on screen
+                       core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
+                       // Image rectangle on screen
+                       core::rect<s32> rect = imgrect + spec.pos;
+                       const video::SColor color(255,255,255,255);
+                       const video::SColor colors[] = {color,color,color,color};
+                       driver->draw2DImage(texture, rect,
+                               core::rect<s32>(core::position2d<s32>(0,0),
+                                               core::dimension2di(texture->getOriginalSize())),
+                               NULL/*&AbsoluteClippingRect*/, colors, true);
+               }
+               else {
+                       errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
+                       errorstream << "\t" << spec.name << std::endl;
+               }
        }
        
+       /*
+               Draw Boxes
+       */
+       for(u32 i=0; i<m_boxes.size(); i++)
+       {
+               const BoxDrawSpec &spec = m_boxes[i];
+
+               irr::video::SColor todraw = spec.color;
+
+               todraw.setAlpha(140);
+
+               core::rect<s32> rect(spec.pos.X,spec.pos.Y,
+                                                       spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
+
+               driver->draw2DRectangle(todraw, rect, 0);
+       }
        /*
                Draw images
        */
        for(u32 i=0; i<m_images.size(); i++)
        {
                const ImageDrawSpec &spec = m_images[i];
-               video::ITexture *texture =
-                               m_gamedef->tsrc()->getTexture(spec.name);
-               // Image size on screen
-               core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
-               // Image rectangle on screen
-               core::rect<s32> rect = imgrect + spec.pos;
-               const video::SColor color(255,255,255,255);
-               const video::SColor colors[] = {color,color,color,color};
-               driver->draw2DImage(texture, rect,
-                       core::rect<s32>(core::position2d<s32>(0,0),
-                                       core::dimension2di(texture->getOriginalSize())),
-                       NULL/*&AbsoluteClippingRect*/, colors, true);
+               video::ITexture *texture = 0;
+
+               if (m_gamedef != 0)
+                       texture = m_gamedef->tsrc()->getTexture(spec.name);
+               else
+               {
+                       texture = driver->getTexture(spec.name.c_str());
+                       m_Textures.push_back(texture);
+               }
+               if (texture != 0) {
+                       const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
+                       // Image size on screen
+                       core::rect<s32> imgrect;
+
+                       if (spec.scale)
+                               imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
+                       else {
+
+                               imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
+                       }
+                       // Image rectangle on screen
+                       core::rect<s32> rect = imgrect + spec.pos;
+                       const video::SColor color(255,255,255,255);
+                       const video::SColor colors[] = {color,color,color,color};
+                       driver->draw2DImage(texture, rect,
+                               core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
+                               NULL/*&AbsoluteClippingRect*/, colors, true);
+               }
+               else {
+                       errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
+                       errorstream << "\t" << spec.name << std::endl;
+               }
        }
        
        /*
@@ -843,6 +1823,9 @@ void GUIFormSpecMenu::drawMenu()
        */
        for(u32 i=0; i<m_itemimages.size(); i++)
        {
+               if (m_gamedef == 0)
+                       break;
+
                const ImageDrawSpec &spec = m_itemimages[i];
                IItemDefManager *idef = m_gamedef->idef();
                ItemStack item;
@@ -1025,24 +2008,77 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
        return ItemStack();
 }
 
-void GUIFormSpecMenu::acceptInput()
+void GUIFormSpecMenu::acceptInput(int eventtype)
 {
        if(m_text_dst)
        {
                std::map<std::string, std::string> fields;
-               gui::IGUIElement *e;
+
                for(u32 i=0; i<m_fields.size(); i++)
                {
                        const FieldSpec &s = m_fields[i];
                        if(s.send) 
                        {
-                               if(s.is_button)
+                               if(s.ftype == f_Button)
                                {
                                        fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
                                }
+                               else if(s.ftype == f_ListBox) {
+                                       std::stringstream ss;
+                                       if (eventtype == gui::EGET_LISTBOX_CHANGED) {
+                                               ss << "CHG:";
+                                       }
+                                       else {
+                                               ss << "DCL:";
+                                       }
+                                       ss << (getListboxIndex(wide_to_narrow(s.fname.c_str()))+1);
+                                       fields[wide_to_narrow(s.fname.c_str())] = ss.str();
+                               }
+                               else if(s.ftype == f_DropDown) {
+                                       // no dynamic cast possible due to some distributions shipped
+                                       // without rtti support in irrlicht
+                                       IGUIElement * element = getElementFromId(s.fid);
+                                       gui::IGUIComboBox *e = NULL;
+                                       if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
+                                               e = static_cast<gui::IGUIComboBox*>(element);
+                                       }
+                                       fields[wide_to_narrow(s.fname.c_str())] =
+                                                       wide_to_narrow(e->getItem(e->getSelected()));
+                               }
+                               else if (s.ftype == f_TabHeader) {
+                                       // no dynamic cast possible due to some distributions shipped
+                                       // without rtti support in irrlicht
+                                       IGUIElement * element = getElementFromId(s.fid);
+                                       gui::IGUITabControl *e = NULL;
+                                       if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
+                                               e = static_cast<gui::IGUITabControl*>(element);
+                                       }
+
+                                       if (e != 0) {
+                                               std::stringstream ss;
+                                               ss << (e->getActiveTab() +1);
+                                               fields[wide_to_narrow(s.fname.c_str())] = ss.str();
+                                       }
+                               }
+                               else if (s.ftype == f_CheckBox) {
+                                       // no dynamic cast possible due to some distributions shipped
+                                       // without rtti support in irrlicht
+                                       IGUIElement * element = getElementFromId(s.fid);
+                                       gui::IGUICheckBox *e = NULL;
+                                       if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
+                                               e = static_cast<gui::IGUICheckBox*>(element);
+                                       }
+
+                                       if (e != 0) {
+                                               if (e->isChecked())
+                                                       fields[wide_to_narrow(s.fname.c_str())] = "true";
+                                               else
+                                                       fields[wide_to_narrow(s.fname.c_str())] = "false";
+                                       }
+                               }
                                else
                                {
-                                       e = getElementFromId(s.fid);
+                                       IGUIElement* e = getElementFromId(s.fid);
                                        if(e != NULL)
                                        {
                                                fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
@@ -1050,6 +2086,7 @@ void GUIFormSpecMenu::acceptInput()
                                }
                        }
                }
+
                m_text_dst->gotText(fields);
        }
 }
@@ -1062,13 +2099,20 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                if (event.KeyInput.PressedDown && (kp == EscapeKey ||
                        kp == getKeySetting("keymap_inventory")))
                {
-                       quitMenu();
+                       if (m_allowclose)
+                               quitMenu();
+                       else
+                               m_text_dst->gotText(narrow_to_wide("MenuQuit"));
                        return true;
                }
                if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
                {
                        acceptInput();
-                       quitMenu();
+
+                       if (m_allowclose)
+                               quitMenu();
+                       else
+                               m_text_dst->gotText(narrow_to_wide("KeyEnter"));
                        return true;
                }
        }
@@ -1360,6 +2404,27 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
        }
        if(event.EventType==EET_GUI_EVENT)
        {
+
+               if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED
+                                               && isVisible())
+               {
+                       // find the element that was clicked
+                       for(u32 i=0; i<m_fields.size(); i++)
+                       {
+                               FieldSpec &s = m_fields[i];
+                               // if its a button, set the send field so
+                               // lua knows which button was pressed
+                               if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID()))
+                               {
+                                       s.send = true;
+                                       acceptInput();
+                                       s.send = false;
+                                       // Restore focus to the full form
+                                       Environment->setFocus(this);
+                                       return true;
+                               }
+                       }
+               }
                if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
                                && isVisible())
                {
@@ -1371,28 +2436,37 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                                return true;
                        }
                }
-               if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
+               if((event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED) ||
+                               (event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED))
                {
-                       switch(event.GUIEvent.Caller->getID())
-                       {
-                       case 257:
+                       unsigned int btn_id = event.GUIEvent.Caller->getID();
+
+                       if (btn_id == 257) {
                                acceptInput();
-                               quitMenu();
+                               if (m_allowclose)
+                                       quitMenu();
+                               else
+                                       m_text_dst->gotText(narrow_to_wide("ExitButton"));
                                // quitMenu deallocates menu
                                return true;
                        }
+
                        // find the element that was clicked
                        for(u32 i=0; i<m_fields.size(); i++)
                        {
                                FieldSpec &s = m_fields[i];
                                // if its a button, set the send field so 
                                // lua knows which button was pressed
-                               if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
+                               if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
+                                               (s.fid == event.GUIEvent.Caller->getID()))
                                {
                                        s.send = true;
                                        acceptInput();
                                        if(s.is_exit){
-                                               quitMenu();
+                                               if (m_allowclose)
+                                                       quitMenu();
+                                               else
+                                                       m_text_dst->gotText(narrow_to_wide("ExitButton"));
                                                return true;
                                        }else{
                                                s.send = false;
@@ -1408,13 +2482,103 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                        if(event.GUIEvent.Caller->getID() > 257)
                        {
                                acceptInput();
-                               quitMenu();
+                               if (m_allowclose)
+                                       quitMenu();
+                               else
+                                       m_text_dst->gotText(narrow_to_wide("EditBoxEnter"));
                                // quitMenu deallocates menu
                                return true;
                        }
                }
+
+               if((event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN) ||
+                       (event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED))
+               {
+                       int current_id = event.GUIEvent.Caller->getID();
+                       if(current_id > 257)
+                       {
+                               // find the element that was clicked
+                               for(u32 i=0; i<m_fields.size(); i++)
+                               {
+                                       FieldSpec &s = m_fields[i];
+                                       // if its a button, set the send field so
+                                       // lua knows which button was pressed
+                                       if ((s.ftype == f_ListBox) && (s.fid == current_id))
+                                       {
+                                               s.send = true;
+                                               acceptInput(event.GUIEvent.EventType);
+                                               s.send=false;
+                                               // Restore focus to the full form
+                                               Environment->setFocus(this);
+                                       }
+                               }
+                               return true;
+                       }
+               }
        }
 
        return Parent ? Parent->OnEvent(event) : false;
 }
 
+irr::video::SColor GUIFormSpecMenu::getColor(std::string color,bool& valid_color) {
+
+       if (color == "YLW") {
+               valid_color = true;
+               return irr::video::SColor(255,255,255,0);
+       }
+
+       if (color == "GRN") {
+               valid_color = true;
+               return irr::video::SColor(255,34,249,34);
+       }
+
+       if (color == "LIM") {
+               valid_color = true;
+               return irr::video::SColor(255,50,205,50);
+       }
+
+       if (color == "RED") {
+               valid_color = true;
+               return irr::video::SColor(255,255,0,0);
+       }
+
+       if (color == "ORN") {
+               valid_color = true;
+               return irr::video::SColor(255,255,140,0);
+       }
+
+       if (color == "BLU") {
+               valid_color = true;
+               return irr::video::SColor(255,0,0,255);
+       }
+
+       if (color == "CYN") {
+               valid_color = true;
+               return irr::video::SColor(255,0,255,255);
+       }
+
+       if (color == "BLK") {
+               valid_color = true;
+               return irr::video::SColor(255,0,0,0);
+       }
+
+       if (color == "BRN") {
+               valid_color = true;
+               return irr::video::SColor(255,139,69,19);
+       }
+
+       if (color == "WHT") {
+               valid_color = true;
+               return irr::video::SColor(255,255,255,255);
+       }
+
+       if (color == "GRY") {
+               valid_color = true;
+               return irr::video::SColor(255,205,201,201);
+       }
+
+       valid_color = false;
+
+       return irr::video::SColor(0,0,0,0);
+}
+
index ae985adde157330cdcecab1a285f640c282ac8cf..f5a273668d65a9abb78854734eddcbb04b4cfd85 100644 (file)
@@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef GUIINVENTORYMENU_HEADER
 #define GUIINVENTORYMENU_HEADER
 
+#include <utility>
+
 #include "irrlichttypes_extrabloated.h"
 #include "inventory.h"
 #include "inventorymanager.h"
@@ -29,6 +31,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class IGameDef;
 class InventoryManager;
 
+typedef enum {
+       f_Button,
+       f_ListBox,
+       f_TabHeader,
+       f_CheckBox,
+       f_DropDown,
+       f_Unknown
+} FormspecFieldType;
+
 struct TextDest
 {
        virtual ~TextDest() {};
@@ -113,10 +124,19 @@ class GUIFormSpecMenu : public GUIModalMenu
                        pos(a_pos),
                        geom(a_geom)
                {
+                       scale = true;
+               }
+               ImageDrawSpec(const std::string &a_name,
+                               v2s32 a_pos):
+                       name(a_name),
+                       pos(a_pos)
+               {
+                       scale = false;
                }
                std::string name;
                v2s32 pos;
                v2s32 geom;
+               bool scale;
        };
        
        struct FieldSpec
@@ -131,7 +151,7 @@ class GUIFormSpecMenu : public GUIModalMenu
                        fid(id)
                {
                        send = false;
-                       is_button = false;
+                       ftype = f_Unknown;
                        is_exit = false;
                        tooltip="";
                }
@@ -140,12 +160,24 @@ class GUIFormSpecMenu : public GUIModalMenu
                std::wstring fdefault;
                int fid;
                bool send;
-               bool is_button;
+               FormspecFieldType ftype;
                bool is_exit;
                core::rect<s32> rect;
                std::string tooltip;
        };
 
+       struct BoxDrawSpec {
+               BoxDrawSpec(v2s32 a_pos, v2s32 a_geom,irr::video::SColor a_color):
+                       pos(a_pos),
+                       geom(a_geom),
+                       color(a_color)
+               {
+               }
+               v2s32 pos;
+               v2s32 geom;
+               irr::video::SColor color;
+       };
+
 public:
        GUIFormSpecMenu(irr::IrrlichtDevice* dev,
                        gui::IGUIElement* parent, s32 id,
@@ -153,6 +185,7 @@ class GUIFormSpecMenu : public GUIModalMenu
                        InventoryManager *invmgr,
                        IGameDef *gamedef
                        );
+
        ~GUIFormSpecMenu();
 
        void setFormSpec(const std::string &formspec_string,
@@ -175,6 +208,20 @@ class GUIFormSpecMenu : public GUIModalMenu
                m_text_dst = text_dst;
        }
 
+       void allowClose(bool value)
+       {
+               m_allowclose = value;
+       }
+
+       void useGettext(bool value) {
+               m_use_gettext = true;
+       }
+
+       void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0)) {
+               m_lock = lock;
+               m_lockscreensize = basescreensize;
+       }
+
        void removeChildren();
        /*
                Remove and re-add (or reposition) stuff
@@ -188,18 +235,21 @@ class GUIFormSpecMenu : public GUIModalMenu
        void updateSelectedItem();
        ItemStack verifySelectedItem();
 
-       void acceptInput();
+       void acceptInput(int evttype=-1);
        bool OnEvent(const SEvent& event);
        
+       int getListboxIndex(std::string listboxname);
+
 protected:
        v2s32 getBasePos() const
        {
-               return padding + AbsoluteRect.UpperLeftCorner;
+                       return padding + offset + AbsoluteRect.UpperLeftCorner;
        }
 
        v2s32 padding;
        v2s32 spacing;
        v2s32 imgsize;
+       v2s32 offset;
        
        irr::IrrlichtDevice* m_device;
        InventoryManager *m_invmgr;
@@ -214,7 +264,10 @@ class GUIFormSpecMenu : public GUIModalMenu
        std::vector<ImageDrawSpec> m_backgrounds;
        std::vector<ImageDrawSpec> m_images;
        std::vector<ImageDrawSpec> m_itemimages;
+       std::vector<BoxDrawSpec> m_boxes;
        std::vector<FieldSpec> m_fields;
+       std::vector<std::pair<FieldSpec,gui::IGUIListBox*> > m_listboxes;
+       std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
 
        ItemSpec *m_selected_item;
        u32 m_selected_amount;
@@ -228,6 +281,74 @@ class GUIFormSpecMenu : public GUIModalMenu
 
        v2s32 m_pointer;
        gui::IGUIStaticText *m_tooltip_element;
+
+       bool m_allowclose;
+       bool m_use_gettext;
+       bool m_lock;
+       v2u32 m_lockscreensize;
+private:
+       typedef struct {
+               v2s32 size;
+               s32 helptext_h;
+               core::rect<s32> rect;
+               v2s32 basepos;
+               int bp_set;
+               v2u32 screensize;
+               std::map<std::wstring,int> listbox_selections;
+       } parserData;
+
+       std::vector<video::ITexture *> m_Textures;
+
+       void parseElement(parserData* data,std::string element);
+
+       void parseSize(parserData* data,std::string element);
+       void parseList(parserData* data,std::string element);
+       void parseCheckbox(parserData* data,std::string element);
+       void parseImage(parserData* data,std::string element);
+       void parseItemImage(parserData* data,std::string element);
+       void parseButton(parserData* data,std::string element,std::string typ);
+       void parseBackground(parserData* data,std::string element);
+       void parseTextList(parserData* data,std::string element);
+       void parseDropDown(parserData* data,std::string element);
+       void parsePwdField(parserData* data,std::string element);
+       void parseField(parserData* data,std::string element,std::string type);
+       void parseSimpleField(parserData* data,std::vector<std::string> &parts);
+       void parseTextArea(parserData* data,std::vector<std::string>& parts,std::string type);
+       void parseLabel(parserData* data,std::string element);
+       void parseVertLabel(parserData* data,std::string element);
+       void parseImageButton(parserData* data,std::string element,std::string type);
+       void parseItemImageButton(parserData* data,std::string element);
+       void parseTabHeader(parserData* data,std::string element);
+       void parseBox(parserData* data,std::string element);
+
+       irr::video::SColor getColor(std::string color,bool& valid_color);
+};
+
+class FormspecFormSource: public IFormSource
+{
+public:
+       FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
+       {
+               m_formspec = formspec;
+               m_game_formspec = game_formspec;
+       }
+
+       ~FormspecFormSource()
+       {
+               *m_game_formspec = 0;
+       }
+
+       void setForm(std::string formspec) {
+               m_formspec = formspec;
+       }
+
+       std::string getForm()
+       {
+               return m_formspec;
+       }
+
+       std::string m_formspec;
+       FormspecFormSource** m_game_formspec;
 };
 
 #endif
diff --git a/src/guiLuaApi.cpp b/src/guiLuaApi.cpp
new file mode 100644 (file)
index 0000000..bc02c06
--- /dev/null
@@ -0,0 +1,1067 @@
+/*
+Minetest
+Copyright (C) 2013 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+extern "C" {
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+#include "porting.h"
+#include "guiMainMenu.h"
+#include "subgame.h"
+#include "guiKeyChangeMenu.h"
+#include "guiFileSelectMenu.h"
+#include "main.h"
+#include "settings.h"
+#include "filesys.h"
+#include "convert_json.h"
+
+
+#include "IFileArchive.h"
+#include "IFileSystem.h"
+
+#include "guiLuaApi.h"
+#include "guiEngine.h"
+
+#define API_FCT(name) registerFunction(L,#name,l_##name,top)
+
+void guiLuaApi::initialize(lua_State* L,GUIEngine* engine)
+{
+       lua_pushlightuserdata(L, engine);
+       lua_setfield(L, LUA_REGISTRYINDEX, "engine");
+
+       lua_pushstring(L, DIR_DELIM);
+       lua_setglobal(L, "DIR_DELIM");
+
+       lua_newtable(L);
+       lua_setglobal(L, "gamedata");
+
+       lua_newtable(L);
+       lua_setglobal(L, "engine");
+
+       lua_getglobal(L, "engine");
+       int top = lua_gettop(L);
+
+       bool retval = true;
+
+       //add api functions
+       retval &= API_FCT(update_formspec);
+       retval &= API_FCT(set_clouds);
+       retval &= API_FCT(get_textlist_index);
+       retval &= API_FCT(get_worlds);
+       retval &= API_FCT(get_games);
+       retval &= API_FCT(start);
+       retval &= API_FCT(close);
+       retval &= API_FCT(get_favorites);
+       retval &= API_FCT(show_keys_menu);
+       retval &= API_FCT(setting_set);
+       retval &= API_FCT(setting_get);
+       retval &= API_FCT(setting_getbool);
+       retval &= API_FCT(setting_setbool);
+       retval &= API_FCT(create_world);
+       retval &= API_FCT(delete_world);
+       retval &= API_FCT(delete_favorite);
+       retval &= API_FCT(set_background);
+       retval &= API_FCT(set_topleft_text);
+       retval &= API_FCT(get_modpath);
+       retval &= API_FCT(get_gamepath);
+       retval &= API_FCT(get_dirlist);
+       retval &= API_FCT(create_dir);
+       retval &= API_FCT(delete_dir);
+       retval &= API_FCT(copy_dir);
+       retval &= API_FCT(extract_zip);
+       retval &= API_FCT(get_scriptdir);
+       retval &= API_FCT(show_file_open_dialog);
+       retval &= API_FCT(get_version);
+       retval &= API_FCT(download_file);
+       retval &= API_FCT(get_modstore_details);
+       retval &= API_FCT(get_modstore_list);
+
+       if (!retval) {
+               //TODO show error
+       }
+}
+
+/******************************************************************************/
+bool guiLuaApi::registerFunction(      lua_State* L,
+                                                                       const char* name,
+                                                                       lua_CFunction fct,
+                                                                       int top
+                                                                       )
+{
+       lua_pushstring(L,name);
+       lua_pushcfunction(L,fct);
+       lua_settable(L, top);
+
+       return true;
+}
+
+/******************************************************************************/
+GUIEngine* guiLuaApi::get_engine(lua_State *L)
+{
+       // Get server from registry
+       lua_getfield(L, LUA_REGISTRYINDEX, "engine");
+       GUIEngine* sapi_ptr = (GUIEngine*) lua_touserdata(L, -1);
+       lua_pop(L, 1);
+       return sapi_ptr;
+}
+
+/******************************************************************************/
+std::string guiLuaApi::getTextData(lua_State *L, std::string name)
+{
+       lua_getglobal(L, "gamedata");
+
+       lua_getfield(L, -1, name.c_str());
+
+       if(lua_isnil(L, -1))
+               return "";
+
+       return luaL_checkstring(L, -1);
+}
+
+/******************************************************************************/
+int guiLuaApi::getIntegerData(lua_State *L, std::string name,bool& valid)
+{
+       lua_getglobal(L, "gamedata");
+
+       lua_getfield(L, -1, name.c_str());
+
+       if(lua_isnil(L, -1)) {
+               valid = false;
+               return -1;
+               }
+
+       valid = true;
+       return luaL_checkinteger(L, -1);
+}
+
+/******************************************************************************/
+int guiLuaApi::getBoolData(lua_State *L, std::string name,bool& valid)
+{
+       lua_getglobal(L, "gamedata");
+
+       lua_getfield(L, -1, name.c_str());
+
+       if(lua_isnil(L, -1)) {
+               valid = false;
+               return false;
+               }
+
+       valid = true;
+       return lua_toboolean(L, -1);
+}
+
+/******************************************************************************/
+int guiLuaApi::l_update_formspec(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       if (engine->m_startgame)
+               return 0;
+
+       //read formspec
+       std::string formspec(luaL_checkstring(L, 1));
+
+       if (engine->m_formspecgui != 0) {
+               engine->m_formspecgui->setForm(formspec);
+       }
+
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_start(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       //update c++ gamedata from lua table
+
+       bool valid = false;
+
+
+       engine->m_data->selected_world  = getIntegerData(L, "selected_world",valid) -1;
+       engine->m_data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
+       engine->m_data->name                    = getTextData(L,"playername");
+       engine->m_data->password                = getTextData(L,"password");
+       engine->m_data->address                 = getTextData(L,"address");
+       engine->m_data->port                    = getTextData(L,"port");
+
+       //close menu next time
+       engine->m_startgame = true;
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_close(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       engine->m_data->kill = true;
+
+       //close menu next time
+       engine->m_startgame = true;
+       engine->m_menu->quitMenu();
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_set_background(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::string backgroundlevel(luaL_checkstring(L, 1));
+       std::string texturename(luaL_checkstring(L, 2));
+
+       bool retval = false;
+
+       if (backgroundlevel == "background") {
+               retval |= engine->setTexture(TEX_LAYER_BACKGROUND,texturename);
+       }
+
+       if (backgroundlevel == "overlay") {
+               retval |= engine->setTexture(TEX_LAYER_OVERLAY,texturename);
+       }
+
+       if (backgroundlevel == "header") {
+               retval |= engine->setTexture(TEX_LAYER_HEADER,texturename);
+       }
+
+       if (backgroundlevel == "footer") {
+               retval |= engine->setTexture(TEX_LAYER_FOOTER,texturename);
+       }
+
+       lua_pushboolean(L,retval);
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_set_clouds(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       bool value = lua_toboolean(L,1);
+
+       engine->m_clouds_enabled = value;
+
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_textlist_index(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::string listboxname(luaL_checkstring(L, 1));
+
+       int selection = engine->m_menu->getListboxIndex(listboxname);
+
+       if (selection >= 0)
+               selection++;
+
+       lua_pushinteger(L, selection);
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_worlds(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::vector<WorldSpec> worlds = getAvailableWorlds();
+
+       lua_newtable(L);
+       int top = lua_gettop(L);
+       unsigned int index = 1;
+
+       for (unsigned int i = 0; i < worlds.size(); i++)
+       {
+               lua_pushnumber(L,index);
+
+               lua_newtable(L);
+               int top_lvl2 = lua_gettop(L);
+
+               lua_pushstring(L,"path");
+               lua_pushstring(L,worlds[i].path.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_pushstring(L,"name");
+               lua_pushstring(L,worlds[i].name.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_pushstring(L,"gameid");
+               lua_pushstring(L,worlds[i].gameid.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_settable(L, top);
+               index++;
+       }
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_games(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::vector<SubgameSpec> games = getAvailableGames();
+
+       lua_newtable(L);
+       int top = lua_gettop(L);
+       unsigned int index = 1;
+
+       for (unsigned int i = 0; i < games.size(); i++)
+       {
+               lua_pushnumber(L,index);
+               lua_newtable(L);
+               int top_lvl2 = lua_gettop(L);
+
+               lua_pushstring(L,"id");
+               lua_pushstring(L,games[i].id.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_pushstring(L,"path");
+               lua_pushstring(L,games[i].path.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_pushstring(L,"gamemods_path");
+               lua_pushstring(L,games[i].gamemods_path.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_pushstring(L,"name");
+               lua_pushstring(L,games[i].name.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_pushstring(L,"menuicon_path");
+               lua_pushstring(L,games[i].menuicon_path.c_str());
+               lua_settable(L, top_lvl2);
+
+               lua_pushstring(L,"addon_mods_paths");
+               lua_newtable(L);
+               int table2 = lua_gettop(L);
+               int internal_index=1;
+               for (std::set<std::string>::iterator iter = games[i].addon_mods_paths.begin();
+                               iter != games[i].addon_mods_paths.end(); iter++) {
+                       lua_pushnumber(L,internal_index);
+                       lua_pushstring(L,(*iter).c_str());
+                       lua_settable(L, table2);
+                       internal_index++;
+               }
+               lua_settable(L, top_lvl2);
+               lua_settable(L, top);
+               index++;
+       }
+       return 1;
+}
+/******************************************************************************/
+int guiLuaApi::l_get_modstore_details(lua_State *L)
+{
+       const char *modid       = luaL_checkstring(L, 1);
+
+       if (modid != 0) {
+               Json::Value details;
+               std::string url = "";
+               try{
+                       url = g_settings->get("modstore_details_url");
+               }
+               catch(SettingNotFoundException &e) {
+                       lua_pushnil(L);
+                       return 1;
+               }
+
+               size_t idpos = url.find("*");
+               url.erase(idpos,1);
+               url.insert(idpos,modid);
+
+               details = getModstoreUrl(url);
+
+               ModStoreModDetails current_mod = readModStoreModDetails(details);
+
+               if ( current_mod.valid) {
+                       lua_newtable(L);
+                       int top = lua_gettop(L);
+
+                       lua_pushstring(L,"id");
+                       lua_pushnumber(L,current_mod.id);
+                       lua_settable(L, top);
+
+                       lua_pushstring(L,"title");
+                       lua_pushstring(L,current_mod.title.c_str());
+                       lua_settable(L, top);
+
+                       lua_pushstring(L,"basename");
+                       lua_pushstring(L,current_mod.basename.c_str());
+                       lua_settable(L, top);
+
+                       lua_pushstring(L,"description");
+                       lua_pushstring(L,current_mod.description.c_str());
+                       lua_settable(L, top);
+
+                       lua_pushstring(L,"author");
+                       lua_pushstring(L,current_mod.author.username.c_str());
+                       lua_settable(L, top);
+
+                       lua_pushstring(L,"download_url");
+                       lua_pushstring(L,current_mod.versions[0].file.c_str());
+                       lua_settable(L, top);
+
+                       lua_pushstring(L,"license");
+                       lua_pushstring(L,current_mod.license.shortinfo.c_str());
+                       lua_settable(L, top);
+
+                       lua_pushstring(L,"rating");
+                       lua_pushnumber(L,current_mod.rating);
+                       lua_settable(L, top);
+
+                       //TODO depends
+
+                       //TODO softdepends
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_modstore_list(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::string listtype = "local";
+
+       if (!lua_isnone(L,1)) {
+               listtype = luaL_checkstring(L,1);
+       }
+       Json::Value mods;
+       std::string url = "";
+       try{
+               url = g_settings->get("modstore_listmods_url");
+       }
+       catch(SettingNotFoundException &e) {
+               lua_pushnil(L);
+               return 1;
+       }
+
+       mods = getModstoreUrl(url);
+
+       std::vector<ModStoreMod> moddata = readModStoreList(mods);
+
+       lua_newtable(L);
+       int top = lua_gettop(L);
+       unsigned int index = 1;
+
+       for (unsigned int i = 0; i < moddata.size(); i++)
+       {
+               if (moddata[i].valid) {
+                       lua_pushnumber(L,index);
+                       lua_newtable(L);
+
+                       int top_lvl2 = lua_gettop(L);
+
+                       lua_pushstring(L,"id");
+                       lua_pushnumber(L,moddata[i].id);
+                       lua_settable(L, top_lvl2);
+
+                       lua_pushstring(L,"title");
+                       lua_pushstring(L,moddata[i].title.c_str());
+                       lua_settable(L, top_lvl2);
+
+                       lua_pushstring(L,"basename");
+                       lua_pushstring(L,moddata[i].basename.c_str());
+                       lua_settable(L, top_lvl2);
+
+                       lua_settable(L, top);
+                       index++;
+               }
+       }
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_favorites(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::string listtype = "local";
+
+       if (!lua_isnone(L,1)) {
+               listtype = luaL_checkstring(L,1);
+       }
+
+       std::vector<ServerListSpec> servers;
+#if USE_CURL
+       if(listtype == "online") {
+               servers = ServerList::getOnline();
+       } else {
+               servers = ServerList::getLocal();
+       }
+#else
+       servers = ServerList::getLocal();
+#endif
+
+       lua_newtable(L);
+       int top = lua_gettop(L);
+       unsigned int index = 1;
+
+       for (unsigned int i = 0; i < servers.size(); i++)
+       {
+               lua_pushnumber(L,index);
+
+               lua_newtable(L);
+               int top_lvl2 = lua_gettop(L);
+
+               if (servers[i]["clients"].asString().size()) {
+
+                       const char* clients_raw = servers[i]["clients"].asString().c_str();
+                       char* endptr = 0;
+                       int numbervalue = strtol(clients_raw,&endptr,10);
+
+                       if ((*clients_raw != 0) && (*endptr == 0)) {
+                               lua_pushstring(L,"clients");
+                               lua_pushnumber(L,numbervalue);
+                               lua_settable(L, top_lvl2);
+                       }
+               }
+
+               if (servers[i]["clients_max"].asString().size()) {
+
+                       const char* clients_max_raw = servers[i]["clients"].asString().c_str();
+                       char* endptr = 0;
+                       int numbervalue = strtol(clients_max_raw,&endptr,10);
+
+                       if ((*clients_max_raw != 0) && (*endptr == 0)) {
+                               lua_pushstring(L,"clients_max");
+                               lua_pushnumber(L,numbervalue);
+                               lua_settable(L, top_lvl2);
+                       }
+               }
+
+               if (servers[i]["version"].asString().size()) {
+                       lua_pushstring(L,"version");
+                       lua_pushstring(L,servers[i]["version"].asString().c_str());
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["password"].asString().size()) {
+                       lua_pushstring(L,"password");
+                       lua_pushboolean(L,true);
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["creative"].asString().size()) {
+                       lua_pushstring(L,"creative");
+                       lua_pushboolean(L,true);
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["damage"].asString().size()) {
+                       lua_pushstring(L,"damage");
+                       lua_pushboolean(L,true);
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["pvp"].asString().size()) {
+                       lua_pushstring(L,"pvp");
+                       lua_pushboolean(L,true);
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["description"].asString().size()) {
+                       lua_pushstring(L,"description");
+                       lua_pushstring(L,servers[i]["description"].asString().c_str());
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["name"].asString().size()) {
+                       lua_pushstring(L,"name");
+                       lua_pushstring(L,servers[i]["name"].asString().c_str());
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["address"].asString().size()) {
+                       lua_pushstring(L,"address");
+                       lua_pushstring(L,servers[i]["address"].asString().c_str());
+                       lua_settable(L, top_lvl2);
+               }
+
+               if (servers[i]["port"].asString().size()) {
+                       lua_pushstring(L,"port");
+                       lua_pushstring(L,servers[i]["port"].asString().c_str());
+                       lua_settable(L, top_lvl2);
+               }
+
+               lua_settable(L, top);
+               index++;
+       }
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_delete_favorite(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::vector<ServerListSpec> servers;
+
+       std::string listtype = "local";
+
+       if (!lua_isnone(L,2)) {
+               listtype = luaL_checkstring(L,2);
+       }
+
+       if ((listtype != "local") &&
+               (listtype != "online"))
+               return 0;
+
+#if USE_CURL
+       if(listtype == "online") {
+               servers = ServerList::getOnline();
+       } else {
+               servers = ServerList::getLocal();
+       }
+#else
+       servers = ServerList::getLocal();
+#endif
+
+       int fav_idx     = luaL_checkinteger(L,1) -1;
+
+       if ((fav_idx >= 0) &&
+                       (fav_idx < (int) servers.size())) {
+
+               ServerList::deleteEntry(servers[fav_idx]);
+       }
+
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_show_keys_menu(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       GUIKeyChangeMenu *kmenu
+               = new GUIKeyChangeMenu( engine->m_device->getGUIEnvironment(),
+                                                               engine->m_parent,
+                                                               -1,
+                                                               engine->m_menumanager);
+       kmenu->drop();
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_set(lua_State *L)
+{
+       const char *name = luaL_checkstring(L, 1);
+       const char *value = luaL_checkstring(L, 2);
+       g_settings->set(name, value);
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_get(lua_State *L)
+{
+       const char *name = luaL_checkstring(L, 1);
+       try{
+               std::string value = g_settings->get(name);
+               lua_pushstring(L, value.c_str());
+       } catch(SettingNotFoundException &e){
+               lua_pushnil(L);
+       }
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_getbool(lua_State *L)
+{
+       const char *name = luaL_checkstring(L, 1);
+       try{
+               bool value = g_settings->getBool(name);
+               lua_pushboolean(L, value);
+       } catch(SettingNotFoundException &e){
+               lua_pushnil(L);
+       }
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_setting_setbool(lua_State *L)
+{
+       const char *name = luaL_checkstring(L, 1);
+       bool value = lua_toboolean(L,2);
+
+       g_settings->setBool(name,value);
+
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_create_world(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       const char *name        = luaL_checkstring(L, 1);
+       int gameidx                     = luaL_checkinteger(L,2) -1;
+
+       std::string path = porting::path_user + DIR_DELIM
+                       "worlds" + DIR_DELIM
+                       + name;
+
+       std::vector<SubgameSpec> games = getAvailableGames();
+
+       if ((gameidx >= 0) &&
+                       (gameidx < (int) games.size())) {
+
+               // Create world if it doesn't exist
+               if(!initializeWorld(path, games[gameidx].id)){
+                       lua_pushstring(L, "Failed to initialize world");
+
+               }
+               else {
+               lua_pushnil(L);
+               }
+       }
+       else {
+               lua_pushstring(L, "Invalid game index");
+       }
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_delete_world(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       int worldidx    = luaL_checkinteger(L,1) -1;
+
+       std::vector<WorldSpec> worlds = getAvailableWorlds();
+
+       if ((worldidx >= 0) &&
+               (worldidx < (int) worlds.size())) {
+
+               WorldSpec spec = worlds[worldidx];
+
+               std::vector<std::string> paths;
+               paths.push_back(spec.path);
+               fs::GetRecursiveSubPaths(spec.path, paths);
+
+               // Delete files
+               if (!fs::DeletePaths(paths)) {
+                       lua_pushstring(L, "Failed to delete world");
+               }
+               else {
+                       lua_pushnil(L);
+               }
+       }
+       else {
+               lua_pushstring(L, "Invalid world index");
+       }
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_set_topleft_text(lua_State *L)
+{
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       std::string text = "";
+
+       if (!lua_isnone(L,1) && !lua_isnil(L,1))
+               text = luaL_checkstring(L, 1);
+
+       engine->setTopleftText(text);
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_modpath(lua_State *L)
+{
+       //TODO this path may be controversial!
+       std::string modpath
+                       = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "mods" + DIR_DELIM);
+       lua_pushstring(L, modpath.c_str());
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_gamepath(lua_State *L)
+{
+       std::string gamepath
+                       = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "games"    + DIR_DELIM);
+       lua_pushstring(L, gamepath.c_str());
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_dirlist(lua_State *L) {
+       const char *path        = luaL_checkstring(L, 1);
+       bool dironly            = lua_toboolean(L, 2);
+
+       std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
+
+       unsigned int index = 1;
+       lua_newtable(L);
+       int table = lua_gettop(L);
+
+       for (unsigned int i=0;i< dirlist.size(); i++) {
+               if ((dirlist[i].dir) || (dironly == false)) {
+                       lua_pushnumber(L,index);
+                       lua_pushstring(L,dirlist[i].name.c_str());
+                       lua_settable(L, table);
+                       index++;
+               }
+       }
+
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_create_dir(lua_State *L) {
+       const char *path        = luaL_checkstring(L, 1);
+
+       if (guiLuaApi::isMinetestPath(path)) {
+               lua_pushboolean(L,fs::CreateAllDirs(path));
+               return 1;
+       }
+       lua_pushboolean(L,false);
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_delete_dir(lua_State *L) {
+       const char *path        = luaL_checkstring(L, 1);
+
+       std::string absolute_path = fs::RemoveRelativePathComponents(path);
+
+       if (guiLuaApi::isMinetestPath(absolute_path)) {
+               lua_pushboolean(L,fs::RecursiveDelete(absolute_path));
+               return 1;
+       }
+       lua_pushboolean(L,false);
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_copy_dir(lua_State *L) {
+       const char *source      = luaL_checkstring(L, 1);
+       const char *destination = luaL_checkstring(L, 2);
+
+       bool keep_source = true;
+
+       if ((!lua_isnone(L,3)) &&
+                       (!lua_isnil(L,3))) {
+               keep_source = lua_toboolean(L,3);
+       }
+
+       std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
+       std::string absolute_source = fs::RemoveRelativePathComponents(source);
+
+       if ((guiLuaApi::isMinetestPath(absolute_source)) &&
+                       (guiLuaApi::isMinetestPath(absolute_destination))) {
+               bool retval = fs::CopyDir(absolute_source,absolute_destination);
+
+               if (retval && (!keep_source)) {
+
+                       retval &= fs::RecursiveDelete(absolute_source);
+               }
+               lua_pushboolean(L,retval);
+               return 1;
+       }
+       lua_pushboolean(L,false);
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_extract_zip(lua_State *L) {
+
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       const char *zipfile     = luaL_checkstring(L, 1);
+       const char *destination = luaL_checkstring(L, 2);
+
+       std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
+
+       if (guiLuaApi::isMinetestPath(absolute_destination)) {
+               fs::CreateAllDirs(absolute_destination);
+
+               io::IFileSystem* fs = engine->m_device->getFileSystem();
+
+               fs->addFileArchive(zipfile,true,false,io::EFAT_ZIP);
+
+               assert(fs->getFileArchiveCount() > 0);
+
+               /**********************************************************************/
+               /* WARNING this is not threadsafe!!                                   */
+               /**********************************************************************/
+               io::IFileArchive* opened_zip =
+                       fs->getFileArchive(fs->getFileArchiveCount()-1);
+
+               const io::IFileList* files_in_zip = opened_zip->getFileList();
+
+               unsigned int number_of_files = files_in_zip->getFileCount();
+
+               for (unsigned int i=0; i < number_of_files;  i++) {
+                       std::string fullpath = destination;
+                       fullpath += DIR_DELIM;
+                       fullpath += files_in_zip->getFullFileName(i).c_str();
+
+                       if (files_in_zip->isDirectory(i)) {
+                               if (! fs::CreateAllDirs(fullpath) ) {
+                                       fs->removeFileArchive(fs->getFileArchiveCount()-1);
+                                       lua_pushboolean(L,false);
+                                       return 1;
+                               }
+                       }
+                       else {
+                               io::IReadFile* toread = opened_zip->createAndOpenFile(i);
+
+                               FILE *targetfile = fopen(fullpath.c_str(),"wb");
+
+                               if (targetfile == NULL) {
+                                       fs->removeFileArchive(fs->getFileArchiveCount()-1);
+                                       lua_pushboolean(L,false);
+                                       return 1;
+                               }
+
+                               char read_buffer[1024];
+                               unsigned int total_read = 0;
+
+                               while (total_read < toread->getSize()) {
+
+                                       unsigned int bytes_read =
+                                                       toread->read(read_buffer,sizeof(read_buffer));
+                                       unsigned int bytes_written;
+                                       if ((bytes_read < 0 ) ||
+                                               (bytes_written = fwrite(read_buffer, 1, bytes_read, targetfile) != bytes_read))
+                                       {
+                                               fclose(targetfile);
+                                               fs->removeFileArchive(fs->getFileArchiveCount()-1);
+                                               lua_pushboolean(L,false);
+                                               return 1;
+                                       }
+                                       total_read += bytes_read;
+                               }
+
+                               fclose(targetfile);
+                       }
+
+               }
+
+               fs->removeFileArchive(fs->getFileArchiveCount()-1);
+               lua_pushboolean(L,true);
+               return 1;
+       }
+
+       lua_pushboolean(L,false);
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_scriptdir(lua_State *L) {
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       lua_pushstring(L,engine->getScriptDir().c_str());
+       return 1;
+}
+
+/******************************************************************************/
+bool guiLuaApi::isMinetestPath(std::string path) {
+
+
+       if (fs::PathStartsWith(path,fs::TempPath()))
+               return true;
+
+       /* games */
+       if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "games")))
+               return true;
+
+       /* mods */
+       if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "mods")))
+               return true;
+
+       /* worlds */
+       if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "worlds")))
+               return true;
+
+
+       return false;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_show_file_open_dialog(lua_State *L) {
+
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       const char *formname= luaL_checkstring(L, 1);
+       const char *title       = luaL_checkstring(L, 2);
+
+       GUIFileSelectMenu* fileOpenMenu =
+               new GUIFileSelectMenu(engine->m_device->getGUIEnvironment(),
+                                                               engine->m_parent,
+                                                               -1,
+                                                               engine->m_menumanager,
+                                                               title,
+                                                               formname);
+       fileOpenMenu->setTextDest(engine->m_buttonhandler);
+       fileOpenMenu->drop();
+       return 0;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_get_version(lua_State *L) {
+       lua_pushstring(L,VERSION_STRING);
+       return 1;
+}
+
+/******************************************************************************/
+int guiLuaApi::l_download_file(lua_State *L) {
+       GUIEngine* engine = get_engine(L);
+       assert(engine != 0);
+
+       const char *url    = luaL_checkstring(L, 1);
+       const char *target = luaL_checkstring(L, 2);
+
+       //check path
+       std::string absolute_destination = fs::RemoveRelativePathComponents(target);
+
+       if (guiLuaApi::isMinetestPath(absolute_destination)) {
+               if (engine->downloadFile(url,absolute_destination)) {
+                       lua_pushboolean(L,true);
+                       return 1;
+               }
+       }
+       lua_pushboolean(L,false);
+       return 1;
+}
diff --git a/src/guiLuaApi.h b/src/guiLuaApi.h
new file mode 100644 (file)
index 0000000..e0157f4
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+Minetest
+Copyright (C) 2013 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef GUILUAAPI_H_
+#define GUILUAAPI_H_
+
+/******************************************************************************/
+/* Includes                                                                   */
+/******************************************************************************/
+#include "serverlist.h"
+
+/******************************************************************************/
+/* Typedefs and macros                                                        */
+/******************************************************************************/
+typedef int (*lua_CFunction) (lua_State *L);
+
+/******************************************************************************/
+/* forward declarations                                                       */
+/******************************************************************************/
+class GUIEngine;
+
+
+/******************************************************************************/
+/* declarations                                                               */
+/******************************************************************************/
+
+/** Implementation of lua api support for mainmenu */
+class guiLuaApi {
+
+public:
+       /**
+        * initialize given Lua stack
+        * @param L lua stack to initialize
+        * @param engine pointer to GUIEngine element to use as reference
+        */
+       static void initialize(lua_State* L,GUIEngine* engine);
+
+       /** default destructor */
+       virtual ~guiLuaApi() {}
+
+private:
+       /**
+        * read a text variable from gamedata table within lua stack
+        * @param L stack to read variable from
+        * @param name name of variable to read
+        * @return string value of requested variable
+        */
+       static std::string getTextData(lua_State *L, std::string name);
+
+       /**
+        * read a integer variable from gamedata table within lua stack
+        * @param L stack to read variable from
+        * @param name name of variable to read
+        * @return integer value of requested variable
+        */
+       static int getIntegerData(lua_State *L, std::string name,bool& valid);
+
+       /**
+        * read a bool variable from gamedata table within lua stack
+        * @param L stack to read variable from
+        * @param name name of variable to read
+        * @return bool value of requested variable
+        */
+       static int getBoolData(lua_State *L, std::string name,bool& valid);
+
+       /**
+        * get the corresponding engine pointer from a lua stack
+        * @param L stack to read pointer from
+        * @return pointer to GUIEngine
+        */
+       static GUIEngine* get_engine(lua_State *L);
+
+
+       /**
+        * register a static member function as lua api call at current position of stack
+        * @param L stack to registe fct to
+        * @param name of function within lua
+        * @param fct C-Function to call on lua call of function
+        * @param top current top of stack
+        */
+       static bool registerFunction(   lua_State* L,
+                                                                       const char* name,
+                                                                       lua_CFunction fct,
+                                                                       int top
+                                                               );
+
+       /**
+        * check if a path is within some of minetests folders
+        * @param path path to check
+        * @return true/false
+        */
+       static bool isMinetestPath(std::string path);
+
+       //api calls
+
+       static int l_start(lua_State *L);
+
+       static int l_close(lua_State *L);
+
+       static int l_create_world(lua_State *L);
+
+       static int l_delete_world(lua_State *L);
+
+       static int l_get_worlds(lua_State *L);
+
+       static int l_get_games(lua_State *L);
+
+       static int l_get_favorites(lua_State *L);
+
+       static int l_delete_favorite(lua_State *L);
+
+       static int l_get_version(lua_State *L);
+
+       //gui
+
+       static int l_show_keys_menu(lua_State *L);
+
+       static int l_show_file_open_dialog(lua_State *L);
+
+       static int l_set_topleft_text(lua_State *L);
+
+       static int l_set_clouds(lua_State *L);
+
+       static int l_get_textlist_index(lua_State *L);
+
+       static int l_set_background(lua_State *L);
+
+       static int l_update_formspec(lua_State *L);
+
+       //settings
+
+       static int l_setting_set(lua_State *L);
+
+       static int l_setting_get(lua_State *L);
+
+       static int l_setting_getbool(lua_State *L);
+
+       static int l_setting_setbool(lua_State *L);
+
+       //filesystem
+
+       static int l_get_scriptdir(lua_State *L);
+
+       static int l_get_modpath(lua_State *L);
+
+       static int l_get_gamepath(lua_State *L);
+
+       static int l_get_dirlist(lua_State *L);
+
+       static int l_create_dir(lua_State *L);
+
+       static int l_delete_dir(lua_State *L);
+
+       static int l_copy_dir(lua_State *L);
+
+       static int l_extract_zip(lua_State *L);
+
+       static int l_get_modstore_details(lua_State *L);
+
+       static int l_get_modstore_list(lua_State *L);
+
+       static int l_download_file(lua_State *L);
+
+
+};
+
+#endif
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
deleted file mode 100644 (file)
index 223bba9..0000000
+++ /dev/null
@@ -1,1521 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "guiMainMenu.h"
-#include "guiKeyChangeMenu.h"
-#include "guiCreateWorld.h"
-#include "guiConfigureWorld.h"
-#include "guiMessageMenu.h"
-#include "guiConfirmMenu.h"
-#include "debug.h"
-#include "serialization.h"
-#include <string>
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IGUIListBox.h>
-#include <IGUITabControl.h>
-#include <IGUIImage.h>
-// For IGameCallback
-#include "guiPauseMenu.h"
-#include "gettext.h"
-#include "tile.h" // getTexturePath
-#include "filesys.h"
-#include "util/string.h"
-#include "subgame.h"
-
-#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0]))
-#define LSTRING(x) LSTRING_(x)
-#define LSTRING_(x) L##x
-
-const wchar_t *contrib_core_strs[] = {
-       L"Perttu Ahola (celeron55) <celeron55@gmail.com>",
-       L"Ryan Kwolek (kwolekr) <kwolekr@minetest.net>",
-       L"PilzAdam <pilzadam@minetest.net>",
-       L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>",
-       L"Lisa Milne (darkrose) <lisa@ltmnet.com>",
-       L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>",
-       L"proller <proler@gmail.com>",
-       L"sfan5 <sfan5@live.de>",
-       L"kahrl <kahrl@gmx.net>"
-};
-
-const wchar_t *contrib_active_strs[] = {
-       L"sapier <sapier@gmx.net>",
-       L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>",
-       L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
-       L"Jeija <jeija@mesecons.net>",
-       L"MirceaKitsune <mirceakitsune@gmail.com>",
-       L"ShadowNinja",
-       L"dannydark <the_skeleton_of_a_child@yahoo.co.uk>",
-       L"0gb.us <0gb.us@0gb.us>"
-};
-
-const wchar_t *contrib_previous_strs[] = {
-       L"Giuseppe Bilotta (Oblomov) <giuseppe.bilotta@gmail.com>",
-       L"Jonathan Neuschafer <j.neuschaefer@gmx.net>",
-       L"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>",
-       L"Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>",
-       L"matttpt <matttpt@gmail.com>",
-       L"JacobF <queatz@gmail.com>" 
-};
-
-
-struct CreateWorldDestMainMenu : public CreateWorldDest
-{
-       CreateWorldDestMainMenu(GUIMainMenu *menu):
-               m_menu(menu)
-       {}
-       void accepted(std::wstring name, std::string gameid)
-       {
-               std::string name_narrow = wide_to_narrow(name);
-               if(!string_allowed_blacklist(name_narrow, WORLDNAME_BLACKLISTED_CHARS))
-               {
-                       wchar_t* text = wgettext("Cannot create world: Name contains invalid characters");
-                       m_menu->displayMessageMenu(text);
-                       delete[] text;
-                       return;
-               }
-               std::vector<WorldSpec> worlds = getAvailableWorlds();
-               for(std::vector<WorldSpec>::iterator i = worlds.begin();
-                   i != worlds.end(); i++)
-               {
-                       if((*i).name == name_narrow)
-                       {
-                               wchar_t* text = wgettext("Cannot create world: A world by this name already exists");
-                               m_menu->displayMessageMenu(text);
-                               delete[] text;
-                               return;
-                       }
-               }
-               m_menu->createNewWorld(name, gameid);
-       }
-       GUIMainMenu *m_menu;
-};
-
-struct ConfirmDestDeleteWorld : public ConfirmDest
-{
-       ConfirmDestDeleteWorld(WorldSpec spec, GUIMainMenu *menu,
-                       const std::vector<std::string> &paths):
-               m_spec(spec),
-               m_menu(menu),
-               m_paths(paths)
-       {}
-       void answer(bool answer)
-       {
-               if(answer == false)
-                       return;
-               m_menu->deleteWorld(m_paths);
-       }
-       WorldSpec m_spec;
-       GUIMainMenu *m_menu;
-       std::vector<std::string> m_paths;
-};
-
-enum
-{
-       GUI_ID_QUIT_BUTTON = 101,
-       GUI_ID_NAME_INPUT,
-       GUI_ID_ADDRESS_INPUT,
-       GUI_ID_PORT_INPUT,
-       GUI_ID_FANCYTREE_CB,
-       GUI_ID_SMOOTH_LIGHTING_CB,
-       GUI_ID_3D_CLOUDS_CB,
-       GUI_ID_OPAQUE_WATER_CB,
-       GUI_ID_MIPMAP_CB,
-       GUI_ID_ANISOTROPIC_CB,
-       GUI_ID_BILINEAR_CB,
-       GUI_ID_TRILINEAR_CB,
-       GUI_ID_SHADERS_CB,
-       GUI_ID_PRELOAD_ITEM_VISUALS_CB,
-       GUI_ID_ENABLE_PARTICLES_CB,
-       GUI_ID_LIQUID_FINITE_CB,
-       GUI_ID_DAMAGE_CB,
-       GUI_ID_CREATIVE_CB,
-       GUI_ID_PUBLIC_CB,
-       GUI_ID_JOIN_GAME_BUTTON,
-       GUI_ID_CHANGE_KEYS_BUTTON,
-       GUI_ID_DELETE_WORLD_BUTTON,
-       GUI_ID_CREATE_WORLD_BUTTON,
-       GUI_ID_CONFIGURE_WORLD_BUTTON,
-       GUI_ID_WORLD_LISTBOX,
-       GUI_ID_TAB_CONTROL,
-       GUI_ID_SERVERLIST,
-       GUI_ID_SERVERLIST_TOGGLE,
-       GUI_ID_SERVERLIST_DELETE,
-       GUI_ID_SERVERLIST_TITLE,
-       GUI_ID_GAME_BUTTON_FIRST = 130,
-       GUI_ID_GAME_BUTTON_MAX = 150,
-};
-
-GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id,
-               IMenuManager *menumgr,
-               MainMenuData *data,
-               IGameCallback *gamecallback
-):
-       GUIModalMenu(env, parent, id, menumgr),
-       m_data(data),
-       m_accepted(false),
-       m_gamecallback(gamecallback),
-       m_is_regenerating(false)
-{
-       assert(m_data);
-       this->env = env;
-       this->parent = parent;
-       this->id = id;
-       this->menumgr = menumgr;
-}
-
-GUIMainMenu::~GUIMainMenu()
-{
-       removeChildren();
-}
-
-void GUIMainMenu::removeChildren()
-{
-       const core::list<gui::IGUIElement*> &children = getChildren();
-       core::list<gui::IGUIElement*> children_copy;
-       for(core::list<gui::IGUIElement*>::ConstIterator
-                       i = children.begin(); i != children.end(); i++)
-       {
-               children_copy.push_back(*i);
-       }
-       for(core::list<gui::IGUIElement*>::Iterator
-                       i = children_copy.begin();
-                       i != children_copy.end(); i++)
-       {
-               (*i)->remove();
-       }
-}
-
-void GUIMainMenu::regenerateGui(v2u32 screensize)
-{
-       m_is_regenerating = true;
-       /*
-               Read stuff from elements into m_data
-       */
-       readInput(m_data);
-
-       /*
-               Remove stuff
-       */
-       removeChildren();
-       
-       /*
-               Calculate new sizes and positions
-       */
-       
-       v2s32 size(screensize.X, screensize.Y);
-
-       core::rect<s32> rect(
-                       screensize.X/2 - size.X/2,
-                       screensize.Y/2 - size.Y/2,
-                       screensize.X/2 + size.X/2,
-                       screensize.Y/2 + size.Y/2
-       );
-
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-
-       //v2s32 size = rect.getSize();
-
-       /*
-               Add stuff
-       */
-
-       changeCtype("");
-
-       // Version
-       {
-               core::rect<s32> rect(0, 0, size.X, 40);
-               rect += v2s32(4, 0);
-               std::string t = "Minetest " VERSION_STRING;
-               if(m_data->selected_game_name != "" &&
-                               m_data->selected_tab == TAB_SINGLEPLAYER){
-                       t += "/";
-                       t += m_data->selected_game_name;
-               }
-               Environment->addStaticText(narrow_to_wide(t).c_str(),
-                               rect, false, true, this, -1);
-       }
-
-       //v2s32 center(size.X/2, size.Y/2);
-       v2s32 c800(size.X/2-400, size.Y/2-270);
-       
-       m_topleft_client = c800 + v2s32(90, 70+50+30);
-       m_size_client = v2s32(620, 270);
-
-       m_size_server = v2s32(620, 140);
-
-       if(m_data->selected_tab == TAB_ADVANCED)
-       {
-               m_topleft_client = c800 + v2s32(90, 70+50+30);
-               m_size_client = v2s32(620, 200);
-
-               m_size_server = v2s32(620, 140);
-       }
-
-       m_topleft_server = m_topleft_client + v2s32(0, m_size_client.Y+20);
-       
-       // Tabs
-       {
-               core::rect<s32> rect(0, 0, m_size_client.X, 30);
-               rect += m_topleft_client + v2s32(0, -30);
-               gui::IGUITabControl *e = Environment->addTabControl(
-                               rect, this, true, true, GUI_ID_TAB_CONTROL);
-               wchar_t* text = wgettext("Singleplayer");
-               e->addTab(text);
-               delete[] text;
-               text = wgettext("Multiplayer");
-               e->addTab(text);
-               delete[] text;
-               text = wgettext("Advanced");
-               e->addTab(text);
-               delete[] text;
-               text = wgettext("Settings");
-               e->addTab(text);
-               delete[] text;
-               text = wgettext("Credits");
-               e->addTab(text);
-               delete[] text;
-
-               e->setActiveTab(m_data->selected_tab);
-
-       }
-       
-       if(m_data->selected_tab == TAB_SINGLEPLAYER)
-       {
-               // HYBRID
-               {
-                       core::rect<s32> rect(0, 0, 10, m_size_client.Y);
-                       rect += m_topleft_client + v2s32(15, 0);
-                       //const wchar_t *text = L"H\nY\nB\nR\nI\nD";
-                       const wchar_t *text = L"S\nI\nN\nG\nL\nE\n \nP\nL\nA\nY\nE\nR\n";
-                       gui::IGUIStaticText *t =
-                       Environment->addStaticText(text, rect, false, true, this, -1);
-                       t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               }
-               u32 bs = 5;
-               // World selection listbox
-               u32 world_sel_h = 160;
-               u32 world_sel_w = 365;
-               //s32 world_sel_x = 50;
-               s32 world_sel_x = m_size_client.X-world_sel_w-30;
-               s32 world_sel_y = 30;
-               u32 world_button_count = 3;
-               u32 world_button_w = (world_sel_w)/world_button_count - bs
-                               + bs/(world_button_count-1);
-               {
-                       core::rect<s32> rect(0, 0, world_sel_w-4, 20);
-                       rect += m_topleft_client + v2s32(world_sel_x+4, world_sel_y-20);
-                       wchar_t* text = wgettext("Select World:");
-                       /*gui::IGUIStaticText *e =*/ Environment->addStaticText(
-                                       text,
-                                       rect, false, true, this, -1);
-                       delete[] text;
-                       /*e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);*/
-               }
-               {
-                       core::rect<s32> rect(0, 0, world_sel_w, world_sel_h);
-                       rect += m_topleft_client + v2s32(world_sel_x, world_sel_y);
-                       gui::IGUIListBox *e = Environment->addListBox(rect, this,
-                                       GUI_ID_WORLD_LISTBOX);
-                       e->setDrawBackground(true);
-                       m_world_indices.clear();
-                       for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
-                               const WorldSpec &spec = m_data->worlds[wi];
-                               if(spec.gameid == m_data->selected_game){
-                                       //e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
-                                       e->addItem(narrow_to_wide(spec.name).c_str());
-                                       m_world_indices.push_back(wi);
-                                       if(m_data->selected_world == (int)wi)
-                                               e->setSelected(m_world_indices.size()-1);
-                               }
-                       }
-                       Environment->setFocus(e);
-               }
-               // Delete world button
-               {
-                       core::rect<s32> rect(0, 0, world_button_w, 30);
-                       rect += m_topleft_client + v2s32(world_sel_x, world_sel_y+world_sel_h+0);
-                       wchar_t* text = wgettext("Delete");
-                       Environment->addButton(rect, this, GUI_ID_DELETE_WORLD_BUTTON,
-                                text);
-                       delete[] text;
-               }
-               // Create world button
-               {
-                       core::rect<s32> rect(0, 0, world_button_w, 30);
-                       rect += m_topleft_client + v2s32(world_sel_x+world_button_w+bs, world_sel_y+world_sel_h+0);
-                       wchar_t* text = wgettext("New");
-                       Environment->addButton(rect, this, GUI_ID_CREATE_WORLD_BUTTON,
-                                text);
-                       delete[] text;
-               }
-               // Configure world button
-               {
-                       core::rect<s32> rect(0, 0, world_button_w, 30);
-                       rect += m_topleft_client + v2s32(world_sel_x+(world_button_w+bs)*2,
-                                       world_sel_y+world_sel_h+0);
-                       wchar_t* text = wgettext("Configure");
-                       Environment->addButton(rect, this, GUI_ID_CONFIGURE_WORLD_BUTTON,
-                               text);
-                       delete[] text;
-               }
-               // Start game button
-               {
-                       /*core::rect<s32> rect(0, 0, world_button_w, 30);
-                       rect += m_topleft_client + v2s32(world_sel_x+(world_button_w+bs)*3,
-                                       world_sel_y+world_sel_h+0);*/
-                       u32 bw = 160;
-                       /*core::rect<s32> rect(0, 0, bw, 30);
-                       rect += m_topleft_client + v2s32(m_size_client.X-bw-30,
-                                       m_size_client.Y-30-15);*/
-                       core::rect<s32> rect(0, 0, bw, 30);
-                       rect += m_topleft_client + v2s32(world_sel_x+world_sel_w-bw,
-                                       world_sel_y+world_sel_h+30+bs);
-                       wchar_t* text = wgettext("Play");
-                       Environment->addButton(rect, this,
-                                       GUI_ID_JOIN_GAME_BUTTON, text);
-                       delete[] text;
-               }
-               // Options
-               s32 option_x = 50;
-               //s32 option_x = 50+world_sel_w+20;
-               s32 option_y = 30;
-               u32 option_w = 150;
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += m_topleft_client + v2s32(option_x, option_y+20*0);
-                       wchar_t* text = wgettext("Creative Mode");
-                       Environment->addCheckBox(m_data->creative_mode, rect, this,
-                                       GUI_ID_CREATIVE_CB, text);
-                       delete[] text;
-               }
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += m_topleft_client + v2s32(option_x, option_y+20*1);
-                       wchar_t* text = wgettext("Enable Damage");
-                       Environment->addCheckBox(m_data->enable_damage, rect, this,
-                                       GUI_ID_DAMAGE_CB, text);
-                       delete[] text;
-               }
-               changeCtype("C");
-
-               /* Add game selection buttons */
-               video::IVideoDriver* driver = Environment->getVideoDriver();
-               for(size_t i=0; i<m_data->games.size(); i++){
-                       const SubgameSpec *spec = &m_data->games[i];
-                       v2s32 p(8 + i*(48+8), screensize.Y - (48+8));
-                       core::rect<s32> rect(0, 0, 48, 48);
-                       rect += p;
-                       video::ITexture *bgtexture = NULL;
-                       if(spec->menuicon_path != "")
-                               bgtexture = driver->getTexture(spec->menuicon_path.c_str());
-                       gui::IGUIButton *b = Environment->addButton(rect, this,
-                                       GUI_ID_GAME_BUTTON_FIRST+i, narrow_to_wide(wrap_rows(spec->id, 4)).c_str());
-                       if(bgtexture){
-                               b->setImage(bgtexture);
-                               b->setText(L"");
-                               b->setDrawBorder(false);
-                               b->setUseAlphaChannel(true);
-                       }
-               }
-       }
-       else if(m_data->selected_tab == TAB_MULTIPLAYER)
-       {
-               changeCtype("");
-               // CLIENT
-               {
-                       core::rect<s32> rect(0, 0, 10, m_size_client.Y);
-                       rect += m_topleft_client + v2s32(15, 0);
-                       const wchar_t *text = L"C\nL\nI\nE\nN\nT";
-                       gui::IGUIStaticText *t =
-                       Environment->addStaticText(text, rect, false, true, this, -1);
-                       t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               }
-               // Nickname + password
-               {
-                       core::rect<s32> rect(0, 0, 110, 20);
-                       wchar_t* text = wgettext("Name/Password");
-                       rect += m_topleft_client + v2s32(m_size_client.X-60-100, 10+6);
-                       Environment->addStaticText(text,
-                               rect, false, true, this, -1);
-                       delete [] text;
-               }
-               changeCtype("C");
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       rect += m_topleft_client + v2s32(m_size_client.X-60-100, 50);
-                       gui::IGUIElement *e = 
-                       Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
-                       if(m_data->name == L"")
-                               Environment->setFocus(e);
-               }
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       rect += m_topleft_client + v2s32(m_size_client.X-60-100, 90);
-                       gui::IGUIEditBox *e =
-                       Environment->addEditBox(L"", rect, true, this, 264);
-                       e->setPasswordBox(true);
-                       if(m_data->name != L"" && m_data->address != L"")
-                               Environment->setFocus(e);
-
-               }
-               changeCtype("");
-               // Server List
-               {
-                       core::rect<s32> rect(0, 0, 390, 140);
-                       rect += m_topleft_client + v2s32(50, 30);
-                       gui::IGUIListBox *e = Environment->addListBox(rect, this,
-                                       GUI_ID_SERVERLIST);
-                       e->setDrawBackground(true);
-#if USE_CURL
-                       if(m_data->selected_serverlist == SERVERLIST_FAVORITES) {
-                               m_data->servers = ServerList::getLocal();
-                               {
-                                       core::rect<s32> rect(0, 0, 390, 20);
-                                       rect += m_topleft_client + v2s32(50, 10);
-                                       wchar_t* text = wgettext("Favorites:");
-                                       Environment->addStaticText(text,
-                                               rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
-                                       delete[] text;
-                               }
-                       } else {
-                               m_data->servers = ServerList::getOnline();
-                               {
-                                       core::rect<s32> rect(0, 0, 390, 20);
-                                       rect += m_topleft_client + v2s32(50, 10);
-                                       wchar_t* text = wgettext("Public Server List:");
-                                       Environment->addStaticText(text,
-                                               rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
-                                       delete[] text;
-                               }
-                       }
-#else
-                       m_data->servers = ServerList::getLocal();
-                       {
-                               core::rect<s32> rect(0, 0, 390, 20);
-                               rect += m_topleft_client + v2s32(50, 10);
-                               wchar_t* text = wgettext("Favorites:");
-                               Environment->addStaticText(text,
-                                       rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
-                               delete[] text;
-                       }
-#endif
-                       updateGuiServerList();
-                       e->setSelected(0);
-               }
-               // Address + port
-               {
-                       core::rect<s32> rect(0, 0, 110, 20);
-                       rect += m_topleft_client + v2s32(50, m_size_client.Y-50-15+6);
-                       wchar_t* text = wgettext("Address/Port");
-                       Environment->addStaticText(text,
-                               rect, false, true, this, -1);
-                       delete [] text;
-               }
-               changeCtype("C");
-               {
-                       core::rect<s32> rect(0, 0, 260, 30);
-                       rect += m_topleft_client + v2s32(50, m_size_client.Y-25-15);
-                       gui::IGUIElement *e = 
-                       Environment->addEditBox(m_data->address.c_str(), rect, true,
-                                       this, GUI_ID_ADDRESS_INPUT);
-                       if(m_data->name != L"" && m_data->address == L"")
-                               Environment->setFocus(e);
-               }
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       rect += m_topleft_client + v2s32(50+260+10, m_size_client.Y-25-15);
-                       Environment->addEditBox(m_data->port.c_str(), rect, true,
-                                       this, GUI_ID_PORT_INPUT);
-               }
-               changeCtype("");
-               #if USE_CURL
-               // Toggle Serverlist (Favorites/Online)
-               {
-                       core::rect<s32> rect(0, 0, 260, 30);
-                       rect += m_topleft_client + v2s32(50,
-                                       180);
-                       wchar_t* text = wgettext("Show Public");
-                       gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_TOGGLE,
-                               text);
-                       delete[] text;
-                       e->setIsPushButton(true);
-                       if (m_data->selected_serverlist == SERVERLIST_PUBLIC)
-                       {
-                               wchar_t* text = wgettext("Show Favorites");
-                               e->setText(text);
-                               e->setPressed();
-                               delete[] text;
-                       }
-               }
-               #endif
-               // Delete Local Favorite
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       rect += m_topleft_client + v2s32(50+260+10, 180);
-                       wchar_t* text = wgettext("Delete");
-                       gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_DELETE,
-                                       text);
-                       if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // Hidden when on public list
-                               e->setVisible(false);
-
-                       delete [] text;
-               }
-               // Start game button
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       rect += m_topleft_client + v2s32(m_size_client.X-130-30,
-                                       m_size_client.Y-25-15);
-                       wchar_t* text = wgettext("Connect");
-                       Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
-                               text);
-                       delete[] text;
-               }
-               changeCtype("C");
-       }
-       else if(m_data->selected_tab == TAB_ADVANCED)
-       {
-               changeCtype("");
-               // CLIENT
-               {
-                       core::rect<s32> rect(0, 0, 10, m_size_client.Y);
-                       rect += m_topleft_client + v2s32(15, 0);
-                       const wchar_t *text = L"C\nL\nI\nE\nN\nT";
-                       gui::IGUIStaticText *t =
-                       Environment->addStaticText(text, rect, false, true, this, -1);
-                       t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               }
-               // Nickname + password
-               {
-                       core::rect<s32> rect(0, 0, 110, 20);
-                       rect += m_topleft_client + v2s32(35+30, 35+6);
-                       wchar_t* text = wgettext("Name/Password");
-                       Environment->addStaticText(text,
-                               rect, false, true, this, -1);
-                       delete [] text;
-               }
-               changeCtype("C");
-               {
-                       core::rect<s32> rect(0, 0, 230, 30);
-                       rect += m_topleft_client + v2s32(160+30, 35);
-                       gui::IGUIElement *e = 
-                       Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
-                       if(m_data->name == L"")
-                               Environment->setFocus(e);
-               }
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       rect += m_topleft_client + v2s32(m_size_client.X-60-100, 35);
-                       gui::IGUIEditBox *e =
-                       Environment->addEditBox(L"", rect, true, this, 264);
-                       e->setPasswordBox(true);
-                       if(m_data->name != L"" && m_data->address != L"")
-                               Environment->setFocus(e);
-
-               }
-               changeCtype("");
-               // Address + port
-               {
-                       core::rect<s32> rect(0, 0, 110, 20);
-                       rect += m_topleft_client + v2s32(35+30, 75+6);
-                       wchar_t* text = wgettext("Address/Port");
-                       Environment->addStaticText(text,
-                               rect, false, true, this, -1);
-                       delete[] text;
-               }
-               changeCtype("C");
-               {
-                       core::rect<s32> rect(0, 0, 230, 30);
-                       rect += m_topleft_client + v2s32(160+30, 75);
-                       gui::IGUIElement *e = 
-                       Environment->addEditBox(m_data->address.c_str(), rect, true,
-                                       this, GUI_ID_ADDRESS_INPUT);
-                       if(m_data->name != L"" && m_data->address == L"")
-                               Environment->setFocus(e);
-               }
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       rect += m_topleft_client + v2s32(m_size_client.X-60-100, 75);
-                       Environment->addEditBox(m_data->port.c_str(), rect, true,
-                                       this, GUI_ID_PORT_INPUT);
-               }
-               changeCtype("");
-               {
-                       core::rect<s32> rect(0, 0, 400, 20);
-                       rect += m_topleft_client + v2s32(160+30, 75+35);
-                       wchar_t* text = wgettext("Leave address blank to start a local server.");
-                       Environment->addStaticText(text,
-                               rect, false, true, this, -1);
-                       delete[] text;
-               }
-               // Start game button
-               {
-                       core::rect<s32> rect(0, 0, 180, 30);
-                       rect += m_topleft_client + v2s32(m_size_client.X-180-30,
-                                       m_size_client.Y-30-20);
-                       wchar_t* text = wgettext("Start Game / Connect");
-                       Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
-                               text);
-                       delete[] text;
-               }
-               /*
-                       Server section
-               */
-               // SERVER
-               {
-                       core::rect<s32> rect(0, 0, 10, m_size_server.Y);
-                       rect += m_topleft_server + v2s32(15, 0);
-                       const wchar_t *text = L"S\nE\nR\nV\nE\nR";
-                       gui::IGUIStaticText *t =
-                       Environment->addStaticText(text, rect, false, true, this, -1);
-                       t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               }
-               // Server parameters
-               {
-                       core::rect<s32> rect(0, 0, 250, 30);
-                       rect += m_topleft_server + v2s32(30+20+250+20, 20);
-                       wchar_t* text = wgettext("Creative Mode");
-                       Environment->addCheckBox(m_data->creative_mode, rect, this, GUI_ID_CREATIVE_CB,
-                               text);
-                       delete[] text;
-               }
-               {
-                       core::rect<s32> rect(0, 0, 250, 30);
-                       rect += m_topleft_server + v2s32(30+20+250+20, 40);
-                       wchar_t* text = wgettext("Enable Damage");
-                       Environment->addCheckBox(m_data->enable_damage, rect, this, GUI_ID_DAMAGE_CB,
-                               text);
-                       delete[] text;
-               }
-               #if USE_CURL
-               {
-                       core::rect<s32> rect(0, 0, 250, 30);
-                       rect += m_topleft_server + v2s32(30+20+250+20, 60);
-                       wchar_t* text = wgettext("Public");
-                       Environment->addCheckBox(m_data->enable_public, rect, this, GUI_ID_PUBLIC_CB,
-                               text);
-                       delete[] text;
-               }
-               #endif
-               // Delete world button
-               {
-                       core::rect<s32> rect(0, 0, 130, 30);
-                       rect += m_topleft_server + v2s32(30+20+250+20, 90);
-                       wchar_t* text = wgettext("Delete world");
-                       Environment->addButton(rect, this, GUI_ID_DELETE_WORLD_BUTTON,
-                                text );
-                       delete[] text;
-               }
-               // Create world button
-               {
-                       core::rect<s32> rect(0, 0, 130, 30);
-                       rect += m_topleft_server + v2s32(30+20+250+20+140, 90);
-                       wchar_t* text = wgettext("Create world");
-                       Environment->addButton(rect, this, GUI_ID_CREATE_WORLD_BUTTON,
-                                text );
-                       delete[] text;
-               }
-               // World selection listbox
-               {
-                       core::rect<s32> rect(0, 0, 250, 120);
-                       rect += m_topleft_server + v2s32(30+20, 10);
-                       gui::IGUIListBox *e = Environment->addListBox(rect, this,
-                                       GUI_ID_WORLD_LISTBOX);
-                       e->setDrawBackground(true);
-                       m_world_indices.clear();
-                       for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
-                               const WorldSpec &spec = m_data->worlds[wi];
-                               e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
-                               m_world_indices.push_back(wi);
-                       }
-                       e->setSelected(m_data->selected_world);
-               }
-               changeCtype("C");
-       }
-       else if(m_data->selected_tab == TAB_SETTINGS)
-       {
-               {
-                       core::rect<s32> rect(0, 0, 10, m_size_client.Y);
-                       rect += m_topleft_client + v2s32(15, 0);
-                       const wchar_t *text = L"S\nE\nT\nT\nI\nN\nG\nS";
-                       gui::IGUIStaticText *t =
-                       Environment->addStaticText(text, rect, false, true, this, -1);
-                       t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               }
-               s32 option_x = 70;
-               s32 option_y = 50;
-               u32 option_w = 150;
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += m_topleft_client + v2s32(option_x, option_y);
-                       wchar_t* text = wgettext("Fancy trees");
-                       Environment->addCheckBox(m_data->fancy_trees, rect, this,
-                                       GUI_ID_FANCYTREE_CB, text);
-                       delete[] text;
-               }
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += m_topleft_client + v2s32(option_x, option_y+20);
-                       wchar_t* text = wgettext("Smooth Lighting");
-                       Environment->addCheckBox(m_data->smooth_lighting, rect, this,
-                                       GUI_ID_SMOOTH_LIGHTING_CB, text);
-                       delete[] text;
-               }
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += m_topleft_client + v2s32(option_x, option_y+20*2);
-                       wchar_t* text = wgettext("3D Clouds");
-                       Environment->addCheckBox(m_data->clouds_3d, rect, this,
-                                       GUI_ID_3D_CLOUDS_CB, text);
-                       delete[] text;
-               }
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += m_topleft_client + v2s32(option_x, option_y+20*3);
-                       wchar_t* text = wgettext("Opaque water");
-                       Environment->addCheckBox(m_data->opaque_water, rect, this,
-                                       GUI_ID_OPAQUE_WATER_CB, text);
-                       delete[] text;
-               }
-
-
-               // Anisotropic/mipmap/bi-/trilinear settings
-
-               {
-                       core::rect<s32> rect(0, 0, option_w+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175, option_y);
-                       wchar_t* text = wgettext("Mip-Mapping");
-                       Environment->addCheckBox(m_data->mip_map, rect, this,
-                                      GUI_ID_MIPMAP_CB, text);
-                       delete[] text;
-               }
-
-               {
-                       core::rect<s32> rect(0, 0, option_w+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175, option_y+20);
-                       wchar_t* text = wgettext("Anisotropic Filtering");
-                       Environment->addCheckBox(m_data->anisotropic_filter, rect, this,
-                                      GUI_ID_ANISOTROPIC_CB, text);
-                       delete[] text;
-               }
-
-               {
-                       core::rect<s32> rect(0, 0, option_w+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175, option_y+20*2);
-                       wchar_t* text = wgettext("Bi-Linear Filtering");
-                       Environment->addCheckBox(m_data->bilinear_filter, rect, this,
-                                      GUI_ID_BILINEAR_CB, text);
-                       delete[] text;
-               }
-
-               {
-                       core::rect<s32> rect(0, 0, option_w+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175, option_y+20*3);
-                       wchar_t* text = wgettext("Tri-Linear Filtering");
-                       Environment->addCheckBox(m_data->trilinear_filter, rect, this,
-                                      GUI_ID_TRILINEAR_CB, text);
-                       delete[] text;
-               }
-
-               // shader/on demand image loading/particles settings
-               {
-                       core::rect<s32> rect(0, 0, option_w+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175*2, option_y);
-                       wchar_t* text = wgettext("Shaders");
-                       Environment->addCheckBox(m_data->enable_shaders, rect, this,
-                                       GUI_ID_SHADERS_CB, text);
-                       delete[] text;
-               }
-
-               {
-                       core::rect<s32> rect(0, 0, option_w+20+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175*2, option_y+20);
-                       wchar_t* text = wgettext("Preload item visuals");
-                       Environment->addCheckBox(m_data->preload_item_visuals, rect, this,
-                                       GUI_ID_PRELOAD_ITEM_VISUALS_CB, text);
-                       delete[] text;
-               }
-
-               {
-                       core::rect<s32> rect(0, 0, option_w+20+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175*2, option_y+20*2);
-                       wchar_t* text = wgettext("Enable Particles");
-                       Environment->addCheckBox(m_data->enable_particles, rect, this,
-                                       GUI_ID_ENABLE_PARTICLES_CB, text);
-                       delete[] text;
-               }
-
-               {
-                       core::rect<s32> rect(0, 0, option_w+20+20, 30);
-                       rect += m_topleft_client + v2s32(option_x+175*2, option_y+20*3);
-                       wchar_t* text = wgettext("Finite liquid");
-                       Environment->addCheckBox(m_data->liquid_finite, rect, this,
-                                       GUI_ID_LIQUID_FINITE_CB, text);
-                       delete[] text;
-               }
-
-               // Key change button
-               {
-                       core::rect<s32> rect(0, 0, 120, 30);
-                       /*rect += m_topleft_client + v2s32(m_size_client.X-120-30,
-                                       m_size_client.Y-30-20);*/
-                       rect += m_topleft_client + v2s32(option_x, option_y+120);
-                       wchar_t* text = wgettext("Change keys");
-                       Environment->addButton(rect, this,
-                                       GUI_ID_CHANGE_KEYS_BUTTON, text);
-                       delete[] text;
-               }
-               changeCtype("C");
-       }
-       else if(m_data->selected_tab == TAB_CREDITS)
-       {
-               // CREDITS
-               {
-                       core::rect<s32> rect(0, 0, 9, m_size_client.Y);
-                       rect += m_topleft_client + v2s32(15, 0);
-                       const wchar_t *text = L"C\nR\nE\nD\nI\nT\nS";
-                       gui::IGUIStaticText *t =
-                       Environment->addStaticText(text, rect, false, true, this, -1);
-                       t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               }
-               {
-                       core::rect<s32> rect(0, 0, 130, 70);
-                       rect += m_topleft_client + v2s32(35, 160);
-                       Environment->addStaticText(
-                               L"Minetest " LSTRING(VERSION_STRING) L"\nhttp://minetest.net/",
-                                rect, false, true, this, -1);
-               }
-               {
-                       video::SColor yellow(255, 255, 255, 0);
-                       core::rect<s32> rect(0, 0, 450, 260);
-                       rect += m_topleft_client + v2s32(168, 5);
-                       
-                       irr::gui::IGUIListBox *list = Environment->addListBox(rect, this);
-                       
-                       list->addItem(L"Core Developers");
-                       list->setItemOverrideColor(list->getItemCount() - 1, yellow);
-                       for (int i = 0; i != ARRAYLEN(contrib_core_strs); i++)
-                               list->addItem(contrib_core_strs[i]);
-                       list->addItem(L"");
-                       list->addItem(L"Active Contributors");
-                       list->setItemOverrideColor(list->getItemCount() - 1, yellow);
-                       for (int i = 0; i != ARRAYLEN(contrib_active_strs); i++)
-                               list->addItem(contrib_active_strs[i]);
-                       list->addItem(L"");
-                       list->addItem(L"Previous Contributors");
-                       list->setItemOverrideColor(list->getItemCount() - 1, yellow);
-                       for (int i = 0; i != ARRAYLEN(contrib_previous_strs); i++)
-                               list->addItem(contrib_previous_strs[i]);
-                       list->addItem(L"");
-               }
-       }
-
-       m_is_regenerating = false;
-}
-
-void GUIMainMenu::drawMenu()
-{
-       gui::IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-
-       /* Draw menu background */
-
-       /*video::SColor bgcolor(140,0,0,0);
-       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
-
-       video::SColor bgcolor(140,0,0,0);
-
-       if(getTab() == TAB_SINGLEPLAYER)
-       {
-               {
-                       core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
-                       rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
-                       driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
-               }
-       }
-       else if(getTab() == TAB_MULTIPLAYER)
-       {
-               {
-                       core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
-                       rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
-                       driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
-               }
-       }
-       else if(getTab() == TAB_ADVANCED)
-       {
-               {
-                       core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
-                       rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
-                       driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
-               }
-               {
-                       core::rect<s32> rect(0, 0, m_size_server.X, m_size_server.Y);
-                       rect += AbsoluteRect.UpperLeftCorner + m_topleft_server;
-                       driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
-               }
-       }
-       else if(getTab() == TAB_SETTINGS)
-       {
-               {
-                       core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
-                       rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
-                       driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
-               }
-       }
-       else if(getTab() == TAB_CREDITS)
-       {
-               {
-                       core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
-                       rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
-                       driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
-               }
-               video::ITexture *logotexture =
-                               driver->getTexture(getTexturePath("logo.png").c_str());
-               if(logotexture)
-               {
-                       v2s32 logosize(logotexture->getOriginalSize().Width,
-                                       logotexture->getOriginalSize().Height);
-
-                       core::rect<s32> rect(0,0,logosize.X,logosize.Y);
-                       rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
-                       rect += v2s32(50, 60);
-                       driver->draw2DImage(logotexture, rect,
-                               core::rect<s32>(core::position2d<s32>(0,0),
-                               core::dimension2di(logotexture->getSize())),
-                               NULL, NULL, true);
-               }
-       }
-
-       /* Draw UI elements */
-
-       gui::IGUIElement::draw();
-}
-
-void GUIMainMenu::readInput(MainMenuData *dst)
-{
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_TAB_CONTROL);
-               if(e != NULL && e->getType() == gui::EGUIET_TAB_CONTROL)
-                       dst->selected_tab = ((gui::IGUITabControl*)e)->getActiveTab();
-       }
-       if(dst->selected_tab == TAB_SINGLEPLAYER)
-       {
-               dst->simple_singleplayer_mode = true;
-       }
-       else
-       {
-               dst->simple_singleplayer_mode = false;
-               {
-                       gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
-                       if(e != NULL)
-                               dst->name = e->getText();
-               }
-               {
-                       gui::IGUIElement *e = getElementFromId(264);
-                       if(e != NULL)
-                               dst->password = e->getText();
-               }
-               {
-                       gui::IGUIElement *e = getElementFromId(GUI_ID_ADDRESS_INPUT);
-                       if(e != NULL)
-                               dst->address = e->getText();
-               }
-               {
-                       gui::IGUIElement *e = getElementFromId(GUI_ID_PORT_INPUT);
-                       if(e != NULL)
-                               dst->port = e->getText();
-               }
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_CREATIVE_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->creative_mode = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_DAMAGE_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_PUBLIC_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->enable_public = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->fancy_trees = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_SMOOTH_LIGHTING_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->smooth_lighting = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_3D_CLOUDS_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->clouds_3d = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_OPAQUE_WATER_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->opaque_water = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_MIPMAP_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->mip_map = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_ANISOTROPIC_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->anisotropic_filter = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_BILINEAR_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->bilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_TRILINEAR_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->trilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_SHADERS_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->enable_shaders = ((gui::IGUICheckBox*)e)->isChecked() ? 2 : 0;
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_PRELOAD_ITEM_VISUALS_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->preload_item_visuals = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_ENABLE_PARTICLES_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->enable_particles = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_LIQUID_FINITE_CB);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       dst->liquid_finite = ((gui::IGUICheckBox*)e)->isChecked();
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
-               if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX){
-                       int list_i = ((gui::IGUIListBox*)e)->getSelected();
-                       if(list_i == -1)
-                               dst->selected_world = -1;
-                       else
-                               dst->selected_world = m_world_indices[list_i];
-               }
-       }
-       {
-               ServerListSpec server =
-               getServerListSpec(wide_to_narrow(dst->address), wide_to_narrow(dst->port));
-               dst->servername = server["name"].asString();
-               dst->serverdescription = server["description"].asString();
-       }
-}
-
-void GUIMainMenu::acceptInput()
-{
-       readInput(m_data);
-       m_accepted = true;
-}
-
-bool GUIMainMenu::OnEvent(const SEvent& event)
-{
-       if(event.EventType==EET_KEY_INPUT_EVENT)
-       {
-               if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
-               {
-                       m_gamecallback->exitToOS();
-                       quitMenu();
-                       return true;
-               }
-               if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
-               {
-                       acceptInput();
-                       quitMenu();
-                       return true;
-               }
-       }
-       if(event.EventType==EET_GUI_EVENT)
-       {
-               if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
-                               && isVisible())
-               {
-                       if(!canTakeFocus(event.GUIEvent.Element))
-                       {
-                               dstream<<"GUIMainMenu: Not allowing focus change."
-                                               <<std::endl;
-                               // Returning true disables focus change
-                               return true;
-                       }
-               }
-               if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED)
-               {
-                       if(!m_is_regenerating)
-                               regenerateGui(m_screensize_old);
-                       return true;
-               }
-               if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED && event.GUIEvent.Caller->getID() == GUI_ID_SERVERLIST)
-               {
-                       serverListOnSelected();
-                       return true;
-               }
-               if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
-               {
-                       switch(event.GUIEvent.Caller->getID())
-                       {
-                       case GUI_ID_JOIN_GAME_BUTTON: {
-                               MainMenuData cur;
-                               readInput(&cur);
-                               if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
-                               {
-                                       wchar_t* text = wgettext("Address required.");
-                                       (new GUIMessageMenu(env, parent, -1, menumgr,
-                                                       text)
-                                                       )->drop();
-                                       delete[] text;
-                                       return true;
-                               }
-                               acceptInput();
-                               quitMenu();
-                               return true;
-                       }
-                       case GUI_ID_CHANGE_KEYS_BUTTON: {
-                               GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(env, parent, -1,menumgr);
-                               kmenu->drop();
-                               return true;
-                       }
-                       case GUI_ID_DELETE_WORLD_BUTTON: {
-                               MainMenuData cur;
-                               readInput(&cur);
-                               if(cur.selected_world == -1){
-                                       wchar_t* text = wgettext("Cannot delete world: Nothing selected");
-                                       (new GUIMessageMenu(env, parent, -1, menumgr,
-                                                       text)
-                                                       )->drop();
-                                       delete[] text;
-                               } else {
-                                       WorldSpec spec = m_data->worlds[cur.selected_world];
-                                       // Get files and directories involved
-                                       std::vector<std::string> paths;
-                                       paths.push_back(spec.path);
-                                       fs::GetRecursiveSubPaths(spec.path, paths);
-                                       // Launch confirmation dialog
-                                       ConfirmDestDeleteWorld *dest = new
-                                                       ConfirmDestDeleteWorld(spec, this, paths);
-                                       wchar_t* text1 = wgettext("Delete world");
-                                       wchar_t* text2 = wgettext("Files to be deleted");
-                                       std::wstring text = text1;
-                                       text += L" \"";
-                                       text += narrow_to_wide(spec.name);
-                                       text += L"\"?\n\n";
-                                       text += text2;
-                                       text += L":\n";
-                                       delete[] text1;
-                                       delete[] text2;
-                                       for(u32 i=0; i<paths.size(); i++){
-                                               if(i == 3){ text += L"..."; break; }
-                                               text += narrow_to_wide(paths[i]) + L"\n";
-                                       }
-                                       (new GUIConfirmMenu(env, parent, -1, menumgr, dest,
-                                                       text.c_str()))->drop();
-                               }
-                               return true;
-                       }
-                       case GUI_ID_CREATE_WORLD_BUTTON: {
-                               const std::vector<SubgameSpec> &games = m_data->games;
-                               if(games.size() == 0){
-                                       wchar_t* text = wgettext("Cannot create world: No games found");
-                                       GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
-                                                       -1, menumgr,
-                                                       text);
-                                       menu->drop();
-                                       delete[] text;
-                               } else {
-                                       CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
-                                       GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
-                                                       menumgr, dest, games, m_data->selected_game);
-                                       menu->drop();
-                               }
-                               return true;
-                       }
-                       case GUI_ID_CONFIGURE_WORLD_BUTTON: {
-                               MainMenuData cur;
-                               readInput(&cur);
-                               if(cur.selected_world == -1)
-                               {
-                                       wchar_t* text = wgettext("Cannot configure world: Nothing selected");
-                                       (new GUIMessageMenu(env, parent, -1, menumgr,
-                                                       text)
-                                                       )->drop();
-                                       delete[] text;
-                               } 
-                               else 
-                               {
-                                       WorldSpec wspec = m_data->worlds[cur.selected_world];
-                                       GUIConfigureWorld *menu = new GUIConfigureWorld(env, parent,
-                                                                               -1, menumgr, wspec);
-                                       menu->drop();
-                               }
-                               return true;
-                       }
-                       case GUI_ID_SERVERLIST_DELETE: {
-                               gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
-                               s32 selected = ((gui::IGUIListBox*)serverlist)->getSelected();
-                               if (selected == -1) return true;
-                               ServerList::deleteEntry(m_data->servers[selected]);
-                               m_data->servers = ServerList::getLocal();
-                               updateGuiServerList();
-                               if (selected > 0)
-                                       selected -= 1;
-                               serverlist->setSelected(selected);
-                               serverListOnSelected();
-                               return true;
-                       }
-                       #if USE_CURL
-                       case GUI_ID_SERVERLIST_TOGGLE: {
-                               gui::IGUIElement *togglebutton = getElementFromId(GUI_ID_SERVERLIST_TOGGLE);
-                               gui::IGUIElement *deletebutton = getElementFromId(GUI_ID_SERVERLIST_DELETE);
-                               gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
-                               gui::IGUIElement *title = getElementFromId(GUI_ID_SERVERLIST_TITLE);
-                               if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // switch to favorite list
-                               {
-                                       m_data->servers = ServerList::getLocal();
-                                       wchar_t* text1 = wgettext("Show Public");
-                                       wchar_t* text2 = wgettext("Favorites:");
-                                       togglebutton->setText(text1);
-                                       title->setText(text2);
-                                       delete[] text1;
-                                       delete[] text2;
-                                       deletebutton->setVisible(true);
-                                       updateGuiServerList();
-                                       serverlist->setSelected(0);
-                                       m_data->selected_serverlist = SERVERLIST_FAVORITES;
-                               }
-                               else // switch to online list
-                               {
-                                       m_data->servers = ServerList::getOnline();
-                                       wchar_t* text1 = wgettext("Show Favorites");
-                                       wchar_t* text2 = wgettext("Public Server List:");
-                                       togglebutton->setText(text1);
-                                       title->setText(text2);
-                                       delete[] text1;
-                                       delete[] text2;
-                                       deletebutton->setVisible(false);
-                                       updateGuiServerList();
-                                       serverlist->setSelected(0);
-                                       m_data->selected_serverlist = SERVERLIST_PUBLIC;
-                               }
-                               serverListOnSelected();
-                       }
-                       #endif
-                       }
-                       /* Game buttons */
-                       int eid = event.GUIEvent.Caller->getID();
-                       if(eid >= GUI_ID_GAME_BUTTON_FIRST &&
-                                       eid <= GUI_ID_GAME_BUTTON_MAX){
-                               m_data->selected_game =
-                                               m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].id;
-                               m_data->selected_game_name =
-                                               m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].name;
-                               regenerateGui(m_screensize_old);
-                       }
-               }
-               if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
-               {
-                       switch(event.GUIEvent.Caller->getID())
-                       {
-                               case GUI_ID_ADDRESS_INPUT: case GUI_ID_PORT_INPUT: case GUI_ID_NAME_INPUT: case 264:
-                               MainMenuData cur;
-                               readInput(&cur);
-                               if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
-                               {
-                                       wchar_t* text = wgettext("Address required.");
-                                       (new GUIMessageMenu(env, parent, -1, menumgr,
-                                                       text)
-                                                       )->drop();
-                                       delete[] text;
-                                       return true;
-                               }
-                               acceptInput();
-                               quitMenu();
-                               return true;
-                       }
-               }
-               if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED)
-               {
-                       readInput(m_data);
-               }
-               if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
-               {
-                       switch(event.GUIEvent.Caller->getID())
-                       {
-                       case GUI_ID_WORLD_LISTBOX:
-                               acceptInput();
-                               if(getTab() != TAB_SINGLEPLAYER)
-                                       m_data->address = L""; // Force local game
-                               quitMenu();
-                               return true;
-                       case GUI_ID_SERVERLIST:
-                               gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
-                               if (serverlist->getSelected() > -1)
-                               {
-                                       acceptInput();
-                                       quitMenu();
-                                       return true;
-                               }
-                       }
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
-void GUIMainMenu::createNewWorld(std::wstring name, std::string gameid)
-{
-       if(name == L"")
-               return;
-       acceptInput();
-       m_data->create_world_name = name;
-       m_data->create_world_gameid = gameid;
-       quitMenu();
-}
-
-void GUIMainMenu::deleteWorld(const std::vector<std::string> &paths)
-{
-       // Delete files
-       bool did = fs::DeletePaths(paths);
-       if(!did){
-               wchar_t* text = wgettext("Failed to delete all world files");
-               GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
-                               -1, menumgr, text);
-               delete[] text;
-               menu->drop();
-       }
-       // Quit menu to refresh it
-       acceptInput();
-       m_data->only_refresh = true;
-       quitMenu();
-}
-       
-int GUIMainMenu::getTab()
-{
-       gui::IGUIElement *e = getElementFromId(GUI_ID_TAB_CONTROL);
-       if(e != NULL && e->getType() == gui::EGUIET_TAB_CONTROL)
-               return ((gui::IGUITabControl*)e)->getActiveTab();
-       return TAB_SINGLEPLAYER; // Default
-}
-
-void GUIMainMenu::displayMessageMenu(std::wstring msg)
-{
-       (new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
-}
-
-void GUIMainMenu::updateGuiServerList()
-{
-       gui::IGUIListBox *serverlist = (gui::IGUIListBox *)getElementFromId(GUI_ID_SERVERLIST);
-       serverlist->clear();
-
-       for(std::vector<ServerListSpec>::iterator i = m_data->servers.begin();
-               i != m_data->servers.end(); i++)
-       {
-               std::string text;
-
-               if ((*i)["clients"].asString().size())
-                       text += (*i)["clients"].asString();
-               if ((*i)["clients_max"].asString().size())
-                       text += "/" + (*i)["clients_max"].asString();
-               text += " ";
-               if ((*i)["version"].asString().size())
-                       text += (*i)["version"].asString() + " ";
-               if ((*i)["password"].asString().size())
-                       text += "*";
-               if ((*i)["creative"].asString().size())
-                       text += "C";
-               if ((*i)["damage"].asString().size())
-                       text += "D";
-               if ((*i)["pvp"].asString().size())
-                       text += "P";
-               text += " ";
-
-               if ((*i)["name"] != "" && (*i)["description"] != "")
-                       text += (*i)["name"].asString() + " (" +  (*i)["description"].asString() + ")";
-               else if ((*i)["name"] !="")
-                       text += (*i)["name"].asString();
-               else
-                       text += (*i)["address"].asString() + ":" + (*i)["port"].asString();
-               
-               serverlist->addItem(narrow_to_wide(text).c_str());
-       }
-}
-
-void GUIMainMenu::serverListOnSelected()
-{
-       if (!m_data->servers.empty())
-       {
-               gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
-               u16 id = serverlist->getSelected();
-               //if (id < 0) return; // u16>0!
-               ((gui::IGUIEditBox*)getElementFromId(GUI_ID_ADDRESS_INPUT))
-               ->setText(narrow_to_wide(m_data->servers[id]["address"].asString()).c_str());
-               ((gui::IGUIEditBox*)getElementFromId(GUI_ID_PORT_INPUT))
-               ->setText(narrow_to_wide(m_data->servers[id]["port"].asString()).c_str());
-       }
-}
-
-ServerListSpec GUIMainMenu::getServerListSpec(std::string address, std::string port)
-{
-       ServerListSpec server;
-       server["address"] = address;
-       server["port"] = port;
-       for(std::vector<ServerListSpec>::iterator i = m_data->servers.begin();
-               i != m_data->servers.end(); i++)
-       {
-               if ((*i)["address"] == address && (*i)["port"] == port)
-               {
-                       server["description"] = (*i)["description"];
-                       server["name"] = (*i)["name"];
-                       break;
-               }
-       }
-       return server;
-}
index 8697344c84e763b493daa0b76e4a50951981a36b..5eaca23fa43cf500622b1072e078b0f75865b3fc 100644 (file)
@@ -24,15 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "modalMenu.h"
 #include <string>
 #include <list>
-#include "subgame.h"
-#include "serverlist.h"
-
-class IGameCallback;
-
-enum {
-       SERVERLIST_FAVORITES,
-       SERVERLIST_PUBLIC,
-};
 
 enum
 {
@@ -45,113 +36,31 @@ enum
 
 struct MainMenuData
 {
-       // These are in the native format of the gui elements
-       // Generic
-       int selected_tab;
-       std::string selected_game;
-       std::string selected_game_name;
        // Client options
        std::string servername;
        std::string serverdescription;
-       std::wstring address;
-       std::wstring port;
-       std::wstring name;
-       std::wstring password;
-       bool fancy_trees;
-       bool smooth_lighting;
-       bool clouds_3d;
-       bool opaque_water;
-       bool mip_map;
-       bool anisotropic_filter;
-       bool bilinear_filter;
-       bool trilinear_filter;
-       int enable_shaders;
-       bool preload_item_visuals;
-       bool enable_particles;
-       bool liquid_finite;
+       std::string address;
+       std::string port;
+       std::string name;
+       std::string password;
+
        // Server options
-       bool creative_mode;
-       bool enable_damage;
        bool enable_public;
        int selected_world;
        bool simple_singleplayer_mode;
-       // Actions
-       std::wstring create_world_name;
-       std::string create_world_gameid;
-       bool only_refresh;
-
-       int selected_serverlist;
 
-       std::vector<WorldSpec> worlds;
-       std::vector<SubgameSpec> games;
-       std::vector<ServerListSpec> servers;
+       // Actions
+       bool kill;
 
+       //error handling
+       std::string errormessage;
        MainMenuData():
-               // Generic
-               selected_tab(0),
-               selected_game("minetest"),
-               selected_game_name("Minetest"),
-               // Client opts
-               fancy_trees(false),
-               smooth_lighting(false),
-               // Server opts
-               creative_mode(false),
-               enable_damage(false),
                enable_public(false),
                selected_world(0),
                simple_singleplayer_mode(false),
-               // Actions
-               only_refresh(false),
-
-               selected_serverlist(SERVERLIST_FAVORITES)
+               errormessage("")
        {}
 };
 
-class GUIMainMenu : public GUIModalMenu
-{
-public:
-       GUIMainMenu(gui::IGUIEnvironment* env,
-                       gui::IGUIElement* parent, s32 id,
-                       IMenuManager *menumgr,
-                       MainMenuData *data,
-                       IGameCallback *gamecallback);
-       ~GUIMainMenu();
-       
-       void removeChildren();
-       // Remove and re-add (or reposition) stuff
-       void regenerateGui(v2u32 screensize);
-       void drawMenu();
-       void readInput(MainMenuData *dst);
-       void acceptInput();
-       bool getStatus()
-       { return m_accepted; }
-       bool OnEvent(const SEvent& event);
-       void createNewWorld(std::wstring name, std::string gameid);
-       void deleteWorld(const std::vector<std::string> &paths);
-       int getTab();
-       void displayMessageMenu(std::wstring msg);
-       
-private:
-       MainMenuData *m_data;
-       bool m_accepted;
-       IGameCallback *m_gamecallback;
-
-       gui::IGUIEnvironment* env;
-       gui::IGUIElement* parent;
-       s32 id;
-       IMenuManager *menumgr;
-
-       std::vector<int> m_world_indices;
-
-       bool m_is_regenerating;
-       v2s32 m_topleft_client;
-       v2s32 m_size_client;
-       v2s32 m_topleft_server;
-       v2s32 m_size_server;
-       void updateGuiServerList();
-       void serverListOnSelected();
-       ServerListSpec getServerListSpec(std::string address, std::string port);
-};
-
 #endif
 
index eda992793641949068475179dc0afc16a71bf240..02bffa84d5253bbf03639ad17c9dcc130d20e8fd 100644 (file)
@@ -78,6 +78,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serverlist.h"
 #include "sound.h"
 #include "sound_openal.h"
+#include "guiEngine.h"
 
 /*
        Settings.
@@ -660,180 +661,6 @@ class RandomInputHandler : public InputHandler
        bool rightreleased;
 };
 
-struct MenuTextures
-{
-       std::string current_gameid;
-       bool global_textures;
-       video::ITexture *background;
-       video::ITexture *overlay;
-       video::ITexture *header;
-       video::ITexture *footer;
-
-       MenuTextures():
-               global_textures(false),
-               background(NULL),
-               overlay(NULL),
-               header(NULL),
-               footer(NULL)
-       {}
-
-       static video::ITexture* getMenuTexture(const std::string &tname,
-                       video::IVideoDriver* driver, const SubgameSpec *spec)
-       {
-               if(spec){
-                       std::string path;
-                       // eg. minetest_menu_background.png (for texture packs)
-                       std::string pack_tname = spec->id + "_menu_" + tname + ".png";
-                       path = getTexturePath(pack_tname);
-                       if(path != "")
-                               return driver->getTexture(path.c_str());
-                       // eg. games/minetest_game/menu/background.png
-                       path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
-                       if(path != "")
-                               return driver->getTexture(path.c_str());
-               } else {
-                       std::string path;
-                       // eg. menu_background.png
-                       std::string pack_tname = "menu_" + tname + ".png";
-                       path = getTexturePath(pack_tname);
-                       if(path != "")
-                               return driver->getTexture(path.c_str());
-               }
-               return NULL;
-       }
-
-       void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
-       {
-               if(tab == TAB_SINGLEPLAYER){
-                       if(spec->id == current_gameid)
-                               return;
-                       current_gameid = spec->id;
-                       global_textures = false;
-                       background = getMenuTexture("background", driver, spec);
-                       overlay = getMenuTexture("overlay", driver, spec);
-                       header = getMenuTexture("header", driver, spec);
-                       footer = getMenuTexture("footer", driver, spec);
-               } else {
-                       if(global_textures)
-                               return;
-                       current_gameid = "";
-                       global_textures = true;
-                       background = getMenuTexture("background", driver, NULL);
-                       overlay = getMenuTexture("overlay", driver, NULL);
-                       header = getMenuTexture("header", driver, NULL);
-                       footer = getMenuTexture("footer", driver, NULL);
-               }
-       }
-};
-
-void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
-       v2u32 screensize = driver->getScreenSize();
-       video::ITexture *texture = menutextures.background;
-
-       /* If no texture, draw background of solid color */
-       if(!texture){
-               video::SColor color(255,80,58,37);
-               core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
-               driver->draw2DRectangle(color, rect, NULL);
-               return;
-       }
-
-       /* Draw background texture */
-       v2u32 sourcesize = texture->getSize();
-       driver->draw2DImage(texture,
-               core::rect<s32>(0, 0, screensize.X, screensize.Y),
-               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
-               NULL, NULL, true);
-}
-
-void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
-       v2u32 screensize = driver->getScreenSize();
-       video::ITexture *texture = menutextures.overlay;
-
-       /* If no texture, draw nothing */
-       if(!texture)
-               return;
-
-       /* Draw overlay texture */
-       v2u32 sourcesize = texture->getSize();
-       driver->draw2DImage(texture,
-               core::rect<s32>(0, 0, screensize.X, screensize.Y),
-               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
-               NULL, NULL, true);
-}
-
-void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
-       core::dimension2d<u32> screensize = driver->getScreenSize();
-       video::ITexture *texture = menutextures.header;
-
-       /* If no texture, draw nothing */
-       if(!texture)
-               return;
-
-       f32 mult = (((f32)screensize.Width / 2)) /
-               ((f32)texture->getOriginalSize().Width);
-
-       v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
-                       ((f32)texture->getOriginalSize().Height) * mult);
-
-       // Don't draw the header is there isn't enough room
-       s32 free_space = (((s32)screensize.Height)-320)/2;
-       if (free_space > splashsize.Y) {
-               core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
-               splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
-                       ((free_space/2)-splashsize.Y/2)+10);
-
-               video::SColor bgcolor(255,50,50,50);
-
-               driver->draw2DImage(texture, splashrect,
-                       core::rect<s32>(core::position2d<s32>(0,0),
-                       core::dimension2di(texture->getSize())),
-                       NULL, NULL, true);
-       }
-}
-
-void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
-{
-       core::dimension2d<u32> screensize = driver->getScreenSize();
-       video::ITexture *texture = menutextures.footer;
-
-       /* If no texture, draw nothing */
-       if(!texture)
-               return;
-
-       f32 mult = (((f32)screensize.Width)) /
-               ((f32)texture->getOriginalSize().Width);
-
-       v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
-                       ((f32)texture->getOriginalSize().Height) * mult);
-
-       // Don't draw the footer if there isn't enough room
-       s32 free_space = (((s32)screensize.Height)-320)/2;
-       if (free_space > footersize.Y) {
-               core::rect<s32> rect(0,0,footersize.X,footersize.Y);
-               rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
-               rect -= v2s32(footersize.X/2, 0);
-
-               driver->draw2DImage(texture, rect,
-                       core::rect<s32>(core::position2d<s32>(0,0),
-                       core::dimension2di(texture->getSize())),
-                       NULL, NULL, true);
-       }
-}
-
-static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
-{
-       for(size_t i=0; i<menudata.games.size(); i++){
-               if(menudata.games[i].id == menudata.selected_game){
-                       return &menudata.games[i];
-               }
-       }
-       return NULL;
-}
-
 #endif // !SERVER
 
 // These are defined global so that they're not optimized too much.
@@ -1730,83 +1557,37 @@ int main(int argc, char *argv[])
                                
                                // Initialize menu data
                                MainMenuData menudata;
-                               if(g_settings->exists("selected_mainmenu_tab"))
-                                       menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
-                               if(g_settings->exists("selected_serverlist"))
-                                       menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
-                               if(g_settings->exists("selected_mainmenu_game")){
-                                       menudata.selected_game = g_settings->get("selected_mainmenu_game");
-                                       menudata.selected_game_name = findSubgame(menudata.selected_game).name;
-                               }
-                               menudata.address = narrow_to_wide(address);
-                               menudata.name = narrow_to_wide(playername);
-                               menudata.port = narrow_to_wide(itos(port));
+                               menudata.kill = kill;
+                               menudata.address = address;
+                               menudata.name = playername;
+                               menudata.port = itos(port);
+                               menudata.errormessage = wide_to_narrow(error_message);
+                               error_message = L"";
                                if(cmd_args.exists("password"))
-                                       menudata.password = narrow_to_wide(cmd_args.get("password"));
-                               menudata.fancy_trees = g_settings->getBool("new_style_leaves");
-                               menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
-                               menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
-                               menudata.opaque_water = g_settings->getBool("opaque_water");
-                               menudata.mip_map = g_settings->getBool("mip_map");
-                               menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
-                               menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
-                               menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
-                               menudata.enable_shaders = g_settings->getS32("enable_shaders");
-                               menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
-                               menudata.enable_particles = g_settings->getBool("enable_particles");
-                               menudata.liquid_finite = g_settings->getBool("liquid_finite");
-                               driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
-                               menudata.creative_mode = g_settings->getBool("creative_mode");
-                               menudata.enable_damage = g_settings->getBool("enable_damage");
+                                       menudata.password = cmd_args.get("password");
+
+                               driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
+
                                menudata.enable_public = g_settings->getBool("server_announce");
-                               // Default to selecting nothing
-                               menudata.selected_world = -1;
-                               // Get world listing for the menu
+
                                std::vector<WorldSpec> worldspecs = getAvailableWorlds();
-                               // If there is only one world, select it
-                               if(worldspecs.size() == 1){
-                                       menudata.selected_world = 0;
-                               }
-                               // Otherwise try to select according to selected_world_path
-                               else if(g_settings->exists("selected_world_path")){
-                                       std::string trypath = g_settings->get("selected_world_path");
-                                       for(u32 i=0; i<worldspecs.size(); i++){
-                                               if(worldspecs[i].path == trypath){
-                                                       menudata.selected_world = i;
-                                                       break;
-                                               }
-                                       }
-                               }
+
                                // If a world was commanded, append and select it
                                if(commanded_world != ""){
+
                                        std::string gameid = getWorldGameId(commanded_world, true);
                                        std::string name = _("[--world parameter]");
                                        if(gameid == ""){
                                                gameid = g_settings->get("default_game");
                                                name += " [new]";
                                        }
-                                       WorldSpec spec(commanded_world, name, gameid);
-                                       worldspecs.push_back(spec);
-                                       menudata.selected_world = worldspecs.size()-1;
-                               }
-                               // Copy worldspecs to menu
-                               menudata.worlds = worldspecs;
-                               // Get game listing
-                               menudata.games = getAvailableGames();
-                               // If selected game doesn't exist, take first from list
-                               if(findSubgame(menudata.selected_game).id == "" &&
-                                               !menudata.games.empty()){
-                                       menudata.selected_game = menudata.games[0].id;
+                                       //TODO find within worldspecs and set config
                                }
-                               const SubgameSpec *menugame = getMenuGame(menudata);
-
-                               MenuTextures menutextures;
-                               menutextures.update(driver, menugame, menudata.selected_tab);
 
                                if(skip_main_menu == false)
                                {
                                        video::IVideoDriver* driver = device->getVideoDriver();
-                                       float fps_max = g_settings->getFloat("fps_max");
+
                                        infostream<<"Waiting for other menus"<<std::endl;
                                        while(device->run() && kill == false)
                                        {
@@ -1814,7 +1595,6 @@ int main(int argc, char *argv[])
                                                        break;
                                                driver->beginScene(true, true,
                                                                video::SColor(255,128,128,128));
-                                               drawMenuBackground(driver, menutextures);
                                                guienv->drawAll();
                                                driver->endScene();
                                                // On some computers framerate doesn't seem to be
@@ -1823,170 +1603,40 @@ int main(int argc, char *argv[])
                                        }
                                        infostream<<"Waited for other menus"<<std::endl;
 
-                                       GUIMainMenu *menu =
-                                                       new GUIMainMenu(guienv, guiroot, -1, 
-                                                               &g_menumgr, &menudata, g_gamecallback);
-                                       menu->allowFocusRemoval(true);
-
-                                       if(error_message != L"")
-                                       {
-                                               verbosestream<<"error_message = "
-                                                               <<wide_to_narrow(error_message)<<std::endl;
-
-                                               GUIMessageMenu *menu2 =
-                                                               new GUIMessageMenu(guienv, guiroot, -1, 
-                                                                       &g_menumgr, error_message.c_str());
-                                               menu2->drop();
-                                               error_message = L"";
-                                       }
-
-                                       // Time is in milliseconds, for clouds
-                                       u32 lasttime = device->getTimer()->getTime();
+                                       GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata);
+                                       
+                                       delete temp;
+                                       //once finished you'll never end up here
+                                       smgr->clear();
+                                       kill = menudata.kill;
 
-                                       MenuMusicFetcher soundfetcher;
-                                       ISoundManager *sound = NULL;
-#if USE_SOUND
-                                       sound = createOpenALSoundManager(&soundfetcher);
-#endif
-                                       if(!sound)
-                                               sound = &dummySoundManager;
-                                       SimpleSoundSpec spec;
-                                       spec.name = "main_menu";
-                                       spec.gain = 1;
-                                       s32 handle = sound->playSound(spec, true);
-
-                                       infostream<<"Created main menu"<<std::endl;
-
-                                       while(device->run() && kill == false)
-                                       {
-                                               if(menu->getStatus() == true)
-                                                       break;
-
-                                               // Game can be selected in the menu
-                                               menugame = getMenuGame(menudata);
-                                               menutextures.update(driver, menugame, menu->getTab());
-                                               // Clouds for the main menu
-                                               bool cloud_menu_background = g_settings->getBool("menu_clouds");
-                                               if(menugame){
-                                                       // If game has regular background and no overlay, don't use clouds
-                                                       if(cloud_menu_background && menutextures.background &&
-                                                                       !menutextures.overlay){
-                                                               cloud_menu_background = false;
-                                                       }
-                                                       // If game game has overlay and no regular background, always draw clouds
-                                                       else if(menutextures.overlay && !menutextures.background){
-                                                               cloud_menu_background = true;
-                                                       }
-                                               }
-
-                                               // Time calc for the clouds
-                                               f32 dtime=0; // in seconds
-                                               if (cloud_menu_background) {
-                                                       u32 time = device->getTimer()->getTime();
-                                                       if(time > lasttime)
-                                                               dtime = (time - lasttime) / 1000.0;
-                                                       else
-                                                               dtime = 0;
-                                                       lasttime = time;
-                                               }
-
-                                               //driver->beginScene(true, true, video::SColor(255,0,0,0));
-                                               driver->beginScene(true, true, video::SColor(255,140,186,250));
-
-                                               if (cloud_menu_background) {
-                                                       // *3 otherwise the clouds would move very slowly
-                                                       g_menuclouds->step(dtime*3); 
-                                                       g_menuclouds->render();
-                                                       g_menucloudsmgr->drawAll();
-                                                       drawMenuOverlay(driver, menutextures);
-                                                       drawMenuHeader(driver, menutextures);
-                                                       drawMenuFooter(driver, menutextures);
-                                               } else {
-                                                       drawMenuBackground(driver, menutextures);
-                                                       drawMenuHeader(driver, menutextures);
-                                                       drawMenuFooter(driver, menutextures);
-                                               }
-
-                                               guienv->drawAll();
-
-                                               driver->endScene();
-                                               
-                                               // On some computers framerate doesn't seem to be
-                                               // automatically limited
-                                               if (cloud_menu_background) {
-                                                       // Time of frame without fps limit
-                                                       float busytime;
-                                                       u32 busytime_u32;
-                                                       // not using getRealTime is necessary for wine
-                                                       u32 time = device->getTimer()->getTime();
-                                                       if(time > lasttime)
-                                                               busytime_u32 = time - lasttime;
-                                                       else
-                                                               busytime_u32 = 0;
-                                                       busytime = busytime_u32 / 1000.0;
-
-                                                       // FPS limiter
-                                                       u32 frametime_min = 1000./fps_max;
-                       
-                                                       if(busytime_u32 < frametime_min) {
-                                                               u32 sleeptime = frametime_min - busytime_u32;
-                                                               device->sleep(sleeptime);
-                                                       }
-                                               } else {
-                                                       sleep_ms(25);
-                                               }
-                                       }
-                                       sound->stopSound(handle);
-                                       if(sound != &dummySoundManager){
-                                               delete sound;
-                                               sound = NULL;
-                                       }
-
-                                       // Save controls status
-                                       menu->readInput(&menudata);
+                               }
 
-                                       infostream<<"Dropping main menu"<<std::endl;
+                               //update worldspecs (necessary as new world may have been created)
+                               worldspecs = getAvailableWorlds();
 
-                                       menu->drop();
-                               }
+                               if (menudata.name == "")
+                                       menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
+                               else
+                                       playername = menudata.name;
 
-                               playername = wide_to_narrow(menudata.name);
-                               if (playername == "")
-                                       playername = std::string("Guest") + itos(myrand_range(1000,9999));
-                               password = translatePassword(playername, menudata.password);
+                               password = translatePassword(playername, narrow_to_wide(menudata.password));
                                //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
 
-                               address = wide_to_narrow(menudata.address);
-                               int newport = stoi(wide_to_narrow(menudata.port));
+                               address = menudata.address;
+                               int newport = stoi(menudata.port);
                                if(newport != 0)
                                        port = newport;
+
                                simple_singleplayer_mode = menudata.simple_singleplayer_mode;
+
                                // Save settings
-                               g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
-                               g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
-                               g_settings->set("selected_mainmenu_game", menudata.selected_game);
-                               g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
-                               g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
-                               g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
-                               g_settings->set("opaque_water", itos(menudata.opaque_water));
-
-                               g_settings->set("mip_map", itos(menudata.mip_map));
-                               g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
-                               g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
-                               g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
-
-                               g_settings->setS32("enable_shaders", menudata.enable_shaders);
-                               g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
-                               g_settings->set("enable_particles", itos(menudata.enable_particles));
-                               g_settings->set("liquid_finite", itos(menudata.liquid_finite));
-
-                               g_settings->set("creative_mode", itos(menudata.creative_mode));
-                               g_settings->set("enable_damage", itos(menudata.enable_damage));
-                               g_settings->set("server_announce", itos(menudata.enable_public));
                                g_settings->set("name", playername);
                                g_settings->set("address", address);
                                g_settings->set("port", itos(port));
-                               if(menudata.selected_world != -1)
+
+                               if((menudata.selected_world >= 0) &&
+                                               (menudata.selected_world < worldspecs.size()))
                                        g_settings->set("selected_world_path",
                                                        worldspecs[menudata.selected_world].path);
 
@@ -2010,8 +1660,8 @@ int main(int argc, char *argv[])
                                {
                                        ServerListSpec server;
                                        server["name"] = menudata.servername;
-                                       server["address"] = wide_to_narrow(menudata.address);
-                                       server["port"] = wide_to_narrow(menudata.port);
+                                       server["address"] = menudata.address;
+                                       server["port"] = menudata.port;
                                        server["description"] = menudata.serverdescription;
                                        ServerList::insert(server);
                                }
@@ -2022,30 +1672,7 @@ int main(int argc, char *argv[])
                                        infostream<<"Selected world: "<<worldspec.name
                                                        <<" ["<<worldspec.path<<"]"<<std::endl;
                                }
-
-                               // Only refresh if so requested
-                               if(menudata.only_refresh){
-                                       infostream<<"Refreshing menu"<<std::endl;
-                                       continue;
-                               }
                                
-                               // Create new world if requested
-                               if(menudata.create_world_name != L"")
-                               {
-                                       std::string path = porting::path_user + DIR_DELIM
-                                                       "worlds" + DIR_DELIM
-                                                       + wide_to_narrow(menudata.create_world_name);
-                                       // Create world if it doesn't exist
-                                       if(!initializeWorld(path, menudata.create_world_gameid)){
-                                               error_message = wgettext("Failed to initialize world");
-                                               errorstream<<wide_to_narrow(error_message)<<std::endl;
-                                               continue;
-                                       }
-                                       g_settings->set("selected_world_path", path);
-                                       g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
-                                       continue;
-                               }
-
                                // If local game
                                if(current_address == "")
                                {
@@ -2085,8 +1712,10 @@ int main(int argc, char *argv[])
                        }
                        
                        // Break out of menu-game loop to shut down cleanly
-                       if(device->run() == false || kill == true)
+                       if(device->run() == false || kill == true) {
+                               g_settings->updateConfigFile(configpath.c_str());
                                break;
+                       }
 
                        /*
                                Run game
@@ -2138,11 +1767,11 @@ int main(int argc, char *argv[])
                        break;
                }
        } // Menu-game loop
-       
-       
+
+
        g_menuclouds->drop();
        g_menucloudsmgr->drop();
-       
+
        delete input;
 
        /*
index cf7dd6f9fd94fef1e9b6febffe1d3c93d1af0d44..272bc9322a99278f675b9ff84be9966a37c85fa7 100644 (file)
@@ -3237,17 +3237,18 @@ v2s16 ServerMap::getSectorPos(std::string dirname)
 {
        unsigned int x, y;
        int r;
-       size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
-       assert(spos != std::string::npos);
-       if(dirname.size() - spos == 8)
+       std::string component;
+       fs::RemoveLastPathComponent(dirname, &component, 1);
+       if(component.size() == 8)
        {
                // Old layout
-               r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
+               r = sscanf(component.c_str(), "%4x%4x", &x, &y);
        }
-       else if(dirname.size() - spos == 3)
+       else if(component.size() == 3)
        {
                // New layout
-               r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
+               fs::RemoveLastPathComponent(dirname, &component, 2);
+               r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
                // Sign-extend the 12 bit values up to 16 bits...
                if(x&0x800) x|=0xF000;
                if(y&0x800) y|=0xF000;
index 9bcf73aa7c303fa35dc50a4579618f8094133327..75c2dd89cd636ce6cf0434ddff1741b07ca0724d 100644 (file)
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "mods.h"
+#include "main.h"
 #include "filesys.h"
 #include "strfnd.h"
 #include "log.h"
@@ -25,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "strfnd.h"
 #include <cctype>
+#include "convert_json.h"
 
 static bool parseDependsLine(std::istream &is,
                std::string &dep, std::set<char> &symbols)
@@ -389,3 +391,29 @@ void ModConfiguration::resolveDependencies()
        // Step 4: write back list of unsatisfied mods
        m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
 }
+
+#if USE_CURL
+Json::Value getModstoreUrl(std::string url)
+{
+       struct curl_slist *chunk = NULL;
+
+       bool special_http_header = true;
+
+       try{
+               special_http_header = g_settings->getBool("modstore_disable_special_http_header");
+       }
+       catch(SettingNotFoundException &e) {
+       }
+
+       if (special_http_header)
+               chunk = curl_slist_append(chunk, "Accept: application/vnd.minetest.mmdb-v1+json");
+
+       Json::Value retval = fetchJsonValue(url,chunk);
+
+       if (chunk != NULL)
+               curl_slist_free_all(chunk);
+
+       return retval;
+}
+
+#endif
index a8100fcfd8f0ca27c1f93bfcaa126197c5d6ac73..eb453bf6a3e9cf51dbbb6d14faf7ebf365b0d9cf 100644 (file)
@@ -29,6 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <map>
 #include <exception>
 #include <list>
+#include "json/json.h"
+#include "config.h"
+
+#if USE_CURL
+#include <curl/curl.h>
+#endif
 
 #define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
 
@@ -154,4 +160,66 @@ class ModConfiguration
 
 };
 
+#if USE_CURL
+Json::Value getModstoreUrl(std::string url);
+#else
+inline Json::Value getModstoreUrl(std::string url) {
+       return Json::Value();
+}
+#endif
+
+struct ModLicenseInfo {
+       int id;
+       std::string shortinfo;
+       std::string url;
+};
+
+struct ModAuthorInfo {
+       int id;
+       std::string username;
+};
+
+struct ModStoreMod {
+       int id;
+       std::string title;
+       std::string basename;
+       ModAuthorInfo author;
+       float rating;
+       bool valid;
+};
+
+struct ModStoreCategoryInfo {
+       int id;
+       std::string name;
+};
+
+struct ModStoreVersionEntry {
+       int id;
+       std::string date;
+       std::string file;
+       bool approved;
+       //ugly version number
+       int mtversion;
+};
+
+struct ModStoreModDetails {
+       /* version_set?? */
+       std::vector<ModStoreCategoryInfo> categories;
+       ModAuthorInfo author;
+       ModLicenseInfo license;
+       int id;
+       std::string title;
+       std::string basename;
+       std::string description;
+       std::string repository;
+       float rating;
+       std::vector<std::string> depends;
+       std::vector<std::string> softdeps;
+
+       std::string download_url;
+       std::string screenshot_url;
+       std::vector<ModStoreVersionEntry> versions;
+       bool valid;
+};
+
 #endif
index ea5a616c249d8bad66cd9792b16d708f6a270ab5..6a68e0eacc0ad31febfe82e859f6b459e248c0e4 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "log.h"
 #include "json/json.h"
+#include "convert_json.h"
 #if USE_CURL
 #include <curl/curl.h>
 #endif
@@ -68,35 +69,22 @@ std::vector<ServerListSpec> getLocal()
 
 
 #if USE_CURL
-
-static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
-{
-    ((std::string*)userp)->append((char*)contents, size * nmemb);
-    return size * nmemb;
-}
-
-
 std::vector<ServerListSpec> getOnline()
 {
-       std::string liststring;
-       CURL *curl;
+       Json::Value root = fetchJsonValue((g_settings->get("serverlist_url")+"/list").c_str(),0);
 
-       curl = curl_easy_init();
-       if (curl)
-       {
-               CURLcode res;
-
-               curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
-               curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+"/list").c_str());
-               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::WriteCallback);
-               curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
+       std::vector<ServerListSpec> serverlist;
 
-               res = curl_easy_perform(curl);
-               if (res != CURLE_OK)
-                       errorstream<<"Serverlist at url "<<g_settings->get("serverlist_url")<<" not found (internet connection?)"<<std::endl;
-               curl_easy_cleanup(curl);
+       if (root.isArray()) {
+               for (unsigned int i = 0; i < root.size(); i++)
+               {
+                       if (root[i].isObject()) {
+                               serverlist.push_back(root[i]);
+                       }
+               }
        }
-       return ServerList::deSerializeJson(liststring);
+
+       return serverlist;
 }
 
 #endif
@@ -189,30 +177,6 @@ std::string serialize(std::vector<ServerListSpec> serverlist)
        return liststring;
 }
 
-std::vector<ServerListSpec> deSerializeJson(std::string liststring)
-{
-       std::vector<ServerListSpec> serverlist;
-       Json::Value root;
-       Json::Reader reader;
-       std::istringstream stream(liststring);
-       if (!liststring.size()) {
-               return serverlist;
-       }
-       if (!reader.parse( stream, root ) )
-       {
-               errorstream  << "Failed to parse server list " << reader.getFormattedErrorMessages();
-               return serverlist;
-       }
-       if (root["list"].isArray())
-           for (unsigned int i = 0; i < root["list"].size(); i++)
-       {
-               if (root["list"][i].isObject()) {
-                       serverlist.push_back(root["list"][i]);
-               }
-       }
-       return serverlist;
-}
-
 std::string serializeJson(std::vector<ServerListSpec> serverlist)
 {
        Json::Value root;
index b66f65dafc6841fa912932bd824e1a9baffa181a..e609fe26f6c7e6ef705c8623ccc1ea44cc8aa3b4 100644 (file)
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "log.h"
 #include "util/string.h"
+#include "filesys.h"
 #include "voxelalgorithms.h"
 #include "inventory.h"
 #include "util/numeric.h"
@@ -171,6 +172,212 @@ struct TestUtilities: public TestBase
        }
 };
 
+struct TestPath: public TestBase
+{
+       // adjusts a POSIX path to system-specific conventions
+       // -> changes '/' to DIR_DELIM
+       // -> absolute paths start with "C:\\" on windows
+       std::string p(std::string path)
+       {
+               for(size_t i = 0; i < path.size(); ++i){
+                       if(path[i] == '/'){
+                               path.replace(i, 1, DIR_DELIM);
+                               i += std::string(DIR_DELIM).size() - 1; // generally a no-op
+                       }
+               }
+
+               #ifdef _WIN32
+               if(path[0] == '\\')
+                       path = "C:" + path;
+               #endif
+
+               return path;
+       }
+
+       void Run()
+       {
+               std::string path, result, removed;
+
+               /*
+                       Test fs::IsDirDelimiter
+               */
+               UASSERT(fs::IsDirDelimiter('/') == true);
+               UASSERT(fs::IsDirDelimiter('A') == false);
+               UASSERT(fs::IsDirDelimiter(0) == false);
+               #ifdef _WIN32
+               UASSERT(fs::IsDirDelimiter('\\') == true);
+               #else
+               UASSERT(fs::IsDirDelimiter('\\') == false);
+               #endif
+
+               /*
+                       Test fs::PathStartsWith
+               */
+               {
+                       const int numpaths = 12;
+                       std::string paths[numpaths] = {
+                               "",
+                               p("/"),
+                               p("/home/user/minetest"),
+                               p("/home/user/minetest/bin"),
+                               p("/home/user/.minetest"),
+                               p("/tmp/dir/file"),
+                               p("/tmp/file/"),
+                               p("/tmP/file"),
+                               p("/tmp"),
+                               p("/tmp/dir"),
+                               p("/home/user2/minetest/worlds"),
+                               p("/home/user2/minetest/world"),
+                       };
+                       /*
+                               expected fs::PathStartsWith results
+                               0 = returns false
+                               1 = returns true
+                               2 = returns false on windows, false elsewhere
+                               3 = returns true on windows, true elsewhere
+                               4 = returns true if and only if
+                                   FILESYS_CASE_INSENSITIVE is true
+                       */
+                       int expected_results[numpaths][numpaths] = {
+                               {1,2,0,0,0,0,0,0,0,0,0,0},
+                               {1,1,0,0,0,0,0,0,0,0,0,0},
+                               {1,1,1,0,0,0,0,0,0,0,0,0},
+                               {1,1,1,1,0,0,0,0,0,0,0,0},
+                               {1,1,0,0,1,0,0,0,0,0,0,0},
+                               {1,1,0,0,0,1,0,0,1,1,0,0},
+                               {1,1,0,0,0,0,1,4,1,0,0,0},
+                               {1,1,0,0,0,0,4,1,4,0,0,0},
+                               {1,1,0,0,0,0,0,0,1,0,0,0},
+                               {1,1,0,0,0,0,0,0,1,1,0,0},
+                               {1,1,0,0,0,0,0,0,0,0,1,0},
+                               {1,1,0,0,0,0,0,0,0,0,0,1},
+                       };
+
+                       for (int i = 0; i < numpaths; i++)
+                       for (int j = 0; j < numpaths; j++){
+                               /*verbosestream<<"testing fs::PathStartsWith(\""
+                                       <<paths[i]<<"\", \""
+                                       <<paths[j]<<"\")"<<std::endl;*/
+                               bool starts = fs::PathStartsWith(paths[i], paths[j]);
+                               int expected = expected_results[i][j];
+                               if(expected == 0){
+                                       UASSERT(starts == false);
+                               }
+                               else if(expected == 1){
+                                       UASSERT(starts == true);
+                               }
+                               #ifdef _WIN32
+                               else if(expected == 2){
+                                       UASSERT(starts == false);
+                               }
+                               else if(expected == 3){
+                                       UASSERT(starts == true);
+                               }
+                               #else
+                               else if(expected == 2){
+                                       UASSERT(starts == true);
+                               }
+                               else if(expected == 3){
+                                       UASSERT(starts == false);
+                               }
+                               #endif
+                               else if(expected == 4){
+                                       UASSERT(starts == (bool)FILESYS_CASE_INSENSITIVE);
+                               }
+                       }
+               }
+
+               /*
+                       Test fs::RemoveLastPathComponent
+               */
+               UASSERT(fs::RemoveLastPathComponent("") == "");
+               path = p("/home/user/minetest/bin/..//worlds/world1");
+               result = fs::RemoveLastPathComponent(path, &removed, 0);
+               UASSERT(result == path);
+               UASSERT(removed == "");
+               result = fs::RemoveLastPathComponent(path, &removed, 1);
+               UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
+               UASSERT(removed == p("world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 2);
+               UASSERT(result == p("/home/user/minetest/bin/.."));
+               UASSERT(removed == p("worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 3);
+               UASSERT(result == p("/home/user/minetest/bin"));
+               UASSERT(removed == p("../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 4);
+               UASSERT(result == p("/home/user/minetest"));
+               UASSERT(removed == p("bin/../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 5);
+               UASSERT(result == p("/home/user"));
+               UASSERT(removed == p("minetest/bin/../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 6);
+               UASSERT(result == p("/home"));
+               UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 7);
+               #ifdef _WIN32
+               UASSERT(result == "C:");
+               #else
+               UASSERT(result == "");
+               #endif
+               UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
+
+               /*
+                       Now repeat the test with a trailing delimiter
+               */
+               path = p("/home/user/minetest/bin/..//worlds/world1/");
+               result = fs::RemoveLastPathComponent(path, &removed, 0);
+               UASSERT(result == path);
+               UASSERT(removed == "");
+               result = fs::RemoveLastPathComponent(path, &removed, 1);
+               UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
+               UASSERT(removed == p("world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 2);
+               UASSERT(result == p("/home/user/minetest/bin/.."));
+               UASSERT(removed == p("worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 3);
+               UASSERT(result == p("/home/user/minetest/bin"));
+               UASSERT(removed == p("../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 4);
+               UASSERT(result == p("/home/user/minetest"));
+               UASSERT(removed == p("bin/../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 5);
+               UASSERT(result == p("/home/user"));
+               UASSERT(removed == p("minetest/bin/../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 6);
+               UASSERT(result == p("/home"));
+               UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
+               result = fs::RemoveLastPathComponent(path, &removed, 7);
+               #ifdef _WIN32
+               UASSERT(result == "C:");
+               #else
+               UASSERT(result == "");
+               #endif
+               UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
+
+               /*
+                       Test fs::RemoveRelativePathComponent
+               */
+               path = p("/home/user/minetest/bin");
+               result = fs::RemoveRelativePathComponents(path);
+               UASSERT(result == path);
+               path = p("/home/user/minetest/bin/../worlds/world1");
+               result = fs::RemoveRelativePathComponents(path);
+               UASSERT(result == p("/home/user/minetest/worlds/world1"));
+               path = p("/home/user/minetest/bin/../worlds/world1/");
+               result = fs::RemoveRelativePathComponents(path);
+               UASSERT(result == p("/home/user/minetest/worlds/world1"));
+               path = p(".");
+               result = fs::RemoveRelativePathComponents(path);
+               UASSERT(result == "");
+               path = p("./subdir/../..");
+               result = fs::RemoveRelativePathComponents(path);
+               UASSERT(result == "");
+               path = p("/a/b/c/.././../d/../e/f/g/../h/i/j/../../../..");
+               result = fs::RemoveRelativePathComponents(path);
+               UASSERT(result == p("/a/e"));
+       }
+};
+
 struct TestSettings: public TestBase
 {
        void Run()
@@ -1785,6 +1992,7 @@ void run_tests()
 
        infostream<<"run_tests() started"<<std::endl;
        TEST(TestUtilities);
+       TEST(TestPath);
        TEST(TestSettings);
        TEST(TestCompress);
        TEST(TestSerialization);
diff --git a/textures/base/pack/no_screenshot.png b/textures/base/pack/no_screenshot.png
new file mode 100644 (file)
index 0000000..e309a3e
Binary files /dev/null and b/textures/base/pack/no_screenshot.png differ