Mercurial > wow > whichrankdoeswhat
changeset 17:b9897e874fac v1.3.6
merge tag
| author | Farmbuyer of US-Kilrogg <farmbuyer@gmail.com> |
|---|---|
| date | Fri, 03 Aug 2012 03:40:12 -0400 |
| parents | 3a2beea01a28 (diff) bb135ad3065c (current diff) |
| children | 23fbc8ea967e |
| files | .hgtags |
| diffstat | 14 files changed, 1008 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Fri Aug 03 07:35:59 2012 +0000 +++ b/.hgtags Fri Aug 03 03:40:12 2012 -0400 @@ -1,1 +1,2 @@ -0000000000000000000000000000000000000000 v1.3.5 +4f0a29493035fd8907919015d21f9145d3e5920a beta3 +4b870c06a6c8f9a8e4822bbff61b97983ca7186a beta4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.pkgmeta Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,34 @@ +package-as: WhichRankDoesWhat + +externals: + libs/LibStub: + url: svn://svn.wowace.com/wow/libstub/mainline/trunk + tag: latest + libs/CallbackHandler-1.0: + url: svn://svn.wowace.com/wow/callbackhandler/mainline/trunk/CallbackHandler-1.0 + tag: latest + + # for Ace3 stuff, it's usually better to grab the alphas (they get tested) + # than waiting for bugfixes to get tagged + libs/AceAddon-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceAddon-3.0 + libs/AceConfig-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceConfig-3.0 + libs/AceEvent-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceEvent-3.0 + libs/AceConsole-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceConsole-3.0 + libs/AceDBOptions-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceDBOptions-3.0 + libs/AceDB-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceDB-3.0 + libs/AceLocale-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceLocale-3.0 + libs/AceGUI-3.0: + url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceGUI-3.0 + + libs/lib-st: + url: svn://svn.wowace.com/wow/lib-st/mainline/trunk + tag: latest + +# vim: et
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AceGUIWidget-lib-st.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,252 @@ +--[[----------------------------------------------------------------------------- +lib-st Beta Wrapper Widget + +lib-st does not recycle the objects (called "ST" here) that it creates and +returns. We therefore do not try to hold onto an ST when the widget is +being recycled. This means that Constructor() does very little work, and +does not actually construct an ST. + +OnAcquire cannot construct an ST either, because we don't yet have any +creation parameters from the user to feed to CreateST. (Allowing such to +be passed along from AceGUI:Create() would require changes to core AceGUI +code, and I don't feel like trying to overcome that inertia.) + +The upshot is that the widget returned from Create is broken and useless +until its CreateST member has been called. This means that correct behavior +depends entirely on the user remembering to do so. + +"The gods do not protect fools. Fools are protected by more capable fools." +- Ringworld + + +Version 1 initial functioning implementation +Version 2 reshuffle to follow new AceGUI widget coding style +Version 3 add .tail_offset, defaulting to same absolute value as .head_offset +Version 4 restore original frame methods, as fortold by ancient prophecy +Version 5 don't bogart the widget object +-farmbuyer +-------------------------------------------------------------------------------]] +local Type, Version = "lib-st", 4 +local AceGUI = LibStub and LibStub("AceGUI-3.0", true) +if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end + +-- Lua APIs +local ipairs, error = ipairs, error + +-- WoW APIs +local debugstack = debugstack +local CreateFrame = CreateFrame + + +--[[----------------------------------------------------------------------------- +Support functions +-------------------------------------------------------------------------------]] + +-- Some AceGUI functions simply Won't Work in this context. Name them +-- here, and code calling them will get a somewhat informative error(). +local oopsfuncs = { + 'SetRelativeWidth', 'SetRelativeHeight', + 'SetFullWidth', 'SetFullHeight', +} +local err = "Oops! The AceGUI function you tried to call (%s) does not " + .. "make sense with lib-st and has not been implemented." + +local function Oops(self) + -- if you ever wanted an example of "brown paper bag" code, here it is + local ds = debugstack(0) + local func = ds:match("AceGUIWidget%-lib%-st%.lua:%d+:%s+in function `(%a+)'") + error(err:format(func or "?")) +end + + +--[[ + Users with an ST already constructed can drop it into a widget directly + using this routine. It must be safe to call this more than once with + new widgets on the same ST. + + This is where most of the intelligence of the wrapper is located. That + is, if you can call my code "intelligent" with a straight face. Lemme + try again. + + Think of the widget wrapper as a brain. When ALL THREE neurons manage + to fire at the same time and produce a thought, this function represents + the thought. Sigh. +]] +local ShiftingSetPoint, ShiftingSetAllPoints +local function WrapST (self, st) + if not st.frame then + error"lib-st instance has no '.frame' field... wtf did you pass to this function?" + end + --if st.frame.obj and (st.frame.obj ~= self) then + -- error"lib-st instance already has an '.obj' field from a different widget, cannot use with AceGUI!" + --end + self.st = st + if not st.head then + error"lib-st instance has no '.head' field, must use either ScrollingTable:CreateST or this widget's CreateST first" + end + self.frame = st.frame -- gutsy, but looks doable + + -- Possibly have already wrapped this ST in a previous widget, careful. + --if st.frame.obj ~= self then + self.frame.customSetPoint = rawget(self.frame,"SetPoint") + self.frame.realSetPoint = self.frame.SetPoint + self.frame.SetPoint = ShiftingSetPoint + self.frame.SetAllPoints = ShiftingSetAllPoints + --end + + -- This needs the .frame field. This also unconditionally creates .obj + -- inside that field and calls a SetScript on it as well. + return AceGUI:RegisterAsWidget(self) +end + + +--[[----------------------------------------------------------------------------- +Scripts +-------------------------------------------------------------------------------]] +--[[ + All of an ST's subframes are attached to its main frame, which we have in + the st.frame link, and that's what AceGUI uses for all positioning. Except + that ST:SetDisplayCols creates its "head" row /above/ the main frame, and + so the row of labels eats into whatever upper border space AceGUI calculates, + often overlapping other elements. + + We get around this by replacing ST's main frame's SetPoint with a custom + version that just moves everything down a few pixels to allow room for the + head row. + + FIXME this may need to be a secure hook (ugh, would end up calling the real + setpoint twice) rather than a replacement. +]] +local DEFAULT_OFFSET = 7 +function ShiftingSetPoint(frame,anchor,other,otheranchor,xoff,yoff) + local ho,to = frame.obj.head_offset, frame.obj.tail_offset + yoff = yoff or 0 + if anchor:sub(1,3) == "TOP" then + yoff = yoff - ho + elseif anchor:sub(1,6) == "BOTTOM" then + yoff = yoff + to + end + return frame.realSetPoint(frame,anchor,other,otheranchor,xoff,yoff) +end +function ShiftingSetAllPoints(frame,other) + ShiftingSetPoint(frame,"TOPLEFT",other,"TOPLEFT",0,0) + ShiftingSetPoint(frame,"BOTTOMRIGHT",other,"BOTTOMRIGHT",0,0) +end + + +--[[----------------------------------------------------------------------------- +Methods +-------------------------------------------------------------------------------]] +local methods = { + -- -------------------------------------------------------------- + -- These are expected by AceGUI containers (and AceGUI users) + -- + ["OnAcquire"] = function (self) + -- Almost nothing can usefully be done here. + self.head_offset = DEFAULT_OFFSET + self.tail_offset = DEFAULT_OFFSET + end, + + ["OnRelease"] = function (self) + if self.st then + self.st.frame:ClearAllPoints() + self.st:Hide() + end + self.st = nil + self.frame.realSetPoint = nil + self.frame.SetAllPoints = nil + self.frame.SetPoint = self.frame.customSetPoint + self.frame.customSetPoint = nil + end, + + --[[ + STs don't use a "normal" SetWidth, if we define "normal" to be the + behavior of the blizzard :SetWidth. Column width is passed in during + creation of the whole ST. The SetWidth defined by an ST takes no + arguments; "ReCalculateWidth" would be a more precise description of + what it does. + + Parts of AceGUI look for a .width field because a widget's SetWidth + sets such. ST calculates a total width and dispatches it to its member + frame... but doesn't store a local copy. We need to bridge these + differences. + + This widget wrapper does not make use of On{Width,Height}Set hooks, + but the acegui widget base functions do. Since we're not inheriting + them, we may as well supply them. + ]] + ["SetWidth"] = function (self) + self.st:SetWidth() -- re-total the columns + local w = self.st.frame:GetWidth() -- fetch the answer back + self.frame.width = w -- store it for acegui + if self.OnWidthSet then + self:OnWidthSet(w) + end + end, + + -- Everything said about SetWidth applies here too. + ["SetHeight"] = function (self) + self.st:SetHeight() + local h = self.st.frame:GetHeight() + self.frame.height = h + if self.OnHeightSet then + self:OnHeightSet(h) + end + end, + + -- Some of the container layouts call Show/Hide on the innermost frame + -- directly. We need to make sure the slightly-higher-level routine is + -- also called. + ["LayoutFinished"] = function (self) + if self.frame:IsShown() then + self.st:Show() + else + self.st:Hide() + end + end, + + -- -------------------------------------------------------------- + -- Functions specific to this widget + -- + + ["GetSTLibrary"] = function (self) -- Purely for convenience + return LibST + end, + + --[[ + Replacement wrapper, so that instead of + st = ScrollingTable:CreateST( args ) + the user should be able to do + st = AceGUI:Create("lib-st"):CreateST( args ) + instead, without needing to get a lib-st handle. + ]] + ["CreateST"] = function (self, ...) + return self:WrapST( LibST:CreateST(...) ) + end, + + ["WrapST"] = WrapST, +} + + +--[[----------------------------------------------------------------------------- +Constructor +-------------------------------------------------------------------------------]] +local function Constructor() + -- .frame not done here, see WrapST + local widget = { + type = Type + } + for method, func in pairs(methods) do + widget[method] = func + end + + for _,func in ipairs(oopsfuncs) do + widget[func] = Oops + end + + -- AceGUI:RegisterAsWidget needs .frame + return widget +end + +AceGUI:RegisterWidgetType(Type,Constructor,Version) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WhichRankDoesWhat.toc Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,37 @@ +## Interface: 40300 +## Title: Which Rank Does What +## Version: @project-version@ +## Notes: Displays a grid of guild ranks versus permissions. +## Author: Farmbuyer of US-Kilrogg +## SavedVariables: wrdwDB +## OptionalDeps: Ace3, lib-st, LibFarmbuyer +## LoadOnDemand: 1 +## LoadWith: Blizzard_GuildUI, Blizzard_GuildControlUI, Blizzard_GuildBankUI + +#@no-lib-strip@ +libs\LibStub\LibStub.lua +libs\CallbackHandler-1.0\CallbackHandler-1.0.xml +libs\AceAddon-3.0\AceAddon-3.0.xml +libs\AceDB-3.0\AceDB-3.0.xml +libs\AceDBOptions-3.0\AceDBOptions-3.0.xml +libs\AceEvent-3.0\AceEvent-3.0.xml +libs\AceLocale-3.0\AceLocale-3.0.xml +libs\AceConsole-3.0\AceConsole-3.0.xml +libs\AceGUI-3.0\AceGUI-3.0.xml +libs\AceConfig-3.0\AceConfig-3.0.xml +libs\lib-st\lib-st.xml +#@end-no-lib-strip@ + +locale-enUS.lua +locale-deDE.lua +locale-esES.lua +locale-esMX.lua +locale-frFR.lua +locale-koKR.lua +locale-ruRU.lua +locale-zhCN.lua +locale-zhTW.lua + +AceGUIWidget-lib-st.lua +main.lua +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-deDE.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "deDE") +if not L then return end + +--@localization(locale="deDE", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-enUS.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,7 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "enUS", --[[default=]]true, --[[silent=]]true) + +--@localization(locale="enUS", format="lua_additive_table", same-key-is-true=true)@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-esES.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "esES") +if not L then return end + +--@localization(locale="esES", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-esMX.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "esMX") +if not L then return end + +--@localization(locale="esMX", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-frFR.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "frFR") +if not L then return end + +--@localization(locale="frFR", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-koKR.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "koKR") +if not L then return end + +--@localization(locale="koKR", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-ruRU.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "ruRU") +if not L then return end + +--@localization(locale="ruRU", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-zhCN.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "zhCN") +if not L then return end + +--@localization(locale="zhCN", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale-zhTW.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,8 @@ +-- Which Rank Does What Locale +-- Please use the Localization App on WoWAce to Update this +-- http://www.wowace.com/addons/whichrankdoeswhat/localization/ +local nametag = ... +local L = LibStub("AceLocale-3.0"):NewLocale(nametag, "zhTW") +if not L then return end + +--@localization(locale="zhTW", format="lua_additive_table", handle-unlocalized="comment")@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.lua Fri Aug 03 03:40:12 2012 -0400 @@ -0,0 +1,612 @@ +local nametag, addon = ... +local L = LibStub("AceLocale-3.0"):GetLocale(nametag) + +addon.defaults = { + profile = { + enable = true, + guildcontrol = true, + }, +} + + +addon.options = { + name = "", + type = 'group', + childGroups = 'tab', + handler = addon, -- functions listed as strings called as addon:func + get = "GetOption", + set = "SetOption", + args = { + general = { + name = GENERAL, + desc = L["General options"], + type = 'group', + order = 10, + args = { + version = { + --name = filled in during OnInit + type = 'description', + fontSize = "large", + cmdHidden = true, + width = 'full', + order = 1, + }, + enable = { + name = ENABLE, + desc = L["Use this addon"], + type = 'toggle', + arg = "ToggleEnable", + order = 5, + }, + guildcontrol = { + name = L["Guild Control for non-GMs"], + desc = L["Make the grayed-out Guild Control button activate this addon instead."], + type = 'toggle', + width = 'double', -- else it gets cut off + order = 10, + }, + break1 = { + name = '', + type = 'description', + cmdHidden = true, + width = 'full', + order = 14, + }, + popup = { + name = "/wrdw", + desc = L["Toggle WRDW window"], + type = 'execute', + func = function() + InterfaceOptionsFrameCancel:Click() + HideUIPanel(GameMenuFrame) + addon:BuildWindow() + end, + order = 15, + }, + }, + }, + --profiles = filled in OnInit + }, +} + + +----------------------------------------------------------------------------- +-- other locals +local AceGUI = LibStub("AceGUI-3.0") +local st_rowheight = 25 +local st_displayed_rows = 15 --math.floor(366/st_rowheight) +local st_colwidth = 80 --65 +local cols_per_group = 6 +local num_flagsets +local sidetabs +local incomplete +local flagmap + +-- Remove children ST widgets without explicitly Release()'ing them. As there +-- are no children other than STs, no "normal" widget resources are leaked. +local function DisownChildren (container) + for i,v in ipairs(container.children) do + container.children[i] = nil + v.frame:Hide() + v.frame:ClearAllPoints() + end +end + +local function setstatus(txt) + addon.display:SetStatusText(txt) + addon.tooltip = #txt > 40 and txt or nil +end + + +----------------------------------------------------------------------------- +addon = LibStub("AceAddon-3.0"):NewAddon(addon, nametag, + "AceConsole-3.0", "AceEvent-3.0") + +-- Thanks to jerry for the nifty arg idea. +function addon:SetOption (info, value) + local name = info[#info] + self.db.profile[name] = value + local arg = info.arg + if arg then self[arg](self) end +end + +function addon:GetOption (info) + local name = info[#info] + return self.db.profile[name] +end + +function addon:OnInitialize() + self.db = LibStub("AceDB-3.0"):New("wrdwDB", self.defaults, --[[Default=]]true) + + local AceDBOptions = LibStub("AceDBOptions-3.0", true) + if AceDBOptions then + self.options.args.profiles = AceDBOptions:GetOptionsTable(self.db) + self.options.args.profiles.order = 200 + end + + self.options.args.general.args.version.name = + "|cff30adffVersion " .. (GetAddOnMetadata(nametag, "Version") or "?") .. "|r" + LibStub("AceConfig-3.0"):RegisterOptionsTable(nametag, self.options) + self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions(nametag, "WhichRankDoesWhat") + --self.optionsFrame.okay = function() pattern_editing_safe = false end + --self.optionsFrame.refresh = self.optionsFrame.okay + --self.optionsFrame.cancel = self.optionsFrame.okay + + self:SetEnabledState(self.db.profile.enable) + self.OnInitialize = nil +end + + +function addon:OnEnable() + self:RegisterEvent("GUILD_RANKS_UPDATE") + self:RegisterChatCommand("wrdw", "OnSlashCommand") + + -- Ideally, most of this stuff wouldn't be done at load time at all; this + -- whole addon should be LoD. + if (not IsGuildLeader()) and self.db.profile.guildcontrol then + local function onclick() addon:BuildWindow() end + local function onenter(this) GameTooltip_AddNewbieTip(this, GUILDCONTROL, 1.0, 1.0, 1.0, "/wrdw", 1) end + GuildFrame_LoadUI() + if not NUM_RANK_FLAGS then + UIParentLoadAddOn("Blizzard_GuildControlUI") + end + local function noreallyitsokay() + GuildControlButton:Enable() + GuildControlButton:SetScript("OnClick", onclick) + GuildControlButton:SetScript("OnEnter", onenter) + end + hooksecurefunc("GuildInfoFrame_UpdatePermissions", noreallyitsokay) + -- This doesn't seem to be used anymore...? + hooksecurefunc("GuildFrame_CheckPermissions", noreallyitsokay) + end +end + +function addon:OnDisable() + self:Print(L["You will need to relog or /reload to fully disable this addon."]) +end + +function addon:ToggleEnable() + if self.db.profile.enable then + self:Enable() + else + self:Disable() + end +end + +function addon:OnSlashCommand (input) + if not NUM_RANK_FLAGS then -- in case a GM didn't get it loaded earlier + GuildFrame_LoadUI() + UIParentLoadAddOn("Blizzard_GuildControlUI") + end + if not input or input:trim() == "" then + if self.display and self.display:IsShown() then + self.display:Hide() + else + self:BuildWindow() + end + else + --LibStub("AceConfigCmd-3.0").HandleCommand(self, "wrdw", nametag, input) + LibStub("AceConfigDialog-3.0"):Open(nametag) + end +end + + +-- Something somewhere has changed, redo the cache +do + local text = '|cffff1010' .. L["Guild flags have changed!"] .. '|r ' .. + L["You must close and reopen this window to display the changes."] + function addon:GUILD_RANKS_UPDATE() + self.perms = nil + if (not incomplete) and self.display and self.display:IsVisible() then + setstatus(text) + end + end +end + + +function addon:BuildPerms() + assert(UIParentLoadAddOn("Blizzard_GuildControlUI")) + local check = "|TInterface\\Buttons\\UI-CheckBox-Check:"..(st_rowheight+5).."|t" + + -- http://www.wowace.com/addons/lib-st/pages/set-data/minimal-dataset-format/ + local p,v = {}, {} + flagmap = {} + num_flagsets = math.floor(NUM_RANK_FLAGS/cols_per_group + 1) + for flagset = 1, num_flagsets do + p[flagset] = {} + end + + for r = 1, GuildControlGetNumRanks() do + GuildControlSetRank(r) + + -- permissions (most special handling goes here) + -- flag 14 is no longer used + -- flags 15 and 16 may have numeric values not just a boolean + -- flag 21 is... not included yet, apparently + local flags = { GuildControlGetRankFlags() } + for flagset = 1, num_flagsets do + local row = { GuildControlGetRankName(r) } + for c_offset = 1, cols_per_group do while true do + local c = (flagset-1) * cols_per_group + c_offset + --if c == 14 or c > NUM_RANK_FLAGS then break end + if c > NUM_RANK_FLAGS then break end + local newcol = #row + 1 + flagmap[flagset..'x'..newcol] = c + if c == 15 or c == 16 then + local val = GetGuildBankWithdrawGoldLimit() + row[newcol] = flags[c] and ((val == -1) and check or val) or "" + elseif c == 14 then + row[newcol] = "" + else + row[newcol] = flags[c] and check or "" + end + break + end end + p[flagset][r] = row + end + + -- guild vault + local banktabs = {} + for t = 1, GetNumGuildBankTabs() do + -- isViewable, canDeposit, editText, numWithdrawals + banktabs[t] = { GuildControlGetRankName(r), GetGuildBankTabPermissions(t) } + banktabs[t][2] = banktabs[t][2] and check or "" + banktabs[t][3] = banktabs[t][3] and check or "" + banktabs[t][4] = banktabs[t][4] and check or "" + local withdraw = banktabs[t][5] + banktabs[t][5] = (withdraw == -1) and check or (withdraw == 0) and "" or withdraw + end + v[r] = banktabs + end + self.perms = p + -- This one needs to be turned inside-out to match the data requirements + self.vault = {} + for t = 1, GetNumGuildBankTabs() do + self.vault[t] = {} + for r = 1, #v do + self.vault[t][r] = v[r][t] + end + end +end + + +local make_sidetab +do + local lastclicked + local function OnClick (thistab) + if thistab == lastclicked then return end + for i = 1, #sidetabs do + sidetabs[i]:SetChecked(false) + end + thistab:SetChecked(true) -- should be redundant, but just in case + lastclicked = thistab + if thistab.callback then + thistab:callback(thistab:GetID()) + end + end + + -- Some magic numbers here wrt the index + function make_sidetab (index, callback) + if not sidetabs then + sidetabs = {} + end + + local tab = CreateFrame("CheckButton", "WRDWTab"..index, addon.display.frame, "SpellBookSkillLineTabTemplate", index) + if index > 1 then + tab:SetPoint("TOPLEFT", sidetabs[index-1], "BOTTOMLEFT", 0, -17) + else + tab:SetNormalTexture("Interface\\SpellBook\\GuildSpellbooktabBG") + tab.TabardEmblem:Show() + tab.TabardIconFrame:Show() + SetLargeGuildTabardTextures("player", tab.TabardEmblem, tab:GetNormalTexture(), tab.TabardIconFrame) + tab:SetPoint("TOPLEFT", addon.display.frame, "TOPRIGHT", 0, -17) + end + tab:SetScript("OnClick", OnClick) + --tab:SetChecked(false) -- is default + tab:Show() + tab.callback = callback + sidetabs[index] = tab + return tab + end + + function addon:BuildVaultTabs() + incomplete = nil + local offset = 1 -- number of tabs already made + local function pick_a_tab (tab, id) + DisownChildren(self.display) + self.display:AddChild(self.vault_sts[id-offset]) + local buttons = self.display:GetUserData("extra buttons") + buttons['prev']:Disable() + buttons['next']:Disable() + end + for t = 1, GetNumGuildBankTabs() do + local name, icon = GetGuildBankTabInfo(t) + incomplete = incomplete or icon == [[Interface\Icons\INV_Misc_QuestionMark]] + local tab = make_sidetab(t+offset, pick_a_tab) + tab:SetNormalTexture(icon) + tab.tooltip = name + end + if incomplete then + setstatus(L["Guild vault information is incomplete. Be closer to a vault, and give it some time. You may need to relog and/or open the guild roster/vault to force a client update."]) + end + end +end + + +-- The "closebutton" variable isn't accessible through the widget. I should +-- probably just constuct an entire Frame-equse thing by hand instead... gah. +local function FIXFRAME (container, ...) + for i = 1, select('#',...) do + local child = select(i,...) + if child:GetObjectType() == "Button" and child:GetText() == CLOSE then + container:SetUserData("close button", child) + return + end + end +end + +local function adjust_flagset (button) + local key = button.button_key + assert (key == 'prev' or key == 'next') + local flagset = addon.current_main_st + if key == 'prev' then + flagset = flagset - 1 + else + flagset = flagset + 1 + end + return flagset +end + +local function AddedButton_OnEnter (button) + -- not very generic, that's okay until we need to be + local high = adjust_flagset(button) * cols_per_group + local low = high - cols_per_group + 1 + if high > NUM_RANK_FLAGS then high = NUM_RANK_FLAGS end + setstatus(L["Show flag columns %d - %d"]:format(low,high)) +end +local function AddedButton_OnLeave (button) + setstatus("") +end +local function AddedButton_OnClick (button) + addon:DoMainST(adjust_flagset(button)) + -- the buttons may have changed state, adjust their status text + if button:IsEnabled() then + AddedButton_OnEnter (button) + else + AddedButton_OnLeave (button) + end +end + +local function AddButton (container, key, label) + assert(not tonumber(key)) + local all = container:GetUserData("extra buttons") or {} + container:SetUserData("extra buttons", all) + local n = #all + local closebutton = assert(container:GetUserData("close button"), "something horrible") + local b = CreateFrame("Button", nil, container.frame, "UIPanelButtonTemplate") + b.button_key = key + b.obj = self + b:SetScript("OnClick", AddedButton_OnClick) + b:SetScript("OnEnter", AddedButton_OnEnter) + b:SetScript("OnLeave", AddedButton_OnLeave) + b:SetText(label) + b:SetHeight(20) + b:SetWidth(50) -- "Close" is 100 + b:SetPoint("BOTTOMRIGHT", closebutton, "BOTTOMLEFT", -5, 0) + b:Show() + all[n+1] = b + all[key] = b + + for i = n, 1, -1 do + local ob = all[i] + ob:ClearAllPoints() + ob:SetPoint("BOTTOMRIGHT", all[i+1], "BOTTOMLEFT", -5, 0) + end + -- The Frame's statusbar is not accessible via the Frame widget itself. + -- Which is nice and properly encapsulated and all, but also inconvenient. + -- We'll take the long route there. + local sb = container.statustext:GetParent() + assert (sb.obj == container) + sb:ClearAllPoints() + sb:SetPoint("BOTTOMLEFT", 15, 15) -- default + sb:SetPoint("BOTTOMRIGHT", all[1], "BOTTOMLEFT", -5, 0) +end + + +local function st_OnEnter (rowFrame, cellFrame, data, cols, row, realrow, column, sttable, button, ...) + if (row == nil) or (realrow == nil) then -- mouseover column header + setstatus(cellFrame:GetText():gsub('\n',' ')) + return true + end + return false -- continue with default highlighting behavior +end +local function st_OnLeave (rowFrame, cellFrame, data, cols, row, realrow, column, sttable, button, ...) + setstatus("") + return false -- continue with default un-highlighting behavior +end +local function st_OnClick (rowFrame, cellFrame, data, cols, row, realrow, column, sttable, button, ...) + if (row == nil) or (realrow == nil) then return true end -- click column header, suppress reordering + -- more here? + return true -- do not do anything further +end + + +local function OnEnterStatusBar (container) + if not addon.tooltip then return end + GameTooltip:SetOwner (container.frame, "ANCHOR_RIGHT") + GameTooltip:ClearLines() + GameTooltip:AddLine (nametag) + GameTooltip:AddLine (addon.tooltip, 0.8, 0.8, 0.8, 1) + GameTooltip:Show() +end + + +function addon:BuildMainSTs (permissions, parent_frame) + local errtxt = "flagset %d, column %d, failed to map to a flag number" + -- if this language uses a trailing colon, strip it + local ranklabel = GUILDCONTROL_RANKLABEL:gsub(":$","") + self.main_sts = {} + + for flagset = 1, #permissions do + local cols = {{ + name = ranklabel, + width = 10 * #ranklabel, + }} + for c = #cols+1, #permissions[flagset][1] do -- all ranks work here + local f = flagmap[flagset..'x'..c] + if not f then error(errtxt:format(flagset, c)) end + table.insert(cols,{ + -- the only special handling outside BuildPerms + name = _G[f == 14 and 'UNUSED' or 'GUILDCONTROL_OPTION'..f], + width = st_colwidth, + }) + end + + local ST = LibStub("ScrollingTable"):CreateST (cols, st_displayed_rows, st_rowheight, --[[highlight=]]nil, parent_frame) + ST:Hide() + + ST:SetData(permissions[flagset], --[[minimal format=]]true) + ST:RegisterEvents{ + OnEnter = st_OnEnter, + OnLeave = st_OnLeave, + OnClick = st_OnClick, + OnDoubleClick = st_OnClick, + } + self.main_sts[flagset] = ST + end +end + +function addon:BuildVaultSTs (permissions, parent_frame) + self.vault_sts = {} + local cols = { + self.main_sts[1].st.cols[1], + { name = GUILDCONTROL_VIEW_TAB, width = 80 }, + { name = GUILDCONTROL_DEPOSIT_ITEMS, width = 80 }, + { name = GUILDCONTROL_UPDATE_TEXT, width = 80 }, + { name = GUILDCONTROL_WITHDRAW_ITEMS, width = 150 }, + } + + for tab = 1, #permissions do + local ST = LibStub("ScrollingTable"):CreateST (cols, st_displayed_rows, st_rowheight, --[[highlight=]]nil, parent_frame) + ST:Hide() + ST:SetData(permissions[tab], --[[minimal format=]]true) + ST:RegisterEvents{ + OnEnter = st_OnEnter, + OnLeave = st_OnLeave, + OnClick = st_OnClick, + OnDoubleClick = st_OnClick, + } + self.vault_sts[tab] = ST + end +end + + +function addon:DoMainST (index) + self.current_main_st = index + DisownChildren(self.display) + self.display:AddChild(self.main_sts[index]) + local buttons = self.display:GetUserData("extra buttons") + buttons['prev'][index <= 1 and 'Disable' or 'Enable'](buttons['prev']) + buttons['next'][index >= num_flagsets and 'Disable' or 'Enable'](buttons['next']) +end + + +-- Under normal conditions, this massive wodge is built once, and then merely +-- :Show'n and :Hide'n. Only if info gets out of date do we release/destroy +-- the UI elements and rebuild. +function addon:BuildWindow() + local need_tabs + if self.display then + self.display:Hide() + else + self.display = AceGUI:Create("Frame") + self.display:SetTitle("Which Rank Does What") + self.display:SetLayout("Fill") + self.display:EnableResize(false) + self.display:SetStatusTable{ + width = (st_colwidth+4) * cols_per_group -- flag columns + + 105, -- rank label column + height = 500, + } + self.display:ApplyStatus() + FIXFRAME (self.display, self.display.frame:GetChildren()) + AddButton (self.display, 'prev', "<<") + AddButton (self.display, 'next', ">>") + self.display:SetCallback("OnEnterStatusBar", OnEnterStatusBar) + self.display:SetCallback("OnLeaveStatusBar", GameTooltip_Hide) + self.display:SetCallback("OnClose", function(_d) + if incomplete or (not self.perms) then + -- stuff changed while open + self.perms = nil + self.display = nil + if sidetabs then for i,s in ipairs(sidetabs) do + s:Hide() + s:ClearAllPoints() + s:SetParent(nil) -- Blizzard does this too. Huh. + end end + sidetabs = nil + for i,b in ipairs(_d:GetUserData("extra buttons")) do + b.obj = nil + b:Hide() + b:ClearAllPoints() + b:SetParent(nil) + end + AceGUI:Release(_d) + end + end) + need_tabs = true + end + + if not self.perms then + need_tabs = true + self:BuildPerms() -- creates self.perms and self.vault + DisownChildren(self.display) + -- Could be new rows, fewer rows, changed tickboxes... ugh, trying to + -- update the scrolltable is a pain. Throw it out and start over. + if sidetabs then for i,s in ipairs(sidetabs) do + s:Hide() + s:ClearAllPoints() + s:SetParent(nil) -- Blizzard does this too. Huh. + end end + if self.main_sts then for i = 1, #self.main_sts do + if self.main_sts[i] and self.main_sts[i].st then + self.main_sts[i]:Release() + end + end end + if self.vault_sts then for i = 1, #self.vault_sts do + if self.vault_sts[i] and self.vault_sts[i].st then + self.vault_sts[i]:Release() + end + end end + self.main_sts = nil + self.vault_sts = nil + end + if not self.main_sts then + self:BuildMainSTs (self.perms, self.display.content) + for i,st in ipairs(self.main_sts) do + self.main_sts[i] = AceGUI:Create("lib-st"):WrapST(st) + self.main_sts[i].head_offset = 20 + end + self:DoMainST(1) + + self:BuildVaultSTs (self.vault, self.display.content) + for i,st in ipairs(self.vault_sts) do + self.vault_sts[i] = AceGUI:Create("lib-st"):WrapST(st) + self.vault_sts[i].head_offset = 20 + end + end + + if need_tabs or incomplete then + local maintab = make_sidetab(1, function (this, id) + self:DoMainST(self.current_main_st) + end) + maintab.tooltip = [[Rank permissions]] + maintab:SetChecked(true) + self:BuildVaultTabs() + end + + self.display:Show() + return self.display +end + +-- vim:noet
