vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
local MAJOR_VERSION = "2.0"
local MINOR_VERSION = tonumber((string.gsub("$Revision: 9980 $", "^.-(%d+).-$", "%1")))

if FUBAR_REVISION and FUBAR_REVISION > MINOR_VERSION then
        MINOR_VERSION = FUBAR_REVISION
end
FUBAR_REVISION = nil

local Compost = AceLibrary("Compost-2.0")
local Dewdrop = AceLibrary("Dewdrop-2.0")
local Tablet = AceLibrary:HasInstance("Tablet-2.0") and AceLibrary("Tablet-2.0")
local Jostle = AceLibrary("Jostle-2.0")

local L = AceLibrary("AceLocale-2.0"):new("FuBar")

FuBar = AceLibrary("AceAddon-2.0"):new("AceDB-2.0", "AceConsole-2.0", "AceEvent-2.0", "AceHook-2.0")
local FuBar = FuBar
FuBar.title = "FuBar"
FuBar.version = MAJOR_VERSION .. "." .. MINOR_VERSION
FuBar.date = string.gsub("$Date: 2006-09-03 12:10:31 -1000 (Sun, 03 Sep 2006) $", "^.-(%d%d%d%d%-%d%d%-%d%d).-$", "%1")

FuBar:RegisterDB("FuBar2DB")

local charID = AceLibrary("AceDB-2.0").CHAR_ID
local realmID = AceLibrary("AceDB-2.0").REALM_ID
local classID = AceLibrary("AceDB-2.0").CLASS_ID

local _G = getfenv(0)
local plugins = {}

local FuBar_Panel = FuBar_Panel
_G.FuBar_Panel = nil

if not FUBAR_DEFAULTS then
        FUBAR_DEFAULTS = function()
                return {
                        fontSize = 12,
                        adjust = true,
                        panels = {
                                [1] = {
                                        attachPoint = "TOP",
                                        plugins = {
                                                left = {
                                                        "LocationFu",
                                                        "ExperienceFu",
                                                        "PerformanceFu",
                                                },
                                                center = {},
                                                right = {
                                                        "ClockFu",
                                                        "VolumeFu",
                                                }
                                        }
                                }, 
                                [2] = {
                                        attachPoint = "BOTTOM"
                                }
                        }
                }
        end
end

local CATEGORIES = {
        ["Action Bars"] = L["Action Bars"],
        ["Auction"] = L["Auction"],
        ["Audio"] = L["Audio"],
        ["Battlegrounds/PvP"] = L["Battlegrounds/PvP"],
        ["Buffs"] = L["Buffs"],
        ["Chat/Communication"] = L["Chat/Communication"],
        ["Druid"] = L["Druid"],
        ["Hunter"] = L["Hunter"],
        ["Mage"] = L["Mage"],
        ["Paladin"] = L["Paladin"],
        ["Priest"] = L["Priest"],
        ["Rogue"] = L["Rogue"],
        ["Shaman"] = L["Shaman"],
        ["Warlock"] = L["Warlock"],
        ["Warrior"] = L["Warrior"],
        ["Healer"] = L["Healer"],
        ["Tank"] = L["Tank"],
        ["Caster"] = L["Caster"],
        ["Combat"] = L["Combat"],
        ["Compilations"] = L["Compilations"],
        ["Data Export"] = L["Data Export"],
        ["Development Tools"] = L["Development Tools"],
        ["Guild"] = L["Guild"],
        ["Frame Modification"] = L["Frame Modification"],
        ["Interface Enhancements"] = L["Interface Enhancements"],
        ["Inventory"] = L["Inventory"],
        ["Library"] = L["Library"],
        ["Map"] = L["Map"],
        ["Mail"] = L["Mail"],
        ["Miscellaneous"] = L["Miscellaneous"],
        ["Quest"] = L["Quest"],
        ["Raid"] = L["Raid"],
        ["Tradeskill"] = L["Tradeskill"],
        ["UnitFrame"] = L["UnitFrame"],
        ["Others"] = L["Others"],
}

local basePluginFrame

local alreadyEnabled = false

local backgrounds = {["Interface\\AddOns\\FuBar\\background"] = L["Default"]}

function FuBar:RegisterSkin(name, bgFile)
        assert(type(name) == "string", "You must provide a name for your skin")
        assert(type(bgFile) == "string", "You must provide a file for your skin")
        if not backgrounds[bgFile] then
                backgrounds[bgFile] = name
        end
end

local function SetBackground(bgFile)
        if backgrounds[bgFile] then
                FuBar.db.profile.skin = bgFile
                FuBar_Panel:SetBackground(bgFile)
        end
end

local function GetBackground()
        return backgrounds[FuBar.db.profile.skin] and FuBar.db.profile.skin or "Interface\\AddOns\\FuBar\\background"
end

local function background_sort(alpha, bravo)
        return backgrounds[alpha] < backgrounds[bravo]
end

local optionsTable = {
        handler = FuBar,
        type = "group",
        args = {
                overflow = {
                        type = "toggle",
                        name = L["Overflow plugins"],
                        desc = L["Let plugins overflow onto another panel"],
                        get = "IsOverflowing",
                        set = "ToggleOverflowing",
                },
                create = {
                        type = "execute",
                        name = L["Create new panel"],
                        desc = L["Create new panel"],
                        func = "new",
                        handler = FuBar_Panel
                },
                adjust = {
                        type = "toggle",
                        name = L["Auto-adjust frames"],
                        desc = L["Toggle auto-adjustment of blizzard's frames"],
                        get = "IsAdjust",
                        set = "ToggleAdjust",
                },
                autohideTop = {
                        type = "toggle",
                        name = L["Auto-hide top panels"],
                        desc = L["Toggle auto-hiding of the top panels"],
                        get = "IsAutoHidingTop",
                        set = "ToggleAutoHidingTop",
                },
                autohideBottom = {
                        type = "toggle",
                        name = L["Auto-hide bottom panels"],
                        desc = L["Toggle auto-hiding of the bottom panels"],
                        get = "IsAutoHidingBottom",
                        set = "ToggleAutoHidingBottom",
                },
                texture = {
                        type = "text",
                        name = L["Texture"],
                        desc = L["Change the texture of the panels"],
                        get = GetBackground,
                        set = SetBackground,
                        validate = backgrounds
                },
                spacing = {
                        type = "group",
                        name = L["Spacing"],
                        desc = L["Spacing between plugins"],
                        args = {
                                left = {
                                        type = "range",
                                        name = L["Left-aligned spacing"],
                                        desc = L["Set spacing between left-aligned plugins"],
                                        max = 40,
                                        min = 0,
                                        step = 1,
                                        get = "GetLeftSpacing",
                                        set = "SetLeftSpacing",
                                },
                                center = {
                                        type = "range",
                                        name = L["Center-aligned spacing"],
                                        desc = L["Set spacing between center-aligned plugins"],
                                        max = 40,
                                        min = 0,
                                        step = 1,
                                        get = "GetCenterSpacing",
                                        set = "SetCenterSpacing",
                                },
                                right = {
                                        type = "range",
                                        name = L["Right-aligned spacing"],
                                        desc = L["Set spacing between right-aligned plugins"],
                                        max = 40,
                                        min = 0,
                                        step = 1,
                                        get = "GetRightSpacing",
                                        set = "SetRightSpacing",
                                },
                        }
                },
                fontsize = {
                        type = "group",
                        name = L["Font size"],
                        desc = L["Font size"],
                        args = {
                                panel = {
                                        type = "range",
                                        name = L["Panel font size"],
                                        desc = L["Set font size for the plugins on the panel"],
                                        max = 24,
                                        min = 7,
                                        step = 1,
                                        get = "GetFontSize",
                                        set = "SetFontSize",
                                },
                                tooltip = {
                                        type = "range",
                                        name = L["Tooltip font size"],
                                        desc = L["Set font size for the tooltip"],
                                        max = 2,
                                        min = 0.5,
                                        isPercent = true,
                                        get = function()
                                                return Tablet:GetFontSizePercent(FuBar.db.profile.tooltip)
                                        end,
                                        set = function(value)
                                                return Tablet:SetFontSizePercent(FuBar.db.profile.tooltip, value)
                                        end,
                                        hidden = function()
                                                return not Tablet
                                        end
                                }
                        }
                },
                transparency = {
                        type = "group",
                        name = L["Transparency"],
                        desc = L["Transparency"],
                        args = {
                                panel = {
                                        type = "range",
                                        name = L["Panel transparency"],
                                        desc = L["Set transparency of the panels"],
                                        max = 1,
                                        min = 0,
                                        step = 0.05,
                                        isPercent = true,
                                        get = "GetTransparency",
                                        set = "SetTransparency",
                                },
                                tooltip = {
                                        type = "range",
                                        name = L["Tooltip transparency"],
                                        desc = L["Set transparency of the tooltip"],
                                        max = 1,
                                        min = 0,
                                        step = 0.05,
                                        isPercent = true,
                                        get = function()
                                                return Tablet:GetTransparency(FuBar.db.profile.tooltip)
                                        end,
                                        set = function(value)
                                                return Tablet:SetTransparency(FuBar.db.profile.tooltip, value)
                                        end,
                                        hidden = function()
                                                return not Tablet
                                        end
                                }
                        }
                },
                thickness = {
                        type = "range",
                        name = L["Thickness"],
                        desc = L["Set thickness between the panels"],
                        max = 20,
                        min = 0,
                        step = 1,
                        get = "GetThickness",
                        set = "SetThickness",
                },
        }
}

FuBar:RegisterChatCommand(L:GetTable("ChatCommands"), optionsTable)

optionsTable.args.standby = nil

function FuBar:GetScaledCursorPosition()
        local x, y = GetCursorPosition()
        local scale = GetScreenHeight() / 768
        return x * scale, y * scale
end

function FuBar:Reset()
        self:UnregisterEvent("PLAYER_LOGOUT")
        FuBar2DB = {}
        ReloadUI()
end

function FuBar:GetPanel(panelId)
        return FuBar_Panel.instances[panelId]
end

function FuBar:GetNumPanels()
        return table.getn(FuBar_Panel.instances)
end

function FuBar:GetBottommostTopPanel()
        local bottom = GetScreenHeight()
        local best
        for i = 1, self:GetNumPanels() do
                local panel = self:GetPanel(i)
                if panel:GetAttachPoint() == "TOP" and panel.frame:GetBottom() < bottom then
                        bottom = panel.frame:GetBottom()
                        best = panel
                end
        end
        return best
end

function FuBar:GetTopmostBottomPanel()
        local top = 0
        local best
        for i = 1, self:GetNumPanels() do
                local panel = self:GetPanel(i)
                if panel:GetAttachPoint() == "BOTTOM" and panel.frame:GetTop() > top then
                        top = panel.frame:GetTop()
                        best = panel
                end
        end
        return best
end

local isChangingProfile, previousProfile

function FuBar:IsChangingProfile()
        return isChangingProfile
end

function FuBar:GetPluginProfiling()
        for _,p in ipairs(plugins) do
                if p:GetTitle() == self.db.profile.profilePlugin then
                        return p, p:GetTitle()
                end
        end
end

function FuBar:SetPluginProfiling(plugin)
        self.db.profile.profilePlugin = plugin:GetTitle()
end

function FuBar:IsAdjust()
        return self.db.profile.adjust
end

function FuBar:ToggleAdjust()
        self.db.profile.adjust = not self.db.profile.adjust
        self:UpdateJostleAdjustments()
        return self.db.profile.adjust
end

function FuBar:IsOverflowing()
        return not self.db.profile.overflow
end

function FuBar:ToggleOverflowing()
        self.db.profile.overflow = not self.db.profile.overflow
        for i = 1, self:GetNumPanels() do
                self:GetPanel(i):CheckForOverlap()
        end
        return not self.db.profile.overflow
end

function FuBar:UpdateJostleAdjustments()
        if not self.db.profile.adjust then
                Jostle:DisableTopAdjusting()
                Jostle:DisableBottomAdjusting()
        else
                if self:IsAutoHidingTop() then
                        Jostle:DisableTopAdjusting()
                else
                        Jostle:EnableTopAdjusting()
                end
                if self:IsAutoHidingBottom() then
                        Jostle:DisableBottomAdjusting()
                else
                        Jostle:EnableBottomAdjusting()
                end
        end
end

function FuBar:IsAutoHidingTop()
        return self.db.profile.autohideTop
end

local autohideTopTime, autohideBottomTime

function FuBar:ToggleAutoHidingTop()
        self.db.profile.autohideTop = not self.db.profile.autohideTop
        self:UpdateJostleAdjustments()
        if not self.db.profile.autohideTop then
                for i = 1, self:GetNumPanels() do
                        if self:GetPanel(i):GetAttachPoint() == "TOP" then
                                local frame = getglobal("FuBarFrame" .. i)
                                frame:SetAlpha(1)
                                frame:SetFrameStrata("HIGH")
                        end
                end
        else
                self:ScheduleEvent("FuBar_AutoHideTop", self.OnUpdate_AutoHideTop, 1, self)
        end
        autohideTopTime = GetTime()
        return self.db.profile.autohideTop
end

function FuBar:IsAutoHidingBottom()
        return self.db.profile.autohideBottom
end

function FuBar:ToggleAutoHidingBottom()
        self.db.profile.autohideBottom = not self.db.profile.autohideBottom
        self:UpdateJostleAdjustments()
        if not self.db.profile.autohideBottom then
                for i = 1, self:GetNumPanels() do
                        if self:GetPanel(i):GetAttachPoint() == "BOTTOM" then
                                local frame = getglobal("FuBarFrame" .. i)
                                frame:SetAlpha(1)
                                frame:SetFrameStrata("HIGH")
                        end
                end
        else
                self:ScheduleEvent("FuBar_AutoHideBottom", self.OnUpdate_AutoHideBottom, 1, self)
        end
        autohideBottomTime = GetTime()
        return self.db.profile.autohideBottom
end

function FuBar:GetFontSize()
        if not self.db.profile then
                return 12
        end
        return self.db.profile.fontSize or 12
end
        
function FuBar:SetFontSize(size)
        size = size or 12
        self.db.profile.fontSize = size
        for i,plugin in ipairs(plugins) do
                if type(plugin.SetFontSize) ~= "function" then
                        table.remove(plugins, i)
                        i = i - 1
                else
                        plugin:SetFontSize(size)
                end
        end
        for i = 1, self:GetNumPanels() do
                self:GetPanel(i):UpdateTexture()
        end
        self:Update()
        Jostle:Refresh()
end

function FuBar:GetLeftSpacing()
        if not self.db.profile then
                return 20
        end
        return self.db.profile.leftSpacing or 20
end

function FuBar:SetLeftSpacing(size)
        self.db.profile.leftSpacing = size
        self:Update()
end

function FuBar:GetCenterSpacing()
        if not self.db.profile then
                return 20
        end
        return self.db.profile.centerSpacing or 20
end

function FuBar:SetCenterSpacing(size)
        self.db.profile.centerSpacing = size
        self:Update()
end
        
function FuBar:GetRightSpacing()
        if not self.db.profile then
                return 20
        end
        return self.db.profile.rightSpacing or 20
end

function FuBar:SetRightSpacing(size)
        self.db.profile.rightSpacing = size
        self:Update()
end

function FuBar:SetThickness(size)
        self.db.profile.thickness = size
        for i = 1, self:GetNumPanels() do
                self:GetPanel(i):UpdateTexture()
        end
        self:Update()
        Jostle:Refresh()
end

function FuBar:GetThickness(size)
        if not self.db.profile then
                return 5
        end
        return self.db.profile.thickness or 5
end

function FuBar:GetTransparency()
        return self.db.profile.transparency or 0.8
end

function FuBar:SetTransparency(value)
        self.db.profile.transparency = value
        for i = 1, self:GetNumPanels() do
                self:GetPanel(i):UpdateTexture()
        end
end

local function CheckLoadCondition(loadCondition)
        return RunScript("(function()"..loadCondition.." end)()")
end

local function _IsCorrectPlugin(plugin)
        if type(plugin) ~= "table" then
                return false
        elseif type(plugin.GetName) ~= "function" then
                return false
        elseif type(plugin.GetTitle) ~= "function" then
                return false
        elseif type(plugin.GetCategory) ~= "function" then
                return false
        elseif type(plugin.SetFontSize) ~= "function" then
                return false
        elseif type(plugin.GetFrame) ~= "function" then
                return false
        elseif type(plugin.Show) ~= "function" then
                return false
        elseif type(plugin.Hide) ~= "function" then
                return false
        elseif type(plugin.GetPanel) ~= "function" then
                return false
        elseif type(plugin:GetName()) ~= "string" then
                return false
        elseif type(plugin:GetTitle()) ~= "string" then
                return false
        elseif type(plugin:GetCategory()) ~= "string" then
                return false
        end
        local frame = plugin:GetFrame()
        if type(frame) ~= "table" then
                return false
        elseif type(frame[0]) ~= "userdata" then
                return false
        elseif type(frame.GetFrameType) ~= "function" then
                return false
        elseif type(frame:GetFrameType()) ~= "string" then
                return false
        end
        return true
end

local donothing = function() end

local function IsCorrectPlugin(plugin)
        local add = DEFAULT_CHAT_FRAME.AddMessage
        DEFAULT_CHAT_FRAME.AddMessage = donothing
        local ret, msg = pcall(_IsCorrectPlugin, plugin)
        DEFAULT_CHAT_FRAME.AddMessage = add
        if ret then
                return msg
        end
end

local function _IsCorrectPanel(panel)
        if type(panel) ~= "table" then
                return false
        elseif type(panel.AddPlugin) ~= "function" then
                return false
        elseif type(panel.RemovePlugin) ~= "function" then
                return false
        elseif type(panel.GetNumPlugins) ~= "function" then
                return false
        elseif type(panel:GetNumPlugins()) ~= "number" then
                return false
        elseif type(panel.GetPlugin) ~= "function" then
                return false
        elseif type(panel.HasPlugin) ~= "function" then
                return false
        elseif type(panel.GetPluginSide) ~= "function" then
                return false
        end
        return true
end

local function IsCorrectPanel(panel)
        local add = DEFAULT_CHAT_FRAME.AddMessage
        DEFAULT_CHAT_FRAME.AddMessage = donothing
        local ret, msg = pcall(_IsCorrectPanel, panel)
        DEFAULT_CHAT_FRAME.AddMessage = add
        if ret then
                return msg
        end
end

local n
local mt = {
        __newindex = function(self, k, v)
                rawset(self, k, v)
                n[k] = v
        end,
}

local function IsAceAddon(addon)
        if type(addon) ~= "table" then
                return false
        elseif addon == AceAddon then
                return true
        elseif not getmetatable(addon) then
                return false
        else
                return IsAceAddon(getmetatable(addon).__index)
        end
end

local blank = {}
local function LoadAddOnWrapper(addon)
        local o = getfenv(0)
        n = {}
        setmetatable(o, mt)
        local success, ret = pcall(LoadAddOn, addon)
        setmetatable(o, blank)
        if not ret then
                FuBar:Print("Error loading LoadOnDemand plugin " .. addon)
                return
        elseif not success then
                FuBar:Print("Error loading LoadOnDemand plugin " .. addon .. ": " .. ret)
                return
        end
        
        local dependencies
        local plugin
        for k,v in pairs(n) do
                if IsCorrectPlugin(v) then
                        if plugin then
                                if not dependencies then
                                        dependencies = {}
                                end
                                table.insert(dependencies, plugin)
                        end
                        plugin = v
                        if IsAceAddon(plugin) then
                                if not plugin._initialized then
                                        if not AceData.profileBasePath then
                                                --plugin._initialized = true
                                                --plugin:Initialize()
                                                --ace1ToBeInitialized[plugin] = true
                                        else
                                                plugin._initialized = true
                                                ace:InitializeApp(plugin)
                                        end
                                end
                        else
                                if not plugin._initialized then
                                        plugin._initialized = true
                                        if type(plugin.Initialize) == "function" then
                                                plugin:Initialize()
                                        end
                                end
                        end
                end
        end
        if not plugin then
                FuBar:Print("Error loading LoadOnDemand plugin " .. addon)
                return
        end
        return ret, plugin, dependencies
end

local toBeLoaded

local function CleanDB()
        local self = FuBar
        
        local t = Compost:Acquire()
        for _,panel in ipairs(self.db.profile.panels) do
                if panel and panel.plugins then
                        for _,part in pairs(panel.plugins) do
                                if part then
                                        for i,plugin in ipairs(part) do
                                                if t[plugin] then
                                                        i = i - 1
                                                        table.remove(part, i)
                                                else
                                                        t[plugin] = true
                                                end
                                        end
                                end
                        end
                end
        end
        t = Compost:Reclaim(t)
end

function FuBar:OnInitialize()
        toBeLoaded = {}
        
        if not next(FuBar.db.profile) then
                local def
                local profile = self:GetProfile()
                if string.find(profile, "^char/") or string.find(profile, "^class/") then
                        local _,class = UnitClass("player")
                        def = _G["FUBAR_DEFAULTS_" .. class] or FUBAR_DEFAULTS
                else
                        def = FUBAR_DEFAULTS
                end
                if type(def) == "function" then
                        def = def()
                end
                for k,v in pairs(def) do
                        FuBar.db.profile[k] = v
                end
        end
        
        if not self.db.profile.loadOnDemand then
                self.db.profile.loadOnDemand = {}
        end
        
        if not self.db.profile.tooltip then
                self.db.profile.tooltip = {}
        end
        
        self:SetFontSize(self:GetFontSize())
        
        self.menu = function(level, value)
                local panelId = FuBar_Panel.selectedPanel or 1
                local panel = self:GetPanel(panelId)
                
                if not panel then
                        Dewdrop:Close()
                        return
                end
                
                if level == 1 then
                        Dewdrop:AddLine(
                                'text', self.name,
                                'isTitle', true
                        )
                        
                        local menuTitles = Compost:Acquire()
                        for _,plugin in ipairs(plugins) do
                                local alreadyExists = false
                                local category = plugin:GetCategory()
                                if not CATEGORIES[category] then
                                        category = L["Others"]
                                end
                                menuTitles[category] = true
                        end
                        for _,name in ipairs(toBeLoaded) do
                                local category = self.db.profile.loadOnDemand[name].category
                                if not CATEGORIES[category] then
                                        category = L["Others"]
                                end
                                menuTitles[category] = true
                        end
                        local titles = Compost:Acquire()
                        for name in menuTitles do
                                table.insert(titles, name)
                        end
                        Compost:Reclaim(menuTitles)
                        table.sort(titles)
                        for _,category in ipairs(titles) do
                                Dewdrop:AddLine(
                                        'text', CATEGORIES[category],
                                        'value', category,
                                        'hasArrow', true
                                )
                        end
                        Compost:Reclaim(titles)
                        
                        Dewdrop:AddLine()
                        
                        Dewdrop:AddLine(
                                'text', L["Attach"],
                                'hasArrow', true,
                                'value', "attach"
                        )
                        
                        Dewdrop:AddLine(
                                'text', L["Lock panel"],
                                'arg1', panel,
                                'func', "ToggleLocked",
                                'checked', panel:IsLocked()
                        )
                        
                        Dewdrop:AddLine(
                                'text', L["Remove panel"],
                                'arg1', panel,
                                'func', "WarnDestroy",
                                'disabled', panelId == 1 and not self:GetPanel(2),
                                'closeWhenClicked', true
                        )
                        
                        Dewdrop:AddLine()
                        
                        Dewdrop:FeedAceOptionsTable(optionsTable)
                        
                        Dewdrop:AddLine(
                                'text', CLOSE,
                                'arg1', Dewdrop,
                                'func', "Close"
                        )
                elseif Dewdrop:FeedAceOptionsTable(optionsTable) then
                elseif level == 2 then
                        if value == "attach" then
                                Dewdrop:AddLine(
                                        'text', L["Attach to top"],
                                        'arg1', panel,
                                        'func', panel:GetAttachPoint() ~= "TOP" and "SetAttachPoint",
                                        'arg2', "TOP",
                                        'checked', panel:GetAttachPoint() == "TOP",
                                        'closeWhenClicked', true,
                                        'isRadio', true
                                )
                                
                                Dewdrop:AddLine(
                                        'text', L["Attach to bottom"],
                                        'arg1', panel,
                                        'func', panel:GetAttachPoint() ~= "BOTTOM" and "SetAttachPoint",
                                        'arg2', "BOTTOM",
                                        'checked', panel:GetAttachPoint() == "BOTTOM",
                                        'closeWhenClicked', true,
                                        'isRadio', true
                                )
                                
                                Dewdrop:AddLine(
                                        'text', L["Detach panel"],
                                        'arg1', panel,
                                        'func', panel:GetAttachPoint() ~= "NONE" and "SetAttachPoint",
                                        'arg2', "NONE",
                                        'checked', panel:GetAttachPoint() == "NONE",
                                        'closeWhenClicked', true,
                                        'isRadio', true
                                )
                        else
                                for _,plugin in ipairs(plugins) do
                                        local category = plugin:GetCategory()
                                        if not CATEGORIES[category] then
                                                category = L["Others"]
                                        end
                                        if category == value then
                                                Dewdrop:AddLine(
                                                        'text', plugin:GetTitle(),
                                                        'arg1', plugin,
                                                        'func', plugin:GetPanel() and "Hide" or "Show",
                                                        'arg2', FuBar_Panel.selectedPanel or 1,
                                                        'checked', plugin:GetPanel()
                                                )
                                        end
                                end
                                for i,name in ipairs(toBeLoaded) do
                                        local i, name = i, name
                                        local lod = self.db.profile.loadOnDemand[name]
                                        
                                        local category = lod.category
                                        if not CATEGORIES[category] then
                                                category = L["Others"]
                                        end
                                        if category == value then
                                                Dewdrop:AddLine(
                                                        'text', lod.title,
                                                        'arg1', self,
                                                        'func', "LoadPlugin",
                                                        'arg2', name
                                                )
                                        end
                                end
                        end
                end
        end
        
        self:ScheduleEvent(self.LoadLoadOnDemandPlugins, 0, self)
        
        CleanDB()
end

function FuBar:LoadPlugin(name)
        if not alreadyEnabled then
                local dep1,dep2,dep3,dep4 = GetAddOnDependencies(name)
                if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then
                        if not self.acePluginToLoad then
                                self.acePluginToLoad = {}
                        end
                        self.acePluginToLoad[name] = true
                        return
                end
        end
        local loaded, plugin, dependencies = LoadAddOnWrapper(name)
        if loaded then
                if type(plugin.SetLoadOnDemand) == "function" then
                        plugin:SetLoadOnDemand(true)
                end
                self.db.profile.loadOnDemand[name] = {
                        title = plugin:GetTitle(),
                        category = plugin:GetCategory(),
                        disabled = nil,
                        condition = type(plugin.GetLoadCondition) == "function" and plugin:GetLoadCondition() or nil,
                }
                plugin:Show(FuBar_Panel.selectedPanel or false)
                if dependencies then
                        for _,v in ipairs(dependencies) do
                                if type(v.SetLoadOnDemand) == "function" then
                                        v:SetLoadOnDemand(true)
                                end
                                self.db.profile.loadOnDemand[name].disabled = nil
                                for j,u in ipairs(toBeLoaded) do
                                        if u == v:GetTitle() then
                                                table.remove(toBeLoaded, j)
                                                break
                                        end
                                end
                                if v.hideWithoutStandby then
                                        v:EnableApp()
                                else
                                        v:Show(panelId or false)
                                end
                        end
                end
                self:SetFontSize(self:GetFontSize())
                for i,n in ipairs(toBeLoaded) do
                        if n == name then
                                table.remove(toBeLoaded, i)
                                break
                        end
                end
                self.db.profile.loadOnDemand[name].disabled = nil
        end
end

function FuBar:OnEnable()
        if alreadyEnabled then
                ReloadUI()
                return
        end
        alreadyEnabled = true
        
        if AceLibrary:HasInstance("Tablet-2.0") then
                Tablet = AceLibrary("Tablet-2.0")
        end
        if not self.db.profile.panels then
                self.db.profile.panels = {}
        end
        
        if not self.db.profile.minimap then
                self.db.profile.minimap = {}
        end
        
        for i = table.getn(self.db.profile.panels), 1, -1 do
                if self.db.profile.panels[i] then
                        FuBar_Panel:new()
                end
        end
        
        self:CheckResolution()
        
        if self:GetNumPanels() == 0 then
                FuBar_Panel:new()
        end
        
        if FuBar.db.profile.skin then
                SetBackground(FuBar.db.profile.skin)
        end
        
        self:RegisterEvent("AceLibrary_Register")
--      self:RegisterEvent("DISPLAY_SIZE_CHANGED", "OnDisplaySizeChanged")
        self:RegisterEvent("CVAR_UPDATE", "OnCVarUpdate")
        
        self:Hook("RestartGx")
        
        local function func()
                if not self:SetupPlugins() then
                        self:ScheduleEvent(func, 0)
                        return
                end
                self:Update()
                
                self:ScheduleEvent("FuBar_AutoHideTop", self.OnUpdate_AutoHideTop, 1, self)
                self:ScheduleEvent("FuBar_AutoHideBottom", self.OnUpdate_AutoHideBottom, 1, self)
        end
        self:RegisterEvent("AceEvent_FullyInitialized", func, true)
        
        self:UpdateJostleAdjustments()
        
        if self.db.profile.skin and backgrounds[self.db.profile.skin] then
                FuBar_Panel:SetBackground(self.db.profile.skin)
        end
        
        self:RegisterEvent("PLAYER_ENTERING_WORLD", "PLAYER_ENTERING_WORLD", true)
end

function FuBar:LoadLoadOnDemandPlugins()
        for i = 1, GetNumAddOns() do
                local name, title, notes, enabled, loadable, reason, security = GetAddOnInfo(i)
                if IsAddOnLoadOnDemand(i) and enabled and loadable and not IsAddOnLoaded(i) then
                        local deps = Compost:Acquire(GetAddOnDependencies(name))
                        local good = false
                        for _,dep in ipairs(deps) do
                                if dep == "FuBar" then
                                        good = true
                                        break
                                end
                        end
                        if good then
                                if not self.db.profile.loadOnDemand[name] then
                                        self:LoadPlugin(name)
                                elseif not self.db.profile.loadOnDemand[name].disabled and (not self.db.profile.loadOnDemand[name].condition or CheckLoadCondition(self.db.profile.loadOnDemand[name].condition)) then
                                        self:LoadPlugin(name)
                                else
                                        table.insert(toBeLoaded, name)
                                end
                        end
                        Compost:Reclaim(deps)
                end
        end
end

function FuBar:AceLibrary_Register(major, instance)
        if major == "Tablet-2.0" then
                Tablet = instance
        end
end

function FuBar:PLAYER_ENTERING_WORLD()
        if self.acePluginToLoad then
                for name in pairs(self.acePluginToLoad) do
                        self:LoadPlugin(name)
                end
                Compost:Reclaim(self.acePluginToLoad)
                self.acePluginToLoad = nil
        end
end

function FuBar:RestartGx()
        self.hooks.RestartGx.orig()
        self:ScheduleEvent(self.Update, 0, self)
end

function FuBar:OnCVarUpdate()
        if arg1 == "USE_UISCALE" then
                self:ScheduleEvent(self.CheckResolution, 0, self)
        end
end

local lastScreenWidth = GetScreenWidth()
function FuBar:CheckResolution()
        local screenWidth = GetScreenWidth()
        if lastScreenWidth ~= screenWidth then
                for i = 1, self:GetNumPanels() do
                        self:GetPanel(i):Update()
                end
                lastScreenWidth = screenWidth
                Jostle:Refresh()
        end
end

local inTopPanel, inBottomPanel

function FuBar:OnUpdate_AutoHideTop()
        if not inTopPanel and self:IsAutoHidingTop() then
                for i = 1, self:GetNumPanels() do
                        if self:GetPanel(i):GetAttachPoint() == "TOP" then
                                local frame = getglobal("FuBarFrame" .. i)
                                frame:SetAlpha(0)
                                frame:SetFrameStrata("BACKGROUND")
                        end
                end
        end
end

function FuBar:OnUpdate_AutoHideBottom()
        if not inBottomPanel and self:IsAutoHidingBottom() then
                for i = 1, self:GetNumPanels() do
                        if self:GetPanel(i):GetAttachPoint() == "BOTTOM" then
                                local frame = getglobal("FuBarFrame" .. i)
                                frame:SetAlpha(0)
                                frame:SetFrameStrata("BACKGROUND")
                        end
                end
        end
end

local doneSetupPlugins, pluginsToSetup

function FuBar:OnDisable()
        for i = 1, self:GetNumPanels() do
                self:GetPanel(i).frame:Hide()
        end
end

function FuBar:Report()
        local report = Compost:Acquire(
                Compost:AcquireHash(
                        'text', self.loc.ARGUMENT_ADJUST,
                        'val', self:IsAdjust() and 1 or 0,
                        'map', self.loc.MAP_ONOFF
                )
        )
        self.cmd:report(report)
        Compost:Reclaim(report, 1)
end

function FuBar:GetNumPlugins()
        return table.getn(plugins)
end

function FuBar:GetPlugin(i)
        return plugins[i]
end

function FuBar:RegisterPlugin(plugin)
        if not IsCorrectPlugin(plugin) then
                return
        end
        table.insert(plugins, plugin)
        
        local frame = plugin:GetFrame()
        local downTime
        if frame:HasScript("OnClick") then
                local OnClick = frame:GetScript("OnClick")
                frame:SetScript("OnClick", function()
                        if OnClick and (not downTime or GetTime() < downTime + 0.5) and (not this.stopClick or GetTime() > this.stopClick) then
                                OnClick()
                        end
                        downTime = nil
                end)
        end
        local OnMouseDown = frame:GetScript("OnMouseDown")
        frame:SetScript("OnMouseDown", function()
                if arg1 == "LeftButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
                        downTime = GetTime()
                        FuBar:Plugin_StartDrag(plugin)
                end
                if OnMouseDown then
                        OnMouseDown()
                end
        end)
        local OnMouseUp = frame:GetScript("OnMouseUp")
        frame:SetScript("OnMouseUp", function()
                if arg1 == "LeftButton" then
                        if not FuBar:Plugin_StopDrag(plugin) then
                                if OnMouseUp then
                                        OnMouseUp()
                                end
                        else
                                this.stopClick = GetTime() + 0.05
                        end
                elseif OnMouseUp then
                        OnMouseUp()
                end
        end)
        local OnEnter = frame:GetScript("OnEnter")
        frame:SetScript("OnEnter", function()
                FuBar:Panel_OnEnter(plugin)
                if OnEnter then
                        OnEnter()
                end
        end)
        local OnLeave = frame:GetScript("OnLeave")
        frame:SetScript("OnLeave", function()
                FuBar:Panel_OnLeave(this.plugin)
                if OnLeave then
                        OnLeave()
                end
        end)
end

function FuBar:Update()
        for panelId = 1, self:GetNumPanels() do
                self:GetPanel(panelId):Update()
        end
end

function FuBar:SetupPlugins()
        doneSetupPlugins = true
        if pluginsToSetup then
                for i = 1, self:GetNumPanels() do
                        local panel = self:GetPanel(i)
                        for h = 1, 3 do
                                local side
                                if h == 1 then
                                        side = "LEFT"
                                elseif h == 2 then
                                        side = "CENTER"
                                else
                                        side = "RIGHT"
                                end
                                local order = panel:GetSavedOrder(side)
                                for _,name in ipairs(order) do
                                        for plugin in pairs(pluginsToSetup) do
                                                if not plugin:IsDisabled() and plugin:GetTitle() == name then
                                                        if not panel:AddPlugin(plugin, nil, side) then
                                                                doneSetupPlugins = false
                                                                return
                                                        end
                                                        pluginsToSetup[plugin] = nil
                                                        break
                                                end
                                        end
                                end
                        end
                end
                
                local order = self.db.profile.detached
                if order then
                        for name in pairs(order) do
                                for plugin in pairs(pluginsToSetup) do
                                        if not plugin:IsDisabled() and plugin:GetTitle() == name then
                                                pluginsToSetup[plugin] = nil
                                                plugin:Show(0)
                                                break
                                        end
                                end
                        end
                else
                        self.db.profile.detached = {}
                end
                
                for plugin in pairs(pluginsToSetup) do
                        if type(plugin.GetDefaultPosition) == "function" and plugin:GetDefaultPosition() == "MINIMAP" then
                                plugin:Show(0)
                        else
                                self.lastPanelId = (self.lastPanelId or 0) + 1
                                if self.lastPanelId > self:GetNumPanels() then
                                        self.lastPanelId = 1
                                end
                                self:GetPanel(self.lastPanelId):AddPlugin(plugin, nil, type(plugin.GetDefaultPosition) == "function" and plugin:GetDefaultPosition() or "LEFT", true)
                        end
                end
                
                pluginsToSetup = nil
        end
        return true
end

function FuBar:ShowPlugin(plugin, panelId)
        if doneSetupPlugins then
                if not panelId then
                        doneSetupPlugins = false
                        pluginsToSetup = Compost:Acquire()
                        pluginsToSetup[plugin] = true
                        local function func()
                                if not self:SetupPlugins() then
                                        self:ScheduleEvent(func, 0)
                                        return
                                end
                                self:Update()
                        end
                        func()
                else
                        self:GetPanel(panelId):AddPlugin(plugin, nil, type(plugin.GetDefaultPosition) == "function" and plugin:GetDefaultPosition() or "LEFT", true)
                end
        else
                if not pluginsToSetup then
                        pluginsToSetup = {}
                end
                pluginsToSetup[plugin] = true
        end
end

local previousPosition_panel, previousPosition_index, previousPosition_side, previousPosition_x, previousPosition_y

function FuBar:Plugin_StartDrag(plugin)
        local panel = plugin:GetPanel()
        assert(panel, plugin:GetTitle() .. ": You must be attached to a panel.")
        self:Panel_OnLeave()
        local index, side = panel:IndexOfPlugin(plugin)
        
        if index then
                if not panel:IsLocked() then
                        if index < panel:GetNumPlugins(side) then
                                local frame = panel:GetPlugin(index + 1, side).frame
                                local x, y = frame:GetLeft(), frame:GetBottom()
                                frame:ClearAllPoints()
                                frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x, y)
                        end
                        
                        previousPosition_panel = panel
                        previousPosition_index = index
                        previousPosition_side = side
                        previousPosition_x, previousPosition_y = self:GetScaledCursorPosition()
                        
                        FuBar_Panel:PreventUpdate()
                        panel:RemovePlugin(index, side)
                        plugin:GetFrame():StartMoving()
                end
        end
end

function FuBar:Plugin_StopDrag(plugin)
        plugin:GetFrame():StopMovingOrSizing()
        if previousPosition_panel and not previousPosition_panel:IsLocked() then
                local insertAt
                local panel = previousPosition_panel
                local side = previousPosition_side
                local x, y = self:GetScaledCursorPosition()
                local px, py = previousPosition_x, previousPosition_y

                for panelId = 1, self:GetNumPanels() do
                        local p = self:GetPanel(panelId)
                        local frame = p.frame
                        local x1, x2, y1, y2 = frame:GetLeft(), frame:GetRight(), frame:GetBottom(), frame:GetTop()
                        if x1 <= x and x <= x2 and y1 <= y and y <= y2 then
                                panel = p
                                break
                        end
                end
                
                if panel:GetNumPlugins(side) == 0 then
                        insertAt = 1
                else
                        for i = 1, panel:GetNumPlugins(side) + 1 do
                                local left, right
                                local leftPlugin
                                local rightPlugin
                                
                                leftPlugin = panel:GetPlugin(i - 1, side)
                                rightPlugin = panel:GetPlugin(i, side)
                                
                                if side == "RIGHT" then
                                        leftPlugin, rightPlugin = rightPlugin, leftPlugin
                                end
                                left = leftPlugin and leftPlugin:GetFrame():GetCenter() or 0
                                right = rightPlugin and rightPlugin:GetFrame():GetCenter() or GetScreenWidth()
                                if left <= x and x <= right then
                                        insertAt = i
                                        break
                                end
                        end
                end
                if not insertAt then
                        insertAt = panel:GetNumPlugins(side) + 1
                end
                
                panel:AddPlugin(plugin, insertAt, side)
                FuBar_Panel:StopPreventUpdate()
                previousPosition_panel:Update()
                panel:Update()
                previousPosition_panel = nil
                previousPosition_index = nil
                previousPosition_side = nil
                previousPosition_x = nil
                previousPosition_y = nil
                if math.abs(px - x) <= 5 and math.abs(py - y) <= 5 then
                        return false
                else
                        return true
                end
        end
end

function FuBar:Panel_OnEnter(plugin)
        local point = plugin
        if type(plugin) == "table" then
                point = plugin:GetPanel():GetAttachPoint()
        end
        if point == "TOP" then
                inTopPanel = true
                self:CancelScheduledEvent("FuBar_AutoHideTop")
        elseif point == "BOTTOM" then
                inBottomPanel = true
                self:CancelScheduledEvent("FuBar_AutoHideBottom")
        end
        for i = 1, self:GetNumPanels() do
                local panel = self:GetPanel(i)
                if panel:GetAttachPoint() == point then
                        panel.frame:SetAlpha(1)
                        panel.frame:SetFrameStrata("HIGH")
                end
        end
end

function FuBar:Panel_OnLeave(plugin)
        local point = plugin
        if type(plugin) == "table" then
                if not plugin:GetPanel() then
                        return
                end
                point = plugin:GetPanel():GetAttachPoint()
        end
        if point == "TOP" then
                inTopPanel = false
                self:ScheduleEvent("FuBar_AutoHideTop", self.OnUpdate_AutoHideTop, 1, self)
        elseif point == "BOTTOM" then
                inBottomPanel = false
                self:ScheduleEvent("FuBar_AutoHideBottom", self.OnUpdate_AutoHideBottom, 1, self)
        end
end

function FuBar:OnProfileEnable(oldName, _, copyFrom)
        isChangingProfile = true
        if not next(FuBar.db.profile) then
                local def
                local profile = self:GetProfile()
                if string.find(profile, "^char/") or string.find(profile, "^class/") then
                        local _,class = UnitClass("player")
                        def = _G["FUBAR_DEFAULTS_" .. class] or FUBAR_DEFAULTS
                else
                        def = FUBAR_DEFAULTS
                end
                if type(def) == "function" then
                        def = def()
                end
                for k,v in pairs(def) do
                        FuBar.db.profile[k] = v
                end
        end
        
        if not self.db.profile.loadOnDemand then
                self.db.profile.loadOnDemand = {}
        end
        
        if not self.db.profile.tooltip then
                self.db.profile.tooltip = {}
        end
        
        if not self.db.profile.panels then
                self.db.profile.panels = {}
        end
        
        CleanDB()
        
        doneSetupPlugins = false
        pluginsToSetup = {}
        for i,plugin in ipairs(plugins) do
                pluginsToSetup[plugin] = true
                local frame = plugin:GetFrame()
                frame:Hide()
                frame:ClearAllPoints()
                plugin:SetPanel(nil)
        end
        
        for i = self:GetNumPanels(), 1, -1 do
                self:GetPanel(i):del(true)
        end
        
        for i = table.getn(self.db.profile.panels), 1, -1 do
                if self.db.profile.panels[i] then
                        FuBar_Panel:new()
                end
        end
        
        if self:GetNumPanels() == 0 then
                FuBar_Panel:new()
        end
        
        self:CheckResolution()
        
        for i = 1, GetNumAddOns() do
                local name, title, notes, enabled, loadable, reason, security = GetAddOnInfo(i)
                if IsAddOnLoadOnDemand(i) and enabled and loadable and not IsAddOnLoaded(i) then
                        local good = false
                        for _,dep in ipairs({GetAddOnDependencies(name)}) do
                                if dep == "FuBar" then
                                        good = true
                                end
                        end
                        if good then
                                if not self.db.profile.loadOnDemand[name] then
                                        self:LoadPlugin(name)
                                elseif not self.db.profile.loadOnDemand[name].disabled and (not self.db.profile.loadOnDemand[name].condition or CheckLoadCondition(self.db.profile.loadOnDemand[name].condition)) then
                                        self:LoadPlugin(name)
                                else
                                        local exists = false
                                        for _,n in ipairs(toBeLoaded) do
                                                if n == name then
                                                        exists = true
                                                        break
                                                end
                                        end
                                        if not exists then
                                                table.insert(toBeLoaded, name)
                                        end
                                end
                        end
                end
        end
        
        for i,plugin in ipairs(plugins) do
                if plugin.IsDisabled and plugin:IsDisabled() and plugin:GetPanel() then
                        plugin:GetPanel():RemovePlugin(plugin)
                end
        end
        
        local name = self:GetProfile()
        for _,plugin in ipairs(plugins) do
                if type(plugin.SetProfile) == "function" and not plugin.independentProfile then
                        plugin:SetProfile(name, copyFrom)
                end
        end
        
        self:SetFontSize(self:GetFontSize())
        
        local function func()
                if not self:SetupPlugins() then
                        self:ScheduleEvent(func, 0)
                        return
                end
                self:Update()
        end
        self:ScheduleEvent(func, 0.01)
        
        isChangingProfile = false
end