Advertisement
xponen

cawidget.lua with error filter

Sep 17th, 2013
192
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 54.03 KB | None | 0 0
  1. -- $Id: cawidgets.lua 4261 2009-03-31 16:34:36Z licho $
  2. --------------------------------------------------------------------------------
  3. --------------------------------------------------------------------------------
  4. --
  5. --  file:    widgets.lua
  6. --  brief:   the widget manager, a call-in router
  7. --  author:  Dave Rodgers
  8. --
  9. --  modified by jK and quantum
  10. --
  11. --  Copyright (C) 2007,2008,2009.
  12. --  Licensed under the terms of the GNU GPL, v2 or later.
  13. --
  14. --------------------------------------------------------------------------------
  15. --------------------------------------------------------------------------------
  16.  
  17. -- stable release?
  18. local isStable = false
  19. local resetWidgetDetailLevel = false -- has widget detail level changed
  20.  
  21. local ORDER_VERSION = 8 --- change this to reset enabled/disabled widgets
  22. local DATA_VERSION = 9 -- change this to reset widget settings
  23.  
  24. function includeZIPFirst(filename, envTable)
  25.   if (string.find(filename, '.h.lua', 1, true)) then
  26.     filename = 'Headers/' .. filename
  27.   end
  28.   return VFS.Include(LUAUI_DIRNAME .. filename, envTable, VFS.ZIP_FIRST)
  29. end
  30.  
  31.  
  32. include("keysym.h.lua")
  33. include("utils.lua")
  34. includeZIPFirst("system.lua")
  35. includeZIPFirst("cache.lua") --contain cached override for Spring.GetVisibleUnit (performance optimization). All overrides that are placed here have global reach
  36. include("callins.lua")
  37. include("savetable.lua")
  38. include("utility_two.lua") --contain file backup function
  39. local myName, transmitMagic, voiceMagic, transmitLobbyMagic, MessageProcessor = include("chat_preprocess.lua") -- contain stuff that preprocess chat message for Chili Chat widgets
  40.  
  41. local modShortUpper = Game.modShortName:upper()
  42. local ORDER_FILENAME     = LUAUI_DIRNAME .. 'Config/' .. modShortUpper .. '_order.lua'
  43. local CONFIG_FILENAME    = LUAUI_DIRNAME .. 'Config/' .. modShortUpper .. '_data.lua'
  44. local WIDGET_DIRNAME     = LUAUI_DIRNAME .. 'Widgets/'
  45.  
  46. local HANDLER_BASENAME = "cawidgets.lua"
  47. local SELECTOR_BASENAME = 'selector.lua'
  48.  
  49. do
  50.     local isMission = Game.modDesc:find("Mission Mutator")
  51.     if isMission then -- all missions will be forced to use a specific name
  52.         if not VFS.FileExists(ORDER_FILENAME) or not VFS.FileExists(CONFIG_FILENAME) then
  53.             ORDER_FILENAME     = LUAUI_DIRNAME .. 'Config/ZK_order.lua' --use "ZK" name when running any mission mod (provided that there's no existing config file)
  54.             CONFIG_FILENAME    = LUAUI_DIRNAME .. 'Config/ZK_data.lua'
  55.         end
  56.     end
  57. end
  58.  
  59. local SAFEWRAP = 1
  60. -- 0: disabled
  61. -- 1: enabled, but can be overriden by widget.GetInfo().unsafe
  62. -- 2: always enabled
  63.  
  64. local SAFEDRAW = false  -- requires SAFEWRAP to work
  65. local glPopAttrib  = gl.PopAttrib
  66. local glPushAttrib = gl.PushAttrib
  67. local pairs = pairs
  68. local ipairs = ipairs
  69.  
  70. do -- create backup for ZK_data.lua and ZK_order.lua to workaround against case of file corruption when OS crash
  71.     local fileToCheck = {ORDER_FILENAME,CONFIG_FILENAME}
  72.     local extraText = {'-- Widget Order List  (0 disables a widget)', '-- Widget Custom Data'} --this is a header text that is appended to start of file
  73.     for i=1, #fileToCheck do
  74.         CheckLUAFileAndBackup(fileToCheck[i], extraText[i])
  75.     end
  76. end
  77.  
  78. -- read local widgets config
  79. local localWidgetsFirst = false
  80. local localWidgets = false
  81.  
  82. if VFS.FileExists(CONFIG_FILENAME) then --check config file whether user want to use localWidgetsFirst
  83.   local cadata = VFS.Include(CONFIG_FILENAME)
  84.   if cadata["Local Widgets Config"] then
  85.     localWidgetsFirst = cadata["Local Widgets Config"].localWidgetsFirst
  86.     localWidgets = cadata["Local Widgets Config"].localWidgets
  87.   end
  88. end
  89.  
  90. local VFSMODE
  91. VFSMODE = localWidgetsFirst and VFS.RAW_FIRST
  92. VFSMODE = VFSMODE or localWidgets and VFS.ZIP_FIRST
  93. VFSMODE = VFSMODE or VFS.ZIP
  94.  
  95. local detailLevel = Spring.GetConfigInt("widgetDetailLevel", 3)
  96.  
  97. --------------------------------------------------------------------------------
  98.  
  99. -- install bindings for TweakMode and the Widget Selector
  100.  
  101. Spring.SendCommands({
  102.   "unbindkeyset  Any+f11",
  103.   "unbindkeyset Ctrl+f11",
  104.   "bind    f11  luaui selector",
  105.   "bind  C+f11  luaui tweakgui",
  106.   "echo LuaUI: bound F11 to the widget selector",
  107.   "echo LuaUI: bound CTRL+F11 to tweak mode"
  108. })
  109.  
  110.  
  111. --------------------------------------------------------------------------------
  112. --------------------------------------------------------------------------------
  113. --
  114. --  the widgetHandler object
  115. --
  116.  
  117. widgetHandler = {
  118.  
  119.   widgets = {},
  120.  
  121.   configData = {},
  122.   orderList = {},
  123.  
  124.   knownWidgets = {},
  125.   knownCount = 0,
  126.   knownChanged = true,
  127.  
  128.   commands = {},
  129.   customCommands = {},
  130.   inCommandsChanged = false,
  131.  
  132.   autoModWidgets = false,
  133.  
  134.   actionHandler = include("actions.lua"),
  135.  
  136.   WG = {}, -- shared table for widgets
  137.  
  138.   globals = {}, -- global vars/funcs
  139.  
  140.   mouseOwner = nil,
  141.   ownedButton = 0,
  142.  
  143.   tweakMode = false,
  144. }
  145.  
  146.  
  147. -- these call-ins are set to 'nil' if not used
  148. -- they are setup in UpdateCallIns()
  149. local flexCallIns = {
  150.   'GameOver',
  151.   'GamePaused',
  152.   'GameFrame',
  153.   'GameSetup',
  154.   'TeamDied',
  155.   'TeamChanged',
  156.   'PlayerAdded',
  157.   'PlayerChanged',
  158.   "PlayerRemoved",
  159.   'ShockFront',
  160.   'WorldTooltip',
  161.   'MapDrawCmd',
  162.   'DefaultCommand',
  163.   'UnitCreated',
  164.   'UnitFinished',
  165.   'UnitFromFactory',
  166.   'UnitDestroyed',
  167.   'UnitExperience',
  168.   'UnitTaken',
  169.   'UnitGiven',
  170.   'UnitIdle',
  171.   'UnitCommand',
  172.   'UnitCmdDone',
  173.   'UnitDamaged',
  174.   'UnitEnteredRadar',
  175.   'UnitEnteredLos',
  176.   'UnitLeftRadar',
  177.   'UnitLeftLos',
  178.   'UnitEnteredWater',
  179.   'UnitEnteredAir',
  180.   'UnitLeftWater',
  181.   'UnitLeftAir',
  182.   'UnitSeismicPing',
  183.   'UnitLoaded',
  184.   'UnitUnloaded',
  185.   'UnitCloaked',
  186.   'UnitDecloaked',
  187.   'UnitMoveFailed',
  188.   'RecvLuaMsg',
  189.   'StockpileChanged',
  190.   'DrawGenesis',
  191.   'DrawWorld',
  192.   'DrawWorldPreUnit',
  193.   'DrawWorldShadow',
  194.   'DrawWorldReflection',
  195.   'DrawWorldRefraction',
  196.   'DrawScreenEffects',
  197.   'DrawInMiniMap',
  198.   'RecvSkirmishAIMessage',
  199.   'SelectionChanged',
  200.   'AddTransmitLine',
  201.   'AddConsoleMessage',
  202.   'VoiceCommand',
  203. }
  204. local flexCallInMap = {}
  205. for _,ci in ipairs(flexCallIns) do
  206.   flexCallInMap[ci] = true
  207. end
  208.  
  209. local callInLists = {
  210.   'GamePreload',
  211.   'GameStart',
  212.   'Shutdown',
  213.   'Update',
  214.   'TextCommand',
  215.   'CommandNotify',
  216.   'AddConsoleLine',
  217.   'ViewResize',
  218.   'DrawScreen',
  219.   'KeyPress',
  220.   'KeyRelease',
  221.   'MousePress',
  222.   'MouseWheel',
  223.   'JoyAxis',
  224.   'JoyHat',
  225.   'JoyButtonDown',
  226.   'JoyButtonUp',  
  227.   'IsAbove',
  228.   'GetTooltip',
  229.   'GroupChanged',
  230.   'CommandsChanged',
  231.   'TweakMousePress',
  232.   'TweakMouseWheel',
  233.   'TweakIsAbove',
  234.   'TweakGetTooltip',
  235.   'GameProgress',
  236.   'UnsyncedHeightMapUpdate',
  237. -- these use mouseOwner instead of lists
  238. --  'MouseMove',
  239. --  'MouseRelease',
  240. --  'TweakKeyPress',
  241. --  'TweakKeyRelease',
  242. --  'TweakMouseMove',
  243. --  'TweakMouseRelease',
  244.  
  245. -- uses the DrawScreenList
  246. --  'TweakDrawScreen',
  247. }
  248.  
  249. -- append the flex call-ins
  250. for _,uci in ipairs(flexCallIns) do
  251.   table.insert(callInLists, uci)
  252. end
  253.  
  254.  
  255. -- initialize the call-in lists
  256. do
  257.   for _,listname in ipairs(callInLists) do
  258.     widgetHandler[listname..'List'] = {}
  259.   end
  260. end
  261.  
  262.  
  263. --------------------------------------------------------------------------------
  264. --------------------------------------------------------------------------------
  265. --
  266. --  Reverse integer iterator for drawing
  267. --
  268.  
  269. local function rev_iter(t, key)
  270.   if (key <= 1) then
  271.     return nil
  272.   else
  273.     local nkey = key - 1
  274.     return nkey, t[nkey]
  275.   end
  276. end
  277.  
  278. local function ripairs(t)
  279.   return rev_iter, t, (1 + #t)
  280. end
  281.  
  282. --------------------------------------------------------------------------------
  283. --------------------------------------------------------------------------------
  284. -- helper functions
  285. ---include chat preprocess here
  286.  
  287. --------------------------------------------------------------------------------
  288. --------------------------------------------------------------------------------
  289.  
  290. function widgetHandler:LoadOrderList()
  291.   local chunk, err = loadfile(ORDER_FILENAME)
  292.   if (chunk == nil) then
  293.     self.orderList = {} -- safety
  294.     return {}
  295.   else
  296.     local tmp = {}
  297.     setfenv(chunk, tmp)
  298.     self.orderList = chunk()
  299.     if (not self.orderList) then
  300.       self.orderList = {} -- safety
  301.     end
  302.     if (self.orderList.version or 0) < ORDER_VERSION then
  303.         self.orderList = {}
  304.         self.orderList.version = ORDER_VERSION
  305.     end
  306.     local detailLevel = Spring.GetConfigInt("widgetDetailLevel", 2)
  307.     if (self.orderList.lastWidgetDetailLevel ~= detailLevel) then
  308.         resetWidgetDetailLevel = true
  309.         self.orderList.lastWidgetDetailLevel = detailLevel
  310.     end
  311.   end
  312. end
  313.  
  314.  
  315. function widgetHandler:SaveOrderList()
  316.   -- update the current order
  317.   for i,w in ipairs(self.widgets) do
  318.     self.orderList[w.whInfo.name] = i
  319.   end
  320.   table.save(self.orderList, ORDER_FILENAME,
  321.              '-- Widget Order List  (0 disables a widget)')
  322. end
  323.  
  324.  
  325. --------------------------------------------------------------------------------
  326.  
  327. function widgetHandler:LoadConfigData()
  328.   local chunk, err = loadfile(CONFIG_FILENAME)
  329.   if (chunk == nil) then
  330.     return {}
  331.   else
  332.     local tmp = {}
  333.     setfenv(chunk, tmp)
  334.     self.configData = chunk()
  335.     if (not self.configData) then
  336.       self.configData = {} -- safety
  337.     end
  338.     if (self.configData.version or 0) < DATA_VERSION then
  339.         self.configData = {}
  340.         self.configData.version = DATA_VERSION
  341.     end
  342.  
  343.   end
  344. end
  345.  
  346.  
  347. function widgetHandler:SaveConfigData()
  348.   resetWidgetDetailLevel = false
  349.   self:LoadConfigData()
  350.   for _,w in ipairs(self.widgets) do
  351.     if (w.GetConfigData) then
  352.       local ok, err = pcall(function()
  353.         self.configData[w.whInfo.name] = w:GetConfigData()
  354.       end)
  355.       if not ok then Spring.Log(HANDLER_BASENAME, LOG.ERROR, "Failed to GetConfigData from: " .. w.whInfo.name.." ("..err..")") end
  356.     end
  357.   end
  358.   table.save(self.configData, CONFIG_FILENAME, '-- Widget Custom Data')
  359. end
  360.  
  361.  
  362. function widgetHandler:SendConfigData()
  363.   self:LoadConfigData()
  364.   for _,w in ipairs(self.widgets) do
  365.     local data = self.configData[w.whInfo.name]
  366.     if (w.SetConfigData and data) then
  367.       w:SetConfigData(data)
  368.     end
  369.   end
  370. end
  371.  
  372.  
  373. --------------------------------------------------------------------------------
  374.  
  375. function widgetHandler:Initialize()
  376.   if Game.modVersion:find("stable",1,true) then
  377.     isStable = true
  378.   end
  379.  
  380.   self:LoadOrderList()
  381.   self:LoadConfigData()
  382.  
  383.   local autoModWidgets = Spring.GetConfigInt('LuaAutoModWidgets', 1)
  384.   self.autoModWidgets = (autoModWidgets ~= 0)
  385.  
  386.   -- create the "LuaUI/Config" directory
  387.   Spring.CreateDir(LUAUI_DIRNAME .. 'Config')
  388.  
  389.   local unsortedWidgets = {}
  390.  
  391.   -- stuff the widgets into unsortedWidgets
  392.   local widgetFiles = VFS.DirList(WIDGET_DIRNAME, "*.lua", VFSMODE)
  393.   for k,wf in ipairs(widgetFiles) do
  394.     local widget = self:LoadWidget(wf)
  395.     if (widget) then
  396.       table.insert(unsortedWidgets, widget)
  397.     end
  398.   end
  399.  
  400.   -- sort the widgets  
  401.   table.sort(unsortedWidgets, function(w1, w2)
  402.     local l1 = w1.whInfo.layer
  403.     local l2 = w2.whInfo.layer
  404.     if (l1 ~= l2) then
  405.       return (l1 < l2)
  406.     end
  407.     local n1 = w1.whInfo.name
  408.     local n2 = w2.whInfo.name
  409.     local o1 = self.orderList[n1]
  410.     local o2 = self.orderList[n2]
  411.     if (o1 ~= o2) then
  412.       return (o1 < o2)
  413.     else
  414.       return (n1 < n2)
  415.     end
  416.   end)
  417.  
  418.   -- first add the api widgets
  419.   for _,w in ipairs(unsortedWidgets) do
  420.     if (w.whInfo.api) then
  421.       widgetHandler:InsertWidget(w)
  422.  
  423.       local name = w.whInfo.name
  424.       local basename = w.whInfo.basename
  425.       Spring.Echo(string.format("Loaded API widget:  %-18s  <%s>", name, basename))
  426.     end
  427.   end
  428.  
  429.   -- add the widgets  
  430.   for _,w in ipairs(unsortedWidgets) do
  431.     if (not w.whInfo.api) then
  432.       widgetHandler:InsertWidget(w)
  433.  
  434.       local name = w.whInfo.name
  435.       local basename = w.whInfo.basename
  436.       Spring.Echo(string.format("Loaded widget:  %-18s  <%s>", name, basename))
  437.     end
  438.   end
  439.  
  440.   -- save the active widgets, and their ordering
  441.   self:SaveOrderList()
  442.   self:SaveConfigData()
  443. end
  444.  
  445.  
  446. function widgetHandler:LoadWidget(filename, _VFSMODE)
  447.   _VFSMODE = _VFSMODE or VFSMODE
  448.   local basename = Basename(filename)
  449.   local text = VFS.LoadFile(filename, _VFSMODE)
  450.  
  451.   if (text == nil) then
  452.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Failed to load: ' .. basename .. '  (missing file: ' .. filename ..')')
  453.     return nil
  454.   end
  455.   local chunk, err = loadstring(text, filename)
  456.   if (chunk == nil) then
  457.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Failed to load: ' .. basename .. '  (' .. err .. ')')
  458.     return nil
  459.   end
  460.  
  461.   local widget = widgetHandler:NewWidget()
  462.   setfenv(chunk, widget)
  463.   local success, err = pcall(chunk)
  464.   if (not success) then
  465.     Spring.Echo('Failed to load: ' .. basename .. '  (' .. err .. ')')
  466.     return nil
  467.   end
  468.   if (err == false) then
  469.     return nil -- widget asked for a silent death
  470.   end
  471.  
  472.   -- raw access to widgetHandler
  473.   if (widget.GetInfo and widget:GetInfo().handler) then
  474.     widget.widgetHandler = self
  475.   end
  476.  
  477.   self:FinalizeWidget(widget, filename, basename)
  478.   local name = widget.whInfo.name
  479.   if (basename == SELECTOR_BASENAME) then
  480.     self.orderList[name] = 1  --  always enabled
  481.   end
  482.  
  483.   err = self:ValidateWidget(widget)
  484.   if (err) then
  485.     Spring.Echo('Failed to load: ' .. basename .. '  (' .. err .. ')')
  486.     return nil
  487.   end
  488.  
  489.   local knownInfo = self.knownWidgets[name]
  490.   if (knownInfo) then
  491.     if (knownInfo.active) then
  492.       Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Failed to load: ' .. basename .. '  (duplicate name)')
  493.       return nil
  494.     end
  495.   else
  496.     -- create a knownInfo table
  497.     knownInfo = {}
  498.     knownInfo.desc     = widget.whInfo.desc
  499.     knownInfo.author   = widget.whInfo.author
  500.     knownInfo.basename = widget.whInfo.basename
  501.     knownInfo.filename = widget.whInfo.filename
  502.     knownInfo.alwaysStart = widget.whInfo.alwaysStart
  503.     knownInfo.fromZip  = true
  504.     if (_VFSMODE ~= VFS.ZIP) then
  505.       if (_VFSMODE == VFS.RAW_FIRST) then
  506.         knownInfo.fromZip = not VFS.FileExists(filename,VFS.RAW_ONLY)
  507.       else
  508.         knownInfo.fromZip = VFS.FileExists(filename,VFS.ZIP_ONLY)
  509.       end
  510.     end
  511.     self.knownWidgets[name] = knownInfo
  512.     self.knownCount = self.knownCount + 1
  513.     self.knownChanged = true
  514.   end
  515.   knownInfo.active = true
  516.  
  517.   if (widget.GetInfo == nil) then
  518.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Failed to load: ' .. basename .. '  (no GetInfo() call)')
  519.     return nil
  520.   end
  521.  
  522.   local info  = widget:GetInfo()
  523.   local order = self.orderList[name]
  524.  
  525.   local enabled = ((order ~= nil) and (order > 0)) or
  526.       ((order == nil) and  -- unknown widget
  527.        (info.enabled and ((not knownInfo.fromZip) or self.autoModWidgets))) or info.alwaysStart
  528.  
  529.   -- experimental widget, disabled by default in stable
  530.   if info.experimental and isStable then
  531.     enabled = false
  532.   end
  533.  
  534.   if resetWidgetDetailLevel and info.detailsDefault ~= nil then
  535.     if type(info.detailsDefault) == "table" then
  536.         enabled = info.detailsDefault[detailLevel] and true
  537.     elseif type(info.detailsDefault) == "number" then
  538.         enabled = detailLevel >= info.detailsDefault
  539.     elseif tonumber(info.detailsDefault) then
  540.         enabled = detailLevel >= tonumber(info.detailsDefault)
  541.     end
  542.   end
  543.              
  544.   if (enabled) then
  545.     -- this will be an active widget
  546.     if (order == nil) then
  547.       self.orderList[name] = 12345  -- back of the pack
  548.     else
  549.       self.orderList[name] = order
  550.     end
  551.   else
  552.     self.orderList[name] = 0
  553.     self.knownWidgets[name].active = false
  554.     return nil
  555.   end
  556.  
  557.   -- load the config data  
  558.   local config = self.configData[name]
  559.   if (widget.SetConfigData and config) then
  560.     widget:SetConfigData(config)
  561.   end
  562.    
  563.   return widget
  564. end
  565.  
  566.  
  567. function widgetHandler:NewWidget()
  568.   local widget = {}
  569.   if (true) then
  570.     -- copy the system calls into the widget table
  571.     for k,v in pairs(System) do
  572.       widget[k] = v
  573.     end
  574.   else
  575.     -- use metatable redirection
  576.     setmetatable(widget, {
  577.       __index = System,
  578.       __metatable = true,
  579.     })
  580.   end
  581.   widget.WG = self.WG    -- the shared table
  582.   widget.widget = widget -- easy self referencing
  583.  
  584.   -- wrapped calls (closures)
  585.   widget.widgetHandler = {}
  586.   local wh = widget.widgetHandler
  587.   local self = self
  588.   widget.include  = function (f) return include(f, widget) end
  589.   wh.ForceLayout  = function (_) self:ForceLayout() end
  590.   wh.RaiseWidget  = function (_) self:RaiseWidget(widget) end
  591.   wh.LowerWidget  = function (_) self:LowerWidget(widget) end
  592.   wh.RemoveWidget = function (_) self:RemoveWidget(widget) end
  593.   wh.GetCommands  = function (_) return self.commands end
  594.   wh.InTweakMode  = function (_) return self.tweakMode end
  595.   wh.GetViewSizes = function (_) return self:GetViewSizes() end
  596.   wh.GetHourTimer = function (_) return self:GetHourTimer() end
  597.   wh.IsMouseOwner = function (_) return (self.mouseOwner == widget) end
  598.   wh.DisownMouse  = function (_)
  599.     if (self.mouseOwner == widget) then
  600.       self.mouseOwner = nil
  601.     end
  602.   end
  603.  
  604.   wh.isStable = function (_) return self:isStable() end
  605.  
  606.   wh.UpdateCallIn = function (_, name)
  607.     self:UpdateWidgetCallIn(name, widget)
  608.   end
  609.   wh.RemoveCallIn = function (_, name)
  610.     self:RemoveWidgetCallIn(name, widget)
  611.   end
  612.  
  613.   wh.AddAction    = function (_, cmd, func, data, types)
  614.     return self.actionHandler:AddAction(widget, cmd, func, data, types)
  615.   end
  616.   wh.RemoveAction = function (_, cmd, types)
  617.     return self.actionHandler:RemoveAction(widget, cmd, types)
  618.   end
  619.  
  620.   wh.AddLayoutCommand = function (_, cmd)
  621.     if (self.inCommandsChanged) then
  622.       table.insert(self.customCommands, cmd)
  623.     else
  624.       Spring.Log(HANDLER_BASENAME, LOG.ERROR, "AddLayoutCommand() can only be used in CommandsChanged()")
  625.     end
  626.   end
  627.  
  628.   wh.RegisterGlobal = function(_, name, value)
  629.     return self:RegisterGlobal(widget, name, value)
  630.   end
  631.   wh.DeregisterGlobal = function(_, name)
  632.     return self:DeregisterGlobal(widget, name)
  633.   end
  634.   wh.SetGlobal = function(_, name, value)
  635.     return self:SetGlobal(widget, name, value)
  636.   end
  637.  
  638.   wh.ConfigLayoutHandler = function(_, d) self:ConfigLayoutHandler(d) end
  639.  
  640.   ----
  641.   widget.ProcessConsoleBuffer = function(_,_, num)  -- FIXME: probably not the least hacky way to make ProcessConsoleBuffer accessible to widgets
  642.     return MessageProcessor:ProcessConsoleBuffer(num)
  643.   end
  644.   ----
  645.  
  646.   return widget
  647. end
  648.  
  649.  
  650. function widgetHandler:FinalizeWidget(widget, filename, basename)
  651.   local wi
  652.  
  653.   if (widget.GetInfo == nil) then
  654.     wi = {}
  655.     wi.filename = filename
  656.     wi.basename = basename
  657.     wi.name  = basename
  658.     wi.layer = 0
  659.   else
  660.     local info = widget:GetInfo()
  661.     wi = info
  662.     wi.filename = filename
  663.     wi.basename = basename
  664.     wi.name     = wi.name    or basename
  665.     wi.layer    = wi.layer   or 0
  666.     wi.desc     = wi.desc    or ""
  667.     wi.author   = wi.author  or ""
  668.     wi.license  = wi.license or ""
  669.     wi.enabled  = wi.enabled or false
  670.     wi.api      = wi.api or false
  671.  
  672.     -- exprimental widget
  673.     -- change name for separate settings and disable by default
  674.     if info.experimental and isStable then
  675.       wi.name = wi.name .. " (experimental)"
  676.       wi.enabled = false
  677.     end
  678.  
  679.   end
  680.  
  681.   widget.whInfo = {}  --  a proxy table
  682.   local mt = {
  683.     __index = wi,
  684.     __newindex = function() error("whInfo tables are read-only") end,
  685.     __metatable = "protected"
  686.   }
  687.   setmetatable(widget.whInfo, mt)
  688. end
  689.  
  690.  
  691. function widgetHandler:ValidateWidget(widget)
  692.   if (widget.GetTooltip and not widget.IsAbove) then
  693.     return "Widget has GetTooltip() but not IsAbove()"
  694.   end
  695.   if (widget.TweakGetTooltip and not widget.TweakIsAbove) then
  696.     return "Widget has TweakGetTooltip() but not TweakIsAbove()"
  697.   end
  698.   return nil
  699. end
  700.  
  701.  
  702. --------------------------------------------------------------------------------
  703. --------------------------------------------------------------------------------
  704.  
  705. local function HandleError(widget, funcName, status, ...)
  706.   if (status) then
  707.     return ...
  708.   end
  709.  
  710.   if (funcName ~= 'Shutdown') then
  711.     widgetHandler:RemoveWidget(widget)
  712.   else
  713.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Error in Shutdown()')
  714.   end
  715.   local name = widget.whInfo.name
  716.   local error_message = select(1,...)
  717.   Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Error in ' .. funcName ..'(): ' .. tostring(error_message))
  718.   Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Removed widget: ' .. name)
  719.   return nil
  720. end
  721.  
  722. local function SafeWrapFuncNoGL(func, funcName)
  723.   return function(w, ...)
  724.     return HandleError(w, funcName, pcall(func, w, ...))
  725.   end
  726. end
  727.  
  728. local function SafeWrapFuncGL(func, funcName)
  729.   local wh = widgetHandler
  730.  
  731.   return function(w, ...)
  732.  
  733.     glPushAttrib(GL.ALL_ATTRIB_BITS)
  734.     local r = { pcall(func, w, ...) }
  735.     glPopAttrib()
  736.  
  737.     if (r[1]) then
  738.       table.remove(r, 1)
  739.       return unpack(r)
  740.     else
  741.       if (funcName ~= 'Shutdown') then
  742.         widgetHandler:RemoveWidget(w)
  743.       else
  744.         Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Error in Shutdown()')
  745.       end
  746.       local name = w.whInfo.name
  747.       Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Error in ' .. funcName ..'(): ' .. tostring(r[2]))
  748.       Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'Removed widget: ' .. name)
  749.       return nil
  750.     end
  751.   end
  752. end
  753.  
  754.  
  755. local function SafeWrapFunc(func, funcName)
  756.   if (not SAFEDRAW) then
  757.     return SafeWrapFuncNoGL(func, funcName)
  758.   else
  759.     if (string.sub(funcName, 1, 4) ~= 'Draw') then
  760.       return SafeWrapFuncNoGL(func, funcName)
  761.     else
  762.       return SafeWrapFuncGL(func, funcName)
  763.     end
  764.   end
  765. end
  766.  
  767.  
  768. local function SafeWrapWidget(widget)
  769.   if (SAFEWRAP <= 0) then
  770.     return
  771.   elseif (SAFEWRAP == 1) then
  772.     if (widget.GetInfo and widget.GetInfo().unsafe) then
  773.       Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'LuaUI: loaded unsafe widget: ' .. widget.whInfo.name)
  774.       return
  775.     end
  776.   end
  777.  
  778.   for _,ciName in ipairs(callInLists) do
  779.     if (widget[ciName]) then
  780.       widget[ciName] = SafeWrapFunc(widget[ciName], ciName)
  781.     end
  782.     if (widget.Initialize) then
  783.       widget.Initialize = SafeWrapFunc(widget.Initialize, 'Initialize')
  784.     end
  785.   end
  786. end
  787.  
  788.  
  789. --------------------------------------------------------------------------------
  790.  
  791. local function ArrayInsert(t, f, w)
  792.   if (f) then
  793.     local layer = w.whInfo.layer
  794.     local index = 1
  795.     for i,v in ipairs(t) do
  796.       if (v == w) then
  797.         return -- already in the table
  798.       end
  799.       if (layer >= v.whInfo.layer) then
  800.         index = i + 1
  801.       end
  802.     end
  803.     table.insert(t, index, w)
  804.   end
  805. end
  806.  
  807.  
  808. local function ArrayRemove(t, w)
  809.   for k,v in ipairs(t) do
  810.     if (v == w) then
  811.       table.remove(t, k)
  812.       -- break
  813.     end
  814.   end
  815. end
  816.  
  817.  
  818. function widgetHandler:InsertWidget(widget)
  819.   if (widget == nil) then
  820.     return
  821.   end
  822.  
  823.   SafeWrapWidget(widget)
  824.  
  825.   ArrayInsert(self.widgets, true, widget)
  826.   for _,listname in ipairs(callInLists) do
  827.     local func = widget[listname]
  828.     if (type(func) == 'function') then
  829.       ArrayInsert(self[listname..'List'], func, widget)
  830.     end
  831.   end
  832.   self:UpdateCallIns()
  833.  
  834.   if (widget.Initialize) then
  835.     widget:Initialize()
  836.   end
  837. end
  838.  
  839.  
  840. function widgetHandler:RemoveWidget(widget)
  841.   if (widget == nil) then
  842.     return
  843.   end
  844.  
  845.   local name = widget.whInfo.name
  846.   if (widget.GetConfigData) then
  847.     local ok, err = pcall(function()
  848.       self.configData[name] = widget:GetConfigData()
  849.     end)
  850.     if not ok then Spring.Log(HANDLER_BASENAME, LOG.ERROR, "Failed to GetConfigData: " .. name.." ("..err..")") end
  851.   end
  852.   self.knownWidgets[name].active = false
  853.   if (widget.Shutdown) then
  854.     widget:Shutdown()
  855.   end
  856.   ArrayRemove(self.widgets, widget)
  857.   self:RemoveWidgetGlobals(widget)
  858.   self.actionHandler:RemoveWidgetActions(widget)
  859.   for _,listname in ipairs(callInLists) do
  860.     ArrayRemove(self[listname..'List'], widget)
  861.   end
  862.   self:UpdateCallIns()
  863. end
  864.  
  865.  
  866. --------------------------------------------------------------------------------
  867.  
  868. function widgetHandler:UpdateCallIn(name)
  869.   local listName = name .. 'List'
  870.   if ((name == 'Update')     or
  871.       (name == 'DrawScreen')) then
  872.     return
  873.   end
  874.  
  875.   if ((#self[listName] > 0) or
  876.       (not flexCallInMap[name]) or
  877.       ((name == 'GotChatMsg')     and actionHandler.HaveChatAction()) or
  878.       ((name == 'RecvFromSynced') and actionHandler.HaveSyncAction())) then
  879.     -- always assign these call-ins
  880.     local selffunc = self[name]
  881.     _G[name] = function(...)
  882.       return selffunc(self, ...)
  883.     end
  884.   else
  885.     _G[name] = nil
  886.   end
  887.   Script.UpdateCallIn(name)
  888. end
  889.  
  890.  
  891. function widgetHandler:UpdateWidgetCallIn(name, w)
  892.   local listName = name .. 'List'
  893.   local ciList = self[listName]
  894.   if (ciList) then
  895.     local func = w[name]
  896.     if (type(func) == 'function') then
  897.       ArrayInsert(ciList, func, w)
  898.     else
  899.       ArrayRemove(ciList, w)
  900.     end
  901.     self:UpdateCallIn(name)
  902.   else
  903.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'UpdateWidgetCallIn: bad name: ' .. name)
  904.   end
  905. end
  906.  
  907.  
  908. function widgetHandler:RemoveWidgetCallIn(name, w)
  909.   local listName = name .. 'List'
  910.   local ciList = self[listName]
  911.   if (ciList) then
  912.     ArrayRemove(ciList, w)
  913.     self:UpdateCallIn(name)
  914.   else
  915.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, 'RemoveWidgetCallIn: bad name: ' .. name)
  916.   end
  917. end
  918.  
  919.  
  920. function widgetHandler:UpdateCallIns()
  921.   for _,name in ipairs(callInLists) do
  922.     self:UpdateCallIn(name)
  923.   end
  924. end
  925.  
  926.  
  927. --------------------------------------------------------------------------------
  928.  
  929. function widgetHandler:EnableWidget(name)
  930.   local ki = self.knownWidgets[name]
  931.   if (not ki) then
  932.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, "EnableWidget(), could not find widget: " .. tostring(name))
  933.     return false
  934.   end
  935.   if (not ki.active) then
  936.     Spring.Echo('Loading:  '..ki.filename)
  937.     local order = widgetHandler.orderList[name]
  938.     if (not order or (order <= 0)) then
  939.       self.orderList[name] = 1
  940.     end
  941.     local w = self:LoadWidget(ki.filename)
  942.     if (not w) then return false end
  943.     self:InsertWidget(w)
  944.     self:SaveOrderList()
  945.   end
  946.   return true
  947. end
  948.  
  949.  
  950. function widgetHandler:DisableWidget(name)
  951.   local ki = self.knownWidgets[name]
  952.   if (not ki) then
  953.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, "DisableWidget(), could not find widget: " .. tostring(name))
  954.     return false
  955.   end
  956.   if (ki.active) then
  957.     local w = self:FindWidget(name)
  958.     if (not w) then return false end
  959.     Spring.Echo('Removed:  '..ki.filename)
  960.     self:RemoveWidget(w)     -- deactivate
  961.     self.orderList[name] = 0 -- disable
  962.     self:SaveOrderList()
  963.   end
  964.   return true
  965. end
  966.  
  967.  
  968. function widgetHandler:ToggleWidget(name)
  969.   local ki = self.knownWidgets[name]
  970.   if (not ki) then
  971.     Spring.Log(HANDLER_BASENAME, LOG.ERROR, "ToggleWidget(), could not find widget: " .. tostring(name))
  972.     return
  973.   end
  974.   if (ki.active) then
  975.     return self:DisableWidget(name)
  976.   elseif (self.orderList[name] <= 0) then
  977.     return self:EnableWidget(name)
  978.   else
  979.     -- the widget is not active, but enabled; disable it
  980.     self.orderList[name] = 0
  981.     self:SaveOrderList()
  982.   end
  983.   return true
  984. end
  985.  
  986.  
  987. --------------------------------------------------------------------------------
  988.  
  989. local function FindWidgetIndex(t, w)
  990.   for k,v in ipairs(t) do
  991.     if (v == w) then
  992.       return k
  993.     end
  994.   end
  995.   return nil
  996. end
  997.  
  998.  
  999. local function FindLowestIndex(t, i, layer)
  1000.   for x = (i - 1), 1, -1 do
  1001.     if (t[x].whInfo.layer < layer) then
  1002.       return x + 1
  1003.     end
  1004.   end
  1005.   return 1
  1006. end
  1007.  
  1008.  
  1009. function widgetHandler:RaiseWidget(widget)
  1010.   if (widget == nil) then
  1011.     return
  1012.   end
  1013.   local function Raise(t, f, w)
  1014.     if (f == nil) then return end
  1015.     local i = FindWidgetIndex(t, w)
  1016.     if (i == nil) then return end
  1017.     local n = FindLowestIndex(t, i, w.whInfo.layer)
  1018.     if (n and (n < i)) then
  1019.       table.remove(t, i)
  1020.       table.insert(t, n, w)
  1021.     end
  1022.   end
  1023.   Raise(self.widgets, true, widget)
  1024.   for _,listname in ipairs(callInLists) do
  1025.     Raise(self[listname..'List'], widget[listname], widget)
  1026.   end
  1027. end
  1028.  
  1029.  
  1030. local function FindHighestIndex(t, i, layer)
  1031.   local ts = #t
  1032.   for x = (i + 1),ts do
  1033.     if (t[x].whInfo.layer > layer) then
  1034.       return (x - 1)
  1035.     end
  1036.   end
  1037.   return (ts + 1)
  1038. end
  1039.  
  1040.  
  1041. function widgetHandler:LowerWidget(widget)
  1042.   if (widget == nil) then
  1043.     return
  1044.   end
  1045.   local function Lower(t, f, w)
  1046.     if (f == nil) then return end
  1047.     local i = FindWidgetIndex(t, w)
  1048.     if (i == nil) then return end
  1049.     local n = FindHighestIndex(t, i, w.whInfo.layer)
  1050.     if (n and (n > i)) then
  1051.       table.insert(t, n, w)
  1052.       table.remove(t, i)
  1053.     end
  1054.   end
  1055.   Lower(self.widgets, true, widget)
  1056.   for _,listname in ipairs(callInLists) do
  1057.     Lower(self[listname..'List'], widget[listname], widget)
  1058.   end
  1059. end
  1060.  
  1061.  
  1062. function widgetHandler:FindWidget(name)
  1063.   if (type(name) ~= 'string') then
  1064.     return nil
  1065.   end
  1066.   for k,v in ipairs(self.widgets) do
  1067.     if (name == v.whInfo.name) then
  1068.     return v,k
  1069.     end
  1070.   end
  1071.   return nil
  1072. end
  1073.  
  1074.  
  1075. --------------------------------------------------------------------------------
  1076. --------------------------------------------------------------------------------
  1077. --
  1078. --  Global var/func management
  1079. --
  1080.  
  1081. function widgetHandler:RegisterGlobal(owner, name, value)
  1082.   if ((name == nil)        or
  1083.       (_G[name])           or
  1084.       (self.globals[name]) or
  1085.       (CallInsMap[name])) then
  1086.     return false
  1087.   end
  1088.   _G[name] = value
  1089.   self.globals[name] = owner
  1090.   return true
  1091. end
  1092.  
  1093.  
  1094. function widgetHandler:DeregisterGlobal(owner, name)
  1095.   if ((name == nil) or (self.globals[name] and (self.globals[name] ~= owner))) then
  1096.     return false
  1097.   end
  1098.   _G[name] = nil
  1099.   self.globals[name] = nil
  1100.   return true
  1101. end
  1102.  
  1103.  
  1104. function widgetHandler:SetGlobal(owner, name, value)
  1105.   if ((name == nil) or (self.globals[name] ~= owner)) then
  1106.     return false
  1107.   end
  1108.   _G[name] = value
  1109.   return true
  1110. end
  1111.  
  1112.  
  1113. function widgetHandler:RemoveWidgetGlobals(owner)
  1114.   local count = 0
  1115.   for name, o in pairs(self.globals) do
  1116.     if (o == owner) then
  1117.       _G[name] = nil
  1118.       self.globals[name] = nil
  1119.       count = count + 1
  1120.     end
  1121.   end
  1122.   return count
  1123. end
  1124.  
  1125.  
  1126. --------------------------------------------------------------------------------
  1127. --------------------------------------------------------------------------------
  1128. --
  1129. --  Helper facilities
  1130. --
  1131.  
  1132. local hourTimer = 0
  1133.  
  1134.  
  1135. function widgetHandler:GetHourTimer()
  1136.   return hourTimer
  1137. end
  1138.  
  1139. function widgetHandler:GetViewSizes()
  1140.   --FIXME remove
  1141.   return gl.GetViewSizes()
  1142. end
  1143.  
  1144. function widgetHandler:ForceLayout()
  1145.   forceLayout = true  --  in main.lua
  1146. end
  1147.  
  1148.  
  1149. function widgetHandler:ConfigLayoutHandler(data)
  1150.   ConfigLayoutHandler(data)
  1151. end
  1152.  
  1153. function widgetHandler:isStable()
  1154.   return isStable
  1155. end
  1156.  
  1157. --------------------------------------------------------------------------------
  1158. --------------------------------------------------------------------------------
  1159. --
  1160. --  The call-in distribution routines
  1161. --
  1162.  
  1163. function widgetHandler:Shutdown()
  1164.   self:SaveOrderList()
  1165.   self:SaveConfigData()
  1166.   for _,w in ipairs(self.ShutdownList) do
  1167.     w:Shutdown()
  1168.   end
  1169.   return
  1170. end
  1171.  
  1172. function widgetHandler:Update()
  1173.   local deltaTime = Spring.GetLastUpdateSeconds()  
  1174.   -- update the hour timer
  1175.   hourTimer = (hourTimer + deltaTime)%3600
  1176.   for _,w in ipairs(self.UpdateList) do
  1177.     w:Update(deltaTime)
  1178.   end
  1179.   return
  1180. end
  1181.  
  1182.  
  1183. function widgetHandler:ConfigureLayout(command)
  1184.   if (command == 'tweakgui') then
  1185.     self.tweakMode = true
  1186.     Spring.Echo("LuaUI TweakMode: ON")
  1187.     return true
  1188.   elseif (command == 'reconf') then
  1189.     self:SendConfigData()
  1190.     return true
  1191.   elseif (command == 'selector') then
  1192.     for _,w in ipairs(self.widgets) do
  1193.       if (w.whInfo.basename == SELECTOR_BASENAME) then
  1194.         return true  -- there can only be one
  1195.       end
  1196.     end
  1197.     local sw = self:LoadWidget(LUAUI_DIRNAME .. SELECTOR_BASENAME, VFS.RAW_FIRST)
  1198.     self:InsertWidget(sw)
  1199.     self:RaiseWidget(sw)
  1200.     return true
  1201.   elseif (string.find(command, 'togglewidget') == 1) then
  1202.     self:ToggleWidget(string.sub(command, 14))
  1203.     return true
  1204.   elseif (string.find(command, 'enablewidget') == 1) then
  1205.     self:EnableWidget(string.sub(command, 14))
  1206.     return true
  1207.   elseif (string.find(command, 'disablewidget') == 1) then
  1208.     self:DisableWidget(string.sub(command, 15))
  1209.     return true
  1210.   end
  1211.  
  1212.   if (self.actionHandler:TextAction(command)) then
  1213.     return true
  1214.   end
  1215.  
  1216.   for _,w in ipairs(self.TextCommandList) do
  1217.     if (w:TextCommand(command)) then
  1218.       return true
  1219.     end
  1220.   end
  1221.   return false
  1222. end
  1223.  
  1224.  
  1225. function widgetHandler:CommandNotify(id, params, options)
  1226.   for _,w in ipairs(self.CommandNotifyList) do
  1227.     if (w:CommandNotify(id, params, options)) then
  1228.       return true
  1229.     end
  1230.   end
  1231.   return false
  1232. end
  1233.  
  1234. --NOTE: StringStarts() and MessageProcessor is included in "chat_preprocess.lua"
  1235. function widgetHandler:AddConsoleLine(msg, priority)
  1236.   if StringStarts(msg, transmitLobbyMagic) then -- sending to the lobby
  1237.     return -- ignore
  1238.   elseif StringStarts(msg, transmitMagic) then -- receiving from the lobby
  1239.     if StringStarts(msg, voiceMagic) then
  1240.       local tableString = string.sub(msg, string.len(voiceMagic) + 1) -- strip the magic string
  1241.       local voiceCommandParams = Deserialize("return "..tableString) -- deserialize voice command parameters in table form      
  1242.       for _,w in ipairs(self.VoiceCommandList) do
  1243.         w:VoiceCommand(voiceCommandParams.commandName, voiceCommandParams)
  1244.       end
  1245.       return
  1246.     else
  1247.       for _,w in ipairs(self.AddTransmitLineList) do
  1248.         w:AddTransmitLine(msg, priority)
  1249.       end
  1250.       return
  1251.     end
  1252.   else
  1253.     --censor message for muted player. This is mandatory, everyone is forced to close ears to muted players (ie: if it is optional, then everyone will opt to hear muted player for spec-cheat info. Thus it will defeat the purpose of mute)
  1254.     local newMsg = { text = msg, priority = priority }
  1255.     MessageProcessor:ProcessConsoleLine(newMsg)
  1256.     if newMsg.msgtype ~= 'other' and newMsg.msgtype ~= 'autohost' then
  1257.         local playerID_msg = newMsg.player and newMsg.player.id --retrieve playerID from message.
  1258.         local customkeys = select(10, Spring.GetPlayerInfo(playerID_msg))
  1259.         if customkeys and customkeys.muted then
  1260.             local myPlayerID = Spring.GetLocalPlayerID()
  1261.             if myPlayerID == playerID_msg then --if I am the muted, then:
  1262.                 newMsg.argument="<your message was blocked by mute>"    --remind myself that I am muted.       
  1263.                 msg = "<your message was blocked by mute>"
  1264.             else --if I am NOT the muted, then: delete this message
  1265.                 return
  1266.             end
  1267.             --TODO: improve chili_chat2 spam-filter/dedupe-detection too.
  1268.         end
  1269.     end
  1270.     if newMsg.msgtype == 'other' and (newMsg.argument):find('Error: OpenGL: source') then
  1271.         return
  1272.     end
  1273.  
  1274.     --send message to widget:AddConsoleLine
  1275.     for _,w in ipairs(self.AddConsoleLineList) do
  1276.         w:AddConsoleLine(msg, priority)
  1277.     end
  1278.    
  1279.     --send message to widget:AddConsoleMessage
  1280.     if newMsg.msgtype == 'point' or newMsg.msgtype == 'label' then
  1281.         return -- ignore all console messages about points... those come in through the MapDrawCmd callin
  1282.     end    
  1283.     for _,w in ipairs(self.AddConsoleMessageList) do
  1284.         w:AddConsoleMessage(newMsg)
  1285.     end
  1286.   end
  1287. end
  1288.  
  1289.  
  1290. function widgetHandler:GroupChanged(groupID)
  1291.   for _,w in ipairs(self.GroupChangedList) do
  1292.     w:GroupChanged(groupID)
  1293.   end
  1294.   return
  1295. end
  1296.  
  1297.  
  1298. function widgetHandler:CommandsChanged()
  1299.   widgetHandler:UpdateSelection() -- for selectionchanged
  1300.   self.inCommandsChanged = true
  1301.   self.customCommands = {}
  1302.   for _,w in ipairs(self.CommandsChangedList) do
  1303.     w:CommandsChanged()
  1304.   end
  1305.   self.inCommandsChanged = false
  1306.   return
  1307. end
  1308.  
  1309.  
  1310. --------------------------------------------------------------------------------
  1311. --
  1312. --  Drawing call-ins
  1313. --
  1314.  
  1315.  
  1316. function widgetHandler:ViewResize(viewGeometry)
  1317.   local vsx = viewGeometry.viewSizeX
  1318.   local vsy = viewGeometry.viewSizeY
  1319.    
  1320.   for _,w in ipairs(self.ViewResizeList) do
  1321.     w:ViewResize(vsx, vsy, viewGeometry)
  1322.   end
  1323.   return
  1324. end
  1325.  
  1326. function widgetHandler:DrawScreen()
  1327.   if (self.tweakMode) then
  1328.     gl.Color(0, 0, 0, 0.5)
  1329.     local sx, sy, px, py = Spring.GetViewGeometry()
  1330.     gl.Shape(GL.QUADS, {
  1331.       {v = { px,  py }}, {v = { px+sx, py }}, {v = { px+sx, py+sy }}, {v = { px, py+sy }}
  1332.     })
  1333.     gl.Color(1, 1, 1)
  1334.   end
  1335.   for _,w in ripairs(self.DrawScreenList) do
  1336.     w:DrawScreen()
  1337.     if (self.tweakMode and w.TweakDrawScreen) then
  1338.       w:TweakDrawScreen()
  1339.     end
  1340.   end
  1341.   return
  1342. end
  1343.  
  1344.  
  1345. function widgetHandler:DrawGenesis()
  1346.   for _,w in ripairs(self.DrawGenesisList) do
  1347.     w:DrawGenesis()
  1348.   end
  1349.   return
  1350. end
  1351.  
  1352.  
  1353. function widgetHandler:DrawWorld()
  1354.   for _,w in ripairs(self.DrawWorldList) do
  1355.     w:DrawWorld()
  1356.   end
  1357.   return
  1358. end
  1359.  
  1360.  
  1361. function widgetHandler:DrawWorldPreUnit()
  1362.   for _,w in ripairs(self.DrawWorldPreUnitList) do
  1363.     w:DrawWorldPreUnit()
  1364.   end
  1365.   return
  1366. end
  1367.  
  1368.  
  1369. function widgetHandler:DrawWorldShadow()
  1370.   for _,w in ripairs(self.DrawWorldShadowList) do
  1371.     w:DrawWorldShadow()
  1372.   end
  1373.   return
  1374. end
  1375.  
  1376.  
  1377. function widgetHandler:DrawWorldReflection()
  1378.   for _,w in ripairs(self.DrawWorldReflectionList) do
  1379.     w:DrawWorldReflection()
  1380.   end
  1381.   return
  1382. end
  1383.  
  1384.  
  1385. function widgetHandler:DrawWorldRefraction()
  1386.   for _,w in ripairs(self.DrawWorldRefractionList) do
  1387.     w:DrawWorldRefraction()
  1388.   end
  1389.   return
  1390. end
  1391.  
  1392.  
  1393. function widgetHandler:DrawScreenEffects(vsx, vsy)
  1394.   for _,w in ripairs(self.DrawScreenEffectsList) do
  1395.     w:DrawScreenEffects(vsx, vsy)
  1396.   end
  1397.   return
  1398. end
  1399.  
  1400.  
  1401. function widgetHandler:DrawInMiniMap(xSize, ySize)
  1402.   for _,w in ripairs(self.DrawInMiniMapList) do
  1403.     w:DrawInMiniMap(xSize, ySize)
  1404.   end
  1405.   return
  1406. end
  1407.  
  1408.  
  1409. --------------------------------------------------------------------------------
  1410. --
  1411. --  Keyboard call-ins
  1412. --
  1413.  
  1414. function widgetHandler:KeyPress(key, mods, isRepeat, label, unicode)
  1415.   if (self.tweakMode) then
  1416.     local mo = self.mouseOwner
  1417.     if (mo and mo.TweakKeyPress) then
  1418.       mo:TweakKeyPress(key, mods, isRepeat, label, unicode)
  1419.     end
  1420.     return true
  1421.   end
  1422.  
  1423.   if (self.actionHandler:KeyAction(true, key, mods, isRepeat)) then
  1424.     return true
  1425.   end
  1426.  
  1427.   for _,w in ipairs(self.KeyPressList) do
  1428.     if (w:KeyPress(key, mods, isRepeat, label, unicode)) then
  1429.       return true
  1430.     end
  1431.   end
  1432.   return false
  1433. end
  1434.  
  1435.  
  1436. function widgetHandler:KeyRelease(key, mods, label, unicode)
  1437.   if (self.tweakMode) then
  1438.     local mo = self.mouseOwner
  1439.     if (mo and mo.TweakKeyRelease) then
  1440.       mo:TweakKeyRelease(key, mods, label, unicode)
  1441.     elseif (key == KEYSYMS.ESCAPE) then
  1442.       Spring.Echo("LuaUI TweakMode: OFF")
  1443.       self.tweakMode = false
  1444.     end
  1445.     return true
  1446.   end
  1447.  
  1448.   if (self.actionHandler:KeyAction(false, key, mods, false)) then
  1449.     return true
  1450.   end
  1451.  
  1452.   for _,w in ipairs(self.KeyReleaseList) do
  1453.     if (w:KeyRelease(key, mods, label, unicode)) then
  1454.       return true
  1455.     end
  1456.   end
  1457.   return false
  1458. end
  1459.  
  1460.  
  1461. --------------------------------------------------------------------------------
  1462. --
  1463. --  Mouse call-ins
  1464. --
  1465.  
  1466. do
  1467.   local lastDrawFrame = 0
  1468.   local lastx,lasty = 0,0
  1469.   local lastWidget
  1470.  
  1471.   local spGetDrawFrame = Spring.GetDrawFrame
  1472.  
  1473.   -- local helper (not a real call-in)
  1474.   function widgetHandler:WidgetAt(x, y)
  1475.     local drawframe = spGetDrawFrame()
  1476.     if (lastDrawFrame == drawframe)and(lastx == x)and(lasty == y) then
  1477.       return lastWidget
  1478.     end
  1479.  
  1480.     lastDrawFrame = drawframe
  1481.     lastx = x
  1482.     lasty = y
  1483.  
  1484.     if (not self.tweakMode) then
  1485.       for _,w in ipairs(self.IsAboveList) do
  1486.         if (w:IsAbove(x, y)) then
  1487.           lastWidget = w
  1488.           return w
  1489.         end
  1490.       end
  1491.     else
  1492.       for _,w in ipairs(self.TweakIsAboveList) do
  1493.         if (w:TweakIsAbove(x, y)) then
  1494.           lastWidget = w
  1495.           return w
  1496.         end
  1497.       end
  1498.     end
  1499.     lastWidget = nil
  1500.     return nil
  1501.   end
  1502. end
  1503.  
  1504.  
  1505. function widgetHandler:MousePress(x, y, button)
  1506.   local mo = self.mouseOwner
  1507.   if (not self.tweakMode) then
  1508.     if (mo) then
  1509.       mo:MousePress(x, y, button)
  1510.       return true  --  already have an active press
  1511.     end
  1512.     for _,w in ipairs(self.MousePressList) do
  1513.       if (w:MousePress(x, y, button)) then
  1514.         self.mouseOwner = w
  1515.         return true
  1516.       end
  1517.     end
  1518.     return false
  1519.   else
  1520.     if (mo) then
  1521.       mo:TweakMousePress(x, y, button)
  1522.       return true  --  already have an active press
  1523.     end
  1524.     for _,w in ipairs(self.TweakMousePressList) do
  1525.       if (w:TweakMousePress(x, y, button)) then
  1526.         self.mouseOwner = w
  1527.         return true
  1528.       end
  1529.     end
  1530.     return true  --  always grab the mouse
  1531.   end
  1532. end
  1533.  
  1534.  
  1535. function widgetHandler:MouseMove(x, y, dx, dy, button)
  1536.   local mo = self.mouseOwner
  1537.   if (not self.tweakMode) then
  1538.     if (mo and mo.MouseMove) then
  1539.       return mo:MouseMove(x, y, dx, dy, button)
  1540.     end
  1541.   else
  1542.     if (mo and mo.TweakMouseMove) then
  1543.       mo:TweakMouseMove(x, y, dx, dy, button)
  1544.     end
  1545.     return true
  1546.   end
  1547. end
  1548.  
  1549.  
  1550. function widgetHandler:MouseRelease(x, y, button)
  1551.   local mo = self.mouseOwner
  1552.   local mx, my, lmb, mmb, rmb = Spring.GetMouseState()
  1553.   if (not (lmb or mmb or rmb)) then
  1554.     self.mouseOwner = nil
  1555.   end
  1556.  
  1557.   if (not self.tweakMode) then
  1558.     if (mo and mo.MouseRelease) then
  1559.       return mo:MouseRelease(x, y, button)
  1560.     end
  1561.     return -1
  1562.   else
  1563.     if (mo and mo.TweakMouseRelease) then
  1564.       mo:TweakMouseRelease(x, y, button)
  1565.     end
  1566.     return -1
  1567.   end
  1568. end
  1569.  
  1570.  
  1571. function widgetHandler:MouseWheel(up, value)
  1572.   if (not self.tweakMode) then
  1573.     for _,w in ipairs(self.MouseWheelList) do
  1574.       if (w:MouseWheel(up, value)) then
  1575.         return true
  1576.       end
  1577.     end
  1578.     return false
  1579.   else
  1580.     for _,w in ipairs(self.TweakMouseWheelList) do
  1581.       if (w:TweakMouseWheel(up, value)) then
  1582.         return true
  1583.       end
  1584.     end
  1585.     return false -- FIXME: always grab in tweakmode?
  1586.   end
  1587. end
  1588.  
  1589. function widgetHandler:JoyAxis(axis, value)
  1590.     for _,w in ipairs(self.JoyAxisList) do
  1591.         if (w:JoyAxis(axis, value)) then
  1592.         return true
  1593.         end
  1594.     end
  1595.     return false
  1596. end
  1597.  
  1598. function widgetHandler:JoyHat(hat, value)
  1599.     for _,w in ipairs(self.JoyHatList) do
  1600.         if (w:JoyHat(hat, value)) then
  1601.         return true
  1602.         end
  1603.     end
  1604.     return false
  1605. end
  1606.  
  1607. function widgetHandler:JoyButtonDown(button, state)
  1608.     for _,w in ipairs(self.JoyButtonDownList) do
  1609.         if (w:JoyButtonDown(button, state)) then
  1610.         return true
  1611.         end
  1612.     end
  1613.     return false
  1614. end
  1615.  
  1616. function widgetHandler:JoyButtonUp(button, state)
  1617.     for _,w in ipairs(self.JoyButtonUpList) do
  1618.         if (w:JoyButtonUp(button, state)) then
  1619.         return true
  1620.         end
  1621.     end
  1622.     return false
  1623. end
  1624.  
  1625. function widgetHandler:IsAbove(x, y)
  1626.   if (self.tweakMode) then
  1627.     return true
  1628.   end
  1629.   return (widgetHandler:WidgetAt(x, y) ~= nil)
  1630. end
  1631.  
  1632.  
  1633. function widgetHandler:GetTooltip(x, y)
  1634.   if (not self.tweakMode) then
  1635.     for _,w in ipairs(self.GetTooltipList) do
  1636.       if (w:IsAbove(x, y)) then
  1637.         local tip = w:GetTooltip(x, y)
  1638.         if ((type(tip) == 'string') and (#tip > 0)) then
  1639.           return tip
  1640.         end
  1641.       end
  1642.     end
  1643.     return ""
  1644.   else
  1645.     for _,w in ipairs(self.TweakGetTooltipList) do
  1646.       if (w:TweakIsAbove(x, y)) then
  1647.         local tip = w:TweakGetTooltip(x, y) or ''
  1648.         if ((type(tip) == 'string') and (#tip > 0)) then
  1649.           return tip
  1650.         end
  1651.       end
  1652.     end
  1653.     return "Tweak Mode  --  hit ESCAPE to cancel"
  1654.   end
  1655. end
  1656.  
  1657.  
  1658. --------------------------------------------------------------------------------
  1659. --
  1660. --  Game call-ins
  1661. --
  1662.  
  1663. function widgetHandler:GamePreload()
  1664.   for _,w in ipairs(self.GamePreloadList) do
  1665.     w:GamePreload()
  1666.   end
  1667.   return
  1668. end
  1669.  
  1670. function widgetHandler:GameStart()
  1671.   for _,w in ipairs(self.GameStartList) do
  1672.     w:GameStart()
  1673.   end
  1674.  
  1675.     local plist = ""
  1676.     gaiaTeam = Spring.GetGaiaTeamID()
  1677.     for _, teamID in ipairs(Spring.GetTeamList()) do
  1678.         local teamLuaAI = Spring.GetTeamLuaAI(teamID)
  1679.         if ((teamLuaAI == nil or teamLuaAI == "") and teamID ~= gaiaTeam) then
  1680.             local _,_,_,ai,side,ally = Spring.GetTeamInfo(teamID)
  1681.             if (not ai) then
  1682.                 for _, pid in ipairs(Spring.GetPlayerList(teamID)) do
  1683.                     local name, active, spec = Spring.GetPlayerInfo(pid)
  1684.                     if active and not spec then plist = plist .. "," .. name end
  1685.                 end
  1686.             end
  1687.         end
  1688.     end
  1689.     Spring.SendCommands("wbynum 255 SPRINGIE:stats,plist".. plist)
  1690.   return
  1691. end
  1692.  
  1693. function widgetHandler:GameOver()
  1694.   for _,w in ipairs(self.GameOverList) do
  1695.     w:GameOver()
  1696.   end
  1697.   return
  1698. end
  1699.  
  1700.  
  1701. function widgetHandler:GamePaused(playerID, paused)
  1702.   for _,w in ipairs(self.GamePausedList) do
  1703.     w:GamePaused(playerID, paused)
  1704.   end
  1705.   return
  1706. end
  1707.  
  1708.  
  1709. function widgetHandler:TeamDied(teamID)
  1710.   for _,w in ipairs(self.TeamDiedList) do
  1711.     w:TeamDied(teamID)
  1712.   end
  1713.   return
  1714. end
  1715.  
  1716.  
  1717. function widgetHandler:TeamChanged(teamID)
  1718.   for _,w in ipairs(self.TeamChangedList) do
  1719.     w:TeamChanged(teamID)
  1720.   end
  1721.   return
  1722. end
  1723.  
  1724.  
  1725. function widgetHandler:PlayerAdded(playerID, reason)
  1726.   --ListMutedPlayers()
  1727.   for _,w in ipairs(self.PlayerAddedList) do
  1728.     w:PlayerAdded(playerID, reason)
  1729.   end
  1730.   return
  1731. end
  1732.  
  1733.  
  1734. function widgetHandler:PlayerChanged(playerID)
  1735.   for _,w in ipairs(self.PlayerChangedList) do
  1736.     w:PlayerChanged(playerID)
  1737.   end
  1738.   return
  1739. end
  1740.  
  1741.  
  1742. function widgetHandler:PlayerRemoved(playerID, reason)
  1743.   for _,w in ipairs(self.PlayerRemovedList) do
  1744.     w:PlayerRemoved(playerID, reason)
  1745.   end
  1746.   return
  1747. end
  1748.  
  1749.  
  1750. function widgetHandler:GameFrame(frameNum)
  1751.   for _,w in ipairs(self.GameFrameList) do
  1752.     w:GameFrame(frameNum)
  1753.   end
  1754.   return
  1755. end
  1756.  
  1757.  
  1758. function widgetHandler:ShockFront(power, dx, dy, dz)
  1759.   for _,w in ipairs(self.ShockFrontList) do
  1760.     w:ShockFront(power, dx, dy, dz)
  1761.   end
  1762.   return
  1763. end
  1764.  
  1765. function widgetHandler:RecvSkirmishAIMessage(aiTeam, dataStr)
  1766.   for _,w in ipairs(self.RecvSkirmishAIMessageList) do
  1767.     local dataRet = w:RecvSkirmishAIMessage(aiTeam, dataStr)
  1768.     if (dataRet) then
  1769.       return dataRet
  1770.     end
  1771.   end
  1772. end
  1773.  
  1774. function widgetHandler:WorldTooltip(ttType, ...)
  1775.   for _,w in ipairs(self.WorldTooltipList) do
  1776.     local tt = w:WorldTooltip(ttType, ...)
  1777.     if ((type(tt) == 'string') and (#tt > 0)) then
  1778.       return tt
  1779.     end
  1780.   end
  1781.   return
  1782. end
  1783.  
  1784.  
  1785. function widgetHandler:MapDrawCmd(playerID, cmdType, px, py, pz, ...)
  1786.   local customkeys = select(10, Spring.GetPlayerInfo(playerID))
  1787.   if customkeys and customkeys.muted then
  1788.     return true
  1789.   end
  1790.  
  1791.   local retval = false
  1792.   for _,w in ipairs(self.MapDrawCmdList) do
  1793.     local takeEvent = w:MapDrawCmd(playerID, cmdType, px, py, pz, ...)
  1794.     if (takeEvent) then
  1795.       retval = true
  1796.     end
  1797.   end
  1798.   return retval
  1799. end
  1800.  
  1801.  
  1802. function widgetHandler:GameSetup(state, ready, playerStates)
  1803.   for _,w in ipairs(self.GameSetupList) do
  1804.     local success, newReady = w:GameSetup(state, ready, playerStates)
  1805.     if (success) then
  1806.       return true, newReady
  1807.     end
  1808.   end
  1809.   return false
  1810. end
  1811.  
  1812.  
  1813. function widgetHandler:DefaultCommand(...)
  1814.   for _,w in ripairs(self.DefaultCommandList) do
  1815.     local result = w:DefaultCommand(...)
  1816.     if (type(result) == 'number') then
  1817.       return result
  1818.     end
  1819.   end
  1820.   return nil  --  not a number, use the default engine command
  1821. end
  1822.  
  1823.  
  1824. --------------------------------------------------------------------------------
  1825. --
  1826. --  Unit call-ins
  1827. --
  1828.  
  1829. function widgetHandler:UnitCreated(unitID, unitDefID, unitTeam, builderID)
  1830.   for _,w in ipairs(self.UnitCreatedList) do
  1831.     w:UnitCreated(unitID, unitDefID, unitTeam, builderID)
  1832.   end
  1833.   return
  1834. end
  1835.  
  1836.  
  1837. function widgetHandler:UnitFinished(unitID, unitDefID, unitTeam)
  1838.   for _,w in ipairs(self.UnitFinishedList) do
  1839.     w:UnitFinished(unitID, unitDefID, unitTeam)
  1840.   end
  1841.   return
  1842. end
  1843.  
  1844.  
  1845. function widgetHandler:UnitFromFactory(unitID, unitDefID, unitTeam,
  1846.                                        factID, factDefID, userOrders)
  1847.   for _,w in ipairs(self.UnitFromFactoryList) do
  1848.     w:UnitFromFactory(unitID, unitDefID, unitTeam,
  1849.                       factID, factDefID, userOrders)
  1850.   end
  1851.   return
  1852. end
  1853.  
  1854.  
  1855. function widgetHandler:UnitDestroyed(unitID, unitDefID, unitTeam)
  1856.   for _,w in ipairs(self.UnitDestroyedList) do
  1857.     w:UnitDestroyed(unitID, unitDefID, unitTeam)
  1858.   end
  1859.   return
  1860. end
  1861.  
  1862.  
  1863. function widgetHandler:UnitExperience(unitID,     unitDefID,     unitTeam,
  1864.                                       experience, oldExperience)
  1865.   for _,w in ipairs(self.UnitExperienceList) do
  1866.     w:UnitExperience(unitID,     unitDefID,     unitTeam,
  1867.                     experience, oldExperience)
  1868.   end
  1869.   return
  1870. end
  1871.  
  1872.  
  1873. function widgetHandler:UnitTaken(unitID, unitDefID, unitTeam, newTeam)
  1874.   for _,w in ipairs(self.UnitTakenList) do
  1875.     w:UnitTaken(unitID, unitDefID, unitTeam, newTeam)
  1876.   end
  1877.   return
  1878. end
  1879.  
  1880.  
  1881. function widgetHandler:UnitGiven(unitID, unitDefID, unitTeam, oldTeam)
  1882.   for _,w in ipairs(self.UnitGivenList) do
  1883.     w:UnitGiven(unitID, unitDefID, unitTeam, oldTeam)
  1884.   end
  1885.   return
  1886. end
  1887.  
  1888.  
  1889. function widgetHandler:UnitIdle(unitID, unitDefID, unitTeam)
  1890.   for _,w in ipairs(self.UnitIdleList) do
  1891.     w:UnitIdle(unitID, unitDefID, unitTeam)
  1892.   end
  1893.   return
  1894. end
  1895.  
  1896.  
  1897. function widgetHandler:UnitCommand(unitID, unitDefID, unitTeam,
  1898.                                    cmdId, cmdOpts, cmdParams)
  1899.   for _,w in ipairs(self.UnitCommandList) do
  1900.     w:UnitCommand(unitID, unitDefID, unitTeam,
  1901.                   cmdId, cmdOpts, cmdParams)
  1902.   end
  1903.   return
  1904. end
  1905.  
  1906.  
  1907. function widgetHandler:UnitCmdDone(unitID, unitDefID, unitTeam, cmdID, cmdTag)
  1908.   for _,w in ipairs(self.UnitCmdDoneList) do
  1909.     w:UnitCmdDone(unitID, unitDefID, unitTeam, cmdID, cmdTag)
  1910.   end
  1911.   return
  1912. end
  1913.  
  1914.  
  1915. function widgetHandler:UnitDamaged(unitID, unitDefID, unitTeam,
  1916.                                    damage, paralyzer)
  1917.   for _,w in ipairs(self.UnitDamagedList) do
  1918.     w:UnitDamaged(unitID, unitDefID, unitTeam, damage, paralyzer)
  1919.   end
  1920.   return
  1921. end
  1922.  
  1923.  
  1924. function widgetHandler:UnitEnteredRadar(unitID, unitTeam)
  1925.   for _,w in ipairs(self.UnitEnteredRadarList) do
  1926.     w:UnitEnteredRadar(unitID, unitTeam)
  1927.   end
  1928.   return
  1929. end
  1930.  
  1931.  
  1932. function widgetHandler:UnitEnteredLos(unitID, unitTeam)
  1933.   for _,w in ipairs(self.UnitEnteredLosList) do
  1934.     w:UnitEnteredLos(unitID, unitTeam)
  1935.   end
  1936.   return
  1937. end
  1938.  
  1939.  
  1940. function widgetHandler:UnitLeftRadar(unitID, unitTeam)
  1941.   for _,w in ipairs(self.UnitLeftRadarList) do
  1942.     w:UnitLeftRadar(unitID, unitTeam)
  1943.   end
  1944.   return
  1945. end
  1946.  
  1947.  
  1948. function widgetHandler:UnitLeftLos(unitID, unitDefID, unitTeam)
  1949.   for _,w in ipairs(self.UnitLeftLosList) do
  1950.     w:UnitLeftLos(unitID, unitDefID, unitTeam)
  1951.   end
  1952.   return
  1953. end
  1954.  
  1955.  
  1956. function widgetHandler:UnitEnteredWater(unitID, unitDefID, unitTeam)
  1957.   for _,w in ipairs(self.UnitEnteredWaterList) do
  1958.     w:UnitEnteredWater(unitID, unitDefID, unitTeam)
  1959.   end
  1960.   return
  1961. end
  1962.  
  1963.  
  1964. function widgetHandler:UnitEnteredAir(unitID, unitDefID, unitTeam)
  1965.   for _,w in ipairs(self.UnitEnteredAirList) do
  1966.     w:UnitEnteredAir(unitID, unitDefID, unitTeam)
  1967.   end
  1968.   return
  1969. end
  1970.  
  1971.  
  1972. function widgetHandler:UnitLeftWater(unitID, unitDefID, unitTeam)
  1973.   for _,w in ipairs(self.UnitLeftWaterList) do
  1974.     w:UnitLeftWater(unitID, unitDefID, unitTeam)
  1975.   end
  1976.   return
  1977. end
  1978.  
  1979.  
  1980. function widgetHandler:UnitLeftAir(unitID, unitDefID, unitTeam)
  1981.   for _,w in ipairs(self.UnitLeftAirList) do
  1982.     w:UnitLeftAir(unitID, unitDefID, unitTeam)
  1983.   end
  1984.   return
  1985. end
  1986.  
  1987.  
  1988. function widgetHandler:UnitSeismicPing(x, y, z, strength)
  1989.   for _,w in ipairs(self.UnitSeismicPingList) do
  1990.     w:UnitSeismicPing(x, y, z, strength)
  1991.   end
  1992.   return
  1993. end
  1994.  
  1995.  
  1996. function widgetHandler:UnitLoaded(unitID, unitDefID, unitTeam,
  1997.                                   transportID, transportTeam)
  1998.   for _,w in ipairs(self.UnitLoadedList) do
  1999.     w:UnitLoaded(unitID, unitDefID, unitTeam,
  2000.                  transportID, transportTeam)
  2001.   end
  2002.   return
  2003. end
  2004.  
  2005.  
  2006. function widgetHandler:UnitUnloaded(unitID, unitDefID, unitTeam,
  2007.                                     transportID, transportTeam)
  2008.   for _,w in ipairs(self.UnitUnloadedList) do
  2009.     w:UnitUnloaded(unitID, unitDefID, unitTeam,
  2010.                    transportID, transportTeam)
  2011.   end
  2012.   return
  2013. end
  2014.  
  2015.  
  2016. function widgetHandler:UnitCloaked(unitID, unitDefID, unitTeam)
  2017.   for _,w in ipairs(self.UnitCloakedList) do
  2018.     w:UnitCloaked(unitID, unitDefID, unitTeam)
  2019.   end
  2020.   return
  2021. end
  2022.  
  2023.  
  2024. function widgetHandler:UnitDecloaked(unitID, unitDefID, unitTeam)
  2025.   for _,w in ipairs(self.UnitDecloakedList) do
  2026.     w:UnitDecloaked(unitID, unitDefID, unitTeam)
  2027.   end
  2028.   return
  2029. end
  2030.  
  2031.  
  2032. function widgetHandler:UnitMoveFailed(unitID, unitDefID, unitTeam)
  2033.   for _,w in ipairs(self.UnitMoveFailedList) do
  2034.     w:UnitMoveFailed(unitID, unitDefID, unitTeam)
  2035.   end
  2036.   return
  2037. end
  2038.  
  2039.  
  2040. function widgetHandler:RecvLuaMsg(msg, playerID)
  2041.   local retval = false
  2042.   for _,w in ipairs(self.RecvLuaMsgList) do
  2043.     if (w:RecvLuaMsg(msg, playerID)) then
  2044.       retval = true
  2045.     end
  2046.   end
  2047.   return retval  --  FIXME  --  another actionHandler type?
  2048. end
  2049.  
  2050.  
  2051. function widgetHandler:StockpileChanged(unitID, unitDefID, unitTeam,
  2052.                                         weaponNum, oldCount, newCount)
  2053.   for _,w in ipairs(self.StockpileChangedList) do
  2054.     w:StockpileChanged(unitID, unitDefID, unitTeam,
  2055.                        weaponNum, oldCount, newCount)
  2056.   end
  2057.   return
  2058. end
  2059.  
  2060.  
  2061. -- local helper (not a real call-in)
  2062. local oldSelection = {}
  2063. function widgetHandler:UpdateSelection()
  2064.   local changed
  2065.   local newSelection = Spring.GetSelectedUnits()
  2066.   if (#newSelection == #oldSelection) then
  2067.     for i=1, #newSelection do
  2068.       if (newSelection[i] ~= oldSelection[i]) then -- it seems the order stays
  2069.         changed = true
  2070.         break
  2071.       end                                          
  2072.     end
  2073.   else
  2074.     changed = true
  2075.   end
  2076.   if (changed) then
  2077.     widgetHandler:SelectionChanged(newSelection)
  2078.   end
  2079.   oldSelection = newSelection
  2080. end
  2081.  
  2082.  
  2083. function widgetHandler:SelectionChanged(selectedUnits)
  2084.   for _,w in ipairs(self.SelectionChangedList) do
  2085.     local unitArray = w:SelectionChanged(selectedUnits)
  2086.     if (unitArray) then
  2087.       Spring.SelectUnitArray(unitArray)
  2088.       break
  2089.     end
  2090.   end
  2091.   return
  2092. end
  2093.  
  2094.  
  2095. function widgetHandler:GameProgress(frame)
  2096.   for _,w in ipairs(self.GameProgressList) do
  2097.     w:GameProgress(frame)
  2098.   end
  2099.   return
  2100. end
  2101.  
  2102. function widgetHandler:UnsyncedHeightMapUpdate(x1,z1,x2,z2)
  2103.   for _,w in ipairs(self.UnsyncedHeightMapUpdateList) do
  2104.     w:UnsyncedHeightMapUpdate(x1,z1,x2,z2)
  2105.   end
  2106.   return
  2107. end
  2108.  
  2109. --------------------------------------------------------------------------------
  2110. --------------------------------------------------------------------------------
  2111.  
  2112. widgetHandler:Initialize()
  2113.  
  2114. --------------------------------------------------------------------------------
  2115. --------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement