You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
11 KiB

---------------------------------------------------------------------------------
--
-- Prat - A framework for World of Warcraft chat mods
--
-- Copyright (C) 2006-2018 Prat Development Team
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- 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 General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to:
--
-- Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor,
-- Boston, MA 02110-1301, USA.
--
--
-------------------------------------------------------------------------------
--[[ BEGIN STANDARD HEADER ]] --
-- Imports
local _G = _G
local LibStub = LibStub
local tonumber = tonumber
local tostring = tostring
local pairs, ipairs = pairs, ipairs
local type = type
local Prat = Prat
local pcall = pcall
local setmetatable = setmetatable
local tinsert = tinsert
--[[ END STANDARD HEADER ]] --
Prat.Addon.defaultModuleState = true
--[[
Module system flow:
1) Module code loaded by the client
2) Module first asks if it should be INSTALLED
3) If no, the module code returns before creating the module
4) If yes, the module is given the name it should register with
5) The module code creates a new module calling NewModule
6) Once the module has been created, we recieve OnModuleCreated, we want to
remember that this module is installed, so we save that info
7) The module will have its on initalize called after Prat's.
8) If the module is disabled it will stop at this point
9) If the module is enabled, it will call its OnEnable
That give us the states: EXISTS, INSTALLED, INITIALIZED, ENABLED,
DISABLED
]]
local function NOP() end
do
Prat.Modules = {}
function Prat.RequestModuleName(self, name) -- <== EXISTS
if type(name) ~= "string" then
name = tostring(self)
end
Prat.CreateModuleControlOption(name)
-- Duh, this still requires separate loader due to the saved variable
if Prat.db and Prat.db.profile.modules[name] == 1 then
Prat.Modules[name] = "EXISTS"
end
if not Prat.Modules[name] then
Prat.Modules[name] = "EXISTS"
return name
end
end
end
do
local module_defaults = {}
function Prat.SetModuleDefaults(self, module, defaults)
module_defaults[type(module) == "table" and module.name or module] = defaults
end
function Prat.GetModuleDefaults(self, module, defaults)
return module_defaults[type(module) == "table" and module.name or module]
end
local module_init = {}
function Prat.SetModuleInit(self, module, init)
module_init[type(module) == "table" and module.name or module or "null"] = init
end
local function GetModuleInit(module)
return module_init[type(module) == "table" and module.name or module or "null"]
end
local sectionlist = {
--display
["Prat_ChannelColorMemory"] = "display",
["Prat_ChannelSticky"] = "display",
["Prat_ChatFrames"] = "display",
["Prat_Fading"] = "display",
["Prat_History"] = "display",
["Prat_Frames"] = "display",
["Prat_Editbox"] = "display",
["Prat_Paragraph"] = "display",
["Prat_Scroll"] = "display",
["Prat_Clear"] = "display",
["Prat_Font"] = "display",
["Prat_ChatTabs"] = "display",
["Prat_Buttons"] = "display",
["Prat_Original Buttons"] = "display",
--formatting
["Prat_ChannelNames"] = "formatting",
["Prat_PlayerNames"] = "formatting",
["Prat_ServerNames"] = "formatting",
["Prat_Substitutions"] = "formatting",
["Prat_Timestamps"] = "formatting",
["Prat_UrlCopy"] = "formatting",
--extras
["Prat_AddonMsgs"] = "extras",
["Prat_EventNames"] = "extras",
["Prat_PopupMessage"] = "extras",
["Prat_Sounds"] = "extras",
}
setmetatable(sectionlist, {
__index = function(t, k, v)
return "extras"
end
})
local function onInit(self) -- ==> INSTALLED -> INITIALIZED
local defaults, opts, init
defaults, module_defaults[self.name] = module_defaults[self.name] or {}
self.db = Prat.db:RegisterNamespace(self.name, defaults)
init = GetModuleInit(self)
if init then
init(self)
Prat.SetModuleInit(self, self, nil)
end
opts = Prat.GetModuleOptions(self.name)
if opts then
opts.handler = self
opts.disabled = "IsDisabled"
Prat.Options.args[sectionlist[self.name]].args[self.name], opts = opts
Prat.SetModuleOptions(self, self.name, nil)
end
local v = Prat.db.profile.modules[self.moduleName]
if v == 4 or v == 5 then
self.db.profile.on = (v == 5) and true or false
Prat.db.profile.modules[self.moduleName] = v - 2
else
Prat.db.profile.modules[self.moduleName] = self.db.profile.on and 3 or 2
end
self:SetEnabledState(self.db.profile.on)
Prat.Modules[self.name] = "INITALIZED"
end
local function onEnable(self) -- ==> INITIALIZED/DISABLED -> ENABLED
-- Print("onEnable() "..self.name)
local pats = Prat.GetModulePatterns(self)
if pats then
for _, v in pairs(pats) do
if v then
Prat.RegisterPattern(v, self.name)
end
end
end
self:OnModuleEnable()
Prat.Modules[self.name] = "ENABLED"
end
local function onDisable(self) -- ==>INITIALIZED/ENABLED -> DISABLED
-- Print("onDisable() "..self.name)
Prat.UnregisterAllPatterns(self.name)
self:OnModuleDisable()
Prat.UnregisterAllChatEvents(self)
Prat.Modules[self.name] = "DISABLED"
end
local function setValue(self, info, b)
self.db.profile[info[#info]] = b
self:OnValueChanged(info, b)
end
local function getValue(self, info)
return self.db.profile[info[#info]]
end
local function getSubValue(self, info, val)
return self.db.profile[info[#info]][val]
end
local function setSubValue(self, info, val, b)
self.db.profile[info[#info]][val] = b
self:OnSubValueChanged(info, val, b)
end
local defclr = {
r = 1,
b = 1,
g = 1,
a = 1
}
local function getColorValue(self, info)
local c = self.db.profile[info[#info]] or defclr
return c.r, c.g, c.b, c.a
end
local function setColorValue(self, info, r, g, b, a)
local c = self.db.profile[info[#info]] or defclr
c.r, c.g, c.b, c.a = r, g, b, a
self:OnColorValueChanged(info, r, g, b, a)
end
local function outputText(self, ...)
local frame, message, r, g, b = ...
if type(frame) ~= "table" or type(frame.AddMessage) ~= "function" then
frame, message, r, g, b = _G.DEFAULT_CHAT_FRAME, ...
end
if not message then return end
local header = "|cffffff78" .. tostring(Prat) .."|r (|cff80ff80" .. self.moduleName .. "|r) : %s"
frame:AddMessage(header:format(message), r, g, b)
end
local function isDisabled(self)
return not self:IsEnabled()
end
local function getDescription(self)
return self.PL.module_desc
end
local prototype = {
OnEnable = onEnable,
OnDisable = onDisable,
OnInitialize = onInit,
OnModuleEnable = NOP,
OnModuleDisable = NOP,
OnModuleInit = NOP,
OnValueChanged = NOP,
OnSubValueChanged = NOP,
OnColorValueChanged = NOP,
GetValue = getValue,
SetValue = setValue,
GetSubValue = getSubValue,
SetSubValue = setSubValue,
GetColorValue = getColorValue,
SetColorValue = setColorValue,
IsDisabled = isDisabled,
GetDescription = getDescription,
Output = outputText,
-- Standard fields
section = "extras",
}
function Prat.NewModule(self, name, ...) -- <== INSTALLED (Ace3 does the <== INITIALIZED)
local addon = Prat.Addon:NewModule(name, prototype, ...)
addon.PL = Prat:GetLocalizer({})
return addon
end
-- local locs, section
-- function NewModuleEx(self, name, locs, section, ...) -- <== INSTALLED (Ace3 does the <== INITIALIZED)
-- return Addon:NewModule(name, prototype, ...)
-- end
function Prat.Addon:OnModuleCreated(module) -- EXISTS -> INSTALLED
--[==[@debug@
_G[module.moduleName:lower()] = module
--@end-debug@]==]
Prat.Modules[module.name], Prat.Modules[module.moduleName] = "INSTALLED"
end
end
--[[
For module options, i want to use the single closure style executed from the main chunk of the module,
such as:
SetModuleOptionTable(name, function() return { ... } )
This way the options can be GC'd by from the modules, before the decision is made as
to whether we will actually load the module. they will go into a table here, and either
free'd, given back to the module for it to execute, or possible executed on this end.
In any case there will only be 1 copy of the closure, and if executed it will create its data
and then it can be freed leaving no code behind. Prat 2.0 used alot of memory solely because of
its options tables, this tries to avoid that.
]]
do
local module_options = {}
function Prat.SetModuleOptions(self, module, options)
module_options[type(module) == "table" and module.name or module or "null"] = options
end
function Prat.GetModuleOptions(module)
return module_options[type(module) == "table" and module.name or module or "null"]
end
end
do
local module_patterns = {}
function Prat.SetModulePatterns(self, module, patterns)
module_patterns[type(module) == "table" and module.name or module or "null"] = patterns
end
function Prat.GetModulePatterns(module)
return module_patterns[type(module) == "table" and module.name or module or "null"]
end
end
do
local modules_toload = {}
local extensions_toload = {}
function Prat.AddModuleToLoad(self, module_closure)
tinsert(modules_toload, module_closure)
end
function Prat.AddModuleExtension(self, extension_closure)
tinsert(extensions_toload, extension_closure)
end
local function loadNow(self, mod)
local success, ret = pcall(mod)
if not success then
_G.geterrorhandler()(ret)
end
end
function Prat.LoadModules()
for i = 1, #modules_toload, 1 do
local success, ret = pcall(modules_toload[i])
if not success then
_G.geterrorhandler()(ret)
end
modules_toload[i] = nil
end
modules_toload = nil
for i = 1, #extensions_toload, 1 do
local success, ret = pcall(extensions_toload[i])
if not success then
_G.geterrorhandler()(ret)
end
extensions_toload[i] = nil
end
extensions_toload = nil
Prat.LoadModules = nil
Prat.AddModuleToLoad = loadNow
Prat.AddModuleExtension = loadNow
end
end