changeset 1:f7c747904387

adding pkgmeta
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Mon, 01 Nov 2010 18:36:46 +0000
parents a3b67ffe00a0
children fb9a91642a60
files .pkgmeta GuildDelta.toc gd.lua
diffstat 3 files changed, 551 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.pkgmeta	Mon Nov 01 18:36:46 2010 +0000
@@ -0,0 +1,19 @@
+package-as: GuildDelta
+
+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
+  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/AceConsole-3.0:
+    url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceConsole-3.0
+  libs/AceGUI-3.0:
+    url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceGUI-3.0
+
+# vim: et
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GuildDelta.toc	Mon Nov 01 18:36:46 2010 +0000
@@ -0,0 +1,18 @@
+## Interface: 40000
+## Title: Guild Deltas
+## Version: 4.0
+## Notes: On login, displays changes to your guild since the last time you logged in.
+## Author: Farmbuyer of Kilrogg
+## SavedVariables: GuildDeltaSV
+## OptionalDeps: Ace3
+
+#@no-lib-strip@
+libs\LibStub\LibStub.lua
+libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
+libs\AceAddon-3.0\AceAddon-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
+#@end-no-lib-strip@
+
+gd.lua
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gd.lua	Mon Nov 01 18:36:46 2010 +0000
@@ -0,0 +1,514 @@
+
+local addon = CreateFrame("Frame") --select(2,...)
+local SV
+
+local DEFAULT_CHAT = 2   -- combat log (no constant predefined for that)
+
+local l10n
+--if GetLocale() == "enUS" then
+	l10n= {
+		["LEFT"] = "Players who have left the guild:  ",
+		["JOINED"] = "Players who have joined the guild:  ",
+		["RANK"] = "Players whose rank has changed:",
+		["LEVEL"] = "Players whose level has changed:",
+		["NOTES"] = "Players whose notes have changed:",
+
+		["Oabbrev"] = "O",       -- abbreviation for "Officer", specifically the non-public note
+		["FIELD_rank"] = "Rank",
+		["FIELD_level"] = "Level",
+		["FIELD_notes"] = "Player/Officer notes",
+
+		-- Something needs to be done about the walls of description text below.
+	}
+--elseif .... end
+
+local guild_selection, log_ever_shown
+addon.options = {
+	name = "Guild Delta",
+	type = 'group',
+	handler = addon,    -- functions listed as strings called as addon:func
+	args = {
+		version = {
+			--name = filled in during OnEnable
+			type = 'description',
+			fontSize = "large",
+			--image = "Interface\\AddOns\\GuildDelta\\test2",
+			cmdHidden = true,
+			order = 1,
+		},
+		note = {
+			--name = filled in locals section
+			type = 'description',
+			cmdHidden = true,
+			width = 'full',
+			order = 2,
+		},
+		reset = {
+			name = "Reset Output",
+			desc = "Restores default output settings",
+			type = 'execute',
+			order = 5,
+			func = "SetChat",
+			arg = DEFAULT_CHAT,
+		},
+		spacer2 = {
+			name = '',
+			type = 'description',
+			cmdHidden = true,
+			width = 'full',
+			order = 6,
+		},
+		print_chatframes = {
+			name = "Print Chatframe Numbers",
+			desc = "Print each chat window number in its frame, for easy reference in the next slider option",
+			type = 'execute',
+			--func = print_chatframes filled in below
+			order = 10,
+		},
+		chatframe_num = {
+			name = "Output Chatframe",
+			desc = "Which chat window to use for printing all the output during login",
+			type = 'range',
+			min  = 1,
+			max  = NUM_CHAT_WINDOWS,
+			step = 1,
+			get = function() return tonumber(SV.chatframe) or --[[in case of custom name]]DEFAULT_CHAT end,
+			set = "SetChat",
+			order = 15,
+		},
+		chatframe_name = {
+			name = "Chatframe Override",
+			desc = "<Advanced>  If blank, uses the numerical slider.  If set, it is the NAME of a frame with AddMessage capability to use for output.",
+			type = 'input',
+			get = function()
+				return type(SV.chatframe) == 'string' and SV.chatframe or nil
+			end,
+			set = "SetChat",
+			order = 16,
+		},
+		fields = {
+			name = "Fields",
+			desc = "Track changes to these player fields",
+			type = 'multiselect',
+			order = 20,
+			-- these need to be of function type rather than string keys of members
+			values = function(info) return addon:MakeFieldList() end,
+			get = function(info,x) return SV.fields[x] end,
+			set = function(info,x,val) SV.fields[x] = val end,
+		},
+		spacer1 = {
+			name = '',
+			type = 'description',
+			cmdHidden = true,
+			width = 'full',
+			order = 29,
+		},
+		guilds = {
+			name = "Guilds",
+			desc = "Guilds for which a roster is known",
+			type = 'select',
+			order = 30,
+			width = 'double',
+			values = function(info) return addon:MakeGuildList() end,
+			get = function(info) return guild_selection end,
+			set = function(info,val) guild_selection = val end,
+		},
+		clearguild = {
+			name = "Reset Guild",
+			desc = "Erase stored data for selected guild; information will be scanned from scratch on next login.",
+			type = 'execute',
+			order = 32,
+			disabled = function() return not guild_selection end,
+			func = function()
+				assert(type(guild_selection)=='string')
+				local g,r = guild_selection:match("<([^>]+)> %- (.*)")
+				local m = SV.members[r]
+				if m then
+					m[g] = nil
+				else
+					addon:Print("Hm, error.", r, "can't be matched as a realm name.  Please report this as a bug, including the name of the realm and guild.")
+				end
+			end,
+		},
+		persist = {
+			name = "Logging",
+			type = 'group',
+			inline = true,
+			order = 60,
+			args = {
+				note = {
+					--name = filled in locals section
+					type = 'description',
+					cmdHidden = true,
+					width = 'full',
+					order = 1,
+				},
+				enable = {
+					name = "Enable Logging",
+					desc = [[Accumulate all deltas, including a timestamp.  |cffFF0000WARNING|r: if logging was enabled and you turn it off, the log itself will not be saved when exiting the game.]],
+					type = 'toggle',
+					order = 2,
+					get = function() return SV.logging end,
+					set = function(i,v)
+						SV.logging = v
+						log_ever_shown = SV.logging or log_ever_shown
+					end,
+				},
+				clearlog = {
+					name = "Reset Log",
+					desc = "Erase accumulated deltas.",
+					type = 'execute',
+					order = 3,
+					hidden = function() return not (SV.logging or log_ever_shown) end,
+					disabled = function() return not SV.logging end,
+					func = function()
+						SV.logtext = nil
+					end,
+				},
+				log = {
+					name = "Log",
+					desc = "If you make changes, don't forget to click 'Accept' to save them.",
+					type = 'input',
+					order = 10,
+					multiline = 15,
+					width = 'full',
+					hidden = function() return not (SV.logging or log_ever_shown) end,
+					disabled = function() return not SV.logging end,
+					get = function() return SV.logtext end,
+					set = function(i,t) SV.logtext = t end,
+				},
+			},
+		},
+	}
+}
+
+
+-----------------------------------------------------------------------------
+-- other locals
+local tinsert, GetGuildRosterInfo = _G.table.insert, _G.GetGuildRosterInfo
+local chatframe, fieldlist
+local function prt (...)
+	return chatframe:AddMessage(...)
+end
+local function cprt (txt)
+	if SV.logging then addon:AddLogNote(txt) end
+	return prt(txt, 255/255, 26/255, 160/255)
+end
+
+function addon:current_guild_info (N)
+	local ret = {}
+	for i = 1, N do
+		local name,rank_as_string,_,level,_,_,publicnote,officernote = GetGuildRosterInfo(i)
+		-- This technically does not work out to the same as A?B:C, combined
+		-- with the logic below.  It does, however, still result in the entry
+		-- not appearing in the returned table.
+		publicnote = publicnote ~= "" and publicnote or nil
+		officernote = officernote ~= "" and officernote or nil
+		if name then -- redundant, but apparently happens on extreme lag
+			tinsert(ret,
+				{name   = name,
+				 rank   = SV.fields.rank and rank_as_string or nil,
+				 level  = SV.fields.level and level or nil,
+				 pnote  = SV.fields.notes and publicnote or nil,
+				 onote  = SV.fields.notes and officernote or nil,
+				})
+		end
+	end
+	table.sort(ret, function (l,r) return l.name < r.name end)
+	return ret
+end
+
+function addon.options.args.print_chatframes.func()
+	for i = 1, NUM_CHAT_WINDOWS do
+		local cf = _G["ChatFrame"..i]
+		if not cf then break end
+		addon:Print(cf, "This is frame number", i)
+	end
+end
+
+addon.options.args.note.name = 
+"You can use the '/guilddelta' command to open the options window.\n\n"..
+"The guild roster has already been scanned by the time you see this.  Therefore, "..
+"if you make any changes to the Fields section below, you should probably relog "..
+"immediately to begin tracking the changed fields.  Changes to the contents *OF* "..
+"those fields will not be noticed until the first login after *that*.\n\n"
+
+addon.options.args.persist.args.note.name = 
+"Enabling logging will accumulate the text of the 'deltas' as you see them.  "..
+"This can grow large over time, depending on the activity of your guilds, so you "..
+"should use the Reset Log button below from time to time.\n\n"..
+"If you click inside the text area below, you can use Control-A to select all "..
+"the text, and Control-C to copy it to your computer's clipboard.  If you make "..
+"any changes to the text, they will be preserved.  (You can remove uninteresting "..
+"changes, add reminders to yourself, and so forth.)\n\n"
+
+
+-----------------------------------------------------------------------------
+addon = LibStub("AceAddon-3.0"):NewAddon(addon, "GuildDelta",
+		        "AceConsole-3.0")
+
+function addon:OnInitialize()
+	if _G.GuildDeltaSV == nil then
+		-- Defaults need to transition from potential older savedvars
+		_G.GuildDeltaSV = {
+			chatframe = _G.GuildDelta_chatframe or DEFAULT_CHAT,
+			fields = _G.GuildDelta_fields or { rank = true, level = false, notes = true },
+			members = _G.GuildDelta_memberdata or {}
+		}
+	end
+	SV = _G.GuildDeltaSV
+end
+
+-- Remove anything that normal operation doesn't need after finishing its work.
+function addon:cleanup()
+	prt = nil; cprt = nil
+	self.current_guild_info = nil
+	self.cleanup = nil
+	self.unload = nil
+	self.logquay = nil
+	self.AddLogNote = nil
+	self.FinishLog = nil
+end
+function addon:unload()
+	self:cleanup()
+	LibStub("AceAddon-3.0").addons["GuildDelta"] = nil
+	l10n = nil; addon = nil;
+	-- put the userdata back so it counts as a Frame object again
+	local ud = self[0]
+	table.wipe(self)
+	self[0] = ud
+end
+
+function addon:OnEnable()
+	if not IsInGuild() then
+		self:Print("You are not in a guild, not loading.")
+		return self:unload()
+	end
+
+	AutoCompleteInfoDelayer:HookScript("OnFinished",
+		function() self:RegisterEvent("GUILD_ROSTER_UPDATE") end)
+	self:SetScript("OnEvent", self.GuildUpdate)
+
+	self.options.args.version.name =
+		"|cff30adffVersion " .. (GetAddOnMetadata("GuildDelta", "Version") or "?") .. "|r"
+	LibStub("AceConfig-3.0"):RegisterOptionsTable("GuildDelta", self.options)
+	--[[self.optionsFrame =]] LibStub("AceConfigDialog-3.0"):AddToBlizOptions("GuildDelta", "Guild Delta")
+	self:RegisterChatCommand("guilddelta", "OnChatCommand")
+	self:SetChat(false)
+	log_ever_shown = SV.logging
+	self.OnEnable = nil
+end
+
+
+function addon:OnChatCommand (input)
+	if not input or input:trim() == "" then
+		LibStub("AceConfigDialog-3.0"):Open("GuildDelta")
+	else
+		LibStub("AceConfigCmd-3.0").HandleCommand(self, "guilddelta", "GuildDelta", input)
+	end
+end
+
+
+-- 0 args:  called during startup to initialize
+-- 1 arg:   resetting via menus to defaults (see arg field)
+-- 2 args:  setting via menus to new value, number or custom name
+function addon:SetChat (info, value)
+	local n, nframe
+	if info then  -- coming via menu
+		n = info.arg or value
+	else
+		n = SV.chatframe
+	end
+	if type(n) == 'number' then
+		nframe = _G["ChatFrame"..n]
+	else
+		nframe = _G[n]
+	end
+	if type(nframe) == 'table' and type(nframe.AddMessage) == 'function' then
+		if type(info) ~= 'boolean' then
+			self:Print("Now printing to chat frame", n,
+				(type(nframe.name)=='string' and ("(".. nframe.name .. ")") or ""))
+		end
+		SV.chatframe = n
+		chatframe = nframe
+	else
+		self:Printf("EEEEEEEK!  '%s' was not a valid chat frame number/name, no change has been made.", n)
+	end
+end
+
+
+-- Not a "normal" PLAYER_LOGOUT handler; this only fires if the player is in
+-- a guild and the update has already run.
+function addon:PLAYER_LOGOUT()
+	if not SV.logging then
+		SV.logtext = nil
+	end
+end
+
+
+function addon:GuildUpdate()
+	local current_n = GetNumGuildMembers(true)
+	if current_n <= 0 then
+		-- catch the hell up, servers...
+		return GuildRoster()
+	end
+	self:UnregisterEvent("GUILD_ROSTER_UPDATE")
+	self:SetScript("OnEvent", self.PLAYER_LOGOUT) -- keepin' it real^H^H^H^Hsmall and kludgey
+	self:RegisterEvent("PLAYER_LOGOUT")
+	self.GuildUpdate = nil
+
+	local guild, realm = (GetGuildInfo("player")), GetRealmName()
+	local members = SV.members
+	if members[realm] 
+	   and members[realm][guild]
+	   and #(members[realm][guild]) > 0
+	then
+		-- moved the normal case below
+	else
+		-- new user, or new guild, or any number of things
+		self:Print("GuildDelta initializing roster...")
+		members[realm] = members[realm] or {}
+		members[realm][guild] = self:current_guild_info(current_n)
+		return
+	end
+
+	-- table.insert with notes if available
+	-- concatentation of all strings faster than string.format
+	local function tins (t, x)
+		local s = x.name
+		if x.onote and (x.onote ~= "") then
+			s = s .. "(" .. l10n.Oabbrev .. ": " .. x.onote .. ")"
+		end
+		if x.pnote and (x.pnote ~= "") then
+			s = s .. "(" .. x.pnote .. ")"
+		end
+		tinsert(t, s)
+	end
+
+	-- build the current list
+	local previous, current = members[realm][guild],
+							  self:current_guild_info(current_n)
+	local previous_n = #previous
+
+	-- walk both and do equivalence comparison
+	local joined, left, rank, level, notes = {}, {}, {}, {}, {}
+	local p, c = 1, 1
+	while p <= previous_n and c <= current_n do
+		local P, C = previous[p], current[c]
+
+		if P.name == C.name then
+			-- normal case
+			p = p + 1
+			c = c + 1
+			-- but can now compare details
+			if C.rank and P.rank and (P.rank ~= C.rank) then
+				tinsert(rank, {C.name, P.rank.." --> "..C.rank})
+			end
+			if C.level and P.level and (P.level ~= C.level) then
+				tinsert(level, {C.name, P.level.." --> "..C.level})
+			end
+			if C.pnote and (P.pnote ~= C.pnote) then
+				tinsert(notes, {C.name, C.pnote})
+			end
+			if C.onote and (P.onote ~= C.onote) then
+				tinsert(notes, {C.name, "["..l10n.Oabbrev.."]: "..C.onote})
+			end
+
+		elseif P.name < C.name then
+			-- entry at index p not at c -> somebody has left
+			tins (left, P)
+			p = p + 1
+
+		else
+			-- entry at index c not at p -> somebody has joined
+			tins (joined, C)
+			c = c + 1
+		end
+	end
+
+	-- leftovers
+	for i = p, previous_n do
+		tins (left, previous[i])
+	end
+	for i = c, current_n do
+		tins (joined, current[i])
+	end
+
+	-- show results
+	if SV.logging then self.logquay = {} end
+	local m
+	if #left > 0 then
+		m = l10n.LEFT .. table.concat(left, ", ")
+		cprt(m)
+	end
+
+	if #joined > 0 then
+		m = l10n.JOINED .. table.concat(joined, ", ")
+		cprt(m)
+	end
+
+	if #rank > 0 then
+		cprt(l10n.RANK)
+		for i = 1, #rank do
+			cprt(rank[i][1]..':  '..rank[i][2])
+		end
+	end
+
+	if #level > 0 then
+		cprt(l10n.LEVEL)
+		for i = 1, #level do
+			cprt(level[i][1]..':  '..level[i][2])
+		end
+	end
+
+	if #notes > 0 then
+		cprt(l10n.NOTES)
+		for i = 1, #notes do
+			cprt(notes[i][1]..': "'..notes[i][2]..'"')
+		end
+	end
+
+	if SV.logging then self:FinishLog(guild,realm) end
+	members[realm][guild] = current
+end
+
+
+function addon:MakeFieldList()
+	if not fieldlist then
+		fieldlist = {}
+		for name in pairs(SV.fields) do
+			fieldlist[name] = l10n["FIELD_"..name]
+		end
+	end
+	return fieldlist
+end
+
+function addon:MakeGuildList()
+	local list = {}
+	local K
+	for rname,rdata in pairs(SV.members) do
+		for g in pairs(rdata) do
+			K = ("<%s> - %s"):format(g,rname)
+			list[K] = K
+		end
+	end
+	return list
+end
+
+function addon:AddLogNote (txt)
+	tinsert(self.logquay,txt)
+end
+
+function addon:FinishLog(g,r)
+	if #self.logquay > 0 then
+		Calendar_LoadUI()
+		local _,M,D,Y = CalendarGetDate()
+		local h,m = GetGameTime()
+		local timestamp = ("%.4d/%.2d/%.2d %.2d:%.2d    <%s> - %s\n"):format(Y,M,D,h,m,g,r)
+		SV.logtext = timestamp
+		             .. table.concat(self.logquay, '\n')
+		             .. (SV.logtext and ('\n\n'..SV.logtext) or '')
+	end
+end
+
+-- vim:noet