vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
-------------------------------------------------------------------------------
-- Locals                                                                    --
-------------------------------------------------------------------------------

local L = AceLibrary("AceLocale-2.2"):new("ClosetGnome")
local gratuity = AceLibrary("Gratuity-2.0")
local dewdrop = AceLibrary("Dewdrop-2.0")
local tablet = AceLibrary("Tablet-2.0")
local compost = AceLibrary("Compost-2.0")

local deequipQueue = nil
local queuedSet = nil
local lastEquippedSet = nil
local slotLocks = nil
local queuedForDelete = nil

local lua51 = nil
local keybindingFrame = "ClosetGnomeKeybindingFrame"

local _G = getfenv(0)

local slots = {
        {slot = "Head"},
        {slot = "Shoulder"},
        {slot = "Chest"},
        {slot = "Waist"},
        {slot = "Legs"},
        {slot = "Feet"},
        {slot = "Wrist"},
        {slot = "Hands"},
        {slot = "MainHand"},
        {slot = "SecondaryHand"},
        {slot = "Ranged"},
        {slot = "Ammo"},
        {slot = "Neck"},
        {slot = "Back"},
        {slot = "Finger0"},
        {slot = "Finger1"},
        {slot = "Trinket0"},
        {slot = "Trinket1"},
        {slot = "Relic"},
}

-------------------------------------------------------------------------------
-- Localization                                                              --
-------------------------------------------------------------------------------

L:RegisterTranslations("enUS", function() return {
        ["ClosetGnome"] = true,
        ["ClosetGnome options."] = true,
        ["Enable or disable the slots you want in this set by clicking the items in your character frame. Green slots are enabled, red are disabled. Type in the set name below, and click Add."] = true,
        ["Add"] = true,
        ["Creates a new set, or updates an existing one."] = true,
        ["Wear"] = true,
        ["Change equipment set."] = true,
        ["Cancel"] = true,
        ["Always equip weapons"] = true,
        ["Equip weapons in sets even if you are in combat."] = true,
        ["Equip %s."] = true,
        ["Quips"] = true,
        ["Toggle outputting random quips when equipping sets."] = true,
        ["Delete"] = true,
        ["Deletes the equipment set %s."] = true,
        ["Delete a set."] = true,
        ["Are you sure you want to delete the set %s?"] = true,
        ["<set name>"] = true,
        ["Couldn't find %s in your inventory."] = true,
        ["Enter the set name to delete it."] = true,
        ["Keybinding"] = true,
        ["Assign a keybinding to a set."] = true,
        ["Assign a keybinding to %s (or empty to clear)."] = true,
        ["<key binding>"] = true,
        ["%s is not a valid keybinding."] = true,
        ["Registering keybinding %s to set %s."] = true,
        ["%s is already registered to %s."] = true,
        ["Removing keybinding %s from set %s."] = true,

        ["In combat: %s queued for swap."] = true,
        ["Added set: %s."] = true,
        ["Deleted set: %s."] = true,
        ["Updating set: %s."] = true,
        ["Please use a proper name for your set."] = true,

        ["Available sets"] = true,
        ["Last equipped:"] = true,
        ["Click a set above to change your equipment. Ctrl-click a set to try it on. The last equipped set is indicated by a star."] = true,
        ["You have not created any equipment sets yet, please right-click the icon and click 'Add' to create a set."] = true,

        -- This is what a normal bag is called, as returned from GetItemInfo's
        -- subType
        ["Bag"] = true,
        ["Container"] = true,

        -- Random quips when you equip a set.
        [1] = "Your ClosetGnome rapidly dresses you up in %s before he runs off with another sock.",
        [2] = "Once in a blue moon your ClosetGnome will equip you with an Artifact item. Not today, however, you'll have to settle for %s.",
        [3] = "ClosetGnomes are tidy creatures. Not yours, though, as he equips %s he just piles your old outfit in the corner.",
        [4] = "It's almost as if he dances around you while he dresses you up in %s. Although it's a very ugly dance.",
        [5] = "Since you forgot to feed your ClosetGnome today, he expresses his anger by equipping your %s backwards.",
        [6] = "Never let your ClosetGnome tend to other needs than %s.",
        [7] = "%s looks good on you today, at least compared to how it looked on your ClosetGnome when you caught him wearing it.",
        [8] = "ClosetGnome equips %s.",
        [9] = "Your ClosetGnome flinches in disgust as he equips %s. You're not sure why, but what's that smell ...",
        [10] = "Is that a whirlwind?! No, it's ClosetGnome dressing you in %s!",
        [11] = "Recently returned from the ClosetGnome convention, he's got a lot to talk about while dressing you in %s.",
        [12] = "Noone knows if ClosetGnomes party, but you do. This is how it looked last time you dressed yourself in %s after a party.",
        [13] = "'Getting Jiggy With It' was never your favorite, but you have to admit that it fits nicely with %s.",
        [14] = "Even though your washer is clearly labeled 'Certified by ClosetGnomes, Inc.', it's clear that %s has not been cleaned in a while.",
        [15] = "%s has the same color socks as your last set, and the one before that, and before that.",
        [16] = "Instead of equipping %s, your ClosetGnome starts gnawing on your leg. You shake him off and do it yourself.",
        [17] = "Noone has ever suspected you of having a ClosetGnome. Most of them are actually good at equipping things like %s.",
        [18] = "What kind of person are you that dresses up in %s so often? Your ClosetGnome eyes you up and down. He looks .. hungry.",
        [19] = "Your ClosetGnome is hungry and scavenges the pockets of %s for food, but finds nothing. Do you ever feed him?",
        [20] = "The first rule of the ClosetGnomes is that they do not talk about %s.",
        [21] = "%s seems different somehow.",
        [22] = "There is no %s.",
        [23] = "You feel the need - the need for %s!",
        [24] = "Your ClosetGnome equips %s. Shaken, not stirred.",
        [25] = "ClosetGnome puts %s in a corner. Then comes back and equips it.",
        [26] = "Hasta la vista, %s.",
        [27] = "Keep your %s close, but your ClosetGnome closer.",
        [28] = "A boy's best friend is a ClosetGnome that comes with %s.",
        [29] = "You love the smell of %s in the morning.",
        [30] = "May your ClosetGnome be with you. And may he bring %s.",
        [31] = "You make your ClosetGnome an offer he can't refuse, and so he equips %s.",
} end)

L:RegisterTranslations("koKR", function() return {
--      ["ClosetGnome"] = true,
        ["ClosetGnome options."] = "ClosetGnome 설정.",
        ["Enable or disable the slots you want in this set by clicking the items in your character frame. Green slots are enabled, red are disabled. Type in the set name below, and click Add."] = "이 세트에 케릭터 창의 아이템을 클릭해서 원하는 슬롯을 활성화 하거나 비활성화 합니다. 활성화 된 슬롯은 녹색, 비활성화 된 것은 붉은색입니다. 세트명을 입력하고 추가를 클릭하세요.",
        ["Add"] = "추가",
        ["Creates a new set."] = "새로운 세트를 생성합니다.",
        ["Wear"] = "착용",
        ["Change equipment set."] = "착용장비 세트를 변경합니다.",
        ["Cancel"] = "취소",
        ["Always equip weapons"] = "항상 무기 착용",
--      ["Quips"] = true,
--      ["Toggle outputting random quips when equipping sets."] = true,
        ["Equip weapons in sets even if you are in combat."] = "전투중에도 세트에 속하는 무기를 착용합니다.",
        ["Equip %s."] = "%s|1을;를; 착용합니다.",
        ["Delete"] = "삭제",
        ["Enter the set name to delete it."] = "삭제를 세트명을 입력하세요.",
        ["<set name>"] = "<세트명>",
        ["Couldn't find %s in your inventory."] = "인벤토리에서 %s|1을;를; 찾을 수 없습니다.",

        ["In combat: %s queued for swap."] = "전투중: %s|1이;가; 스왑을 위해 대기합니다.",
        ["Added set: %s."] = "세트 추가: %s.",
        ["Deleted set: %s."] = "세트 삭제: %s.",
        ["Updating set: %s."] = "세트 갱신: %s.",

        ["Available sets"] = "이용가능한 세트",
        --["Click a set above to change your equipment. Ctrl-click a set to try it on. The last equipped set is indicated by a star."] = true,
        ["You have not created any equipment sets yet, please right-click the icon and click 'Add' to create a set."] = "아직 생성된 장비 세트가 없습니다, 아이콘을 우클릭 한 후 '추가'를 클릭해서 세트를 생성하세요.",

        -- This is what a normal bag is called, as returned from GetItemInfo's
        -- subType
        ["Bag"] = "가방",

        -- Random quips when you equip a set.
        [1] = "그가 다른 신발을 신고 도망치기 전에 ClosetGnome 은 %s 세트를 신속히 착용합니다.",
        [8] = "ClosetGnome이 %s 세트를 착용합니다.",
        [18] = "%s 세트를 자주 입는 당신은 뭐하는 사람이에요? 당신의 ClosetGnome이 당신을 위아래로 훌터봅니다. 그는 굶주린것 같습니다...",
        [19] = "당신의 ClosetGnome이 굶주려 %s 세트를 뒤지지만 아무것도 찾지 못했습니다. 그에게 먹을것 좀 주시겠습니까?",
} end)

L:RegisterTranslations("deDE", function() return {
        ["ClosetGnome options."] = "ClosetGnome Optionen",
        ["Enable or disable the slots you want in this set by clicking the items in your character frame. Green slots are enabled, red are disabled. Type in the set name below, and click Add."] = "Aktiviere und deaktiviere die einzelnen Slots f\195\188r dieses Set durch klicken im Charakterbildschirm. Gr\195\188n bedeutet aktiviert, rot deaktiviert. Gebe einen Namen f\195\188r das set ein und dr\195\188cke Hinzuf\195\188gen",
        ["Add"] = "Hinzuf\195\188gen",
        ["Creates a new set, or updates an existing one."] = "Legt ein neues Set an, oder aktualisiert ein bestehendes.",
        ["Wear"] = "Anziehen",
        ["Change equipment set."] = "Wechsle Set",
        ["Cancel"] = "Abbrechen",
        ["Always equip weapons"] = "Waffen immer anlegen",
--      ["Quips"] = true,
--      ["Toggle outputting random quips when equipping sets."] = true,
        ["Equip weapons in sets even if you are in combat."] = "Waffen schon im Kampf wechseln",
        ["Equip %s."] = "Trage %s",
        ["Delete"] = "L\195\182schen",
        ["Enter the set name to delete it."] = "Gib den Namen des zu l\195\182schemdem Sets ein",
        ["<set name>"] = "<Name des Sets>",
        ["Couldn't find %s in your inventory."] = "Konnte %s nicht im Inventar finden",

        ["In combat: %s queued for swap."] = "Kampf: %s in der Warteschlange",
        ["Added set: %s."] = "Set hinzugef\195\188gt: %s",
        ["Deleted set: %s."] = "Set gel\195\182scht: %s",
        ["Updating set: %s."] = "Set upgedatet: %s",

        ["Last equipped:"] = "Zuletzt angelegt:",
        ["Available sets"] = "Verf\195\188gbare Sets:",
        --["Click a set above to change your equipment. Ctrl-click a set to try it on. The last equipped set is indicated by a star."] = true,
        ["You have not created any equipment sets yet, please right-click the icon and click 'Add' to create a set."] = "Es wurden noch keine Ausr\195\188stungssets erstellt, Klicke mit der rechten Maustaste auf das Icon, dann 'Hinzuf\195\188gen' um ein Set zu erstellen",

        -- This is what a normal bag is called, as returned from GetItemInfo's
        -- subType
        ["Bag"] = "Beh\195\164lter",
} end)

L:RegisterTranslations("zhCN", function() return {
        ["ClosetGnome"] = "ClosetGnome",
        ["ClosetGnome options."] = "ClosetGnome 设置.",
        ["Enable or disable the slots you want in this set by clicking the items in your character frame. Green slots are enabled, red are disabled. Type in the set name below, and click Add."] = "在人物装备框架上点击物品, 可决定在套装设置中是否使用此槽. 绿色框为使用, 红色框为忽略. 如果你想在换装时使一个槽为空.那么在创建设置时就要先移出此槽的物品,并确定此槽框体为绿色. 按住CTRL并点击装备, 则为使用该物品的图标作为套装设置的图标. 选好后在下面栏内输入设置名, 并点击添加. ",
        ["Add"] = "添加",
        ["Creates a new set, or updates an existing one."] = "创建新设置, 或更新一个现有的设置",
        ["Wear"] = "穿上",
        ["Change equipment set."] = "换装",
        ["Cancel"] = "取消",
        ["Always equip weapons"] = "一直装备武器",
        ["Equip weapons in sets even if you are in combat."] = "进入战斗状态时, 仍使用套装设置中的武器",
        ["Equip %s."] = "装备 %s.",
        ["Quips"] = "讽剌",
        ["Toggle outputting random quips when equipping sets."] = "当使用装备设定时, 随机说出讽剌的话. ",
        ["Delete"] = "删除",
        ["Deletes the equipment set %s."] = "删除装备设置 %s",
        ["Delete a set."] = "删除一个设置",
        ["Are you sure you want to delete the set %s?"] = "你确定想删除设置 %s 吗?",
        ["<set name>"] = "<设置名称>",
        ["Couldn't find %s in your inventory."] = "在你的物品中无法找到 %s.",
        ["Enter the set name to delete it."] = "输入设置名称以删除它",
        ["Keybinding"] = "按键捆绑",
        ["Assign a keybinding to a set."] = "给设定分配一个按键绑定",
        ["Assign a keybinding to %s (or empty to clear)."] = "分配一个按键绑定给 %s (或清空).",
        ["<key binding>"] = "<按钮绑定>",
        ["%s is not a valid keybinding."] = "%s 是一个无效的按键绑定",
        ["Registering keybinding %s to set %s."] = "寄存按键绑定 %s 给设置 %s",
        ["%s is already registered to %s."] = "%s 已经注册给 %s ",
        ["Removing keybinding %s from set %s."] = "从设置 %s 中移除按键绑定 %s",

        ["In combat: %s queued for swap."] = "进入战斗: %s 交换队列.",
        ["Added set: %s."] = "添加设置: %s.",
        ["Deleted set: %s."] = "删除设置: %s.",
        ["Updating set: %s."] = "更新设置: %s.",
        ["Please use a proper name for your set."] = "请为你的设置使用正确的名字",

        ["Available sets"] = "可用套装",
        ["Last equipped:"] = "最后装备的套装",
        ["Click a set above to change your equipment. Ctrl-click a set to try it on. The last equipped set is indicated by a star."] = "在一个设置上点击以更换你的装备. 按住Ctrl并点击一个设置尝试打开它. 最后一次使用的套装用星号表示",
        ["You have not created any equipment sets yet, please right-click the icon and click 'Add' to create a set."] = "你仍未创建任何装备设置, 请用右键点击图标, 并点击 '添加' 创建设置.",

        -- This is what a normal bag is called, as returned from GetItemInfo's
        -- subType
        ["Bag"] = "背包",

        -- 当你配备一个设置时, 随机说出讽剌的话.
        [1] = "Your ClosetGnome rapidly dresses you up in %s before he runs off with another sock.",
        [2] = "Once in a blue moon your ClosetGnome will equip you with an Artifact item. Not today, however, you'll have to settle for %s.",
        [3] = "ClosetGnomes are tidy creatures. Not yours, though, as he equips %s he just piles your old outfit in the corner.",
        [4] = "It's almost as if he dances around you while he dresses you up in %s. Although it's a very ugly dance.",
        [5] = "Since you forgot to feed your ClosetGnome today, he expresses his anger by equipping your %s backwards.",
        [6] = "Never let your ClosetGnome tend to other needs than %s.",
        [7] = "%s looks good on you today, at least compared to how it looked on your ClosetGnome when you caught him wearing it.",
        [8] = "ClosetGnome equips %s.",
        [9] = "Your ClosetGnome flinches in disgust as he equips %s. You're not sure why, but what's that smell ...",
        [10] = "Is that a whirlwind?! No, it's ClosetGnome dressing you in %s!",
        [11] = "Recently returned from the ClosetGnome convention, he's got a lot to talk about while dressing you in %s.",
        [12] = "Noone knows if ClosetGnomes party, but you do. This is how it looked last time you dressed yourself in %s after a party.",
        [13] = "'Getting Jiggy With It' was never your favorite, but you have to admit that it fits nicely with %s.",
        [14] = "Even though your washer is clearly labeled 'Certified by ClosetGnomes, Inc.', it's clear that %s has not been cleaned in a while.",
        [15] = "%s has the same color socks as your last set, and the one before that, and before that.",
        [16] = "Instead of equipping %s, your ClosetGnome starts gnawing on your leg. You shake him off and do it yourself.",
        [17] = "Noone has ever suspected you of having a ClosetGnome. Most of them are actually good at equipping things like %s.",
        [18] = "What kind of person are you that dresses up in %s so often? Your ClosetGnome eyes you up and down. He looks .. hungry.",
        [19] = "Your ClosetGnome is hungry and scavenges the pockets of %s for food, but finds nothing. Do you ever feed him?",
        [20] = "The first rule of the ClosetGnomes is that they do not talk about %s.",
        [21] = "%s seems different somehow.",
        [22] = "There is no %s.",
        [23] = "You feel the need - the need for %s!",
        [24] = "Your ClosetGnome equips %s. Shaken, not stirred.",
        [25] = "ClosetGnome puts %s in a corner. Then comes back and equips it.",
        [26] = "Hasta la vista, %s.",
        [27] = "Keep your %s close, but your ClosetGnome closer.",
        [28] = "A boy's best friend is a ClosetGnome that comes with %s.",
        [29] = "You love the smell of %s in the morning.",
        [30] = "May your ClosetGnome be with you. And may he bring %s.",
        [31] = "You make your ClosetGnome an offer he can't refuse, and so he equips %s.",
} end)

-------------------------------------------------------------------------------
-- Addon declaration                                                         --
-------------------------------------------------------------------------------

ClosetGnome = AceLibrary("AceAddon-2.0"):new("AceHook-2.1", "AceConsole-2.0", "AceDB-2.0", "AceEvent-2.0", "FuBarPlugin-2.0")
ClosetGnome:RegisterDB("ClosetGnomeDB", "ClosetGnomePerCharDB")
ClosetGnome:RegisterDefaults("profile", {
        quips = true,
})
ClosetGnome:RegisterDefaults("char", {
        set = {},
        icons = {},
        keybindings = {},
        alwaysWeapons = true,
})
ClosetGnome.OnMenuRequest = {
        type = "group",
        name = L["ClosetGnome"],
        desc = L["ClosetGnome options."],
        args = {
                add = {
                        type = "execute",
                        name = L["Add"],
                        desc = L["Creates a new set, or updates an existing one."],
                        func = function() ClosetGnome:CreateSet() end,
                        order = 101,
                },
                wear = {
                        type = "group",
                        name = L["Wear"],
                        desc = L["Change equipment set."],
                        args = {},
                        order = 102,
                        disabled = function() return next(ClosetGnome.OnMenuRequest.args.wear.args) == nil end
                },
                key = {
                        type = "group",
                        name = L["Keybinding"],
                        desc = L["Assign a keybinding to a set."],
                        args = {},
                        order = 103,
                        hidden = function() return not lua51 end,
                        disabled = function() return next(ClosetGnome.OnMenuRequest.args.wear.args) == nil end
                },
                spacer = { type = "header", order = 104 },
                alwaysweapons = {
                        type = "toggle",
                        name = L["Always equip weapons"],
                        desc = L["Equip weapons in sets even if you are in combat."],
                        get = function() return ClosetGnome.db.char.alwaysWeapons end,
                        set = function(v) ClosetGnome.db.char.alwaysWeapons = v end,
                        order = 105,
                },
                quips = {
                        type = "toggle",
                        name = L["Quips"],
                        desc = L["Toggle outputting random quips when equipping sets."],
                        get = function() return ClosetGnome.db.profile.quips end,
                        set = function(v) ClosetGnome.db.profile.quips = v end,
                        order = 106,
                },
                delete = {
                        type = "group",
                        name = L["Delete"],
                        desc = L["Delete a set."],
                        args = {},
                        order = 107,
                        disabled = function() return next(ClosetGnome.OnMenuRequest.args.wear.args) == nil end
                },
        },
}
ClosetGnome:RegisterChatCommand({"/closetgnome", "/cg"}, ClosetGnome.OnMenuRequest, "CLOSETGNOME")

-- FuBar stuff.
ClosetGnome.hideWithoutStandby = true
local defaultIcon = "Interface\\Icons\\INV_Chest_Cloth_17"
ClosetGnome.hasIcon = defaultIcon
ClosetGnome.hasNoColor = true
ClosetGnome.defaultMinimapPosition = 240
ClosetGnome.clickableTooltip  = true

-------------------------------------------------------------------------------
-- Initialization                                                            --
-------------------------------------------------------------------------------

function ClosetGnome:OnInitialize()
        if not StaticPopupDialogs then
                StaticPopupDialogs = {}
        end
        StaticPopupDialogs["ClosetGnomeAdd"] = {
                text = L["Enable or disable the slots you want in this set by clicking the items in your character frame. Green slots are enabled, red are disabled. Type in the set name below, and click Add."],
                button1 = L["Add"],
                button2 = L["Cancel"],
                OnCancel = function() self:CancelCreateSet() end,
                sound = "levelup2",
                whileDead = 1,
                hideOnEscape = 1,
                timeout = 0,
                OnShow = function()
                        -- We have to do this onshow to reset the previous text
                        getglobal(this:GetName().."EditBox"):SetText("")
                end,
                OnAccept = function()
                        local name = getglobal(this:GetParent():GetName().."EditBox"):GetText()
                        self:AddSetFromDoll(name)
                end,
                hasEditBox = 1,
        }
        StaticPopupDialogs["ClosetGnomeDelete"] = {
                text = L["Are you sure you want to delete the set %s?"],
                button1 = L["Delete"],
                button2 = L["Cancel"],
                showAlert = 1,
                timeout = 0,
                OnAccept = function() self:DeleteSet(queuedForDelete, true) end,
                OnCancel = function() queuedForDelete = nil end,
        }
end

function ClosetGnome:OnEnable()
        -- Temporary table for which slots to use when adding a new set.
        self.activeSlots = compost:Acquire()

        -- If we're in TBC, create the button frames for keybindings
        if lua51 == nil then lua51 = loadstring("return function(...) return ... end") and true or false end
        if lua51 and _G["UIParent"] ~= nil and SetBindingClick then
                for k, v in pairs(self.db.char.keybindings) do
                        if v then
                                self:RegisterBinding(v, k)
                        end
                end
        end

        self:RegisterEvent("AceEvent_FullyInitialized")

        self:UpdateSetMenu()
end

function ClosetGnome:OnDisable()
        compost:Reclaim(self.activeSlots)
        self.activeSlots = nil
        if slotLocks then
                compost:Reclaim(slotLocks, 1)
                slotLocks = nil
        end

        if lua51 then
                for k, v in pairs(self.db.char.keybindings) do
                        self:UnsetBinding(k)
                end
        end
end


-------------------------------------------------------------------------------
-- Keybindings                                                               --
-------------------------------------------------------------------------------

local validKeys = nil
function ClosetGnome:ValidateBinding(combo)
        local a, b, modifier, key = string.find(combo, "(%u+)\-([%u%d]+)")
        if not a or not b or not modifier or not key then return false end
        if modifier ~= "CTRL" and modifier ~= "ALT" and modifier ~= "SHIFT" then return false end
        return string.len(key) == 1 or (string.len(key) == 2 and string.find(key, "F%d")) or key == "DELETE" or key == "INSERT"
end

function ClosetGnome:RegisterBinding(combo, set)
        if not lua51 or not combo or not set then return end

        if not self:ValidateBinding(combo) then
                self:Print(string.format(L["%s is not a valid keybinding."], "|cffd9d919"..combo.."|r"))
                return
        end

        local shouldPrint = true
        for k, v in pairs(self.db.char.keybindings) do
                if v == combo then
                        shouldPrint = nil
                elseif k == set then
                        self:UnsetBinding(set)
                end
        end

        if shouldPrint then
                self:Print(string.format(L["Registering keybinding %s to set %s."], "|cffd9d919"..combo.."|r", "|cffd9d919"..set.."|r"))
        end

        local comboCopy = combo
        local setCopy = set
        local frame = _G[keybindingFrame..comboCopy] or CreateFrame("Button", keybindingFrame..comboCopy, _G["UIParent"])
        frame:SetScript("OnMouseUp", function() ClosetGnome:KeybindingClicked(comboCopy, setCopy) end)
        self.db.char.keybindings[setCopy] = comboCopy
        SetBindingClick(comboCopy, keybindingFrame..comboCopy)
end

function ClosetGnome:UnsetBinding(set)
        if not lua51 or not set or not self.db.char.keybindings[set] then return end
        local combo = self.db.char.keybindings[set]

        local frame = _G[keybindingFrame..combo]
        if frame then
                frame:SetScript("OnMouseUp", nil)
                frame = nil
        end

        SetBinding(combo) -- Unset the binding.
end

function ClosetGnome:ClearBinding(set)
        if not lua51 or not set or not self.db.char.keybindings[set] then return end

        self:UnsetBinding(set)

        local combo = self.db.char.keybindings[set]
        self:Print(string.format(L["Removing keybinding %s from set %s."], "|cffd9d919"..combo.."|r", "|cffd9d919"..set.."|r"))
        self.db.char.keybindings[set] = nil
end

function ClosetGnome:KeybindingClicked(combo, set)
        if not lua51 or not set or not self:ValidateBinding(combo) then return end
        self:WearSet(set)
end

-------------------------------------------------------------------------------
-- Menu                                                                      --
-------------------------------------------------------------------------------

function ClosetGnome:UpdateSetMenu()
        compost:Reclaim(self.OnMenuRequest.args.wear.args, 1)
        self.OnMenuRequest.args.wear.args = compost:Acquire()
        for k, v in pairs(self.db.char.set) do
                if not self:HideSetFromUI(k) then
                        local set = k
                        local menuKey = string.gsub(k, " ", "")
                        self.OnMenuRequest.args.wear.args[menuKey] = compost:Acquire()
                        self.OnMenuRequest.args.wear.args[menuKey].type = "execute"
                        self.OnMenuRequest.args.wear.args[menuKey].name = set
                        self.OnMenuRequest.args.wear.args[menuKey].desc = string.format(L["Equip %s."], set)
                        self.OnMenuRequest.args.wear.args[menuKey].func = function() ClosetGnome:WearSet(set) end
                        if self.db.char.icons[set] then
                                self.OnMenuRequest.args.wear.args[menuKey].icon = self.db.char.icons[set]
                        end
                end
        end

        compost:Reclaim(self.OnMenuRequest.args.delete.args, 1)
        self.OnMenuRequest.args.delete.args = compost:Acquire()
        for k, v in pairs(self.db.char.set) do
                local set = k
                local menuKey = string.gsub(k, " ", "")
                self.OnMenuRequest.args.delete.args[menuKey] = compost:Acquire()
                self.OnMenuRequest.args.delete.args[menuKey].type = "execute"
                self.OnMenuRequest.args.delete.args[menuKey].name = set
                self.OnMenuRequest.args.delete.args[menuKey].desc = string.format(L["Deletes the equipment set %s."], set)
                self.OnMenuRequest.args.delete.args[menuKey].func = function() ClosetGnome:DeleteSet(set, false) end
                if self.db.char.icons[set] then
                        self.OnMenuRequest.args.delete.args[menuKey].icon = self.db.char.icons[set]
                end
        end

        if lua51 then
                compost:Reclaim(self.OnMenuRequest.args.key.args, 1)
                self.OnMenuRequest.args.key.args = compost:Acquire()
                for k, v in pairs(self.db.char.set) do
                        local set = k
                        local menuKey = string.gsub(k, " ", "")
                        self.OnMenuRequest.args.key.args[menuKey] = compost:Acquire()
                        self.OnMenuRequest.args.key.args[menuKey].type = "text"
                        self.OnMenuRequest.args.key.args[menuKey].name = set
                        self.OnMenuRequest.args.key.args[menuKey].desc = string.format(L["Assign a keybinding to %s (or empty to clear)."], set)
                        self.OnMenuRequest.args.key.args[menuKey].usage = L["<key binding>"]
                        self.OnMenuRequest.args.key.args[menuKey].validate = function(input)
                                return ClosetGnome:ValidateBinding(input) or input == ""
                        end
                        self.OnMenuRequest.args.key.args[menuKey].get = function()
                                return ClosetGnome.db.char.keybindings[set]
                        end
                        self.OnMenuRequest.args.key.args[menuKey].set = function(v)
                                if v == "" then
                                        ClosetGnome:ClearBinding(set)
                                else
                                        ClosetGnome:RegisterBinding(v, set)
                                end
                        end
                        if self.db.char.icons[set] then
                                self.OnMenuRequest.args.key.args[menuKey].icon = self.db.char.icons[set]
                        end
                end
        end
end

-------------------------------------------------------------------------------
-- Addon methods                                                             --
-------------------------------------------------------------------------------

-- Opens the character frame and lets the user uncheck the items he doesn't
-- want.
function ClosetGnome:CreateSet()
        self.activeSlots = compost:Acquire()
        for _, item in ipairs(slots) do
                local gslot = _G["Character" .. item.slot .. "Slot"]
                if not item.frame and not item.texture and gslot then
                        -- We used to just :GetScript("OnClick") on the existing slot,
                        -- and save that so that we could override it and put it back
                        -- after we close the character frame, but this is not possible
                        -- in TBC with the protected code stuff. WoW will complain that
                        -- the script is tainted, and the normal slots won't work again
                        -- after adding a set, so we create our own frame that we set on
                        -- top of the existing one now.
                        local button = CreateFrame("Button", gslot:GetName() .. "ClosetGnome", gslot)
                        button:SetAlpha(0)
                        button:SetHeight(gslot:GetHeight())
                        button:SetWidth(gslot:GetWidth())
                        button:SetPoint("CENTER", gslot, "CENTER", 0, 1)
                        button:Hide()

                        local texture = gslot:CreateTexture(gslot:GetName() .. "Texture", "OVERLAY")
                        texture:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
                        texture:SetVertexColor(0, 255, 0)
                        texture:SetBlendMode("ADD")
                        texture:SetAlpha(0.75)
                        texture:SetHeight(68)
                        texture:SetWidth(68)
                        texture:SetPoint("CENTER", gslot, "CENTER", 0, 1)
                        texture:Hide()

                        item.frame = button
                        item.texture = texture
                end
                if item.frame and item.texture and gslot then
                        item.frame:EnableMouse(true)
                        item.frame:Show()
                        item.texture:SetVertexColor(0, 255, 0)
                        item.texture:Show()
                        self.activeSlots[gslot:GetName()] = true
                        item.frame:SetScript("OnEnter", function()
                                -- Horrible hack? :(
                                if PaperDollItemSlotButton_OnEnter and type(PaperDollItemSlotButton_OnEnter) == "function" then
                                        this = getglobal(string.gsub(this:GetName(), "ClosetGnome", ""))
                                        PaperDollItemSlotButton_OnEnter()
                                end
                        end)
                        item.frame:SetScript("OnClick", function()
                                local slot = string.gsub(this:GetName(), "ClosetGnome", "")
                                if IsControlKeyDown() then
                                        -- Don't want an empty slot as icon.
                                        local id = GetInventorySlotInfo(string.sub(slot, 10))
                                        local currentItem = ClosetGnome:ItemNameFromSlot(id)
                                        if currentItem == false then return end
                                        if ClosetGnome.activeSlots["icon"] then
                                                local oldSlot = ClosetGnome.activeSlots["icon"]
                                                ClosetGnome.activeSlots[oldSlot] = true
                                                getglobal(oldSlot.."Texture"):SetVertexColor(0, 255, 0)
                                        end
                                        ClosetGnome.activeSlots["icon"] = slot
                                        ClosetGnome.activeSlots[slot] = true
                                        getglobal(slot.."Texture"):SetVertexColor(0, 0, 255)
                                else
                                        ClosetGnome.activeSlots[slot] = not ClosetGnome.activeSlots[slot]
                                        if ClosetGnome.activeSlots[slot] then
                                                if ClosetGnome.activeSlots["icon"] == slot then
                                                        getglobal(slot.."Texture"):SetVertexColor(0, 0, 255)
                                                else
                                                        getglobal(slot.."Texture"):SetVertexColor(0, 255, 0)
                                                end
                                        else
                                                getglobal(slot.."Texture"):SetVertexColor(255, 0, 0)
                                        end
                                end
                        end)
                end
        end

        -- Hook Fizzle to prevent it from styling the slots while we add a set.
        if IsAddOnLoaded("Fizzle") and Fizzle and type(Fizzle) == "table" and type(Fizzle.UpdateItems) == "function" then
                self:Hook(Fizzle, "UpdateItems", "Noop")
        end

        ShowUIPanel(CharacterFrame)
        CharacterFrame_ShowSubFrame("PaperDollFrame")
        OpenAllBags(1)
        self:Hook("CharacterFrame_OnHide", true)
        self:Hook("CharacterFrameTab_OnClick", "Noop", true)
        if lua51 then
                self:Hook("PaperDollItemSlotButton_OnModifiedClick", "Noop", true)
        end

        dewdrop:Close()
        StaticPopup_Show("ClosetGnomeAdd")
end

function ClosetGnome:CancelCreateSet()
        CloseAllBags()
        HideUIPanel(CharacterFrame)
        if self:IsHooked("CharacterFrame_OnHide") then
                self:Unhook("CharacterFrame_OnHide")
        end
        if Fizzle and self:IsHooked(Fizzle, "UpdateItems") then
                self:Unhook(Fizzle, "UpdateItems")
        end
        if lua51 and self:IsHooked("PaperDollItemSlotButton_OnModifiedClick") then
                self:Unhook("PaperDollItemSlotButton_OnModifiedClick")
        end
end

function ClosetGnome:AddSetFromDoll(name)
        local slots = compost:Acquire()
        local icon = nil
        for slot, active in pairs(self.activeSlots) do
                if slot == "icon" then
                        icon = string.sub(active, 10)
                elseif active then
                        table.insert(slots, string.sub(slot, 10))
                end
        end
        self:AddSet(name, slots, icon)
        compost:Reclaim(slots)
        slots = nil
        icon = nil

        HideUIPanel(CharacterFrame)
        self:CharacterFrame_OnHide()
end

function ClosetGnome:AddSet(name, activeSlots, iconSlot)
        local concatedName = string.gsub(name, " ", "")
        if not name or name == "" or concatedName == "" then
                self:Print(L["Please use a proper name for your set."])
                return
        end
        if self:HasSet(concatedName) then name = concatedName end

        local text = string.format(L["Added set: %s."], name)
        local event = "ClosetGnome_AddSet"
        if self:HasSet(name) then
                text = string.format(L["Updating set: %s."], name)
                event = "ClosetGnome_UpdateSet"
        end

        for i, slot in pairs(activeSlots) do
                local id = GetInventorySlotInfo(slot)
                if not id then error(string.format("The given slot %q does not exist.", slot)) end
                local currentItem = self:ItemNameFromSlot(id)
                if not self.db.char.set[name] then self.db.char.set[name] = {} end
                self.db.char.set[name][id] = currentItem
        end

        if iconSlot then
                local slotId = GetInventorySlotInfo(iconSlot)
                if not slotId then error(string.format("The given icon slot %q does not exist.", iconSlot)) end
                self.db.char.icons[name] = GetInventoryItemTexture("player", slotId)
        end

        lastEquippedSet = name
        self:TriggerEvent(event, name)
        self:Print(text)

        self:UpdateSetMenu()
        self:UpdateDisplay()
end

function ClosetGnome:DeleteSet(name, verified)
        if not verified then
                queuedForDelete = name
                StaticPopup_Show("ClosetGnomeDelete", name)
                return
        end

        queuedForDelete = nil
        if self.db.char.set[name] then
                for i in pairs(self.db.char.set[name]) do
                        self.db.char.set[name][i] = nil
                end
                self.db.char.set[name] = nil
                self.db.char.icons[name] = nil
                self:ClearBinding(name)

                if lastEquippedSet == name then
                        lastEquippedSet = nil
                end

                self:TriggerEvent("ClosetGnome_DeleteSet", name)
                self:Print(string.format(L["Deleted set: %s."], name))
                self:UpdateSetMenu()
                self:UpdateDisplay()

                dewdrop:Close()
        end
end

function ClosetGnome:WearSet(name)
        if CursorHasItem() then return end

        local set = self.db.char.set[name]
        if not set then return end

        dewdrop:Close()

        if self:IsSetFullyEquipped(name) then
                lastEquippedSet = name
                return
        end

        if UnitAffectingCombat("player") then
                if self.db.char.alwaysWeapons then
                        if deequipQueue then
                                compost:Reclaim(deequipQueue)
                                deequipQueue = nil
                        end
                        for slot, item in pairs(set) do
                                if slot == 16 or slot == 17 or slot == 18 then
                                        local currentItem = self:ItemNameFromSlot(slot)
                                        if item ~= currentItem then
                                                self:EquipItem(slot, item)
                                        end
                                end
                        end
                        self:ProcessDeequipQueue()
                        lastEquippedSet = name
                        self:UpdateDisplay()
                end

                if self:IsSetFullyEquipped(name) then
                        self:TriggerEvent("ClosetGnome_WearSet", name)
                        return
                end

                self:TriggerEvent("ClosetGnome_PartlyWearSet", name)
                if not self:IsEventRegistered("PLAYER_REGEN_ENABLED") then
                        self:RegisterEvent("PLAYER_REGEN_ENABLED")
                end
                self:Print(string.format(L["In combat: %s queued for swap."], name))
                queuedSet = name
                return
        end

        if self:IsEventRegistered("PLAYER_REGEN_ENABLED") then
                self:UnregisterEvent("PLAYER_REGEN_ENABLED")
        end

        queuedSet = nil

        if self.db.profile.quips then
                -- I don't want the addon name in front for this one.
                -- Thanks to MrPlow for the color :)
                DEFAULT_CHAT_FRAME:AddMessage(self:GetQuip(name))
        end

        if deequipQueue then
                compost:Reclaim(deequipQueue)
                deequipQueue = nil
        end
        for slot, item in pairs(set) do
                local currentItem = self:ItemNameFromSlot(slot)
                if item ~= currentItem then
                        self:EquipItem(slot, item)
                end
        end
        self:ProcessDeequipQueue()
        lastEquippedSet = name
        self:TriggerEvent("ClosetGnome_WearSet", name)

        self:UpdateDisplay()
end

-- fizzle

-------------------------------------------------------------------------------
-- Utility                                                                   --
-------------------------------------------------------------------------------

function ClosetGnome:GetQuip(input)
        local rand = 1
        repeat rand = math.random(1, 31) until L[rand] ~= nil
        return "|cffeda55f"..string.format(L[rand], "|cffd9d919"..input.."|cffeda55f").."|r"
end

function ClosetGnome:ItemNameFromSlot(slot)
        assert(slot, "Slot must be non-nil.")
        local slotId = tonumber(slot)
        if not slotId then slotId = GetInventorySlotInfo(slot) end
        if not slotId then error(string.format("Unknown inventory slot %q.", slot)) end
        local hasItem = gratuity:SetInventoryItem("player", slotId)
        if not hasItem then return false end
        local currentItem = gratuity:GetLine(1)
        if not currentItem then currentItem = false end
        return currentItem
end

function ClosetGnome:IsSetFullyEquipped(name)
        local set = self.db.char.set[name]
        if not set or not name then return false end
        for slot, item in pairs(set) do
                local currentItem = self:ItemNameFromSlot(slot)
                if item ~= currentItem then
                        return false
                end
        end
        return true
end

function ClosetGnome:IsNormalBag(bagId)
        if bagId == 0 or bagId == -1 then return true end
        local _, _, linkId = string.find(GetInventoryItemLink("player", ContainerIDToInventoryID(bagId)), "item:(%d+)")
        if not linkId then return false end
        local _, _, _, _, _, bagType = GetItemInfo(linkId)
        if bagType and (bagType == L["Bag"] or bagType == L["Container"]) then return true end
        return false
end

function ClosetGnome:LocateFreeSlot()
        for theBag = NUM_BAG_FRAMES, 0, -1 do
                if self:IsNormalBag(theBag) then
                        if not slotLocks then slotLocks = compost:Acquire() end
                        if not slotLocks[theBag] then slotLocks[theBag] = compost:Acquire() end
                        local numSlot = GetContainerNumSlots(theBag)
                        for theSlot = 1, numSlot, 1 do
                                if not slotLocks[theBag][theSlot] then
                                        local texture = GetContainerItemInfo(theBag, theSlot)
                                        if not texture then
                                                return theBag, theSlot
                                        end
                                end
                        end
                end
        end
        return nil
end

function ClosetGnome:ProcessDeequipQueue()
        if not deequipQueue then return end
        if not slotLocks then slotLocks = compost:Acquire() end
        for i, slot in pairs(deequipQueue) do
                PickupInventoryItem(slot)
                local toBag, toSlot = self:LocateFreeSlot()
                if toBag ~= nil then
                        slotLocks[toBag][toSlot] = true
                        PickupContainerItem(toBag, toSlot)
                else
                        AutoEquipCursorItem()
                        break
                end
        end
        compost:Reclaim(deequipQueue)
        deequipQueue = nil
        if slotLocks then
                compost:Reclaim(slotLocks, 1)
                slotLocks = nil
        end
end

function ClosetGnome:EquipItem(slot, item, secondTry)
        if item == false then
                -- We need to find a free slot in the inventory for this item.
                -- If there's anything in the slot.
                local hasItem = self:ItemNameFromSlot(slot)
                if hasItem ~= false then
                        if not deequipQueue then deequipQueue = compost:Acquire() end
                        table.insert(deequipQueue, slot)
                end
        else
                local bagNum, slotNum = self:FindItem(item)
                if bagNum > -1 then
                        PickupContainerItem(bagNum, slotNum)
                        if slot < 11 or slot == 15 or slot == 19 then
                                AutoEquipCursorItem()
                        else
                                EquipCursorItem(slot)
                        end
                else
                        if not secondTry then
                                self:ScheduleEvent("cg"..slot..item, self.EquipItem, 0.1, self, slot, item, true)
                        else
                                self:Print(string.format(L["Couldn't find %s in your inventory."], item))
                        end
                end
        end
end

function ClosetGnome:HasSet(name)
        return self.db.char.set[name] ~= nil
end

function ClosetGnome:FindItem(item)
        for i = NUM_BAG_FRAMES, 0, -1 do
                for j = GetContainerNumSlots(i), 1, -1 do
                        local _, _, locked = GetContainerItemInfo(i, j)
                        if not locked then
                                gratuity:SetBagItem(i, j)
                                local bagItem = gratuity:GetLine(1)
                                if bagItem == item then return i, j end
                        end
                end
        end
        return -1
end

-------------------------------------------------------------------------------
-- Hooks                                                                     --
-------------------------------------------------------------------------------

-- Only hooked if we are showing the paper doll frame with set adding
function ClosetGnome:CharacterFrame_OnHide()
        for _, item in ipairs(slots) do
                if item.frame then
                        item.frame:EnableMouse(false)
                        item.frame:SetScript("OnEnter", nil)
                        item.frame:SetScript("OnClick", nil)
                        item.frame:Hide()
                        item.texture:Hide()
                end
        end

        StaticPopup_Hide("ClosetGnomeAdd")
        if self:IsHooked("CharacterFrame_OnHide") then
                self:Unhook("CharacterFrame_OnHide")
        end
        if self:IsHooked("CharacterFrameTab_OnClick") then
                self:Unhook("CharacterFrameTab_OnClick")
        end
        if Fizzle and self:IsHooked(Fizzle, "UpdateItems") then
                self:Unhook(Fizzle, "UpdateItems")
        end
        if lua51 and self:IsHooked("PaperDollItemSlotButton_OnModifiedClick") then
                self:Unhook("PaperDollItemSlotButton_OnModifiedClick")
        end
end

function ClosetGnome:Noop() --[[ noop ]] end

-------------------------------------------------------------------------------
-- Events                                                                    --
-------------------------------------------------------------------------------

function ClosetGnome:AceEvent_FullyInitialized()
        -- Loop through the current sets and check if we have any of them equipped.
        for k,v in pairs(self.db.char.set) do
                if self:IsSetFullyEquipped(k) then
                        lastEquippedSet = k
                        self:TriggerEvent("ClosetGnome_WearSet", k)
                        break
                end
        end
        self:UpdateDisplay()

        self:Print("|cff00ff00ClosetGnome still has 1 or 2 issues that has to be resolved before release, but I consider it usable. Please direct any comments to me on IRC or to the forum post.|r")
end

function ClosetGnome:PLAYER_REGEN_ENABLED()
        if not queuedSet then return end
        self:WearSet(queuedSet)
end

-------------------------------------------------------------------------------
-- FuBarPlugin                                                               --
-------------------------------------------------------------------------------

function ClosetGnome:OnTextUpdate()
        if lastEquippedSet then
                self:SetText(lastEquippedSet)
        else
                self:SetText(L["ClosetGnome"])
        end
        if self.db.char.icons[lastEquippedSet] then
                self:SetIcon(self.db.char.icons[lastEquippedSet])
        else
                self:SetIcon(defaultIcon)
        end
end

function ClosetGnome:OnDoubleClick()
        self:CreateSet()
end

-- Hook this method and return true for any sets you do not want listed in the
-- tablet.
function ClosetGnome:HideSetFromUI(set)
        return false
end

function ClosetGnome:OnTooltipUpdate()
        local cat = nil
        local function clickfunc(name)
                if IsControlKeyDown() then
                        local set = self.db.char.set[string.gsub(name, "*$", "")]

                        DressUpModel:Undress()
                        for i=1,19 do
                                local item = set[i]

                                if i ~= 18 and item ~= false then
                                        -- slot isn't to be emptied, and isn't ranged
                                        local link

                                        if item then
                                                local bagNum, slotNum = self:FindItem(item)
                                                if bagNum > -1 then
                                                        link = GetContainerItemLink(bagNum, slotNum)
                                                end
                                        end

                                        if not link then
                                                -- either unable to find in inventory, or
                                                -- slot not in set. either way, use
                                                -- currently equipped
                                                link = GetInventoryItemLink("player", i)
                                        end

                                        if link then
                                                DressUpItemLink(link)
                                        end
                                end
                        end
                else
                        ClosetGnome:WearSet(name)
                end
        end

        local sets = compost:Acquire()
        for k in pairs(self.db.char.set) do table.insert(sets, k) end
        table.sort(sets)

        for i,k in ipairs(sets) do
                if not self:HideSetFromUI(k) then
                        if not cat then
                                cat = tablet:AddCategory(
                                        "columns", 2,
                                        "text", L["Available sets"],
                                        "showWithoutChildren", false,
                                        "child_justify", "LEFT",
                                        "child_justify2", "RIGHT",
                                        "child_hasCheck", true,
                                        "child_checked", true,
                                        "child_text2R", 0.5,
                                        "child_text2G", 0.5,
                                        "child_text2B", 0.5
                                )
                        end
                        local set = k
                        if lastEquippedSet == set then set = set .. "*" end
                        cat:AddLine(
                                "text", set,
                                "func", clickfunc,
                                "arg1", set,
                                "checkIcon", self.db.char.icons[k] or "",
                                "text2", self.db.char.keybindings[k] and "("..self.db.char.keybindings[k]..")" or ""
                        )
                end
        end
        compost:Reclaim(sets)
        sets = nil

        if cat ~= nil then
                tablet:SetHint(L["Click a set above to change your equipment. Ctrl-click a set to try it on. The last equipped set is indicated by a star."])
        else
                tablet:SetHint(L["You have not created any equipment sets yet, please right-click the icon and click 'Add' to create a set."])
        end
end