# HG changeset patch
# User yellowfive
# Date 1433527515 25200
# Node ID 01b63b8ed81107abf9d76f559bd8ff1e113b8951
# Parent 75431c084aa0c50ea16807e169df83aab7b41ace
total rewrite to version 21
diff -r 75431c084aa0 -r 01b63b8ed811 AskMrRobot-Serializer/AskMrRobot-Serializer.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/AskMrRobot-Serializer/AskMrRobot-Serializer.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,1061 @@
+-- AskMrRobot-Serializer will serialize and communicate character data between users.
+-- This is used primarily to associate character information to logs uploaded to askmrrobot.com.
+
+local MAJOR, MINOR = "AskMrRobot-Serializer", 21
+local Amr, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not Amr then return end -- already loaded by something else
+
+-- event and comm used for player snapshotting on entering combat
+LibStub("AceEvent-3.0"):Embed(Amr)
+LibStub("AceComm-3.0"):Embed(Amr)
+
+----------------------------------------------------------------------------------------
+-- Constants
+----------------------------------------------------------------------------------------
+
+-- prefix used for communicating gear snapshots created by the AMR serializer
+Amr.ChatPrefix = "_AMRS"
+
+-- map of region ids to AMR region names
+Amr.RegionNames = {
+ [1] = "US",
+ [2] = "KR",
+ [3] = "EU",
+ [4] = "TW",
+ [5] = "CN"
+}
+
+-- map of the skillLine returned by profession API to the AMR profession name
+Amr.ProfessionSkillLineToName = {
+ [794] = "Archaeology",
+ [171] = "Alchemy",
+ [164] = "Blacksmithing",
+ [185] = "Cooking",
+ [333] = "Enchanting",
+ [202] = "Engineering",
+ [129] = "First Aid",
+ [356] = "Fishing",
+ [182] = "Herbalism",
+ [773] = "Inscription",
+ [755] = "Jewelcrafting",
+ [165] = "Leatherworking",
+ [186] = "Mining",
+ [393] = "Skinning",
+ [197] = "Tailoring"
+}
+
+-- all slot IDs that we care about, ordered in AMR standard display order
+Amr.SlotIds = { 16, 17, 1, 2, 3, 15, 5, 9, 10, 6, 7, 8, 11, 12, 13, 14 }
+
+Amr.SpecIds = {
+ [250] = 1, -- DeathKnightBlood
+ [251] = 2, -- DeathKnightFrost
+ [252] = 3, -- DeathKnightUnholy
+ [102] = 4, -- DruidBalance
+ [103] = 5, -- DruidFeral
+ [104] = 6, -- DruidGuardian
+ [105] = 7, -- DruidRestoration
+ [253] = 8, -- HunterBeastMastery
+ [254] = 9, -- HunterMarksmanship
+ [255] = 10, -- HunterSurvival
+ [62] = 11, -- MageArcane
+ [63] = 12, -- MageFire
+ [64] = 13, -- MageFrost
+ [268] = 14, -- MonkBrewmaster
+ [270] = 15, -- MonkMistweaver
+ [269] = 16, -- MonkWindwalker
+ [65] = 17, -- PaladinHoly
+ [66] = 18, -- PaladinProtection
+ [70] = 19, -- PaladinRetribution
+ [256] = 20, -- PriestDiscipline
+ [257] = 21, -- PriestHoly
+ [258] = 22, -- PriestShadow
+ [259] = 23, -- RogueAssassination
+ [260] = 24, -- RogueCombat
+ [261] = 25, -- RogueSubtlety
+ [262] = 26, -- ShamanElemental
+ [263] = 27, -- ShamanEnhancement
+ [264] = 28, -- ShamanRestoration
+ [265] = 29, -- WarlockAffliction
+ [266] = 30, -- WarlockDemonology
+ [267] = 31, -- WarlockDestruction
+ [71] = 32, -- WarriorArms
+ [72] = 33, -- WarriorFury
+ [73] = 34 -- WarriorProtection
+}
+
+Amr.ClassIds = {
+ ["NONE"] = 0,
+ ["DEATHKNIGHT"] = 1,
+ ["DRUID"] = 2,
+ ["HUNTER"] = 3,
+ ["MAGE"] = 4,
+ ["MONK"] = 5,
+ ["PALADIN"] = 6,
+ ["PRIEST"] = 7,
+ ["ROGUE"] = 8,
+ ["SHAMAN"] = 9,
+ ["WARLOCK"] = 10,
+ ["WARRIOR"] = 11,
+}
+
+Amr.ProfessionIds = {
+ ["None"] = 0,
+ ["Mining"] = 1,
+ ["Skinning"] = 2,
+ ["Herbalism"] = 3,
+ ["Enchanting"] = 4,
+ ["Jewelcrafting"] = 5,
+ ["Engineering"] = 6,
+ ["Blacksmithing"] = 7,
+ ["Leatherworking"] = 8,
+ ["Inscription"] = 9,
+ ["Tailoring"] = 10,
+ ["Alchemy"] = 11,
+ ["Fishing"] = 12,
+ ["Cooking"] = 13,
+ ["First Aid"] = 14,
+ ["Archaeology"] = 15
+}
+
+Amr.RaceIds = {
+ ["None"] = 0,
+ ["BloodElf"] = 1,
+ ["Draenei"] = 2,
+ ["Dwarf"] = 3,
+ ["Gnome"] = 4,
+ ["Human"] = 5,
+ ["NightElf"] = 6,
+ ["Orc"] = 7,
+ ["Tauren"] = 8,
+ ["Troll"] = 9,
+ ["Scourge"] = 10,
+ ["Undead"] = 10,
+ ["Goblin"] = 11,
+ ["Worgen"] = 12,
+ ["Pandaren"] = 13
+}
+
+Amr.FactionIds = {
+ ["None"] = 0,
+ ["Alliance"] = 1,
+ ["Horde"] = 2
+}
+
+Amr.InstanceIds = {
+ Auchindoun = 1182,
+ BloodmaulSlagMines = 1175,
+ GrimrailDepot = 1208,
+ IronDocks = 1195,
+ ShadowmoonBurialGrounds = 1176,
+ Skyreach = 1209,
+ TheEverbloom = 1279,
+ UpperBlackrockSpire = 1358,
+ Highmaul = 1228,
+ BlackrockFoundry = 1205
+}
+
+-- instances that AskMrRobot currently supports logging for
+Amr.SupportedInstanceIds = {
+ --[1182] = true,
+ --[1175] = true,
+ --[1208] = true,
+ --[1195] = true,
+ --[1176] = true,
+ --[1209] = true,
+ --[1279] = true,
+ --[1358] = true,
+ [1228] = true,
+ [1205] = true
+}
+
+Amr.SPEC_WARRIORPROTECTION = 34
+Amr.SUBSPEC_WARRIORPROTECTION = 38
+Amr.SUBSPEC_WARRIORPROTECTIONGLAD = 39
+Amr.SPELL_ID_GLADIATOR_STANCE = 156291
+Amr.SPELL_ID_DEFENSIVE_STANCE = 71
+
+-- IDs of set tokens that we would care about in a player's inventory
+Amr.SetTokenIds = {
+ [120285] = true,
+ [120284] = true,
+ [120283] = true,
+ [120282] = true,
+ [120281] = true,
+ [120280] = true,
+ [120279] = true,
+ [120278] = true,
+ [120277] = true,
+ [120256] = true,
+ [120255] = true,
+ [120254] = true,
+ [120253] = true,
+ [120252] = true,
+ [120251] = true,
+ [120250] = true,
+ [120249] = true,
+ [120248] = true,
+ [120247] = true,
+ [120246] = true,
+ [120245] = true,
+ [120244] = true,
+ [120243] = true,
+ [120242] = true,
+ [120241] = true,
+ [120240] = true,
+ [120239] = true,
+ [120238] = true,
+ [120237] = true,
+ [120236] = true,
+ [120235] = true,
+ [120234] = true,
+ [120233] = true,
+ [120232] = true,
+ [120231] = true,
+ [120230] = true,
+ [120229] = true,
+ [120228] = true,
+ [120227] = true,
+ [120226] = true,
+ [120225] = true,
+ [120224] = true,
+ [120223] = true,
+ [120222] = true,
+ [120221] = true,
+ [120220] = true,
+ [120219] = true,
+ [120218] = true,
+ [120217] = true,
+ [120216] = true,
+ [120215] = true,
+ [120214] = true,
+ [120213] = true,
+ [120212] = true,
+ [120211] = true,
+ [120210] = true,
+ [120209] = true,
+ [120208] = true,
+ [120207] = true,
+ [120206] = true,
+ [119323] = true,
+ [119322] = true,
+ [119321] = true,
+ [119320] = true,
+ [119319] = true,
+ [119318] = true,
+ [119316] = true,
+ [119315] = true,
+ [119314] = true,
+ [119313] = true,
+ [119312] = true,
+ [119311] = true,
+ [119310] = true,
+ [119309] = true,
+ [119308] = true,
+ [119307] = true,
+ [119306] = true,
+ [119305] = true,
+ [105868] = true,
+ [105867] = true,
+ [105866] = true,
+ [105865] = true,
+ [105864] = true,
+ [105863] = true,
+ [105862] = true,
+ [105861] = true,
+ [105860] = true,
+ [105859] = true,
+ [105858] = true,
+ [105857] = true,
+ [99756] = true,
+ [99755] = true,
+ [99754] = true,
+ [99753] = true,
+ [99752] = true,
+ [99751] = true,
+ [99750] = true,
+ [99749] = true,
+ [99748] = true,
+ [99747] = true,
+ [99746] = true,
+ [99745] = true,
+ [99744] = true,
+ [99743] = true,
+ [99742] = true,
+ [99740] = true,
+ [99739] = true,
+ [99738] = true,
+ [99737] = true,
+ [99736] = true,
+ [99735] = true,
+ [99734] = true,
+ [99733] = true,
+ [99732] = true,
+ [99731] = true,
+ [99730] = true,
+ [99729] = true,
+ [99728] = true,
+ [99727] = true,
+ [99726] = true,
+ [99725] = true,
+ [99724] = true,
+ [99723] = true,
+ [99722] = true,
+ [99721] = true,
+ [99720] = true,
+ [99719] = true,
+ [99718] = true,
+ [99717] = true,
+ [99716] = true,
+ [99715] = true,
+ [99714] = true,
+ [99713] = true,
+ [99712] = true,
+ [99711] = true,
+ [99710] = true,
+ [99709] = true,
+ [99708] = true,
+ [99707] = true,
+ [99706] = true,
+ [99705] = true,
+ [99704] = true,
+ [99703] = true,
+ [99702] = true,
+ [99701] = true,
+ [99700] = true,
+ [99699] = true,
+ [99698] = true,
+ [99697] = true,
+ [99696] = true,
+ [99695] = true,
+ [99694] = true,
+ [99693] = true,
+ [99692] = true,
+ [99691] = true,
+ [99690] = true,
+ [99689] = true,
+ [99688] = true,
+ [99687] = true,
+ [99686] = true,
+ [99685] = true,
+ [99684] = true,
+ [99683] = true,
+ [99682] = true,
+ [99681] = true,
+ [99680] = true,
+ [99679] = true,
+ [99678] = true,
+ [99677] = true,
+ [99676] = true,
+ [99675] = true,
+ [99674] = true,
+ [99673] = true,
+ [99672] = true,
+ [99671] = true,
+ [99670] = true,
+ [99669] = true,
+ [99668] = true,
+ [99667] = true,
+ [96701] = true,
+ [96700] = true,
+ [96699] = true,
+ [96633] = true,
+ [96632] = true,
+ [96631] = true,
+ [96625] = true,
+ [96624] = true,
+ [96623] = true,
+ [96601] = true,
+ [96600] = true,
+ [96599] = true,
+ [96568] = true,
+ [96567] = true,
+ [96566] = true,
+ [95957] = true,
+ [95956] = true,
+ [95955] = true,
+ [95889] = true,
+ [95888] = true,
+ [95887] = true,
+ [95881] = true,
+ [95880] = true,
+ [95879] = true,
+ [95857] = true,
+ [95856] = true,
+ [95855] = true,
+ [95824] = true,
+ [95823] = true,
+ [95822] = true,
+ [95583] = true,
+ [95582] = true,
+ [95581] = true,
+ [95580] = true,
+ [95579] = true,
+ [95578] = true,
+ [95577] = true,
+ [95576] = true,
+ [95575] = true,
+ [95574] = true,
+ [95573] = true,
+ [95572] = true,
+ [95571] = true,
+ [95570] = true,
+ [95569] = true,
+ [89278] = true,
+ [89277] = true,
+ [89276] = true,
+ [89275] = true,
+ [89274] = true,
+ [89273] = true,
+ [89272] = true,
+ [89271] = true,
+ [89270] = true,
+ [89269] = true,
+ [89268] = true,
+ [89267] = true,
+ [89266] = true,
+ [89265] = true,
+ [89264] = true,
+ [89263] = true,
+ [89262] = true,
+ [89261] = true,
+ [89260] = true,
+ [89259] = true,
+ [89258] = true,
+ [89257] = true,
+ [89256] = true,
+ [89255] = true,
+ [89254] = true,
+ [89253] = true,
+ [89252] = true,
+ [89251] = true,
+ [89250] = true,
+ [89249] = true,
+ [89248] = true,
+ [89247] = true,
+ [89246] = true,
+ [89245] = true,
+ [89244] = true,
+ [89243] = true,
+ [89242] = true,
+ [89241] = true,
+ [89240] = true,
+ [89239] = true,
+ [89238] = true,
+ [89237] = true,
+ [89236] = true,
+ [89235] = true,
+ [89234] = true,
+ [78876] = true,
+ [78875] = true,
+ [78874] = true,
+ [78873] = true,
+ [78872] = true,
+ [78871] = true,
+ [78867] = true,
+ [78866] = true,
+ [78865] = true,
+ [78864] = true,
+ [78863] = true,
+ [78862] = true,
+ [78861] = true,
+ [78860] = true,
+ [78859] = true,
+ [78858] = true,
+ [78857] = true,
+ [78856] = true,
+ [78855] = true,
+ [78854] = true,
+ [78853] = true,
+ [78849] = true,
+ [78848] = true,
+ [78847] = true,
+ [78184] = true,
+ [78183] = true,
+ [78181] = true,
+ [78180] = true,
+ [78179] = true,
+ [78178] = true,
+ [78176] = true,
+ [78175] = true,
+ [78174] = true,
+ [78173] = true,
+ [78171] = true,
+ [78170] = true,
+ [71687] = true,
+ [71686] = true,
+ [71685] = true,
+ [71683] = true,
+ [71682] = true,
+ [71680] = true,
+ [71679] = true,
+ [71678] = true,
+ [71676] = true,
+ [71675] = true,
+ [71673] = true,
+ [71672] = true,
+ [71671] = true,
+ [71669] = true,
+ [71668] = true,
+ [67431] = true,
+ [67430] = true,
+ [67429] = true,
+ [67428] = true,
+ [67427] = true,
+ [67426] = true,
+ [67425] = true,
+ [67424] = true,
+ [67423] = true,
+ [66998] = true,
+ [65089] = true,
+ [65088] = true,
+ [65087] = true,
+ [63684] = true,
+ [63683] = true,
+ [63682] = true,
+ [45652] = true,
+ [45651] = true,
+ [45650] = true,
+ [45649] = true,
+ [45648] = true,
+ [45647] = true,
+ [45643] = true,
+ [45642] = true,
+ [45641] = true,
+ [40630] = true,
+ [40629] = true,
+ [40628] = true,
+ [40621] = true,
+ [40620] = true,
+ [40619] = true,
+ [40618] = true,
+ [40617] = true,
+ [40616] = true,
+ [34544] = true,
+ [31100] = true,
+ [31099] = true,
+ [31098] = true,
+ [31097] = true,
+ [31096] = true,
+ [31095] = true,
+ [30247] = true,
+ [30246] = true,
+ [30245] = true,
+ [30244] = true,
+ [30243] = true,
+ [30242] = true,
+ [29767] = true,
+ [29766] = true,
+ [29765] = true,
+ [29761] = true,
+ [29760] = true,
+ [29759] = true
+}
+
+
+----------------------------------------------------------------------------------------
+-- Public Utility Methods
+----------------------------------------------------------------------------------------
+
+-- item link format: |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:upgradeId:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r
+-- get an object with all of the parts of the item link format that we care about
+function Amr.ParseItemLink(itemLink)
+ if not itemLink then return nil end
+
+ local str = string.match(itemLink, "|Hitem:([\-%d:]+)|")
+ if not str then return nil end
+
+ local parts = { strsplit(":", str) }
+
+ local item = {}
+ item.id = tonumber(parts[1])
+ item.enchantId = tonumber(parts[2])
+ item.gemIds = { tonumber(parts[3]), tonumber(parts[4]), tonumber(parts[5]), tonumber(parts[6]) }
+ item.suffixId = math.abs(tonumber(parts[7])) -- convert suffix to positive number, that's what we use in our code
+ --item.uniqueId = tonumber(parts[8])
+ --item.level = tonumber(parts[9])
+ item.upgradeId = tonumber(parts[10])
+ --item.difficultyId = tonumber(parts[11])
+
+ local numBonuses = tonumber(parts[12])
+ if numBonuses and numBonuses > 0 then
+ item.bonusIds = {}
+ for i = 13, 12 + numBonuses do
+ table.insert(item.bonusIds, tonumber(parts[i]))
+ end
+ table.sort(item.bonusIds)
+ end
+
+ return item
+end
+
+-- returns true if this is an instance that AskMrRobot supports for logging
+function Amr.IsSupportedInstanceId(instanceMapID)
+ if Amr.SupportedInstanceIds[tonumber(instanceMapID)] then
+ return true
+ else
+ return false
+ end
+end
+
+-- returns true if currently in a supported instance for logging
+function Amr.IsSupportedInstance()
+ local zone, _, difficultyIndex, _, _, _, _, instanceMapID = GetInstanceInfo()
+ return Amr.IsSupportedInstanceId(instanceMapID)
+end
+
+
+----------------------------------------------------------------------------------------
+-- Character Reading
+----------------------------------------------------------------------------------------
+
+local function readProfessionInfo(prof, ret)
+ if prof then
+ local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof);
+ if Amr.ProfessionSkillLineToName[skillLine] ~= nil then
+ ret.Professions[Amr.ProfessionSkillLineToName[skillLine]] = skillLevel;
+ end
+ end
+end
+
+local function getSpecId(specGroup)
+ local spec = GetSpecialization(false, false, specGroup);
+ return spec and GetSpecializationInfo(spec);
+end
+
+local function getTalents(specGroup)
+ local talentInfo = {}
+ local maxTiers = 7
+ for tier = 1, maxTiers do
+ for col = 1, 3 do
+ local id, name, texture, selected, available = GetTalentInfo(tier, col, specGroup)
+ if selected then
+ talentInfo[tier] = col
+ end
+ end
+ end
+
+ local str = ""
+ for i = 1, maxTiers do
+ if talentInfo[i] then
+ str = str .. talentInfo[i]
+ else
+ str = str .. '0'
+ end
+ end
+
+ return str
+end
+
+local function getGlyphs(specGroup)
+ local glyphs = {}
+ for i = 1, NUM_GLYPH_SLOTS do
+ local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, specGroup)
+ if (glyphID) then
+ table.insert(glyphs, glyphSpellID)
+ end
+ end
+ return glyphs;
+end
+
+-- get specs, talents, and glyphs
+local function readSpecs(ret, subspecs)
+
+ for group = 1, GetNumSpecGroups() do
+ -- spec, convert game spec id to one of our spec ids
+ local specId = getSpecId(group)
+ if specId then
+ ret.Specs[group] = Amr.SpecIds[specId]
+
+ -- if this is a protection warrior, use buffs to determine subspec
+ if ret.Specs[group] == Amr.SPEC_WARRIORPROTECTION then
+ local subspec = 0
+
+ if ret.ActiveSpec ~= group then
+ -- this spec isn't active, so we can't use current buffs to determine spec, see if any old data is compatible
+ if subspecs and (subspecs[group] == Amr.SUBSPEC_WARRIORPROTECTION or subspecs[group] == Amr.SUBSPEC_WARRIORPROTECTIONGLAD) then
+ subspec = subspecs[group]
+ end
+ else
+ for i=1,40 do
+ local name,_,_,_,_,_,_,_,_,_,spellId = UnitAura("player", i, "HELPFUL")
+ if not name then break end
+
+ if spellId == Amr.SPELL_ID_DEFENSIVE_STANCE then
+ subspec = Amr.SUBSPEC_WARRIORPROTECTION
+ break
+ elseif spellId == Amr.SPELL_ID_GLADIATOR_STANCE then
+ subspec = Amr.SUBSPEC_WARRIORPROTECTIONGLAD
+ break
+ end
+ end
+ end
+
+ if subspec == 0 then
+ ret.SubSpecs[group] = nil
+ else
+ ret.SubSpecs[group] = subspec
+ end
+ end
+ else
+ ret.Specs[group] = 0
+ end
+
+ ret.Talents[group] = getTalents(group)
+ ret.Glyphs[group] = getGlyphs(group)
+ end
+end
+
+-- get currently equipped items, store with currently active spec
+local function readEquippedItems(ret)
+ local equippedItems = {};
+ for slotNum = 1, #Amr.SlotIds do
+ local slotId = Amr.SlotIds[slotNum]
+ local itemLink = GetInventoryItemLink("player", slotId)
+ if itemLink then
+ equippedItems[slotId] = itemLink
+ end
+ end
+
+ -- store last-seen equipped gear for each spec
+ ret.Equipped[GetActiveSpecGroup()] = equippedItems
+end
+
+-- Get all data about the player as an object, includes:
+-- serializer version
+-- region/realm/name
+-- guild
+-- race
+-- faction
+-- level
+-- professions
+-- spec/talent/glyphs for both specs
+-- equipped gear for the current spec
+--
+function Amr:GetPlayerData(subspecs)
+
+ local ret = {}
+
+ ret.Region = Amr.RegionNames[GetCurrentRegion()]
+ ret.Realm = GetRealmName()
+ ret.Name = UnitName("player")
+ ret.Guild = GetGuildInfo("player")
+ ret.ActiveSpec = GetActiveSpecGroup()
+ ret.Level = UnitLevel("player");
+
+ local cls, clsEn = UnitClass("player")
+ ret.Class = clsEn;
+
+ local race, raceEn = UnitRace("player")
+ ret.Race = raceEn;
+ ret.Faction = UnitFactionGroup("player")
+
+ ret.Professions = {};
+ local prof1, prof2, archaeology, fishing, cooking, firstAid = GetProfessions();
+ readProfessionInfo(prof1, ret)
+ readProfessionInfo(prof2, ret)
+ readProfessionInfo(archaeology, ret)
+ readProfessionInfo(fishing, ret)
+ readProfessionInfo(cooking, ret)
+ readProfessionInfo(firstAid, ret)
+
+ ret.Specs = {}
+ ret.SubSpecs = {} -- only filled in for ambiguous cases, right now just prot/glad warrior
+ ret.Talents = {}
+ ret.Glyphs = {}
+ readSpecs(ret, subspecs)
+
+ ret.Equipped = {}
+ readEquippedItems(ret)
+
+ return ret
+end
+
+
+----------------------------------------------------------------------------------------
+-- Serialization
+----------------------------------------------------------------------------------------
+
+local function toCompressedNumberList(list)
+ -- ensure the values are numbers, sorted from lowest to highest
+ local nums = {}
+ for i, v in ipairs(list) do
+ table.insert(nums, tonumber(v))
+ end
+ table.sort(nums)
+
+ local ret = {}
+ local prev = 0
+ for i, v in ipairs(nums) do
+ local diff = v - prev
+ table.insert(ret, diff)
+ prev = v
+ end
+
+ return table.concat(ret, ",")
+end
+
+-- make this utility publicly available
+function Amr:ToCompressedNumberList(list)
+ return toCompressedNumberList(list)
+end
+
+-- appends a list of items to the export
+local function appendItemsToExport(fields, itemObjects)
+
+ -- sort by item id so we can compress it more easily
+ table.sort(itemObjects, function(a, b) return a.id < b.id end)
+
+ -- append to the export string
+ local prevItemId = 0
+ local prevGemId = 0
+ local prevEnchantId = 0
+ local prevUpgradeId = 0
+ local prevBonusId = 0
+ for i, itemData in ipairs(itemObjects) do
+ local itemParts = {}
+
+ table.insert(itemParts, itemData.id - prevItemId)
+ prevItemId = itemData.id
+
+ if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end
+ if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end
+ if itemData.upgradeId ~= 0 then
+ table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId))
+ prevUpgradeId = itemData.upgradeId
+ end
+ if itemData.bonusIds then
+ for bIndex, bValue in ipairs(itemData.bonusIds) do
+ table.insert(itemParts, "b" .. (bValue - prevBonusId))
+ prevBonusId = bValue
+ end
+ end
+ if itemData.gemIds[1] ~= 0 then
+ table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId))
+ prevGemId = itemData.gemIds[1]
+ end
+ if itemData.gemIds[2] ~= 0 then
+ table.insert(itemParts, "y" .. (itemData.gemIds[2] - prevGemId))
+ prevGemId = itemData.gemIds[2]
+ end
+ if itemData.gemIds[3] ~= 0 then
+ table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId))
+ prevGemId = itemData.gemIds[3]
+ end
+ if itemData.enchantId ~= 0 then
+ table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId))
+ prevEnchantId = itemData.enchantId
+ end
+
+ table.insert(fields, table.concat(itemParts, ""))
+ end
+end
+
+-- Serialize just the identity portion of a player (region/realm/name) in the same format used by the full serialization
+function Amr:SerializePlayerIdentity(data)
+ local fields = {}
+ table.insert(fields, MINOR)
+ table.insert(fields, data.Region)
+ table.insert(fields, data.Realm)
+ table.insert(fields, data.Name)
+ return "$" .. table.concat(fields, ";") .. "$"
+end
+
+-- Serialize player data gathered by GetPlayerData. This can be augmented with extra data if desired (augmenting used mainly by AskMrRobot addon).
+-- Pass complete = true to do a complete export of this extra information, otherwise it is ignored.
+-- Extra data can include:
+-- equipped gear for the player's inactive spec, slot id to item link dictionary
+-- Reputations
+-- BagItems, BankItems, VoidItems, lists of item links
+--
+function Amr:SerializePlayerData(data, complete)
+
+ local fields = {}
+
+ -- compressed string uses a fixed order rather than inserting identifiers
+ table.insert(fields, MINOR)
+ table.insert(fields, data.Region)
+ table.insert(fields, data.Realm)
+ table.insert(fields, data.Name)
+
+ -- guild name
+ if data.Guild == nil then
+ table.insert(fields, "")
+ else
+ table.insert(fields, data.Guild)
+ end
+
+ -- race, default to pandaren if we can't read it for some reason
+ local raceval = Amr.RaceIds[data.Race]
+ if raceval == nil then raceval = 13 end
+ table.insert(fields, raceval)
+
+ -- faction, default to alliance if we can't read it for some reason
+ raceval = Amr.FactionIds[data.Faction]
+ if raceval == nil then raceval = 1 end
+ table.insert(fields, raceval)
+
+ table.insert(fields, data.Level)
+
+ local profs = {}
+ local noprofs = true
+ if data.Professions then
+ for k, v in pairs(data.Professions) do
+ local profval = Amr.ProfessionIds[k]
+ if profval ~= nil then
+ noprofs = false
+ table.insert(profs, profval .. ":" .. v)
+ end
+ end
+ end
+
+ if noprofs then
+ table.insert(profs, "0:0")
+ end
+
+ table.insert(fields, table.concat(profs, ","))
+
+ -- export specs
+ table.insert(fields, data.ActiveSpec)
+ for spec = 1, 2 do
+ if data.Specs[spec] and (complete or spec == data.ActiveSpec) then
+ table.insert(fields, ".s" .. spec) -- indicates the start of a spec block
+
+ -- we use subspec for some ambiguous specs like prot/glad warrior
+ if data.SubSpecs[spec] then
+ table.insert(fields, string.format("s%s", data.SubSpecs[spec]))
+ else
+ table.insert(fields, data.Specs[spec])
+ end
+
+ table.insert(fields, data.Talents[spec])
+ table.insert(fields, toCompressedNumberList(data.Glyphs[spec]))
+ end
+ end
+
+ -- export equipped gear
+ if data.Equipped then
+ for spec = 1, 2 do
+ if data.Equipped[spec] and (complete or spec == data.ActiveSpec) then
+ table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block
+
+ local itemObjects = {}
+ for k, v in pairs(data.Equipped[spec]) do
+ local itemData = Amr.ParseItemLink(v)
+ itemData.slot = k
+ table.insert(itemObjects, itemData)
+ end
+
+ appendItemsToExport(fields, itemObjects)
+ end
+ end
+ end
+
+ -- if doing a complete export, include reputations and bank/bag items too
+ if complete then
+
+ -- export reputations
+ local reps = {}
+ local noreps = true
+ if data.Reputations then
+ for k, v in pairs(data.Reputations) do
+ noreps = false
+ table.insert(reps, k .. ":" .. v)
+ end
+ end
+ if noreps then
+ table.insert(reps, "_")
+ end
+
+ table.insert(fields, ".r")
+ table.insert(fields, table.concat(reps, ","))
+
+ -- export bag and bank
+ local itemObjects = {}
+ if data.BagItems then
+ for i, v in ipairs(data.BagItems) do
+ local itemData = Amr.ParseItemLink(v)
+ if itemData ~= nil and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id]) then
+ table.insert(itemObjects, itemData)
+ end
+ end
+ end
+ if data.BankItems then
+ for i, v in ipairs(data.BankItems) do
+ local itemData = Amr.ParseItemLink(v)
+ if itemData ~= nil and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id]) then
+ table.insert(itemObjects, itemData)
+ end
+ end
+ end
+ if data.VoidItems then
+ for i, v in ipairs(data.VoidItems) do
+ local itemData = Amr.ParseItemLink(v)
+ if itemData ~= nil and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id]) then
+ table.insert(itemObjects, itemData)
+ end
+ end
+ end
+
+ table.insert(fields, ".inv")
+ appendItemsToExport(fields, itemObjects)
+ end
+
+ return "$" .. table.concat(fields, ";") .. "$"
+
+end
+
+-- Shortcut for the common use case: serialize the player's currently active setup with no extras.
+function Amr:SerializePlayer()
+ local data = self:GetPlayerData()
+ return self:SerializePlayerData(data)
+end
+
+
+----------------------------------------------------------------------------------------------------------------------
+-- Character Snapshots
+-- This feature snapshots a player's gear/talents/glyphs when entering combat. It is enabled by default. Consumers
+-- of this library can create a setting to enable/disable it as desired per a user setting.
+--
+-- You should register for the AMR_SNAPSHOT_STATE_CHANGED message (sent via AceEvent-3.0 messaging) to ensure that
+-- your addon settings stay in sync with any other addon that may also be trying to control the enabled state.
+--
+-- Note that if a user has the main AMR addon installed, it will always enable snapshotting, and override any attempt
+-- to disable it by immediately re-enabling it and thus re-triggering AMR_SNAPSHOT_STATE_CHANGED.
+----------------------------------------------------------------------------------------------------------------------
+Amr._snapshotEnabled = true
+
+-- Enable snapshotting of character data when entering combat. Sends this player's character data to anyone logging with the AskMrRobot addon.
+function Amr:EnableSnapshots()
+ self._snapshotEnabled = true
+ self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled)
+end
+
+-- Disable snapshotting of character data when entering combat.
+function Amr:DisableSnapshots()
+ self._snapshotEnabled = false
+ self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled)
+end
+
+function Amr:IsSnapshotEnabled()
+ return self._snapshotEnabled
+end
+
+
+function Amr:PLAYER_REGEN_DISABLED()
+--function Amr:GARRISON_MISSION_NPC_OPENED()
+
+ -- send data about this character when a player enters combat in a supported zone
+ if self._snapshotEnabled and Amr.IsSupportedInstance() then
+ local t = time()
+ local player = self:GetPlayerData()
+ local msg = self:SerializePlayerData(player)
+ msg = string.format("%s\r%s\n%s\n%s\n%s\n%s", MINOR, t, player.Region, player.Realm, player.Name, msg)
+
+ self:SendCommMessage(Amr.ChatPrefix, msg, "RAID")
+ end
+end
+
+Amr:RegisterEvent("PLAYER_REGEN_DISABLED")
+--Amr:RegisterEvent("GARRISON_MISSION_NPC_OPENED") -- for debugging, fire this event when open mission table
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 AskMrRobot-Serializer/AskMrRobot-Serializer.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/AskMrRobot-Serializer/AskMrRobot-Serializer.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 AskMrRobot.lua
--- a/AskMrRobot.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1065 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
-AskMrRobot.eventListener = CreateFrame("FRAME") -- Need a frame to respond to events
-AskMrRobot.eventListener:RegisterEvent("ADDON_LOADED") -- Fired when saved variables are loaded
-AskMrRobot.eventListener:RegisterEvent("ITEM_PUSH")
-AskMrRobot.eventListener:RegisterEvent("DELETE_ITEM_CONFIRM")
-AskMrRobot.eventListener:RegisterEvent("UNIT_INVENTORY_CHANGED")
-AskMrRobot.eventListener:RegisterEvent("BANKFRAME_OPENED")
---AskMrRobot.eventListener:RegisterEvent("BANKFRAME_CLOSED");
-AskMrRobot.eventListener:RegisterEvent("PLAYERBANKSLOTS_CHANGED")
-AskMrRobot.eventListener:RegisterEvent("CHARACTER_POINTS_CHANGED")
-AskMrRobot.eventListener:RegisterEvent("CONFIRM_TALENT_WIPE")
-AskMrRobot.eventListener:RegisterEvent("PLAYER_TALENT_UPDATE")
-AskMrRobot.eventListener:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
-AskMrRobot.eventListener:RegisterEvent("PLAYER_ENTERING_WORLD")
---AskMrRobot.eventListener:RegisterEvent("PLAYER_LOGOUT") -- Fired when about to log out
---AskMrRobot.eventListener:RegisterEvent("PLAYER_LEVEL_UP")
---AskMrRobot.eventListener:RegisterEvent("GET_ITEM_INFO_RECEIVED")
-AskMrRobot.eventListener:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
-AskMrRobot.eventListener:RegisterEvent("SOCKET_INFO_UPDATE")
-AskMrRobot.eventListener:RegisterEvent("SOCKET_INFO_CLOSE")
-AskMrRobot.eventListener:RegisterEvent("BAG_UPDATE")
-AskMrRobot.eventListener:RegisterEvent("ITEM_UNLOCKED")
-AskMrRobot.eventListener:RegisterEvent("PLAYER_REGEN_DISABLED")
---AskMrRobot.eventListener:RegisterEvent("ENCOUNTER_START")
-AskMrRobot.eventListener:RegisterEvent("CHAT_MSG_ADDON")
-AskMrRobot.eventListener:RegisterEvent("UPDATE_INSTANCE_INFO")
-AskMrRobot.eventListener:RegisterEvent("PLAYER_DIFFICULTY_CHANGED")
-AskMrRobot.eventListener:RegisterEvent("VOID_STORAGE_OPEN")
-AskMrRobot.eventListener:RegisterEvent("VOID_STORAGE_CONTENTS_UPDATE")
-AskMrRobot.eventListener:RegisterEvent("PLAYER_DIFFICULTY_CHANGED")
-AskMrRobot.eventListener:RegisterEvent("VOID_STORAGE_UPDATE")
-
-AskMrRobot.AddonName = ...
-AskMrRobot.ChatPrefix = "_AMR"
-
--- flag to turn on debugging behavior
-AskMrRobot.debug = false
-
--- the main user interface window
-AskMrRobot.mainWindow = nil
-
-local _amrLDB
-local _minimapIcon
-
-function AskMrRobot.eventListener:OnEvent(event, ...)
- if event == "ADDON_LOADED" then
- local addon = select(1, ...)
- if (addon == "AskMrRobot") then
- AskMrRobot.InitializeSettings()
- AskMrRobot.InitializeMinimap()
-
- AskMrRobot.mainWindow = AskMrRobot.AmrUI:new()
-
- -- listen for messages from other AMR addons
- RegisterAddonMessagePrefix(AskMrRobot.ChatPrefix)
- end
-
- elseif event == "UNIT_INVENTORY_CHANGED" then
- AskMrRobot.ScanEquipped()
-
- elseif event == "ITEM_PUSH" or event == "DELETE_ITEM_CONFIRM" or event == "SOCKET_INFO_CLOSE" or event == "PLAYER_SPECIALIZATION_CHANGED" or event == "BAG_UPDATE" then
- --if AskMrRobot_ReforgeFrame then
- -- AskMrRobot_ReforgeFrame:OnUpdate()
- --end
- --AskMrRobot.SaveBags();
- --AskMrRobot.SaveEquiped();
- --AskMrRonot.GetCurrencies();
- --AskMrRobot.GetGold();
-
- elseif event == "BANKFRAME_OPENED" or event == "PLAYERBANKSLOTS_CHANGED" then
- AskMrRobot.ScanBank();
- elseif event == "VOID_STORAGE_OPEN" or event == "VOID_STORAGE_CONTENTS_UPDATE" or event == "VOID_STORAGE_DEPOSIT_UPDATE" or event == "VOID_STORAGE_UPDATE" then
- AskMrRobot.ScanVoidStorage();
- elseif event == "CHARACTER_POINTS_CHANGED" or event == "CONFIRM_TALENT_WIPE" or event == "PLAYER_TALENT_UPDATE" or event == "ACTIVE_TALENT_GROUP_CHANGED" then
- --AskMrRobot.GetAmrSpecializations();
- --if AskMrRobot_ReforgeFrame then
- -- AskMrRobot_ReforgeFrame:OnUpdate()
- --end
-
- elseif event == "ITEM_UNLOCKED" then
- --AskMrRobot.On_ITEM_UNLOCKED()
-
- elseif event == "PLAYER_ENTERING_WORLD" then
- AskMrRobot.RecordLogin()
-
- elseif event == "PLAYER_REGEN_DISABLED" then
- -- send data about this character when a player enters combat in a supported zone
- if AskMrRobot.IsSupportedInstance() then
- local t = time()
- AskMrRobot.SaveAll()
- AskMrRobot.ExportToAddonChat(t)
- AskMrRobot.CombatLogTab.SaveExtras(t)
- end
-
- elseif event == "CHAT_MSG_ADDON" then
- local chatPrefix, message = select(1, ...)
- local isLogging = AskMrRobot.CombatLogTab.IsLogging()
- if (isLogging and chatPrefix == AskMrRobot.ChatPrefix) then
- if AskMrRobot.mainWindow then
- AskMrRobot.mainWindow.combatLogTab:ReadAddonMessage(message)
- end
- end
-
- elseif event == "UPDATE_INSTANCE_INFO" or event == "PLAYER_DIFFICULTY_CHANGED" then
- if AskMrRobot.mainWindow then
- AskMrRobot.mainWindow.combatLogTab:UpdateAutoLogging()
- end
- end
-
-end
-
-AskMrRobot.eventListener:SetScript("OnEvent", AskMrRobot.eventListener.OnEvent)
-
-
-SLASH_AMR1 = "/amr";
-function SlashCmdList.AMR(msg)
-
- if msg == 'toggle' then
- AskMrRobot.mainWindow:Toggle()
- elseif msg == 'show' then
- AskMrRobot.mainWindow:Show()
- elseif msg == 'hide' then
- AskMrRobot.mainWindow:Hide()
- elseif msg == 'export' then
- AskMrRobot.mainWindow.exportTab:Show()
- elseif msg == 'wipe' then
- AskMrRobot.mainWindow.combatLogTab:LogWipe()
- elseif msg == 'unwipe' then
- AskMrRobot.mainWindow.combatLogTab:LogUnwipe()
- else
- print(L.AMR_SLASH_COMMAND_TEXT_1 .. L.AMR_SLASH_COMMAND_TEXT_2 .. L.AMR_SLASH_COMMAND_TEXT_3 .. L.AMR_SLASH_COMMAND_TEXT_4 .. L.AMR_SLASH_COMMAND_TEXT_5 .. L.AMR_SLASH_COMMAND_TEXT_6 .. L.AMR_SLASH_COMMAND_TEXT_7)
- end
-end
-
--- initialize settings when the addon loads
-function AskMrRobot.InitializeSettings()
-
- -- global settings
- if not AmrSettings then AmrSettings = {} end
-
- -- per-character settings
- if not AmrDb then AmrDb = {} end
-
- -- addon stuff
- if not AmrDb.IconInfo then AmrDb.IconInfo = {} end
- if not AmrDb.Options then AmrDb.Options = {} end
- if not AmrDb.LastCharacterImport then AmrDb.LastCharacterImport = "" end
- if not AmrDb.LastCharacterImportDate then AmrDb.LastCharacterImportDate = "" end
-
- if not AmrDb.SendSettings then
- AmrDb.SendSettings = {
- SendGems = true,
- SendEnchants = true,
- SendEnchantMaterials = true,
- SendToType = L.AMR_SHOPPINGLISTTAB_DROPDOWN_FRIEND,
- SendTo = ""
- }
- end
-
- -- fix a translation bug in a previous version
- if L.AMR_SHOPPINGLISTTAB_DROPDOWN_FRIEND ~= "a friend" and AmrDb.SendSettings.SendToType == "a friend" then
- AmrDb.SendSettings.SendToType = L.AMR_SHOPPINGLISTTAB_DROPDOWN_FRIEND
- end
-
- -- character stuff
- AskMrRobot.ScanCharacter()
- if not AmrDb.BankItems then AmrDb.BankItems = {} end
- if not AmrDb.BagItems then AmrDb.BagItems = {} end
- if not AmrDb.Currencies then AmrDb.Currencies = {} end
- if not AmrDb.Reps then AmrDb.Reps = {} end
- -- data saved for both specs
- if not AmrDb.Specs then AmrDb.Specs = {} end
- if not AmrDb.Glyphs then AmrDb.Glyphs = {} end
- if not AmrDb.Talents then AmrDb.Talents = {} end
- if not AmrDb.Equipped then AmrDb.Equipped = {} end
-
- -- combat log specific settings
- AskMrRobot.CombatLogTab.InitializeVariable()
-end
-
--- record logins when the addon starts up, used to help figure out which character logged which parts of a log file
-function AskMrRobot.RecordLogin()
-
- -- only need to record the region now (only thing we can't get from the log file still)
- AmrSettings.Region = AskMrRobot.regionNames[GetCurrentRegion()]
-
- -- remove the old Logins data, don't need it anymore
- AmrSettings.Logins = nil
-end
-
-function AskMrRobot.InitializeMinimap()
-
- -- minimap icon and data broker icon plugin thingy
- _amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject("AskMrRobot", {
- type = "launcher",
- text = "Ask Mr. Robot",
- icon = "Interface\\AddOns\\AskMrRobot\\Media\\icon",
- OnClick = function()
- if IsControlKeyDown() then
- AskMrRobot.mainWindow.combatLogTab:LogWipe()
- elseif IsModifiedClick("CHATLINL") then
- AskMrRobot.mainWindow.exportTab:Show()
- else
- AskMrRobot.mainWindow:Toggle()
- end
- end,
- OnTooltipShow = function(tt)
- tt:AddLine("Ask Mr. Robot", 1, 1, 1);
- tt:AddLine(" ");
- tt:AddLine(L.AMR_ON_EVENT_TOOLTIP)
- end
- });
-
- AskMrRobot.AmrUpdateMinimap()
-end
-
-function AskMrRobot.AmrUpdateMinimap()
- if AmrDb.Options.hideMapIcon then
- if _minimapIcon then
- _minimapIcon:Hide("AskMrRobot")
- end
- else
- if not _minimapIcon then
- _minimapIcon = LibStub("LibDBIcon-1.0")
- _minimapIcon:Register("AskMrRobot", _amrLDB, AmrDb.IconInfo)
- end
-
- if AskMrRobot.CombatLogTab.IsLogging() then
- _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green'
- else
- _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon'
- end
-
- _minimapIcon:Show("AskMrRobot")
- end
-end
-
-
-function AskMrRobot.SaveAll()
- AskMrRobot.ScanCharacter()
- AskMrRobot.ScanBags()
- AskMrRobot.ScanEquipped()
- AskMrRobot.GetCurrencies()
- AskMrRobot.GetReputations()
- AskMrRobot.GetSpecs()
-end
-
--- gets all basic character properties
-function AskMrRobot.ScanCharacter()
- AmrDb.Region = AskMrRobot.regionNames[GetCurrentRegion()]
- AmrDb.RealmName = GetRealmName()
- AmrDb.CharacterName = UnitName("player")
- AmrDb.Guild = GetGuildInfo("player")
- AmrDb.ActiveSpec = GetActiveSpecGroup()
- AmrDb.Level = UnitLevel("player");
-
- local cls, clsEn = UnitClass("player")
- AmrDb.Class = clsEn;
-
- local race, raceEn = UnitRace("player")
- AmrDb.Race = raceEn;
- AmrDb.Faction = UnitFactionGroup("player")
-
- AskMrRobot.GetAmrProfessions()
-end
-
-function AskMrRobot.GetAmrProfessions()
-
- local profMap = {
- [794] = "Archaeology",
- [171] = "Alchemy",
- [164] = "Blacksmithing",
- [185] = "Cooking",
- [333] = "Enchanting",
- [202] = "Engineering",
- [129] = "First Aid",
- [356] = "Fishing",
- [182] = "Herbalism",
- [773] = "Inscription",
- [755] = "Jewelcrafting",
- [165] = "Leatherworking",
- [186] = "Mining",
- [393] = "Skinning",
- [197] = "Tailoring"
- }
-
- local prof1, prof2, archaeology, fishing, cooking, firstAid = GetProfessions();
- AmrDb.Professions = {};
- if prof1 then
- local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof1);
- if profMap[skillLine] ~= nil then
- AmrDb.Professions[profMap[skillLine]] = skillLevel;
- end
- end
- if prof2 then
- local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof2);
- if profMap[skillLine] ~= nil then
- AmrDb.Professions[profMap[skillLine]] = skillLevel;
- end
- end
- if archaeology then
- local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(archaeology);
- if profMap[skillLine] ~= nil then
- AmrDb.Professions[profMap[skillLine]] = skillLevel;
- end
- end
- if fishing then
- local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(fishing);
- if profMap[skillLine] ~= nil then
- AmrDb.Professions[profMap[skillLine]] = skillLevel;
- end
- end
- if cooking then
- local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(cooking);
- if profMap[skillLine] ~= nil then
- AmrDb.Professions[profMap[skillLine]] = skillLevel;
- end
- end
- if firstAid then
- local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(firstAid);
- if profMap[skillLine] ~= nil then
- AmrDb.Professions[profMap[skillLine]] = skillLevel;
- end
- end
-end
-
-
--- use some local variables to deal with the fact that a user can close the bank before a scan completes
-local _lastBankBagId = nil
-local _lastBankSlotId = nil
-local BACKPACK_CONTAINER = 0
-local BANK_CONTAINER = -1
-
-local function scanBag(bagId, isBank, bagTable, bagItemsWithCount)
- local numSlots = GetContainerNumSlots(bagId)
- for slotId = 1, numSlots do
- local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
- -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked
- if itemLink ~= nil then
- local itemData = AskMrRobot.parseItemLink(itemLink)
- if itemData ~= nil then
- if itemCount == 1 then
- if isBank then
- _lastBankBagId = bagId
- _lastBankSlotId = slotId
- end
- tinsert(bagTable, itemLink)
- end
- if bagItemsWithCount then
- if bagItemsWithCount[itemData.id] then
- bagItemsWithCount[itemData.id] = bagItemsWithCount[itemData.id] + itemCount
- else
- bagItemsWithCount[itemData.id] = itemCount
- end
- end
- end
- end
- end
-end
-
-function AskMrRobot.ScanBank()
- --REAGENTBANK_CONTAINER (-3)
- local bankItems = {}
- local bankItemsAndCounts = {}
-
- scanBag(BANK_CONTAINER, true, bankItems, bankItemsAndCounts)
- scanBag(REAGENTBANK_CONTAINER, true, bankItems, bankItemsAndCounts)
- for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
- scanBag(bagId, true, bankItems, bankItemsAndCounts)
- end
-
- -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data
- if _lastBankBagId ~= nil then
- local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId)
- if itemLink ~= nil then --still open
- AmrDb.BankItems = bankItems
- AmrDb.BankItemsAndCounts = bankItemsAndCounts
- end
- end
-end
-
-function AskMrRobot.ScanVoidStorage()
- if IsVoidStorageReady() then
- local voidItems = {}
- local VOID_STORAGE_MAX = 80
- local VOID_STORAGE_PAGES = 2
-
- for page = 1,VOID_STORAGE_PAGES do
- for i = 1,VOID_STORAGE_MAX do
- local itemId = GetVoidItemInfo(page, i)
- if itemId then
- local itemLink = GetVoidItemHyperlinkString(((page - 1) * VOID_STORAGE_MAX) + i);
- if itemLink then
- tinsert(voidItems, itemLink)
- end
- end
- end
- end
-
- AmrDb.VoidItems = voidItems
- end
-end
-
-function AskMrRobot.ScanBags(bagItemsWithCount)
- local bagItems = {}
- scanBag(BACKPACK_CONTAINER, false, bagItems, bagItemsWithCount) -- backpack
- for bagId = 1, NUM_BAG_SLOTS do
- scanBag(bagId, false, bagItems, bagItemsWithCount)
- end
- AmrDb.BagItems = bagItems
-end
-
-function AskMrRobot.ScanEquipped()
- local equippedItems = {};
- for slotNum = 1, #AskMrRobot.slotIds do
- local slotId = AskMrRobot.slotIds[slotNum];
- local itemLink = GetInventoryItemLink("player", slotId);
- if (itemLink ~= nil) then
- equippedItems[slotId] = itemLink;
- end
- end
-
- -- store last-seen equipped gear for each spec
- AmrDb.Equipped[GetActiveSpecGroup()] = equippedItems
-end
-
-
-local function getCurrencyAmount(index)
- local localized_label, amount, icon_file_name = GetCurrencyInfo(index);
- return amount;
-end
-
-function AskMrRobot.GetCurrencies()
- local currencies = {};
- currencies[-1] = GetMoney()
-
- local currencyList = {61, 81, 241, 361, 384, 394, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 416, 515, 614, 615, 676, 679, 823}
- for i, currency in pairs(currencyList) do
- local amount = getCurrencyAmount(currency)
- if amount ~= 0 then
- currencies[currency] = amount
- end
- end
-
- AmrDb.Currencies = currencies
-end
-
-
-local function getRepStanding(factionId)
- local name, description, standingId, _ = GetFactionInfoByID(factionId)
- return standingId - 1; -- our rep enum correspond to what the armory returns, are 1 less than what the game returns
-end
-
-function AskMrRobot.GetReputations()
- local reps = {}
-
- local repList = {1375,1376,1270,1269,1341,1337,1387,1388,1435}
- for i, repId in pairs(repList) do
- local standing = getRepStanding(repId)
- if standing >= 0 then
- reps[repId] = standing
- end
- end
-
- AmrDb.Reps = reps;
-end
-
-
-local function getSpecId(specGroup)
- local spec = GetSpecialization(false, false, specGroup);
- return spec and GetSpecializationInfo(spec);
-end
-
-local function getTalents(specGroup)
- local talentInfo = {}
- local maxTiers = 7
- for tier = 1, maxTiers do
- for col = 1, 3 do
- local id, name, texture, selected, available = GetTalentInfo(tier, col, specGroup)
- if selected then
- talentInfo[tier] = col
- end
- end
- end
-
- local str = ""
- for i = 1, maxTiers do
- if talentInfo[i] then
- str = str .. talentInfo[i]
- else
- str = str .. '0'
- end
- end
-
- return str
-end
-
-local function getGlyphs(specGroup)
- local glyphs = {}
- for i = 1, NUM_GLYPH_SLOTS do
- local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, specGroup)
- if (glyphID) then
- tinsert(glyphs, glyphSpellID)
- end
- end
- return glyphs;
-end
-
--- get specs, talents, and glyphs
-function AskMrRobot.GetSpecs()
-
- AmrDb.Specs = {}
- AmrDb.Talents = {}
- AmrDb.Glyphs = {}
-
- for group = 1, GetNumSpecGroups() do
- -- spec, convert game spec id to one of our spec ids
- local specId = getSpecId(group)
- if specId then
- AmrDb.Specs[group] = AskMrRobot.specIds[specId]
- else
- AmrDb.Specs[group] = 0
- end
-
- AmrDb.Talents[group] = getTalents(group)
- AmrDb.Glyphs[group] = getGlyphs(group)
- end
-end
-
-----------------------------------------------------------------------------
--- Export
-----------------------------------------------------------------------------
-
-function AskMrRobot.toCompressedNumberList(list)
- -- ensure the values are numbers, sorted from lowest to highest
- local nums = {}
- for i, v in ipairs(list) do
- table.insert(nums, tonumber(v))
- end
- table.sort(nums)
-
- local ret = {}
- local prev = 0
- for i, v in ipairs(nums) do
- local diff = v - prev
- table.insert(ret, diff)
- prev = v
- end
-
- return table.concat(ret, ",")
-end
-
--- appends a list of items to the export
-local function appendItemsToExport(fields, itemObjects)
-
- -- sort by item id so we can compress it more easily
- table.sort(itemObjects, function(a, b) return a.id < b.id end)
-
- -- append to the export string
- local prevItemId = 0
- local prevGemId = 0
- local prevEnchantId = 0
- local prevUpgradeId = 0
- local prevBonusId = 0
- for i, itemData in ipairs(itemObjects) do
- local itemParts = {}
-
- table.insert(itemParts, itemData.id - prevItemId)
- prevItemId = itemData.id
-
- if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end
- if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end
- if itemData.upgradeId ~= 0 then
- table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId))
- prevUpgradeId = itemData.upgradeId
- end
- if itemData.bonusIds then
- for bIndex, bValue in ipairs(itemData.bonusIds) do
- table.insert(itemParts, "b" .. (bValue - prevBonusId))
- prevBonusId = bValue
- end
- end
- if itemData.gemIds[1] ~= 0 then
- table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId))
- prevGemId = itemData.gemIds[1]
- end
- if itemData.gemIds[2] ~= 0 then
- table.insert(itemParts, "y" .. (itemData.gemIds[2] - prevGemId))
- prevGemId = itemData.gemIds[2]
- end
- if itemData.gemIds[3] ~= 0 then
- table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId))
- prevGemId = itemData.gemIds[3]
- end
- if itemData.enchantId ~= 0 then
- table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId))
- prevEnchantId = itemData.enchantId
- end
-
- table.insert(fields, table.concat(itemParts, ""))
- end
-end
-
--- create a compact string representing this player
--- if complete is true, exports everything (inventory, both specs)
--- otherwise only exports the player's active gear, spec, etc.
-function AskMrRobot.ExportToCompressedString(complete)
- local fields = {}
-
- -- compressed string uses a fixed order rather than inserting identifiers
- table.insert(fields, GetAddOnMetadata(AskMrRobot.AddonName, "Version"))
- table.insert(fields, AmrDb.Region)
- table.insert(fields, AmrDb.RealmName)
- table.insert(fields, AmrDb.CharacterName)
-
- -- guild name
- local guildName = GetGuildInfo("player")
- if guildName == nil then
- table.insert(fields, "")
- else
- table.insert(fields, guildName)
- end
-
- -- race, default to pandaren if we can't read it for some reason
- local raceval = AskMrRobot.raceIds[AmrDb.Race]
- if raceval == nil then raceval = 13 end
- table.insert(fields, raceval)
-
- -- faction, default to alliance if we can't read it for some reason
- raceval = AskMrRobot.factionIds[AmrDb.Faction]
- if raceval == nil then raceval = 1 end
- table.insert(fields, raceval)
-
- table.insert(fields, AmrDb.Level)
-
- local profs = {}
- local noprofs = true
- if AmrDb.Professions then
- for k, v in pairs(AmrDb.Professions) do
- local profval = AskMrRobot.professionIds[k]
- if profval ~= nil then
- noprofs = false
- table.insert(profs, profval .. ":" .. v)
- end
- end
- end
-
- if noprofs then
- table.insert(profs, "0:0")
- end
-
- table.insert(fields, table.concat(profs, ","))
-
- -- export specs
- table.insert(fields, AmrDb.ActiveSpec)
- for spec = 1, 2 do
- if AmrDb.Specs[spec] and (complete or spec == AmrDb.ActiveSpec) then
- table.insert(fields, ".s" .. spec) -- indicates the start of a spec block
- table.insert(fields, AmrDb.Specs[spec])
- table.insert(fields, AmrDb.Talents[spec])
- table.insert(fields, AskMrRobot.toCompressedNumberList(AmrDb.Glyphs[spec]))
- end
- end
-
- -- export equipped gear
- if AmrDb.Equipped then
- for spec = 1, 2 do
- if AmrDb.Equipped[spec] and (complete or spec == AmrDb.ActiveSpec) then
- table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block
-
- local itemObjects = {}
- for k, v in pairs(AmrDb.Equipped[spec]) do
- local itemData = AskMrRobot.parseItemLink(v)
- itemData.slot = k
- table.insert(itemObjects, itemData)
- end
-
- appendItemsToExport(fields, itemObjects)
- end
- end
- end
-
- -- if doing a complete export, include reputations and bank/bag items too
- if complete then
-
- -- export reputations
- local reps = {}
- local noreps = true
- if AmrDb.Reps then
- for k, v in pairs(AmrDb.Reps) do
- noreps = false
- table.insert(reps, k .. ":" .. v)
- end
- end
- if noreps then
- table.insert(reps, "_")
- end
-
- table.insert(fields, ".r")
- table.insert(fields, table.concat(reps, ","))
-
- -- export bag and bank
- local itemObjects = {}
- if AmrDb.BagItems then
- for i, v in ipairs(AmrDb.BagItems) do
- local _,_,_,_,_,_,_,stackCount = GetItemInfo(v)
- if stackCount == 1 then
- local itemData = AskMrRobot.parseItemLink(v)
- if itemData ~= nil then
- table.insert(itemObjects, itemData)
- end
- end
- end
- end
- if AmrDb.BankItems then
- for i, v in ipairs(AmrDb.BankItems) do
- local _,_,_,_,_,_,_,stackCount = GetItemInfo(v)
- if stackCount == 1 then
- local itemData = AskMrRobot.parseItemLink(v)
- if itemData ~= nil then
- table.insert(itemObjects, itemData)
- end
- end
- end
- end
- if AmrDb.VoidItems then
- for i, v in ipairs(AmrDb.VoidItems) do
- local _,_,_,_,_,_,_,stackCount = GetItemInfo(v)
- if stackCount == 1 then
- local itemData = AskMrRobot.parseItemLink(v)
- if itemData ~= nil then
- table.insert(itemObjects, itemData)
- end
- end
- end
- end
-
- table.insert(fields, ".inv")
- appendItemsToExport(fields, itemObjects)
- end
-
- return "$" .. table.concat(fields, ";") .. "$"
-end
-
-function AskMrRobot.ExportToAddonChat(timestamp)
- local msg = AskMrRobot.ExportToCompressedString(false)
- local msgPrefix = timestamp .. "\n" .. AmrDb.Region .. "\n" .. AmrDb.RealmName .. "\n" .. AmrDb.CharacterName .. "\n"
-
- -- break the data into 250 character chunks (to deal with the short limit on addon message size)
- local chunks = {}
- local i = 1
- local length = string.len(msg)
- local chunkLen = 249 - string.len(msgPrefix)
- while (i <= length) do
- local endpos = math.min(i + chunkLen, length)
- table.insert(chunks, msgPrefix .. string.sub(msg, i, endpos))
- i = endpos + 1
- end
-
- for i, v in ipairs(chunks) do
- SendAddonMessage(AskMrRobot.ChatPrefix, v, "RAID")
- end
-
- -- send a completion message
- SendAddonMessage(AskMrRobot.ChatPrefix, msgPrefix .. "done", "RAID")
-end
-
--- Create an export string that can be copied to the website
-function AskMrRobot.ExportToString()
- return AskMrRobot.ExportToCompressedString(true)
-end
-
-
-----------------------------------------------------------------------------
--- Import
-----------------------------------------------------------------------------
-
--- imports will give us extra information about items, gems, and enchants
-AskMrRobot.ExtraItemData = {} -- keyed by item id
-AskMrRobot.ExtraGemData = {} -- keyed by gem enchant id
-AskMrRobot.ExtraEnchantData = {} -- keyed by enchant id
-
--- the data that was last imported
-AskMrRobot.ImportData = nil -- keyed by slot id
-
-local MIN_IMPORT_VERSION = 13
-
---
--- Import a character, returning nil on success, otherwise an error message, import result stored in AskMrRobot.ImportData
---
-function AskMrRobot.ImportCharacter(data, isTest)
-
- -- make sure all data is up to date before importing
- AskMrRobot.SaveAll()
-
- if data == nil or string.len(data) == 0 then
- return L.AMR_IMPORT_ERROR_EMPTY
- end
-
- local data1 = { strsplit("$", data) }
- if #data1 ~= 3 then
- return L.AMR_IMPORT_ERROR_FORMAT
- end
-
- local parts = { strsplit(";", data1[2]) }
-
- -- require a minimum version
- local ver = tonumber(parts[1])
- if ver < MIN_IMPORT_VERSION then
- return L.AMR_IMPORT_ERROR_VERSION
- end
-
- -- require name match (don't match realm due to language issues for now)
- if not isTest then
- local region = parts[2]
- local realm = parts[3]
- local name = parts[4]
- if name ~= AmrDb.CharacterName then
- local badPers = name .. " (" .. realm .. ")"
- local goodPers = AmrDb.CharacterName .. " (" .. AmrDb.RealmName .. ")"
- return L.AMR_IMPORT_ERROR_CHAR:format(badPers, goodPers)
- end
-
- -- require race match
- local race = tonumber(parts[6])
- if race ~= AskMrRobot.raceIds[AmrDb.Race] then
- return L.AMR_IMPORT_ERROR_RACE
- end
-
- -- require faction match
- local faction = tonumber(parts[7])
- if faction ~= AskMrRobot.factionIds[AmrDb.Faction] then
- return L.AMR_IMPORT_ERROR_FACTION
- end
-
- -- require level match
- local level = tonumber(parts[8])
- if level ~= AmrDb.Level then
- return L.AMR_IMPORT_ERROR_LEVEL
- end
-
- -- require spec match
- local spec = tonumber(parts[12])
- if spec ~= AmrDb.Specs[AmrDb.ActiveSpec] then
- --print(AmrDb.ActiveSpec)
- --print(spec)
- --print(AmrDb.Specs[AmrDb.ActiveSpec])
- local _, specName = GetSpecializationInfoByID(AskMrRobot.gameSpecIds[spec])
- return L.AMR_IMPORT_ERROR_SPEC:format(specName)
- end
-
- -- require talent match
- local talents = parts[13]
- if talents ~= AmrDb.Talents[AmrDb.ActiveSpec] then
- return L.AMR_IMPORT_ERROR_TALENT
- end
-
- -- require glyph match
- -- TODO: re-enable this check when glyphs are more consistent
- --local glyphs = parts[14]
- --if glyphs ~= AskMrRobot.toCompressedNumberList(AmrDb.Glyphs[AmrDb.ActiveSpec]) then
- -- return L.AMR_IMPORT_ERROR_GLYPH
- --end
- end
-
-
- -- if we make it this far, the data is valid, so read item information
- local importData = {}
-
- local itemInfo = {}
- local gemInfo = {}
- local enchantInfo = {}
-
- local prevItemId = 0
- local prevGemId = 0
- local prevEnchantId = 0
- local prevUpgradeId = 0
- local prevBonusId = 0
- local digits = {
- ["-"] = true,
- ["0"] = true,
- ["1"] = true,
- ["2"] = true,
- ["3"] = true,
- ["4"] = true,
- ["5"] = true,
- ["6"] = true,
- ["7"] = true,
- ["8"] = true,
- ["9"] = true,
- }
- for i = 16, #parts do
- local itemString = parts[i]
- if itemString ~= "" and itemString ~= "_" then
- local tokens = {}
- local bonusIds = {}
- local hasBonuses = false
- local token = ""
- local prop = "i"
- local tokenComplete = false
- for j = 1, string.len(itemString) do
- local c = string.sub(itemString, j, j)
- if digits[c] == nil then
- tokenComplete = true
- else
- token = token .. c
- end
-
- if tokenComplete or j == string.len(itemString) then
- local val = tonumber(token)
- if prop == "i" then
- val = val + prevItemId
- prevItemId = val
- elseif prop == "u" then
- val = val + prevUpgradeId
- prevUpgradeId = val
- elseif prop == "b" then
- val = val + prevBonusId
- prevBonusId = val
- elseif prop == "x" or prop == "y" or prop == "z" then
- val = val + prevGemId
- prevGemId = val
- elseif prop == "e" then
- val = val + prevEnchantId
- prevEnchantId = val
- end
-
- if prop == "b" then
- table.insert(bonusIds, val)
- hasBonuses = true
- else
- tokens[prop] = val
- end
-
- token = ""
- tokenComplete = false
-
- -- we have moved on to the next token
- prop = c
- end
- end
-
- local obj = {}
- importData[tonumber(tokens["s"])] = obj
-
- obj.id = tokens["i"]
- obj.suffixId = tokens["f"] or 0
- obj.upgradeId = tokens["u"] or 0
- obj.enchantId = tokens["e"] or 0
-
- obj.gemIds = {}
- table.insert(obj.gemIds, tokens["x"] or 0)
- table.insert(obj.gemIds, tokens["y"] or 0)
- table.insert(obj.gemIds, tokens["z"] or 0)
- table.insert(obj.gemIds, 0)
-
- if hasBonuses then
- obj.bonusIds = bonusIds
- end
-
- local itemObj = {}
- itemObj.id = obj.id
- itemInfo[obj.id] = itemObj
-
- -- look for any socket color information, add to our extra data
- if tokens["c"] then
- itemObj.socketColors = {}
- for j = 1, string.len(tokens["c"]) do
- table.insert(itemObj.socketColors, tonumber(string.sub(tokens["c"], j, j)))
- end
- end
-
- -- look for item ID duplicate info, deals with old SoO items
- if tokens["d"] then
- itemObj.duplicateId = tonumber(tokens["d"])
- itemInfo[itemObj.duplicateId] = itemObj
- end
-
- end
- end
-
- -- now read any extra display information
- parts = { strsplit("@", data1[3]) }
- for i = 1, #parts do
- local infoParts = { strsplit("\\", parts[i]) }
-
- if infoParts[1] == "g" then
-
- local gemObj = {}
- gemObj.enchantId = tonumber(infoParts[2])
- gemObj.id = tonumber(infoParts[3])
-
- local identicalGems = infoParts[4]
- if string.len(identicalGems) > 0 then
- gemObj.identicalGroup = {}
- identicalGems = { strsplit(",", identicalGems) }
- for j = 1, #identicalGems do
- gemObj.identicalGroup[tonumber(identicalGems[j])] = true
- end
- end
-
- gemObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.AMR_STAT_SHORT_STRINGS[s] end)
- if infoParts[6] == nil or string.len(infoParts[6]) == 0 then
- gemObj.identicalItemGroup = {[gemObj.id]=true}
- else
- local identicalIds = { strsplit(',', infoParts[6]) }
- gemObj.identicalItemGroup = {}
- for j = 1, #identicalIds do
- gemObj.identicalItemGroup[tonumber(identicalIds[j])] = true
- end
- end
-
- gemInfo[gemObj.enchantId] = gemObj
-
- elseif infoParts[1] == "e" then
-
- local enchObj = {}
- enchObj.id = tonumber(infoParts[2])
- enchObj.itemId = tonumber(infoParts[3])
- enchObj.spellId = tonumber(infoParts[4])
- enchObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.AMR_STAT_SHORT_STRINGS[s] end)
-
- local mats = infoParts[6]
- if string.len(mats) > 0 then
- enchObj.materials = {}
- mats = { strsplit(",", mats) }
- for j = 1, #mats do
- local kv = { strsplit("=", mats[j]) }
- enchObj.materials[tonumber(kv[1])] = tonumber(kv[2])
- end
- end
-
- enchantInfo[enchObj.id] = enchObj
-
- end
- end
-
- if isTest then
-
- -- print result for debugging
- --for k,v in pairs(importData) do
- -- local blah = AskMrRobot.createItemLink(v)
- -- print(blah)
- --local name, link = GetItemInfo(blah)
- --print(link)
- --if link == nil then
- -- print(blah)
- -- print("bad item: " .. v.id)
- --end
- --end
-
-
- else
- -- we have succeeded, record the result
- AskMrRobot.ImportData = importData
- AskMrRobot.ExtraItemData = itemInfo
- AskMrRobot.ExtraGemData = gemInfo
- AskMrRobot.ExtraEnchantData = enchantInfo
-
- AmrDb.LastCharacterImport = data
- AmrDb.LastCharacterImportDate = date()
- end
-end
diff -r 75431c084aa0 -r 01b63b8ed811 AskMrRobot.toc
--- a/AskMrRobot.toc Tue Feb 24 21:50:13 2015 -0800
+++ b/AskMrRobot.toc Fri Jun 05 11:05:15 2015 -0700
@@ -1,56 +1,49 @@
## Interface: 60100
## Title: Ask Mr. Robot
## Author: Team Robot, Inc.
-## Version: 20
-## Notes: Exports/Imports data to/from askmrrobot.com.
+## Version: 21
+## Notes: Gear import/export, combat logging, and more.
## URL: www.askmrrobot.com
-## DefaultState: Enabled
-## LoadOnDemand: 0
-## SavedVariablesPerCharacter: AmrDb
-## SavedVariables: AmrSettings
+## SavedVariables: AskMrRobotDb2
-#@no-lib-strip@
-Libs\LibStub\Libstub.lua
-Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua
-#@end-no-lib-strip@
+Libs\LibStub\LibStub.lua
+Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
+Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
+Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
+Libs\AceAddon-3.0\AceAddon-3.0.xml
+Libs\AceSerializer-3.0\AceSerializer-3.0.xml
+Libs\AceEvent-3.0\AceEvent-3.0.xml
+Libs\AceComm-3.0\AceComm-3.0.xml
+Libs\AceConsole-3.0\AceConsole-3.0.xml
+Libs\AceDB-3.0\AceDB-3.0.xml
+Libs\AceLocale-3.0\AceLocale-3.0.xml
+Libs\AceGUI-3.0\AceGUI-3.0.xml
-Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
+localization\enUS.lua
-#@no-lib-strip@
-Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
-#@end-no-lib-strip@
+AskMrRobot-Serializer\AskMrRobot-Serializer.xml
-#translations first
-Localization\localization.en.lua
-Localization\localization.de.lua
+Core.lua
+Constants.lua
+ui\Ui.lua
-wait.lua
-sort.lua
+ui\AmrUiLabel.lua
+ui\AmrUiButton.lua
+ui\AmrUiTextButton.lua
+ui\AmrUiCheckBox.lua
+ui\AmrUiTextarea.lua
+ui\AmrUiDropDown.lua
+ui\AmrUiPanel.lua
+ui\AmrUiFrame.lua
+ui\AmrUiTabGroup.lua
+ui\AmrUiIcon.lua
+ui\AmrUiScrollFrame.lua
-ui\Components.lua
-amr-constants.lua
-AskMrRobot.lua
-AskMrRobotUi.lua
-ui\RobotStamp.lua
-ui\ItemTooltipFrame.lua
-ui\ItemLinkText.lua
-ui\ItemIcon.lua
-ui\GemIcon.lua
-ui\EnchantLinkText.lua
-ui\FontString.lua
-ui\ScrollFrame.lua
-ui\ExportTab.lua
-ui\JewelPanel.lua
-ui\SummaryTab.lua
-ui\GemTab.lua
-ui\EnchantTab.lua
-ui\HelpTab.lua
-ui\ImportTab.lua
-ui\ShoppingListTab.lua
-ui\GearComparisonTab.lua
-ui\CombatLogTab.lua
-ui\SettingsTab.lua
-
-AskMrRobot.xml
-
-
+Export.lua
+Import.lua
+Shopping.lua
+Gear.lua
+CombatLog.lua
+Loot.lua
+TeamOptimizer.lua
+Options.lua
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 AskMrRobot.xml
--- a/AskMrRobot.xml Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-
-
-
-
- self:SetOwner(WorldFrame, "ANCHOR_NONE");
-
-
-
-
diff -r 75431c084aa0 -r 01b63b8ed811 AskMrRobotUi.lua
--- a/AskMrRobotUi.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
-AskMrRobot.AmrUI = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
-local _menuIds = {
- export = 1,
- gear = 2,
- combatLog = 3,
- settings = 4,
- help = 5
-}
-
-function AskMrRobot.AmrUI:new()
- local o = AskMrRobot.Frame:new("AskMrRobot_Dialog", nil, "BasicFrameTemplateWithInset")
-
- -- use the AmrUI class
- setmetatable(o, { __index = AskMrRobot.AmrUI })
-
- o:RegisterForDrag("LeftButton");
- o:SetWidth(615)
- o:SetHeight(550)
- o.InsetBg:SetPoint("TOPLEFT", 140, -24)
-
- o:SetParent("UIParent")
- o:SetPoint("CENTER")
- o:Hide()
- o:EnableMouse(true)
- o:EnableKeyboard(true)
- o.hideOnEscape = 1
- o:SetMovable(true)
- o:SetToplevel(true)
-
- o:SetScript("OnDragStart", AskMrRobot.AmrUI.OnDragStart)
- o:SetScript("OnDragStop", AskMrRobot.AmrUI.OnDragStop)
- o:SetScript("OnHide", AskMrRobot.AmrUI.OnHide)
- o:SetScript("OnShow", AskMrRobot.AmrUI.OnShow)
-
- o:RegisterEvent("AUCTION_HOUSE_CLOSED")
- o:RegisterEvent("AUCTION_HOUSE_SHOW")
- o:RegisterEvent("SOCKET_INFO_UPDATE")
- o:RegisterEvent("SOCKET_INFO_CLOSE")
-
- o:SetScript("OnEvent", function(...)
- o:OnEvent(...)
- end)
-
- tinsert(UISpecialFrames, o:GetName())
-
- -- initialize some fields
- o.initialized = false
- o.visible = false
-
- -- title
- o.TitleText:SetText("--BETA-- Ask Mr. Robot v" .. GetAddOnMetadata(AskMrRobot.AddonName, "Version"))
-
- -- create the main menu
- o.menu = o:createMainMenu()
-
- local tabArea = AskMrRobot.Frame:new(nil, o)
- tabArea:SetPoint("TOPLEFT", 155, -30)
- tabArea:SetPoint("BOTTOMRIGHT")
-
- o.exportTab = AskMrRobot.ExportTab:new(tabArea)
- o.menu[_menuIds["export"]].element = o.exportTab
-
- o.gearComparisonTab = AskMrRobot.GearComparisonTab:new(tabArea)
- o.menu[_menuIds["gear"]].element = o.gearComparisonTab
-
- o.combatLogTab = AskMrRobot.CombatLogTab:new(tabArea)
- o.menu[_menuIds["combatLog"]].element = o.combatLogTab
-
- o.settingsTab = AskMrRobot.SettingsTab:new(tabArea)
- o.menu[_menuIds["settings"]].element = o.settingsTab
-
- o.helpTab = AskMrRobot.HelpTab:new(tabArea)
- o.menu[_menuIds["help"]].element = o.helpTab
-
- o:Hide()
- o:ShowMenu("export")
-
- return o
-end
-
-function AskMrRobot.AmrUI:createMainMenu()
- local buttons = {}
-
- local function onTabButtonClick(clickedButton, event, ...)
- for i = 1, #buttons do
- local button = buttons[i]
- if clickedButton == button then
- button.highlight:SetVertexColor(1, 1, 0)
- button:LockHighlight()
- if button.element then
- button.element:Show()
- end
- else
- button.highlight:SetVertexColor(.196, .388, .8)
- button:UnlockHighlight()
- if button.element then
- button.element:Hide()
- end
- end
- end
- end
-
- local function createButton(text, spacing)
- local lastButton = #buttons
- local i = lastButton + 1
- local tabButton = CreateFrame("Button", "AmrTabButton" .. i, self, "OptionsListButtonTemplate")
- tabButton:SetText(text)
- tabText = tabButton:GetFontString()
- tabText:SetPoint("LEFT", 6, 0)
- if i == 1 then
- tabButton:SetPoint("TOPLEFT", 2, spacing)
- else
- tabButton:SetPoint("TOPLEFT", "AmrTabButton" .. lastButton, "BOTTOMLEFT", 0, spacing)
- end
- tabButton:SetWidth(140)
- tabButton:SetHeight(20)
- tinsert(buttons, tabButton)
- tabButton:SetScript("OnClick", onTabButtonClick)
- end
-
- createButton(L.AMR_UI_MENU_EXPORT, -35)
- createButton(L.AMR_UI_MENU_GEAR, -20)
- createButton(L.AMR_UI_MENU_COMBAT_LOG, 0)
- createButton(L.AMR_UI_MENU_SETTINGS, 0)
- createButton(L.AMR_UI_MENU_HELP, 0)
-
- return buttons
-end
-
-function AskMrRobot.AmrUI:ShowMenu(menu)
- local id = _menuIds[menu]
- if id then
- self.menu[id]:Click()
- end
-end
-
-function AskMrRobot.AmrUI:Toggle()
- if self.visible then
- self:Hide()
- else
- self.visible = true
- self:Show()
- end
-end
-
-function AskMrRobot.AmrUI:OnShow()
-
-end
-
-function AskMrRobot.AmrUI:OnDragStart()
- if not self.isLocked then
- self:StartMoving();
- end
-end
-
-function AskMrRobot.AmrUI:OnDragStop()
- self:StopMovingOrSizing()
-end
-
-function AskMrRobot.AmrUI:OnHide()
- self.visible = false
- self:StopMovingOrSizing()
-end
-
-function AskMrRobot.AmrUI:OnEvent(frame, event, ...)
- local handler = self["On_" .. event]
- if handler then
- handler(self, ...)
- end
-end
diff -r 75431c084aa0 -r 01b63b8ed811 CombatLog.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/CombatLog.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,564 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _btnToggle = nil
+local _panelUndoWipe = nil
+local _chkAutoAll = nil
+local _autoChecks = nil
+
+local function createDifficultyCheckBox(instanceId, difficultyId)
+ local chk = AceGUI:Create("AmrUiCheckBox")
+ chk:SetText(L.DifficultyNames[difficultyId])
+ chk:SetCallback("OnClick", function(widget)
+ Amr:ToggleAutoLog(instanceId, difficultyId)
+ end)
+
+ _autoChecks[instanceId][difficultyId] = chk
+ return chk
+end
+
+-- render a group of controls for auto-logging of a raid zone
+local function renderAutoLogSection(instanceId, container)
+ _autoChecks[instanceId] = {}
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(200)
+ lbl:SetText(L.InstanceNames[instanceId])
+ lbl:SetFont(Amr.CreateFont("Regular", 20, Amr.Colors.White))
+ container:AddChild(lbl)
+
+ local line = AceGUI:Create("AmrUiPanel")
+ line:SetHeight(1)
+ line:SetBackgroundColor(Amr.Colors.White)
+ line:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -7)
+ line:SetPoint("TOPRIGHT", lbl.frame, "BOTTOMRIGHT", 0, -7)
+ container:AddChild(line)
+
+ local chkMythic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Mythic)
+ chkMythic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -8)
+ container:AddChild(chkMythic)
+
+ local chkNormal = createDifficultyCheckBox(instanceId, Amr.Difficulties.Normal)
+ chkNormal:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -30)
+ container:AddChild(chkNormal)
+
+ -- find the widest of mythic/normal
+ local w = math.max(chkMythic:GetWidth(), chkNormal:GetWidth())
+
+ local chkHeroic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Heroic)
+ chkHeroic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -8)
+ container:AddChild(chkHeroic)
+
+ local chkLfr = createDifficultyCheckBox(instanceId, Amr.Difficulties.Lfr)
+ chkLfr:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -30)
+ container:AddChild(chkLfr)
+
+ return lbl
+end
+
+-- renders the main UI for the Combat Log tab
+function Amr:RenderTabLog(container)
+
+ -- main commands
+ _btnToggle = AceGUI:Create("AmrUiButton")
+ _btnToggle:SetText(L.LogButtonStartText)
+ _btnToggle:SetBackgroundColor(Amr.Colors.Green)
+ _btnToggle:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ _btnToggle:SetWidth(200)
+ _btnToggle:SetHeight(26)
+ _btnToggle:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40)
+ _btnToggle:SetCallback("OnClick", function() Amr:ToggleLogging() end)
+ container:AddChild(_btnToggle)
+
+ _lblLogging = AceGUI:Create("AmrUiLabel")
+ _lblLogging:SetText(L.LogNote)
+ _lblLogging:SetWidth(200)
+ _lblLogging:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.BrightGreen))
+ _lblLogging:SetJustifyH("MIDDLE")
+ _lblLogging:SetPoint("TOP", _btnToggle.frame, "BOTTOM", 0, -5)
+ container:AddChild(_lblLogging)
+
+ local btnReload = AceGUI:Create("AmrUiButton")
+ btnReload:SetText(L.LogButtonReloadText)
+ btnReload:SetBackgroundColor(Amr.Colors.Blue)
+ btnReload:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnReload:SetWidth(200)
+ btnReload:SetHeight(26)
+ btnReload:SetPoint("TOPLEFT", _btnToggle.frame, "TOPRIGHT", 40, 0)
+ btnReload:SetCallback("OnClick", ReloadUI)
+ container:AddChild(btnReload)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(L.LogReloadNote)
+ lbl:SetWidth(200)
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetJustifyH("MIDDLE")
+ lbl:SetPoint("TOP", btnReload.frame, "BOTTOM", 0, -5)
+ container:AddChild(lbl)
+
+ -- container for undo wipe so we can hide/show it all
+ _panelUndoWipe = AceGUI:Create("AmrUiPanel")
+ _panelUndoWipe:SetLayout("None")
+ _panelUndoWipe:SetBackgroundColor(Amr.Colors.Black, 0)
+ _panelUndoWipe:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -40)
+ container:AddChild(_panelUndoWipe)
+
+ local btnUndoWipe = AceGUI:Create("AmrUiButton")
+ btnUndoWipe:SetText(L.LogButtonUndoWipeText)
+ btnUndoWipe:SetBackgroundColor(Amr.Colors.Orange)
+ btnUndoWipe:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnUndoWipe:SetWidth(200)
+ btnUndoWipe:SetHeight(26)
+ btnUndoWipe:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -40)
+ btnUndoWipe:SetCallback("OnClick", function() Amr:UndoWipe() end)
+ _panelUndoWipe:AddChild(btnUndoWipe)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(L.LogUndoWipeNote)
+ lbl:SetWidth(200)
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetJustifyH("MIDDLE")
+ lbl:SetPoint("TOP", btnUndoWipe.frame, "BOTTOM", 0, -5)
+ _panelUndoWipe:AddChild(lbl)
+
+ local lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetText(L.LogUndoWipeDate(date("%B %d", time()), date("%I:%M %p", time())))
+ lbl2:SetWidth(200)
+ lbl2:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl2:SetJustifyH("MIDDLE")
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -2)
+ _panelUndoWipe:AddChild(lbl2)
+
+ local btnWipe = AceGUI:Create("AmrUiButton")
+ btnWipe:SetText(L.LogButtonWipeText)
+ btnWipe:SetBackgroundColor(Amr.Colors.Orange)
+ btnWipe:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnWipe:SetWidth(200)
+ btnWipe:SetHeight(26)
+ btnWipe:SetPoint("TOPRIGHT", btnUndoWipe.frame, "TOPLEFT", -40, 0)
+ btnWipe:SetCallback("OnClick", function() Amr:Wipe() end)
+ container:AddChild(btnWipe)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(L.LogWipeNote)
+ lbl:SetWidth(200)
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetJustifyH("MIDDLE")
+ lbl:SetPoint("TOP", btnWipe.frame, "BOTTOM", 0, -5)
+ container:AddChild(lbl)
+
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetText(L.LogWipeNote2("/amr wipe"))
+ lbl2:SetWidth(200)
+ lbl2:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl2:SetJustifyH("MIDDLE")
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -2)
+ container:AddChild(lbl2)
+
+ -- auto-logging controls
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(600)
+ lbl:SetText(L.LogAutoTitle)
+ lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
+ lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -40)
+ container:AddChild(lbl)
+
+ _chkAutoAll = AceGUI:Create("AmrUiCheckBox")
+ _chkAutoAll:SetText(L.LogAutoAllText)
+ _chkAutoAll:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -15)
+ _chkAutoAll:SetCallback("OnClick", function(widget) Amr:ToggleAllAutoLog() end)
+ container:AddChild(_chkAutoAll)
+
+ _autoChecks = {}
+
+ -- go through all supported instances, rendering in a left->right pattern, 2 per row
+ local autoSections = {}
+ for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
+ local autoSection = renderAutoLogSection(instanceId, container)
+ if i == 1 then
+ autoSection:SetPoint("TOPLEFT", _chkAutoAll.frame, "BOTTOMLEFT", -1, -15)
+ elseif i % 2 == 0 then
+ autoSection:SetPoint("TOPLEFT", autoSections[i - 1].frame, "TOPRIGHT", 40, 0)
+ else
+ autoSection:SetPoint("TOPLEFT", autoSections[i - 2].frame, "BOTTOMLEFT", 0, -15)
+ end
+
+ table.insert(autoSections, autoSection)
+ end
+ autoSections = nil
+
+ -- instructions
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(L.LogInstructionsTitle)
+ lbl:SetWidth(480)
+ lbl:SetFont(Amr.CreateFont("Italic", 24, Amr.Colors.Text))
+ lbl:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", 0, -40)
+ container:AddChild(lbl)
+
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetText(L.LogInstructions)
+ lbl2:SetWidth(480)
+ lbl2:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.Text))
+ lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -10)
+ container:AddChild(lbl2)
+
+ -- initialize state of controls
+ Amr:RefreshLogUi()
+end
+
+function Amr:ReleaseTabLog()
+ _btnToggle = nil
+ _panelUndoWipe = nil
+ _chkAutoAll = nil
+ _autoChecks = nil
+end
+
+local function isAllAutoLoggingEnabled()
+ -- see if all auto-logging options are enabled
+ local allChecked = true
+ for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
+ for k, difficultyId in pairs(Amr.Difficulties) do
+ if not Amr.db.profile.Logging.Auto[instanceId][difficultyId] then
+ allChecked = false
+ break
+ end
+ end
+ if not allChecked then break end
+ end
+
+ return allChecked
+end
+
+-- check current zone and auto-logging settings, and enable logging if appropriate
+local function updateAutoLogging(force)
+
+ -- get the info about the instance
+ local zone, _, difficultyId, _, _, _, _, instanceId = GetInstanceInfo()
+
+ if not force and zone == Amr.db.char.Logging.LastZone and difficultyId == Amr.db.char.Logging.LastDiff then
+ -- do nothing if the zone hasn't actually changed, otherwise we may override the user's manual enable/disable
+ return
+ end
+
+ Amr.db.char.Logging.LastZone = zone
+ Amr.db.char.Logging.LastDiff = difficultyId
+
+ if Amr.IsSupportedInstanceId(instanceId) and Amr.db.profile.Logging.Auto[tonumber(instanceId)][tonumber(difficultyId)] then
+ -- we are in a supported zone that we want to auto-log, turn logging on
+ -- (supported check is probably redundant, but just in case someone has old settings lying around)
+ if not Amr:IsLogging() then
+ Amr:StartLogging()
+ end
+ else
+ -- not in a zone that we want to auto-log, turn logging off
+ if Amr:IsLogging() then
+ Amr:StopLogging()
+ end
+ end
+end
+
+-- refresh the state of the tab based on current settings
+function Amr:RefreshLogUi()
+ if not _btnToggle then return end
+
+ -- set state of logging button based on whether it is on or off
+ if self:IsLogging() then
+ _btnToggle:SetBackgroundColor(Amr.Colors.Red)
+ _btnToggle:SetText(L.LogButtonStopText)
+ else
+ _btnToggle:SetBackgroundColor(Amr.Colors.Green)
+ _btnToggle:SetText(L.LogButtonStartText)
+ end
+
+ _lblLogging:SetVisible(self:IsLogging())
+
+ -- hide/show undo wipe button based on whether a wipe has been called recently
+ _panelUndoWipe:SetVisible(Amr.db.char.Logging.LastWipe and true or false)
+
+ local all = isAllAutoLoggingEnabled()
+ _chkAutoAll:SetChecked(all)
+
+ for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
+ for k, difficultyId in pairs(Amr.Difficulties) do
+ _autoChecks[instanceId][difficultyId]:SetChecked(Amr.db.profile.Logging.Auto[instanceId][difficultyId])
+ end
+ end
+end
+
+function Amr:IsLogging()
+ return Amr.db.char.Logging.Enabled
+end
+
+function Amr:ToggleLogging()
+ if not Amr.db.char.Logging.Enabled then
+ Amr:StartLogging()
+ else
+ Amr:StopLogging()
+ end
+end
+
+function Amr:StartLogging()
+
+ local now = time()
+ local oldDuration = 60 * 60 * 24 * 10
+
+ -- prune out entries in log data that are more than 10 days old
+
+ -- player data
+ local playerData = Amr.db.global.Logging.PlayerData
+ if playerData then
+ for name, timeList in pairs(playerData) do
+ for timestamp, dataString in pairs(timeList) do
+ if difftime(now, tonumber(timestamp)) > oldDuration then
+ timeList[timestamp] = nil
+ end
+ end
+
+ if next(timeList) == nil then
+ playerData[name] = nil
+ end
+ end
+ end
+
+ -- same idea with extra info (auras, pets, whatever we end up adding to it)
+ local extraData = Amr.db.global.Logging.PlayerExtras
+ if extraData then
+ for name, timeList in pairs(extraData) do
+ for timestamp, dataString in pairs(timeList) do
+ if difftime(now, tonumber(timestamp)) > oldDuration then
+ timeList[timestamp] = nil
+ end
+ end
+
+ if next(timeList) == nil then
+ extraData[name] = nil
+ end
+ end
+ end
+
+ -- delete wipes that are more than 10 days old
+ if Amr.db.global.Logging.Wipes then
+ local wipes = Amr.db.global.Logging.Wipes
+ local i = 1
+ while i <= #wipes do
+ local t = wipes[i]
+ if difftime(now, t) > oldDuration then
+ table.remove(wipes, i)
+ else
+ i = i + 1
+ end
+ end
+ end
+
+ -- delete the last wipe date if it is more than 10 days old
+ if Amr.db.char.Logging.LastWipe and difftime(now, Amr.db.char.Logging.LastWipe) > oldDuration then
+ Amr.db.char.Logging.LastWipe = nil
+ end
+
+ -- always enable advanced combat logging via our addon, gathers more detailed data for better analysis
+ SetCVar("advancedCombatLogging", 1)
+ LoggingCombat(true)
+ Amr.db.char.Logging.Enabled = true
+
+ self:Print(L.LogChatStart)
+
+ self:UpdateMinimap()
+ self:RefreshLogUi()
+end
+
+function Amr:StopLogging()
+
+ LoggingCombat(false)
+ Amr.db.char.Logging.Enabled = false
+
+ self:Print(L.LogChatStop)
+
+ self:UpdateMinimap()
+ self:RefreshLogUi()
+end
+
+function Amr:Wipe()
+ local t = time()
+ table.insert(Amr.db.global.Logging.Wipes, t)
+ Amr.db.char.Logging.LastWipe = t
+
+ self:Print(L.LogChatWipe(date('%I:%M %p', t)))
+
+ self:RefreshLogUi()
+end
+
+function Amr:UndoWipe()
+
+ local t = Amr.db.char.Logging.LastWipe
+ local wipes = Amr.db.global.Logging.Wipes
+
+ if not t then
+ self:Print(L.LogChatNoWipes)
+ else
+ -- find this wipe and remove it, may not be the last one if this person is raiding on multiple characters
+ for i = #wipes, 1, -1 do
+ if wipes[i] == t then
+ table.remove(wipes, i)
+ break
+ end
+ end
+
+ Amr.db.char.Logging.LastWipe = nil
+ self:Print(L.LogChatUndoWipe(date('%I:%M %p', t)))
+ end
+
+ self:RefreshLogUi()
+end
+
+function Amr:ToggleAutoLog(instanceId, difficultyId)
+
+ local byDiff = Amr.db.profile.Logging.Auto[instanceId]
+ byDiff[difficultyId] = not byDiff[difficultyId]
+
+ self:RefreshLogUi()
+
+ -- see if we should turn logging on right now
+ updateAutoLogging(true)
+end
+
+function Amr:ToggleAllAutoLog()
+
+ local val = not isAllAutoLoggingEnabled()
+
+ for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
+ for k, difficultyId in pairs(Amr.Difficulties) do
+ Amr.db.profile.Logging.Auto[instanceId][difficultyId] = val
+ end
+ end
+
+ self:RefreshLogUi()
+
+ -- see if we should turn logging on right now
+ updateAutoLogging(true)
+end
+
+function Amr:ProcessPlayerSnapshot(msg)
+ if not self:IsLogging() then return end
+
+ -- message will be of format: timestamp\nregion\nrealm\nname\n[stuff]
+ local parts = {}
+ for part in string.gmatch(msg, "([^\n]+)") do
+ table.insert(parts, part)
+ end
+
+ local timestamp = tonumber(parts[1])
+ local name = parts[2] .. ":" .. parts[3] .. ":" .. parts[4]
+ local setup = parts[5]
+
+ -- initialize the player's table
+ local playerList = Amr.db.global.Logging.PlayerData[name]
+ if not playerList then
+ playerList = {}
+ Amr.db.global.Logging.PlayerData[name] = playerList
+ end
+
+ -- find the most recent setup already recorded for this player
+ local previousSetup = nil
+ local previousTime = 0
+ for t, v in pairs(playerList) do
+ if t > previousTime then
+ previousSetup = v
+ previousTime = t
+ end
+ end
+
+ -- if the previous setup is more than 12 hours old, don't consider it
+ if previousSetup and difftime(timestamp, previousTime) > 60 * 60 * 12 then
+ previousSetup = nil
+ end
+
+ -- we only need to keep this setup if it is different than the previous
+ if setup ~= previousSetup then
+ playerList[timestamp] = setup
+ end
+
+end
+
+-- read auras and pet mapping info (pet may not be necessary anymore... but doesn't hurt)
+local function getPlayerExtraData(data, unitId, petId)
+
+ local guid = UnitGUID(unitId)
+ if guid == nil then return end
+
+ local fields = {}
+
+ local buffs = {}
+ for i=1,40 do
+ local _,_,_,count,_,_,_,_,_,_,spellId = UnitAura(unitId, i, "HELPFUL")
+ table.insert(buffs, spellId)
+ end
+ if not buffs or #buffs == 0 then
+ table.insert(fields, "_")
+ else
+ table.insert(fields, Amr.Serializer:ToCompressedNumberList(buffs))
+ end
+
+ local petGuid = UnitGUID(petId)
+ if petGuid then
+ table.insert(fields, guid .. "," .. petGuid)
+ else
+ table.insert(fields, '_')
+ end
+
+ local name = GetUnitName(unitId, true) -- GetRaidRosterInfo(rosterIndex)
+ local realm = GetRealmName()
+ local region = Amr.RegionNames[GetCurrentRegion()]
+ local splitPos = string.find(name, "-")
+ if splitPos ~= nil then
+ realm = string.sub(name, splitPos + 1)
+ name = string.sub(name, 1, splitPos - 1)
+ end
+
+ data[region .. ":" .. realm .. ":" .. name] = table.concat(fields, ";")
+end
+
+local function logPlayerExtraData()
+ if not Amr:IsLogging() or not Amr:IsSupportedInstance() then return end
+
+ local timestamp = time()
+ local units = {}
+ local petUnits = {}
+
+ if IsInRaid() then
+ for i = 1,40 do
+ table.insert(units, "raid" .. i)
+ table.insert(petUnits, "raidpet" .. i)
+ end
+ elseif IsInGroup() then
+ table.insert(units, "player")
+ table.insert(petUnits, "pet")
+ for i = 1,4 do
+ table.insert(units, "party" .. i)
+ table.insert(petUnits, "partypet" .. i)
+ end
+ else
+ return
+ end
+
+ local data = {}
+ for i = 1,#units do
+ getPlayerExtraData(data, units[i], petUnits[i])
+ end
+
+ for name, val in pairs(data) do
+ -- record aura stuff, we never check for duplicates, need to know it at each point in time
+ if Amr.db.global.Logging.PlayerExtras[name] == nil then
+ Amr.db.global.Logging.PlayerExtras[name] = {}
+ end
+ Amr.db.global.Logging.PlayerExtras[name][timestamp] = val
+ end
+end
+
+function Amr:InitializeCombatLog()
+ updateAutoLogging()
+end
+
+Amr:AddEventHandler("UPDATE_INSTANCE_INFO", updateAutoLogging)
+Amr:AddEventHandler("PLAYER_DIFFICULTY_CHANGED", updateAutoLogging)
+Amr:AddEventHandler("PLAYER_REGEN_DISABLED", logPlayerExtraData)
diff -r 75431c084aa0 -r 01b63b8ed811 Constants.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Constants.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,223 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+
+-- min import version that we will read from the website
+Amr.MIN_IMPORT_VERSION = 20
+
+-- min addon version that we will support for inter-addon communication for e.g. the team optimizer
+Amr.MIN_ADDON_VERSION = 23
+
+-- import some constants from the serializer for convenience
+Amr.ChatPrefix = Amr.Serializer.ChatPrefix
+Amr.RegionNames = Amr.Serializer.RegionNames
+Amr.SlotIds = Amr.Serializer.SlotIds
+Amr.SpecIds = Amr.Serializer.SpecIds
+Amr.ClassIds = Amr.Serializer.ClassIds
+Amr.ProfessionIds = Amr.Serializer.ProfessionIds
+Amr.RaceIds = Amr.Serializer.RaceIds
+Amr.FactionIds = Amr.Serializer.FactionIds
+Amr.InstanceIds = Amr.Serializer.InstanceIds
+Amr.SupportedInstanceIds = Amr.Serializer.SupportedInstanceIds
+Amr.ParseItemLink = Amr.Serializer.ParseItemLink
+Amr.IsSupportedInstanceId = Amr.Serializer.IsSupportedInstanceId
+Amr.IsSupportedInstance = Amr.Serializer.IsSupportedInstance
+Amr.SetTokenIds = Amr.Serializer.SetTokenIds
+
+-- map of slot ID to display text
+Amr.SlotDisplayText = {
+ [1] = _G["HEADSLOT"],
+ [2] = _G["NECKSLOT"],
+ [3] = _G["SHOULDERSLOT"],
+ [5] = _G["CHESTSLOT"],
+ [6] = _G["WAISTSLOT"],
+ [7] = _G["LEGSSLOT"],
+ [8] = _G["FEETSLOT"],
+ [9] = _G["WRISTSLOT"],
+ [10] = _G["HANDSSLOT"],
+ [11] = _G["FINGER0SLOT"] .. " 1",
+ [12] = _G["FINGER1SLOT"] .. " 2",
+ [13] = _G["TRINKET0SLOT"] .. " 1",
+ [14] = _G["TRINKET1SLOT"] .. " 2",
+ [15] = _G["BACKSLOT"],
+ [16] = _G["MAINHANDSLOT"],
+ [17] = _G["SECONDARYHANDSLOT"]
+}
+
+Amr.SlotEnumDisplayText = {
+ Head = _G["HEADSLOT"],
+ Neck = _G["NECKSLOT"],
+ Shoulder = _G["SHOULDERSLOT"],
+ Chest = _G["CHESTSLOT"],
+ Waist = _G["WAISTSLOT"],
+ Legs = _G["LEGSSLOT"],
+ Feet = _G["FEETSLOT"],
+ Wrist = _G["WRISTSLOT"],
+ Hands = _G["HANDSSLOT"],
+ Finger1 = _G["FINGER0SLOT"],
+ Finger2 = _G["FINGER0SLOT"],
+ Trinket1 = _G["TRINKET0SLOT"],
+ Trinket2 = _G["TRINKET0SLOT"],
+ Back = _G["BACKSLOT"],
+ MainHand = _G["MAINHANDSLOT"],
+ OffHand = _G["SECONDARYHANDSLOT"]
+}
+
+Amr.SpecIcons = {
+ [1] = "spell_deathknight_bloodpresence", -- DeathKnightBlood
+ [2] = "spell_deathknight_frostpresence", -- DeathKnightFrost
+ [3] = "spell_deathknight_unholypresence", -- DeathKnightUnholy
+ [4] = "spell_nature_starfall", -- DruidBalance
+ [5] = "ability_druid_catform", -- DruidFeral
+ [6] = "ability_racial_bearform", -- DruidGuardian
+ [7] = "spell_nature_healingtouch", -- DruidRestoration
+ [8] = "ability_hunter_bestialdiscipline", -- HunterBeastMastery
+ [9] = "ability_hunter_focusedaim", -- HunterMarksmanship
+ [10] = "ability_hunter_camouflage", -- HunterSurvival
+ [11] = "spell_holy_magicalsentry", -- MageArcane
+ [12] = "spell_fire_firebolt02", -- MageFire
+ [13] = "spell_frost_frostbolt02", -- MageFrost
+ [14] = "spell_monk_brewmaster_spec", -- MonkBrewmaster
+ [15] = "spell_monk_mistweaver_spec", -- MonkMistweaver
+ [16] = "spell_monk_windwalker_spec", -- MonkWindwalker
+ [17] = "spell_holy_holybolt", -- PaladinHoly
+ [18] = "ability_paladin_shieldofthetemplar", -- PaladinProtection
+ [19] = "spell_holy_auraoflight", -- PaladinRetribution
+ [20] = "spell_holy_powerwordshield", -- PriestDiscipline
+ [21] = "spell_holy_guardianspirit", -- PriestHoly
+ [22] = "spell_shadow_shadowwordpain", -- PriestShadow
+ [23] = "ability_rogue_eviscerate", -- RogueAssassination
+ [24] = "ability_backstab", -- RogueCombat
+ [25] = "ability_stealth", -- RogueSubtlety
+ [26] = "spell_nature_lightning", -- ShamanElemental
+ [27] = "spell_nature_lightningshield", -- ShamanEnhancement
+ [28] = "spell_nature_magicimmunity", -- ShamanRestoration
+ [29] = "spell_shadow_deathcoil", -- WarlockAffliction
+ [30] = "spell_shadow_metamorphosis", -- WarlockDemonology
+ [31] = "spell_shadow_rainoffire", -- WarlockDestruction
+ [32] = "ability_warrior_savageblow", -- WarriorArms
+ [33] = "ability_warrior_innerrage", -- WarriorFury
+ [34] = "ability_warrior_defensivestance", -- WarriorProtection
+ [38] = "ability_warrior_defensivestance", -- WarriorProtection, used for special subspec handling
+ [39] = "spell_warrior_gladiatorstance" -- WarriorProtectionGlad, used for special subspec handling
+}
+
+-- instance IDs ordered in preferred display order
+Amr.InstanceIdsOrdered = { 1205, 1228 }
+
+Amr.Difficulties = {
+ Lfr = 17,
+ Normal = 14,
+ Heroic = 15,
+ Mythic = 16
+}
+
+-- get the game's spec id from the AMR spec id
+function Amr.GetGameSpecId(specId)
+ for k, v in pairs(Amr.SpecIds) do
+ if v == specId then return k end
+ end
+ return nil
+end
+
+
+------------------------------------------------------------------------------------------
+-- Item Methods
+------------------------------------------------------------------------------------------
+
+-- item link format: |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:upgradeId:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r
+
+function Amr.CreateItemLink(itemObj)
+
+ if itemObj == nil or itemObj.id == nil or itemObj.id == 0 then return nil end
+
+ local parts = {}
+ table.insert(parts, "item")
+ table.insert(parts, itemObj.id)
+ table.insert(parts, itemObj.enchantId)
+ table.insert(parts, itemObj.gemIds[1])
+ table.insert(parts, itemObj.gemIds[2])
+ table.insert(parts, itemObj.gemIds[3])
+ table.insert(parts, itemObj.gemIds[4])
+
+ if itemObj.suffixId == 0 then
+ table.insert(parts, 0)
+ else
+ table.insert(parts, -math.abs(itemObj.suffixId))
+ end
+
+ table.insert(parts, 0)
+ table.insert(parts, UnitLevel("player"))
+ table.insert(parts, itemObj.upgradeId)
+ table.insert(parts, 0)
+
+ if itemObj.bonusIds then
+ table.insert(parts, #itemObj.bonusIds)
+ for i,v in ipairs(itemObj.bonusIds) do
+ table.insert(parts, v)
+ end
+ end
+
+ return table.concat(parts, ":")
+end
+
+-- a unique ID useful for determining if a player has an item equipped or not
+function Amr.GetItemUniqueId(item, noUpgrade)
+ if item == nil then return "" end
+ local ret = item.id .. ""
+ if item.bonusIds then
+ for i = 1, #item.bonusIds do
+ ret = ret .. "b" .. item.bonusIds[i]
+ end
+ end
+ if item.suffixId ~= 0 then
+ ret = ret .. "s" .. item.suffixId
+ end
+ if not noUpgrade and item.upgradeId ~= 0 then
+ ret = ret .. "u" .. item.upgradeId
+ end
+ return ret
+end
+
+-- the server event for getting item info does not specify which item it just fetched... have to track manually
+local _pendingItemIds = {}
+
+-- helper for getting item information, which is not always guaranteed to be loaded into memory
+function Amr.GetItemInfo(itemIdOrLinkOrName, callback, customArg)
+ if not itemIdOrLinkOrName then
+ callback(customArg)
+ return
+ end
+
+ -- see if we can get the information immediately
+ local name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemIdOrLinkOrName)
+ if name then
+ callback(customArg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice)
+ return
+ end
+
+ -- get the list of registered callbacks for this particular item
+ local list = _pendingItemIds[itemIdOrLinkOrName]
+ -- if there was a list, then just add the callback to the list
+ if list then
+ table.insert(list, { Callback = callback, Arg = customArg })
+ else
+ -- there wasn't a list, so make a new one with this callback
+ _pendingItemIds[itemIdOrLinkOrName] = { { Callback = callback, Arg = customArg } }
+ end
+end
+
+Amr:AddEventHandler("GET_ITEM_INFO_RECEIVED", function()
+ -- go through all unresolved items since we don't know which one was just resolved
+ for itemId, callbacks in pairs(_pendingItemIds) do
+ -- attempt to get the item info again, remove from pending list if we find it
+ local name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemId)
+ if name then
+ _pendingItemIds[itemId] = nil
+
+ -- call each callback
+ for i = 1, #callbacks do
+ callbacks[i].Callback(callbacks[i].Arg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice)
+ end
+ end
+ end
+end)
diff -r 75431c084aa0 -r 01b63b8ed811 Core.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Core.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,663 @@
+-- AskMrRobot
+-- Does cool stuff associated with askmrrobot.com:
+-- Import/Export gear and optimization solutions from/to the website
+-- Improve the combat logging experience and augment it with extra data not available directly in the log file
+-- Team Optimizer convenience functionality
+
+AskMrRobot = LibStub("AceAddon-3.0"):NewAddon("AskMrRobot", "AceEvent-3.0", "AceComm-3.0", "AceConsole-3.0", "AceSerializer-3.0")
+local Amr = AskMrRobot
+Amr.Serializer = LibStub("AskMrRobot-Serializer")
+
+Amr.ADDON_NAME = "AskMrRobot"
+
+-- types of inter-addon messages that we receive, used to parcel them out to the proper handlers
+Amr.MessageTypes = {
+ Version = "_V",
+ VersionRequest = "_VR",
+ Team = "_T"
+}
+
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- minimap icon and LDB support
+local _amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject(Amr.ADDON_NAME, {
+ type = "launcher",
+ text = "Ask Mr. Robot",
+ icon = "Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\icon",
+ OnClick = function(self, button, down)
+ if button == "LeftButton" then
+ if IsControlKeyDown() then
+ Amr:Wipe()
+ else
+ Amr:Toggle()
+ end
+ elseif button == "RightButton" then
+ Amr:EquipGearSet()
+ end
+ end,
+ OnTooltipShow = function(tt)
+ tt:AddLine("Ask Mr. Robot", 1, 1, 1);
+ tt:AddLine(" ");
+ tt:AddLine(L.MinimapTooltip)
+ end
+})
+local _icon = LibStub("LibDBIcon-1.0")
+
+
+-- initialize the database
+local function initializeDb()
+
+ local defaults = {
+ char = {
+ FirstUse = true, -- true if this is first time use, gets cleared after seeing the export help splash window
+ SubSpecs = {}, -- last seen subspecs for this character, used to deal with some ambiguous specs
+ Equipped = {}, -- for each spec group (1 or 2), slot id to item link
+ BagItems = {}, -- list of item links for bag
+ BankItems = {}, -- list of item links for bank
+ VoidItems = {}, -- list of item links for void storage
+ BagItemsAndCounts = {}, -- used mainly for the shopping list
+ BankItemsAndCounts = {}, -- used mainly for the shopping list
+ GearSets = {}, -- imported gear sets, key by spec group (1 or 2), slot id to item object
+ ExtraItemData = {}, -- for each spec group (1 or 2): mainly for legacy support, item id to object with socketColor and duplicateId information
+ ExtraGemData = {}, -- for each spec group (1 or 2): gem enchant id to gem display information, and data used to detect identical gems (mainly for legacy support)
+ ExtraEnchantData = {}, -- for each spec group (1 or 2): enchant id to enchant display information and material information
+ Logging = { -- character logging settings
+ Enabled = false, -- whether logging is currently on or not
+ LastZone = nil, -- last zone the player was in
+ LastDiff = nil, -- last difficulty for the last zone the player was in
+ LastWipe = nil -- last time a wipe was called by this player
+ },
+ TeamOpt = {
+ AllItems = {}, -- all equippable items no matter where it is, list of item unique ids, used to determine when a player gains a new equippable item
+ History = {}, -- history of drops since joining the current group
+ Rolls = {}, -- current loot choices for a loot distribution in progress
+ Role = nil, -- Leader or Member, changes UI to the mode most appropriate for this user
+ Loot = {}, -- the last loot seen by the master looter
+ LootGuid = nil, -- guid of the last unit looted by the master looter, will be "container" if there is no target
+ LootInProgress = false -- true if looting is currently in progress
+ }
+ },
+ profile = {
+ minimap = { -- minimap hide/show and position settings
+ hide = false
+ },
+ window = {}, -- main window position settings
+ lootWindow = {}, -- loot window position settings
+ shopWindow = {}, -- shopping list window position settings
+ options = {
+ autoGear = false, -- auto-equip saved gear sets when changing specs
+ shopAh = false -- auto-show shopping list at AH
+ },
+ Logging = { -- global logging settings
+ Auto = {} -- for each instanceId, for each difficultyId, true if auto-logging enabled
+ }
+ },
+ global = {
+ Region = nil, -- region that this user is in, all characters on the same account should be the same region
+ Shopping = {}, -- shopping list data stored globally for access on any character
+ Logging = { -- a lot of log data is stored globally for simplicity, can only be raiding with one character at a time
+ Wipes = {}, -- times that a wipe was called
+ PlayerData = {}, -- player data gathered at fight start
+ PlayerExtras = {} -- player extra data like auras, gathered at fight start
+ },
+ TeamOpt = { -- this stuff is stored globally in case a player e.g. switches to an alt in a raid group
+ LootGear = {}, -- gear info that needs to be transmitted with the next loot
+ Rankings = {}, -- last rankings imported by the loot ranker
+ RankingString = nil -- last ranking string imported, kept around for efficient serialization
+ }
+ }
+ }
+
+ -- set defaults for auto-logging
+ for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
+ local byDiff = defaults.profile.Logging.Auto[instanceId]
+ if not byDiff then
+ byDiff = {}
+ defaults.profile.Logging.Auto[instanceId] = byDiff
+ end
+
+ for k, difficultyId in pairs(Amr.Difficulties) do
+ if byDiff[difficultyId] == nil then
+ byDiff[difficultyId] = false
+ end
+ end
+ end
+
+ Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb2", defaults)
+
+ Amr.db.RegisterCallback(Amr, "OnProfileChanged", "RefreshConfig")
+ Amr.db.RegisterCallback(Amr, "OnProfileCopied", "RefreshConfig")
+ Amr.db.RegisterCallback(Amr, "OnProfileReset", "RefreshConfig")
+end
+
+function Amr:OnInitialize()
+
+ initializeDb()
+
+ Amr:RegisterChatCommand("amr", "SlashCommand")
+
+ _icon:Register(Amr.ADDON_NAME, _amrLDB, self.db.profile.minimap)
+
+ -- listen for inter-addon communication
+ self:RegisterComm(Amr.ChatPrefix, "OnCommReceived")
+end
+
+local _enteredWorld = false
+local _pendingInit = false
+
+function finishInitialize()
+
+ -- record region, the only thing that we still can't get from the log file
+ Amr.db.global.Region = Amr.RegionNames[GetCurrentRegion()]
+
+ -- make sure that some initialization is deferred until after PLAYER_ENTERING_WORLD event so that data we need is available;
+ -- also delay this initialization for a few extra seconds to deal with some event spam that is otherwise hard to identify and ignore when a player logs in
+ Amr.Wait(5, function()
+ Amr:InitializeVersions()
+ Amr:InitializeGear()
+ Amr:InitializeExport()
+ Amr:InitializeCombatLog()
+ Amr:InitializeTeamOpt()
+ end)
+end
+
+function onPlayerEnteringWorld()
+
+ _enteredWorld = true
+
+ if _pendingInit then
+ finishInitialize()
+ _pendingInit = false
+ end
+end
+
+function Amr:OnEnable()
+
+ -- listen for changes to the snapshot enable state, and always make sure it is enabled if using the core AskMrRobot addon
+ self:RegisterMessage("AMR_SNAPSHOT_STATE_CHANGED", function(eventName, isEnabled)
+ if not isEnabled then
+ -- immediately re-enable on any attempt to disable
+ Amr.Serializer:EnableSnapshots()
+ end
+ end)
+ self.Serializer:EnableSnapshots()
+
+ -- update based on current configuration whenever enabled
+ self:RefreshConfig()
+
+ -- if we have fully entered the world, do initialization; otherwise wait for PLAYER_ENTERING_WORLD to continue
+ if not _enteredWorld then
+ _pendingInit = true
+ else
+ _pendingInit = false
+ finishInitialize()
+ end
+end
+
+function Amr:OnDisable()
+ -- disabling is not supported
+end
+
+
+----------------------------------------------------------------------------------------
+-- Slash Commands
+----------------------------------------------------------------------------------------
+local _slashMethods = {
+ hide = "Hide",
+ show = "Show",
+ toggle = "Toggle",
+ equip = "EquipGearSet", -- parameter is "primary" or "secondary", or no parameter to toggle
+ version = "PrintVersions",
+ wipe = "Wipe",
+ undowipe = "UndoWipe",
+ test = "Test"
+}
+
+function Amr:SlashCommand(input)
+ input = string.lower(input)
+ local parts = {}
+ for w in input:gmatch("%S+") do
+ table.insert(parts, w)
+ end
+
+ if #parts == 0 then return end
+
+ local func = _slashMethods[parts[1]]
+ if not func then return end
+
+ local funcArgs = {}
+ for i = 2, #parts do
+ table.insert(funcArgs, parts[i])
+ end
+
+ Amr[func](Amr, unpack(funcArgs))
+end
+
+
+----------------------------------------------------------------------------------------
+-- Configuration
+----------------------------------------------------------------------------------------
+
+-- refresh all state based on the current values of configuration options
+function Amr:RefreshConfig()
+
+ self:UpdateMinimap()
+ self:RefreshOptionsUi()
+ self:RefreshLogUi()
+end
+
+function Amr:UpdateMinimap()
+
+ if self.db.profile.minimap.hide or not Amr:IsEnabled() then
+ _icon:Hide(Amr.ADDON_NAME)
+ else
+ -- change icon color if logging
+ if Amr:IsLogging() then
+ _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green'
+ else
+ _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon'
+ end
+
+ _icon:Show(Amr.ADDON_NAME)
+ end
+end
+
+
+----------------------------------------------------------------------------------------
+-- Version Checking
+----------------------------------------------------------------------------------------
+
+-- version of addon being run by each person in the player's raid or group
+Amr.GroupVersions = {}
+
+local function toGroupVersionKey(realm, name)
+ realm = string.gsub(realm, "%s+", "")
+ return name .. "-" .. realm
+end
+
+-- prune out version information for players no longer in the current raid group
+local function pruneVersionInfo()
+
+ local newVersions = {}
+ local units = Amr:GetGroupUnitIdentifiers()
+
+ for i, unitId in ipairs(units) do
+ local realm, name = Amr:GetRealmAndName(unitId)
+ if realm then
+ local key = toGroupVersionKey(realm, name)
+ newVersions[key] = Amr.GroupVersions[key]
+ end
+ end
+
+ Amr.GroupVersions = newVersions
+end
+
+-- send version information to other people in the same raid group
+local function sendVersionInfo()
+
+ local realm = GetRealmName()
+ local name = UnitName("player")
+ local ver = GetAddOnMetadata(Amr.ADDON_NAME, "Version")
+
+ local msg = string.format("%s\n%s\n%s\n%s", Amr.MessageTypes.Version, realm, name, ver)
+ Amr:SendAmrCommMessage(msg)
+end
+
+local function onVersionInfoReceived(message)
+
+ -- message will be of format: realm\nname\nversion
+ local parts = {}
+ for part in string.gmatch(message, "([^\n]+)") do
+ table.insert(parts, part)
+ end
+
+ local key = toGroupVersionKey(parts[2], parts[3])
+ local ver = parts[4]
+
+ Amr.GroupVersions[key] = tonumber(ver)
+
+ -- make sure that versions are properly pruned in case this message arrived late and the player has since been removed from the group
+ pruneVersionInfo()
+end
+
+-- get the addon version another person in the player's raid/group is running, or 0 if they are not running the addon
+function Amr:GetAddonVersion(realm, name)
+ local ver = Amr.GroupVersions[toGroupVersionKey(realm, name)]
+ return ver or 0
+end
+
+function Amr:PrintVersions()
+
+ if not IsInGroup() and not IsInRaid() then
+ self:Print(L.VersionChatNotGrouped)
+ return
+ end
+
+ local units = self:GetGroupUnitIdentifiers()
+
+ local msg = {}
+ table.insert(msg, L.VersionChatTitle)
+
+ for i, unitId in ipairs(units) do
+ local realm, name = self:GetRealmAndName(unitId)
+ if realm then
+ local key = toGroupVersionKey(realm, name)
+ local ver = Amr.GroupVersions[key]
+ if not ver then
+ table.insert(msg, key .. " |cFFFF0000" .. L.VersionChatNotInstalled .. "|r")
+ else
+ table.insert(msg, key .. " v" .. ver)
+ end
+ end
+ end
+
+ msg = table.concat(msg, "\n")
+ print(msg)
+end
+
+function Amr:InitializeVersions()
+ Amr:AddEventHandler("GROUP_ROSTER_UPDATE", pruneVersionInfo)
+ Amr:AddEventHandler("GROUP_ROSTER_UPDATE", sendVersionInfo)
+
+ -- request version information from anyone in my group upon initialization
+ if IsInGroup() or IsInRaid() then
+ Amr:SendAmrCommMessage(Amr.MessageTypes.VersionRequest)
+ end
+end
+
+
+----------------------------------------------------------------------------------------
+-- Generic Helpers
+----------------------------------------------------------------------------------------
+
+local _waitTable = {}
+local _waitFrame = nil
+
+-- execute the specified function after the specified delay (in seconds)
+function Amr.Wait(delay, func, ...)
+ if not _waitFrame then
+ _waitFrame = CreateFrame("Frame", "AmrWaitFrame", UIParent)
+ _waitFrame:SetScript("OnUpdate", function (self, elapse)
+ local count = #_waitTable
+ local i = 1
+ while(i <= count) do
+ local waitRecord = table.remove(_waitTable, i)
+ local d = table.remove(waitRecord, 1)
+ local f = table.remove(waitRecord, 1)
+ local p = table.remove(waitRecord, 1)
+ if d > elapse then
+ table.insert(_waitTable, i, { d-elapse, f, p })
+ i = i + 1
+ else
+ count = count - 1
+ f(unpack(p))
+ end
+ end
+ end)
+ end
+ table.insert(_waitTable, { delay, func, {...} })
+ return true
+end
+
+-- helper to iterate over a table in order by its keys
+function Amr.spairs(t, order)
+ -- collect the keys
+ local keys = {}
+ for k in pairs(t) do keys[#keys+1] = k end
+
+ -- if order function given, sort by it by passing the table and keys a, b,
+ -- otherwise just sort the keys
+ if order then
+ table.sort(keys, function(a,b) return order(t, a, b) end)
+ else
+ table.sort(keys)
+ end
+
+ -- return the iterator function
+ local i = 0
+ return function()
+ i = i + 1
+ if keys[i] then
+ return keys[i], t[keys[i]]
+ end
+ end
+end
+
+function Amr.StartsWith(str, prefix)
+ if string.len(str) < string.len(prefix) then return false end
+ return string.sub(str, 1, string.len(prefix)) == prefix
+end
+
+-- helper to get the unit identifiers (e.g. to pass to GetUnitName) for all members of the player's current group/raid
+function Amr:GetGroupUnitIdentifiers()
+
+ local units = {}
+ if IsInRaid() then
+ for i = 1,40 do
+ table.insert(units, "raid" .. i)
+ end
+ elseif IsInGroup() then
+ table.insert(units, "player")
+ for i = 1,4 do
+ table.insert(units, "party" .. i)
+ end
+ else
+ table.insert(units, "player")
+ end
+
+ return units
+end
+
+-- helper to get the realm and name from a unitId (e.g. "player" or "raid1")
+function Amr:GetRealmAndName(unitId)
+
+ local name = GetUnitName(unitId, true)
+ if not name then return end
+
+ local realm = GetRealmName()
+ local splitPos = string.find(name, "-")
+ if splitPos ~= nil then
+ realm = string.sub(name, splitPos + 1)
+ name = string.sub(name, 1, splitPos - 1)
+ end
+
+ return realm, name
+end
+
+-- find the unitid of a player given the name and realm... this comes from the server so the realm will be in english...
+-- TODO: more robust handling of players with same name but different realms in the same group on non-english clients
+function Amr:GetUnitId(unitRealm, unitName)
+
+ local nameMatches = {}
+
+ local units = Amr:GetGroupUnitIdentifiers()
+ for i, unitId in ipairs(units) do
+ local realm, name = Amr:GetRealmAndName(unitId)
+ if realm then
+ -- remove spaces to ensure proper matches
+ realm = string.gsub(realm, "%s+", "")
+ unitRealm = string.gsub(unitRealm, "%s+", "")
+
+ if unitRealm == realm and unitName == name then return unitId end
+ if unitName == name then
+ table.insert(nameMatches, unitId)
+ end
+ end
+ end
+
+ -- only one player with same name, must be the player of interest
+ if #nameMatches == 1 then return nameMatches[1] end
+
+ -- could not find or ambiguous
+ return nil
+end
+
+
+-- scanning tooltip b/c for some odd reason the api has no way to get basic item properties...
+-- so you have to generate a fake item tooltip and search for pre-defined strings in the display text
+local _scanTt
+function Amr:GetScanningTooltip()
+ if not _scanTt then
+ _scanTt = CreateFrame("GameTooltip", "AmrUiScanTooltip", nil, "GameTooltipTemplate")
+ _scanTt:SetOwner(UIParent, "ANCHOR_NONE")
+ end
+ return _scanTt
+end
+
+local function scanTooltipHelper(txt, ...)
+ for i = 1, select("#", ...) do
+ local region = select(i, ...)
+ if region and region:GetObjectType() == "FontString" then
+ local text = region:GetText() -- string or nil
+ print(text)
+ end
+ end
+end
+
+-- search the tooltip for txt, returns true if it is encountered on any line
+function Amr:IsTextInTooltip(tt, txt)
+ local regions = { tt:GetRegions() }
+ for i, region in ipairs(regions) do
+ if region and region:GetObjectType() == "FontString" then
+ if region:GetText() == txt then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+-- helper to determine if an item in the player's bag is soulbound
+function Amr:IsSoulbound(bagId, slotId)
+ local tt = self:GetScanningTooltip()
+ tt:ClearLines()
+ if bagId then
+ tt:SetBagItem(bagId, slotId)
+ else
+ tt:SetInventoryItem("player", slotId)
+ end
+ return self:IsTextInTooltip(tt, ITEM_SOULBOUND)
+end
+
+-- helper to determine if an item has a unique constraint
+function Amr:IsUnique(bagId, slotId)
+ local tt = self:GetScanningTooltip()
+ tt:ClearLines()
+ if bagId then
+ tt:SetBagItem(bagId, slotId)
+ else
+ tt:SetInventoryItem("player", slotId)
+ end
+ if self:IsTextInTooltip(tt, ITEM_UNIQUE_EQUIPPABLE) then return true end
+ if self:IsTextInTooltip(tt, ITEM_UNIQUE) then return true end
+ return false
+end
+
+
+----------------------------------------------------------------------------------------
+-- Inter-Addon Communication
+----------------------------------------------------------------------------------------
+function Amr:SendAmrCommMessage(message, channel)
+ -- prepend version to all messages
+ local v = GetAddOnMetadata(Amr.ADDON_NAME, "Version")
+ message = v .. "\r" .. message
+
+ Amr:SendCommMessage(Amr.ChatPrefix, message, channel or "RAID")
+end
+
+function Amr:OnCommReceived(prefix, message, distribution, sender)
+
+ local parts = {}
+ for part in string.gmatch(message, "([^\r]+)") do
+ table.insert(parts, part)
+ end
+
+ local ver = parts[1]
+ if ver then ver = tonumber(ver) end
+ if ver then
+ -- newest versions of the addon start all messages with a version number
+ message = parts[2]
+ end
+
+ -- we always allow version checks, even from old versions of the addon that aren't otherwise compatible
+ if Amr.StartsWith(message, Amr.MessageTypes.Version) then
+ -- version checking between group members
+ if Amr.StartsWith(message, Amr.MessageTypes.VersionRequest) then
+ sendVersionInfo()
+ else
+ onVersionInfoReceived(message)
+ end
+
+ return
+ end
+
+ -- any other kind of message is ignored if the version is too old
+ if not ver or ver < Amr.MIN_ADDON_VERSION then return end
+
+ if Amr.StartsWith(message, Amr.MessageTypes.Team) then
+ -- if fully initialized, process team optimizer messages
+ if Amr["ProcessTeamMessage"] then
+ Amr:ProcessTeamMessage(message)
+ end
+ else
+ -- if we are fully loaded, process a player snapshot when it is received (combat logging)
+ if Amr["ProcessPlayerSnapshot"] then
+ self:ProcessPlayerSnapshot(message)
+ end
+ end
+end
+
+
+----------------------------------------------------------------------------------------
+-- Events
+----------------------------------------------------------------------------------------
+local _eventHandlers = {}
+
+local function handleEvent(eventName, ...)
+ local list = _eventHandlers[eventName]
+ if list then
+ --print(eventName .. " handled")
+ for i, handler in ipairs(list) do
+ if type(handler) == "function" then
+ handler(select(1, ...))
+ else
+ Amr[handler](Amr, select(1, ...))
+ end
+ end
+ end
+end
+
+-- WoW and Ace seem to work on a "one handler" kind of approach to events (as far as I can tell from the sparse documentation of both).
+-- This is a simple wrapper to allow adding multiple handlers to the same event, thus allowing better encapsulation of code from file to file.
+function Amr:AddEventHandler(eventName, methodOrName)
+ local list = _eventHandlers[eventName]
+ if not list then
+ list = {}
+ _eventHandlers[eventName] = list
+ Amr:RegisterEvent(eventName, handleEvent)
+ end
+ table.insert(list, methodOrName)
+end
+
+Amr:AddEventHandler("PLAYER_ENTERING_WORLD", onPlayerEnteringWorld)
+
+
+----------------------------------------------------------------------------------------
+-- Debugging
+----------------------------------------------------------------------------------------
+--[[
+function Amr:Test(val1, val2, val3)
+
+ local link = GetLootSlotLink(tonumber(val1))
+ local index = Amr:TestLootIndex(link)
+ print("loot index: " .. index)
+
+ if val2 then
+ local candidate = Amr:TestLootCandidate(link, val2, val3)
+ print("loot candidate: " .. candidate)
+
+ GiveMasterLoot(index, candidate)
+ end
+end
+]]
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Export.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Export.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,264 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _lastExport = nil
+local _txt = nil
+
+local function createLabel(container, text, width)
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(width or 800)
+ lbl:SetText(text)
+ lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
+ container:AddChild(lbl)
+ return lbl
+end
+
+local function onSplashClose()
+ Amr:HideCover()
+ Amr.db.char.FirstUse = false
+end
+
+-- render a splash screen with first-time help
+local function renderSplash(container)
+ local panel = Amr:RenderCoverChrome(container, 700, 450)
+
+ local lbl = createLabel(panel, L.ExportSplashTitle, 650)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
+ lbl:SetPoint("TOP", panel.content, "TOP", 0, -10)
+
+ local lbl2 = createLabel(panel, L.ExportSplashSubtitle, 650)
+ lbl2:SetJustifyH("CENTER")
+ lbl2:SetFont(Amr.CreateFont("Bold", 18, Amr.Colors.TextTan))
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -20)
+
+ lbl = createLabel(panel, L.ExportSplash1, 650)
+ lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
+ lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -70)
+
+ lbl2 = createLabel(panel, L.ExportSplash2, 650)
+ lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
+ lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -15)
+
+ lbl = createLabel(panel, L.ExportSplash3, 650)
+ lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
+ lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -15)
+
+ local btn = AceGUI:Create("AmrUiButton")
+ btn:SetText(L.ExportSplashClose)
+ btn:SetBackgroundColor(Amr.Colors.Green)
+ btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btn:SetWidth(120)
+ btn:SetHeight(28)
+ btn:SetPoint("BOTTOM", panel.content, "BOTTOM", 0, 20)
+ btn:SetCallback("OnClick", onSplashClose)
+ panel:AddChild(btn)
+end
+
+-- renders the main UI for the Export tab
+function Amr:RenderTabExport(container)
+
+ local lbl = createLabel(container, L.ExportTitle)
+ lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
+ lbl:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40)
+
+ local lbl2 = createLabel(container, L.ExportHelp1)
+ lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -10)
+
+ lbl = createLabel(container, L.ExportHelp2)
+ lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -10)
+
+ lbl2 = createLabel(container, L.ExportHelp3)
+ lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -10)
+
+ lbl = createLabel(container, L.ExportHelp4)
+ lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -10)
+
+ _txt = AceGUI:Create("AmrUiTextarea")
+ _txt:SetWidth(800)
+ _txt:SetHeight(300)
+ _txt:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -20)
+ _txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
+ container:AddChild(_txt)
+
+ local data = self:ExportCharacter()
+ local txt = Amr.Serializer:SerializePlayerData(data, true)
+ _txt:SetText(txt)
+ _txt:SetFocus(true)
+
+ -- update shopping list data
+ Amr:UpdateShoppingData(data)
+
+ -- show help splash if first time a user is using this
+ if Amr.db.char.FirstUse then
+ Amr:ShowCover(renderSplash)
+ AceGUI:ClearFocus()
+ end
+end
+
+function Amr:ReleaseTabExport()
+end
+
+function Amr:GetExportText()
+ return _txt:GetText()
+end
+
+
+-- use some local variables to deal with the fact that a user can close the bank before a scan completes
+local _lastBankBagId = nil
+local _lastBankSlotId = nil
+
+local function scanBag(bagId, isBank, bagTable, bagItemsWithCount)
+ local numSlots = GetContainerNumSlots(bagId)
+ for slotId = 1, numSlots do
+ local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
+ if itemLink ~= nil then
+ local itemData = Amr.Serializer.ParseItemLink(itemLink)
+ if itemData ~= nil then
+ -- only add equippable items to bag data
+ if IsEquippableItem(itemLink) or Amr.SetTokenIds[itemData.id] then
+ if isBank then
+ _lastBankBagId = bagId
+ _lastBankSlotId = slotId
+ end
+
+ table.insert(bagTable, itemLink)
+ end
+
+ -- all items and counts, used for e.g. shopping list and reagents, etc.
+ if bagItemsWithCount then
+ if bagItemsWithCount[itemData.id] then
+ bagItemsWithCount[itemData.id] = bagItemsWithCount[itemData.id] + itemCount
+ else
+ bagItemsWithCount[itemData.id] = itemCount
+ end
+ end
+ end
+ end
+ end
+end
+
+-- get the player's current gear and save it, also returns the data from GetPlayerData for efficiency
+local function getEquipped()
+ local data = Amr.Serializer:GetPlayerData(Amr.db.char.SubSpecs)
+ local spec = GetActiveSpecGroup()
+
+ Amr.db.char.Equipped[spec] = data.Equipped[spec]
+ Amr.db.char.SubSpecs[spec] = data.SubSpecs[spec]
+
+ return data
+end
+
+local function scanBags()
+
+ local bagItems = {}
+ local itemsAndCounts = {}
+
+ scanBag(BACKPACK_CONTAINER, false, bagItems, itemsAndCounts) -- backpack
+ for bagId = 1, NUM_BAG_SLOTS do
+ scanBag(bagId, false, bagItems, itemsAndCounts)
+ end
+
+ Amr.db.char.BagItems = bagItems
+ Amr.db.char.BagItemsAndCounts = itemsAndCounts
+end
+
+-- scan the player's bank and save the contents, must be at the bank
+local function scanBank()
+
+ local bankItems = {}
+ local itemsAndCounts = {}
+
+ scanBag(BANK_CONTAINER, true, bankItems, itemsAndCounts)
+ scanBag(REAGENTBANK_CONTAINER, true, bankItems, itemsAndCounts)
+ for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
+ scanBag(bagId, true, bankItems, itemsAndCounts)
+ end
+
+ -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data
+ if _lastBankBagId ~= nil then
+ local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId)
+ if itemLink ~= nil then --still open
+ Amr.db.char.BankItems = bankItems
+ Amr.db.char.BankItemsAndCounts = itemsAndCounts
+ end
+ end
+
+end
+
+-- scan the player's void storage and save the contents, must be at void storage
+local function scanVoid()
+
+ if IsVoidStorageReady() then
+ local voidItems = {}
+ local VOID_STORAGE_MAX = 80
+ local VOID_STORAGE_PAGES = 2
+
+ for page = 1,VOID_STORAGE_PAGES do
+ for i = 1,VOID_STORAGE_MAX do
+ local itemId = GetVoidItemInfo(page, i)
+ if itemId then
+ local itemLink = GetVoidItemHyperlinkString(((page - 1) * VOID_STORAGE_MAX) + i);
+ if itemLink then
+ tinsert(voidItems, itemLink)
+ end
+ end
+ end
+ end
+
+ Amr.db.char.VoidItems = voidItems
+ end
+
+end
+
+local function getRepStanding(factionId)
+ local name, description, standingId, _ = GetFactionInfoByID(factionId)
+ return standingId - 1; -- our rep enum correspond to what the armory returns, are 1 less than what the game returns
+end
+
+local function getReputations()
+ local reps = {}
+
+ local repList = {1375,1376,1270,1269,1341,1337,1387,1388,1435}
+ for i, repId in pairs(repList) do
+ local standing = getRepStanding(repId)
+ if standing >= 0 then
+ reps[repId] = standing
+ end
+ end
+
+ return reps
+end
+
+-- Returns a data object containing all information about the current player needed for an export:
+-- gear, spec, reputations, bag, bank, and void storage items.
+function Amr:ExportCharacter()
+
+ local data = getEquipped()
+ scanBags()
+
+ -- get extra data that is not necessary for the base serializer, but that we add here for completeness
+ data.Equipped = Amr.db.char.Equipped
+ data.Reputations = getReputations()
+ data.BagItems = Amr.db.char.BagItems
+ data.BankItems = Amr.db.char.BankItems
+ data.VoidItems = Amr.db.char.VoidItems
+
+ return data
+end
+
+function Amr:InitializeExport()
+ Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID)
+ if unitID and unitID ~= "player" then return end
+ getEquipped()
+ end)
+end
+
+Amr:AddEventHandler("BANKFRAME_OPENED", scanBank)
+Amr:AddEventHandler("PLAYERBANKSLOTS_CHANGED", scanBank)
+
+Amr:AddEventHandler("VOID_STORAGE_OPEN", scanVoid)
+Amr:AddEventHandler("VOID_STORAGE_CONTENTS_UPDATE", scanVoid)
+Amr:AddEventHandler("VOID_STORAGE_DEPOSIT_UPDATE", scanVoid)
+Amr:AddEventHandler("VOID_STORAGE_UPDATE", scanVoid)
diff -r 75431c084aa0 -r 01b63b8ed811 Gear.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Gear.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,745 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _gearTabs
+local _activeTab
+
+-- Returns a number indicating how different two items are (0 means the same, higher means more different)
+local function countItemDifferences(item1, item2)
+ if item1 == nil and item2 == nil then return 0 end
+
+ -- different items (id + bonus ids + suffix, constitutes a different physical drop)
+ if Amr.GetItemUniqueId(item1, true) ~= Amr.GetItemUniqueId(item2, true) then
+ return 1000
+ end
+
+ -- different upgrade levels of the same item (only for older gear, player has control over upgrade level)
+ if item1.upgradeId ~= item2.upgradeId then
+ return 100
+ end
+
+ -- different gems
+ local gemDiffs = 0
+ for i = 1, 3 do
+ if item1.gemIds[i] ~= item2.gemIds[i] then
+ gemDiffs = gemDiffs + 1
+ end
+ end
+
+ -- different enchants
+ local enchantDiff = 0
+ if item1.enchantId ~= item2.enchantId then
+ enchantDiff = 1
+ end
+
+ return gemDiffs + enchantDiff
+end
+
+-- given a table of items (keyed or indexed doesn't matter) find closest match to item, or nil if none are a match
+local function findMatchingItemFromTable(item, list, bestLink, bestItem, bestDiff, bestLoc, usedItems, tableType)
+ if not list then return nil end
+
+ for k,v in pairs(list) do
+ local listItem = Amr.ParseItemLink(v)
+ if listItem then
+ local diff = countItemDifferences(item, listItem)
+ if diff < bestDiff then
+ -- each physical item can only be used once, the usedItems table has items we can't use in this search
+ local key = string.format("%s_%s", tableType, k)
+ if not usedItems[key] then
+ bestLink = v
+ bestItem = listItem
+ bestDiff = diff
+ bestLoc = string.format("%s_%s", tableType, k)
+ end
+ end
+ if diff == 0 then break end
+ end
+ end
+
+ return bestLink, bestItem, bestDiff, bestLoc
+end
+
+-- search the player's equipped gear, bag, bank, and void storage for an item that best matches the specified item
+function Amr:FindMatchingItem(item, player, usedItems)
+ if not item then return nil end
+
+ local equipped = player.Equipped and player.Equipped[player.ActiveSpec] or nil
+ local bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, nil, 1000, nil, usedItems, "equip")
+ bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bag")
+ bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BankItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bank")
+ bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.VoidItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "void")
+
+ if bestDiff >= 1000 then
+ return nil, nil, 1000
+ else
+ usedItems[bestLoc] = true
+ return bestLink, bestItem, bestDiff
+ end
+end
+
+local function renderEmptyGear(container)
+
+ local panelBlank = AceGUI:Create("AmrUiPanel")
+ panelBlank:SetLayout("None")
+ panelBlank:SetBackgroundColor(Amr.Colors.Black, 0.4)
+ panelBlank:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0)
+ panelBlank:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
+ container:AddChild(panelBlank)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(L.GearBlank)
+ lbl:SetWidth(700)
+ lbl:SetJustifyH("MIDDLE")
+ lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
+ lbl:SetPoint("BOTTOM", panelBlank.content, "CENTER", 0, 20)
+ panelBlank:AddChild(lbl)
+
+ local lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetText(L.GearBlank2)
+ lbl2:SetWidth(700)
+ lbl2:SetJustifyH("MIDDLE")
+ lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
+ lbl2:SetPoint("TOP", lbl.frame, "CENTER", 0, -20)
+ panelBlank:AddChild(lbl2)
+end
+
+local function renderGear(spec, container)
+
+ local player = Amr:ExportCharacter()
+ local gear = Amr.db.char.GearSets[spec]
+ local equipped = player.Equipped[player.ActiveSpec]
+
+ if not gear then
+ -- no gear has been imported for this spec so show a message
+ renderEmptyGear(container)
+ else
+ local panelGear = AceGUI:Create("AmrUiPanel")
+ panelGear:SetLayout("None")
+ panelGear:SetBackgroundColor(Amr.Colors.Black, 0.3)
+ panelGear:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0)
+ panelGear:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT", -300, 0)
+ container:AddChild(panelGear)
+
+ local panelMods = AceGUI:Create("AmrUiPanel")
+ panelMods:SetLayout("None")
+ panelMods:SetPoint("TOPLEFT", panelGear.frame, "TOPRIGHT", 15, 0)
+ panelMods:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
+ panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3)
+ container:AddChild(panelMods)
+
+ -- spec icon
+ local icon = AceGUI:Create("AmrUiIcon")
+ icon:SetIconBorderColor(Amr.Colors.Classes[player.Class])
+ icon:SetWidth(48)
+ icon:SetHeight(48)
+
+ local iconSpec
+ if player.SubSpecs[spec] then
+ iconSpec = player.SubSpecs[spec]
+ else
+ iconSpec = player.Specs[spec]
+ end
+
+ icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[iconSpec])
+ icon:SetPoint("TOPLEFT", panelGear.content, "TOPLEFT", 10, -10)
+ panelGear:AddChild(icon)
+
+ local btnEquip = AceGUI:Create("AmrUiButton")
+ btnEquip:SetText(L.GearButtonEquip(spec))
+ btnEquip:SetBackgroundColor(Amr.Colors.Green)
+ btnEquip:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
+ btnEquip:SetWidth(300)
+ btnEquip:SetHeight(26)
+ btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0)
+ btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0)
+ btnEquip:SetCallback("OnClick", function(widget)
+ Amr:EquipGearSet(spec)
+ end)
+ panelGear:AddChild(btnEquip)
+
+ local btnShop = AceGUI:Create("AmrUiButton")
+ btnShop:SetText(L.GearButtonShop)
+ btnShop:SetBackgroundColor(Amr.Colors.Blue)
+ btnShop:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
+ btnShop:SetWidth(300)
+ btnShop:SetHeight(26)
+ btnShop:SetPoint("LEFT", btnEquip.frame, "RIGHT", 75, 0)
+ btnShop:SetPoint("RIGHT", panelMods.content, "RIGHT", -20, 0)
+ btnShop:SetCallback("OnClick", function(widget) Amr:ShowShopWindow() end)
+ panelMods:AddChild(btnShop)
+
+ -- each physical item can only be used once, this tracks ones we have already used
+ local usedItems = {}
+
+ -- gear list
+ local prevElem = icon
+ for slotNum = 1, #Amr.SlotIds do
+ local slotId = Amr.SlotIds[slotNum]
+
+ local equippedItemLink = equipped and equipped[slotId] or nil
+ local equippedItem = Amr.ParseItemLink(equippedItemLink)
+ local optimalItem = gear[slotId]
+ local optimalItemLink = Amr.CreateItemLink(optimalItem)
+
+ -- see if item is currently equipped, is false if don't have any item for that slot (e.g. OH for a 2-hander)
+ local isEquipped = false
+ if equippedItem and optimalItem and Amr.GetItemUniqueId(equippedItem) == Amr.GetItemUniqueId(optimalItem) then
+ isEquipped = true
+ end
+
+ -- find the item in the player's inventory that best matches what the optimization wants to use
+ local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
+
+ -- slot label
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(Amr.SlotDisplayText[slotId])
+ lbl:SetWidth(85)
+ lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
+ lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12)
+ panelGear:AddChild(lbl)
+ prevElem = lbl
+
+ -- ilvl label
+ local lblIlvl = AceGUI:Create("AmrUiLabel")
+ lblIlvl:SetWidth(45)
+ lblIlvl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0)
+ panelGear:AddChild(lblIlvl)
+
+ -- equipped label
+ local lblEquipped = AceGUI:Create("AmrUiLabel")
+ lblEquipped:SetWidth(20)
+ lblEquipped:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
+ lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0)
+ lblEquipped:SetText(isEquipped and "E" or "")
+ panelGear:AddChild(lblEquipped)
+
+ -- item name/link label
+ local lblItem = AceGUI:Create("AmrUiLabel")
+ lblItem:SetWordWrap(false)
+ lblItem:SetWidth(345)
+ lblItem:SetFont(Amr.CreateFont(isEquipped and "Regular" or "Bold", isEquipped and 14 or 15, Amr.Colors.White))
+ lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0)
+ panelGear:AddChild(lblItem)
+
+ -- fill the name/ilvl labels, which may require asynchronous loading of item information
+ if optimalItemLink then
+ Amr.GetItemInfo(optimalItemLink, function(obj, name, link, quality, iLevel)
+ -- set item name, tooltip, and ilvl
+ obj.nameLabel:SetText(link:gsub("%[", ""):gsub("%]", ""))
+ Amr:SetItemTooltip(obj.nameLabel, link)
+ obj.ilvlLabel:SetText(iLevel)
+ end, { ilvlLabel = lblIlvl, nameLabel = lblItem })
+ end
+
+ -- modifications
+ if optimalItem then
+ local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id]
+
+ -- gems
+ if itemInfo and itemInfo.socketColors then
+ for i = 1, #itemInfo.socketColors do
+ local g = optimalItem.gemIds[i]
+ local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
+
+ -- highlight for gem that doesn't match
+ local socketBorder = AceGUI:Create("AmrUiPanel")
+ socketBorder:SetLayout("None")
+ socketBorder:SetBackgroundColor(Amr.Colors.Black, isGemEquipped and 0 or 1)
+ socketBorder:SetWidth(26)
+ socketBorder:SetHeight(26)
+ socketBorder:SetPoint("LEFT", lblItem.frame, "RIGHT", 30, 0)
+ if isGemEquipped then
+ socketBorder:SetAlpha(0.3)
+ end
+ panelMods:AddChild(socketBorder)
+
+ local socketBg = AceGUI:Create("AmrUiIcon")
+ socketBg:SetLayout("None")
+ socketBg:SetBorderWidth(2)
+ socketBg:SetIconBorderColor(Amr.Colors.Green, isGemEquipped and 0 or 1)
+ socketBg:SetWidth(24)
+ socketBg:SetHeight(24)
+ socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1)
+ socketBorder:AddChild(socketBg)
+
+ local socketIcon = AceGUI:Create("AmrUiIcon")
+ socketIcon:SetBorderWidth(1)
+ socketIcon:SetIconBorderColor(Amr.Colors.White)
+ socketIcon:SetWidth(18)
+ socketIcon:SetHeight(18)
+ socketIcon:SetPoint("CENTER", socketBg.content, "CENTER")
+ socketBg:AddChild(socketIcon)
+
+ -- get icon for optimized gem
+ if g ~= 0 then
+ local gemInfo = Amr.db.char.ExtraGemData[spec][g]
+ if gemInfo then
+ Amr.GetItemInfo(gemInfo.id, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)
+ -- set icon and a tooltip
+ obj:SetIcon(texture)
+ Amr:SetItemTooltip(obj, link)
+ end, socketIcon)
+ end
+ end
+ end
+ end
+
+ -- enchant
+ if optimalItem.enchantId and optimalItem.enchantId ~= 0 then
+ local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == optimalItem.enchantId
+
+ local lblEnchant = AceGUI:Create("AmrUiLabel")
+ lblEnchant:SetWordWrap(false)
+ lblEnchant:SetWidth(170)
+ lblEnchant:SetFont(Amr.CreateFont(isEnchantEquipped and "Regular" or "Bold", 14, isEnchantEquipped and Amr.Colors.TextGray or Amr.Colors.White))
+ lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0)
+
+ local enchInfo = Amr.db.char.ExtraEnchantData[spec][optimalItem.enchantId]
+ if enchInfo then
+ lblEnchant:SetText(enchInfo.text)
+
+ Amr.GetItemInfo(enchInfo.itemId, function(obj, name, link)
+ Amr:SetItemTooltip(obj, link)
+ end, lblEnchant)
+ --Amr:SetSpellTooltip(lblEnchant, enchInfo.spellId)
+ end
+ panelMods:AddChild(lblEnchant)
+ end
+ end
+
+ prevElem = lbl
+ end
+ end
+end
+
+local function onGearTabSelected(container, event, group)
+ container:ReleaseChildren()
+ _activeTab = group
+ renderGear(tonumber(group), container)
+end
+
+local function onImportClick(widget)
+ Amr:ShowImportWindow()
+end
+
+-- renders the main UI for the Gear tab
+function Amr:RenderTabGear(container)
+
+ local btnImport = AceGUI:Create("AmrUiButton")
+ btnImport:SetText(L.GearButtonImportText)
+ btnImport:SetBackgroundColor(Amr.Colors.Orange)
+ btnImport:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnImport:SetWidth(120)
+ btnImport:SetHeight(26)
+ btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81)
+ btnImport:SetCallback("OnClick", onImportClick)
+ container:AddChild(btnImport)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(L.GearImportNote)
+ lbl:SetWidth(100)
+ lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
+ lbl:SetJustifyH("MIDDLE")
+ lbl:SetPoint("TOP", btnImport.frame, "BOTTOM", 0, -5)
+ container:AddChild(lbl)
+
+ local lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetText(L.GearTipTitle)
+ lbl2:SetWidth(140)
+ lbl2:SetFont(Amr.CreateFont("Italic", 20, Amr.Colors.Text))
+ lbl2:SetJustifyH("MIDDLE")
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -50)
+ container:AddChild(lbl2)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetText(L.GearTipText)
+ lbl:SetWidth(140)
+ lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text))
+ lbl:SetJustifyH("MIDDLE")
+ lbl:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -5)
+ container:AddChild(lbl)
+
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetText(L.GearTipCommands)
+ lbl2:SetWidth(130)
+ lbl2:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text))
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 10, -5)
+ container:AddChild(lbl2)
+
+ --[[
+ local btnClean = AceGUI:Create("AmrUiButton")
+ btnClean:SetText(L.GearButtonCleanText)
+ btnClean:SetBackgroundColor(Amr.Colors.Orange)
+ btnClean:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnClean:SetWidth(120)
+ btnClean:SetHeight(26)
+ btnClean:SetPoint("BOTTOMLEFT", container.content, "BOTTOMLEFT", 0, 5)
+ btnClean:SetCallback("OnClick", function(widget) Amr:CleanBags() end)
+ container:AddChild(btnClean)
+ ]]
+
+ local t = AceGUI:Create("AmrUiTabGroup")
+ t:SetLayout("None")
+ t:SetTabs({
+ {text=L.GearTabPrimary, value="1", style="bold"},
+ {text=L.GearTabSecondary, value="2", style="bold"}
+ })
+ t:SetCallback("OnGroupSelected", onGearTabSelected)
+ t:SetPoint("TOPLEFT", container.content, "TOPLEFT", 144, -30)
+ t:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
+ container:AddChild(t)
+ _gearTabs = t;
+
+ if not _activeTab then
+ _activeTab = tostring(GetActiveSpecGroup())
+ end
+
+ t:SelectTab(_activeTab)
+end
+
+-- do cleanup when the gear tab is released
+function Amr:ReleaseTabGear()
+ _gearTabs = nil
+end
+
+-- show and update the gear tab for the specified spec
+function Amr:ShowGearTab(spec)
+ if not _gearTabs then return end
+
+ _activeTab = tostring(spec)
+ _gearTabs:SelectTab(_activeTab)
+end
+
+-- refresh display of the current gear tab
+function Amr:RefreshGearTab()
+ if not _gearTabs then return end
+ _gearTabs:SelectTab(_activeTab)
+end
+
+
+------------------------------------------------------------------------------------------------
+-- Gear Set Management
+------------------------------------------------------------------------------------------------
+local _waitingForSpec = 0
+local _waitingForItemLock = nil
+local _pendingEquip = nil
+
+-- scan a bag for the best matching item
+local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
+ local numSlots = GetContainerNumSlots(bagId)
+ for slotId = 1, numSlots do
+ local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
+ -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked
+ if itemLink then
+ local bagItem = Amr.ParseItemLink(itemLink)
+ if bagItem ~= nil then
+ local diff = countItemDifferences(item, bagItem)
+ if diff < bestDiff then
+ bestItem = { bag = bagId, slot = slotId }
+ bestDiff = diff
+ bestLink = itemLink
+ end
+ end
+ end
+ end
+ return bestItem, bestDiff, bestLink
+end
+
+-- find the first empty slot in the player's backpack+bags
+local function findFirstEmptyBagSlot()
+
+ local bagIds = {}
+ table.insert(bagIds, BACKPACK_CONTAINER)
+ for bagId = 1, NUM_BAG_SLOTS do
+ table.insert(bagIds, bagId)
+ end
+
+ for i, bagId in ipairs(bagIds) do
+ local numSlots = GetContainerNumSlots(bagId)
+ for slotId = 1, numSlots do
+ local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
+ if not itemLink then
+ return bagId, slotId
+ end
+ end
+ end
+
+ return nil, nil
+end
+
+local function finishEquipGearSet()
+ if not _pendingEquip then return end
+
+ _pendingEquip.tries = _pendingEquip.tries + 1
+ if _pendingEquip.tries > 16 then
+ _pendingEquip = nil
+ else
+ -- start over again, trying any items that could not be equipped in the previous pass (unique constraints)
+ Amr:EquipGearSet(_pendingEquip.spec)
+ end
+end
+
+-- equip the next slot in a pending equip
+local function tryEquipNextItem()
+ if not _pendingEquip then return end
+
+ local item = _pendingEquip.itemsToEquip[_pendingEquip.nextSlot]
+
+ local bestItem = nil
+ local bestLink = nil
+ local bestDiff = 1000
+
+ -- find the best matching item
+
+ -- equipped items
+ for slotNum = 1, #Amr.SlotIds do
+ local slotId = Amr.SlotIds[slotNum]
+ local itemLink = GetInventoryItemLink("player", slotId)
+ if itemLink then
+ local invItem = Amr.ParseItemLink(itemLink)
+ if invItem ~= nil then
+ local diff = countItemDifferences(item, invItem)
+ if diff < bestDiff then
+ bestItem = { slot = slotId }
+ bestDiff = diff
+ bestLink = itemLink
+ end
+ end
+ end
+ end
+
+ -- inventory
+ bestItem, bestDiff, bestLink = scanBagForItem(item, BACKPACK_CONTAINER, bestItem, bestDiff, bestLink)
+ for bagId = 1, NUM_BAG_SLOTS do
+ bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
+ end
+
+ -- bank
+ bestItem, bestDiff = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink)
+ for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
+ bestItem, bestDiff = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
+ end
+
+ ClearCursor()
+
+ if not bestItem then
+ -- stop if we can't find an item
+ Amr:Print(L.GearEquipErrorNotFound)
+ Amr:Print(L.GearEquipErrorNotFound2)
+ _pendingEquip = nil
+ return
+
+ elseif bestItem and bestItem.bag and bestItem.bag >= NUM_BAG_SLOTS + 1 and bestItem.bag <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS then
+ -- find first empty bag slot
+ local invBag, invSlot = findFirstEmptyBagSlot()
+ if not invBag then
+ -- stop if bags are too full
+ Amr:Print(L.GearEquipErrorBagFull)
+ _pendingEquip = nil
+ return
+ end
+
+ -- move from bank to bag
+ PickupContainerItem(bestItem.bag, bestItem.slot)
+ PickupContainerItem(invBag, invSlot)
+
+ -- set flag so that when we clear cursor and release the item lock, we can respond to the event and continue
+ _waitingForItemLock = {
+ bagId = invBag,
+ slotId = invSlot
+ }
+
+ ClearCursor()
+
+ -- now we need to wait for game event to continue and try this item again after it is in our bag
+ return
+ else
+ if not Amr:IsSoulbound(bestItem.bag, bestItem.slot) then
+ -- if an item is not soulbound, then warn the user and quit
+ Amr:Print(L.GearEquipErrorSoulbound(bestLink))
+ _pendingEquip = nil
+ return
+ else
+ local slotId = _pendingEquip.nextSlot
+
+ -- an item in the player's bags or already equipped, equip it
+ _pendingEquip.bag = bestItem.bag
+ _pendingEquip.slot = bestItem.slot
+ _pendingEquip.destSlot = slotId
+
+ if bestItem.bag then
+ PickupContainerItem(bestItem.bag, bestItem.slot)
+ else
+ PickupInventoryItem(bestItem.slot)
+ end
+ PickupInventoryItem(slotId)
+ ClearCursor()
+ end
+ end
+
+end
+
+local function onItemUnlocked(bagId, slotId)
+
+ if _waitingForItemLock then
+ -- waiting on a move from bank to bags to complete, just continue as normal afterwards
+ if bagId == _waitingForItemLock.bagId and slotId == _waitingForItemLock.slotId then
+ _waitingForItemLock = nil
+ tryEquipNextItem()
+ end
+
+ elseif _pendingEquip and _pendingEquip.destSlot then
+ -- waiting on an item swap to complete successfully so that we can go on to the next item
+
+ -- inventory slot we're swapping to is still locked, can't continue yet
+ if IsInventoryItemLocked(_pendingEquip.destSlot) then return end
+
+ if _pendingEquip.bag then
+ local _, _, locked = GetContainerItemInfo(_pendingEquip.bag, _pendingEquip.slot)
+ -- the bag slot we're swapping from is still locked, can't continue yet
+ if locked then return end
+ else
+ -- inventory slot we're swapping from is still locked, can't continue yet
+ if IsInventoryItemLocked(_pendingEquip.slot) then return end
+ end
+
+ -- move on to the next item, this item is done
+ _pendingEquip.itemsToEquip[_pendingEquip.destSlot] = nil
+ _pendingEquip.destSlot = nil
+ _pendingEquip.bag = nil
+ _pendingEquip.slot = nil
+
+ _pendingEquip.remaining = _pendingEquip.remaining - 1
+ if _pendingEquip.remaining > 0 then
+ for slotId, item in pairs(_pendingEquip.itemsToEquip) do
+ _pendingEquip.nextSlot = slotId
+ break
+ end
+ tryEquipNextItem()
+ else
+ finishEquipGearSet()
+ end
+
+ end
+end
+
+local function startEquipGearSet(spec)
+
+ local gear = Amr.db.char.GearSets[spec]
+ if not gear then
+ Amr:Print(L.GearEquipErrorEmpty)
+ return
+ end
+
+ local player = Amr:ExportCharacter()
+
+ local itemsToEquip = {}
+ local remaining = 0
+ local usedItems = {}
+ local firstSlot = nil
+
+ -- check for items that need to be equipped
+ for slotNum = 1, #Amr.SlotIds do
+ local slotId = Amr.SlotIds[slotNum]
+
+ local old = player.Equipped[spec][slotId]
+ old = Amr.ParseItemLink(old)
+
+ local new = gear[slotId]
+
+ local diff = countItemDifferences(old, new)
+ if diff < 1000 then
+ -- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems)
+ local bestLink, bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems)
+ if bestDiff and bestDiff < diff then
+ itemsToEquip[slotId] = new
+ remaining = remaining + 1
+ end
+ else
+ itemsToEquip[slotId] = new
+ remaining = remaining + 1
+ end
+ end
+
+ if remaining > 0 then
+ _pendingEquip = {
+ tries = _pendingEquip and _pendingEquip.spec == spec and _pendingEquip.tries or 0,
+ spec = spec,
+ itemsToEquip = itemsToEquip,
+ remaining = remaining,
+ nextSlot = firstSlot
+ }
+
+ -- starting item
+ for slotId, item in pairs(_pendingEquip.itemsToEquip) do
+ _pendingEquip.nextSlot = slotId
+ break
+ end
+
+ tryEquipNextItem()
+ else
+ _pendingEquip = nil
+ end
+end
+
+local function onActiveTalentGroupChanged()
+ local auto = Amr.db.profile.options.autoGear
+ local currentSpec = GetActiveSpecGroup()
+
+ if currentSpec == _waitingForSpec or auto then
+ -- spec is what we want, now equip the gear
+ startEquipGearSet(currentSpec)
+ end
+
+ _waitingForSpec = 0
+end
+
+-- activate the specified spec and then equip the saved gear set for either primary (1) or secondary (2) spec
+function Amr:EquipGearSet(spec)
+
+ -- if no argument, then toggle spec
+ if not spec then
+ spec = GetActiveSpecGroup() == 1 and 2 or 1
+ end
+
+ -- allow some flexibility in the arguments
+ if spec == "primary" or spec == "Primary" then spec = 1 end
+ if spec == "secondary" or spec == "Secondary" then spec = 2 end
+ if spec == "1" or spec == "2" then spec = tonumber(spec) end
+
+ -- only spec 1 or 2 are valid
+ if spec ~= 1 and spec ~= 2 then return end
+
+ if UnitAffectingCombat("player") then
+ Amr:Print(L.GearEquipErrorCombat)
+ return
+ end
+
+ _waitingForSpec = spec
+
+ local currentSpec = GetActiveSpecGroup()
+ if currentSpec ~= spec then
+ SetActiveSpecGroup(spec)
+ else
+ onActiveTalentGroupChanged()
+ end
+end
+
+-- moves any gear in bags to the bank if not part of main or off spec gear set
+function Amr:CleanBags()
+ -- TODO: implement
+end
+
+function Amr:InitializeGear()
+ Amr:AddEventHandler("ACTIVE_TALENT_GROUP_CHANGED", onActiveTalentGroupChanged)
+
+ Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID)
+ if unitID and unitID ~= "player" then return end
+ Amr:RefreshGearTab()
+ end)
+
+ Amr:AddEventHandler("ITEM_UNLOCKED", onItemUnlocked)
+end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Import.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Import.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,347 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _txtImport
+local _lblError
+
+local function onImportOkClick(widget)
+ local txt = _txtImport:GetText()
+ local msg = Amr:ImportCharacter(txt)
+ if msg then
+ _lblError:SetText(msg)
+ _txtImport:SetFocus(true)
+ else
+ Amr:HideCover()
+ Amr:RefreshGearTab()
+ end
+end
+
+local function onImportCancelClick(widget)
+ Amr:HideCover()
+end
+
+local function renderImportWindow(container)
+
+ local panelImport = Amr:RenderCoverChrome(container, 700, 450)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(600)
+ lbl:SetText(L.ImportHeader)
+ lbl:SetPoint("TOP", panelImport.content, "TOP", 0, -10)
+ panelImport:AddChild(lbl)
+
+ _txtImport = AceGUI:Create("AmrUiTextarea")
+ _txtImport:SetWidth(600)
+ _txtImport:SetHeight(300)
+ _txtImport:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
+ _txtImport:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
+ panelImport:AddChild(_txtImport)
+
+ local btnImportOk = AceGUI:Create("AmrUiButton")
+ btnImportOk:SetText(L.ImportButtonOk)
+ btnImportOk:SetBackgroundColor(Amr.Colors.Green)
+ btnImportOk:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnImportOk:SetWidth(120)
+ btnImportOk:SetHeight(28)
+ btnImportOk:SetPoint("TOPLEFT", _txtImport.frame, "BOTTOMLEFT", 0, -10)
+ btnImportOk:SetCallback("OnClick", onImportOkClick)
+ panelImport:AddChild(btnImportOk)
+
+ local btnImportCancel = AceGUI:Create("AmrUiButton")
+ btnImportCancel:SetText(L.ImportButtonCancel)
+ btnImportCancel:SetBackgroundColor(Amr.Colors.Green)
+ btnImportCancel:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnImportCancel:SetWidth(120)
+ btnImportCancel:SetHeight(28)
+ btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0)
+ btnImportCancel:SetCallback("OnClick", onImportCancelClick)
+ panelImport:AddChild(btnImportCancel)
+
+ _lblError = AceGUI:Create("AmrUiLabel")
+ _lblError:SetWidth(600)
+ _lblError:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Red))
+ _lblError:SetText("")
+ _lblError:SetPoint("TOPLEFT", btnImportOk.frame, "BOTTOMLEFT", 0, -20)
+ panelImport:AddChild(_lblError)
+
+end
+
+function Amr:ShowImportWindow()
+ -- this is shown as a modal dialog
+ Amr:ShowCover(renderImportWindow)
+
+ _txtImport:SetText("")
+ _txtImport:SetFocus(true)
+end
+
+----------------------------------------------------------------------------
+-- Import Parsing
+----------------------------------------------------------------------------
+
+--
+-- Import a character, returning nil on success, otherwise an error message, import result stored in the db.
+--
+function Amr:ImportCharacter(data, isTest)
+
+ -- make sure all data is up to date before importing and get a local copy of player's current state
+ local currentPlayerData = self:ExportCharacter()
+
+ if data == nil or string.len(data) == 0 then
+ return L.ImportErrorEmpty
+ end
+
+ -- if multiple specs are included in the data, parse each individually, then quit
+ local specParts = { strsplit("\n", data) }
+ if #specParts > 1 then
+ for i = 1, #specParts do
+ local err = self:ImportCharacter(specParts[i], isTest)
+ if err ~= nil then
+ return err
+ end
+ end
+ return
+ end
+
+ local data1 = { strsplit("$", data) }
+ if #data1 ~= 3 then
+ return L.ImportErrorFormat
+ end
+
+ local parts = { strsplit(";", data1[2]) }
+
+ -- require a minimum version
+ local ver = tonumber(parts[1])
+ if ver < Amr.MIN_IMPORT_VERSION then
+ return L.ImportErrorVersion
+ end
+
+ -- require name match (don't match realm due to language issues for now)
+ if not isTest then
+ local region = parts[2]
+ local realm = parts[3]
+ local name = parts[4]
+ if name ~= currentPlayerData.Name then
+ local importPlayerName = name .. " (" .. realm .. ")"
+ local you = currentPlayerData.Name .. " (" .. currentPlayerData.Realm .. ")"
+ return L.ImportErrorChar(importPlayerName, you)
+ end
+
+ -- require race match
+ local race = tonumber(parts[6])
+ if race ~= Amr.RaceIds[currentPlayerData.Race] then
+ return L.ImportErrorRace
+ end
+
+ -- require faction match
+ local faction = tonumber(parts[7])
+ if faction ~= Amr.FactionIds[currentPlayerData.Faction] then
+ return L.ImportErrorFaction
+ end
+
+ -- require level match
+ local level = tonumber(parts[8])
+ if level ~= currentPlayerData.Level then
+ return L.ImportErrorLevel
+ end
+ end
+
+ -- if we make it this far, the data is valid, so read item information
+ local specSlot = tonumber(parts[10])
+
+ local importData = {}
+
+ local itemInfo = {}
+ local gemInfo = {}
+ local enchantInfo = {}
+
+ local prevItemId = 0
+ local prevGemId = 0
+ local prevEnchantId = 0
+ local prevUpgradeId = 0
+ local prevBonusId = 0
+ local digits = {
+ ["-"] = true,
+ ["0"] = true,
+ ["1"] = true,
+ ["2"] = true,
+ ["3"] = true,
+ ["4"] = true,
+ ["5"] = true,
+ ["6"] = true,
+ ["7"] = true,
+ ["8"] = true,
+ ["9"] = true,
+ }
+ for i = 16, #parts do
+ local itemString = parts[i]
+ if itemString ~= "" and itemString ~= "_" then
+ local tokens = {}
+ local bonusIds = {}
+ local hasBonuses = false
+ local token = ""
+ local prop = "i"
+ local tokenComplete = false
+ for j = 1, string.len(itemString) do
+ local c = string.sub(itemString, j, j)
+ if digits[c] == nil then
+ tokenComplete = true
+ else
+ token = token .. c
+ end
+
+ if tokenComplete or j == string.len(itemString) then
+ local val = tonumber(token)
+ if prop == "i" then
+ val = val + prevItemId
+ prevItemId = val
+ elseif prop == "u" then
+ val = val + prevUpgradeId
+ prevUpgradeId = val
+ elseif prop == "b" then
+ val = val + prevBonusId
+ prevBonusId = val
+ elseif prop == "x" or prop == "y" or prop == "z" then
+ val = val + prevGemId
+ prevGemId = val
+ elseif prop == "e" then
+ val = val + prevEnchantId
+ prevEnchantId = val
+ end
+
+ if prop == "b" then
+ table.insert(bonusIds, val)
+ hasBonuses = true
+ else
+ tokens[prop] = val
+ end
+
+ token = ""
+ tokenComplete = false
+
+ -- we have moved on to the next token
+ prop = c
+ end
+ end
+
+ local obj = {}
+ importData[tonumber(tokens["s"])] = obj
+
+ obj.id = tokens["i"]
+ obj.suffixId = tokens["f"] or 0
+ obj.upgradeId = tokens["u"] or 0
+ obj.enchantId = tokens["e"] or 0
+
+ obj.gemIds = {}
+ table.insert(obj.gemIds, tokens["x"] or 0)
+ table.insert(obj.gemIds, tokens["y"] or 0)
+ table.insert(obj.gemIds, tokens["z"] or 0)
+ table.insert(obj.gemIds, 0)
+
+ if hasBonuses then
+ obj.bonusIds = bonusIds
+ end
+
+ local itemObj = {}
+ itemObj.id = obj.id
+ itemInfo[obj.id] = itemObj
+
+ -- look for any socket color information, add to our extra data
+ if tokens["c"] then
+ itemObj.socketColors = {}
+ for j = 1, string.len(tokens["c"]) do
+ table.insert(itemObj.socketColors, tonumber(string.sub(tokens["c"], j, j)))
+ end
+ end
+
+ -- look for item ID duplicate info, deals with old SoO items
+ if tokens["d"] then
+ itemObj.duplicateId = tonumber(tokens["d"])
+ itemInfo[itemObj.duplicateId] = itemObj
+ end
+
+ end
+ end
+
+ -- now read any extra display information
+ parts = { strsplit("@", data1[3]) }
+ for i = 1, #parts do
+ local infoParts = { strsplit("\\", parts[i]) }
+
+ if infoParts[1] == "g" then
+
+ local gemObj = {}
+ gemObj.enchantId = tonumber(infoParts[2])
+ gemObj.id = tonumber(infoParts[3])
+
+ local identicalGems = infoParts[4]
+ if string.len(identicalGems) > 0 then
+ gemObj.identicalGroup = {}
+ identicalGems = { strsplit(",", identicalGems) }
+ for j = 1, #identicalGems do
+ gemObj.identicalGroup[tonumber(identicalGems[j])] = true
+ end
+ end
+
+ gemObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.StatsShort[s] end)
+ if infoParts[6] == nil or string.len(infoParts[6]) == 0 then
+ gemObj.identicalItemGroup = {[gemObj.id]=true}
+ else
+ local identicalIds = { strsplit(',', infoParts[6]) }
+ gemObj.identicalItemGroup = {}
+ for j = 1, #identicalIds do
+ gemObj.identicalItemGroup[tonumber(identicalIds[j])] = true
+ end
+ end
+
+ gemInfo[gemObj.enchantId] = gemObj
+
+ elseif infoParts[1] == "e" then
+
+ local enchObj = {}
+ enchObj.id = tonumber(infoParts[2])
+ enchObj.itemId = tonumber(infoParts[3])
+ enchObj.spellId = tonumber(infoParts[4])
+ enchObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.StatsShort[s] end)
+
+ local mats = infoParts[6]
+ if string.len(mats) > 0 then
+ enchObj.materials = {}
+ mats = { strsplit(",", mats) }
+ for j = 1, #mats do
+ local kv = { strsplit("=", mats[j]) }
+ enchObj.materials[tonumber(kv[1])] = tonumber(kv[2])
+ end
+ end
+
+ enchantInfo[enchObj.id] = enchObj
+
+ end
+ end
+
+ if isTest then
+ print("spec " .. specSlot)
+ -- print result for debugging
+ for k,v in pairs(importData) do
+ local blah = Amr.CreateItemLink(v)
+ --print(blah)
+ local name, link = GetItemInfo(blah)
+ print(link)
+ if link == nil then
+ print(blah)
+ print("bad item: " .. v.id)
+ end
+ end
+
+
+ else
+ -- we have succeeded, record the result
+ Amr.db.char.GearSets[specSlot] = importData
+ Amr.db.char.ExtraItemData[specSlot] = itemInfo
+ Amr.db.char.ExtraGemData[specSlot] = gemInfo
+ Amr.db.char.ExtraEnchantData[specSlot] = enchantInfo
+
+ -- also update shopping list after import
+ Amr:UpdateShoppingData(currentPlayerData)
+ end
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceAddon-3.0/AceAddon-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceAddon-3.0/AceAddon-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,674 @@
+--- **AceAddon-3.0** provides a template for creating addon objects.
+-- It'll provide you with a set of callback functions that allow you to simplify the loading
+-- process of your addon.\\
+-- Callbacks provided are:\\
+-- * **OnInitialize**, which is called directly after the addon is fully loaded.
+-- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
+-- * **OnDisable**, which is only called when your addon is manually being disabled.
+-- @usage
+-- -- A small (but complete) addon, that doesn't do anything,
+-- -- but shows usage of the callbacks.
+-- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+--
+-- function MyAddon:OnInitialize()
+-- -- do init tasks here, like loading the Saved Variables,
+-- -- or setting up slash commands.
+-- end
+--
+-- function MyAddon:OnEnable()
+-- -- Do more initialization here, that really enables the use of your addon.
+-- -- Register Events, Hook functions, Create Frames, Get information from
+-- -- the game that wasn't available in OnInitialize
+-- end
+--
+-- function MyAddon:OnDisable()
+-- -- Unhook, Unregister Events, Hide frames that you created.
+-- -- You would probably only use an OnDisable if you want to
+-- -- build a "standby" mode, or be able to toggle modules on/off.
+-- end
+-- @class file
+-- @name AceAddon-3.0.lua
+-- @release $Id: AceAddon-3.0.lua 1084 2013-04-27 20:14:11Z nevcairiel $
+
+local MAJOR, MINOR = "AceAddon-3.0", 12
+local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceAddon then return end -- No Upgrade needed.
+
+AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame
+AceAddon.addons = AceAddon.addons or {} -- addons in general
+AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon.
+AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized
+AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled
+AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon
+
+-- Lua APIs
+local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
+local fmt, tostring = string.format, tostring
+local select, pairs, next, type, unpack = select, pairs, next, type, unpack
+local loadstring, assert, error = loadstring, assert, error
+local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler
+
+--[[
+ xpcall safecall implementation
+]]
+local xpcall = xpcall
+
+local function errorhandler(err)
+ return geterrorhandler()(err)
+end
+
+local function CreateDispatcher(argCount)
+ local code = [[
+ local xpcall, eh = ...
+ local method, ARGS
+ local function call() return method(ARGS) end
+
+ local function dispatch(func, ...)
+ method = func
+ if not method then return end
+ ARGS = ...
+ return xpcall(call, eh)
+ end
+
+ return dispatch
+ ]]
+
+ local ARGS = {}
+ for i = 1, argCount do ARGS[i] = "arg"..i end
+ code = code:gsub("ARGS", tconcat(ARGS, ", "))
+ return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {__index=function(self, argCount)
+ local dispatcher = CreateDispatcher(argCount)
+ rawset(self, argCount, dispatcher)
+ return dispatcher
+end})
+Dispatchers[0] = function(func)
+ return xpcall(func, errorhandler)
+end
+
+local function safecall(func, ...)
+ -- we check to see if the func is passed is actually a function here and don't error when it isn't
+ -- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
+ -- present execution should continue without hinderance
+ if type(func) == "function" then
+ return Dispatchers[select('#', ...)](func, ...)
+ end
+end
+
+-- local functions that will be implemented further down
+local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype
+
+-- used in the addon metatable
+local function addontostring( self ) return self.name end
+
+-- Check if the addon is queued for initialization
+local function queuedForInitialization(addon)
+ for i = 1, #AceAddon.initializequeue do
+ if AceAddon.initializequeue[i] == addon then
+ return true
+ end
+ end
+ return false
+end
+
+--- Create a new AceAddon-3.0 addon.
+-- Any libraries you specified will be embeded, and the addon will be scheduled for
+-- its OnInitialize and OnEnable callbacks.
+-- The final addon object, with all libraries embeded, will be returned.
+-- @paramsig [object ,]name[, lib, ...]
+-- @param object Table to use as a base for the addon (optional)
+-- @param name Name of the addon object to create
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a simple addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0")
+--
+-- -- Create a Addon object based on the table of a frame
+-- local MyFrame = CreateFrame("Frame")
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0")
+function AceAddon:NewAddon(objectorname, ...)
+ local object,name
+ local i=1
+ if type(objectorname)=="table" then
+ object=objectorname
+ name=...
+ i=2
+ else
+ name=objectorname
+ end
+ if type(name)~="string" then
+ error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2)
+ end
+ if self.addons[name] then
+ error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2)
+ end
+
+ object = object or {}
+ object.name = name
+
+ local addonmeta = {}
+ local oldmeta = getmetatable(object)
+ if oldmeta then
+ for k, v in pairs(oldmeta) do addonmeta[k] = v end
+ end
+ addonmeta.__tostring = addontostring
+
+ setmetatable( object, addonmeta )
+ self.addons[name] = object
+ object.modules = {}
+ object.orderedModules = {}
+ object.defaultModuleLibraries = {}
+ Embed( object ) -- embed NewModule, GetModule methods
+ self:EmbedLibraries(object, select(i,...))
+
+ -- add to queue of addons to be initialized upon ADDON_LOADED
+ tinsert(self.initializequeue, object)
+ return object
+end
+
+
+--- Get the addon object by its name from the internal AceAddon registry.
+-- Throws an error if the addon object cannot be found (except if silent is set).
+-- @param name unique name of the addon object
+-- @param silent if true, the addon is optional, silently return nil if its not found
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+function AceAddon:GetAddon(name, silent)
+ if not silent and not self.addons[name] then
+ error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2)
+ end
+ return self.addons[name]
+end
+
+-- - Embed a list of libraries into the specified addon.
+-- This function will try to embed all of the listed libraries into the addon
+-- and error if a single one fails.
+--
+-- **Note:** This function is for internal use by :NewAddon/:NewModule
+-- @paramsig addon, [lib, ...]
+-- @param addon addon object to embed the libs in
+-- @param lib List of libraries to embed into the addon
+function AceAddon:EmbedLibraries(addon, ...)
+ for i=1,select("#", ... ) do
+ local libname = select(i, ...)
+ self:EmbedLibrary(addon, libname, false, 4)
+ end
+end
+
+-- - Embed a library into the addon object.
+-- This function will check if the specified library is registered with LibStub
+-- and if it has a :Embed function to call. It'll error if any of those conditions
+-- fails.
+--
+-- **Note:** This function is for internal use by :EmbedLibraries
+-- @paramsig addon, libname[, silent[, offset]]
+-- @param addon addon object to embed the library in
+-- @param libname name of the library to embed
+-- @param silent marks an embed to fail silently if the library doesn't exist (optional)
+-- @param offset will push the error messages back to said offset, defaults to 2 (optional)
+function AceAddon:EmbedLibrary(addon, libname, silent, offset)
+ local lib = LibStub:GetLibrary(libname, true)
+ if not lib and not silent then
+ error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2)
+ elseif lib and type(lib.Embed) == "function" then
+ lib:Embed(addon)
+ tinsert(self.embeds[addon], libname)
+ return true
+ elseif lib then
+ error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2)
+ end
+end
+
+--- Return the specified module from an addon object.
+-- Throws an error if the addon object cannot be found (except if silent is set)
+-- @name //addon//:GetModule
+-- @paramsig name[, silent]
+-- @param name unique name of the module
+-- @param silent if true, the module is optional, silently return nil if its not found (optional)
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- -- Get the Module
+-- MyModule = MyAddon:GetModule("MyModule")
+function GetModule(self, name, silent)
+ if not self.modules[name] and not silent then
+ error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2)
+ end
+ return self.modules[name]
+end
+
+local function IsModuleTrue(self) return true end
+
+--- Create a new module for the addon.
+-- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\
+-- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as
+-- an addon object.
+-- @name //addon//:NewModule
+-- @paramsig name[, prototype|lib[, lib, ...]]
+-- @param name unique name of the module
+-- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional)
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a module with some embeded libraries
+-- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0")
+--
+-- -- Create a module with a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0")
+function NewModule(self, name, prototype, ...)
+ if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end
+ if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end
+
+ if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end
+
+ -- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well.
+ -- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is.
+ local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name))
+
+ module.IsModule = IsModuleTrue
+ module:SetEnabledState(self.defaultModuleState)
+ module.moduleName = name
+
+ if type(prototype) == "string" then
+ AceAddon:EmbedLibraries(module, prototype, ...)
+ else
+ AceAddon:EmbedLibraries(module, ...)
+ end
+ AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries))
+
+ if not prototype or type(prototype) == "string" then
+ prototype = self.defaultModulePrototype or nil
+ end
+
+ if type(prototype) == "table" then
+ local mt = getmetatable(module)
+ mt.__index = prototype
+ setmetatable(module, mt) -- More of a Base class type feel.
+ end
+
+ safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy.
+ self.modules[name] = module
+ tinsert(self.orderedModules, module)
+
+ return module
+end
+
+--- Returns the real name of the addon or module, without any prefix.
+-- @name //addon//:GetName
+-- @paramsig
+-- @usage
+-- print(MyAddon:GetName())
+-- -- prints "MyAddon"
+function GetName(self)
+ return self.moduleName or self.name
+end
+
+--- Enables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback
+-- and enabling all modules of the addon (unless explicitly disabled).\\
+-- :Enable() also sets the internal `enableState` variable to true
+-- @name //addon//:Enable
+-- @paramsig
+-- @usage
+-- -- Enable MyModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+function Enable(self)
+ self:SetEnabledState(true)
+
+ -- nevcairiel 2013-04-27: don't enable an addon/module if its queued for init still
+ -- it'll be enabled after the init process
+ if not queuedForInitialization(self) then
+ return AceAddon:EnableAddon(self)
+ end
+end
+
+--- Disables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback
+-- and disabling all modules of the addon.\\
+-- :Disable() also sets the internal `enableState` variable to false
+-- @name //addon//:Disable
+-- @paramsig
+-- @usage
+-- -- Disable MyAddon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:Disable()
+function Disable(self)
+ self:SetEnabledState(false)
+ return AceAddon:DisableAddon(self)
+end
+
+--- Enables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object.
+-- @name //addon//:EnableModule
+-- @paramsig name
+-- @usage
+-- -- Enable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+--
+-- -- Enable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:EnableModule("MyModule")
+function EnableModule(self, name)
+ local module = self:GetModule( name )
+ return module:Enable()
+end
+
+--- Disables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object.
+-- @name //addon//:DisableModule
+-- @paramsig name
+-- @usage
+-- -- Disable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Disable()
+--
+-- -- Disable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:DisableModule("MyModule")
+function DisableModule(self, name)
+ local module = self:GetModule( name )
+ return module:Disable()
+end
+
+--- Set the default libraries to be mixed into all modules created by this object.
+-- Note that you can only change the default module libraries before any module is created.
+-- @name //addon//:SetDefaultModuleLibraries
+-- @paramsig lib[, lib, ...]
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Configure default libraries for modules (all modules need AceEvent-3.0)
+-- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0")
+-- -- Create a module
+-- MyModule = MyAddon:NewModule("MyModule")
+function SetDefaultModuleLibraries(self, ...)
+ if next(self.modules) then
+ error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2)
+ end
+ self.defaultModuleLibraries = {...}
+end
+
+--- Set the default state in which new modules are being created.
+-- Note that you can only change the default state before any module is created.
+-- @name //addon//:SetDefaultModuleState
+-- @paramsig state
+-- @param state Default state for new modules, true for enabled, false for disabled
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Set the default state to "disabled"
+-- MyAddon:SetDefaultModuleState(false)
+-- -- Create a module and explicilty enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+function SetDefaultModuleState(self, state)
+ if next(self.modules) then
+ error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2)
+ end
+ self.defaultModuleState = state
+end
+
+--- Set the default prototype to use for new modules on creation.
+-- Note that you can only change the default prototype before any module is created.
+-- @name //addon//:SetDefaultModulePrototype
+-- @paramsig prototype
+-- @param prototype Default prototype for the new modules (table)
+-- @usage
+-- -- Define a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- -- Set the default prototype
+-- MyAddon:SetDefaultModulePrototype(prototype)
+-- -- Create a module and explicitly Enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+-- -- should print "OnEnable called!" now
+-- @see NewModule
+function SetDefaultModulePrototype(self, prototype)
+ if next(self.modules) then
+ error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2)
+ end
+ if type(prototype) ~= "table" then
+ error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2)
+ end
+ self.defaultModulePrototype = prototype
+end
+
+--- Set the state of an addon or module
+-- This should only be called before any enabling actually happend, e.g. in/before OnInitialize.
+-- @name //addon//:SetEnabledState
+-- @paramsig state
+-- @param state the state of an addon or module (enabled=true, disabled=false)
+function SetEnabledState(self, state)
+ self.enabledState = state
+end
+
+
+--- Return an iterator of all modules associated to the addon.
+-- @name //addon//:IterateModules
+-- @paramsig
+-- @usage
+-- -- Enable all modules
+-- for name, module in MyAddon:IterateModules() do
+-- module:Enable()
+-- end
+local function IterateModules(self) return pairs(self.modules) end
+
+-- Returns an iterator of all embeds in the addon
+-- @name //addon//:IterateEmbeds
+-- @paramsig
+local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end
+
+--- Query the enabledState of an addon.
+-- @name //addon//:IsEnabled
+-- @paramsig
+-- @usage
+-- if MyAddon:IsEnabled() then
+-- MyAddon:Disable()
+-- end
+local function IsEnabled(self) return self.enabledState end
+local mixins = {
+ NewModule = NewModule,
+ GetModule = GetModule,
+ Enable = Enable,
+ Disable = Disable,
+ EnableModule = EnableModule,
+ DisableModule = DisableModule,
+ IsEnabled = IsEnabled,
+ SetDefaultModuleLibraries = SetDefaultModuleLibraries,
+ SetDefaultModuleState = SetDefaultModuleState,
+ SetDefaultModulePrototype = SetDefaultModulePrototype,
+ SetEnabledState = SetEnabledState,
+ IterateModules = IterateModules,
+ IterateEmbeds = IterateEmbeds,
+ GetName = GetName,
+}
+local function IsModule(self) return false end
+local pmixins = {
+ defaultModuleState = true,
+ enabledState = true,
+ IsModule = IsModule,
+}
+-- Embed( target )
+-- target (object) - target object to embed aceaddon in
+--
+-- this is a local function specifically since it's meant to be only called internally
+function Embed(target, skipPMixins)
+ for k, v in pairs(mixins) do
+ target[k] = v
+ end
+ if not skipPMixins then
+ for k, v in pairs(pmixins) do
+ target[k] = target[k] or v
+ end
+ end
+end
+
+
+-- - Initialize the addon after creation.
+-- This function is only used internally during the ADDON_LOADED event
+-- It will call the **OnInitialize** function on the addon object (if present),
+-- and the **OnEmbedInitialize** function on all embeded libraries.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- @param addon addon object to intialize
+function AceAddon:InitializeAddon(addon)
+ safecall(addon.OnInitialize, addon)
+
+ local embeds = self.embeds[addon]
+ for i = 1, #embeds do
+ local lib = LibStub:GetLibrary(embeds[i], true)
+ if lib then safecall(lib.OnEmbedInitialize, lib, addon) end
+ end
+
+ -- we don't call InitializeAddon on modules specifically, this is handled
+ -- from the event handler and only done _once_
+end
+
+-- - Enable the addon after creation.
+-- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED,
+-- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons.
+-- It will call the **OnEnable** function on the addon object (if present),
+-- and the **OnEmbedEnable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Enable on the addon itself instead.
+-- @param addon addon object to enable
+function AceAddon:EnableAddon(addon)
+ if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
+ if self.statuses[addon.name] or not addon.enabledState then return false end
+
+ -- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable.
+ self.statuses[addon.name] = true
+
+ safecall(addon.OnEnable, addon)
+
+ -- make sure we're still enabled before continueing
+ if self.statuses[addon.name] then
+ local embeds = self.embeds[addon]
+ for i = 1, #embeds do
+ local lib = LibStub:GetLibrary(embeds[i], true)
+ if lib then safecall(lib.OnEmbedEnable, lib, addon) end
+ end
+
+ -- enable possible modules.
+ local modules = addon.orderedModules
+ for i = 1, #modules do
+ self:EnableAddon(modules[i])
+ end
+ end
+ return self.statuses[addon.name] -- return true if we're disabled
+end
+
+-- - Disable the addon
+-- Note: This function is only used internally.
+-- It will call the **OnDisable** function on the addon object (if present),
+-- and the **OnEmbedDisable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Disable on the addon itself instead.
+-- @param addon addon object to enable
+function AceAddon:DisableAddon(addon)
+ if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
+ if not self.statuses[addon.name] then return false end
+
+ -- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable.
+ self.statuses[addon.name] = false
+
+ safecall( addon.OnDisable, addon )
+
+ -- make sure we're still disabling...
+ if not self.statuses[addon.name] then
+ local embeds = self.embeds[addon]
+ for i = 1, #embeds do
+ local lib = LibStub:GetLibrary(embeds[i], true)
+ if lib then safecall(lib.OnEmbedDisable, lib, addon) end
+ end
+ -- disable possible modules.
+ local modules = addon.orderedModules
+ for i = 1, #modules do
+ self:DisableAddon(modules[i])
+ end
+ end
+
+ return not self.statuses[addon.name] -- return true if we're disabled
+end
+
+--- Get an iterator over all registered addons.
+-- @usage
+-- -- Print a list of all installed AceAddon's
+-- for name, addon in AceAddon:IterateAddons() do
+-- print("Addon: " .. name)
+-- end
+function AceAddon:IterateAddons() return pairs(self.addons) end
+
+--- Get an iterator over the internal status registry.
+-- @usage
+-- -- Print a list of all enabled addons
+-- for name, status in AceAddon:IterateAddonStatus() do
+-- if status then
+-- print("EnabledAddon: " .. name)
+-- end
+-- end
+function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
+
+-- Following Iterators are deprecated, and their addon specific versions should be used
+-- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon)
+function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end
+function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end
+
+-- Event Handling
+local function onEvent(this, event, arg1)
+ -- 2011-08-17 nevcairiel - ignore the load event of Blizzard_DebugTools, so a potential startup error isn't swallowed up
+ if (event == "ADDON_LOADED" and arg1 ~= "Blizzard_DebugTools") or event == "PLAYER_LOGIN" then
+ -- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration
+ while(#AceAddon.initializequeue > 0) do
+ local addon = tremove(AceAddon.initializequeue, 1)
+ -- this might be an issue with recursion - TODO: validate
+ if event == "ADDON_LOADED" then addon.baseName = arg1 end
+ AceAddon:InitializeAddon(addon)
+ tinsert(AceAddon.enablequeue, addon)
+ end
+
+ if IsLoggedIn() then
+ while(#AceAddon.enablequeue > 0) do
+ local addon = tremove(AceAddon.enablequeue, 1)
+ AceAddon:EnableAddon(addon)
+ end
+ end
+ end
+end
+
+AceAddon.frame:RegisterEvent("ADDON_LOADED")
+AceAddon.frame:RegisterEvent("PLAYER_LOGIN")
+AceAddon.frame:SetScript("OnEvent", onEvent)
+
+-- upgrade embeded
+for name, addon in pairs(AceAddon.addons) do
+ Embed(addon, true)
+end
+
+-- 2010-10-27 nevcairiel - add new "orderedModules" table
+if oldminor and oldminor < 10 then
+ for name, addon in pairs(AceAddon.addons) do
+ addon.orderedModules = {}
+ for module_name, module in pairs(addon.modules) do
+ tinsert(addon.orderedModules, module)
+ end
+ end
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceAddon-3.0/AceAddon-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceAddon-3.0/AceAddon-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceComm-3.0/AceComm-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceComm-3.0/AceComm-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,302 @@
+--- **AceComm-3.0** allows you to send messages of unlimited length over the addon comm channels.
+-- It'll automatically split the messages into multiple parts and rebuild them on the receiving end.\\
+-- **ChatThrottleLib** is of course being used to avoid being disconnected by the server.
+--
+-- **AceComm-3.0** can be embeded into your addon, either explicitly by calling AceComm:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceComm itself.\\
+-- It is recommended to embed AceComm, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceComm.
+-- @class file
+-- @name AceComm-3.0
+-- @release $Id: AceComm-3.0.lua 1107 2014-02-19 16:40:32Z nevcairiel $
+
+--[[ AceComm-3.0
+
+TODO: Time out old data rotting around from dead senders? Not a HUGE deal since the number of possible sender names is somewhat limited.
+
+]]
+
+local MAJOR, MINOR = "AceComm-3.0", 9
+
+local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceComm then return end
+
+local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
+local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
+
+-- Lua APIs
+local type, next, pairs, tostring = type, next, pairs, tostring
+local strsub, strfind = string.sub, string.find
+local match = string.match
+local tinsert, tconcat = table.insert, table.concat
+local error, assert = error, assert
+
+-- WoW APIs
+local Ambiguate = Ambiguate
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler, RegisterAddonMessagePrefix
+
+AceComm.embeds = AceComm.embeds or {}
+
+-- for my sanity and yours, let's give the message type bytes some names
+local MSG_MULTI_FIRST = "\001"
+local MSG_MULTI_NEXT = "\002"
+local MSG_MULTI_LAST = "\003"
+local MSG_ESCAPE = "\004"
+
+-- remove old structures (pre WoW 4.0)
+AceComm.multipart_origprefixes = nil
+AceComm.multipart_reassemblers = nil
+
+-- the multipart message spool: indexed by a combination of sender+distribution+
+AceComm.multipart_spool = AceComm.multipart_spool or {}
+
+--- Register for Addon Traffic on a specified prefix
+-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters
+-- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived"
+function AceComm:RegisterComm(prefix, method)
+ if method == nil then
+ method = "OnCommReceived"
+ end
+
+ if #prefix > 16 then -- TODO: 15?
+ error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters")
+ end
+ RegisterAddonMessagePrefix(prefix)
+
+ return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler
+end
+
+local warnedPrefix=false
+
+--- Send a message over the Addon Channel
+-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent)
+-- @param text Data to send, nils (\000) not allowed. Any length.
+-- @param distribution Addon channel, e.g. "RAID", "GUILD", etc; see SendAddonMessage API
+-- @param target Destination for some distributions; see SendAddonMessage API
+-- @param prio OPTIONAL: ChatThrottleLib priority, "BULK", "NORMAL" or "ALERT". Defaults to "NORMAL".
+-- @param callbackFn OPTIONAL: callback function to be called as each chunk is sent. receives 3 args: the user supplied arg (see next), the number of bytes sent so far, and the number of bytes total to send.
+-- @param callbackArg: OPTIONAL: first arg to the callback function. nil will be passed if not specified.
+function AceComm:SendCommMessage(prefix, text, distribution, target, prio, callbackFn, callbackArg)
+ prio = prio or "NORMAL" -- pasta's reference implementation had different prio for singlepart and multipart, but that's a very bad idea since that can easily lead to out-of-sequence delivery!
+ if not( type(prefix)=="string" and
+ type(text)=="string" and
+ type(distribution)=="string" and
+ (target==nil or type(target)=="string") and
+ (prio=="BULK" or prio=="NORMAL" or prio=="ALERT")
+ ) then
+ error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2)
+ end
+
+ local textlen = #text
+ local maxtextlen = 255 -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327
+ local queueName = prefix..distribution..(target or "")
+
+ local ctlCallback = nil
+ if callbackFn then
+ ctlCallback = function(sent)
+ return callbackFn(callbackArg, sent, textlen)
+ end
+ end
+
+ local forceMultipart
+ if match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character
+ -- we need to escape the first character with a \004
+ if textlen+1 > maxtextlen then -- would we go over the size limit?
+ forceMultipart = true -- just make it multipart, no escape problems then
+ else
+ text = "\004" .. text
+ end
+ end
+
+ if not forceMultipart and textlen <= maxtextlen then
+ -- fits all in one message
+ CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen)
+ else
+ maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1)
+
+ -- first part
+ local chunk = strsub(text, 1, maxtextlen)
+ CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen)
+
+ -- continuation
+ local pos = 1+maxtextlen
+
+ while pos+maxtextlen <= textlen do
+ chunk = strsub(text, pos, pos+maxtextlen-1)
+ CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1)
+ pos = pos + maxtextlen
+ end
+
+ -- final part
+ chunk = strsub(text, pos)
+ CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen)
+ end
+end
+
+
+----------------------------------------
+-- Message receiving
+----------------------------------------
+
+do
+ local compost = setmetatable({}, {__mode = "k"})
+ local function new()
+ local t = next(compost)
+ if t then
+ compost[t]=nil
+ for i=#t,3,-1 do -- faster than pairs loop. don't even nil out 1/2 since they'll be overwritten
+ t[i]=nil
+ end
+ return t
+ end
+
+ return {}
+ end
+
+ local function lostdatawarning(prefix,sender,where)
+ DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: lost network data regarding '"..tostring(prefix).."' from '"..tostring(sender).."' (in "..where..")")
+ end
+
+ function AceComm:OnReceiveMultipartFirst(prefix, message, distribution, sender)
+ local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
+ local spool = AceComm.multipart_spool
+
+ --[[
+ if spool[key] then
+ lostdatawarning(prefix,sender,"First")
+ -- continue and overwrite
+ end
+ --]]
+
+ spool[key] = message -- plain string for now
+ end
+
+ function AceComm:OnReceiveMultipartNext(prefix, message, distribution, sender)
+ local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
+ local spool = AceComm.multipart_spool
+ local olddata = spool[key]
+
+ if not olddata then
+ --lostdatawarning(prefix,sender,"Next")
+ return
+ end
+
+ if type(olddata)~="table" then
+ -- ... but what we have is not a table. So make it one. (Pull a composted one if available)
+ local t = new()
+ t[1] = olddata -- add old data as first string
+ t[2] = message -- and new message as second string
+ spool[key] = t -- and put the table in the spool instead of the old string
+ else
+ tinsert(olddata, message)
+ end
+ end
+
+ function AceComm:OnReceiveMultipartLast(prefix, message, distribution, sender)
+ local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
+ local spool = AceComm.multipart_spool
+ local olddata = spool[key]
+
+ if not olddata then
+ --lostdatawarning(prefix,sender,"End")
+ return
+ end
+
+ spool[key] = nil
+
+ if type(olddata) == "table" then
+ -- if we've received a "next", the spooled data will be a table for rapid & garbage-free tconcat
+ tinsert(olddata, message)
+ AceComm.callbacks:Fire(prefix, tconcat(olddata, ""), distribution, sender)
+ compost[olddata] = true
+ else
+ -- if we've only received a "first", the spooled data will still only be a string
+ AceComm.callbacks:Fire(prefix, olddata..message, distribution, sender)
+ end
+ end
+end
+
+
+
+
+
+
+----------------------------------------
+-- Embed CallbackHandler
+----------------------------------------
+
+if not AceComm.callbacks then
+ AceComm.callbacks = CallbackHandler:New(AceComm,
+ "_RegisterComm",
+ "UnregisterComm",
+ "UnregisterAllComm")
+end
+
+AceComm.callbacks.OnUsed = nil
+AceComm.callbacks.OnUnused = nil
+
+local function OnEvent(self, event, prefix, message, distribution, sender)
+ if event == "CHAT_MSG_ADDON" then
+ sender = Ambiguate(sender, "none")
+ local control, rest = match(message, "^([\001-\009])(.*)")
+ if control then
+ if control==MSG_MULTI_FIRST then
+ AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender)
+ elseif control==MSG_MULTI_NEXT then
+ AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender)
+ elseif control==MSG_MULTI_LAST then
+ AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender)
+ elseif control==MSG_ESCAPE then
+ AceComm.callbacks:Fire(prefix, rest, distribution, sender)
+ else
+ -- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!)
+ end
+ else
+ -- single part: fire it off immediately and let CallbackHandler decide if it's registered or not
+ AceComm.callbacks:Fire(prefix, message, distribution, sender)
+ end
+ else
+ assert(false, "Received "..tostring(event).." event?!")
+ end
+end
+
+AceComm.frame = AceComm.frame or CreateFrame("Frame", "AceComm30Frame")
+AceComm.frame:SetScript("OnEvent", OnEvent)
+AceComm.frame:UnregisterAllEvents()
+AceComm.frame:RegisterEvent("CHAT_MSG_ADDON")
+
+
+----------------------------------------
+-- Base library stuff
+----------------------------------------
+
+local mixins = {
+ "RegisterComm",
+ "UnregisterComm",
+ "UnregisterAllComm",
+ "SendCommMessage",
+}
+
+-- Embeds AceComm-3.0 into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceComm-3.0 in
+function AceComm:Embed(target)
+ for k, v in pairs(mixins) do
+ target[v] = self[v]
+ end
+ self.embeds[target] = true
+ return target
+end
+
+function AceComm:OnEmbedDisable(target)
+ target:UnregisterAllComm()
+end
+
+-- Update embeds
+for target, v in pairs(AceComm.embeds) do
+ AceComm:Embed(target)
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceComm-3.0/AceComm-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceComm-3.0/AceComm-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceComm-3.0/ChatThrottleLib.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceComm-3.0/ChatThrottleLib.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,524 @@
+--
+-- ChatThrottleLib by Mikk
+--
+-- Manages AddOn chat output to keep player from getting kicked off.
+--
+-- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept
+-- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
+--
+-- Priorities get an equal share of available bandwidth when fully loaded.
+-- Communication channels are separated on extension+chattype+destination and
+-- get round-robinned. (Destination only matters for whispers and channels,
+-- obviously)
+--
+-- Will install hooks for SendChatMessage and SendAddonMessage to measure
+-- bandwidth bypassing the library and use less bandwidth itself.
+--
+--
+-- Fully embeddable library. Just copy this file into your addon directory,
+-- add it to the .toc, and it's done.
+--
+-- Can run as a standalone addon also, but, really, just embed it! :-)
+--
+-- LICENSE: ChatThrottleLib is released into the Public Domain
+--
+
+local CTL_VERSION = 23
+
+local _G = _G
+
+if _G.ChatThrottleLib then
+ if _G.ChatThrottleLib.version >= CTL_VERSION then
+ -- There's already a newer (or same) version loaded. Buh-bye.
+ return
+ elseif not _G.ChatThrottleLib.securelyHooked then
+ print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, =v16) in it!")
+ -- ATTEMPT to unhook; this'll behave badly if someone else has hooked...
+ -- ... and if someone has securehooked, they can kiss that goodbye too... >.<
+ _G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage
+ if _G.ChatThrottleLib.ORIG_SendAddonMessage then
+ _G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage
+ end
+ end
+ _G.ChatThrottleLib.ORIG_SendChatMessage = nil
+ _G.ChatThrottleLib.ORIG_SendAddonMessage = nil
+end
+
+if not _G.ChatThrottleLib then
+ _G.ChatThrottleLib = {}
+end
+
+ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh)
+local ChatThrottleLib = _G.ChatThrottleLib
+
+ChatThrottleLib.version = CTL_VERSION
+
+
+
+------------------ TWEAKABLES -----------------
+
+ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
+ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
+
+ChatThrottleLib.BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now.
+
+ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value
+
+
+local setmetatable = setmetatable
+local table_remove = table.remove
+local tostring = tostring
+local GetTime = GetTime
+local math_min = math.min
+local math_max = math.max
+local next = next
+local strlen = string.len
+local GetFramerate = GetFramerate
+local strlower = string.lower
+local unpack,type,pairs,wipe = unpack,type,pairs,wipe
+local UnitInRaid,UnitInParty = UnitInRaid,UnitInParty
+
+
+-----------------------------------------------------------------------
+-- Double-linked ring implementation
+
+local Ring = {}
+local RingMeta = { __index = Ring }
+
+function Ring:New()
+ local ret = {}
+ setmetatable(ret, RingMeta)
+ return ret
+end
+
+function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position)
+ if self.pos then
+ obj.prev = self.pos.prev
+ obj.prev.next = obj
+ obj.next = self.pos
+ obj.next.prev = obj
+ else
+ obj.next = obj
+ obj.prev = obj
+ self.pos = obj
+ end
+end
+
+function Ring:Remove(obj)
+ obj.next.prev = obj.prev
+ obj.prev.next = obj.next
+ if self.pos == obj then
+ self.pos = obj.next
+ if self.pos == obj then
+ self.pos = nil
+ end
+ end
+end
+
+
+
+-----------------------------------------------------------------------
+-- Recycling bin for pipes
+-- A pipe is a plain integer-indexed queue of messages
+-- Pipes normally live in Rings of pipes (3 rings total, one per priority)
+
+ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different
+local PipeBin = setmetatable({}, {__mode="k"})
+
+local function DelPipe(pipe)
+ PipeBin[pipe] = true
+end
+
+local function NewPipe()
+ local pipe = next(PipeBin)
+ if pipe then
+ wipe(pipe)
+ PipeBin[pipe] = nil
+ return pipe
+ end
+ return {}
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Recycling bin for messages
+
+ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different
+local MsgBin = setmetatable({}, {__mode="k"})
+
+local function DelMsg(msg)
+ msg[1] = nil
+ -- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them.
+ MsgBin[msg] = true
+end
+
+local function NewMsg()
+ local msg = next(MsgBin)
+ if msg then
+ MsgBin[msg] = nil
+ return msg
+ end
+ return {}
+end
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib:Init
+-- Initialize queues, set up frame for OnUpdate, etc
+
+
+function ChatThrottleLib:Init()
+
+ -- Set up queues
+ if not self.Prio then
+ self.Prio = {}
+ self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+ self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+ self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+ end
+
+ -- v4: total send counters per priority
+ for _, Prio in pairs(self.Prio) do
+ Prio.nTotalSent = Prio.nTotalSent or 0
+ end
+
+ if not self.avail then
+ self.avail = 0 -- v5
+ end
+ if not self.nTotalSent then
+ self.nTotalSent = 0 -- v5
+ end
+
+
+ -- Set up a frame to get OnUpdate events
+ if not self.Frame then
+ self.Frame = CreateFrame("Frame")
+ self.Frame:Hide()
+ end
+ self.Frame:SetScript("OnUpdate", self.OnUpdate)
+ self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds
+ self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
+ self.OnUpdateDelay = 0
+ self.LastAvailUpdate = GetTime()
+ self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup
+
+ -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
+ if not self.securelyHooked then
+ -- Use secure hooks as of v16. Old regular hook support yanked out in v21.
+ self.securelyHooked = true
+ --SendChatMessage
+ hooksecurefunc("SendChatMessage", function(...)
+ return ChatThrottleLib.Hook_SendChatMessage(...)
+ end)
+ --SendAddonMessage
+ hooksecurefunc("SendAddonMessage", function(...)
+ return ChatThrottleLib.Hook_SendAddonMessage(...)
+ end)
+ end
+ self.nBypass = 0
+end
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
+
+local bMyTraffic = false
+
+function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...)
+ if bMyTraffic then
+ return
+ end
+ local self = ChatThrottleLib
+ local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD
+ self.avail = self.avail - size
+ self.nBypass = self.nBypass + size -- just a statistic
+end
+function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
+ if bMyTraffic then
+ return
+ end
+ local self = ChatThrottleLib
+ local size = tostring(text or ""):len() + tostring(prefix or ""):len();
+ size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD
+ self.avail = self.avail - size
+ self.nBypass = self.nBypass + size -- just a statistic
+end
+
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib:UpdateAvail
+-- Update self.avail with how much bandwidth is currently available
+
+function ChatThrottleLib:UpdateAvail()
+ local now = GetTime()
+ local MAX_CPS = self.MAX_CPS;
+ local newavail = MAX_CPS * (now - self.LastAvailUpdate)
+ local avail = self.avail
+
+ if now - self.HardThrottlingBeginTime < 5 then
+ -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
+ avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
+ self.bChoking = true
+ elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs
+ avail = math_min(MAX_CPS, avail + newavail*0.5)
+ self.bChoking = true -- just a statistic
+ else
+ avail = math_min(self.BURST, avail + newavail)
+ self.bChoking = false
+ end
+
+ avail = math_max(avail, 0-(MAX_CPS*2)) -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can.
+
+ self.avail = avail
+ self.LastAvailUpdate = now
+
+ return avail
+end
+
+
+-----------------------------------------------------------------------
+-- Despooling logic
+-- Reminder:
+-- - We have 3 Priorities, each containing a "Ring" construct ...
+-- - ... made up of N "Pipe"s (1 for each destination/pipename)
+-- - and each pipe contains messages
+
+function ChatThrottleLib:Despool(Prio)
+ local ring = Prio.Ring
+ while ring.pos and Prio.avail > ring.pos[1].nSize do
+ local msg = table_remove(ring.pos, 1)
+ if not ring.pos[1] then -- did we remove last msg in this pipe?
+ local pipe = Prio.Ring.pos
+ Prio.Ring:Remove(pipe)
+ Prio.ByName[pipe.name] = nil
+ DelPipe(pipe)
+ else
+ Prio.Ring.pos = Prio.Ring.pos.next
+ end
+ local didSend=false
+ local lowerDest = strlower(msg[3] or "")
+ if lowerDest == "raid" and not UnitInRaid("player") then
+ -- do nothing
+ elseif lowerDest == "party" and not UnitInParty("player") then
+ -- do nothing
+ else
+ Prio.avail = Prio.avail - msg.nSize
+ bMyTraffic = true
+ msg.f(unpack(msg, 1, msg.n))
+ bMyTraffic = false
+ Prio.nTotalSent = Prio.nTotalSent + msg.nSize
+ DelMsg(msg)
+ didSend = true
+ end
+ -- notify caller of delivery (even if we didn't send it)
+ if msg.callbackFn then
+ msg.callbackFn (msg.callbackArg, didSend)
+ end
+ -- USER CALLBACK MAY ERROR
+ end
+end
+
+
+function ChatThrottleLib.OnEvent(this,event)
+ -- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too.
+ local self = ChatThrottleLib
+ if event == "PLAYER_ENTERING_WORLD" then
+ self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning
+ self.avail = 0
+ end
+end
+
+
+function ChatThrottleLib.OnUpdate(this,delay)
+ local self = ChatThrottleLib
+
+ self.OnUpdateDelay = self.OnUpdateDelay + delay
+ if self.OnUpdateDelay < 0.08 then
+ return
+ end
+ self.OnUpdateDelay = 0
+
+ self:UpdateAvail()
+
+ if self.avail < 0 then
+ return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
+ end
+
+ -- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
+ local n = 0
+ for prioname,Prio in pairs(self.Prio) do
+ if Prio.Ring.pos or Prio.avail < 0 then
+ n = n + 1
+ end
+ end
+
+ -- Anything queued still?
+ if n<1 then
+ -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
+ for prioname, Prio in pairs(self.Prio) do
+ self.avail = self.avail + Prio.avail
+ Prio.avail = 0
+ end
+ self.bQueueing = false
+ self.Frame:Hide()
+ return
+ end
+
+ -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
+ local avail = self.avail/n
+ self.avail = 0
+
+ for prioname, Prio in pairs(self.Prio) do
+ if Prio.Ring.pos or Prio.avail < 0 then
+ Prio.avail = Prio.avail + avail
+ if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
+ self:Despool(Prio)
+ -- Note: We might not get here if the user-supplied callback function errors out! Take care!
+ end
+ end
+ end
+
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Spooling logic
+
+function ChatThrottleLib:Enqueue(prioname, pipename, msg)
+ local Prio = self.Prio[prioname]
+ local pipe = Prio.ByName[pipename]
+ if not pipe then
+ self.Frame:Show()
+ pipe = NewPipe()
+ pipe.name = pipename
+ Prio.ByName[pipename] = pipe
+ Prio.Ring:Add(pipe)
+ end
+
+ pipe[#pipe + 1] = msg
+
+ self.bQueueing = true
+end
+
+function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName, callbackFn, callbackArg)
+ if not self or not prio or not prefix or not text or not self.Prio[prio] then
+ error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2)
+ end
+ if callbackFn and type(callbackFn)~="function" then
+ error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
+ end
+
+ local nSize = text:len()
+
+ if nSize>255 then
+ error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2)
+ end
+
+ nSize = nSize + self.MSG_OVERHEAD
+
+ -- Check if there's room in the global available bandwidth gauge to send directly
+ if not self.bQueueing and nSize < self:UpdateAvail() then
+ self.avail = self.avail - nSize
+ bMyTraffic = true
+ _G.SendChatMessage(text, chattype, language, destination)
+ bMyTraffic = false
+ self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
+ if callbackFn then
+ callbackFn (callbackArg, true)
+ end
+ -- USER CALLBACK MAY ERROR
+ return
+ end
+
+ -- Message needs to be queued
+ local msg = NewMsg()
+ msg.f = _G.SendChatMessage
+ msg[1] = text
+ msg[2] = chattype or "SAY"
+ msg[3] = language
+ msg[4] = destination
+ msg.n = 4
+ msg.nSize = nSize
+ msg.callbackFn = callbackFn
+ msg.callbackArg = callbackArg
+
+ self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
+end
+
+
+function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
+ if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
+ error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
+ end
+ if callbackFn and type(callbackFn)~="function" then
+ error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
+ end
+
+ local nSize = text:len();
+
+ if RegisterAddonMessagePrefix then
+ if nSize>255 then
+ error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
+ end
+ else
+ nSize = nSize + prefix:len() + 1
+ if nSize>255 then
+ error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
+ end
+ end
+
+ nSize = nSize + self.MSG_OVERHEAD;
+
+ -- Check if there's room in the global available bandwidth gauge to send directly
+ if not self.bQueueing and nSize < self:UpdateAvail() then
+ self.avail = self.avail - nSize
+ bMyTraffic = true
+ _G.SendAddonMessage(prefix, text, chattype, target)
+ bMyTraffic = false
+ self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
+ if callbackFn then
+ callbackFn (callbackArg, true)
+ end
+ -- USER CALLBACK MAY ERROR
+ return
+ end
+
+ -- Message needs to be queued
+ local msg = NewMsg()
+ msg.f = _G.SendAddonMessage
+ msg[1] = prefix
+ msg[2] = text
+ msg[3] = chattype
+ msg[4] = target
+ msg.n = (target~=nil) and 4 or 3;
+ msg.nSize = nSize
+ msg.callbackFn = callbackFn
+ msg.callbackArg = callbackArg
+
+ self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Get the ball rolling!
+
+ChatThrottleLib:Init()
+
+--[[ WoWBench debugging snippet
+if(WOWB_VER) then
+ local function SayTimer()
+ print("SAY: "..GetTime().." "..arg1)
+ end
+ ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer)
+ ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY")
+end
+]]
+
+
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceConsole-3.0/AceConsole-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceConsole-3.0/AceConsole-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,250 @@
+--- **AceConsole-3.0** provides registration facilities for slash commands.
+-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
+-- to your addons individual needs.
+--
+-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceConsole itself.\\
+-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceConsole.
+-- @class file
+-- @name AceConsole-3.0
+-- @release $Id: AceConsole-3.0.lua 878 2009-11-02 18:51:58Z nevcairiel $
+local MAJOR,MINOR = "AceConsole-3.0", 7
+
+local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceConsole then return end -- No upgrade needed
+
+AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
+AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
+AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
+
+-- Lua APIs
+local tconcat, tostring, select = table.concat, tostring, select
+local type, pairs, error = type, pairs, error
+local format, strfind, strsub = string.format, string.find, string.sub
+local max = math.max
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
+
+local tmp={}
+local function Print(self,frame,...)
+ local n=0
+ if self ~= AceConsole then
+ n=n+1
+ tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
+ end
+ for i=1, select("#", ...) do
+ n=n+1
+ tmp[n] = tostring(select(i, ...))
+ end
+ frame:AddMessage( tconcat(tmp," ",1,n) )
+end
+
+--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] ...
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param ... List of any values to be printed
+function AceConsole:Print(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
+ return Print(self, frame, select(2,...))
+ else
+ return Print(self, DEFAULT_CHAT_FRAME, ...)
+ end
+end
+
+
+--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] "format"[, ...]
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param format Format string - same syntax as standard Lua format()
+-- @param ... Arguments to the format string
+function AceConsole:Printf(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
+ return Print(self, frame, format(select(2,...)))
+ else
+ return Print(self, DEFAULT_CHAT_FRAME, format(...))
+ end
+end
+
+
+
+
+--- Register a simple chat command
+-- @param command Chat command to be registered WITHOUT leading "/"
+-- @param func Function to call when the slash command is being used (funcref or methodname)
+-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
+function AceConsole:RegisterChatCommand( command, func, persist )
+ if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end
+
+ if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk
+
+ local name = "ACECONSOLE_"..command:upper()
+
+ if type( func ) == "string" then
+ SlashCmdList[name] = function(input, editBox)
+ self[func](self, input, editBox)
+ end
+ else
+ SlashCmdList[name] = func
+ end
+ _G["SLASH_"..name.."1"] = "/"..command:lower()
+ AceConsole.commands[command] = name
+ -- non-persisting commands are registered for enabling disabling
+ if not persist then
+ if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
+ AceConsole.weakcommands[self][command] = func
+ end
+ return true
+end
+
+--- Unregister a chatcommand
+-- @param command Chat command to be unregistered WITHOUT leading "/"
+function AceConsole:UnregisterChatCommand( command )
+ local name = AceConsole.commands[command]
+ if name then
+ SlashCmdList[name] = nil
+ _G["SLASH_" .. name .. "1"] = nil
+ hash_SlashCmdList["/" .. command:upper()] = nil
+ AceConsole.commands[command] = nil
+ end
+end
+
+--- Get an iterator over all Chat Commands registered with AceConsole
+-- @return Iterator (pairs) over all commands
+function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
+
+
+local function nils(n, ...)
+ if n>1 then
+ return nil, nils(n-1, ...)
+ elseif n==1 then
+ return nil, ...
+ else
+ return ...
+ end
+end
+
+
+--- Retreive one or more space-separated arguments from a string.
+-- Treats quoted strings and itemlinks as non-spaced.
+-- @param string The raw argument string
+-- @param numargs How many arguments to get (default 1)
+-- @param startpos Where in the string to start scanning (default 1)
+-- @return Returns arg1, arg2, ..., nextposition\\
+-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
+function AceConsole:GetArgs(str, numargs, startpos)
+ numargs = numargs or 1
+ startpos = max(startpos or 1, 1)
+
+ local pos=startpos
+
+ -- find start of new arg
+ pos = strfind(str, "[^ ]", pos)
+ if not pos then -- whoops, end of string
+ return nils(numargs, 1e9)
+ end
+
+ if numargs<1 then
+ return pos
+ end
+
+ -- quoted or space separated? find out which pattern to use
+ local delim_or_pipe
+ local ch = strsub(str, pos, pos)
+ if ch=='"' then
+ pos = pos + 1
+ delim_or_pipe='([|"])'
+ elseif ch=="'" then
+ pos = pos + 1
+ delim_or_pipe="([|'])"
+ else
+ delim_or_pipe="([| ])"
+ end
+
+ startpos = pos
+
+ while true do
+ -- find delimiter or hyperlink
+ local ch,_
+ pos,_,ch = strfind(str, delim_or_pipe, pos)
+
+ if not pos then break end
+
+ if ch=="|" then
+ -- some kind of escape
+
+ if strsub(str,pos,pos+1)=="|H" then
+ -- It's a |H....|hhyper link!|h
+ pos=strfind(str, "|h", pos+2) -- first |h
+ if not pos then break end
+
+ pos=strfind(str, "|h", pos+2) -- second |h
+ if not pos then break end
+ elseif strsub(str,pos, pos+1) == "|T" then
+ -- It's a |T....|t texture
+ pos=strfind(str, "|t", pos+2)
+ if not pos then break end
+ end
+
+ pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
+
+ else
+ -- found delimiter, done with this arg
+ return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
+ end
+
+ end
+
+ -- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
+ return strsub(str, startpos), nils(numargs-1, 1e9)
+end
+
+
+--- embedding and embed handling
+
+local mixins = {
+ "Print",
+ "Printf",
+ "RegisterChatCommand",
+ "UnregisterChatCommand",
+ "GetArgs",
+}
+
+-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceBucket in
+function AceConsole:Embed( target )
+ for k, v in pairs( mixins ) do
+ target[v] = self[v]
+ end
+ self.embeds[target] = true
+ return target
+end
+
+function AceConsole:OnEmbedEnable( target )
+ if AceConsole.weakcommands[target] then
+ for command, func in pairs( AceConsole.weakcommands[target] ) do
+ target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
+ end
+ end
+end
+
+function AceConsole:OnEmbedDisable( target )
+ if AceConsole.weakcommands[target] then
+ for command, func in pairs( AceConsole.weakcommands[target] ) do
+ target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
+ end
+ end
+end
+
+for addon in pairs(AceConsole.embeds) do
+ AceConsole:Embed(addon)
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceConsole-3.0/AceConsole-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceConsole-3.0/AceConsole-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceDB-3.0/AceDB-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceDB-3.0/AceDB-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,745 @@
+--- **AceDB-3.0** manages the SavedVariables of your addon.
+-- It offers profile management, smart defaults and namespaces for modules.\\
+-- Data can be saved in different data-types, depending on its intended usage.
+-- The most common data-type is the `profile` type, which allows the user to choose
+-- the active profile, and manage the profiles of all of his characters.\\
+-- The following data types are available:
+-- * **char** Character-specific data. Every character has its own database.
+-- * **realm** Realm-specific data. All of the players characters on the same realm share this database.
+-- * **class** Class-specific data. All of the players characters of the same class share this database.
+-- * **race** Race-specific data. All of the players characters of the same race share this database.
+-- * **faction** Faction-specific data. All of the players characters of the same faction share this database.
+-- * **factionrealm** Faction and realm specific data. All of the players characters on the same realm and of the same faction share this database.
+-- * **global** Global Data. All characters on the same account share this database.
+-- * **profile** Profile-specific data. All characters using the same profile share this database. The user can control which profile should be used.
+--
+-- Creating a new Database using the `:New` function will return a new DBObject. A database will inherit all functions
+-- of the DBObjectLib listed here. \\
+-- If you create a new namespaced child-database (`:RegisterNamespace`), you'll get a DBObject as well, but note
+-- that the child-databases cannot individually change their profile, and are linked to their parents profile - and because of that,
+-- the profile related APIs are not available. Only `:RegisterDefaults` and `:ResetProfile` are available on child-databases.
+--
+-- For more details on how to use AceDB-3.0, see the [[AceDB-3.0 Tutorial]].
+--
+-- You may also be interested in [[libdualspec-1-0|LibDualSpec-1.0]] to do profile switching automatically when switching specs.
+--
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("DBExample")
+--
+-- -- declare defaults to be used in the DB
+-- local defaults = {
+-- profile = {
+-- setting = true,
+-- }
+-- }
+--
+-- function MyAddon:OnInitialize()
+-- -- Assuming the .toc says ## SavedVariables: MyAddonDB
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
+-- end
+-- @class file
+-- @name AceDB-3.0.lua
+-- @release $Id: AceDB-3.0.lua 1124 2014-10-27 21:00:07Z funkydude $
+local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 26
+local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR)
+
+if not AceDB then return end -- No upgrade needed
+
+-- Lua APIs
+local type, pairs, next, error = type, pairs, next, error
+local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub
+
+AceDB.db_registry = AceDB.db_registry or {}
+AceDB.frame = AceDB.frame or CreateFrame("Frame")
+
+local CallbackHandler
+local CallbackDummy = { Fire = function() end }
+
+local DBObjectLib = {}
+
+--[[-------------------------------------------------------------------------
+ AceDB Utility Functions
+---------------------------------------------------------------------------]]
+
+-- Simple shallow copy for copying defaults
+local function copyTable(src, dest)
+ if type(dest) ~= "table" then dest = {} end
+ if type(src) == "table" then
+ for k,v in pairs(src) do
+ if type(v) == "table" then
+ -- try to index the key first so that the metatable creates the defaults, if set, and use that table
+ v = copyTable(v, dest[k])
+ end
+ dest[k] = v
+ end
+ end
+ return dest
+end
+
+-- Called to add defaults to a section of the database
+--
+-- When a ["*"] default section is indexed with a new key, a table is returned
+-- and set in the host table. These tables must be cleaned up by removeDefaults
+-- in order to ensure we don't write empty default tables.
+local function copyDefaults(dest, src)
+ -- this happens if some value in the SV overwrites our default value with a non-table
+ --if type(dest) ~= "table" then return end
+ for k, v in pairs(src) do
+ if k == "*" or k == "**" then
+ if type(v) == "table" then
+ -- This is a metatable used for table defaults
+ local mt = {
+ -- This handles the lookup and creation of new subtables
+ __index = function(t,k)
+ if k == nil then return nil end
+ local tbl = {}
+ copyDefaults(tbl, v)
+ rawset(t, k, tbl)
+ return tbl
+ end,
+ }
+ setmetatable(dest, mt)
+ -- handle already existing tables in the SV
+ for dk, dv in pairs(dest) do
+ if not rawget(src, dk) and type(dv) == "table" then
+ copyDefaults(dv, v)
+ end
+ end
+ else
+ -- Values are not tables, so this is just a simple return
+ local mt = {__index = function(t,k) return k~=nil and v or nil end}
+ setmetatable(dest, mt)
+ end
+ elseif type(v) == "table" then
+ if not rawget(dest, k) then rawset(dest, k, {}) end
+ if type(dest[k]) == "table" then
+ copyDefaults(dest[k], v)
+ if src['**'] then
+ copyDefaults(dest[k], src['**'])
+ end
+ end
+ else
+ if rawget(dest, k) == nil then
+ rawset(dest, k, v)
+ end
+ end
+ end
+end
+
+-- Called to remove all defaults in the default table from the database
+local function removeDefaults(db, defaults, blocker)
+ -- remove all metatables from the db, so we don't accidentally create new sub-tables through them
+ setmetatable(db, nil)
+ -- loop through the defaults and remove their content
+ for k,v in pairs(defaults) do
+ if k == "*" or k == "**" then
+ if type(v) == "table" then
+ -- Loop through all the actual k,v pairs and remove
+ for key, value in pairs(db) do
+ if type(value) == "table" then
+ -- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables
+ if defaults[key] == nil and (not blocker or blocker[key] == nil) then
+ removeDefaults(value, v)
+ -- if the table is empty afterwards, remove it
+ if next(value) == nil then
+ db[key] = nil
+ end
+ -- if it was specified, only strip ** content, but block values which were set in the key table
+ elseif k == "**" then
+ removeDefaults(value, v, defaults[key])
+ end
+ end
+ end
+ elseif k == "*" then
+ -- check for non-table default
+ for key, value in pairs(db) do
+ if defaults[key] == nil and v == value then
+ db[key] = nil
+ end
+ end
+ end
+ elseif type(v) == "table" and type(db[k]) == "table" then
+ -- if a blocker was set, dive into it, to allow multi-level defaults
+ removeDefaults(db[k], v, blocker and blocker[k])
+ if next(db[k]) == nil then
+ db[k] = nil
+ end
+ else
+ -- check if the current value matches the default, and that its not blocked by another defaults table
+ if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then
+ db[k] = nil
+ end
+ end
+ end
+end
+
+-- This is called when a table section is first accessed, to set up the defaults
+local function initSection(db, section, svstore, key, defaults)
+ local sv = rawget(db, "sv")
+
+ local tableCreated
+ if not sv[svstore] then sv[svstore] = {} end
+ if not sv[svstore][key] then
+ sv[svstore][key] = {}
+ tableCreated = true
+ end
+
+ local tbl = sv[svstore][key]
+
+ if defaults then
+ copyDefaults(tbl, defaults)
+ end
+ rawset(db, section, tbl)
+
+ return tableCreated, tbl
+end
+
+-- Metatable to handle the dynamic creation of sections and copying of sections.
+local dbmt = {
+ __index = function(t, section)
+ local keys = rawget(t, "keys")
+ local key = keys[section]
+ if key then
+ local defaultTbl = rawget(t, "defaults")
+ local defaults = defaultTbl and defaultTbl[section]
+
+ if section == "profile" then
+ local new = initSection(t, section, "profiles", key, defaults)
+ if new then
+ -- Callback: OnNewProfile, database, newProfileKey
+ t.callbacks:Fire("OnNewProfile", t, key)
+ end
+ elseif section == "profiles" then
+ local sv = rawget(t, "sv")
+ if not sv.profiles then sv.profiles = {} end
+ rawset(t, "profiles", sv.profiles)
+ elseif section == "global" then
+ local sv = rawget(t, "sv")
+ if not sv.global then sv.global = {} end
+ if defaults then
+ copyDefaults(sv.global, defaults)
+ end
+ rawset(t, section, sv.global)
+ else
+ initSection(t, section, section, key, defaults)
+ end
+ end
+
+ return rawget(t, section)
+ end
+}
+
+local function validateDefaults(defaults, keyTbl, offset)
+ if not defaults then return end
+ offset = offset or 0
+ for k in pairs(defaults) do
+ if not keyTbl[k] or k == "profiles" then
+ error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset)
+ end
+ end
+end
+
+local preserve_keys = {
+ ["callbacks"] = true,
+ ["RegisterCallback"] = true,
+ ["UnregisterCallback"] = true,
+ ["UnregisterAllCallbacks"] = true,
+ ["children"] = true,
+}
+
+local realmKey = GetRealmName()
+local charKey = UnitName("player") .. " - " .. realmKey
+local _, classKey = UnitClass("player")
+local _, raceKey = UnitRace("player")
+local factionKey = UnitFactionGroup("player")
+local factionrealmKey = factionKey .. " - " .. realmKey
+local localeKey = GetLocale():lower()
+
+local regionTable = { "US", "KR", "EU", "TW", "CN" }
+local regionKey = regionTable[GetCurrentRegion()]
+local factionrealmregionKey = factionrealmKey .. " - " .. regionKey
+
+-- Actual database initialization function
+local function initdb(sv, defaults, defaultProfile, olddb, parent)
+ -- Generate the database keys for each section
+
+ -- map "true" to our "Default" profile
+ if defaultProfile == true then defaultProfile = "Default" end
+
+ local profileKey
+ if not parent then
+ -- Make a container for profile keys
+ if not sv.profileKeys then sv.profileKeys = {} end
+
+ -- Try to get the profile selected from the char db
+ profileKey = sv.profileKeys[charKey] or defaultProfile or charKey
+
+ -- save the selected profile for later
+ sv.profileKeys[charKey] = profileKey
+ else
+ -- Use the profile of the parents DB
+ profileKey = parent.keys.profile or defaultProfile or charKey
+
+ -- clear the profileKeys in the DB, namespaces don't need to store them
+ sv.profileKeys = nil
+ end
+
+ -- This table contains keys that enable the dynamic creation
+ -- of each section of the table. The 'global' and 'profiles'
+ -- have a key of true, since they are handled in a special case
+ local keyTbl= {
+ ["char"] = charKey,
+ ["realm"] = realmKey,
+ ["class"] = classKey,
+ ["race"] = raceKey,
+ ["faction"] = factionKey,
+ ["factionrealm"] = factionrealmKey,
+ ["factionrealmregion"] = factionrealmregionKey,
+ ["profile"] = profileKey,
+ ["locale"] = localeKey,
+ ["global"] = true,
+ ["profiles"] = true,
+ }
+
+ validateDefaults(defaults, keyTbl, 1)
+
+ -- This allows us to use this function to reset an entire database
+ -- Clear out the old database
+ if olddb then
+ for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end
+ end
+
+ -- Give this database the metatable so it initializes dynamically
+ local db = setmetatable(olddb or {}, dbmt)
+
+ if not rawget(db, "callbacks") then
+ -- try to load CallbackHandler-1.0 if it loaded after our library
+ if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end
+ db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy
+ end
+
+ -- Copy methods locally into the database object, to avoid hitting
+ -- the metatable when calling methods
+
+ if not parent then
+ for name, func in pairs(DBObjectLib) do
+ db[name] = func
+ end
+ else
+ -- hack this one in
+ db.RegisterDefaults = DBObjectLib.RegisterDefaults
+ db.ResetProfile = DBObjectLib.ResetProfile
+ end
+
+ -- Set some properties in the database object
+ db.profiles = sv.profiles
+ db.keys = keyTbl
+ db.sv = sv
+ --db.sv_name = name
+ db.defaults = defaults
+ db.parent = parent
+
+ -- store the DB in the registry
+ AceDB.db_registry[db] = true
+
+ return db
+end
+
+-- handle PLAYER_LOGOUT
+-- strip all defaults from all databases
+-- and cleans up empty sections
+local function logoutHandler(frame, event)
+ if event == "PLAYER_LOGOUT" then
+ for db in pairs(AceDB.db_registry) do
+ db.callbacks:Fire("OnDatabaseShutdown", db)
+ db:RegisterDefaults(nil)
+
+ -- cleanup sections that are empty without defaults
+ local sv = rawget(db, "sv")
+ for section in pairs(db.keys) do
+ if rawget(sv, section) then
+ -- global is special, all other sections have sub-entrys
+ -- also don't delete empty profiles on main dbs, only on namespaces
+ if section ~= "global" and (section ~= "profiles" or rawget(db, "parent")) then
+ for key in pairs(sv[section]) do
+ if not next(sv[section][key]) then
+ sv[section][key] = nil
+ end
+ end
+ end
+ if not next(sv[section]) then
+ sv[section] = nil
+ end
+ end
+ end
+ end
+ end
+end
+
+AceDB.frame:RegisterEvent("PLAYER_LOGOUT")
+AceDB.frame:SetScript("OnEvent", logoutHandler)
+
+
+--[[-------------------------------------------------------------------------
+ AceDB Object Method Definitions
+---------------------------------------------------------------------------]]
+
+--- Sets the defaults table for the given database object by clearing any
+-- that are currently set, and then setting the new defaults.
+-- @param defaults A table of defaults for this database
+function DBObjectLib:RegisterDefaults(defaults)
+ if defaults and type(defaults) ~= "table" then
+ error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2)
+ end
+
+ validateDefaults(defaults, self.keys)
+
+ -- Remove any currently set defaults
+ if self.defaults then
+ for section,key in pairs(self.keys) do
+ if self.defaults[section] and rawget(self, section) then
+ removeDefaults(self[section], self.defaults[section])
+ end
+ end
+ end
+
+ -- Set the DBObject.defaults table
+ self.defaults = defaults
+
+ -- Copy in any defaults, only touching those sections already created
+ if defaults then
+ for section,key in pairs(self.keys) do
+ if defaults[section] and rawget(self, section) then
+ copyDefaults(self[section], defaults[section])
+ end
+ end
+ end
+end
+
+--- Changes the profile of the database and all of it's namespaces to the
+-- supplied named profile
+-- @param name The name of the profile to set as the current profile
+function DBObjectLib:SetProfile(name)
+ if type(name) ~= "string" then
+ error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2)
+ end
+
+ -- changing to the same profile, dont do anything
+ if name == self.keys.profile then return end
+
+ local oldProfile = self.profile
+ local defaults = self.defaults and self.defaults.profile
+
+ -- Callback: OnProfileShutdown, database
+ self.callbacks:Fire("OnProfileShutdown", self)
+
+ if oldProfile and defaults then
+ -- Remove the defaults from the old profile
+ removeDefaults(oldProfile, defaults)
+ end
+
+ self.profile = nil
+ self.keys["profile"] = name
+
+ -- if the storage exists, save the new profile
+ -- this won't exist on namespaces.
+ if self.sv.profileKeys then
+ self.sv.profileKeys[charKey] = name
+ end
+
+ -- populate to child namespaces
+ if self.children then
+ for _, db in pairs(self.children) do
+ DBObjectLib.SetProfile(db, name)
+ end
+ end
+
+ -- Callback: OnProfileChanged, database, newProfileKey
+ self.callbacks:Fire("OnProfileChanged", self, name)
+end
+
+--- Returns a table with the names of the existing profiles in the database.
+-- You can optionally supply a table to re-use for this purpose.
+-- @param tbl A table to store the profile names in (optional)
+function DBObjectLib:GetProfiles(tbl)
+ if tbl and type(tbl) ~= "table" then
+ error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2)
+ end
+
+ -- Clear the container table
+ if tbl then
+ for k,v in pairs(tbl) do tbl[k] = nil end
+ else
+ tbl = {}
+ end
+
+ local curProfile = self.keys.profile
+
+ local i = 0
+ for profileKey in pairs(self.profiles) do
+ i = i + 1
+ tbl[i] = profileKey
+ if curProfile and profileKey == curProfile then curProfile = nil end
+ end
+
+ -- Add the current profile, if it hasn't been created yet
+ if curProfile then
+ i = i + 1
+ tbl[i] = curProfile
+ end
+
+ return tbl, i
+end
+
+--- Returns the current profile name used by the database
+function DBObjectLib:GetCurrentProfile()
+ return self.keys.profile
+end
+
+--- Deletes a named profile. This profile must not be the active profile.
+-- @param name The name of the profile to be deleted
+-- @param silent If true, do not raise an error when the profile does not exist
+function DBObjectLib:DeleteProfile(name, silent)
+ if type(name) ~= "string" then
+ error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2)
+ end
+
+ if self.keys.profile == name then
+ error("Cannot delete the active profile in an AceDBObject.", 2)
+ end
+
+ if not rawget(self.profiles, name) and not silent then
+ error("Cannot delete profile '" .. name .. "'. It does not exist.", 2)
+ end
+
+ self.profiles[name] = nil
+
+ -- populate to child namespaces
+ if self.children then
+ for _, db in pairs(self.children) do
+ DBObjectLib.DeleteProfile(db, name, true)
+ end
+ end
+
+ -- switch all characters that use this profile back to the default
+ if self.sv.profileKeys then
+ for key, profile in pairs(self.sv.profileKeys) do
+ if profile == name then
+ self.sv.profileKeys[key] = nil
+ end
+ end
+ end
+
+ -- Callback: OnProfileDeleted, database, profileKey
+ self.callbacks:Fire("OnProfileDeleted", self, name)
+end
+
+--- Copies a named profile into the current profile, overwriting any conflicting
+-- settings.
+-- @param name The name of the profile to be copied into the current profile
+-- @param silent If true, do not raise an error when the profile does not exist
+function DBObjectLib:CopyProfile(name, silent)
+ if type(name) ~= "string" then
+ error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2)
+ end
+
+ if name == self.keys.profile then
+ error("Cannot have the same source and destination profiles.", 2)
+ end
+
+ if not rawget(self.profiles, name) and not silent then
+ error("Cannot copy profile '" .. name .. "'. It does not exist.", 2)
+ end
+
+ -- Reset the profile before copying
+ DBObjectLib.ResetProfile(self, nil, true)
+
+ local profile = self.profile
+ local source = self.profiles[name]
+
+ copyTable(source, profile)
+
+ -- populate to child namespaces
+ if self.children then
+ for _, db in pairs(self.children) do
+ DBObjectLib.CopyProfile(db, name, true)
+ end
+ end
+
+ -- Callback: OnProfileCopied, database, sourceProfileKey
+ self.callbacks:Fire("OnProfileCopied", self, name)
+end
+
+--- Resets the current profile to the default values (if specified).
+-- @param noChildren if set to true, the reset will not be populated to the child namespaces of this DB object
+-- @param noCallbacks if set to true, won't fire the OnProfileReset callback
+function DBObjectLib:ResetProfile(noChildren, noCallbacks)
+ local profile = self.profile
+
+ for k,v in pairs(profile) do
+ profile[k] = nil
+ end
+
+ local defaults = self.defaults and self.defaults.profile
+ if defaults then
+ copyDefaults(profile, defaults)
+ end
+
+ -- populate to child namespaces
+ if self.children and not noChildren then
+ for _, db in pairs(self.children) do
+ DBObjectLib.ResetProfile(db, nil, noCallbacks)
+ end
+ end
+
+ -- Callback: OnProfileReset, database
+ if not noCallbacks then
+ self.callbacks:Fire("OnProfileReset", self)
+ end
+end
+
+--- Resets the entire database, using the string defaultProfile as the new default
+-- profile.
+-- @param defaultProfile The profile name to use as the default
+function DBObjectLib:ResetDB(defaultProfile)
+ if defaultProfile and type(defaultProfile) ~= "string" then
+ error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2)
+ end
+
+ local sv = self.sv
+ for k,v in pairs(sv) do
+ sv[k] = nil
+ end
+
+ local parent = self.parent
+
+ initdb(sv, self.defaults, defaultProfile, self)
+
+ -- fix the child namespaces
+ if self.children then
+ if not sv.namespaces then sv.namespaces = {} end
+ for name, db in pairs(self.children) do
+ if not sv.namespaces[name] then sv.namespaces[name] = {} end
+ initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self)
+ end
+ end
+
+ -- Callback: OnDatabaseReset, database
+ self.callbacks:Fire("OnDatabaseReset", self)
+ -- Callback: OnProfileChanged, database, profileKey
+ self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"])
+
+ return self
+end
+
+--- Creates a new database namespace, directly tied to the database. This
+-- is a full scale database in it's own rights other than the fact that
+-- it cannot control its profile individually
+-- @param name The name of the new namespace
+-- @param defaults A table of values to use as defaults
+function DBObjectLib:RegisterNamespace(name, defaults)
+ if type(name) ~= "string" then
+ error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2)
+ end
+ if defaults and type(defaults) ~= "table" then
+ error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2)
+ end
+ if self.children and self.children[name] then
+ error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2)
+ end
+
+ local sv = self.sv
+ if not sv.namespaces then sv.namespaces = {} end
+ if not sv.namespaces[name] then
+ sv.namespaces[name] = {}
+ end
+
+ local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self)
+
+ if not self.children then self.children = {} end
+ self.children[name] = newDB
+ return newDB
+end
+
+--- Returns an already existing namespace from the database object.
+-- @param name The name of the new namespace
+-- @param silent if true, the addon is optional, silently return nil if its not found
+-- @usage
+-- local namespace = self.db:GetNamespace('namespace')
+-- @return the namespace object if found
+function DBObjectLib:GetNamespace(name, silent)
+ if type(name) ~= "string" then
+ error("Usage: AceDBObject:GetNamespace(name): 'name' - string expected.", 2)
+ end
+ if not silent and not (self.children and self.children[name]) then
+ error ("Usage: AceDBObject:GetNamespace(name): 'name' - namespace does not exist.", 2)
+ end
+ if not self.children then self.children = {} end
+ return self.children[name]
+end
+
+--[[-------------------------------------------------------------------------
+ AceDB Exposed Methods
+---------------------------------------------------------------------------]]
+
+--- Creates a new database object that can be used to handle database settings and profiles.
+-- By default, an empty DB is created, using a character specific profile.
+--
+-- You can override the default profile used by passing any profile name as the third argument,
+-- or by passing //true// as the third argument to use a globally shared profile called "Default".
+--
+-- Note that there is no token replacement in the default profile name, passing a defaultProfile as "char"
+-- will use a profile named "char", and not a character-specific profile.
+-- @param tbl The name of variable, or table to use for the database
+-- @param defaults A table of database defaults
+-- @param defaultProfile The name of the default profile. If not set, a character specific profile will be used as the default.
+-- You can also pass //true// to use a shared global profile called "Default".
+-- @usage
+-- -- Create an empty DB using a character-specific default profile.
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB")
+-- @usage
+-- -- Create a DB using defaults and using a shared default profile
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
+function AceDB:New(tbl, defaults, defaultProfile)
+ if type(tbl) == "string" then
+ local name = tbl
+ tbl = _G[name]
+ if not tbl then
+ tbl = {}
+ _G[name] = tbl
+ end
+ end
+
+ if type(tbl) ~= "table" then
+ error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2)
+ end
+
+ if defaults and type(defaults) ~= "table" then
+ error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2)
+ end
+
+ if defaultProfile and type(defaultProfile) ~= "string" and defaultProfile ~= true then
+ error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string or true expected.", 2)
+ end
+
+ return initdb(tbl, defaults, defaultProfile)
+end
+
+-- upgrade existing databases
+for db in pairs(AceDB.db_registry) do
+ if not db.parent then
+ for name,func in pairs(DBObjectLib) do
+ db[name] = func
+ end
+ else
+ db.RegisterDefaults = DBObjectLib.RegisterDefaults
+ db.ResetProfile = DBObjectLib.ResetProfile
+ end
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceDB-3.0/AceDB-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceDB-3.0/AceDB-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceEvent-3.0/AceEvent-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceEvent-3.0/AceEvent-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,126 @@
+--- AceEvent-3.0 provides event registration and secure dispatching.
+-- All dispatching is done using **CallbackHandler-1.0**. AceEvent is a simple wrapper around
+-- CallbackHandler, and dispatches all game events or addon message to the registrees.
+--
+-- **AceEvent-3.0** can be embeded into your addon, either explicitly by calling AceEvent:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceEvent itself.\\
+-- It is recommended to embed AceEvent, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceEvent.
+-- @class file
+-- @name AceEvent-3.0
+-- @release $Id: AceEvent-3.0.lua 975 2010-10-23 11:26:18Z nevcairiel $
+local MAJOR, MINOR = "AceEvent-3.0", 3
+local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceEvent then return end
+
+-- Lua APIs
+local pairs = pairs
+
+local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
+
+AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame
+AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib
+
+-- APIs and registry for blizzard events, using CallbackHandler lib
+if not AceEvent.events then
+ AceEvent.events = CallbackHandler:New(AceEvent,
+ "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents")
+end
+
+function AceEvent.events:OnUsed(target, eventname)
+ AceEvent.frame:RegisterEvent(eventname)
+end
+
+function AceEvent.events:OnUnused(target, eventname)
+ AceEvent.frame:UnregisterEvent(eventname)
+end
+
+
+-- APIs and registry for IPC messages, using CallbackHandler lib
+if not AceEvent.messages then
+ AceEvent.messages = CallbackHandler:New(AceEvent,
+ "RegisterMessage", "UnregisterMessage", "UnregisterAllMessages"
+ )
+ AceEvent.SendMessage = AceEvent.messages.Fire
+end
+
+--- embedding and embed handling
+local mixins = {
+ "RegisterEvent", "UnregisterEvent",
+ "RegisterMessage", "UnregisterMessage",
+ "SendMessage",
+ "UnregisterAllEvents", "UnregisterAllMessages",
+}
+
+--- Register for a Blizzard Event.
+-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterEvent
+-- @class function
+-- @paramsig event[, callback [, arg]]
+-- @param event The event to register for
+-- @param callback The callback function to call when the event is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister an event.
+-- @name AceEvent:UnregisterEvent
+-- @class function
+-- @paramsig event
+-- @param event The event to unregister
+
+--- Register for a custom AceEvent-internal message.
+-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterMessage
+-- @class function
+-- @paramsig message[, callback [, arg]]
+-- @param message The message to register for
+-- @param callback The callback function to call when the message is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister a message
+-- @name AceEvent:UnregisterMessage
+-- @class function
+-- @paramsig message
+-- @param message The message to unregister
+
+--- Send a message over the AceEvent-3.0 internal message system to other addons registered for this message.
+-- @name AceEvent:SendMessage
+-- @class function
+-- @paramsig message, ...
+-- @param message The message to send
+-- @param ... Any arguments to the message
+
+
+-- Embeds AceEvent into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceEvent in
+function AceEvent:Embed(target)
+ for k, v in pairs(mixins) do
+ target[v] = self[v]
+ end
+ self.embeds[target] = true
+ return target
+end
+
+-- AceEvent:OnEmbedDisable( target )
+-- target (object) - target object that is being disabled
+--
+-- Unregister all events messages etc when the target disables.
+-- this method should be called by the target manually or by an addon framework
+function AceEvent:OnEmbedDisable(target)
+ target:UnregisterAllEvents()
+ target:UnregisterAllMessages()
+end
+
+-- Script to fire blizzard events into the event listeners
+local events = AceEvent.events
+AceEvent.frame:SetScript("OnEvent", function(this, event, ...)
+ events:Fire(event, ...)
+end)
+
+--- Finally: upgrade our old embeds
+for target, v in pairs(AceEvent.embeds) do
+ AceEvent:Embed(target)
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceEvent-3.0/AceEvent-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceEvent-3.0/AceEvent-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/AceGUI-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/AceGUI-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,813 @@
+--- **AceGUI-3.0** provides access to numerous widgets which can be used to create GUIs.
+-- AceGUI is used by AceConfigDialog to create the option GUIs, but you can use it by itself
+-- to create any custom GUI. There are more extensive examples in the test suite in the Ace3
+-- stand-alone distribution.
+--
+-- **Note**: When using AceGUI-3.0 directly, please do not modify the frames of the widgets directly,
+-- as any "unknown" change to the widgets will cause addons that get your widget out of the widget pool
+-- to misbehave. If you think some part of a widget should be modifiable, please open a ticket, and we"ll
+-- implement a proper API to modify it.
+-- @usage
+-- local AceGUI = LibStub("AceGUI-3.0")
+-- -- Create a container frame
+-- local f = AceGUI:Create("Frame")
+-- f:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end)
+-- f:SetTitle("AceGUI-3.0 Example")
+-- f:SetStatusText("Status Bar")
+-- f:SetLayout("Flow")
+-- -- Create a button
+-- local btn = AceGUI:Create("Button")
+-- btn:SetWidth(170)
+-- btn:SetText("Button !")
+-- btn:SetCallback("OnClick", function() print("Click!") end)
+-- -- Add the button to the container
+-- f:AddChild(btn)
+-- @class file
+-- @name AceGUI-3.0
+-- @release $Id: AceGUI-3.0.lua 1102 2013-10-25 14:15:23Z nevcairiel $
+local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 34
+local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR)
+
+if not AceGUI then return end -- No upgrade needed
+
+-- Lua APIs
+local tconcat, tremove, tinsert = table.concat, table.remove, table.insert
+local select, pairs, next, type = select, pairs, next, type
+local error, assert, loadstring = error, assert, loadstring
+local setmetatable, rawget, rawset = setmetatable, rawget, rawset
+local math_max = math.max
+
+-- WoW APIs
+local UIParent = UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: geterrorhandler, LibStub
+
+--local con = LibStub("AceConsole-3.0",true)
+
+AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {}
+AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {}
+AceGUI.WidgetBase = AceGUI.WidgetBase or {}
+AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {}
+AceGUI.WidgetVersions = AceGUI.WidgetVersions or {}
+
+-- local upvalues
+local WidgetRegistry = AceGUI.WidgetRegistry
+local LayoutRegistry = AceGUI.LayoutRegistry
+local WidgetVersions = AceGUI.WidgetVersions
+
+--[[
+ xpcall safecall implementation
+]]
+local xpcall = xpcall
+
+local function errorhandler(err)
+ return geterrorhandler()(err)
+end
+
+local function CreateDispatcher(argCount)
+ local code = [[
+ local xpcall, eh = ...
+ local method, ARGS
+ local function call() return method(ARGS) end
+
+ local function dispatch(func, ...)
+ method = func
+ if not method then return end
+ ARGS = ...
+ return xpcall(call, eh)
+ end
+
+ return dispatch
+ ]]
+
+ local ARGS = {}
+ for i = 1, argCount do ARGS[i] = "arg"..i end
+ code = code:gsub("ARGS", tconcat(ARGS, ", "))
+ return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {__index=function(self, argCount)
+ local dispatcher = CreateDispatcher(argCount)
+ rawset(self, argCount, dispatcher)
+ return dispatcher
+end})
+Dispatchers[0] = function(func)
+ return xpcall(func, errorhandler)
+end
+
+local function safecall(func, ...)
+ return Dispatchers[select("#", ...)](func, ...)
+end
+
+-- Recycling functions
+local newWidget, delWidget
+do
+ -- Version Upgrade in Minor 29
+ -- Internal Storage of the objects changed, from an array table
+ -- to a hash table, and additionally we introduced versioning on
+ -- the widgets which would discard all widgets from a pre-29 version
+ -- anyway, so we just clear the storage now, and don't try to
+ -- convert the storage tables to the new format.
+ -- This should generally not cause *many* widgets to end up in trash,
+ -- since once dialogs are opened, all addons should be loaded already
+ -- and AceGUI should be on the latest version available on the users
+ -- setup.
+ -- -- nevcairiel - Nov 2nd, 2009
+ if oldminor and oldminor < 29 and AceGUI.objPools then
+ AceGUI.objPools = nil
+ end
+
+ AceGUI.objPools = AceGUI.objPools or {}
+ local objPools = AceGUI.objPools
+ --Returns a new instance, if none are available either returns a new table or calls the given contructor
+ function newWidget(type)
+ if not WidgetRegistry[type] then
+ error("Attempt to instantiate unknown widget type", 2)
+ end
+
+ if not objPools[type] then
+ objPools[type] = {}
+ end
+
+ local newObj = next(objPools[type])
+ if not newObj then
+ newObj = WidgetRegistry[type]()
+ newObj.AceGUIWidgetVersion = WidgetVersions[type]
+ else
+ objPools[type][newObj] = nil
+ -- if the widget is older then the latest, don't even try to reuse it
+ -- just forget about it, and grab a new one.
+ if not newObj.AceGUIWidgetVersion or newObj.AceGUIWidgetVersion < WidgetVersions[type] then
+ return newWidget(type)
+ end
+ end
+ return newObj
+ end
+ -- Releases an instance to the Pool
+ function delWidget(obj,type)
+ if not objPools[type] then
+ objPools[type] = {}
+ end
+ if objPools[type][obj] then
+ error("Attempt to Release Widget that is already released", 2)
+ end
+ objPools[type][obj] = true
+ end
+end
+
+
+-------------------
+-- API Functions --
+-------------------
+
+-- Gets a widget Object
+
+--- Create a new Widget of the given type.
+-- This function will instantiate a new widget (or use one from the widget pool), and call the
+-- OnAcquire function on it, before returning.
+-- @param type The type of the widget.
+-- @return The newly created widget.
+function AceGUI:Create(type)
+ if WidgetRegistry[type] then
+ local widget = newWidget(type)
+
+ if rawget(widget, "Acquire") then
+ widget.OnAcquire = widget.Acquire
+ widget.Acquire = nil
+ elseif rawget(widget, "Aquire") then
+ widget.OnAcquire = widget.Aquire
+ widget.Aquire = nil
+ end
+
+ if rawget(widget, "Release") then
+ widget.OnRelease = rawget(widget, "Release")
+ widget.Release = nil
+ end
+
+ if widget.OnAcquire then
+ widget:OnAcquire()
+ else
+ error(("Widget type %s doesn't supply an OnAcquire Function"):format(type))
+ end
+ -- Set the default Layout ("List")
+ safecall(widget.SetLayout, widget, "List")
+ safecall(widget.ResumeLayout, widget)
+ return widget
+ end
+end
+
+--- Releases a widget Object.
+-- This function calls OnRelease on the widget and places it back in the widget pool.
+-- Any data on the widget is being erased, and the widget will be hidden.\\
+-- If this widget is a Container-Widget, all of its Child-Widgets will be releases as well.
+-- @param widget The widget to release
+function AceGUI:Release(widget)
+ safecall(widget.PauseLayout, widget)
+ widget:Fire("OnRelease")
+ safecall(widget.ReleaseChildren, widget)
+
+ if widget.OnRelease then
+ widget:OnRelease()
+-- else
+-- error(("Widget type %s doesn't supply an OnRelease Function"):format(widget.type))
+ end
+ for k in pairs(widget.userdata) do
+ widget.userdata[k] = nil
+ end
+ for k in pairs(widget.events) do
+ widget.events[k] = nil
+ end
+ widget.width = nil
+ widget.relWidth = nil
+ widget.height = nil
+ widget.relHeight = nil
+ widget.noAutoHeight = nil
+ widget.frame:ClearAllPoints()
+ widget.frame:Hide()
+ widget.frame:SetParent(UIParent)
+ widget.frame.width = nil
+ widget.frame.height = nil
+ if widget.content then
+ widget.content.width = nil
+ widget.content.height = nil
+ end
+ delWidget(widget, widget.type)
+end
+
+-----------
+-- Focus --
+-----------
+
+
+--- Called when a widget has taken focus.
+-- e.g. Dropdowns opening, Editboxes gaining kb focus
+-- @param widget The widget that should be focused
+function AceGUI:SetFocus(widget)
+ if self.FocusedWidget and self.FocusedWidget ~= widget then
+ safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
+ end
+ self.FocusedWidget = widget
+end
+
+
+--- Called when something has happened that could cause widgets with focus to drop it
+-- e.g. titlebar of a frame being clicked
+function AceGUI:ClearFocus()
+ if self.FocusedWidget then
+ safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
+ self.FocusedWidget = nil
+ end
+end
+
+-------------
+-- Widgets --
+-------------
+--[[
+ Widgets must provide the following functions
+ OnAcquire() - Called when the object is acquired, should set everything to a default hidden state
+
+ And the following members
+ frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes
+ type - the type of the object, same as the name given to :RegisterWidget()
+
+ Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet
+ It will be cleared automatically when a widget is released
+ Placing values directly into a widget object should be avoided
+
+ If the Widget can act as a container for other Widgets the following
+ content - frame or derivitive that children will be anchored to
+
+ The Widget can supply the following Optional Members
+ :OnRelease() - Called when the object is Released, should remove any additional anchors and clear any data
+ :OnWidthSet(width) - Called when the width of the widget is changed
+ :OnHeightSet(height) - Called when the height of the widget is changed
+ Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead
+ AceGUI already sets a handler to the event
+ :LayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the
+ area used for controls. These can be nil if the layout used the existing size to layout the controls.
+
+]]
+
+--------------------------
+-- Widget Base Template --
+--------------------------
+do
+ local WidgetBase = AceGUI.WidgetBase
+
+ WidgetBase.SetParent = function(self, parent)
+ local frame = self.frame
+ frame:SetParent(nil)
+ frame:SetParent(parent.content)
+ self.parent = parent
+ end
+
+ WidgetBase.SetCallback = function(self, name, func)
+ if type(func) == "function" then
+ self.events[name] = func
+ end
+ end
+
+ WidgetBase.Fire = function(self, name, ...)
+ if self.events[name] then
+ local success, ret = safecall(self.events[name], self, name, ...)
+ if success then
+ return ret
+ end
+ end
+ end
+
+ WidgetBase.SetWidth = function(self, width)
+ self.frame:SetWidth(width)
+ self.frame.width = width
+ if self.OnWidthSet then
+ self:OnWidthSet(width)
+ end
+ end
+
+ WidgetBase.SetRelativeWidth = function(self, width)
+ if width <= 0 or width > 1 then
+ error(":SetRelativeWidth(width): Invalid relative width.", 2)
+ end
+ self.relWidth = width
+ self.width = "relative"
+ end
+
+ WidgetBase.SetHeight = function(self, height)
+ self.frame:SetHeight(height)
+ self.frame.height = height
+ if self.OnHeightSet then
+ self:OnHeightSet(height)
+ end
+ end
+
+ --[[ WidgetBase.SetRelativeHeight = function(self, height)
+ if height <= 0 or height > 1 then
+ error(":SetRelativeHeight(height): Invalid relative height.", 2)
+ end
+ self.relHeight = height
+ self.height = "relative"
+ end ]]
+
+ WidgetBase.IsVisible = function(self)
+ return self.frame:IsVisible()
+ end
+
+ WidgetBase.IsShown= function(self)
+ return self.frame:IsShown()
+ end
+
+ WidgetBase.Release = function(self)
+ AceGUI:Release(self)
+ end
+
+ WidgetBase.SetPoint = function(self, ...)
+ return self.frame:SetPoint(...)
+ end
+
+ WidgetBase.ClearAllPoints = function(self)
+ return self.frame:ClearAllPoints()
+ end
+
+ WidgetBase.GetNumPoints = function(self)
+ return self.frame:GetNumPoints()
+ end
+
+ WidgetBase.GetPoint = function(self, ...)
+ return self.frame:GetPoint(...)
+ end
+
+ WidgetBase.GetUserDataTable = function(self)
+ return self.userdata
+ end
+
+ WidgetBase.SetUserData = function(self, key, value)
+ self.userdata[key] = value
+ end
+
+ WidgetBase.GetUserData = function(self, key)
+ return self.userdata[key]
+ end
+
+ WidgetBase.IsFullHeight = function(self)
+ return self.height == "fill"
+ end
+
+ WidgetBase.SetFullHeight = function(self, isFull)
+ if isFull then
+ self.height = "fill"
+ else
+ self.height = nil
+ end
+ end
+
+ WidgetBase.IsFullWidth = function(self)
+ return self.width == "fill"
+ end
+
+ WidgetBase.SetFullWidth = function(self, isFull)
+ if isFull then
+ self.width = "fill"
+ else
+ self.width = nil
+ end
+ end
+
+-- local function LayoutOnUpdate(this)
+-- this:SetScript("OnUpdate",nil)
+-- this.obj:PerformLayout()
+-- end
+
+ local WidgetContainerBase = AceGUI.WidgetContainerBase
+
+ WidgetContainerBase.PauseLayout = function(self)
+ self.LayoutPaused = true
+ end
+
+ WidgetContainerBase.ResumeLayout = function(self)
+ self.LayoutPaused = nil
+ end
+
+ WidgetContainerBase.PerformLayout = function(self)
+ if self.LayoutPaused then
+ return
+ end
+ safecall(self.LayoutFunc, self.content, self.children)
+ end
+
+ --call this function to layout, makes sure layed out objects get a frame to get sizes etc
+ WidgetContainerBase.DoLayout = function(self)
+ self:PerformLayout()
+-- if not self.parent then
+-- self.frame:SetScript("OnUpdate", LayoutOnUpdate)
+-- end
+ end
+
+ WidgetContainerBase.AddChild = function(self, child, beforeWidget)
+ if beforeWidget then
+ local siblingIndex = 1
+ for _, widget in pairs(self.children) do
+ if widget == beforeWidget then
+ break
+ end
+ siblingIndex = siblingIndex + 1
+ end
+ tinsert(self.children, siblingIndex, child)
+ else
+ tinsert(self.children, child)
+ end
+ child:SetParent(self)
+ child.frame:Show()
+ self:DoLayout()
+ end
+
+ WidgetContainerBase.AddChildren = function(self, ...)
+ for i = 1, select("#", ...) do
+ local child = select(i, ...)
+ tinsert(self.children, child)
+ child:SetParent(self)
+ child.frame:Show()
+ end
+ self:DoLayout()
+ end
+
+ WidgetContainerBase.ReleaseChildren = function(self)
+ local children = self.children
+ for i = 1,#children do
+ AceGUI:Release(children[i])
+ children[i] = nil
+ end
+ end
+
+ WidgetContainerBase.SetLayout = function(self, Layout)
+ self.LayoutFunc = AceGUI:GetLayout(Layout)
+ end
+
+ WidgetContainerBase.SetAutoAdjustHeight = function(self, adjust)
+ if adjust then
+ self.noAutoHeight = nil
+ else
+ self.noAutoHeight = true
+ end
+ end
+
+ local function FrameResize(this)
+ local self = this.obj
+ if this:GetWidth() and this:GetHeight() then
+ if self.OnWidthSet then
+ self:OnWidthSet(this:GetWidth())
+ end
+ if self.OnHeightSet then
+ self:OnHeightSet(this:GetHeight())
+ end
+ end
+ end
+
+ local function ContentResize(this)
+ if this:GetWidth() and this:GetHeight() then
+ this.width = this:GetWidth()
+ this.height = this:GetHeight()
+ this.obj:DoLayout()
+ end
+ end
+
+ setmetatable(WidgetContainerBase, {__index=WidgetBase})
+
+ --One of these function should be called on each Widget Instance as part of its creation process
+
+ --- Register a widget-class as a container for newly created widgets.
+ -- @param widget The widget class
+ function AceGUI:RegisterAsContainer(widget)
+ widget.children = {}
+ widget.userdata = {}
+ widget.events = {}
+ widget.base = WidgetContainerBase
+ widget.content.obj = widget
+ widget.frame.obj = widget
+ widget.content:SetScript("OnSizeChanged", ContentResize)
+ widget.frame:SetScript("OnSizeChanged", FrameResize)
+ setmetatable(widget, {__index = WidgetContainerBase})
+ widget:SetLayout("List")
+ return widget
+ end
+
+ --- Register a widget-class as a widget.
+ -- @param widget The widget class
+ function AceGUI:RegisterAsWidget(widget)
+ widget.userdata = {}
+ widget.events = {}
+ widget.base = WidgetBase
+ widget.frame.obj = widget
+ widget.frame:SetScript("OnSizeChanged", FrameResize)
+ setmetatable(widget, {__index = WidgetBase})
+ return widget
+ end
+end
+
+
+
+
+------------------
+-- Widget API --
+------------------
+
+--- Registers a widget Constructor, this function returns a new instance of the Widget
+-- @param Name The name of the widget
+-- @param Constructor The widget constructor function
+-- @param Version The version of the widget
+function AceGUI:RegisterWidgetType(Name, Constructor, Version)
+ assert(type(Constructor) == "function")
+ assert(type(Version) == "number")
+
+ local oldVersion = WidgetVersions[Name]
+ if oldVersion and oldVersion >= Version then return end
+
+ WidgetVersions[Name] = Version
+ WidgetRegistry[Name] = Constructor
+end
+
+--- Registers a Layout Function
+-- @param Name The name of the layout
+-- @param LayoutFunc Reference to the layout function
+function AceGUI:RegisterLayout(Name, LayoutFunc)
+ assert(type(LayoutFunc) == "function")
+ if type(Name) == "string" then
+ Name = Name:upper()
+ end
+ LayoutRegistry[Name] = LayoutFunc
+end
+
+--- Get a Layout Function from the registry
+-- @param Name The name of the layout
+function AceGUI:GetLayout(Name)
+ if type(Name) == "string" then
+ Name = Name:upper()
+ end
+ return LayoutRegistry[Name]
+end
+
+AceGUI.counts = AceGUI.counts or {}
+
+--- A type-based counter to count the number of widgets created.
+-- This is used by widgets that require a named frame, e.g. when a Blizzard
+-- Template requires it.
+-- @param type The widget type
+function AceGUI:GetNextWidgetNum(type)
+ if not self.counts[type] then
+ self.counts[type] = 0
+ end
+ self.counts[type] = self.counts[type] + 1
+ return self.counts[type]
+end
+
+--- Return the number of created widgets for this type.
+-- In contrast to GetNextWidgetNum, the number is not incremented.
+-- @param type The widget type
+function AceGUI:GetWidgetCount(type)
+ return self.counts[type] or 0
+end
+
+--- Return the version of the currently registered widget type.
+-- @param type The widget type
+function AceGUI:GetWidgetVersion(type)
+ return WidgetVersions[type]
+end
+
+-------------
+-- Layouts --
+-------------
+
+--[[
+ A Layout is a func that takes 2 parameters
+ content - the frame that widgets will be placed inside
+ children - a table containing the widgets to layout
+]]
+
+-- Very simple Layout, Children are stacked on top of each other down the left side
+AceGUI:RegisterLayout("List",
+ function(content, children)
+ local height = 0
+ local width = content.width or content:GetWidth() or 0
+ for i = 1, #children do
+ local child = children[i]
+
+ local frame = child.frame
+ frame:ClearAllPoints()
+ frame:Show()
+ if i == 1 then
+ frame:SetPoint("TOPLEFT", content)
+ else
+ frame:SetPoint("TOPLEFT", children[i-1].frame, "BOTTOMLEFT")
+ end
+
+ if child.width == "fill" then
+ child:SetWidth(width)
+ frame:SetPoint("RIGHT", content)
+
+ if child.DoLayout then
+ child:DoLayout()
+ end
+ elseif child.width == "relative" then
+ child:SetWidth(width * child.relWidth)
+
+ if child.DoLayout then
+ child:DoLayout()
+ end
+ end
+
+ height = height + (frame.height or frame:GetHeight() or 0)
+ end
+ safecall(content.obj.LayoutFinished, content.obj, nil, height)
+ end)
+
+-- A single control fills the whole content area
+AceGUI:RegisterLayout("Fill",
+ function(content, children)
+ if children[1] then
+ children[1]:SetWidth(content:GetWidth() or 0)
+ children[1]:SetHeight(content:GetHeight() or 0)
+ children[1].frame:SetAllPoints(content)
+ children[1].frame:Show()
+ safecall(content.obj.LayoutFinished, content.obj, nil, children[1].frame:GetHeight())
+ end
+ end)
+
+local layoutrecursionblock = nil
+local function safelayoutcall(object, func, ...)
+ layoutrecursionblock = true
+ object[func](object, ...)
+ layoutrecursionblock = nil
+end
+
+AceGUI:RegisterLayout("Flow",
+ function(content, children)
+ if layoutrecursionblock then return end
+ --used height so far
+ local height = 0
+ --width used in the current row
+ local usedwidth = 0
+ --height of the current row
+ local rowheight = 0
+ local rowoffset = 0
+ local lastrowoffset
+
+ local width = content.width or content:GetWidth() or 0
+
+ --control at the start of the row
+ local rowstart
+ local rowstartoffset
+ local lastrowstart
+ local isfullheight
+
+ local frameoffset
+ local lastframeoffset
+ local oversize
+ for i = 1, #children do
+ local child = children[i]
+ oversize = nil
+ local frame = child.frame
+ local frameheight = frame.height or frame:GetHeight() or 0
+ local framewidth = frame.width or frame:GetWidth() or 0
+ lastframeoffset = frameoffset
+ -- HACK: Why did we set a frameoffset of (frameheight / 2) ?
+ -- That was moving all widgets half the widgets size down, is that intended?
+ -- Actually, it seems to be neccessary for many cases, we'll leave it in for now.
+ -- If widgets seem to anchor weirdly with this, provide a valid alignoffset for them.
+ -- TODO: Investigate moar!
+ frameoffset = child.alignoffset or (frameheight / 2)
+
+ if child.width == "relative" then
+ framewidth = width * child.relWidth
+ end
+
+ frame:Show()
+ frame:ClearAllPoints()
+ if i == 1 then
+ -- anchor the first control to the top left
+ frame:SetPoint("TOPLEFT", content)
+ rowheight = frameheight
+ rowoffset = frameoffset
+ rowstart = frame
+ rowstartoffset = frameoffset
+ usedwidth = framewidth
+ if usedwidth > width then
+ oversize = true
+ end
+ else
+ -- if there isn't available width for the control start a new row
+ -- if a control is "fill" it will be on a row of its own full width
+ if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then
+ if isfullheight then
+ -- a previous row has already filled the entire height, there's nothing we can usefully do anymore
+ -- (maybe error/warn about this?)
+ break
+ end
+ --anchor the previous row, we will now know its height and offset
+ rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3))
+ height = height + rowheight + 3
+ --save this as the rowstart so we can anchor it after the row is complete and we have the max height and offset of controls in it
+ rowstart = frame
+ rowstartoffset = frameoffset
+ rowheight = frameheight
+ rowoffset = frameoffset
+ usedwidth = framewidth
+ if usedwidth > width then
+ oversize = true
+ end
+ -- put the control on the current row, adding it to the width and checking if the height needs to be increased
+ else
+ --handles cases where the new height is higher than either control because of the offsets
+ --math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset)
+
+ --offset is always the larger of the two offsets
+ rowoffset = math_max(rowoffset, frameoffset)
+ rowheight = math_max(rowheight, rowoffset + (frameheight / 2))
+
+ frame:SetPoint("TOPLEFT", children[i-1].frame, "TOPRIGHT", 0, frameoffset - lastframeoffset)
+ usedwidth = framewidth + usedwidth
+ end
+ end
+
+ if child.width == "fill" then
+ safelayoutcall(child, "SetWidth", width)
+ frame:SetPoint("RIGHT", content)
+
+ usedwidth = 0
+ rowstart = frame
+ rowstartoffset = frameoffset
+
+ if child.DoLayout then
+ child:DoLayout()
+ end
+ rowheight = frame.height or frame:GetHeight() or 0
+ rowoffset = child.alignoffset or (rowheight / 2)
+ rowstartoffset = rowoffset
+ elseif child.width == "relative" then
+ safelayoutcall(child, "SetWidth", width * child.relWidth)
+
+ if child.DoLayout then
+ child:DoLayout()
+ end
+ elseif oversize then
+ if width > 1 then
+ frame:SetPoint("RIGHT", content)
+ end
+ end
+
+ if child.height == "fill" then
+ frame:SetPoint("BOTTOM", content)
+ isfullheight = true
+ end
+ end
+
+ --anchor the last row, if its full height needs a special case since its height has just been changed by the anchor
+ if isfullheight then
+ rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -height)
+ elseif rowstart then
+ rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3))
+ end
+
+ height = height + rowheight + 3
+ safecall(content.obj.LayoutFinished, content.obj, nil, height)
+ end)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/AceGUI-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/AceGUI-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-BlizOptionsGroup.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-BlizOptionsGroup.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,138 @@
+--[[-----------------------------------------------------------------------------
+BlizOptionsGroup Container
+Simple container widget for the integration of AceGUI into the Blizzard Interface Options
+-------------------------------------------------------------------------------]]
+local Type, Version = "BlizOptionsGroup", 21
+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 pairs = pairs
+
+-- WoW APIs
+local CreateFrame = CreateFrame
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+
+local function OnShow(frame)
+ frame.obj:Fire("OnShow")
+end
+
+local function OnHide(frame)
+ frame.obj:Fire("OnHide")
+end
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+
+local function okay(frame)
+ frame.obj:Fire("okay")
+end
+
+local function cancel(frame)
+ frame.obj:Fire("cancel")
+end
+
+local function default(frame)
+ frame.obj:Fire("default")
+end
+
+local function refresh(frame)
+ frame.obj:Fire("refresh")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetName()
+ self:SetTitle()
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ local contentwidth = width - 63
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - 26
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end,
+
+ ["SetName"] = function(self, name, parent)
+ self.frame.name = name
+ self.frame.parent = parent
+ end,
+
+ ["SetTitle"] = function(self, title)
+ local content = self.content
+ content:ClearAllPoints()
+ if not title or title == "" then
+ content:SetPoint("TOPLEFT", 10, -10)
+ self.label:SetText("")
+ else
+ content:SetPoint("TOPLEFT", 10, -40)
+ self.label:SetText(title)
+ end
+ content:SetPoint("BOTTOMRIGHT", -10, 10)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame")
+ frame:Hide()
+
+ -- support functions for the Blizzard Interface Options
+ frame.okay = okay
+ frame.cancel = cancel
+ frame.default = default
+ frame.refresh = refresh
+
+ frame:SetScript("OnHide", OnHide)
+ frame:SetScript("OnShow", OnShow)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
+ label:SetPoint("TOPLEFT", 10, -15)
+ label:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", 10, -45)
+ label:SetJustifyH("LEFT")
+ label:SetJustifyV("TOP")
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, frame)
+ content:SetPoint("TOPLEFT", 10, -10)
+ content:SetPoint("BOTTOMRIGHT", -10, 10)
+
+ local widget = {
+ label = label,
+ frame = frame,
+ content = content,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-DropDownGroup.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-DropDownGroup.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,157 @@
+--[[-----------------------------------------------------------------------------
+DropdownGroup Container
+Container controlled by a dropdown on the top.
+-------------------------------------------------------------------------------]]
+local Type, Version = "DropdownGroup", 21
+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 assert, pairs, type = assert, pairs, type
+
+-- WoW APIs
+local CreateFrame = CreateFrame
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function SelectedGroup(self, event, value)
+ local group = self.parentgroup
+ local status = group.status or group.localstatus
+ status.selected = value
+ self.parentgroup:Fire("OnGroupSelected", value)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self.dropdown:SetText("")
+ self:SetDropdownWidth(200)
+ self:SetTitle("")
+ end,
+
+ ["OnRelease"] = function(self)
+ self.dropdown.list = nil
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ end,
+
+ ["SetTitle"] = function(self, title)
+ self.titletext:SetText(title)
+ self.dropdown.frame:ClearAllPoints()
+ if title and title ~= "" then
+ self.dropdown.frame:SetPoint("TOPRIGHT", -2, 0)
+ else
+ self.dropdown.frame:SetPoint("TOPLEFT", -1, 0)
+ end
+ end,
+
+ ["SetGroupList"] = function(self,list,order)
+ self.dropdown:SetList(list,order)
+ end,
+
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ end,
+
+ ["SetGroup"] = function(self,group)
+ self.dropdown:SetValue(group)
+ local status = self.status or self.localstatus
+ status.selected = group
+ self:Fire("OnGroupSelected", group)
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ local contentwidth = width - 26
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - 63
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end,
+
+ ["LayoutFinished"] = function(self, width, height)
+ self:SetHeight((height or 0) + 63)
+ end,
+
+ ["SetDropdownWidth"] = function(self, width)
+ self.dropdown:SetWidth(width)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local PaneBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 5, bottom = 3 }
+}
+
+local function Constructor()
+ local frame = CreateFrame("Frame")
+ frame:SetHeight(100)
+ frame:SetWidth(100)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local titletext = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ titletext:SetPoint("TOPLEFT", 4, -5)
+ titletext:SetPoint("TOPRIGHT", -4, -5)
+ titletext:SetJustifyH("LEFT")
+ titletext:SetHeight(18)
+
+ local dropdown = AceGUI:Create("Dropdown")
+ dropdown.frame:SetParent(frame)
+ dropdown.frame:SetFrameLevel(dropdown.frame:GetFrameLevel() + 2)
+ dropdown:SetCallback("OnValueChanged", SelectedGroup)
+ dropdown.frame:SetPoint("TOPLEFT", -1, 0)
+ dropdown.frame:Show()
+ dropdown:SetLabel("")
+
+ local border = CreateFrame("Frame", nil, frame)
+ border:SetPoint("TOPLEFT", 0, -26)
+ border:SetPoint("BOTTOMRIGHT", 0, 3)
+ border:SetBackdrop(PaneBackdrop)
+ border:SetBackdropColor(0.1,0.1,0.1,0.5)
+ border:SetBackdropBorderColor(0.4,0.4,0.4)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, border)
+ content:SetPoint("TOPLEFT", 10, -10)
+ content:SetPoint("BOTTOMRIGHT", -10, 10)
+
+ local widget = {
+ frame = frame,
+ localstatus = {},
+ titletext = titletext,
+ dropdown = dropdown,
+ border = border,
+ content = content,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ dropdown.parentgroup = widget
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,311 @@
+--[[-----------------------------------------------------------------------------
+Frame Container
+-------------------------------------------------------------------------------]]
+local Type, Version = "Frame", 24
+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 pairs, assert, type = pairs, assert, type
+local wipe = table.wipe
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: CLOSE
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Button_OnClick(frame)
+ PlaySound("gsTitleOptionExit")
+ frame.obj:Hide()
+end
+
+local function Frame_OnClose(frame)
+ frame.obj:Fire("OnClose")
+end
+
+local function Frame_OnMouseDown(frame)
+ AceGUI:ClearFocus()
+end
+
+local function Title_OnMouseDown(frame)
+ frame:GetParent():StartMoving()
+ AceGUI:ClearFocus()
+end
+
+local function MoverSizer_OnMouseUp(mover)
+ local frame = mover:GetParent()
+ frame:StopMovingOrSizing()
+ local self = frame.obj
+ local status = self.status or self.localstatus
+ status.width = frame:GetWidth()
+ status.height = frame:GetHeight()
+ status.top = frame:GetTop()
+ status.left = frame:GetLeft()
+end
+
+local function SizerSE_OnMouseDown(frame)
+ frame:GetParent():StartSizing("BOTTOMRIGHT")
+ AceGUI:ClearFocus()
+end
+
+local function SizerS_OnMouseDown(frame)
+ frame:GetParent():StartSizing("BOTTOM")
+ AceGUI:ClearFocus()
+end
+
+local function SizerE_OnMouseDown(frame)
+ frame:GetParent():StartSizing("RIGHT")
+ AceGUI:ClearFocus()
+end
+
+local function StatusBar_OnEnter(frame)
+ frame.obj:Fire("OnEnterStatusBar")
+end
+
+local function StatusBar_OnLeave(frame)
+ frame.obj:Fire("OnLeaveStatusBar")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self.frame:SetParent(UIParent)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ self:SetTitle()
+ self:SetStatusText()
+ self:ApplyStatus()
+ self:Show()
+ self:EnableResize(true)
+ end,
+
+ ["OnRelease"] = function(self)
+ self.status = nil
+ wipe(self.localstatus)
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ local contentwidth = width - 34
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - 57
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end,
+
+ ["SetTitle"] = function(self, title)
+ self.titletext:SetText(title)
+ self.titlebg:SetWidth((self.titletext:GetWidth() or 0) + 10)
+ end,
+
+ ["SetStatusText"] = function(self, text)
+ self.statustext:SetText(text)
+ end,
+
+ ["Hide"] = function(self)
+ self.frame:Hide()
+ end,
+
+ ["Show"] = function(self)
+ self.frame:Show()
+ end,
+
+ ["EnableResize"] = function(self, state)
+ local func = state and "Show" or "Hide"
+ self.sizer_se[func](self.sizer_se)
+ self.sizer_s[func](self.sizer_s)
+ self.sizer_e[func](self.sizer_e)
+ end,
+
+ -- called to set an external table to store status in
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ self:ApplyStatus()
+ end,
+
+ ["ApplyStatus"] = function(self)
+ local status = self.status or self.localstatus
+ local frame = self.frame
+ self:SetWidth(status.width or 700)
+ self:SetHeight(status.height or 500)
+ frame:ClearAllPoints()
+ if status.top and status.left then
+ frame:SetPoint("TOP", UIParent, "BOTTOM", 0, status.top)
+ frame:SetPoint("LEFT", UIParent, "LEFT", status.left, 0)
+ else
+ frame:SetPoint("CENTER")
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local FrameBackdrop = {
+ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
+ edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
+ tile = true, tileSize = 32, edgeSize = 32,
+ insets = { left = 8, right = 8, top = 8, bottom = 8 }
+}
+
+local PaneBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 5, bottom = 3 }
+}
+
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetMovable(true)
+ frame:SetResizable(true)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ frame:SetBackdrop(FrameBackdrop)
+ frame:SetBackdropColor(0, 0, 0, 1)
+ frame:SetMinResize(400, 200)
+ frame:SetToplevel(true)
+ frame:SetScript("OnHide", Frame_OnClose)
+ frame:SetScript("OnMouseDown", Frame_OnMouseDown)
+
+ local closebutton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
+ closebutton:SetScript("OnClick", Button_OnClick)
+ closebutton:SetPoint("BOTTOMRIGHT", -27, 17)
+ closebutton:SetHeight(20)
+ closebutton:SetWidth(100)
+ closebutton:SetText(CLOSE)
+
+ local statusbg = CreateFrame("Button", nil, frame)
+ statusbg:SetPoint("BOTTOMLEFT", 15, 15)
+ statusbg:SetPoint("BOTTOMRIGHT", -132, 15)
+ statusbg:SetHeight(24)
+ statusbg:SetBackdrop(PaneBackdrop)
+ statusbg:SetBackdropColor(0.1,0.1,0.1)
+ statusbg:SetBackdropBorderColor(0.4,0.4,0.4)
+ statusbg:SetScript("OnEnter", StatusBar_OnEnter)
+ statusbg:SetScript("OnLeave", StatusBar_OnLeave)
+
+ local statustext = statusbg:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ statustext:SetPoint("TOPLEFT", 7, -2)
+ statustext:SetPoint("BOTTOMRIGHT", -7, 2)
+ statustext:SetHeight(20)
+ statustext:SetJustifyH("LEFT")
+ statustext:SetText("")
+
+ local titlebg = frame:CreateTexture(nil, "OVERLAY")
+ titlebg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
+ titlebg:SetTexCoord(0.31, 0.67, 0, 0.63)
+ titlebg:SetPoint("TOP", 0, 12)
+ titlebg:SetWidth(100)
+ titlebg:SetHeight(40)
+
+ local title = CreateFrame("Frame", nil, frame)
+ title:EnableMouse(true)
+ title:SetScript("OnMouseDown", Title_OnMouseDown)
+ title:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
+ title:SetAllPoints(titlebg)
+
+ local titletext = title:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ titletext:SetPoint("TOP", titlebg, "TOP", 0, -14)
+
+ local titlebg_l = frame:CreateTexture(nil, "OVERLAY")
+ titlebg_l:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
+ titlebg_l:SetTexCoord(0.21, 0.31, 0, 0.63)
+ titlebg_l:SetPoint("RIGHT", titlebg, "LEFT")
+ titlebg_l:SetWidth(30)
+ titlebg_l:SetHeight(40)
+
+ local titlebg_r = frame:CreateTexture(nil, "OVERLAY")
+ titlebg_r:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
+ titlebg_r:SetTexCoord(0.67, 0.77, 0, 0.63)
+ titlebg_r:SetPoint("LEFT", titlebg, "RIGHT")
+ titlebg_r:SetWidth(30)
+ titlebg_r:SetHeight(40)
+
+ local sizer_se = CreateFrame("Frame", nil, frame)
+ sizer_se:SetPoint("BOTTOMRIGHT")
+ sizer_se:SetWidth(25)
+ sizer_se:SetHeight(25)
+ sizer_se:EnableMouse()
+ sizer_se:SetScript("OnMouseDown",SizerSE_OnMouseDown)
+ sizer_se:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
+
+ local line1 = sizer_se:CreateTexture(nil, "BACKGROUND")
+ line1:SetWidth(14)
+ line1:SetHeight(14)
+ line1:SetPoint("BOTTOMRIGHT", -8, 8)
+ line1:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
+ local x = 0.1 * 14/17
+ line1:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
+
+ local line2 = sizer_se:CreateTexture(nil, "BACKGROUND")
+ line2:SetWidth(8)
+ line2:SetHeight(8)
+ line2:SetPoint("BOTTOMRIGHT", -8, 8)
+ line2:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
+ local x = 0.1 * 8/17
+ line2:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
+
+ local sizer_s = CreateFrame("Frame", nil, frame)
+ sizer_s:SetPoint("BOTTOMRIGHT", -25, 0)
+ sizer_s:SetPoint("BOTTOMLEFT")
+ sizer_s:SetHeight(25)
+ sizer_s:EnableMouse(true)
+ sizer_s:SetScript("OnMouseDown", SizerS_OnMouseDown)
+ sizer_s:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
+
+ local sizer_e = CreateFrame("Frame", nil, frame)
+ sizer_e:SetPoint("BOTTOMRIGHT", 0, 25)
+ sizer_e:SetPoint("TOPRIGHT")
+ sizer_e:SetWidth(25)
+ sizer_e:EnableMouse(true)
+ sizer_e:SetScript("OnMouseDown", SizerE_OnMouseDown)
+ sizer_e:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, frame)
+ content:SetPoint("TOPLEFT", 17, -27)
+ content:SetPoint("BOTTOMRIGHT", -17, 40)
+
+ local widget = {
+ localstatus = {},
+ titletext = titletext,
+ statustext = statustext,
+ titlebg = titlebg,
+ sizer_se = sizer_se,
+ sizer_s = sizer_s,
+ sizer_e = sizer_e,
+ content = content,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ closebutton.obj, statusbg.obj = widget, widget
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-InlineGroup.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-InlineGroup.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,103 @@
+--[[-----------------------------------------------------------------------------
+InlineGroup Container
+Simple container widget that creates a visible "box" with an optional title.
+-------------------------------------------------------------------------------]]
+local Type, Version = "InlineGroup", 21
+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 pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetWidth(300)
+ self:SetHeight(100)
+ self:SetTitle("")
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetTitle"] = function(self,title)
+ self.titletext:SetText(title)
+ end,
+
+
+ ["LayoutFinished"] = function(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight((height or 0) + 40)
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ local contentwidth = width - 20
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - 20
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local PaneBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 5, bottom = 3 }
+}
+
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local titletext = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ titletext:SetPoint("TOPLEFT", 14, 0)
+ titletext:SetPoint("TOPRIGHT", -14, 0)
+ titletext:SetJustifyH("LEFT")
+ titletext:SetHeight(18)
+
+ local border = CreateFrame("Frame", nil, frame)
+ border:SetPoint("TOPLEFT", 0, -17)
+ border:SetPoint("BOTTOMRIGHT", -1, 3)
+ border:SetBackdrop(PaneBackdrop)
+ border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
+ border:SetBackdropBorderColor(0.4, 0.4, 0.4)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, border)
+ content:SetPoint("TOPLEFT", 10, -10)
+ content:SetPoint("BOTTOMRIGHT", -10, 10)
+
+ local widget = {
+ frame = frame,
+ content = content,
+ titletext = titletext,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-ScrollFrame.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-ScrollFrame.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,204 @@
+--[[-----------------------------------------------------------------------------
+ScrollFrame Container
+Plain container that scrolls its content and doesn't grow in height.
+-------------------------------------------------------------------------------]]
+local Type, Version = "ScrollFrame", 23
+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 pairs, assert, type = pairs, assert, type
+local min, max, floor, abs = math.min, math.max, math.floor, math.abs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function FixScrollOnUpdate(frame)
+ frame:SetScript("OnUpdate", nil)
+ frame.obj:FixScroll()
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function ScrollFrame_OnMouseWheel(frame, value)
+ frame.obj:MoveScroll(value)
+end
+
+local function ScrollFrame_OnSizeChanged(frame)
+ frame:SetScript("OnUpdate", FixScrollOnUpdate)
+end
+
+local function ScrollBar_OnScrollValueChanged(frame, value)
+ frame.obj:SetScroll(value)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetScroll(0)
+ self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
+ end,
+
+ ["OnRelease"] = function(self)
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ self.scrollframe:SetPoint("BOTTOMRIGHT")
+ self.scrollbar:Hide()
+ self.scrollBarShown = nil
+ self.content.height, self.content.width = nil, nil
+ end,
+
+ ["SetScroll"] = function(self, value)
+ local status = self.status or self.localstatus
+ local viewheight = self.scrollframe:GetHeight()
+ local height = self.content:GetHeight()
+ local offset
+
+ if viewheight > height then
+ offset = 0
+ else
+ offset = floor((height - viewheight) / 1000.0 * value)
+ end
+ self.content:ClearAllPoints()
+ self.content:SetPoint("TOPLEFT", 0, offset)
+ self.content:SetPoint("TOPRIGHT", 0, offset)
+ status.offset = offset
+ status.scrollvalue = value
+ end,
+
+ ["MoveScroll"] = function(self, value)
+ local status = self.status or self.localstatus
+ local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight()
+
+ if self.scrollBarShown then
+ local diff = height - viewheight
+ local delta = 1
+ if value < 0 then
+ delta = -1
+ end
+ self.scrollbar:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
+ end
+ end,
+
+ ["FixScroll"] = function(self)
+ if self.updateLock then return end
+ self.updateLock = true
+ local status = self.status or self.localstatus
+ local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight()
+ local offset = status.offset or 0
+ local curvalue = self.scrollbar:GetValue()
+ -- Give us a margin of error of 2 pixels to stop some conditions that i would blame on floating point inaccuracys
+ -- No-one is going to miss 2 pixels at the bottom of the frame, anyhow!
+ if viewheight < height + 2 then
+ if self.scrollBarShown then
+ self.scrollBarShown = nil
+ self.scrollbar:Hide()
+ self.scrollbar:SetValue(0)
+ self.scrollframe:SetPoint("BOTTOMRIGHT")
+ self:DoLayout()
+ end
+ else
+ if not self.scrollBarShown then
+ self.scrollBarShown = true
+ self.scrollbar:Show()
+ self.scrollframe:SetPoint("BOTTOMRIGHT", -20, 0)
+ self:DoLayout()
+ end
+ local value = (offset / (viewheight - height) * 1000)
+ if value > 1000 then value = 1000 end
+ self.scrollbar:SetValue(value)
+ self:SetScroll(value)
+ if value < 1000 then
+ self.content:ClearAllPoints()
+ self.content:SetPoint("TOPLEFT", 0, offset)
+ self.content:SetPoint("TOPRIGHT", 0, offset)
+ status.offset = offset
+ end
+ end
+ self.updateLock = nil
+ end,
+
+ ["LayoutFinished"] = function(self, width, height)
+ self.content:SetHeight(height or 0 + 20)
+ self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
+ end,
+
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ if not status.scrollvalue then
+ status.scrollvalue = 0
+ end
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ content.width = width
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ content.height = height
+ end
+}
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ local num = AceGUI:GetNextWidgetNum(Type)
+
+ local scrollframe = CreateFrame("ScrollFrame", nil, frame)
+ scrollframe:SetPoint("TOPLEFT")
+ scrollframe:SetPoint("BOTTOMRIGHT")
+ scrollframe:EnableMouseWheel(true)
+ scrollframe:SetScript("OnMouseWheel", ScrollFrame_OnMouseWheel)
+ scrollframe:SetScript("OnSizeChanged", ScrollFrame_OnSizeChanged)
+
+ local scrollbar = CreateFrame("Slider", ("AceConfigDialogScrollFrame%dScrollBar"):format(num), scrollframe, "UIPanelScrollBarTemplate")
+ scrollbar:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, -16)
+ scrollbar:SetPoint("BOTTOMLEFT", scrollframe, "BOTTOMRIGHT", 4, 16)
+ scrollbar:SetMinMaxValues(0, 1000)
+ scrollbar:SetValueStep(1)
+ scrollbar:SetValue(0)
+ scrollbar:SetWidth(16)
+ scrollbar:Hide()
+ -- set the script as the last step, so it doesn't fire yet
+ scrollbar:SetScript("OnValueChanged", ScrollBar_OnScrollValueChanged)
+
+ local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
+ scrollbg:SetAllPoints(scrollbar)
+ scrollbg:SetTexture(0, 0, 0, 0.4)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, scrollframe)
+ content:SetPoint("TOPLEFT")
+ content:SetPoint("TOPRIGHT")
+ content:SetHeight(400)
+ scrollframe:SetScrollChild(content)
+
+ local widget = {
+ localstatus = { scrollvalue = 0 },
+ scrollframe = scrollframe,
+ scrollbar = scrollbar,
+ content = content,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ scrollframe.obj, scrollbar.obj = widget, widget
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-SimpleGroup.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-SimpleGroup.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,69 @@
+--[[-----------------------------------------------------------------------------
+SimpleGroup Container
+Simple container widget that just groups widgets.
+-------------------------------------------------------------------------------]]
+local Type, Version = "SimpleGroup", 20
+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 pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetWidth(300)
+ self:SetHeight(100)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["LayoutFinished"] = function(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight(height or 0)
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ content:SetWidth(width)
+ content.width = width
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ content:SetHeight(height)
+ content.height = height
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, frame)
+ content:SetPoint("TOPLEFT")
+ content:SetPoint("BOTTOMRIGHT")
+
+ local widget = {
+ frame = frame,
+ content = content,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,350 @@
+--[[-----------------------------------------------------------------------------
+TabGroup Container
+Container that uses tabs on top to switch between groups.
+-------------------------------------------------------------------------------]]
+local Type, Version = "TabGroup", 35
+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 pairs, ipairs, assert, type, wipe = pairs, ipairs, assert, type, wipe
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: PanelTemplates_TabResize, PanelTemplates_SetDisabledTabState, PanelTemplates_SelectTab, PanelTemplates_DeselectTab
+
+-- local upvalue storage used by BuildTabs
+local widths = {}
+local rowwidths = {}
+local rowends = {}
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function UpdateTabLook(frame)
+ if frame.disabled then
+ PanelTemplates_SetDisabledTabState(frame)
+ elseif frame.selected then
+ PanelTemplates_SelectTab(frame)
+ else
+ PanelTemplates_DeselectTab(frame)
+ end
+end
+
+local function Tab_SetText(frame, text)
+ frame:_SetText(text)
+ local width = frame.obj.frame.width or frame.obj.frame:GetWidth() or 0
+ PanelTemplates_TabResize(frame, 0, nil, nil, width, frame:GetFontString():GetStringWidth())
+end
+
+local function Tab_SetSelected(frame, selected)
+ frame.selected = selected
+ UpdateTabLook(frame)
+end
+
+local function Tab_SetDisabled(frame, disabled)
+ frame.disabled = disabled
+ UpdateTabLook(frame)
+end
+
+local function BuildTabsOnUpdate(frame)
+ local self = frame.obj
+ self:BuildTabs()
+ frame:SetScript("OnUpdate", nil)
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Tab_OnClick(frame)
+ if not (frame.selected or frame.disabled) then
+ PlaySound("igCharacterInfoTab")
+ frame.obj:SelectTab(frame.value)
+ end
+end
+
+local function Tab_OnEnter(frame)
+ local self = frame.obj
+ self:Fire("OnTabEnter", self.tabs[frame.id].value, frame)
+end
+
+local function Tab_OnLeave(frame)
+ local self = frame.obj
+ self:Fire("OnTabLeave", self.tabs[frame.id].value, frame)
+end
+
+local function Tab_OnShow(frame)
+ _G[frame:GetName().."HighlightTexture"]:SetWidth(frame:GetTextWidth() + 30)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetTitle()
+ end,
+
+ ["OnRelease"] = function(self)
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ self.tablist = nil
+ for _, tab in pairs(self.tabs) do
+ tab:Hide()
+ end
+ end,
+
+ ["CreateTab"] = function(self, id)
+ local tabname = ("AceGUITabGroup%dTab%d"):format(self.num, id)
+ local tab = CreateFrame("Button", tabname, self.border, "OptionsFrameTabButtonTemplate")
+ tab.obj = self
+ tab.id = id
+
+ tab.text = _G[tabname .. "Text"]
+ tab.text:ClearAllPoints()
+ tab.text:SetPoint("LEFT", 14, -3)
+ tab.text:SetPoint("RIGHT", -12, -3)
+
+ tab:SetScript("OnClick", Tab_OnClick)
+ tab:SetScript("OnEnter", Tab_OnEnter)
+ tab:SetScript("OnLeave", Tab_OnLeave)
+ tab:SetScript("OnShow", Tab_OnShow)
+
+ tab._SetText = tab.SetText
+ tab.SetText = Tab_SetText
+ tab.SetSelected = Tab_SetSelected
+ tab.SetDisabled = Tab_SetDisabled
+
+ return tab
+ end,
+
+ ["SetTitle"] = function(self, text)
+ self.titletext:SetText(text or "")
+ if text and text ~= "" then
+ self.alignoffset = 25
+ else
+ self.alignoffset = 18
+ end
+ self:BuildTabs()
+ end,
+
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ end,
+
+ ["SelectTab"] = function(self, value)
+ local status = self.status or self.localstatus
+ local found
+ for i, v in ipairs(self.tabs) do
+ if v.value == value then
+ v:SetSelected(true)
+ found = true
+ else
+ v:SetSelected(false)
+ end
+ end
+ status.selected = value
+ if found then
+ self:Fire("OnGroupSelected",value)
+ end
+ end,
+
+ ["SetTabs"] = function(self, tabs)
+ self.tablist = tabs
+ self:BuildTabs()
+ end,
+
+
+ ["BuildTabs"] = function(self)
+ local hastitle = (self.titletext:GetText() and self.titletext:GetText() ~= "")
+ local status = self.status or self.localstatus
+ local tablist = self.tablist
+ local tabs = self.tabs
+
+ if not tablist then return end
+
+ local width = self.frame.width or self.frame:GetWidth() or 0
+
+ wipe(widths)
+ wipe(rowwidths)
+ wipe(rowends)
+
+ --Place Text into tabs and get thier initial width
+ for i, v in ipairs(tablist) do
+ local tab = tabs[i]
+ if not tab then
+ tab = self:CreateTab(i)
+ tabs[i] = tab
+ end
+
+ tab:Show()
+ tab:SetText(v.text)
+ tab:SetDisabled(v.disabled)
+ tab.value = v.value
+
+ widths[i] = tab:GetWidth() - 6 --tabs are anchored 10 pixels from the right side of the previous one to reduce spacing, but add a fixed 4px padding for the text
+ end
+
+ for i = (#tablist)+1, #tabs, 1 do
+ tabs[i]:Hide()
+ end
+
+ --First pass, find the minimum number of rows needed to hold all tabs and the initial tab layout
+ local numtabs = #tablist
+ local numrows = 1
+ local usedwidth = 0
+
+ for i = 1, #tablist do
+ --If this is not the first tab of a row and there isn't room for it
+ if usedwidth ~= 0 and (width - usedwidth - widths[i]) < 0 then
+ rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
+ rowends[numrows] = i - 1
+ numrows = numrows + 1
+ usedwidth = 0
+ end
+ usedwidth = usedwidth + widths[i]
+ end
+ rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
+ rowends[numrows] = #tablist
+
+ --Fix for single tabs being left on the last row, move a tab from the row above if applicable
+ if numrows > 1 then
+ --if the last row has only one tab
+ if rowends[numrows-1] == numtabs-1 then
+ --if there are more than 2 tabs in the 2nd last row
+ if (numrows == 2 and rowends[numrows-1] > 2) or (rowends[numrows] - rowends[numrows-1] > 2) then
+ --move 1 tab from the second last row to the last, if there is enough space
+ if (rowwidths[numrows] + widths[numtabs-1]) <= width then
+ rowends[numrows-1] = rowends[numrows-1] - 1
+ rowwidths[numrows] = rowwidths[numrows] + widths[numtabs-1]
+ rowwidths[numrows-1] = rowwidths[numrows-1] - widths[numtabs-1]
+ end
+ end
+ end
+ end
+
+ --anchor the rows as defined and resize tabs to fill thier row
+ local starttab = 1
+ for row, endtab in ipairs(rowends) do
+ local first = true
+ for tabno = starttab, endtab do
+ local tab = tabs[tabno]
+ tab:ClearAllPoints()
+ if first then
+ tab:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 0, -(hastitle and 14 or 7)-(row-1)*20 )
+ first = false
+ else
+ tab:SetPoint("LEFT", tabs[tabno-1], "RIGHT", -10, 0)
+ end
+ end
+
+ -- equal padding for each tab to fill the available width,
+ -- if the used space is above 75% already
+ -- the 18 pixel is the typical width of a scrollbar, so we can have a tab group inside a scrolling frame,
+ -- and not have the tabs jump around funny when switching between tabs that need scrolling and those that don't
+ local padding = 0
+ if not (numrows == 1 and rowwidths[1] < width*0.75 - 18) then
+ padding = (width - rowwidths[row]) / (endtab - starttab+1)
+ end
+
+ for i = starttab, endtab do
+ PanelTemplates_TabResize(tabs[i], padding + 4, nil, nil, width, tabs[i]:GetFontString():GetStringWidth())
+ end
+ starttab = endtab + 1
+ end
+
+ self.borderoffset = (hastitle and 17 or 10)+((numrows)*20)
+ self.border:SetPoint("TOPLEFT", 1, -self.borderoffset)
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ local contentwidth = width - 60
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ self:BuildTabs(self)
+ self.frame:SetScript("OnUpdate", BuildTabsOnUpdate)
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - (self.borderoffset + 23)
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end,
+
+ ["LayoutFinished"] = function(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight((height or 0) + (self.borderoffset + 23))
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local PaneBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 5, bottom = 3 }
+}
+
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame",nil,UIParent)
+ frame:SetHeight(100)
+ frame:SetWidth(100)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
+ titletext:SetPoint("TOPLEFT", 14, 0)
+ titletext:SetPoint("TOPRIGHT", -14, 0)
+ titletext:SetJustifyH("LEFT")
+ titletext:SetHeight(18)
+ titletext:SetText("")
+
+ local border = CreateFrame("Frame", nil, frame)
+ border:SetPoint("TOPLEFT", 1, -27)
+ border:SetPoint("BOTTOMRIGHT", -1, 3)
+ border:SetBackdrop(PaneBackdrop)
+ border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
+ border:SetBackdropBorderColor(0.4, 0.4, 0.4)
+
+ local content = CreateFrame("Frame", nil, border)
+ content:SetPoint("TOPLEFT", 10, -7)
+ content:SetPoint("BOTTOMRIGHT", -10, 7)
+
+ local widget = {
+ num = num,
+ frame = frame,
+ localstatus = {},
+ alignoffset = 18,
+ titletext = titletext,
+ border = border,
+ borderoffset = 27,
+ tabs = {},
+ content = content,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,707 @@
+--[[-----------------------------------------------------------------------------
+TreeGroup Container
+Container that uses a tree control to switch between groups.
+-------------------------------------------------------------------------------]]
+local Type, Version = "TreeGroup", 37
+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 next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
+local math_min, math_max, floor = math.min, math.max, floor
+local select, tremove, unpack, tconcat = select, table.remove, unpack, table.concat
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameTooltip, FONT_COLOR_CODE_CLOSE
+
+-- Recycling functions
+local new, del
+do
+ local pool = setmetatable({},{__mode='k'})
+ function new()
+ local t = next(pool)
+ if t then
+ pool[t] = nil
+ return t
+ else
+ return {}
+ end
+ end
+ function del(t)
+ for k in pairs(t) do
+ t[k] = nil
+ end
+ pool[t] = true
+ end
+end
+
+local DEFAULT_TREE_WIDTH = 175
+local DEFAULT_TREE_SIZABLE = true
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function GetButtonUniqueValue(line)
+ local parent = line.parent
+ if parent and parent.value then
+ return GetButtonUniqueValue(parent).."\001"..line.value
+ else
+ return line.value
+ end
+end
+
+local function UpdateButton(button, treeline, selected, canExpand, isExpanded)
+ local self = button.obj
+ local toggle = button.toggle
+ local frame = self.frame
+ local text = treeline.text or ""
+ local icon = treeline.icon
+ local iconCoords = treeline.iconCoords
+ local level = treeline.level
+ local value = treeline.value
+ local uniquevalue = treeline.uniquevalue
+ local disabled = treeline.disabled
+
+ button.treeline = treeline
+ button.value = value
+ button.uniquevalue = uniquevalue
+ if selected then
+ button:LockHighlight()
+ button.selected = true
+ else
+ button:UnlockHighlight()
+ button.selected = false
+ end
+ local normalTexture = button:GetNormalTexture()
+ local line = button.line
+ button.level = level
+ if ( level == 1 ) then
+ button:SetNormalFontObject("GameFontNormal")
+ button:SetHighlightFontObject("GameFontHighlight")
+ button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2)
+ else
+ button:SetNormalFontObject("GameFontHighlightSmall")
+ button:SetHighlightFontObject("GameFontHighlightSmall")
+ button.text:SetPoint("LEFT", (icon and 16 or 0) + 8 * level, 2)
+ end
+
+ if disabled then
+ button:EnableMouse(false)
+ button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE)
+ else
+ button.text:SetText(text)
+ button:EnableMouse(true)
+ end
+
+ if icon then
+ button.icon:SetTexture(icon)
+ button.icon:SetPoint("LEFT", 8 * level, (level == 1) and 0 or 1)
+ else
+ button.icon:SetTexture(nil)
+ end
+
+ if iconCoords then
+ button.icon:SetTexCoord(unpack(iconCoords))
+ else
+ button.icon:SetTexCoord(0, 1, 0, 1)
+ end
+
+ if canExpand then
+ if not isExpanded then
+ toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
+ toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
+ else
+ toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
+ toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
+ end
+ toggle:Show()
+ else
+ toggle:Hide()
+ end
+end
+
+local function ShouldDisplayLevel(tree)
+ local result = false
+ for k, v in ipairs(tree) do
+ if v.children == nil and v.visible ~= false then
+ result = true
+ elseif v.children then
+ result = result or ShouldDisplayLevel(v.children)
+ end
+ if result then return result end
+ end
+ return false
+end
+
+local function addLine(self, v, tree, level, parent)
+ local line = new()
+ line.value = v.value
+ line.text = v.text
+ line.icon = v.icon
+ line.iconCoords = v.iconCoords
+ line.disabled = v.disabled
+ line.tree = tree
+ line.level = level
+ line.parent = parent
+ line.visible = v.visible
+ line.uniquevalue = GetButtonUniqueValue(line)
+ if v.children then
+ line.hasChildren = true
+ else
+ line.hasChildren = nil
+ end
+ self.lines[#self.lines+1] = line
+ return line
+end
+
+--fire an update after one frame to catch the treeframes height
+local function FirstFrameUpdate(frame)
+ local self = frame.obj
+ frame:SetScript("OnUpdate", nil)
+ self:RefreshTree()
+end
+
+local function BuildUniqueValue(...)
+ local n = select('#', ...)
+ if n == 1 then
+ return ...
+ else
+ return (...).."\001"..BuildUniqueValue(select(2,...))
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Expand_OnClick(frame)
+ local button = frame.button
+ local self = button.obj
+ local status = (self.status or self.localstatus).groups
+ status[button.uniquevalue] = not status[button.uniquevalue]
+ self:RefreshTree()
+end
+
+local function Button_OnClick(frame)
+ local self = frame.obj
+ self:Fire("OnClick", frame.uniquevalue, frame.selected)
+ if not frame.selected then
+ self:SetSelected(frame.uniquevalue)
+ frame.selected = true
+ frame:LockHighlight()
+ self:RefreshTree()
+ end
+ AceGUI:ClearFocus()
+end
+
+local function Button_OnDoubleClick(button)
+ local self = button.obj
+ local status = self.status or self.localstatus
+ local status = (self.status or self.localstatus).groups
+ status[button.uniquevalue] = not status[button.uniquevalue]
+ self:RefreshTree()
+end
+
+local function Button_OnEnter(frame)
+ local self = frame.obj
+ self:Fire("OnButtonEnter", frame.uniquevalue, frame)
+
+ if self.enabletooltips then
+ GameTooltip:SetOwner(frame, "ANCHOR_NONE")
+ GameTooltip:SetPoint("LEFT",frame,"RIGHT")
+ GameTooltip:SetText(frame.text:GetText() or "", 1, .82, 0, true)
+
+ GameTooltip:Show()
+ end
+end
+
+local function Button_OnLeave(frame)
+ local self = frame.obj
+ self:Fire("OnButtonLeave", frame.uniquevalue, frame)
+
+ if self.enabletooltips then
+ GameTooltip:Hide()
+ end
+end
+
+local function OnScrollValueChanged(frame, value)
+ if frame.obj.noupdate then return end
+ local self = frame.obj
+ local status = self.status or self.localstatus
+ status.scrollvalue = floor(value + 0.5)
+ self:RefreshTree()
+ AceGUI:ClearFocus()
+end
+
+local function Tree_OnSizeChanged(frame)
+ frame.obj:RefreshTree()
+end
+
+local function Tree_OnMouseWheel(frame, delta)
+ local self = frame.obj
+ if self.showscroll then
+ local scrollbar = self.scrollbar
+ local min, max = scrollbar:GetMinMaxValues()
+ local value = scrollbar:GetValue()
+ local newvalue = math_min(max,math_max(min,value - delta))
+ if value ~= newvalue then
+ scrollbar:SetValue(newvalue)
+ end
+ end
+end
+
+local function Dragger_OnLeave(frame)
+ frame:SetBackdropColor(1, 1, 1, 0)
+end
+
+local function Dragger_OnEnter(frame)
+ frame:SetBackdropColor(1, 1, 1, 0.8)
+end
+
+local function Dragger_OnMouseDown(frame)
+ local treeframe = frame:GetParent()
+ treeframe:StartSizing("RIGHT")
+end
+
+local function Dragger_OnMouseUp(frame)
+ local treeframe = frame:GetParent()
+ local self = treeframe.obj
+ local frame = treeframe:GetParent()
+ treeframe:StopMovingOrSizing()
+ --treeframe:SetScript("OnUpdate", nil)
+ treeframe:SetUserPlaced(false)
+ --Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
+ treeframe:SetHeight(0)
+ treeframe:SetPoint("TOPLEFT", frame, "TOPLEFT",0,0)
+ treeframe:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT",0,0)
+
+ local status = self.status or self.localstatus
+ status.treewidth = treeframe:GetWidth()
+
+ treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth())
+ -- recalculate the content width
+ treeframe.obj:OnWidthSet(status.fullwidth)
+ -- update the layout of the content
+ treeframe.obj:DoLayout()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetTreeWidth(DEFAULT_TREE_WIDTH, DEFAULT_TREE_SIZABLE)
+ self:EnableButtonTooltips(true)
+ end,
+
+ ["OnRelease"] = function(self)
+ self.status = nil
+ for k, v in pairs(self.localstatus) do
+ if k == "groups" then
+ for k2 in pairs(v) do
+ v[k2] = nil
+ end
+ else
+ self.localstatus[k] = nil
+ end
+ end
+ self.localstatus.scrollvalue = 0
+ self.localstatus.treewidth = DEFAULT_TREE_WIDTH
+ self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
+ end,
+
+ ["EnableButtonTooltips"] = function(self, enable)
+ self.enabletooltips = enable
+ end,
+
+ ["CreateButton"] = function(self)
+ local num = AceGUI:GetNextWidgetNum("TreeGroupButton")
+ local button = CreateFrame("Button", ("AceGUI30TreeButton%d"):format(num), self.treeframe, "OptionsListButtonTemplate")
+ button.obj = self
+
+ local icon = button:CreateTexture(nil, "OVERLAY")
+ icon:SetWidth(14)
+ icon:SetHeight(14)
+ button.icon = icon
+
+ button:SetScript("OnClick",Button_OnClick)
+ button:SetScript("OnDoubleClick", Button_OnDoubleClick)
+ button:SetScript("OnEnter",Button_OnEnter)
+ button:SetScript("OnLeave",Button_OnLeave)
+
+ button.toggle.button = button
+ button.toggle:SetScript("OnClick",Expand_OnClick)
+
+ return button
+ end,
+
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ if not status.groups then
+ status.groups = {}
+ end
+ if not status.scrollvalue then
+ status.scrollvalue = 0
+ end
+ if not status.treewidth then
+ status.treewidth = DEFAULT_TREE_WIDTH
+ end
+ if status.treesizable == nil then
+ status.treesizable = DEFAULT_TREE_SIZABLE
+ end
+ self:SetTreeWidth(status.treewidth,status.treesizable)
+ self:RefreshTree()
+ end,
+
+ --sets the tree to be displayed
+ ["SetTree"] = function(self, tree, filter)
+ self.filter = filter
+ if tree then
+ assert(type(tree) == "table")
+ end
+ self.tree = tree
+ self:RefreshTree()
+ end,
+
+ ["BuildLevel"] = function(self, tree, level, parent)
+ local groups = (self.status or self.localstatus).groups
+ local hasChildren = self.hasChildren
+
+ for i, v in ipairs(tree) do
+ if v.children then
+ if not self.filter or ShouldDisplayLevel(v.children) then
+ local line = addLine(self, v, tree, level, parent)
+ if groups[line.uniquevalue] then
+ self:BuildLevel(v.children, level+1, line)
+ end
+ end
+ elseif v.visible ~= false or not self.filter then
+ addLine(self, v, tree, level, parent)
+ end
+ end
+ end,
+
+ ["RefreshTree"] = function(self,scrollToSelection)
+ local buttons = self.buttons
+ local lines = self.lines
+
+ for i, v in ipairs(buttons) do
+ v:Hide()
+ end
+ while lines[1] do
+ local t = tremove(lines)
+ for k in pairs(t) do
+ t[k] = nil
+ end
+ del(t)
+ end
+
+ if not self.tree then return end
+ --Build the list of visible entries from the tree and status tables
+ local status = self.status or self.localstatus
+ local groupstatus = status.groups
+ local tree = self.tree
+
+ local treeframe = self.treeframe
+
+ status.scrollToSelection = status.scrollToSelection or scrollToSelection -- needs to be cached in case the control hasn't been drawn yet (code bails out below)
+
+ self:BuildLevel(tree, 1)
+
+ local numlines = #lines
+
+ local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
+ if maxlines <= 0 then return end
+
+ local first, last
+
+ scrollToSelection = status.scrollToSelection
+ status.scrollToSelection = nil
+
+ if numlines <= maxlines then
+ --the whole tree fits in the frame
+ status.scrollvalue = 0
+ self:ShowScroll(false)
+ first, last = 1, numlines
+ else
+ self:ShowScroll(true)
+ --scrolling will be needed
+ self.noupdate = true
+ self.scrollbar:SetMinMaxValues(0, numlines - maxlines)
+ --check if we are scrolled down too far
+ if numlines - status.scrollvalue < maxlines then
+ status.scrollvalue = numlines - maxlines
+ end
+ self.noupdate = nil
+ first, last = status.scrollvalue+1, status.scrollvalue + maxlines
+ --show selection?
+ if scrollToSelection and status.selected then
+ local show
+ for i,line in ipairs(lines) do -- find the line number
+ if line.uniquevalue==status.selected then
+ show=i
+ end
+ end
+ if not show then
+ -- selection was deleted or something?
+ elseif show>=first and show<=last then
+ -- all good
+ else
+ -- scrolling needed!
+ if show 100 and status.treewidth > maxtreewidth then
+ self:SetTreeWidth(maxtreewidth, status.treesizable)
+ end
+ treeframe:SetMaxResize(maxtreewidth, 1600)
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - 20
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end,
+
+ ["SetTreeWidth"] = function(self, treewidth, resizable)
+ if not resizable then
+ if type(treewidth) == 'number' then
+ resizable = false
+ elseif type(treewidth) == 'boolean' then
+ resizable = treewidth
+ treewidth = DEFAULT_TREE_WIDTH
+ else
+ resizable = false
+ treewidth = DEFAULT_TREE_WIDTH
+ end
+ end
+ self.treeframe:SetWidth(treewidth)
+ self.dragger:EnableMouse(resizable)
+
+ local status = self.status or self.localstatus
+ status.treewidth = treewidth
+ status.treesizable = resizable
+
+ -- recalculate the content width
+ if status.fullwidth then
+ self:OnWidthSet(status.fullwidth)
+ end
+ end,
+
+ ["GetTreeWidth"] = function(self)
+ local status = self.status or self.localstatus
+ return status.treewidth or DEFAULT_TREE_WIDTH
+ end,
+
+ ["LayoutFinished"] = function(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight((height or 0) + 20)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local PaneBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 5, bottom = 3 }
+}
+
+local DraggerBackdrop = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = nil,
+ tile = true, tileSize = 16, edgeSize = 0,
+ insets = { left = 3, right = 3, top = 7, bottom = 7 }
+}
+
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame", nil, UIParent)
+
+ local treeframe = CreateFrame("Frame", nil, frame)
+ treeframe:SetPoint("TOPLEFT")
+ treeframe:SetPoint("BOTTOMLEFT")
+ treeframe:SetWidth(DEFAULT_TREE_WIDTH)
+ treeframe:EnableMouseWheel(true)
+ treeframe:SetBackdrop(PaneBackdrop)
+ treeframe:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
+ treeframe:SetBackdropBorderColor(0.4, 0.4, 0.4)
+ treeframe:SetResizable(true)
+ treeframe:SetMinResize(100, 1)
+ treeframe:SetMaxResize(400, 1600)
+ treeframe:SetScript("OnUpdate", FirstFrameUpdate)
+ treeframe:SetScript("OnSizeChanged", Tree_OnSizeChanged)
+ treeframe:SetScript("OnMouseWheel", Tree_OnMouseWheel)
+
+ local dragger = CreateFrame("Frame", nil, treeframe)
+ dragger:SetWidth(8)
+ dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
+ dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
+ dragger:SetBackdrop(DraggerBackdrop)
+ dragger:SetBackdropColor(1, 1, 1, 0)
+ dragger:SetScript("OnEnter", Dragger_OnEnter)
+ dragger:SetScript("OnLeave", Dragger_OnLeave)
+ dragger:SetScript("OnMouseDown", Dragger_OnMouseDown)
+ dragger:SetScript("OnMouseUp", Dragger_OnMouseUp)
+
+ local scrollbar = CreateFrame("Slider", ("AceConfigDialogTreeGroup%dScrollBar"):format(num), treeframe, "UIPanelScrollBarTemplate")
+ scrollbar:SetScript("OnValueChanged", nil)
+ scrollbar:SetPoint("TOPRIGHT", -10, -26)
+ scrollbar:SetPoint("BOTTOMRIGHT", -10, 26)
+ scrollbar:SetMinMaxValues(0,0)
+ scrollbar:SetValueStep(1)
+ scrollbar:SetValue(0)
+ scrollbar:SetWidth(16)
+ scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
+
+ local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
+ scrollbg:SetAllPoints(scrollbar)
+ scrollbg:SetTexture(0,0,0,0.4)
+
+ local border = CreateFrame("Frame",nil,frame)
+ border:SetPoint("TOPLEFT", treeframe, "TOPRIGHT")
+ border:SetPoint("BOTTOMRIGHT")
+ border:SetBackdrop(PaneBackdrop)
+ border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
+ border:SetBackdropBorderColor(0.4, 0.4, 0.4)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, border)
+ content:SetPoint("TOPLEFT", 10, -10)
+ content:SetPoint("BOTTOMRIGHT", -10, 10)
+
+ local widget = {
+ frame = frame,
+ lines = {},
+ levels = {},
+ buttons = {},
+ hasChildren = {},
+ localstatus = { groups = {}, scrollvalue = 0 },
+ filter = false,
+ treeframe = treeframe,
+ dragger = dragger,
+ scrollbar = scrollbar,
+ border = border,
+ content = content,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ treeframe.obj, dragger.obj, scrollbar.obj = widget, widget, widget
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,331 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local pairs, assert, type = pairs, assert, type
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontNormal
+
+----------------
+-- Main Frame --
+----------------
+--[[
+ Events :
+ OnClose
+
+]]
+do
+ local Type = "Window"
+ local Version = 4
+
+ local function frameOnClose(this)
+ this.obj:Fire("OnClose")
+ end
+
+ local function closeOnClick(this)
+ PlaySound("gsTitleOptionExit")
+ this.obj:Hide()
+ end
+
+ local function frameOnMouseDown(this)
+ AceGUI:ClearFocus()
+ end
+
+ local function titleOnMouseDown(this)
+ this:GetParent():StartMoving()
+ AceGUI:ClearFocus()
+ end
+
+ local function frameOnMouseUp(this)
+ local frame = this:GetParent()
+ frame:StopMovingOrSizing()
+ local self = frame.obj
+ local status = self.status or self.localstatus
+ status.width = frame:GetWidth()
+ status.height = frame:GetHeight()
+ status.top = frame:GetTop()
+ status.left = frame:GetLeft()
+ end
+
+ local function sizerseOnMouseDown(this)
+ this:GetParent():StartSizing("BOTTOMRIGHT")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizersOnMouseDown(this)
+ this:GetParent():StartSizing("BOTTOM")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizereOnMouseDown(this)
+ this:GetParent():StartSizing("RIGHT")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizerOnMouseUp(this)
+ this:GetParent():StopMovingOrSizing()
+ end
+
+ local function SetTitle(self,title)
+ self.titletext:SetText(title)
+ end
+
+ local function SetStatusText(self,text)
+ -- self.statustext:SetText(text)
+ end
+
+ local function Hide(self)
+ self.frame:Hide()
+ end
+
+ local function Show(self)
+ self.frame:Show()
+ end
+
+ local function OnAcquire(self)
+ self.frame:SetParent(UIParent)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ self:ApplyStatus()
+ self:EnableResize(true)
+ self:Show()
+ end
+
+ local function OnRelease(self)
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ end
+
+ -- called to set an external table to store status in
+ local function SetStatusTable(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ self:ApplyStatus()
+ end
+
+ local function ApplyStatus(self)
+ local status = self.status or self.localstatus
+ local frame = self.frame
+ self:SetWidth(status.width or 700)
+ self:SetHeight(status.height or 500)
+ if status.top and status.left then
+ frame:SetPoint("TOP",UIParent,"BOTTOM",0,status.top)
+ frame:SetPoint("LEFT",UIParent,"LEFT",status.left,0)
+ else
+ frame:SetPoint("CENTER",UIParent,"CENTER")
+ end
+ end
+
+ local function OnWidthSet(self, width)
+ local content = self.content
+ local contentwidth = width - 34
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ end
+
+
+ local function OnHeightSet(self, height)
+ local content = self.content
+ local contentheight = height - 57
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end
+
+ local function EnableResize(self, state)
+ local func = state and "Show" or "Hide"
+ self.sizer_se[func](self.sizer_se)
+ self.sizer_s[func](self.sizer_s)
+ self.sizer_e[func](self.sizer_e)
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local self = {}
+ self.type = "Window"
+
+ self.Hide = Hide
+ self.Show = Show
+ self.SetTitle = SetTitle
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ self.SetStatusText = SetStatusText
+ self.SetStatusTable = SetStatusTable
+ self.ApplyStatus = ApplyStatus
+ self.OnWidthSet = OnWidthSet
+ self.OnHeightSet = OnHeightSet
+ self.EnableResize = EnableResize
+
+ self.localstatus = {}
+
+ self.frame = frame
+ frame.obj = self
+ frame:SetWidth(700)
+ frame:SetHeight(500)
+ frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
+ frame:EnableMouse()
+ frame:SetMovable(true)
+ frame:SetResizable(true)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ frame:SetScript("OnMouseDown", frameOnMouseDown)
+
+ frame:SetScript("OnHide",frameOnClose)
+ frame:SetMinResize(240,240)
+ frame:SetToplevel(true)
+
+ local titlebg = frame:CreateTexture(nil, "BACKGROUND")
+ titlebg:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Title-Background]])
+ titlebg:SetPoint("TOPLEFT", 9, -6)
+ titlebg:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -28, -24)
+
+ local dialogbg = frame:CreateTexture(nil, "BACKGROUND")
+ dialogbg:SetTexture([[Interface\Tooltips\UI-Tooltip-Background]])
+ dialogbg:SetPoint("TOPLEFT", 8, -24)
+ dialogbg:SetPoint("BOTTOMRIGHT", -6, 8)
+ dialogbg:SetVertexColor(0, 0, 0, .75)
+
+ local topleft = frame:CreateTexture(nil, "BORDER")
+ topleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ topleft:SetWidth(64)
+ topleft:SetHeight(64)
+ topleft:SetPoint("TOPLEFT")
+ topleft:SetTexCoord(0.501953125, 0.625, 0, 1)
+
+ local topright = frame:CreateTexture(nil, "BORDER")
+ topright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ topright:SetWidth(64)
+ topright:SetHeight(64)
+ topright:SetPoint("TOPRIGHT")
+ topright:SetTexCoord(0.625, 0.75, 0, 1)
+
+ local top = frame:CreateTexture(nil, "BORDER")
+ top:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ top:SetHeight(64)
+ top:SetPoint("TOPLEFT", topleft, "TOPRIGHT")
+ top:SetPoint("TOPRIGHT", topright, "TOPLEFT")
+ top:SetTexCoord(0.25, 0.369140625, 0, 1)
+
+ local bottomleft = frame:CreateTexture(nil, "BORDER")
+ bottomleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ bottomleft:SetWidth(64)
+ bottomleft:SetHeight(64)
+ bottomleft:SetPoint("BOTTOMLEFT")
+ bottomleft:SetTexCoord(0.751953125, 0.875, 0, 1)
+
+ local bottomright = frame:CreateTexture(nil, "BORDER")
+ bottomright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ bottomright:SetWidth(64)
+ bottomright:SetHeight(64)
+ bottomright:SetPoint("BOTTOMRIGHT")
+ bottomright:SetTexCoord(0.875, 1, 0, 1)
+
+ local bottom = frame:CreateTexture(nil, "BORDER")
+ bottom:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ bottom:SetHeight(64)
+ bottom:SetPoint("BOTTOMLEFT", bottomleft, "BOTTOMRIGHT")
+ bottom:SetPoint("BOTTOMRIGHT", bottomright, "BOTTOMLEFT")
+ bottom:SetTexCoord(0.376953125, 0.498046875, 0, 1)
+
+ local left = frame:CreateTexture(nil, "BORDER")
+ left:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ left:SetWidth(64)
+ left:SetPoint("TOPLEFT", topleft, "BOTTOMLEFT")
+ left:SetPoint("BOTTOMLEFT", bottomleft, "TOPLEFT")
+ left:SetTexCoord(0.001953125, 0.125, 0, 1)
+
+ local right = frame:CreateTexture(nil, "BORDER")
+ right:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ right:SetWidth(64)
+ right:SetPoint("TOPRIGHT", topright, "BOTTOMRIGHT")
+ right:SetPoint("BOTTOMRIGHT", bottomright, "TOPRIGHT")
+ right:SetTexCoord(0.1171875, 0.2421875, 0, 1)
+
+ local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
+ close:SetPoint("TOPRIGHT", 2, 1)
+ close:SetScript("OnClick", closeOnClick)
+ self.closebutton = close
+ close.obj = self
+
+ local titletext = frame:CreateFontString(nil, "ARTWORK")
+ titletext:SetFontObject(GameFontNormal)
+ titletext:SetPoint("TOPLEFT", 12, -8)
+ titletext:SetPoint("TOPRIGHT", -32, -8)
+ self.titletext = titletext
+
+ local title = CreateFrame("Button", nil, frame)
+ title:SetPoint("TOPLEFT", titlebg)
+ title:SetPoint("BOTTOMRIGHT", titlebg)
+ title:EnableMouse()
+ title:SetScript("OnMouseDown",titleOnMouseDown)
+ title:SetScript("OnMouseUp", frameOnMouseUp)
+ self.title = title
+
+ local sizer_se = CreateFrame("Frame",nil,frame)
+ sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
+ sizer_se:SetWidth(25)
+ sizer_se:SetHeight(25)
+ sizer_se:EnableMouse()
+ sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown)
+ sizer_se:SetScript("OnMouseUp", sizerOnMouseUp)
+ self.sizer_se = sizer_se
+
+ local line1 = sizer_se:CreateTexture(nil, "BACKGROUND")
+ self.line1 = line1
+ line1:SetWidth(14)
+ line1:SetHeight(14)
+ line1:SetPoint("BOTTOMRIGHT", -8, 8)
+ line1:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
+ local x = 0.1 * 14/17
+ line1:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
+
+ local line2 = sizer_se:CreateTexture(nil, "BACKGROUND")
+ self.line2 = line2
+ line2:SetWidth(8)
+ line2:SetHeight(8)
+ line2:SetPoint("BOTTOMRIGHT", -8, 8)
+ line2:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
+ local x = 0.1 * 8/17
+ line2:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
+
+ local sizer_s = CreateFrame("Frame",nil,frame)
+ sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0)
+ sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
+ sizer_s:SetHeight(25)
+ sizer_s:EnableMouse()
+ sizer_s:SetScript("OnMouseDown",sizersOnMouseDown)
+ sizer_s:SetScript("OnMouseUp", sizerOnMouseUp)
+ self.sizer_s = sizer_s
+
+ local sizer_e = CreateFrame("Frame",nil,frame)
+ sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25)
+ sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
+ sizer_e:SetWidth(25)
+ sizer_e:EnableMouse()
+ sizer_e:SetScript("OnMouseDown",sizereOnMouseDown)
+ sizer_e:SetScript("OnMouseUp", sizerOnMouseUp)
+ self.sizer_e = sizer_e
+
+ --Container Support
+ local content = CreateFrame("Frame",nil,frame)
+ self.content = content
+ content.obj = self
+ content:SetPoint("TOPLEFT",frame,"TOPLEFT",12,-32)
+ content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-12,13)
+
+ AceGUI:RegisterAsContainer(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(Type,Constructor,Version)
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,109 @@
+--[[-----------------------------------------------------------------------------
+Button Widget
+Graphical Button.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Button", 23
+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 pairs = pairs
+
+-- WoW APIs
+local _G = _G
+local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
+
+local wowMoP
+do
+ local _, _, _, interface = GetBuildInfo()
+ wowMoP = (interface >= 50000)
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Button_OnClick(frame, ...)
+ AceGUI:ClearFocus()
+ PlaySound("igMainMenuOption")
+ frame.obj:Fire("OnClick", ...)
+end
+
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- restore default values
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetDisabled(false)
+ self:SetAutoWidth(false)
+ self:SetText()
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetText"] = function(self, text)
+ self.text:SetText(text)
+ if self.autoWidth then
+ self:SetWidth(self.text:GetStringWidth() + 30)
+ end
+ end,
+
+ ["SetAutoWidth"] = function(self, autoWidth)
+ self.autoWidth = autoWidth
+ if self.autoWidth then
+ self:SetWidth(self.text:GetStringWidth() + 30)
+ end
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ else
+ self.frame:Enable()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local name = "AceGUI30Button" .. AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Button", name, UIParent, wowMoP and "UIPanelButtonTemplate" or "UIPanelButtonTemplate2")
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnClick", Button_OnClick)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+
+ local text = frame:GetFontString()
+ text:ClearAllPoints()
+ text:SetPoint("TOPLEFT", 15, -1)
+ text:SetPoint("BOTTOMRIGHT", -15, 1)
+ text:SetJustifyV("MIDDLE")
+
+ local widget = {
+ text = text,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,295 @@
+--[[-----------------------------------------------------------------------------
+Checkbox Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "CheckBox", 22
+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 select, pairs = select, pairs
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: SetDesaturation, GameFontHighlight
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function AlignImage(self)
+ local img = self.image:GetTexture()
+ self.text:ClearAllPoints()
+ if not img then
+ self.text:SetPoint("LEFT", self.checkbg, "RIGHT")
+ self.text:SetPoint("RIGHT")
+ else
+ self.text:SetPoint("LEFT", self.image,"RIGHT", 1, 0)
+ self.text:SetPoint("RIGHT")
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function CheckBox_OnMouseDown(frame)
+ local self = frame.obj
+ if not self.disabled then
+ if self.image:GetTexture() then
+ self.text:SetPoint("LEFT", self.image,"RIGHT", 2, -1)
+ else
+ self.text:SetPoint("LEFT", self.checkbg, "RIGHT", 1, -1)
+ end
+ end
+ AceGUI:ClearFocus()
+end
+
+local function CheckBox_OnMouseUp(frame)
+ local self = frame.obj
+ if not self.disabled then
+ self:ToggleChecked()
+
+ if self.checked then
+ PlaySound("igMainMenuOptionCheckBoxOn")
+ else -- for both nil and false (tristate)
+ PlaySound("igMainMenuOptionCheckBoxOff")
+ end
+
+ self:Fire("OnValueChanged", self.checked)
+ AlignImage(self)
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetType()
+ self:SetValue(false)
+ self:SetTriState(nil)
+ -- height is calculated from the width and required space for the description
+ self:SetWidth(200)
+ self:SetImage()
+ self:SetDisabled(nil)
+ self:SetDescription(nil)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["OnWidthSet"] = function(self, width)
+ if self.desc then
+ self.desc:SetWidth(width - 30)
+ if self.desc:GetText() and self.desc:GetText() ~= "" then
+ self:SetHeight(28 + self.desc:GetHeight())
+ end
+ end
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ self.text:SetTextColor(0.5, 0.5, 0.5)
+ SetDesaturation(self.check, true)
+ if self.desc then
+ self.desc:SetTextColor(0.5, 0.5, 0.5)
+ end
+ else
+ self.frame:Enable()
+ self.text:SetTextColor(1, 1, 1)
+ if self.tristate and self.checked == nil then
+ SetDesaturation(self.check, true)
+ else
+ SetDesaturation(self.check, false)
+ end
+ if self.desc then
+ self.desc:SetTextColor(1, 1, 1)
+ end
+ end
+ end,
+
+ ["SetValue"] = function(self,value)
+ local check = self.check
+ self.checked = value
+ if value then
+ SetDesaturation(self.check, false)
+ self.check:Show()
+ else
+ --Nil is the unknown tristate value
+ if self.tristate and value == nil then
+ SetDesaturation(self.check, true)
+ self.check:Show()
+ else
+ SetDesaturation(self.check, false)
+ self.check:Hide()
+ end
+ end
+ self:SetDisabled(self.disabled)
+ end,
+
+ ["GetValue"] = function(self)
+ return self.checked
+ end,
+
+ ["SetTriState"] = function(self, enabled)
+ self.tristate = enabled
+ self:SetValue(self:GetValue())
+ end,
+
+ ["SetType"] = function(self, type)
+ local checkbg = self.checkbg
+ local check = self.check
+ local highlight = self.highlight
+
+ local size
+ if type == "radio" then
+ size = 16
+ checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton")
+ checkbg:SetTexCoord(0, 0.25, 0, 1)
+ check:SetTexture("Interface\\Buttons\\UI-RadioButton")
+ check:SetTexCoord(0.25, 0.5, 0, 1)
+ check:SetBlendMode("ADD")
+ highlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
+ highlight:SetTexCoord(0.5, 0.75, 0, 1)
+ else
+ size = 24
+ checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
+ checkbg:SetTexCoord(0, 1, 0, 1)
+ check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+ check:SetTexCoord(0, 1, 0, 1)
+ check:SetBlendMode("BLEND")
+ highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
+ highlight:SetTexCoord(0, 1, 0, 1)
+ end
+ checkbg:SetHeight(size)
+ checkbg:SetWidth(size)
+ end,
+
+ ["ToggleChecked"] = function(self)
+ local value = self:GetValue()
+ if self.tristate then
+ --cycle in true, nil, false order
+ if value then
+ self:SetValue(nil)
+ elseif value == nil then
+ self:SetValue(false)
+ else
+ self:SetValue(true)
+ end
+ else
+ self:SetValue(not self:GetValue())
+ end
+ end,
+
+ ["SetLabel"] = function(self, label)
+ self.text:SetText(label)
+ end,
+
+ ["SetDescription"] = function(self, desc)
+ if desc then
+ if not self.desc then
+ local desc = self.frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ desc:ClearAllPoints()
+ desc:SetPoint("TOPLEFT", self.checkbg, "TOPRIGHT", 5, -21)
+ desc:SetWidth(self.frame.width - 30)
+ desc:SetJustifyH("LEFT")
+ desc:SetJustifyV("TOP")
+ self.desc = desc
+ end
+ self.desc:Show()
+ --self.text:SetFontObject(GameFontNormal)
+ self.desc:SetText(desc)
+ self:SetHeight(28 + self.desc:GetHeight())
+ else
+ if self.desc then
+ self.desc:SetText("")
+ self.desc:Hide()
+ end
+ --self.text:SetFontObject(GameFontHighlight)
+ self:SetHeight(24)
+ end
+ end,
+
+ ["SetImage"] = function(self, path, ...)
+ local image = self.image
+ image:SetTexture(path)
+
+ if image:GetTexture() then
+ local n = select("#", ...)
+ if n == 4 or n == 8 then
+ image:SetTexCoord(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ end
+ AlignImage(self)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Button", nil, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnMouseDown", CheckBox_OnMouseDown)
+ frame:SetScript("OnMouseUp", CheckBox_OnMouseUp)
+
+ local checkbg = frame:CreateTexture(nil, "ARTWORK")
+ checkbg:SetWidth(24)
+ checkbg:SetHeight(24)
+ checkbg:SetPoint("TOPLEFT")
+ checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
+
+ local check = frame:CreateTexture(nil, "OVERLAY")
+ check:SetAllPoints(checkbg)
+ check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+
+ local text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
+ text:SetJustifyH("LEFT")
+ text:SetHeight(18)
+ text:SetPoint("LEFT", checkbg, "RIGHT")
+ text:SetPoint("RIGHT")
+
+ local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
+ highlight:SetBlendMode("ADD")
+ highlight:SetAllPoints(checkbg)
+
+ local image = frame:CreateTexture(nil, "OVERLAY")
+ image:SetHeight(16)
+ image:SetWidth(16)
+ image:SetPoint("LEFT", checkbg, "RIGHT", 1, 0)
+
+ local widget = {
+ checkbg = checkbg,
+ check = check,
+ text = text,
+ highlight = highlight,
+ image = image,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,188 @@
+--[[-----------------------------------------------------------------------------
+ColorPicker Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "ColorPicker", 22
+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 pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: ShowUIPanel, HideUIPanel, ColorPickerFrame, OpacitySliderFrame
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function ColorCallback(self, r, g, b, a, isAlpha)
+ if not self.HasAlpha then
+ a = 1
+ end
+ self:SetColor(r, g, b, a)
+ if ColorPickerFrame:IsVisible() then
+ --colorpicker is still open
+ self:Fire("OnValueChanged", r, g, b, a)
+ else
+ --colorpicker is closed, color callback is first, ignore it,
+ --alpha callback is the final call after it closes so confirm now
+ if isAlpha then
+ self:Fire("OnValueConfirmed", r, g, b, a)
+ end
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function ColorSwatch_OnClick(frame)
+ HideUIPanel(ColorPickerFrame)
+ local self = frame.obj
+ if not self.disabled then
+ ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+ ColorPickerFrame:SetFrameLevel(frame:GetFrameLevel() + 10)
+ ColorPickerFrame:SetClampedToScreen(true)
+
+ ColorPickerFrame.func = function()
+ local r, g, b = ColorPickerFrame:GetColorRGB()
+ local a = 1 - OpacitySliderFrame:GetValue()
+ ColorCallback(self, r, g, b, a)
+ end
+
+ ColorPickerFrame.hasOpacity = self.HasAlpha
+ ColorPickerFrame.opacityFunc = function()
+ local r, g, b = ColorPickerFrame:GetColorRGB()
+ local a = 1 - OpacitySliderFrame:GetValue()
+ ColorCallback(self, r, g, b, a, true)
+ end
+
+ local r, g, b, a = self.r, self.g, self.b, self.a
+ if self.HasAlpha then
+ ColorPickerFrame.opacity = 1 - (a or 0)
+ end
+ ColorPickerFrame:SetColorRGB(r, g, b)
+
+ ColorPickerFrame.cancelFunc = function()
+ ColorCallback(self, r, g, b, a, true)
+ end
+
+ ShowUIPanel(ColorPickerFrame)
+ end
+ AceGUI:ClearFocus()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetHasAlpha(false)
+ self:SetColor(0, 0, 0, 1)
+ self:SetDisabled(nil)
+ self:SetLabel(nil)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetLabel"] = function(self, text)
+ self.text:SetText(text)
+ end,
+
+ ["SetColor"] = function(self, r, g, b, a)
+ self.r = r
+ self.g = g
+ self.b = b
+ self.a = a or 1
+ self.colorSwatch:SetVertexColor(r, g, b, a)
+ end,
+
+ ["SetHasAlpha"] = function(self, HasAlpha)
+ self.HasAlpha = HasAlpha
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if self.disabled then
+ self.frame:Disable()
+ self.text:SetTextColor(0.5, 0.5, 0.5)
+ else
+ self.frame:Enable()
+ self.text:SetTextColor(1, 1, 1)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Button", nil, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnClick", ColorSwatch_OnClick)
+
+ local colorSwatch = frame:CreateTexture(nil, "OVERLAY")
+ colorSwatch:SetWidth(19)
+ colorSwatch:SetHeight(19)
+ colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
+ colorSwatch:SetPoint("LEFT")
+
+ local texture = frame:CreateTexture(nil, "BACKGROUND")
+ texture:SetWidth(16)
+ texture:SetHeight(16)
+ texture:SetTexture(1, 1, 1)
+ texture:SetPoint("CENTER", colorSwatch)
+ texture:Show()
+
+ local checkers = frame:CreateTexture(nil, "BACKGROUND")
+ checkers:SetWidth(14)
+ checkers:SetHeight(14)
+ checkers:SetTexture("Tileset\\Generic\\Checkers")
+ checkers:SetTexCoord(.25, 0, 0.5, .25)
+ checkers:SetDesaturated(true)
+ checkers:SetVertexColor(1, 1, 1, 0.75)
+ checkers:SetPoint("CENTER", colorSwatch)
+ checkers:Show()
+
+ local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight")
+ text:SetHeight(24)
+ text:SetJustifyH("LEFT")
+ text:SetTextColor(1, 1, 1)
+ text:SetPoint("LEFT", colorSwatch, "RIGHT", 2, 0)
+ text:SetPoint("RIGHT")
+
+ --local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ --highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+ --highlight:SetBlendMode("ADD")
+ --highlight:SetAllPoints(frame)
+
+ local widget = {
+ colorSwatch = colorSwatch,
+ text = text,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,471 @@
+--[[ $Id: AceGUIWidget-DropDown-Items.lua 996 2010-12-01 18:34:17Z nevcairiel $ ]]--
+
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local select, assert = select, assert
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame = CreateFrame
+
+local function fixlevels(parent,...)
+ local i = 1
+ local child = select(i, ...)
+ while child do
+ child:SetFrameLevel(parent:GetFrameLevel()+1)
+ fixlevels(child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+local function fixstrata(strata, parent, ...)
+ local i = 1
+ local child = select(i, ...)
+ parent:SetFrameStrata(strata)
+ while child do
+ fixstrata(strata, child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+-- ItemBase is the base "class" for all dropdown items.
+-- Each item has to use ItemBase.Create(widgetType) to
+-- create an initial 'self' value.
+-- ItemBase will add common functions and ui event handlers.
+-- Be sure to keep basic usage when you override functions.
+
+local ItemBase = {
+ -- NOTE: The ItemBase version is added to each item's version number
+ -- to ensure proper updates on ItemBase changes.
+ -- Use at least 1000er steps.
+ version = 1000,
+ counter = 0,
+}
+
+function ItemBase.Frame_OnEnter(this)
+ local self = this.obj
+
+ if self.useHighlight then
+ self.highlight:Show()
+ end
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+end
+
+function ItemBase.Frame_OnLeave(this)
+ local self = this.obj
+
+ self.highlight:Hide()
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+end
+
+-- exported, AceGUI callback
+function ItemBase.OnAcquire(self)
+ self.frame:SetToplevel(true)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+end
+
+-- exported, AceGUI callback
+function ItemBase.OnRelease(self)
+ self:SetDisabled(false)
+ self.pullout = nil
+ self.frame:SetParent(nil)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetPullout(self, pullout)
+ self.pullout = pullout
+
+ self.frame:SetParent(nil)
+ self.frame:SetParent(pullout.itemFrame)
+ self.parent = pullout.itemFrame
+ fixlevels(pullout.itemFrame, pullout.itemFrame:GetChildren())
+end
+
+-- exported
+function ItemBase.SetText(self, text)
+ self.text:SetText(text or "")
+end
+
+-- exported
+function ItemBase.GetText(self)
+ return self.text:GetText()
+end
+
+-- exported
+function ItemBase.SetPoint(self, ...)
+ self.frame:SetPoint(...)
+end
+
+-- exported
+function ItemBase.Show(self)
+ self.frame:Show()
+end
+
+-- exported
+function ItemBase.Hide(self)
+ self.frame:Hide()
+end
+
+-- exported
+function ItemBase.SetDisabled(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.useHighlight = false
+ self.text:SetTextColor(.5, .5, .5)
+ else
+ self.useHighlight = true
+ self.text:SetTextColor(1, 1, 1)
+ end
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetOnLeave(self, func)
+ self.specialOnLeave = func
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetOnEnter(self, func)
+ self.specialOnEnter = func
+end
+
+function ItemBase.Create(type)
+ -- NOTE: Most of the following code is copied from AceGUI-3.0/Dropdown widget
+ local count = AceGUI:GetNextWidgetNum(type)
+ local frame = CreateFrame("Button", "AceGUI30DropDownItem"..count)
+ local self = {}
+ self.frame = frame
+ frame.obj = self
+ self.type = type
+
+ self.useHighlight = true
+
+ frame:SetHeight(17)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+ text:SetTextColor(1,1,1)
+ text:SetJustifyH("LEFT")
+ text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0)
+ text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-8,0)
+ self.text = text
+
+ local highlight = frame:CreateTexture(nil, "OVERLAY")
+ highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+ highlight:SetBlendMode("ADD")
+ highlight:SetHeight(14)
+ highlight:ClearAllPoints()
+ highlight:SetPoint("RIGHT",frame,"RIGHT",-3,0)
+ highlight:SetPoint("LEFT",frame,"LEFT",5,0)
+ highlight:Hide()
+ self.highlight = highlight
+
+ local check = frame:CreateTexture("OVERLAY")
+ check:SetWidth(16)
+ check:SetHeight(16)
+ check:SetPoint("LEFT",frame,"LEFT",3,-1)
+ check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+ check:Hide()
+ self.check = check
+
+ local sub = frame:CreateTexture("OVERLAY")
+ sub:SetWidth(16)
+ sub:SetHeight(16)
+ sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1)
+ sub:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
+ sub:Hide()
+ self.sub = sub
+
+ frame:SetScript("OnEnter", ItemBase.Frame_OnEnter)
+ frame:SetScript("OnLeave", ItemBase.Frame_OnLeave)
+
+ self.OnAcquire = ItemBase.OnAcquire
+ self.OnRelease = ItemBase.OnRelease
+
+ self.SetPullout = ItemBase.SetPullout
+ self.GetText = ItemBase.GetText
+ self.SetText = ItemBase.SetText
+ self.SetDisabled = ItemBase.SetDisabled
+
+ self.SetPoint = ItemBase.SetPoint
+ self.Show = ItemBase.Show
+ self.Hide = ItemBase.Hide
+
+ self.SetOnLeave = ItemBase.SetOnLeave
+ self.SetOnEnter = ItemBase.SetOnEnter
+
+ return self
+end
+
+-- Register a dummy LibStub library to retrieve the ItemBase, so other addons can use it.
+local IBLib = LibStub:NewLibrary("AceGUI-3.0-DropDown-ItemBase", ItemBase.version)
+if IBLib then
+ IBLib.GetItemBase = function() return ItemBase end
+end
+
+--[[
+ Template for items:
+
+-- Item:
+--
+do
+ local widgetType = "Dropdown-Item-"
+ local widgetVersion = 1
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+--]]
+
+-- Item: Header
+-- A single text entry.
+-- Special: Different text color and no highlight
+do
+ local widgetType = "Dropdown-Item-Header"
+ local widgetVersion = 1
+
+ local function OnEnter(this)
+ local self = this.obj
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+ end
+
+ local function OnLeave(this)
+ local self = this.obj
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+ end
+
+ -- exported, override
+ local function SetDisabled(self, disabled)
+ ItemBase.SetDisabled(self, disabled)
+ if not disabled then
+ self.text:SetTextColor(1, 1, 0)
+ end
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.SetDisabled = SetDisabled
+
+ self.frame:SetScript("OnEnter", OnEnter)
+ self.frame:SetScript("OnLeave", OnLeave)
+
+ self.text:SetTextColor(1, 1, 0)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Execute
+-- A simple button
+do
+ local widgetType = "Dropdown-Item-Execute"
+ local widgetVersion = 1
+
+ local function Frame_OnClick(this, button)
+ local self = this.obj
+ if self.disabled then return end
+ self:Fire("OnClick")
+ if self.pullout then
+ self.pullout:Close()
+ end
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.frame:SetScript("OnClick", Frame_OnClick)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Toggle
+-- Some sort of checkbox for dropdown menus.
+-- Does not close the pullout on click.
+do
+ local widgetType = "Dropdown-Item-Toggle"
+ local widgetVersion = 3
+
+ local function UpdateToggle(self)
+ if self.value then
+ self.check:Show()
+ else
+ self.check:Hide()
+ end
+ end
+
+ local function OnRelease(self)
+ ItemBase.OnRelease(self)
+ self:SetValue(nil)
+ end
+
+ local function Frame_OnClick(this, button)
+ local self = this.obj
+ if self.disabled then return end
+ self.value = not self.value
+ if self.value then
+ PlaySound("igMainMenuOptionCheckBoxOn")
+ else
+ PlaySound("igMainMenuOptionCheckBoxOff")
+ end
+ UpdateToggle(self)
+ self:Fire("OnValueChanged", self.value)
+ end
+
+ -- exported
+ local function SetValue(self, value)
+ self.value = value
+ UpdateToggle(self)
+ end
+
+ -- exported
+ local function GetValue(self)
+ return self.value
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.frame:SetScript("OnClick", Frame_OnClick)
+
+ self.SetValue = SetValue
+ self.GetValue = GetValue
+ self.OnRelease = OnRelease
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Menu
+-- Shows a submenu on mouse over
+-- Does not close the pullout on click
+do
+ local widgetType = "Dropdown-Item-Menu"
+ local widgetVersion = 2
+
+ local function OnEnter(this)
+ local self = this.obj
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+
+ self.highlight:Show()
+
+ if not self.disabled and self.submenu then
+ self.submenu:Open("TOPLEFT", self.frame, "TOPRIGHT", self.pullout:GetRightBorderWidth(), 0, self.frame:GetFrameLevel() + 100)
+ end
+ end
+
+ local function OnHide(this)
+ local self = this.obj
+ if self.submenu then
+ self.submenu:Close()
+ end
+ end
+
+ -- exported
+ local function SetMenu(self, menu)
+ assert(menu.type == "Dropdown-Pullout")
+ self.submenu = menu
+ end
+
+ -- exported
+ local function CloseMenu(self)
+ self.submenu:Close()
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.sub:Show()
+
+ self.frame:SetScript("OnEnter", OnEnter)
+ self.frame:SetScript("OnHide", OnHide)
+
+ self.SetMenu = SetMenu
+ self.CloseMenu = CloseMenu
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Separator
+-- A single line to separate items
+do
+ local widgetType = "Dropdown-Item-Separator"
+ local widgetVersion = 1
+
+ -- exported, override
+ local function SetDisabled(self, disabled)
+ ItemBase.SetDisabled(self, disabled)
+ self.useHighlight = false
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.SetDisabled = SetDisabled
+
+ local line = self.frame:CreateTexture(nil, "OVERLAY")
+ line:SetHeight(1)
+ line:SetTexture(.5, .5, .5)
+ line:SetPoint("LEFT", self.frame, "LEFT", 10, 0)
+ line:SetPoint("RIGHT", self.frame, "RIGHT", -10, 0)
+
+ self.text:Hide()
+
+ self.useHighlight = false
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,737 @@
+--[[ $Id: AceGUIWidget-DropDown.lua 1116 2014-10-12 08:15:46Z nevcairiel $ ]]--
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local min, max, floor = math.min, math.max, math.floor
+local select, pairs, ipairs, type = select, pairs, ipairs, type
+local tsort = table.sort
+
+-- WoW APIs
+local PlaySound = PlaySound
+local UIParent, CreateFrame = UIParent, CreateFrame
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: CLOSE
+
+local function fixlevels(parent,...)
+ local i = 1
+ local child = select(i, ...)
+ while child do
+ child:SetFrameLevel(parent:GetFrameLevel()+1)
+ fixlevels(child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+local function fixstrata(strata, parent, ...)
+ local i = 1
+ local child = select(i, ...)
+ parent:SetFrameStrata(strata)
+ while child do
+ fixstrata(strata, child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+do
+ local widgetType = "Dropdown-Pullout"
+ local widgetVersion = 3
+
+ --[[ Static data ]]--
+
+ local backdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
+ edgeSize = 32,
+ tileSize = 32,
+ tile = true,
+ insets = { left = 11, right = 12, top = 12, bottom = 11 },
+ }
+ local sliderBackdrop = {
+ bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
+ edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
+ tile = true, tileSize = 8, edgeSize = 8,
+ insets = { left = 3, right = 3, top = 3, bottom = 3 }
+ }
+
+ local defaultWidth = 200
+ local defaultMaxHeight = 600
+
+ --[[ UI Event Handlers ]]--
+
+ -- HACK: This should be no part of the pullout, but there
+ -- is no other 'clean' way to response to any item-OnEnter
+ -- Used to close Submenus when an other item is entered
+ local function OnEnter(item)
+ local self = item.pullout
+ for k, v in ipairs(self.items) do
+ if v.CloseMenu and v ~= item then
+ v:CloseMenu()
+ end
+ end
+ end
+
+ -- See the note in Constructor() for each scroll related function
+ local function OnMouseWheel(this, value)
+ this.obj:MoveScroll(value)
+ end
+
+ local function OnScrollValueChanged(this, value)
+ this.obj:SetScroll(value)
+ end
+
+ local function OnSizeChanged(this)
+ this.obj:FixScroll()
+ end
+
+ --[[ Exported methods ]]--
+
+ -- exported
+ local function SetScroll(self, value)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+
+ local offset
+ if height > viewheight then
+ offset = 0
+ else
+ offset = floor((viewheight - height) / 1000 * value)
+ end
+ child:ClearAllPoints()
+ child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset)
+ status.offset = offset
+ status.scrollvalue = value
+ end
+
+ -- exported
+ local function MoveScroll(self, value)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+
+ if height > viewheight then
+ self.slider:Hide()
+ else
+ self.slider:Show()
+ local diff = height - viewheight
+ local delta = 1
+ if value < 0 then
+ delta = -1
+ end
+ self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
+ end
+ end
+
+ -- exported
+ local function FixScroll(self)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+ local offset = status.offset or 0
+
+ if viewheight < height then
+ self.slider:Hide()
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset)
+ self.slider:SetValue(0)
+ else
+ self.slider:Show()
+ local value = (offset / (viewheight - height) * 1000)
+ if value > 1000 then value = 1000 end
+ self.slider:SetValue(value)
+ self:SetScroll(value)
+ if value < 1000 then
+ child:ClearAllPoints()
+ child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset)
+ status.offset = offset
+ end
+ end
+ end
+
+ -- exported, AceGUI callback
+ local function OnAcquire(self)
+ self.frame:SetParent(UIParent)
+ --self.itemFrame:SetToplevel(true)
+ end
+
+ -- exported, AceGUI callback
+ local function OnRelease(self)
+ self:Clear()
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ -- exported
+ local function AddItem(self, item)
+ self.items[#self.items + 1] = item
+
+ local h = #self.items * 16
+ self.itemFrame:SetHeight(h)
+ self.frame:SetHeight(min(h + 34, self.maxHeight)) -- +34: 20 for scrollFrame placement (10 offset) and +14 for item placement
+
+ item.frame:SetPoint("LEFT", self.itemFrame, "LEFT")
+ item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT")
+
+ item:SetPullout(self)
+ item:SetOnEnter(OnEnter)
+ end
+
+ -- exported
+ local function Open(self, point, relFrame, relPoint, x, y)
+ local items = self.items
+ local frame = self.frame
+ local itemFrame = self.itemFrame
+
+ frame:SetPoint(point, relFrame, relPoint, x, y)
+
+
+ local height = 8
+ for i, item in pairs(items) do
+ if i == 1 then
+ item:SetPoint("TOP", itemFrame, "TOP", 0, -2)
+ else
+ item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1)
+ end
+
+ item:Show()
+
+ height = height + 16
+ end
+ itemFrame:SetHeight(height)
+ fixstrata("TOOLTIP", frame, frame:GetChildren())
+ frame:Show()
+ self:Fire("OnOpen")
+ end
+
+ -- exported
+ local function Close(self)
+ self.frame:Hide()
+ self:Fire("OnClose")
+ end
+
+ -- exported
+ local function Clear(self)
+ local items = self.items
+ for i, item in pairs(items) do
+ AceGUI:Release(item)
+ items[i] = nil
+ end
+ end
+
+ -- exported
+ local function IterateItems(self)
+ return ipairs(self.items)
+ end
+
+ -- exported
+ local function SetHideOnLeave(self, val)
+ self.hideOnLeave = val
+ end
+
+ -- exported
+ local function SetMaxHeight(self, height)
+ self.maxHeight = height or defaultMaxHeight
+ if self.frame:GetHeight() > height then
+ self.frame:SetHeight(height)
+ elseif (self.itemFrame:GetHeight() + 34) < height then
+ self.frame:SetHeight(self.itemFrame:GetHeight() + 34) -- see :AddItem
+ end
+ end
+
+ -- exported
+ local function GetRightBorderWidth(self)
+ return 6 + (self.slider:IsShown() and 12 or 0)
+ end
+
+ -- exported
+ local function GetLeftBorderWidth(self)
+ return 6
+ end
+
+ --[[ Constructor ]]--
+
+ local function Constructor()
+ local count = AceGUI:GetNextWidgetNum(widgetType)
+ local frame = CreateFrame("Frame", "AceGUI30Pullout"..count, UIParent)
+ local self = {}
+ self.count = count
+ self.type = widgetType
+ self.frame = frame
+ frame.obj = self
+
+ self.OnAcquire = OnAcquire
+ self.OnRelease = OnRelease
+
+ self.AddItem = AddItem
+ self.Open = Open
+ self.Close = Close
+ self.Clear = Clear
+ self.IterateItems = IterateItems
+ self.SetHideOnLeave = SetHideOnLeave
+
+ self.SetScroll = SetScroll
+ self.MoveScroll = MoveScroll
+ self.FixScroll = FixScroll
+
+ self.SetMaxHeight = SetMaxHeight
+ self.GetRightBorderWidth = GetRightBorderWidth
+ self.GetLeftBorderWidth = GetLeftBorderWidth
+
+ self.items = {}
+
+ self.scrollStatus = {
+ scrollvalue = 0,
+ }
+
+ self.maxHeight = defaultMaxHeight
+
+ frame:SetBackdrop(backdrop)
+ frame:SetBackdropColor(0, 0, 0)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ frame:SetClampedToScreen(true)
+ frame:SetWidth(defaultWidth)
+ frame:SetHeight(self.maxHeight)
+ --frame:SetToplevel(true)
+
+ -- NOTE: The whole scroll frame code is copied from the AceGUI-3.0 widget ScrollFrame
+ local scrollFrame = CreateFrame("ScrollFrame", nil, frame)
+ local itemFrame = CreateFrame("Frame", nil, scrollFrame)
+
+ self.scrollFrame = scrollFrame
+ self.itemFrame = itemFrame
+
+ scrollFrame.obj = self
+ itemFrame.obj = self
+
+ local slider = CreateFrame("Slider", "AceGUI30PulloutScrollbar"..count, scrollFrame)
+ slider:SetOrientation("VERTICAL")
+ slider:SetHitRectInsets(0, 0, -10, 0)
+ slider:SetBackdrop(sliderBackdrop)
+ slider:SetWidth(8)
+ slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
+ slider:SetFrameStrata("FULLSCREEN_DIALOG")
+ self.slider = slider
+ slider.obj = self
+
+ scrollFrame:SetScrollChild(itemFrame)
+ scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -12)
+ scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -6, 12)
+ scrollFrame:EnableMouseWheel(true)
+ scrollFrame:SetScript("OnMouseWheel", OnMouseWheel)
+ scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
+ scrollFrame:SetToplevel(true)
+ scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
+ itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0)
+ itemFrame:SetHeight(400)
+ itemFrame:SetToplevel(true)
+ itemFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0)
+ slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0)
+ slider:SetScript("OnValueChanged", OnScrollValueChanged)
+ slider:SetMinMaxValues(0, 1000)
+ slider:SetValueStep(1)
+ slider:SetValue(0)
+
+ scrollFrame:Show()
+ itemFrame:Show()
+ slider:Hide()
+
+ self:FixScroll()
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+end
+
+do
+ local widgetType = "Dropdown"
+ local widgetVersion = 30
+
+ --[[ Static data ]]--
+
+ --[[ UI event handler ]]--
+
+ local function Control_OnEnter(this)
+ this.obj.button:LockHighlight()
+ this.obj:Fire("OnEnter")
+ end
+
+ local function Control_OnLeave(this)
+ this.obj.button:UnlockHighlight()
+ this.obj:Fire("OnLeave")
+ end
+
+ local function Dropdown_OnHide(this)
+ local self = this.obj
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+
+ local function Dropdown_TogglePullout(this)
+ local self = this.obj
+ PlaySound("igMainMenuOptionCheckBoxOn") -- missleading name, but the Blizzard code uses this sound
+ if self.open then
+ self.open = nil
+ self.pullout:Close()
+ AceGUI:ClearFocus()
+ else
+ self.open = true
+ self.pullout:SetWidth(self.pulloutWidth or self.frame:GetWidth())
+ self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, self.label:IsShown() and -2 or 0)
+ AceGUI:SetFocus(self)
+ end
+ end
+
+ local function OnPulloutOpen(this)
+ local self = this.userdata.obj
+ local value = self.value
+
+ if not self.multiselect then
+ for i, item in this:IterateItems() do
+ item:SetValue(item.userdata.value == value)
+ end
+ end
+
+ self.open = true
+ self:Fire("OnOpened")
+ end
+
+ local function OnPulloutClose(this)
+ local self = this.userdata.obj
+ self.open = nil
+ self:Fire("OnClosed")
+ end
+
+ local function ShowMultiText(self)
+ local text
+ for i, widget in self.pullout:IterateItems() do
+ if widget.type == "Dropdown-Item-Toggle" then
+ if widget:GetValue() then
+ if text then
+ text = text..", "..widget:GetText()
+ else
+ text = widget:GetText()
+ end
+ end
+ end
+ end
+ self:SetText(text)
+ end
+
+ local function OnItemValueChanged(this, event, checked)
+ local self = this.userdata.obj
+
+ if self.multiselect then
+ self:Fire("OnValueChanged", this.userdata.value, checked)
+ ShowMultiText(self)
+ else
+ if checked then
+ self:SetValue(this.userdata.value)
+ self:Fire("OnValueChanged", this.userdata.value)
+ else
+ this:SetValue(true)
+ end
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+ end
+
+ --[[ Exported methods ]]--
+
+ -- exported, AceGUI callback
+ local function OnAcquire(self)
+ local pullout = AceGUI:Create("Dropdown-Pullout")
+ self.pullout = pullout
+ pullout.userdata.obj = self
+ pullout:SetCallback("OnClose", OnPulloutClose)
+ pullout:SetCallback("OnOpen", OnPulloutOpen)
+ self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1)
+ fixlevels(self.pullout.frame, self.pullout.frame:GetChildren())
+
+ self:SetHeight(44)
+ self:SetWidth(200)
+ self:SetLabel()
+ self:SetPulloutWidth(nil)
+ end
+
+ -- exported, AceGUI callback
+ local function OnRelease(self)
+ if self.open then
+ self.pullout:Close()
+ end
+ AceGUI:Release(self.pullout)
+ self.pullout = nil
+
+ self:SetText("")
+ self:SetDisabled(false)
+ self:SetMultiselect(false)
+
+ self.value = nil
+ self.list = nil
+ self.open = nil
+ self.hasClose = nil
+
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ -- exported
+ local function SetDisabled(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.text:SetTextColor(0.5,0.5,0.5)
+ self.button:Disable()
+ self.button_cover:Disable()
+ self.label:SetTextColor(0.5,0.5,0.5)
+ else
+ self.button:Enable()
+ self.button_cover:Enable()
+ self.label:SetTextColor(1,.82,0)
+ self.text:SetTextColor(1,1,1)
+ end
+ end
+
+ -- exported
+ local function ClearFocus(self)
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+
+ -- exported
+ local function SetText(self, text)
+ self.text:SetText(text or "")
+ end
+
+ -- exported
+ local function SetLabel(self, text)
+ if text and text ~= "" then
+ self.label:SetText(text)
+ self.label:Show()
+ self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,-14)
+ self:SetHeight(40)
+ self.alignoffset = 26
+ else
+ self.label:SetText("")
+ self.label:Hide()
+ self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,0)
+ self:SetHeight(26)
+ self.alignoffset = 12
+ end
+ end
+
+ -- exported
+ local function SetValue(self, value)
+ if self.list then
+ self:SetText(self.list[value] or "")
+ end
+ self.value = value
+ end
+
+ -- exported
+ local function GetValue(self)
+ return self.value
+ end
+
+ -- exported
+ local function SetItemValue(self, item, value)
+ if not self.multiselect then return end
+ for i, widget in self.pullout:IterateItems() do
+ if widget.userdata.value == item then
+ if widget.SetValue then
+ widget:SetValue(value)
+ end
+ end
+ end
+ ShowMultiText(self)
+ end
+
+ -- exported
+ local function SetItemDisabled(self, item, disabled)
+ for i, widget in self.pullout:IterateItems() do
+ if widget.userdata.value == item then
+ widget:SetDisabled(disabled)
+ end
+ end
+ end
+
+ local function AddListItem(self, value, text, itemType)
+ if not itemType then itemType = "Dropdown-Item-Toggle" end
+ local exists = AceGUI:GetWidgetVersion(itemType)
+ if not exists then error(("The given item type, %q, does not exist within AceGUI-3.0"):format(tostring(itemType)), 2) end
+
+ local item = AceGUI:Create(itemType)
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function AddCloseButton(self)
+ if not self.hasClose then
+ local close = AceGUI:Create("Dropdown-Item-Execute")
+ close:SetText(CLOSE)
+ self.pullout:AddItem(close)
+ self.hasClose = true
+ end
+ end
+
+ -- exported
+ local sortlist = {}
+ local function SetList(self, list, order, itemType)
+ self.list = list
+ self.pullout:Clear()
+ self.hasClose = nil
+ if not list then return end
+
+ if type(order) ~= "table" then
+ for v in pairs(list) do
+ sortlist[#sortlist + 1] = v
+ end
+ tsort(sortlist)
+
+ for i, key in ipairs(sortlist) do
+ AddListItem(self, key, list[key], itemType)
+ sortlist[i] = nil
+ end
+ else
+ for i, key in ipairs(order) do
+ AddListItem(self, key, list[key], itemType)
+ end
+ end
+ if self.multiselect then
+ ShowMultiText(self)
+ AddCloseButton(self)
+ end
+ end
+
+ -- exported
+ local function AddItem(self, value, text, itemType)
+ if self.list then
+ self.list[value] = text
+ AddListItem(self, value, text, itemType)
+ end
+ end
+
+ -- exported
+ local function SetMultiselect(self, multi)
+ self.multiselect = multi
+ if multi then
+ ShowMultiText(self)
+ AddCloseButton(self)
+ end
+ end
+
+ -- exported
+ local function GetMultiselect(self)
+ return self.multiselect
+ end
+
+ local function SetPulloutWidth(self, width)
+ self.pulloutWidth = width
+ end
+
+ --[[ Constructor ]]--
+
+ local function Constructor()
+ local count = AceGUI:GetNextWidgetNum(widgetType)
+ local frame = CreateFrame("Frame", nil, UIParent)
+ local dropdown = CreateFrame("Frame", "AceGUI30DropDown"..count, frame, "UIDropDownMenuTemplate")
+
+ local self = {}
+ self.type = widgetType
+ self.frame = frame
+ self.dropdown = dropdown
+ self.count = count
+ frame.obj = self
+ dropdown.obj = self
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.ClearFocus = ClearFocus
+
+ self.SetText = SetText
+ self.SetValue = SetValue
+ self.GetValue = GetValue
+ self.SetList = SetList
+ self.SetLabel = SetLabel
+ self.SetDisabled = SetDisabled
+ self.AddItem = AddItem
+ self.SetMultiselect = SetMultiselect
+ self.GetMultiselect = GetMultiselect
+ self.SetItemValue = SetItemValue
+ self.SetItemDisabled = SetItemDisabled
+ self.SetPulloutWidth = SetPulloutWidth
+
+ self.alignoffset = 26
+
+ frame:SetScript("OnHide",Dropdown_OnHide)
+
+ dropdown:ClearAllPoints()
+ dropdown:SetPoint("TOPLEFT",frame,"TOPLEFT",-15,0)
+ dropdown:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",17,0)
+ dropdown:SetScript("OnHide", nil)
+
+ local left = _G[dropdown:GetName() .. "Left"]
+ local middle = _G[dropdown:GetName() .. "Middle"]
+ local right = _G[dropdown:GetName() .. "Right"]
+
+ middle:ClearAllPoints()
+ right:ClearAllPoints()
+
+ middle:SetPoint("LEFT", left, "RIGHT", 0, 0)
+ middle:SetPoint("RIGHT", right, "LEFT", 0, 0)
+ right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17)
+
+ local button = _G[dropdown:GetName() .. "Button"]
+ self.button = button
+ button.obj = self
+ button:SetScript("OnEnter",Control_OnEnter)
+ button:SetScript("OnLeave",Control_OnLeave)
+ button:SetScript("OnClick",Dropdown_TogglePullout)
+
+ local button_cover = CreateFrame("BUTTON",nil,self.frame)
+ self.button_cover = button_cover
+ button_cover.obj = self
+ button_cover:SetPoint("TOPLEFT",self.frame,"BOTTOMLEFT",0,25)
+ button_cover:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT")
+ button_cover:SetScript("OnEnter",Control_OnEnter)
+ button_cover:SetScript("OnLeave",Control_OnLeave)
+ button_cover:SetScript("OnClick",Dropdown_TogglePullout)
+
+ local text = _G[dropdown:GetName() .. "Text"]
+ self.text = text
+ text.obj = self
+ text:ClearAllPoints()
+ text:SetPoint("RIGHT", right, "RIGHT" ,-43, 2)
+ text:SetPoint("LEFT", left, "LEFT", 25, 2)
+
+ local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+ label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
+ label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
+ label:SetJustifyH("LEFT")
+ label:SetHeight(18)
+ label:Hide()
+ self.label = label
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,261 @@
+--[[-----------------------------------------------------------------------------
+EditBox Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "EditBox", 25
+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 tostring, pairs = tostring, pairs
+
+-- WoW APIs
+local PlaySound = PlaySound
+local GetCursorInfo, ClearCursor, GetSpellInfo = GetCursorInfo, ClearCursor, GetSpellInfo
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: AceGUIEditBoxInsertLink, ChatFontNormal, OKAY
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+if not AceGUIEditBoxInsertLink then
+ -- upgradeable hook
+ hooksecurefunc("ChatEdit_InsertLink", function(...) return _G.AceGUIEditBoxInsertLink(...) end)
+end
+
+function _G.AceGUIEditBoxInsertLink(text)
+ for i = 1, AceGUI:GetWidgetCount(Type) do
+ local editbox = _G["AceGUI-3.0EditBox"..i]
+ if editbox and editbox:IsVisible() and editbox:HasFocus() then
+ editbox:Insert(text)
+ return true
+ end
+ end
+end
+
+local function ShowButton(self)
+ if not self.disablebutton then
+ self.button:Show()
+ self.editbox:SetTextInsets(0, 20, 3, 3)
+ end
+end
+
+local function HideButton(self)
+ self.button:Hide()
+ self.editbox:SetTextInsets(0, 0, 3, 3)
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Frame_OnShowFocus(frame)
+ frame.obj.editbox:SetFocus()
+ frame:SetScript("OnShow", nil)
+end
+
+local function EditBox_OnEscapePressed(frame)
+ AceGUI:ClearFocus()
+end
+
+local function EditBox_OnEnterPressed(frame)
+ local self = frame.obj
+ local value = frame:GetText()
+ local cancel = self:Fire("OnEnterPressed", value)
+ if not cancel then
+ PlaySound("igMainMenuOptionCheckBoxOn")
+ HideButton(self)
+ end
+end
+
+local function EditBox_OnReceiveDrag(frame)
+ local self = frame.obj
+ local type, id, info = GetCursorInfo()
+ if type == "item" then
+ self:SetText(info)
+ self:Fire("OnEnterPressed", info)
+ ClearCursor()
+ elseif type == "spell" then
+ local name = GetSpellInfo(id, info)
+ self:SetText(name)
+ self:Fire("OnEnterPressed", name)
+ ClearCursor()
+ elseif type == "macro" then
+ local name = GetMacroInfo(id)
+ self:SetText(name)
+ self:Fire("OnEnterPressed", name)
+ ClearCursor()
+ end
+ HideButton(self)
+ AceGUI:ClearFocus()
+end
+
+local function EditBox_OnTextChanged(frame)
+ local self = frame.obj
+ local value = frame:GetText()
+ if tostring(value) ~= tostring(self.lasttext) then
+ self:Fire("OnTextChanged", value)
+ self.lasttext = value
+ ShowButton(self)
+ end
+end
+
+local function EditBox_OnFocusGained(frame)
+ AceGUI:SetFocus(frame.obj)
+end
+
+local function Button_OnClick(frame)
+ local editbox = frame.obj.editbox
+ editbox:ClearFocus()
+ EditBox_OnEnterPressed(editbox)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- height is controlled by SetLabel
+ self:SetWidth(200)
+ self:SetDisabled(false)
+ self:SetLabel()
+ self:SetText()
+ self:DisableButton(false)
+ self:SetMaxLetters(0)
+ end,
+
+ ["OnRelease"] = function(self)
+ self:ClearFocus()
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.editbox:EnableMouse(false)
+ self.editbox:ClearFocus()
+ self.editbox:SetTextColor(0.5,0.5,0.5)
+ self.label:SetTextColor(0.5,0.5,0.5)
+ else
+ self.editbox:EnableMouse(true)
+ self.editbox:SetTextColor(1,1,1)
+ self.label:SetTextColor(1,.82,0)
+ end
+ end,
+
+ ["SetText"] = function(self, text)
+ self.lasttext = text or ""
+ self.editbox:SetText(text or "")
+ self.editbox:SetCursorPosition(0)
+ HideButton(self)
+ end,
+
+ ["GetText"] = function(self, text)
+ return self.editbox:GetText()
+ end,
+
+ ["SetLabel"] = function(self, text)
+ if text and text ~= "" then
+ self.label:SetText(text)
+ self.label:Show()
+ self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,-18)
+ self:SetHeight(44)
+ self.alignoffset = 30
+ else
+ self.label:SetText("")
+ self.label:Hide()
+ self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,0)
+ self:SetHeight(26)
+ self.alignoffset = 12
+ end
+ end,
+
+ ["DisableButton"] = function(self, disabled)
+ self.disablebutton = disabled
+ if disabled then
+ HideButton(self)
+ end
+ end,
+
+ ["SetMaxLetters"] = function (self, num)
+ self.editbox:SetMaxLetters(num or 0)
+ end,
+
+ ["ClearFocus"] = function(self)
+ self.editbox:ClearFocus()
+ self.frame:SetScript("OnShow", nil)
+ end,
+
+ ["SetFocus"] = function(self)
+ self.editbox:SetFocus()
+ if not self.frame:IsShown() then
+ self.frame:SetScript("OnShow", Frame_OnShowFocus)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local editbox = CreateFrame("EditBox", "AceGUI-3.0EditBox"..num, frame, "InputBoxTemplate")
+ editbox:SetAutoFocus(false)
+ editbox:SetFontObject(ChatFontNormal)
+ editbox:SetScript("OnEnter", Control_OnEnter)
+ editbox:SetScript("OnLeave", Control_OnLeave)
+ editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
+ editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
+ editbox:SetScript("OnTextChanged", EditBox_OnTextChanged)
+ editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag)
+ editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag)
+ editbox:SetScript("OnEditFocusGained", EditBox_OnFocusGained)
+ editbox:SetTextInsets(0, 0, 3, 3)
+ editbox:SetMaxLetters(256)
+ editbox:SetPoint("BOTTOMLEFT", 6, 0)
+ editbox:SetPoint("BOTTOMRIGHT")
+ editbox:SetHeight(19)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
+ label:SetPoint("TOPLEFT", 0, -2)
+ label:SetPoint("TOPRIGHT", 0, -2)
+ label:SetJustifyH("LEFT")
+ label:SetHeight(18)
+
+ local button = CreateFrame("Button", nil, editbox, "UIPanelButtonTemplate")
+ button:SetWidth(40)
+ button:SetHeight(20)
+ button:SetPoint("RIGHT", -2, 0)
+ button:SetText(OKAY)
+ button:SetScript("OnClick", Button_OnClick)
+ button:Hide()
+
+ local widget = {
+ alignoffset = 30,
+ editbox = editbox,
+ label = label,
+ button = button,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ editbox.obj, button.obj = widget, widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,78 @@
+--[[-----------------------------------------------------------------------------
+Heading Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "Heading", 20
+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 pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetText()
+ self:SetFullWidth()
+ self:SetHeight(18)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetText"] = function(self, text)
+ self.label:SetText(text or "")
+ if text and text ~= "" then
+ self.left:SetPoint("RIGHT", self.label, "LEFT", -5, 0)
+ self.right:Show()
+ else
+ self.left:SetPoint("RIGHT", -3, 0)
+ self.right:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontNormal")
+ label:SetPoint("TOP")
+ label:SetPoint("BOTTOM")
+ label:SetJustifyH("CENTER")
+
+ local left = frame:CreateTexture(nil, "BACKGROUND")
+ left:SetHeight(8)
+ left:SetPoint("LEFT", 3, 0)
+ left:SetPoint("RIGHT", label, "LEFT", -5, 0)
+ left:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
+ left:SetTexCoord(0.81, 0.94, 0.5, 1)
+
+ local right = frame:CreateTexture(nil, "BACKGROUND")
+ right:SetHeight(8)
+ right:SetPoint("RIGHT", -3, 0)
+ right:SetPoint("LEFT", label, "RIGHT", 5, 0)
+ right:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
+ right:SetTexCoord(0.81, 0.94, 0.5, 1)
+
+ local widget = {
+ label = label,
+ left = left,
+ right = right,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,144 @@
+--[[-----------------------------------------------------------------------------
+Icon Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "Icon", 21
+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 select, pairs, print = select, pairs, print
+
+-- WoW APIs
+local CreateFrame, UIParent, GetBuildInfo = CreateFrame, UIParent, GetBuildInfo
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Button_OnClick(frame, button)
+ frame.obj:Fire("OnClick", button)
+ AceGUI:ClearFocus()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetHeight(110)
+ self:SetWidth(110)
+ self:SetLabel()
+ self:SetImage(nil)
+ self:SetImageSize(64, 64)
+ self:SetDisabled(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetLabel"] = function(self, text)
+ if text and text ~= "" then
+ self.label:Show()
+ self.label:SetText(text)
+ self:SetHeight(self.image:GetHeight() + 25)
+ else
+ self.label:Hide()
+ self:SetHeight(self.image:GetHeight() + 10)
+ end
+ end,
+
+ ["SetImage"] = function(self, path, ...)
+ local image = self.image
+ image:SetTexture(path)
+
+ if image:GetTexture() then
+ local n = select("#", ...)
+ if n == 4 or n == 8 then
+ image:SetTexCoord(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ end
+ end,
+
+ ["SetImageSize"] = function(self, width, height)
+ self.image:SetWidth(width)
+ self.image:SetHeight(height)
+ --self.frame:SetWidth(width + 30)
+ if self.label:IsShown() then
+ self:SetHeight(height + 25)
+ else
+ self:SetHeight(height + 10)
+ end
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ self.label:SetTextColor(0.5, 0.5, 0.5)
+ self.image:SetVertexColor(0.5, 0.5, 0.5, 0.5)
+ else
+ self.frame:Enable()
+ self.label:SetTextColor(1, 1, 1)
+ self.image:SetVertexColor(1, 1, 1, 1)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Button", nil, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnClick", Button_OnClick)
+
+ local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlight")
+ label:SetPoint("BOTTOMLEFT")
+ label:SetPoint("BOTTOMRIGHT")
+ label:SetJustifyH("CENTER")
+ label:SetJustifyV("TOP")
+ label:SetHeight(18)
+
+ local image = frame:CreateTexture(nil, "BACKGROUND")
+ image:SetWidth(64)
+ image:SetHeight(64)
+ image:SetPoint("TOP", 0, -5)
+
+ local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ highlight:SetAllPoints(image)
+ highlight:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight")
+ highlight:SetTexCoord(0, 1, 0.23, 0.77)
+ highlight:SetBlendMode("ADD")
+
+ local widget = {
+ label = label,
+ image = image,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ -- SetText is deprecated, but keep it around for a while. (say, to WoW 4.0)
+ if (select(4, GetBuildInfo()) < 40000) then
+ widget.SetText = widget.SetLabel
+ else
+ widget.SetText = function(self, ...) print("AceGUI-3.0-Icon: SetText is deprecated! Use SetLabel instead!"); self:SetLabel(...) end
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,101 @@
+--[[-----------------------------------------------------------------------------
+InteractiveLabel Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "InteractiveLabel", 20
+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 select, pairs = select, pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Label_OnClick(frame, button)
+ frame.obj:Fire("OnClick", button)
+ AceGUI:ClearFocus()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:LabelOnAcquire()
+ self:SetHighlight()
+ self:SetHighlightTexCoord()
+ self:SetDisabled(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetHighlight"] = function(self, ...)
+ self.highlight:SetTexture(...)
+ end,
+
+ ["SetHighlightTexCoord"] = function(self, ...)
+ local c = select("#", ...)
+ if c == 4 or c == 8 then
+ self.highlight:SetTexCoord(...)
+ else
+ self.highlight:SetTexCoord(0, 1, 0, 1)
+ end
+ end,
+
+ ["SetDisabled"] = function(self,disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:EnableMouse(false)
+ self.label:SetTextColor(0.5, 0.5, 0.5)
+ else
+ self.frame:EnableMouse(true)
+ self.label:SetTextColor(1, 1, 1)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ -- create a Label type that we will hijack
+ local label = AceGUI:Create("Label")
+
+ local frame = label.frame
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnMouseDown", Label_OnClick)
+
+ local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ highlight:SetTexture(nil)
+ highlight:SetAllPoints()
+ highlight:SetBlendMode("ADD")
+
+ label.highlight = highlight
+ label.type = Type
+ label.LabelOnAcquire = label.OnAcquire
+ for method, func in pairs(methods) do
+ label[method] = func
+ end
+
+ return label
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
+
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,239 @@
+--[[-----------------------------------------------------------------------------
+Keybinding Widget
+Set Keybindings in the Config UI.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Keybinding", 24
+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 pairs = pairs
+
+-- WoW APIs
+local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: NOT_BOUND
+
+local wowMoP
+do
+ local _, _, _, interface = GetBuildInfo()
+ wowMoP = (interface >= 50000)
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Keybinding_OnClick(frame, button)
+ if button == "LeftButton" or button == "RightButton" then
+ local self = frame.obj
+ if self.waitingForKey then
+ frame:EnableKeyboard(false)
+ self.msgframe:Hide()
+ frame:UnlockHighlight()
+ self.waitingForKey = nil
+ else
+ frame:EnableKeyboard(true)
+ self.msgframe:Show()
+ frame:LockHighlight()
+ self.waitingForKey = true
+ end
+ end
+ AceGUI:ClearFocus()
+end
+
+local ignoreKeys = {
+ ["BUTTON1"] = true, ["BUTTON2"] = true,
+ ["UNKNOWN"] = true,
+ ["LSHIFT"] = true, ["LCTRL"] = true, ["LALT"] = true,
+ ["RSHIFT"] = true, ["RCTRL"] = true, ["RALT"] = true,
+}
+local function Keybinding_OnKeyDown(frame, key)
+ local self = frame.obj
+ if self.waitingForKey then
+ local keyPressed = key
+ if keyPressed == "ESCAPE" then
+ keyPressed = ""
+ else
+ if ignoreKeys[keyPressed] then return end
+ if IsShiftKeyDown() then
+ keyPressed = "SHIFT-"..keyPressed
+ end
+ if IsControlKeyDown() then
+ keyPressed = "CTRL-"..keyPressed
+ end
+ if IsAltKeyDown() then
+ keyPressed = "ALT-"..keyPressed
+ end
+ end
+
+ frame:EnableKeyboard(false)
+ self.msgframe:Hide()
+ frame:UnlockHighlight()
+ self.waitingForKey = nil
+
+ if not self.disabled then
+ self:SetKey(keyPressed)
+ self:Fire("OnKeyChanged", keyPressed)
+ end
+ end
+end
+
+local function Keybinding_OnMouseDown(frame, button)
+ if button == "LeftButton" or button == "RightButton" then
+ return
+ elseif button == "MiddleButton" then
+ button = "BUTTON3"
+ elseif button == "Button4" then
+ button = "BUTTON4"
+ elseif button == "Button5" then
+ button = "BUTTON5"
+ end
+ Keybinding_OnKeyDown(frame, button)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetWidth(200)
+ self:SetLabel("")
+ self:SetKey("")
+ self.waitingForKey = nil
+ self.msgframe:Hide()
+ self:SetDisabled(false)
+ self.button:EnableKeyboard(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.button:Disable()
+ self.label:SetTextColor(0.5,0.5,0.5)
+ else
+ self.button:Enable()
+ self.label:SetTextColor(1,1,1)
+ end
+ end,
+
+ ["SetKey"] = function(self, key)
+ if (key or "") == "" then
+ self.button:SetText(NOT_BOUND)
+ self.button:SetNormalFontObject("GameFontNormal")
+ else
+ self.button:SetText(key)
+ self.button:SetNormalFontObject("GameFontHighlight")
+ end
+ end,
+
+ ["GetKey"] = function(self)
+ local key = self.button:GetText()
+ if key == NOT_BOUND then
+ key = nil
+ end
+ return key
+ end,
+
+ ["SetLabel"] = function(self, label)
+ self.label:SetText(label or "")
+ if (label or "") == "" then
+ self.alignoffset = nil
+ self:SetHeight(24)
+ else
+ self.alignoffset = 30
+ self:SetHeight(44)
+ end
+ end,
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+
+local ControlBackdrop = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 3, bottom = 3 }
+}
+
+local function keybindingMsgFixWidth(frame)
+ frame:SetWidth(frame.msg:GetWidth() + 10)
+ frame:SetScript("OnUpdate", nil)
+end
+
+local function Constructor()
+ local name = "AceGUI30KeybindingButton" .. AceGUI:GetNextWidgetNum(Type)
+
+ local frame = CreateFrame("Frame", nil, UIParent)
+ local button = CreateFrame("Button", name, frame, wowMoP and "UIPanelButtonTemplate" or "UIPanelButtonTemplate2")
+
+ button:EnableMouse(true)
+ button:RegisterForClicks("AnyDown")
+ button:SetScript("OnEnter", Control_OnEnter)
+ button:SetScript("OnLeave", Control_OnLeave)
+ button:SetScript("OnClick", Keybinding_OnClick)
+ button:SetScript("OnKeyDown", Keybinding_OnKeyDown)
+ button:SetScript("OnMouseDown", Keybinding_OnMouseDown)
+ button:SetPoint("BOTTOMLEFT")
+ button:SetPoint("BOTTOMRIGHT")
+ button:SetHeight(24)
+ button:EnableKeyboard(false)
+
+ local text = button:GetFontString()
+ text:SetPoint("LEFT", 7, 0)
+ text:SetPoint("RIGHT", -7, 0)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
+ label:SetPoint("TOPLEFT")
+ label:SetPoint("TOPRIGHT")
+ label:SetJustifyH("CENTER")
+ label:SetHeight(18)
+
+ local msgframe = CreateFrame("Frame", nil, UIParent)
+ msgframe:SetHeight(30)
+ msgframe:SetBackdrop(ControlBackdrop)
+ msgframe:SetBackdropColor(0,0,0)
+ msgframe:SetFrameStrata("FULLSCREEN_DIALOG")
+ msgframe:SetFrameLevel(1000)
+ msgframe:SetToplevel(true)
+
+ local msg = msgframe:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel.")
+ msgframe.msg = msg
+ msg:SetPoint("TOPLEFT", 5, -5)
+ msgframe:SetScript("OnUpdate", keybindingMsgFixWidth)
+ msgframe:SetPoint("BOTTOM", button, "TOP")
+ msgframe:Hide()
+
+ local widget = {
+ button = button,
+ label = label,
+ msgframe = msgframe,
+ frame = frame,
+ alignoffset = 30,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ button.obj = widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,166 @@
+--[[-----------------------------------------------------------------------------
+Label Widget
+Displays text and optionally an icon.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Label", 23
+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 max, select, pairs = math.max, select, pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+
+local function UpdateImageAnchor(self)
+ if self.resizing then return end
+ local frame = self.frame
+ local width = frame.width or frame:GetWidth() or 0
+ local image = self.image
+ local label = self.label
+ local height
+
+ label:ClearAllPoints()
+ image:ClearAllPoints()
+
+ if self.imageshown then
+ local imagewidth = image:GetWidth()
+ if (width - imagewidth) < 200 or (label:GetText() or "") == "" then
+ -- image goes on top centered when less than 200 width for the text, or if there is no text
+ image:SetPoint("TOP")
+ label:SetPoint("TOP", image, "BOTTOM")
+ label:SetPoint("LEFT")
+ label:SetWidth(width)
+ height = image:GetHeight() + label:GetHeight()
+ else
+ -- image on the left
+ image:SetPoint("TOPLEFT")
+ if image:GetHeight() > label:GetHeight() then
+ label:SetPoint("LEFT", image, "RIGHT", 4, 0)
+ else
+ label:SetPoint("TOPLEFT", image, "TOPRIGHT", 4, 0)
+ end
+ label:SetWidth(width - imagewidth - 4)
+ height = max(image:GetHeight(), label:GetHeight())
+ end
+ else
+ -- no image shown
+ label:SetPoint("TOPLEFT")
+ label:SetWidth(width)
+ height = label:GetHeight()
+ end
+
+ self.resizing = true
+ frame:SetHeight(height)
+ frame.height = height
+ self.resizing = nil
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- set the flag to stop constant size updates
+ self.resizing = true
+ -- height is set dynamically by the text and image size
+ self:SetWidth(200)
+ self:SetText()
+ self:SetImage(nil)
+ self:SetImageSize(16, 16)
+ self:SetColor()
+ self:SetFontObject()
+
+ -- reset the flag
+ self.resizing = nil
+ -- run the update explicitly
+ UpdateImageAnchor(self)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["OnWidthSet"] = function(self, width)
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetText"] = function(self, text)
+ self.label:SetText(text)
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetColor"] = function(self, r, g, b)
+ if not (r and g and b) then
+ r, g, b = 1, 1, 1
+ end
+ self.label:SetVertexColor(r, g, b)
+ end,
+
+ ["SetImage"] = function(self, path, ...)
+ local image = self.image
+ image:SetTexture(path)
+
+ if image:GetTexture() then
+ self.imageshown = true
+ local n = select("#", ...)
+ if n == 4 or n == 8 then
+ image:SetTexCoord(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ else
+ self.imageshown = nil
+ end
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetFont"] = function(self, font, height, flags)
+ self.label:SetFont(font, height, flags)
+ end,
+
+ ["SetFontObject"] = function(self, font)
+ self:SetFont((font or GameFontHighlightSmall):GetFont())
+ end,
+
+ ["SetImageSize"] = function(self, width, height)
+ self.image:SetWidth(width)
+ self.image:SetHeight(height)
+ UpdateImageAnchor(self)
+ end,
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
+ label:SetJustifyH("LEFT")
+ label:SetJustifyV("TOP")
+
+ local image = frame:CreateTexture(nil, "BACKGROUND")
+
+ -- create widget
+ local widget = {
+ label = label,
+ image = image,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,368 @@
+local Type, Version = "MultiLineEditBox", 27
+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 pairs = pairs
+
+-- WoW APIs
+local GetCursorInfo, GetSpellInfo, ClearCursor = GetCursorInfo, GetSpellInfo, ClearCursor
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: ACCEPT, ChatFontNormal
+
+local wowMoP
+do
+ local _, _, _, interface = GetBuildInfo()
+ wowMoP = (interface >= 50000)
+end
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+
+if not AceGUIMultiLineEditBoxInsertLink then
+ -- upgradeable hook
+ hooksecurefunc("ChatEdit_InsertLink", function(...) return _G.AceGUIMultiLineEditBoxInsertLink(...) end)
+end
+
+function _G.AceGUIMultiLineEditBoxInsertLink(text)
+ for i = 1, AceGUI:GetWidgetCount(Type) do
+ local editbox = _G[("MultiLineEditBox%uEdit"):format(i)]
+ if editbox and editbox:IsVisible() and editbox:HasFocus() then
+ editbox:Insert(text)
+ return true
+ end
+ end
+end
+
+
+local function Layout(self)
+ self:SetHeight(self.numlines * 14 + (self.disablebutton and 19 or 41) + self.labelHeight)
+
+ if self.labelHeight == 0 then
+ self.scrollBar:SetPoint("TOP", self.frame, "TOP", 0, -23)
+ else
+ self.scrollBar:SetPoint("TOP", self.label, "BOTTOM", 0, -19)
+ end
+
+ if self.disablebutton then
+ self.scrollBar:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, 21)
+ self.scrollBG:SetPoint("BOTTOMLEFT", 0, 4)
+ else
+ self.scrollBar:SetPoint("BOTTOM", self.button, "TOP", 0, 18)
+ self.scrollBG:SetPoint("BOTTOMLEFT", self.button, "TOPLEFT")
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function OnClick(self) -- Button
+ self = self.obj
+ self.editBox:ClearFocus()
+ if not self:Fire("OnEnterPressed", self.editBox:GetText()) then
+ self.button:Disable()
+ end
+end
+
+local function OnCursorChanged(self, _, y, _, cursorHeight) -- EditBox
+ self, y = self.obj.scrollFrame, -y
+ local offset = self:GetVerticalScroll()
+ if y < offset then
+ self:SetVerticalScroll(y)
+ else
+ y = y + cursorHeight - self:GetHeight()
+ if y > offset then
+ self:SetVerticalScroll(y)
+ end
+ end
+end
+
+local function OnEditFocusLost(self) -- EditBox
+ self:HighlightText(0, 0)
+ self.obj:Fire("OnEditFocusLost")
+end
+
+local function OnEnter(self) -- EditBox / ScrollFrame
+ self = self.obj
+ if not self.entered then
+ self.entered = true
+ self:Fire("OnEnter")
+ end
+end
+
+local function OnLeave(self) -- EditBox / ScrollFrame
+ self = self.obj
+ if self.entered then
+ self.entered = nil
+ self:Fire("OnLeave")
+ end
+end
+
+local function OnMouseUp(self) -- ScrollFrame
+ self = self.obj.editBox
+ self:SetFocus()
+ self:SetCursorPosition(self:GetNumLetters())
+end
+
+local function OnReceiveDrag(self) -- EditBox / ScrollFrame
+ local type, id, info = GetCursorInfo()
+ if type == "spell" then
+ info = GetSpellInfo(id, info)
+ elseif type ~= "item" then
+ return
+ end
+ ClearCursor()
+ self = self.obj
+ local editBox = self.editBox
+ if not editBox:HasFocus() then
+ editBox:SetFocus()
+ editBox:SetCursorPosition(editBox:GetNumLetters())
+ end
+ editBox:Insert(info)
+ self.button:Enable()
+end
+
+local function OnSizeChanged(self, width, height) -- ScrollFrame
+ self.obj.editBox:SetWidth(width)
+end
+
+local function OnTextChanged(self, userInput) -- EditBox
+ if userInput then
+ self = self.obj
+ self:Fire("OnTextChanged", self.editBox:GetText())
+ self.button:Enable()
+ end
+end
+
+local function OnTextSet(self) -- EditBox
+ self:HighlightText(0, 0)
+ self:SetCursorPosition(self:GetNumLetters())
+ self:SetCursorPosition(0)
+ self.obj.button:Disable()
+end
+
+local function OnVerticalScroll(self, offset) -- ScrollFrame
+ local editBox = self.obj.editBox
+ editBox:SetHitRectInsets(0, 0, offset, editBox:GetHeight() - offset - self:GetHeight())
+end
+
+local function OnShowFocus(frame)
+ frame.obj.editBox:SetFocus()
+ frame:SetScript("OnShow", nil)
+end
+
+local function OnEditFocusGained(frame)
+ AceGUI:SetFocus(frame.obj)
+ frame.obj:Fire("OnEditFocusGained")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self.editBox:SetText("")
+ self:SetDisabled(false)
+ self:SetWidth(200)
+ self:DisableButton(false)
+ self:SetNumLines()
+ self.entered = nil
+ self:SetMaxLetters(0)
+ end,
+
+ ["OnRelease"] = function(self)
+ self:ClearFocus()
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ local editBox = self.editBox
+ if disabled then
+ editBox:ClearFocus()
+ editBox:EnableMouse(false)
+ editBox:SetTextColor(0.5, 0.5, 0.5)
+ self.label:SetTextColor(0.5, 0.5, 0.5)
+ self.scrollFrame:EnableMouse(false)
+ self.button:Disable()
+ else
+ editBox:EnableMouse(true)
+ editBox:SetTextColor(1, 1, 1)
+ self.label:SetTextColor(1, 0.82, 0)
+ self.scrollFrame:EnableMouse(true)
+ end
+ end,
+
+ ["SetLabel"] = function(self, text)
+ if text and text ~= "" then
+ self.label:SetText(text)
+ if self.labelHeight ~= 10 then
+ self.labelHeight = 10
+ self.label:Show()
+ end
+ elseif self.labelHeight ~= 0 then
+ self.labelHeight = 0
+ self.label:Hide()
+ end
+ Layout(self)
+ end,
+
+ ["SetNumLines"] = function(self, value)
+ if not value or value < 4 then
+ value = 4
+ end
+ self.numlines = value
+ Layout(self)
+ end,
+
+ ["SetText"] = function(self, text)
+ self.editBox:SetText(text)
+ end,
+
+ ["GetText"] = function(self)
+ return self.editBox:GetText()
+ end,
+
+ ["SetMaxLetters"] = function (self, num)
+ self.editBox:SetMaxLetters(num or 0)
+ end,
+
+ ["DisableButton"] = function(self, disabled)
+ self.disablebutton = disabled
+ if disabled then
+ self.button:Hide()
+ else
+ self.button:Show()
+ end
+ Layout(self)
+ end,
+
+ ["ClearFocus"] = function(self)
+ self.editBox:ClearFocus()
+ self.frame:SetScript("OnShow", nil)
+ end,
+
+ ["SetFocus"] = function(self)
+ self.editBox:SetFocus()
+ if not self.frame:IsShown() then
+ self.frame:SetScript("OnShow", OnShowFocus)
+ end
+ end,
+
+ ["GetCursorPosition"] = function(self)
+ return self.editBox:GetCursorPosition()
+ end,
+
+ ["SetCursorPosition"] = function(self, ...)
+ return self.editBox:SetCursorPosition(...)
+ end,
+
+
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local backdrop = {
+ bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
+ edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], edgeSize = 16,
+ insets = { left = 4, right = 3, top = 4, bottom = 3 }
+}
+
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local widgetNum = AceGUI:GetNextWidgetNum(Type)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
+ label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -4)
+ label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -4)
+ label:SetJustifyH("LEFT")
+ label:SetText(ACCEPT)
+ label:SetHeight(10)
+
+ local button = CreateFrame("Button", ("%s%dButton"):format(Type, widgetNum), frame, wowMoP and "UIPanelButtonTemplate" or "UIPanelButtonTemplate2")
+ button:SetPoint("BOTTOMLEFT", 0, 4)
+ button:SetHeight(22)
+ button:SetWidth(label:GetStringWidth() + 24)
+ button:SetText(ACCEPT)
+ button:SetScript("OnClick", OnClick)
+ button:Disable()
+
+ local text = button:GetFontString()
+ text:ClearAllPoints()
+ text:SetPoint("TOPLEFT", button, "TOPLEFT", 5, -5)
+ text:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -5, 1)
+ text:SetJustifyV("MIDDLE")
+
+ local scrollBG = CreateFrame("Frame", nil, frame)
+ scrollBG:SetBackdrop(backdrop)
+ scrollBG:SetBackdropColor(0, 0, 0)
+ scrollBG:SetBackdropBorderColor(0.4, 0.4, 0.4)
+
+ local scrollFrame = CreateFrame("ScrollFrame", ("%s%dScrollFrame"):format(Type, widgetNum), frame, "UIPanelScrollFrameTemplate")
+
+ local scrollBar = _G[scrollFrame:GetName() .. "ScrollBar"]
+ scrollBar:ClearAllPoints()
+ scrollBar:SetPoint("TOP", label, "BOTTOM", 0, -19)
+ scrollBar:SetPoint("BOTTOM", button, "TOP", 0, 18)
+ scrollBar:SetPoint("RIGHT", frame, "RIGHT")
+
+ scrollBG:SetPoint("TOPRIGHT", scrollBar, "TOPLEFT", 0, 19)
+ scrollBG:SetPoint("BOTTOMLEFT", button, "TOPLEFT")
+
+ scrollFrame:SetPoint("TOPLEFT", scrollBG, "TOPLEFT", 5, -6)
+ scrollFrame:SetPoint("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -4, 4)
+ scrollFrame:SetScript("OnEnter", OnEnter)
+ scrollFrame:SetScript("OnLeave", OnLeave)
+ scrollFrame:SetScript("OnMouseUp", OnMouseUp)
+ scrollFrame:SetScript("OnReceiveDrag", OnReceiveDrag)
+ scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
+ scrollFrame:HookScript("OnVerticalScroll", OnVerticalScroll)
+
+ local editBox = CreateFrame("EditBox", ("%s%dEdit"):format(Type, widgetNum), scrollFrame)
+ editBox:SetAllPoints()
+ editBox:SetFontObject(ChatFontNormal)
+ editBox:SetMultiLine(true)
+ editBox:EnableMouse(true)
+ editBox:SetAutoFocus(false)
+ editBox:SetCountInvisibleLetters(false)
+ editBox:SetScript("OnCursorChanged", OnCursorChanged)
+ editBox:SetScript("OnEditFocusLost", OnEditFocusLost)
+ editBox:SetScript("OnEnter", OnEnter)
+ editBox:SetScript("OnEscapePressed", editBox.ClearFocus)
+ editBox:SetScript("OnLeave", OnLeave)
+ editBox:SetScript("OnMouseDown", OnReceiveDrag)
+ editBox:SetScript("OnReceiveDrag", OnReceiveDrag)
+ editBox:SetScript("OnTextChanged", OnTextChanged)
+ editBox:SetScript("OnTextSet", OnTextSet)
+ editBox:SetScript("OnEditFocusGained", OnEditFocusGained)
+
+
+ scrollFrame:SetScrollChild(editBox)
+
+ local widget = {
+ button = button,
+ editBox = editBox,
+ frame = frame,
+ label = label,
+ labelHeight = 10,
+ numlines = 4,
+ scrollBar = scrollBar,
+ scrollBG = scrollBG,
+ scrollFrame = scrollFrame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ button.obj, editBox.obj, scrollFrame.obj = widget, widget, widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,285 @@
+--[[-----------------------------------------------------------------------------
+Slider Widget
+Graphical Slider, like, for Range values.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Slider", 21
+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 min, max, floor = math.min, math.max, math.floor
+local tonumber, pairs = tonumber, pairs
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function UpdateText(self)
+ local value = self.value or 0
+ if self.ispercent then
+ self.editbox:SetText(("%s%%"):format(floor(value * 1000 + 0.5) / 10))
+ else
+ self.editbox:SetText(floor(value * 100 + 0.5) / 100)
+ end
+end
+
+local function UpdateLabels(self)
+ local min, max = (self.min or 0), (self.max or 100)
+ if self.ispercent then
+ self.lowtext:SetFormattedText("%s%%", (min * 100))
+ self.hightext:SetFormattedText("%s%%", (max * 100))
+ else
+ self.lowtext:SetText(min)
+ self.hightext:SetText(max)
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Frame_OnMouseDown(frame)
+ frame.obj.slider:EnableMouseWheel(true)
+ AceGUI:ClearFocus()
+end
+
+local function Slider_OnValueChanged(frame)
+ local self = frame.obj
+ if not frame.setup then
+ local newvalue = frame:GetValue()
+ if self.step and self.step > 0 then
+ local min_value = self.min or 0
+ newvalue = floor((newvalue - min_value) / self.step + 0.5) * self.step + min_value
+ end
+ if newvalue ~= self.value and not self.disabled then
+ self.value = newvalue
+ self:Fire("OnValueChanged", newvalue)
+ end
+ if self.value then
+ UpdateText(self)
+ end
+ end
+end
+
+local function Slider_OnMouseUp(frame)
+ local self = frame.obj
+ self:Fire("OnMouseUp", self.value)
+end
+
+local function Slider_OnMouseWheel(frame, v)
+ local self = frame.obj
+ if not self.disabled then
+ local value = self.value
+ if v > 0 then
+ value = min(value + (self.step or 1), self.max)
+ else
+ value = max(value - (self.step or 1), self.min)
+ end
+ self.slider:SetValue(value)
+ end
+end
+
+local function EditBox_OnEscapePressed(frame)
+ frame:ClearFocus()
+end
+
+local function EditBox_OnEnterPressed(frame)
+ local self = frame.obj
+ local value = frame:GetText()
+ if self.ispercent then
+ value = value:gsub('%%', '')
+ value = tonumber(value) / 100
+ else
+ value = tonumber(value)
+ end
+
+ if value then
+ PlaySound("igMainMenuOptionCheckBoxOn")
+ self.slider:SetValue(value)
+ self:Fire("OnMouseUp", value)
+ end
+end
+
+local function EditBox_OnEnter(frame)
+ frame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
+end
+
+local function EditBox_OnLeave(frame)
+ frame:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.8)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetWidth(200)
+ self:SetHeight(44)
+ self:SetDisabled(false)
+ self:SetIsPercent(nil)
+ self:SetSliderValues(0,100,1)
+ self:SetValue(0)
+ self.slider:EnableMouseWheel(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.slider:EnableMouse(false)
+ self.label:SetTextColor(.5, .5, .5)
+ self.hightext:SetTextColor(.5, .5, .5)
+ self.lowtext:SetTextColor(.5, .5, .5)
+ --self.valuetext:SetTextColor(.5, .5, .5)
+ self.editbox:SetTextColor(.5, .5, .5)
+ self.editbox:EnableMouse(false)
+ self.editbox:ClearFocus()
+ else
+ self.slider:EnableMouse(true)
+ self.label:SetTextColor(1, .82, 0)
+ self.hightext:SetTextColor(1, 1, 1)
+ self.lowtext:SetTextColor(1, 1, 1)
+ --self.valuetext:SetTextColor(1, 1, 1)
+ self.editbox:SetTextColor(1, 1, 1)
+ self.editbox:EnableMouse(true)
+ end
+ end,
+
+ ["SetValue"] = function(self, value)
+ self.slider.setup = true
+ self.slider:SetValue(value)
+ self.value = value
+ UpdateText(self)
+ self.slider.setup = nil
+ end,
+
+ ["GetValue"] = function(self)
+ return self.value
+ end,
+
+ ["SetLabel"] = function(self, text)
+ self.label:SetText(text)
+ end,
+
+ ["SetSliderValues"] = function(self, min, max, step)
+ local frame = self.slider
+ frame.setup = true
+ self.min = min
+ self.max = max
+ self.step = step
+ frame:SetMinMaxValues(min or 0,max or 100)
+ UpdateLabels(self)
+ frame:SetValueStep(step or 1)
+ if self.value then
+ frame:SetValue(self.value)
+ end
+ frame.setup = nil
+ end,
+
+ ["SetIsPercent"] = function(self, value)
+ self.ispercent = value
+ UpdateLabels(self)
+ UpdateText(self)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local SliderBackdrop = {
+ bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
+ edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
+ tile = true, tileSize = 8, edgeSize = 8,
+ insets = { left = 3, right = 3, top = 6, bottom = 6 }
+}
+
+local ManualBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ tile = true, edgeSize = 1, tileSize = 5,
+}
+
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnMouseDown", Frame_OnMouseDown)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ label:SetPoint("TOPLEFT")
+ label:SetPoint("TOPRIGHT")
+ label:SetJustifyH("CENTER")
+ label:SetHeight(15)
+
+ local slider = CreateFrame("Slider", nil, frame)
+ slider:SetOrientation("HORIZONTAL")
+ slider:SetHeight(15)
+ slider:SetHitRectInsets(0, 0, -10, 0)
+ slider:SetBackdrop(SliderBackdrop)
+ slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
+ slider:SetPoint("TOP", label, "BOTTOM")
+ slider:SetPoint("LEFT", 3, 0)
+ slider:SetPoint("RIGHT", -3, 0)
+ slider:SetValue(0)
+ slider:SetScript("OnValueChanged",Slider_OnValueChanged)
+ slider:SetScript("OnEnter", Control_OnEnter)
+ slider:SetScript("OnLeave", Control_OnLeave)
+ slider:SetScript("OnMouseUp", Slider_OnMouseUp)
+ slider:SetScript("OnMouseWheel", Slider_OnMouseWheel)
+
+ local lowtext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+ lowtext:SetPoint("TOPLEFT", slider, "BOTTOMLEFT", 2, 3)
+
+ local hightext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+ hightext:SetPoint("TOPRIGHT", slider, "BOTTOMRIGHT", -2, 3)
+
+ local editbox = CreateFrame("EditBox", nil, frame)
+ editbox:SetAutoFocus(false)
+ editbox:SetFontObject(GameFontHighlightSmall)
+ editbox:SetPoint("TOP", slider, "BOTTOM")
+ editbox:SetHeight(14)
+ editbox:SetWidth(70)
+ editbox:SetJustifyH("CENTER")
+ editbox:EnableMouse(true)
+ editbox:SetBackdrop(ManualBackdrop)
+ editbox:SetBackdropColor(0, 0, 0, 0.5)
+ editbox:SetBackdropBorderColor(0.3, 0.3, 0.30, 0.80)
+ editbox:SetScript("OnEnter", EditBox_OnEnter)
+ editbox:SetScript("OnLeave", EditBox_OnLeave)
+ editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
+ editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
+
+ local widget = {
+ label = label,
+ slider = slider,
+ lowtext = lowtext,
+ hightext = hightext,
+ editbox = editbox,
+ alignoffset = 25,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ slider.obj, editbox.obj = widget, widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type,Constructor,Version)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceLocale-3.0/AceLocale-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceLocale-3.0/AceLocale-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,137 @@
+--- **AceLocale-3.0** manages localization in addons, allowing for multiple locale to be registered with fallback to the base locale for untranslated strings.
+-- @class file
+-- @name AceLocale-3.0
+-- @release $Id: AceLocale-3.0.lua 1035 2011-07-09 03:20:13Z kaelten $
+local MAJOR,MINOR = "AceLocale-3.0", 6
+
+local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceLocale then return end -- no upgrade needed
+
+-- Lua APIs
+local assert, tostring, error = assert, tostring, error
+local getmetatable, setmetatable, rawset, rawget = getmetatable, setmetatable, rawset, rawget
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GAME_LOCALE, geterrorhandler
+
+local gameLocale = GetLocale()
+if gameLocale == "enGB" then
+ gameLocale = "enUS"
+end
+
+AceLocale.apps = AceLocale.apps or {} -- array of ["AppName"]=localetableref
+AceLocale.appnames = AceLocale.appnames or {} -- array of [localetableref]="AppName"
+
+-- This metatable is used on all tables returned from GetLocale
+local readmeta = {
+ __index = function(self, key) -- requesting totally unknown entries: fire off a nonbreaking error and return key
+ rawset(self, key, key) -- only need to see the warning once, really
+ geterrorhandler()(MAJOR..": "..tostring(AceLocale.appnames[self])..": Missing entry for '"..tostring(key).."'")
+ return key
+ end
+}
+
+-- This metatable is used on all tables returned from GetLocale if the silent flag is true, it does not issue a warning on unknown keys
+local readmetasilent = {
+ __index = function(self, key) -- requesting totally unknown entries: return key
+ rawset(self, key, key) -- only need to invoke this function once
+ return key
+ end
+}
+
+-- Remember the locale table being registered right now (it gets set by :NewLocale())
+-- NOTE: Do never try to register 2 locale tables at once and mix their definition.
+local registering
+
+-- local assert false function
+local assertfalse = function() assert(false) end
+
+-- This metatable proxy is used when registering nondefault locales
+local writeproxy = setmetatable({}, {
+ __newindex = function(self, key, value)
+ rawset(registering, key, value == true and key or value) -- assigning values: replace 'true' with key string
+ end,
+ __index = assertfalse
+})
+
+-- This metatable proxy is used when registering the default locale.
+-- It refuses to overwrite existing values
+-- Reason 1: Allows loading locales in any order
+-- Reason 2: If 2 modules have the same string, but only the first one to be
+-- loaded has a translation for the current locale, the translation
+-- doesn't get overwritten.
+--
+local writedefaultproxy = setmetatable({}, {
+ __newindex = function(self, key, value)
+ if not rawget(registering, key) then
+ rawset(registering, key, value == true and key or value)
+ end
+ end,
+ __index = assertfalse
+})
+
+--- Register a new locale (or extend an existing one) for the specified application.
+-- :NewLocale will return a table you can fill your locale into, or nil if the locale isn't needed for the players
+-- game locale.
+-- @paramsig application, locale[, isDefault[, silent]]
+-- @param application Unique name of addon / module
+-- @param locale Name of the locale to register, e.g. "enUS", "deDE", etc.
+-- @param isDefault If this is the default locale being registered (your addon is written in this language, generally enUS)
+-- @param silent If true, the locale will not issue warnings for missing keys. Must be set on the first locale registered. If set to "raw", nils will be returned for unknown keys (no metatable used).
+-- @usage
+-- -- enUS.lua
+-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "enUS", true)
+-- L["string1"] = true
+--
+-- -- deDE.lua
+-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "deDE")
+-- if not L then return end
+-- L["string1"] = "Zeichenkette1"
+-- @return Locale Table to add localizations to, or nil if the current locale is not required.
+function AceLocale:NewLocale(application, locale, isDefault, silent)
+
+ -- GAME_LOCALE allows translators to test translations of addons without having that wow client installed
+ local gameLocale = GAME_LOCALE or gameLocale
+
+ local app = AceLocale.apps[application]
+
+ if silent and app and getmetatable(app) ~= readmetasilent then
+ geterrorhandler()("Usage: NewLocale(application, locale[, isDefault[, silent]]): 'silent' must be specified for the first locale registered")
+ end
+
+ if not app then
+ if silent=="raw" then
+ app = {}
+ else
+ app = setmetatable({}, silent and readmetasilent or readmeta)
+ end
+ AceLocale.apps[application] = app
+ AceLocale.appnames[app] = application
+ end
+
+ if locale ~= gameLocale and not isDefault then
+ return -- nop, we don't need these translations
+ end
+
+ registering = app -- remember globally for writeproxy and writedefaultproxy
+
+ if isDefault then
+ return writedefaultproxy
+ end
+
+ return writeproxy
+end
+
+--- Returns localizations for the current locale (or default locale if translations are missing).
+-- Errors if nothing is registered (spank developer, not just a missing translation)
+-- @param application Unique name of addon / module
+-- @param silent If true, the locale is optional, silently return nil if it's not found (defaults to false, optional)
+-- @return The locale table for the current language.
+function AceLocale:GetLocale(application, silent)
+ if not silent and not AceLocale.apps[application] then
+ error("Usage: GetLocale(application[, silent]): 'application' - No locales registered for '"..tostring(application).."'", 2)
+ end
+ return AceLocale.apps[application]
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceLocale-3.0/AceLocale-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceLocale-3.0/AceLocale-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceSerializer-3.0/AceSerializer-3.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceSerializer-3.0/AceSerializer-3.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,283 @@
+--- **AceSerializer-3.0** can serialize any variable (except functions or userdata) into a string format,
+-- that can be send over the addon comm channel. AceSerializer was designed to keep all data intact, especially
+-- very large numbers or floating point numbers, and table structures. The only caveat currently is, that multiple
+-- references to the same table will be send individually.
+--
+-- **AceSerializer-3.0** can be embeded into your addon, either explicitly by calling AceSerializer:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceSerializer itself.\\
+-- It is recommended to embed AceSerializer, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceSerializer.
+-- @class file
+-- @name AceSerializer-3.0
+-- @release $Id: AceSerializer-3.0.lua 1038 2011-10-03 01:39:58Z mikk $
+local MAJOR,MINOR = "AceSerializer-3.0", 4
+local AceSerializer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceSerializer then return end
+
+-- Lua APIs
+local strbyte, strchar, gsub, gmatch, format = string.byte, string.char, string.gsub, string.gmatch, string.format
+local assert, error, pcall = assert, error, pcall
+local type, tostring, tonumber = type, tostring, tonumber
+local pairs, select, frexp = pairs, select, math.frexp
+local tconcat = table.concat
+
+-- quick copies of string representations of wonky numbers
+local inf = math.huge
+
+local serNaN -- can't do this in 4.3, see ace3 ticket 268
+local serInf = tostring(inf)
+local serNegInf = tostring(-inf)
+
+
+-- Serialization functions
+
+local function SerializeStringHelper(ch) -- Used by SerializeValue for strings
+ -- We use \126 ("~") as an escape character for all nonprints plus a few more
+ local n = strbyte(ch)
+ if n==30 then -- v3 / ticket 115: catch a nonprint that ends up being "~^" when encoded... DOH
+ return "\126\122"
+ elseif n<=32 then -- nonprint + space
+ return "\126"..strchar(n+64)
+ elseif n==94 then -- value separator
+ return "\126\125"
+ elseif n==126 then -- our own escape character
+ return "\126\124"
+ elseif n==127 then -- nonprint (DEL)
+ return "\126\123"
+ else
+ assert(false) -- can't be reached if caller uses a sane regex
+ end
+end
+
+local function SerializeValue(v, res, nres)
+ -- We use "^" as a value separator, followed by one byte for type indicator
+ local t=type(v)
+
+ if t=="string" then -- ^S = string (escaped to remove nonprints, "^"s, etc)
+ res[nres+1] = "^S"
+ res[nres+2] = gsub(v,"[%c \94\126\127]", SerializeStringHelper)
+ nres=nres+2
+
+ elseif t=="number" then -- ^N = number (just tostring()ed) or ^F (float components)
+ local str = tostring(v)
+ if tonumber(str)==v --[[not in 4.3 or str==serNaN]] or str==serInf or str==serNegInf then
+ -- translates just fine, transmit as-is
+ res[nres+1] = "^N"
+ res[nres+2] = str
+ nres=nres+2
+ else
+ local m,e = frexp(v)
+ res[nres+1] = "^F"
+ res[nres+2] = format("%.0f",m*2^53) -- force mantissa to become integer (it's originally 0.5--0.9999)
+ res[nres+3] = "^f"
+ res[nres+4] = tostring(e-53) -- adjust exponent to counteract mantissa manipulation
+ nres=nres+4
+ end
+
+ elseif t=="table" then -- ^T...^t = table (list of key,value pairs)
+ nres=nres+1
+ res[nres] = "^T"
+ for k,v in pairs(v) do
+ nres = SerializeValue(k, res, nres)
+ nres = SerializeValue(v, res, nres)
+ end
+ nres=nres+1
+ res[nres] = "^t"
+
+ elseif t=="boolean" then -- ^B = true, ^b = false
+ nres=nres+1
+ if v then
+ res[nres] = "^B" -- true
+ else
+ res[nres] = "^b" -- false
+ end
+
+ elseif t=="nil" then -- ^Z = nil (zero, "N" was taken :P)
+ nres=nres+1
+ res[nres] = "^Z"
+
+ else
+ error(MAJOR..": Cannot serialize a value of type '"..t.."'") -- can't produce error on right level, this is wildly recursive
+ end
+
+ return nres
+end
+
+
+
+local serializeTbl = { "^1" } -- "^1" = Hi, I'm data serialized by AceSerializer protocol rev 1
+
+--- Serialize the data passed into the function.
+-- Takes a list of values (strings, numbers, booleans, nils, tables)
+-- and returns it in serialized form (a string).\\
+-- May throw errors on invalid data types.
+-- @param ... List of values to serialize
+-- @return The data in its serialized form (string)
+function AceSerializer:Serialize(...)
+ local nres = 1
+
+ for i=1,select("#", ...) do
+ local v = select(i, ...)
+ nres = SerializeValue(v, serializeTbl, nres)
+ end
+
+ serializeTbl[nres+1] = "^^" -- "^^" = End of serialized data
+
+ return tconcat(serializeTbl, "", 1, nres+1)
+end
+
+-- Deserialization functions
+local function DeserializeStringHelper(escape)
+ if escape<"~\122" then
+ return strchar(strbyte(escape,2,2)-64)
+ elseif escape=="~\122" then -- v3 / ticket 115: special case encode since 30+64=94 ("^") - OOPS.
+ return "\030"
+ elseif escape=="~\123" then
+ return "\127"
+ elseif escape=="~\124" then
+ return "\126"
+ elseif escape=="~\125" then
+ return "\94"
+ end
+ error("DeserializeStringHelper got called for '"..escape.."'?!?") -- can't be reached unless regex is screwed up
+end
+
+local function DeserializeNumberHelper(number)
+ --[[ not in 4.3 if number == serNaN then
+ return 0/0
+ else]]if number == serNegInf then
+ return -inf
+ elseif number == serInf then
+ return inf
+ else
+ return tonumber(number)
+ end
+end
+
+-- DeserializeValue: worker function for :Deserialize()
+-- It works in two modes:
+-- Main (top-level) mode: Deserialize a list of values and return them all
+-- Recursive (table) mode: Deserialize only a single value (_may_ of course be another table with lots of subvalues in it)
+--
+-- The function _always_ works recursively due to having to build a list of values to return
+--
+-- Callers are expected to pcall(DeserializeValue) to trap errors
+
+local function DeserializeValue(iter,single,ctl,data)
+
+ if not single then
+ ctl,data = iter()
+ end
+
+ if not ctl then
+ error("Supplied data misses AceSerializer terminator ('^^')")
+ end
+
+ if ctl=="^^" then
+ -- ignore extraneous data
+ return
+ end
+
+ local res
+
+ if ctl=="^S" then
+ res = gsub(data, "~.", DeserializeStringHelper)
+ elseif ctl=="^N" then
+ res = DeserializeNumberHelper(data)
+ if not res then
+ error("Invalid serialized number: '"..tostring(data).."'")
+ end
+ elseif ctl=="^F" then -- ^F^f
+ local ctl2,e = iter()
+ if ctl2~="^f" then
+ error("Invalid serialized floating-point number, expected '^f', not '"..tostring(ctl2).."'")
+ end
+ local m=tonumber(data)
+ e=tonumber(e)
+ if not (m and e) then
+ error("Invalid serialized floating-point number, expected mantissa and exponent, got '"..tostring(m).."' and '"..tostring(e).."'")
+ end
+ res = m*(2^e)
+ elseif ctl=="^B" then -- yeah yeah ignore data portion
+ res = true
+ elseif ctl=="^b" then -- yeah yeah ignore data portion
+ res = false
+ elseif ctl=="^Z" then -- yeah yeah ignore data portion
+ res = nil
+ elseif ctl=="^T" then
+ -- ignore ^T's data, future extensibility?
+ res = {}
+ local k,v
+ while true do
+ ctl,data = iter()
+ if ctl=="^t" then break end -- ignore ^t's data
+ k = DeserializeValue(iter,true,ctl,data)
+ if k==nil then
+ error("Invalid AceSerializer table format (no table end marker)")
+ end
+ ctl,data = iter()
+ v = DeserializeValue(iter,true,ctl,data)
+ if v==nil then
+ error("Invalid AceSerializer table format (no table end marker)")
+ end
+ res[k]=v
+ end
+ else
+ error("Invalid AceSerializer control code '"..ctl.."'")
+ end
+
+ if not single then
+ return res,DeserializeValue(iter)
+ else
+ return res
+ end
+end
+
+--- Deserializes the data into its original values.
+-- Accepts serialized data, ignoring all control characters and whitespace.
+-- @param str The serialized data (from :Serialize)
+-- @return true followed by a list of values, OR false followed by an error message
+function AceSerializer:Deserialize(str)
+ str = gsub(str, "[%c ]", "") -- ignore all control characters; nice for embedding in email and stuff
+
+ local iter = gmatch(str, "(^.)([^^]*)") -- Any ^x followed by string of non-^
+ local ctl,data = iter()
+ if not ctl or ctl~="^1" then
+ -- we purposefully ignore the data portion of the start code, it can be used as an extension mechanism
+ return false, "Supplied data is not AceSerializer data (rev 1)"
+ end
+
+ return pcall(DeserializeValue, iter)
+end
+
+
+----------------------------------------
+-- Base library stuff
+----------------------------------------
+
+AceSerializer.internals = { -- for test scripts
+ SerializeValue = SerializeValue,
+ SerializeStringHelper = SerializeStringHelper,
+}
+
+local mixins = {
+ "Serialize",
+ "Deserialize",
+}
+
+AceSerializer.embeds = AceSerializer.embeds or {}
+
+function AceSerializer:Embed(target)
+ for k, v in pairs(mixins) do
+ target[v] = self[v]
+ end
+ self.embeds[target] = true
+ return target
+end
+
+-- Update embeds
+for target, v in pairs(AceSerializer.embeds) do
+ AceSerializer:Embed(target)
+end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/AceSerializer-3.0/AceSerializer-3.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceSerializer-3.0/AceSerializer-3.0.xml Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
--- a/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua Tue Feb 24 21:50:13 2015 -0800
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua Fri Jun 05 11:05:15 2015 -0700
@@ -1,4 +1,4 @@
---[[ $Id: CallbackHandler-1.0.lua 14 2010-08-09 00:43:38Z mikk $ ]]
+--[[ $Id: CallbackHandler-1.0.lua 965 2010-08-09 00:47:52Z mikk $ ]]
local MAJOR, MINOR = "CallbackHandler-1.0", 6
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
--- a/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua Tue Feb 24 21:50:13 2015 -0800
+++ b/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua Fri Jun 05 11:05:15 2015 -0700
@@ -1,90 +1,90 @@
-
-assert(LibStub, "LibDataBroker-1.1 requires LibStub")
-assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
-
-local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
-if not lib then return end
-oldminor = oldminor or 0
-
-
-lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
-lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
-local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
-
-if oldminor < 2 then
- lib.domt = {
- __metatable = "access denied",
- __index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
- }
-end
-
-if oldminor < 3 then
- lib.domt.__newindex = function(self, key, value)
- if not attributestorage[self] then attributestorage[self] = {} end
- if attributestorage[self][key] == value then return end
- attributestorage[self][key] = value
- local name = namestorage[self]
- if not name then return end
- callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
- callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
- callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
- callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
- end
-end
-
-if oldminor < 2 then
- function lib:NewDataObject(name, dataobj)
- if self.proxystorage[name] then return end
-
- if dataobj then
- assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
- self.attributestorage[dataobj] = {}
- for i,v in pairs(dataobj) do
- self.attributestorage[dataobj][i] = v
- dataobj[i] = nil
- end
- end
- dataobj = setmetatable(dataobj or {}, self.domt)
- self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
- self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
- return dataobj
- end
-end
-
-if oldminor < 1 then
- function lib:DataObjectIterator()
- return pairs(self.proxystorage)
- end
-
- function lib:GetDataObjectByName(dataobjectname)
- return self.proxystorage[dataobjectname]
- end
-
- function lib:GetNameByDataObject(dataobject)
- return self.namestorage[dataobject]
- end
-end
-
-if oldminor < 4 then
- local next = pairs(attributestorage)
- function lib:pairs(dataobject_or_name)
- local t = type(dataobject_or_name)
- assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
-
- local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
- assert(attributestorage[dataobj], "Data object not found")
-
- return next, attributestorage[dataobj], nil
- end
-
- local ipairs_iter = ipairs(attributestorage)
- function lib:ipairs(dataobject_or_name)
- local t = type(dataobject_or_name)
- assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
-
- local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
- assert(attributestorage[dataobj], "Data object not found")
-
- return ipairs_iter, attributestorage[dataobj], 0
- end
-end
+
+assert(LibStub, "LibDataBroker-1.1 requires LibStub")
+assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
+
+local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
+if not lib then return end
+oldminor = oldminor or 0
+
+
+lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
+lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
+local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
+
+if oldminor < 2 then
+ lib.domt = {
+ __metatable = "access denied",
+ __index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
+ }
+end
+
+if oldminor < 3 then
+ lib.domt.__newindex = function(self, key, value)
+ if not attributestorage[self] then attributestorage[self] = {} end
+ if attributestorage[self][key] == value then return end
+ attributestorage[self][key] = value
+ local name = namestorage[self]
+ if not name then return end
+ callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
+ end
+end
+
+if oldminor < 2 then
+ function lib:NewDataObject(name, dataobj)
+ if self.proxystorage[name] then return end
+
+ if dataobj then
+ assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
+ self.attributestorage[dataobj] = {}
+ for i,v in pairs(dataobj) do
+ self.attributestorage[dataobj][i] = v
+ dataobj[i] = nil
+ end
+ end
+ dataobj = setmetatable(dataobj or {}, self.domt)
+ self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
+ self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
+ return dataobj
+ end
+end
+
+if oldminor < 1 then
+ function lib:DataObjectIterator()
+ return pairs(self.proxystorage)
+ end
+
+ function lib:GetDataObjectByName(dataobjectname)
+ return self.proxystorage[dataobjectname]
+ end
+
+ function lib:GetNameByDataObject(dataobject)
+ return self.namestorage[dataobject]
+ end
+end
+
+if oldminor < 4 then
+ local next = pairs(attributestorage)
+ function lib:pairs(dataobject_or_name)
+ local t = type(dataobject_or_name)
+ assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
+
+ local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
+ assert(attributestorage[dataobj], "Data object not found")
+
+ return next, attributestorage[dataobj], nil
+ end
+
+ local ipairs_iter = ipairs(attributestorage)
+ function lib:ipairs(dataobject_or_name)
+ local t = type(dataobject_or_name)
+ assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
+
+ local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
+ assert(attributestorage[dataobj], "Data object not found")
+
+ return ipairs_iter, attributestorage[dataobj], 0
+ end
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibDataBroker-1.1/README.textile
--- a/Libs/LibDataBroker-1.1/README.textile Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-LibDataBroker is a small WoW addon library designed to provide a "MVC":http://en.wikipedia.org/wiki/Model-view-controller interface for use in various addons.
-LDB's primary goal is to "detach" plugins for TitanPanel and FuBar from the display addon.
-Plugins can provide data into a simple table, and display addons can receive callbacks to refresh their display of this data.
-LDB also provides a place for addons to register "quicklaunch" functions, removing the need for authors to embed many large libraries to create minimap buttons.
-Users who do not wish to be "plagued" by these buttons simply do not install an addon to render them.
-
-Due to it's simple generic design, LDB can be used for any design where you wish to have an addon notified of changes to a table.
-
-h2. Links
-
-* "API documentation":http://github.com/tekkub/libdatabroker-1-1/wikis/api
-* "Data specifications":http://github.com/tekkub/libdatabroker-1-1/wikis/data-specifications
-* "Addons using LDB":http://github.com/tekkub/libdatabroker-1-1/wikis/addons-using-ldb
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibStub/LibStub.lua
--- a/Libs/LibStub/LibStub.lua Tue Feb 24 21:50:13 2015 -0800
+++ b/Libs/LibStub/LibStub.lua Fri Jun 05 11:05:15 2015 -0700
@@ -1,22 +1,13 @@
--- $Id: LibStub.lua 76 2007-09-03 01:50:17Z mikk $
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
--- LibStub is hereby placed in the Public Domain
--- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
local LibStub = _G[LIBSTUB_MAJOR]
--- Check to see is this version of the stub is obsolete
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
- -- LibStub:NewLibrary(major, minor)
- -- major (string) - the major version of the library
- -- minor (string or number ) - the minor version of the library
- --
- -- returns nil if a newer or same version of the lib is already present
- -- returns empty library object or old library object if upgrade is needed
function LibStub:NewLibrary(major, minor)
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
@@ -27,12 +18,6 @@
return self.libs[major], oldminor
end
- -- LibStub:GetLibrary(major, [silent])
- -- major (string) - the major version of the library
- -- silent (boolean) - if true, library is optional, silently return nil if its not found
- --
- -- throws an error if the library can not be found (except silent is set)
- -- returns the library object if found
function LibStub:GetLibrary(major, silent)
if not self.libs[major] and not silent then
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
@@ -40,12 +25,6 @@
return self.libs[major], self.minors[major]
end
- -- LibStub:IterateLibraries()
- --
- -- Returns an iterator for the currently registered libraries
- function LibStub:IterateLibraries()
- return pairs(self.libs)
- end
-
+ function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibStub/LibStub.toc
--- a/Libs/LibStub/LibStub.toc Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-## Interface: 50001
-## Title: Lib: LibStub
-## Notes: Universal Library Stub
-## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel
-## X-Website: http://www.wowace.com/addons/libstub/
-## X-Category: Library
-## X-License: Public Domain
-## X-Curse-Packaged-Version: 1.0.3-50001
-## X-Curse-Project-Name: LibStub
-## X-Curse-Project-ID: libstub
-## X-Curse-Repository-ID: wow/libstub/mainline
-
-LibStub.lua
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibStub/tests/test.lua
--- a/Libs/LibStub/tests/test.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-debugstack = debug.traceback
-strmatch = string.match
-
-loadfile("../LibStub.lua")()
-
-local lib, oldMinor = LibStub:NewLibrary("Pants", 1) -- make a new thingy
-assert(lib) -- should return the library table
-assert(not oldMinor) -- should not return the old minor, since it didn't exist
-
--- the following is to create data and then be able to check if the same data exists after the fact
-function lib:MyMethod()
-end
-local MyMethod = lib.MyMethod
-lib.MyTable = {}
-local MyTable = lib.MyTable
-
-local newLib, newOldMinor = LibStub:NewLibrary("Pants", 1) -- try to register a library with the same version, should silently fail
-assert(not newLib) -- should not return since out of date
-
-local newLib, newOldMinor = LibStub:NewLibrary("Pants", 0) -- try to register a library with a previous, should silently fail
-assert(not newLib) -- should not return since out of date
-
-local newLib, newOldMinor = LibStub:NewLibrary("Pants", 2) -- register a new version
-assert(newLib) -- library table
-assert(rawequal(newLib, lib)) -- should be the same reference as the previous
-assert(newOldMinor == 1) -- should return the minor version of the previous version
-
-assert(rawequal(lib.MyMethod, MyMethod)) -- verify that values were saved
-assert(rawequal(lib.MyTable, MyTable)) -- verify that values were saved
-
-local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 3 Blah") -- register a new version with a string minor version (instead of a number)
-assert(newLib) -- library table
-assert(newOldMinor == 2) -- previous version was 2
-
-local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 4 and please ignore 15 Blah") -- register a new version with a string minor version (instead of a number)
-assert(newLib)
-assert(newOldMinor == 3) -- previous version was 3 (even though it gave a string)
-
-local newLib, newOldMinor = LibStub:NewLibrary("Pants", 5) -- register a new library, using a normal number instead of a string
-assert(newLib)
-assert(newOldMinor == 4) -- previous version was 4 (even though it gave a string)
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibStub/tests/test2.lua
--- a/Libs/LibStub/tests/test2.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-debugstack = debug.traceback
-strmatch = string.match
-
-loadfile("../LibStub.lua")()
-
-for major, library in LibStub:IterateLibraries() do
- -- check that MyLib doesn't exist yet, by iterating through all the libraries
- assert(major ~= "MyLib")
-end
-
-assert(not LibStub:GetLibrary("MyLib", true)) -- check that MyLib doesn't exist yet by direct checking
-assert(not pcall(LibStub.GetLibrary, LibStub, "MyLib")) -- don't silently fail, thus it should raise an error.
-local lib = LibStub:NewLibrary("MyLib", 1) -- create the lib
-assert(lib) -- check it exists
-assert(rawequal(LibStub:GetLibrary("MyLib"), lib)) -- verify that :GetLibrary("MyLib") properly equals the lib reference
-
-assert(LibStub:NewLibrary("MyLib", 2)) -- create a new version
-
-local count=0
-for major, library in LibStub:IterateLibraries() do
- -- check that MyLib exists somewhere in the libraries, by iterating through all the libraries
- if major == "MyLib" then -- we found it!
- count = count +1
- assert(rawequal(library, lib)) -- verify that the references are equal
- end
-end
-assert(count == 1) -- verify that we actually found it, and only once
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibStub/tests/test3.lua
--- a/Libs/LibStub/tests/test3.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-debugstack = debug.traceback
-strmatch = string.match
-
-loadfile("../LibStub.lua")()
-
-local proxy = newproxy() -- non-string
-
-assert(not pcall(LibStub.NewLibrary, LibStub, proxy, 1)) -- should error, proxy is not a string, it's userdata
-local success, ret = pcall(LibStub.GetLibrary, proxy, true)
-assert(not success or not ret) -- either error because proxy is not a string or because it's not actually registered.
-
-assert(not pcall(LibStub.NewLibrary, LibStub, "Something", "No number in here")) -- should error, minor has no string in it.
-
-assert(not LibStub:GetLibrary("Something", true)) -- shouldn't've created it from the above statement
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Libs/LibStub/tests/test4.lua
--- a/Libs/LibStub/tests/test4.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-debugstack = debug.traceback
-strmatch = string.match
-
-loadfile("../LibStub.lua")()
-
-
--- Pretend like loaded libstub is old and doesn't have :IterateLibraries
-assert(LibStub.minor)
-LibStub.minor = LibStub.minor - 0.0001
-LibStub.IterateLibraries = nil
-
-loadfile("../LibStub.lua")()
-
-assert(type(LibStub.IterateLibraries)=="function")
-
-
--- Now pretend that we're the same version -- :IterateLibraries should NOT be re-created
-LibStub.IterateLibraries = 123
-
-loadfile("../LibStub.lua")()
-
-assert(LibStub.IterateLibraries == 123)
-
-
--- Now pretend that a newer version is loaded -- :IterateLibraries should NOT be re-created
-LibStub.minor = LibStub.minor + 0.0001
-
-loadfile("../LibStub.lua")()
-
-assert(LibStub.IterateLibraries == 123)
-
-
--- Again with a huge number
-LibStub.minor = LibStub.minor + 1234567890
-
-loadfile("../LibStub.lua")()
-
-assert(LibStub.IterateLibraries == 123)
-
-
-print("OK")
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Loot.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Loot.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,1037 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _frameLoot
+local _panelLoot
+local _selectedIndex
+local _disenchant = {}
+local _rankPanels
+local _lootButtons
+
+local _widthItemList = 220
+local _widthRankList = 640
+local _widthSpacing = 10
+local _widthRank = 48
+local _widthRankBar = 195
+local _widthRollType = 45
+local _widthColPadding = 8
+local _widthRollExtraSpacing = 4
+
+local _rollTypePos = {
+ Need = 1,
+ Off = 2,
+ Greed = 3,
+ Pass = 4
+}
+
+-- get the index of the loot item that matches up to the specified item
+local function getLootIndex(itemIndex)
+
+ local ranking = Amr.db.global.TeamOpt.Rankings[itemIndex]
+ if not ranking then return nil end
+
+ local itemUniqueId = Amr.GetItemUniqueId(ranking.item)
+ for i = 1, GetNumLootItems() do
+ --local texture, item, quantity, quality, locked = GetLootSlotInfo(i)
+ local lootType = GetLootSlotType(i)
+ if lootType == 1 then
+ local link = GetLootSlotLink(i)
+ local lootItem = Amr.ParseItemLink(link)
+ if Amr.GetItemUniqueId(lootItem) == itemUniqueId then
+ return i, link
+ end
+ end
+ end
+end
+
+local function getLootCandidateIndex(lootIndex, realm, name)
+ -- remove spaces to ensure proper matches, and make all lower
+ realm = string.gsub(realm, "%s+", "")
+ realm = string.lower(realm)
+ name = string.lower(name)
+
+ local nameMatches = {}
+ for i = 1, 40 do
+ local candidate = GetMasterLootCandidate(lootIndex, i)
+ if candidate then
+ local candidateName = candidate
+ local candidateRealm = GetRealmName()
+
+ -- see if realm is in the name
+ local splitPos = string.find(candidateName, "-")
+ if splitPos ~= nil then
+ candidateRealm = string.sub(candidateName, splitPos + 1)
+ candidateName = string.sub(candidateName, 1, splitPos - 1)
+ end
+
+ -- remove spaces to ensure proper matches, and make all lower
+ candidateRealm = string.gsub(candidateRealm, "%s+", "")
+ candidateRealm = string.lower(candidateRealm)
+ candidateName = string.lower(candidateName)
+
+ -- if perfect match then we are done
+ if candidateRealm == realm and candidateName == name then
+ return i
+ end
+
+ if candidateName == name then
+ table.insert(nameMatches, i)
+ end
+ end
+ end
+
+ -- only one player with same name, must be the player of interest
+ if #nameMatches == 1 then
+ return nameMatches[1]
+ end
+
+ -- could not be found or ambiguous
+ return nil
+end
+
+-- helper to send a message telling everyone that an item was just given out
+local function sendGiveLootMessage(itemLink, unitId, isDisenchant)
+ -- some display info
+ local cls, clsEn = UnitClass(unitId)
+ local name = UnitName(unitId)
+
+ local result = isDisenchant and "Disenchant" or (roll and roll.rollType or nil)
+ if result == nil then result = "??" end
+
+ local msg = string.format("%s\n%d\n%s\n%s\n%s\n%s", Amr.LootMessagePrefixes.Give, _selectedIndex, itemLink, result, clsEn, name)
+ Amr:SendAmrCommMessage(msg)
+end
+
+local function onGiveLootClick(widget)
+ local rollIndex = widget:GetUserData("index")
+
+ local rankings = Amr.db.global.TeamOpt.Rankings
+ local ranking = rankings[_selectedIndex]
+ local rank = ranking and ranking.ranks[rollIndex] or nil
+ if rank then
+ local roll = Amr.db.char.TeamOpt.Rolls[_selectedIndex][rollIndex]
+ local isDisenchant = not not _disenchant[_selectedIndex]
+
+ local unitId = Amr:GetUnitId(rank.realm, rank.name)
+ if unitId then
+ -- find the item and loot candidate index
+ local itemIndex, itemLink = getLootIndex(_selectedIndex)
+
+ -- for debugging when don't actually have any loot
+ --itemLink = Amr.CreateItemLink(ranking.item)
+
+ local playerIndex = itemIndex and getLootCandidateIndex(itemIndex, rank.realm, rank.name) or nil
+ if itemIndex and playerIndex then
+ GiveMasterLoot(itemIndex, playerIndex)
+ sendGiveLootMessage(itemLink, unitId, isDisenchant)
+ return
+ end
+
+ end
+ end
+
+ -- if we make it here, we could not give out the item for some reason
+ Amr:Print(L.LootMasterGiveFail)
+end
+
+function Amr:OnLootGiveReceived(parts)
+ if not parts or #parts < 6 then return end
+
+ local rankings = Amr.db.global.TeamOpt.Rankings
+
+ -- index of the item that was given, flag it to hide it from the ui now
+ local index = tonumber(parts[2])
+ local ranking = rankings[index]
+ if ranking then
+ ranking.given = true
+ end
+
+ -- change the selected item index to the next ungiven item
+ for i, obj in ipairs(rankings) do
+ if not obj.given then
+ _selectedIndex = i
+ break
+ end
+ end
+
+ -- add a loot history entry
+ local entry = {
+ link = parts[3],
+ result = parts[4],
+ class = parts[5],
+ name = parts[6]
+ }
+ table.insert(Amr.db.char.TeamOpt.History, entry)
+
+ -- redraw any open windows
+ Amr:RefreshTeamUi()
+ Amr:RefreshLootWindow()
+ Amr:RefreshLootRolls()
+
+ -- if this is the master looter, check if all items have been given out
+ if IsMasterLooter() then
+ local allDone = true
+ for i, ranking in ipairs(rankings) do
+ if not ranking.given then
+ allDone = false
+ break
+ end
+ end
+
+ if allDone then
+ -- send a message indicating that looting is done
+ Amr:SendAmrCommMessage(Amr.LootMessagePrefixes.Finish)
+ end
+ end
+
+end
+
+local function onDisenchantClick()
+ local val = not _disenchant[_selectedIndex]
+ _disenchant[_selectedIndex] = val
+
+ Amr:RefreshLootWindow()
+ Amr:RefreshLootRolls()
+end
+
+local function onRollClick()
+ -- generate a roll for everyone on the current item
+ local rands = {}
+
+ local ranking = Amr.db.global.TeamOpt.Rankings[_selectedIndex]
+ for i, rank in ipairs(ranking.ranks) do
+ local r = math.random(100)
+ rands[i] = r
+ end
+
+ -- transmit the roll data to all group members
+ local msg = string.format("%s\n%d\n%s", Amr.LootMessagePrefixes.Rand, _selectedIndex, Amr:Serialize(rands))
+ Amr:SendAmrCommMessage(msg)
+end
+
+function Amr:OnLootRandReceived(parts)
+ if not parts or #parts < 3 then return end
+
+ local index = tonumber(parts[2])
+ local success, rands = Amr:Deserialize(parts[3])
+ if not index or not success then return end
+
+ local rolls = Amr.db.char.TeamOpt.Rolls[index]
+ for i, r in pairs(rands) do
+ local roll = rolls[i]
+ if not roll then
+ roll = {}
+ rolls[i] = roll
+ end
+
+ roll.rand = r
+ end
+
+ Amr:RefreshLootRolls()
+end
+
+local function onVetoClick(widget)
+ local rollIndex = widget:GetUserData("rollIndex")
+ local rollType = widget:GetUserData("rollType")
+
+ -- acts like a toggle
+ local roll = Amr.db.char.TeamOpt.Rolls[_selectedIndex][rollIndex]
+ local veto = not roll or not roll.vetoes or not roll.vetoes[rollType]
+
+ -- send a message that a veto has been changed
+ local msg = string.format("%s\n%d\n%d\n%s\n%s", Amr.LootMessagePrefixes.Veto, _selectedIndex, rollIndex, rollType, veto and "t" or "f")
+ Amr:SendAmrCommMessage(msg)
+end
+
+function Amr:OnLootVetoReceived(parts)
+ if not parts or #parts < 5 then return end
+
+ local itemIndex = tonumber(parts[2])
+ local rollIndex = tonumber(parts[3])
+ local rollType = parts[4]
+ local veto = parts[5] == "t"
+
+ if itemIndex and rollIndex then
+ local roll = Amr.db.char.TeamOpt.Rolls[itemIndex][rollIndex]
+ if not roll then
+ roll = {}
+ Amr.db.char.TeamOpt.Rolls[itemIndex][rollIndex] = roll
+ end
+
+ if not roll.vetoes then
+ roll.vetoes = {}
+ end
+
+ roll.vetoes[rollType] = veto
+
+ -- if the player chose this option, have to remove it because it has been vetoed
+ if veto and roll.rollType == rollType then
+ roll.rollType = nil
+ end
+
+ Amr:RefreshLootRolls()
+ end
+end
+
+-- a user choice for what they want to do on an item
+local function doRoll(rollType)
+
+ local msg = string.format("%s\n%d\n%s\n%s\n%s", Amr.LootMessagePrefixes.Roll, _selectedIndex, rollType, GetRealmName(), UnitName("player"))
+ Amr:SendAmrCommMessage(msg)
+end
+
+function Amr:OnLootRollReceived(parts)
+ local index = tonumber(parts[2])
+ local rollType = parts[3]
+ local realm = parts[4]
+ local name = parts[5]
+
+ -- for now, this code matches up name/realm to one in the rankings
+ -- TODO: more robust handling of players with same name but different realms in the same group on non-english clients
+ local nameMatches = {}
+ local ranking = Amr.db.global.TeamOpt.Rankings[index]
+ for i, rank in ipairs(ranking.ranks) do
+ if name == rank.name and realm == rank.realm then
+ nameMatches = {}
+ break
+ end
+
+ if name == rank.name then
+ table.insert(nameMatches, rank)
+ end
+ end
+ if #nameMatches == 1 then
+ realm = nameMatches[1].realm
+ name = nameMatches[1].name
+ end
+
+ -- find index of the ranking
+ local rankIndex = nil
+ for i, rank in ipairs(ranking.ranks) do
+ if name == rank.name and realm == rank.realm then
+ rankIndex = i
+ break
+ end
+ end
+
+ if rankIndex then
+ local obj = Amr.db.char.TeamOpt.Rolls[index][rankIndex]
+ if not obj then
+ obj = {}
+ Amr.db.char.TeamOpt.Rolls[index][rankIndex] = obj
+ end
+ obj.rollType = rollType
+ end
+
+ Amr:RefreshLootRolls()
+end
+
+local function renderRollType(rp, rollType, roll, index)
+
+ local icon = rp:GetUserData(rollType)
+ if not icon and roll then
+ -- create icon if we need one
+ icon = AceGUI:Create("AmrUiTextButton")
+ icon:SetWidth(16)
+ icon:SetHeight(16)
+ local pos = _rollTypePos[rollType]
+ local left = _widthRank + _widthColPadding + _widthRankBar + (pos * _widthColPadding) + ((pos - 1) * _widthRollType) + ((_widthRollType - 16) / 2)
+ icon:SetPoint("LEFT", rp.content, "LEFT", left, 0)
+ rp:AddChild(icon)
+ rp:SetUserData(rollType, icon)
+
+ icon:SetUserData("rollType", rollType)
+ icon:SetUserData("rollIndex", index)
+ icon:SetVisible(false)
+
+ icon:SetCallback("OnClick", onVetoClick)
+ end
+
+ if icon then
+ if roll and roll.rollType == rollType then
+ icon:SetVisible(true)
+ icon:SetBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconCheck")
+ icon:SetHoverBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconCheck")
+ elseif roll and roll.vetoes and roll.vetoes[rollType] then
+ icon:SetVisible(true)
+ icon:SetBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconX")
+ icon:SetHoverBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconX")
+ else
+ icon:SetVisible(false)
+ end
+
+ icon:SetDisabled(not IsMasterLooter())
+ end
+
+ -- update button state for this roll type
+ if _lootButtons and _lootButtons[rollType] then
+ _lootButtons[rollType]:SetDisabled(roll and roll.vetoes and roll.vetoes[rollType])
+ end
+end
+
+-- gets the current winner based on rolls and currently selected roll type for each user (returns index into rankings for item, or -1 if no winner yet)
+local function getWinner(itemIndex)
+
+ local rolls = Amr.db.char.TeamOpt.Rolls[itemIndex]
+ if not rolls then return -1 end
+
+ -- go through and find the highest priority roll type
+ local bestRollType
+ local bestTypePos = 100
+ for i, roll in pairs(rolls) do
+ if roll.rollType then
+ local rollPos = _rollTypePos[roll.rollType]
+ if rollPos < bestTypePos and rollPos < _rollTypePos["Pass"] then
+ bestRollType = roll.rollType
+ bestTypePos = rollPos
+ end
+ end
+ end
+
+ -- nobody has chosen anything yet
+ if not bestRollType then return -1 end
+
+ -- find highest roll in the highest priority roll type
+ local maxRoll = -1
+ local bestRoll = -1
+ for i, roll in pairs(rolls) do
+ if roll.rollType == bestRollType and roll.rand and roll.rand > maxRoll then
+ bestRoll = i
+ maxRoll = roll.rand
+ end
+ end
+
+ return bestRoll
+end
+
+function Amr:RefreshLootRolls()
+ if not _rankPanels then return end
+
+ local ranking = Amr.db.global.TeamOpt.Rankings[_selectedIndex]
+ local rolls = Amr.db.char.TeamOpt.Rolls[_selectedIndex]
+ local isDisenchant = _disenchant[_selectedIndex]
+
+ local winnerIndex = getWinner(_selectedIndex)
+
+ for i, rp in pairs(_rankPanels) do
+ local rank = ranking.ranks[i]
+ local roll = rolls[i]
+ if isDisenchant then roll = nil end
+
+ -- clear or set the value of each roll column
+ renderRollType(rp, "Need", roll, i)
+ renderRollType(rp, "Off", roll, i)
+ renderRollType(rp, "Greed", roll, i)
+ renderRollType(rp, "Pass", roll, i)
+
+ -- render the random roll
+ local lbl = rp:GetUserData("randLabel")
+ if roll and roll.rand then
+ if not lbl then
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetJustifyH("RIGHT")
+
+ local left = _widthRank + _widthColPadding + _widthRankBar + (5 * _widthColPadding) + (5 * _widthRollType)
+ lbl:SetPoint("RIGHT", rp.content, "LEFT", left, 0)
+ rp:AddChild(lbl)
+ rp:SetUserData("randLabel", lbl)
+ end
+
+ -- highlight this roll if winner, otherwise unhighlight
+ if i == winnerIndex then
+ lbl:SetFont(Amr.CreateFont("Bold", 18, Amr.Colors.BrightGreen))
+ else
+ lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
+ end
+
+ lbl:SetText(roll.rand)
+ else
+ if lbl then
+ lbl:SetVisible(false)
+ end
+ end
+
+ -- if this person does not have the addon, show a message (except in DE mode)
+ local hasAddon = true
+ local unitId = Amr:GetUnitId(rank.realm, rank.name)
+ if unitId then
+ local realm, name = Amr:GetRealmAndName(unitId)
+ if realm then
+ local ver = Amr:GetAddonVersion(realm, name)
+ hasAddon = ver >= Amr.MIN_ADDON_VERSION
+ end
+ end
+
+ lbl = rp:GetUserData("noaddonLabel")
+ if not hasAddon and not isDisenchant then
+ if not lbl then
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.Red))
+ lbl:SetText(L.LootRankLabelNoAddon)
+ lbl:SetPoint("LEFT", rp.content, "LEFT", _widthRank + _widthColPadding + _widthRankBar + _widthColPadding + 5, 0)
+ rp:AddChild(lbl)
+ rp:SetUserData("noaddonLabel", lbl)
+ end
+ else
+ if lbl then
+ lbl:SetVisible(false)
+ end
+ end
+
+ end
+end
+
+-- helper to create the column bg and header for rank list
+local function createLootRankColumn(container, prevColumn, width, txt, txtAlign, extraPadding)
+ extraPadding = extraPadding and extraPadding or 0
+
+ local panel = AceGUI:Create("AmrUiPanel")
+ panel:SetBackgroundColor(Amr.Colors.Black, 0.3)
+ container:AddChild(panel)
+
+ if prevColumn then
+ -- pad a bit to right of previous column
+ panel:SetPoint("TOPLEFT", prevColumn.content, "TOPRIGHT", _widthColPadding + extraPadding, 0)
+ panel:SetPoint("BOTTOMRIGHT", prevColumn.content, "BOTTOMRIGHT", _widthColPadding + extraPadding + width, 0)
+ else
+ -- first column abs position in the main ranking panel
+ panel:SetPoint("TOPLEFT", container.content, "TOPLEFT", _widthItemList + _widthSpacing, -115)
+ panel:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMLEFT", _widthItemList + _widthSpacing + width, 0)
+ end
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWordWrap(false)
+ lbl:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.TextHeaderDisabled))
+ lbl:SetText(txt)
+ lbl:SetJustifyH(txtAlign)
+ lbl:SetWidth(width)
+ lbl:SetPoint("BOTTOMLEFT", panel.content, "TOPLEFT", 0, 5)
+ container:AddChild(lbl)
+
+ return panel, lbl
+end
+
+function Amr:RefreshLootWindow()
+ if not _panelLoot then return end
+
+ -- clear out any children of the main loot frame and re-render
+ _panelLoot:ReleaseChildren()
+ _rankPanels = {}
+ _lootButtons = {}
+
+ local ml = IsMasterLooter()
+ local myUnitId = Amr:GetUnitId(GetRealmName(), UnitName("player"))
+
+ local rankings = Amr.db.global.TeamOpt.Rankings
+ if rankings and #rankings > 0 then
+
+ -- make sure that an item is selected
+ if not _selectedIndex then
+ for i, ranking in ipairs(rankings) do
+ if not ranking.given then
+ _selectedIndex = i
+ break
+ end
+ end
+ end
+
+ -- render list of items
+ local panelItems = AceGUI:Create("AmrUiPanel")
+ panelItems:SetLayout("Fill")
+ panelItems:SetWidth(_widthItemList)
+ panelItems:SetBackgroundColor(Amr.Colors.Black, 0)
+ panelItems:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", 0, 0)
+ panelItems:SetPoint("BOTTOMLEFT", _panelLoot.content, "BOTTOMLEFT")
+ _panelLoot:AddChild(panelItems)
+
+ local scrollItems = AceGUI:Create("AmrUiScrollFrame")
+ scrollItems:SetLayout("List")
+ panelItems:AddChild(scrollItems)
+
+ -- render the divider between items and ranks
+ local divider = AceGUI:Create("AmrUiPanel")
+ divider:SetBackgroundColor(Amr.Colors.Black, 0.5)
+ divider:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList, 0)
+ divider:SetPoint("BOTTOMRIGHT", _panelLoot.content, "BOTTOMLEFT", _widthItemList + 5, 0)
+ _panelLoot:AddChild(divider)
+
+ local btn, btn2, lbl, lbl2, panel, panel2, chk
+
+ local remainingItems = {}
+ for i, ranking in ipairs(rankings) do
+ if not ranking.given then
+ remainingItems[i] = ranking
+ end
+ end
+
+ for i, ranking in pairs(remainingItems) do
+ btn = AceGUI:Create("AmrUiTextButton")
+ btn:SetWidth(_widthItemList)
+ btn:SetHeight(50)
+ btn:SetJustifyH("LEFT")
+ btn:SetJustifyV("TOP")
+ btn:SetTextPadding(9, nil, nil, 2)
+ btn:SetWordWrap(false)
+ btn:SetUserData("index", i)
+
+ local f
+ if _selectedIndex == i then
+ f = Amr.CreateFont("Bold", 16, Amr.Colors.Text)
+ btn:SetBackgroundColor(Amr.Colors.Black, 0.5)
+ btn:SetHoverBackgroundColor(Amr.Colors.Black, 0.5)
+ else
+ f = Amr.CreateFont("Regular", 14, Amr.Colors.Text)
+ btn:SetHoverBackgroundColor(Amr.Colors.Black, 0.2)
+ end
+
+ btn:SetFont(f)
+ btn:SetHoverFont(f)
+
+ scrollItems:AddChild(btn)
+
+ btn:SetCallback("OnClick", function(widget)
+ Amr:SelectLootItem(widget:GetUserData("index"))
+ end)
+
+ local rankLink = Amr.CreateItemLink(ranking.item)
+ Amr.GetItemInfo(rankLink, function(obj, name, link)
+ -- set item name, tooltip
+ obj:SetText(" " .. link:gsub("%[", ""):gsub("%]", ""))
+ Amr:SetItemTooltip(obj, link, "ANCHOR_BOTTOMLEFT", 0, obj.frame:GetHeight())
+ end, btn)
+
+ -- add a label for slot, armor type
+ local slotText = Amr.SlotEnumDisplayText[ranking.itemInfo.slot]
+ if ranking.itemInfo.slot == 'MainHand' then
+ slotText = ranking.itemInfo.subclass == 'TwoHand' and L.TwoHand or L.OneHand
+ elseif ranking.itemInfo.slot == 'OffHand' then
+ slotText = L.OffHand
+ end
+
+ if ranking.itemInfo.armorType == 'None' and ranking.itemInfo.weaponType ~= 'None' then
+ if ranking.itemInfo.weaponType ~= 'OffHand' then
+ slotText = slotText .. ", " .. L.WeaponTypes[ranking.itemInfo.weaponType]
+ end
+ elseif ranking.itemInfo.armorType ~= 'None' then
+ slotText = slotText .. ", " .. L.ArmorTypes[ranking.itemInfo.armorType]
+ end
+
+ btn:SetSubtextFont(Amr.CreateFont("Regular", 13, Amr.Colors.TextGray))
+ btn:SetSubtextJustifyH("LEFT")
+ btn:SetSubtextJustifyV("BOTTOM")
+ btn:SetSubtextPadding(nil, nil, 9, 7)
+ btn:SetSubtextWordWrap(false)
+ btn:SetSubtext(slotText)
+
+
+ local isDisenchant = not not _disenchant[i]
+
+ if _selectedIndex == i then
+
+ -- see if I am in the list
+ local canLoot = false
+ for j, rank in ipairs(ranking.ranks) do
+ local unitId = Amr:GetUnitId(rank.realm, rank.name)
+ if unitId == myUnitId then
+ canLoot = not rank.notRanked or rank.offspec
+ break
+ end
+ end
+
+ -- render loot options
+ if canLoot then
+ btn = AceGUI:Create("AmrUiButton")
+ btn:SetWidth(120)
+ btn:SetHeight(26)
+ btn:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White))
+ btn:SetBackgroundColor(Amr.Colors.Green)
+ btn:SetText(L.TeamLootOptionNeed)
+ btn:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -7)
+ btn:SetCallback("OnClick", function(widget) doRoll("Need") end)
+ _panelLoot:AddChild(btn)
+ _lootButtons["Need"] = btn
+
+ btn2 = AceGUI:Create("AmrUiButton")
+ btn2:SetWidth(120)
+ btn2:SetHeight(26)
+ btn2:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White))
+ btn2:SetBackgroundColor(Amr.Colors.Orange)
+ btn2:SetText(L.TeamLootOptionPass)
+ btn2:SetPoint("TOPLEFT", btn.frame, "BOTTOMLEFT", 0, -15)
+ btn2:SetCallback("OnClick", function(widget) doRoll("Pass") end)
+ _panelLoot:AddChild(btn2)
+ _lootButtons["Pass"] = btn2
+
+ btn = AceGUI:Create("AmrUiButton")
+ btn:SetWidth(120)
+ btn:SetHeight(26)
+ btn:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White))
+ btn:SetBackgroundColor(Amr.Colors.Blue)
+ btn:SetText(L.TeamLootOptionOff)
+ btn:SetPoint("BOTTOMLEFT", btn2.frame, "TOPRIGHT", 15, 15)
+ btn:SetCallback("OnClick", function(widget) doRoll("Off") end)
+ _panelLoot:AddChild(btn)
+ _lootButtons["Off"] = btn
+
+ btn2 = AceGUI:Create("AmrUiButton")
+ btn2:SetWidth(120)
+ btn2:SetHeight(26)
+ btn2:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White))
+ btn2:SetBackgroundColor(Amr.Colors.Blue)
+ btn2:SetText(L.TeamLootOptionGreed)
+ btn2:SetPoint("TOPLEFT", btn.frame, "BOTTOMLEFT", 0, -15)
+ btn2:SetCallback("OnClick", function(widget) doRoll("Greed") end)
+ _panelLoot:AddChild(btn2)
+ _lootButtons["Greed"] = btn2
+ else
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetText(L.LootIneligible)
+ lbl:SetWidth(255)
+ lbl:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -7)
+ _panelLoot:AddChild(lbl)
+ end
+
+ -- master loot options
+ if ml then
+ chk = AceGUI:Create("AmrUiCheckBox")
+ chk:SetText(L.LootMasterDisenchantText)
+ chk:SetPoint("TOPRIGHT", _panelLoot.content, "TOPRIGHT", -18, -12)
+ chk:SetCallback("OnClick", onDisenchantClick)
+ chk:SetChecked(_disenchant[i])
+ _panelLoot:AddChild(chk)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(120)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetText(L.LootMasterDisenchantLabel)
+ lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
+ lbl:SetPoint("TOP", chk.frame, "BOTTOM", 0, -5)
+ _panelLoot:AddChild(lbl)
+
+ btn2 = AceGUI:Create("AmrUiButton")
+ btn2:SetWidth(120)
+ btn2:SetHeight(26)
+ btn2:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White))
+ btn2:SetBackgroundColor(Amr.Colors.Green)
+ btn2:SetText(L.LootMasterRollText)
+ btn2:SetPoint("RIGHT", chk.frame, "LEFT", -50, 0)
+ btn2:SetCallback("OnClick", onRollClick)
+ _panelLoot:AddChild(btn2)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(120)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetText(L.LootMasterRollLabel)
+ lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
+ lbl:SetPoint("TOP", btn2.frame, "BOTTOM", 0, -5)
+ _panelLoot:AddChild(lbl)
+
+ end
+
+ -- backgrounds for the rank list and headers
+ panel = createLootRankColumn(_panelLoot, nil, _widthRank, isDisenchant and "" or L.LootRankHeaderRank, "RIGHT")
+ panel = createLootRankColumn(_panelLoot, panel, _widthRankBar, isDisenchant and L.LootRankHeaderScoreDisenchant or L.LootRankHeaderScore, "LEFT")
+
+ if not isDisenchant then
+ panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderNeed, "CENTER")
+ panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderOff, "CENTER")
+ panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderGreed, "CENTER")
+ panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderPass, "CENTER")
+ panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderRoll, "RIGHT", _widthRollExtraSpacing)
+ end
+
+ -- rank list for selected item
+ panel = AceGUI:Create("AmrUiPanel")
+ panel:SetLayout("Fill")
+ panel:SetBackgroundColor(Amr.Colors.Black, 0)
+ panel:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -115)
+ panel:SetPoint("BOTTOMRIGHT", _panelLoot.content, "BOTTOMRIGHT")
+ _panelLoot:AddChild(panel)
+
+ local scrollRanks = AceGUI:Create("AmrUiScrollFrame")
+ scrollRanks:SetLayout("List")
+ panel:AddChild(scrollRanks)
+
+ -- find min and max value, used for sizing the bars
+ local rankMin = -0.02
+ local rankMax = 0.02
+ for j, rank in ipairs(ranking.ranks) do
+ if rank.score < rankMin then
+ rankMin = rank.score
+ end
+ if rank.score > rankMax then
+ rankMax = rank.score
+ end
+ end
+
+ -- just make min less than max if they are the same, doesn't really matter what it is, would be a wacky case
+ if rankMin == rankMax then
+ rankMin = rankMax - 1
+ end
+
+ local minWidth = 10
+ local maxWidth = _widthRankBar - 36 - 65 - 2 -- reserve 36 for icon, 65 for bar label, and 2 for a border around the bar
+ local rankCount = 0
+
+ for j, rank in ipairs(ranking.ranks) do
+ local unitId = Amr:GetUnitId(rank.realm, rank.name)
+ if unitId then
+ local skip = false
+ if isDisenchant then
+ skip = true
+ if rank.isMasterLooter or (rank.enchantingSkill and rank.enchantingSkill > 0) then
+ skip = false
+ end
+ end
+
+ if not skip then
+ rankCount = rankCount + 1
+
+ local rp = AceGUI:Create("AmrUiPanel")
+ rp:SetLayout("None")
+ rp:SetBackgroundColor(Amr.Colors.Black, 0)
+ rp:SetWidth(_widthRankList)
+ rp:SetHeight(45)
+ scrollRanks:AddChild(rp)
+ _rankPanels[j] = rp
+
+ if not isDisenchant then
+ panel = AceGUI:Create("AmrUiPanel")
+ panel:SetBackgroundColor(Amr.Colors.Black, 1)
+ panel:SetPoint("TOPLEFT", rp.content, "BOTTOMLEFT", 0, 0)
+ panel:SetPoint("BOTTOMRIGHT", rp.content, "BOTTOMRIGHT", -120, -1)
+ rp:AddChild(panel)
+ end
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Bold", 32, Amr.Colors.White))
+ lbl:SetText(rankCount)
+ lbl:SetWidth(_widthRank - 6)
+ lbl:SetJustifyH("RIGHT")
+ lbl:SetPoint("BOTTOMLEFT", rp.content, "BOTTOMLEFT", 0, 3)
+ rp:AddChild(lbl)
+
+ local cls, clsEn = UnitClass(unitId)
+ local color = clsEn and Amr.Colors.Classes[clsEn] or Amr.Colors.TextHeaderDisabled
+
+ local icon = AceGUI:Create("AmrUiIcon")
+ icon:SetIconBorderColor(color)
+ icon:SetWidth(36)
+ icon:SetHeight(36)
+ icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[rank.specId])
+ icon:SetPoint("BOTTOMLEFT", rp.content, "BOTTOMLEFT", 48 + 8, 0)
+ rp:AddChild(icon)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Bold", 16, color))
+ lbl:SetText(UnitName(unitId))
+ lbl:SetWidth(_widthRankBar - 36 - 8) -- 4px on left and right side
+ lbl:SetPoint("TOPLEFT", icon.frame, "TOPRIGHT", 4, -2)
+ rp:AddChild(lbl)
+
+ if isDisenchant or rank.notRanked then
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 13, Amr.Colors.TextHeaderDisabled))
+ lbl:SetWidth(_widthRankBar - 36 - 4) -- 4px on left side
+ lbl:SetWordWrap(false)
+ lbl:SetPoint("BOTTOMLEFT", icon.frame, "BOTTOMRIGHT", 4, 2)
+ rp:AddChild(lbl)
+
+ if isDisenchant then
+ -- will be disenchanter or ML if we are DEing the item
+ lbl:SetText((rank.enchantingSkill and rank.enchantingSkill > 0) and string.format(L.LootRankLabelDisenchant .. " (%d)", rank.enchantingSkill) or L.LootRankLabelMasterLooter)
+ else
+ -- if this is off spec or just a disenchanter, no score bar just description text
+ lbl:SetText(rank.offspec and L.LootRankLabelOff or ((rank.enchantingSkill and rank.enchantingSkill > 0) and string.format(L.LootRankLabelDisenchant .. " (%d)", rank.enchantingSkill) or L.LootRankLabelMasterLooter))
+ end
+ else
+ local scoreText = rank.score .. "%"
+ local val = rank.score;
+ if rank.isEquipped then
+ scoreText = "E"
+ val = 0
+ elseif val >= 0 then
+ scoreText = "+" .. scoreText
+ end
+
+ local per = (val - rankMin) / (rankMax - rankMin);
+ local w = minWidth + (per * (maxWidth - minWidth));
+ color = val > 0 and Amr.Colors.BarHigh or (val == 0 and Amr.Colors.BarMed or Amr.Colors.BarLow)
+
+ panel = AceGUI:Create("AmrUiPanel")
+ panel:SetLayout("None")
+ panel:SetWidth(w + 2)
+ panel:SetHeight(16)
+ panel:SetBackgroundColor(Amr.Colors.Black, 1)
+ panel:SetPoint("BOTTOMLEFT", icon.frame, "BOTTOMRIGHT", 0, -1)
+ rp:AddChild(panel)
+
+ panel2 = AceGUI:Create("AmrUiPanel")
+ panel2:SetLayout("None")
+ panel2:SetWidth(w)
+ panel2:SetHeight(14)
+ panel2:SetBackgroundColor(color, 1)
+ panel2:SetPoint("TOPLEFT", panel.content, "TOPLEFT", 1, -1)
+ panel:AddChild(panel2)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Bold", 13, color))
+ lbl:SetText(scoreText)
+ lbl:SetWidth(63)
+ lbl:SetWordWrap(false)
+ lbl:SetPoint("LEFT", panel.content, "RIGHT", 2, 0)
+ rp:AddChild(lbl)
+ end
+
+ if ml then
+ btn2 = AceGUI:Create("AmrUiButton")
+ btn2:SetHeight(24)
+ btn2:SetFont(Amr.CreateFont("Regular", 13, Amr.Colors.White))
+ btn2:SetBackgroundColor(Amr.Colors.Green)
+
+ if isDisenchant then
+ btn2:SetWidth(200)
+ btn2:SetText(L.LootMasterGiveDisenchant)
+ btn2:SetPoint("LEFT", rp.content, "LEFT", _widthRank + _widthRankBar + (3 * _widthColPadding), 0)
+ else
+ btn2:SetWidth(85)
+ btn2:SetText(L.LootMasterGiveLoot)
+ btn2:SetPoint("RIGHT", rp.content, "RIGHT", -30, 0)
+ end
+
+ btn2:SetUserData("index", j)
+ btn2:SetCallback("OnClick", onGiveLootClick)
+ rp:AddChild(btn2)
+ end
+ end
+
+ end
+ end
+
+ end
+
+ end
+
+ else
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
+ lbl:SetWidth(800)
+ lbl:SetText(L.LootEmpty)
+ lbl:SetPoint("CENTER", _panelLoot.content, "CENTER")
+ _panelLoot:AddChild(lbl)
+ end
+
+end
+
+-- select a particular loot item to display
+function Amr:SelectLootItem(index)
+ _selectedIndex = index
+ self:RefreshLootWindow()
+ self:RefreshLootRolls()
+end
+
+local function onLootFrameClose(widget)
+ AceGUI:Release(widget)
+ _frameLoot = nil
+ _panelLoot = nil
+ _rankPanels = nil
+ _lootButtons = nil
+end
+
+function Amr:HideLootWindow()
+ if not _frameLoot then return end
+ _frameLoot:Hide()
+end
+
+function Amr:ShowLootWindow()
+ if not _frameLoot then
+ _frameLoot = AceGUI:Create("AmrUiFrame")
+ _frameLoot:SetStatusTable(Amr.db.profile.lootWindow) -- window position is remembered in db
+ _frameLoot:SetCallback("OnClose", onLootFrameClose)
+ _frameLoot:SetLayout("None")
+ _frameLoot:SetWidth(900)
+ _frameLoot:SetHeight(600)
+ _frameLoot:SetBorderColor(Amr.Colors.BorderBlue)
+ _frameLoot:SetBackgroundColor(Amr.Colors.Bg)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(600)
+ lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White))
+ lbl:SetText(L.LootTitle)
+ lbl:SetWordWrap(false)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetPoint("TOP", _frameLoot.content, "TOP", 0, 30)
+ _frameLoot:AddChild(lbl)
+
+ lbl:SetCallback("OnMouseDown", function(widget) _frameLoot:StartMove() end)
+ lbl:SetCallback("OnMouseUp", function(widget) _frameLoot:EndMove() end)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(_widthItemList)
+ lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
+ lbl:SetText(L.LootHelpItems)
+ lbl:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", 0, -10)
+ _frameLoot:AddChild(lbl)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(_widthItemList)
+ lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
+ lbl:SetText(L.LootHelpRanks)
+ lbl:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -10)
+ _frameLoot:AddChild(lbl)
+
+ if IsMasterLooter() then
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(_widthItemList)
+ lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
+ lbl:SetText(L.LootHelpMaster)
+ lbl:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", _widthItemList + _widthSpacing + _widthRank + _widthRankBar + _widthRollType + (_widthColPadding * 3), -10)
+ _frameLoot:AddChild(lbl)
+ end
+
+ _panelLoot = AceGUI:Create("AmrUiPanel")
+ _panelLoot:SetLayout("None")
+ _panelLoot:SetBackgroundColor(Amr.Colors.Black, 0)
+ _panelLoot:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", 0, -40)
+ _panelLoot:SetPoint("BOTTOMRIGHT", _frameLoot.content, "BOTTOMRIGHT", 0, 0)
+ _frameLoot:AddChild(_panelLoot)
+ else
+ _frameLoot:Show()
+ end
+
+ _frameLoot:Raise()
+end
+
+function Amr:OnStartLootReceived(parts)
+ local data = {}
+ for i = 2, #parts do
+ table.insert(data, parts[i])
+ end
+ data = table.concat(data, "\n")
+
+ -- reset rankings to the new data sent out by person in control
+ local rankings = Amr:ParseRankingString(data)
+ Amr.db.global.TeamOpt.Rankings = rankings
+
+ -- reset disenchant state
+ _disenchant = {}
+
+ -- reset roll information when loot is started
+ local rolls = {}
+ for i = 1, #rankings do
+ table.insert(rolls, {})
+ end
+ Amr.db.char.TeamOpt.Rolls = rolls
+
+ -- select first item by default
+ _selectedIndex = #rankings > 0 and 1 or nil
+
+ -- begin looting
+ Amr.db.char.TeamOpt.LootInProgress = true
+
+ Amr:RefreshTeamUi()
+ Amr:ShowLootWindow()
+ Amr:RefreshLootWindow()
+end
diff -r 75431c084aa0 -r 01b63b8ed811 Media/IconCheck.tga
Binary file Media/IconCheck.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/IconScrollDown.tga
Binary file Media/IconScrollDown.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/IconScrollUp.tga
Binary file Media/IconScrollUp.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/IconX.tga
Binary file Media/IconX.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/Ubuntu-Bold.ttf
Binary file Media/Ubuntu-Bold.ttf has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/Ubuntu-Italic.ttf
Binary file Media/Ubuntu-Italic.ttf has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/Ubuntu-Regular.ttf
Binary file Media/Ubuntu-Regular.ttf has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/check-off.tga
Binary file Media/check-off.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/check-on.tga
Binary file Media/check-on.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/round-edge-big.tga
Binary file Media/round-edge-big.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/round-edge.tga
Binary file Media/round-edge.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Media/square-edge.tga
Binary file Media/square-edge.tga has changed
diff -r 75431c084aa0 -r 01b63b8ed811 Options.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Options.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,87 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _chkMinimap
+local _chkAutoGear
+local _chkAh
+
+local function onCheckClick(widget)
+ local setting = widget:GetUserData("setting")
+ local val = widget:GetChecked()
+
+ if setting == "minimap" then
+ Amr.db.profile.minimap.hide = val
+ else
+ Amr.db.profile.options[setting] = val
+ end
+
+ Amr:RefreshConfig()
+end
+
+local function onChkMinimapClick()
+ Amr.db.profile.minimap.hide = _chkMinimap:GetChecked()
+ Amr:RefreshConfig()
+end
+
+local function createCheck(container, setting, text, description)
+
+ local chk = AceGUI:Create("AmrUiCheckBox")
+ chk:SetUserData("setting", setting)
+ chk:SetText(text)
+ chk:SetCallback("OnClick", onCheckClick)
+ container:AddChild(chk)
+
+ local desc = AceGUI:Create("AmrUiLabel")
+ desc:SetWidth(800)
+ desc:SetText(description)
+ desc:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
+ desc:SetPoint("TOPLEFT", chk.frame, "BOTTOMLEFT", 24, -3)
+ container:AddChild(desc)
+
+ return chk, desc
+end
+
+-- renders the main UI for the Combat Log tab
+function Amr:RenderTabOptions(container)
+
+ local header = AceGUI:Create("AmrUiLabel")
+ header:SetWidth(600)
+ header:SetText(L.OptionsHeaderGeneral)
+ header:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
+ header:SetPoint("TOPLEFT", container.content, "TOPLEFT", 12, -40)
+ container:AddChild(header)
+
+ local desc, desc2
+
+ _chkMinimap, desc = createCheck(container, "minimap", L.OptionsHideMinimapName, L.OptionsHideMinimapDesc)
+ _chkMinimap:SetPoint("TOPLEFT", header.frame, "BOTTOMLEFT", 0, -20)
+
+ _chkAutoGear, desc2 = createCheck(container, "autoGear", L.OptionsAutoGearName, L.OptionsAutoGearDesc)
+ _chkAutoGear:SetPoint("TOPLEFT", desc.frame, "BOTTOMLEFT", -24, -20)
+
+ _chkAh, desc = createCheck(container, "shopAh", L.OptionsShopAhName, L.OptionsShopAhDesc)
+ _chkAh:SetPoint("TOPLEFT", desc2.frame, "BOTTOMLEFT", -24, -20)
+
+ -- initialize state of controls
+ Amr:RefreshOptionsUi()
+end
+
+function Amr:ReleaseTabOptions()
+ _chkMinimap = nil
+end
+
+function Amr:RefreshOptionsUi()
+
+ if _chkMinimap then
+ _chkMinimap:SetChecked(self.db.profile.minimap.hide)
+ end
+
+ if _chkAutoGear then
+ _chkAutoGear:SetChecked(self.db.profile.options.autoGear)
+ end
+
+ if _chkAh then
+ _chkAh:SetChecked(self.db.profile.options.shopAh)
+ end
+end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 Shopping.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Shopping.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,433 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _frameShop
+local _panelContent
+local _cboPlayers
+local _selectedPlayer
+local _specs = {
+ [1] = true,
+ [2] = true
+}
+local _chk1
+local _chk2
+local _isAhOpen = false
+
+local function onShopFrameClose(widget)
+ AceGUI:Release(widget)
+ _frameShop = nil
+ _cboPlayers = nil
+ _chk1 = nil
+ _chk2 = nil
+ _panelContent = nil
+end
+
+function Amr:HideShopWindow()
+ if not _frameShop then return end
+ _frameShop:Hide()
+end
+
+local function onPlayerChange(widget, eventName, value)
+ _selectedPlayer = value
+ Amr:RefreshShoppingUi()
+end
+
+local function onSpecClick(widget)
+ local spec = widget:GetUserData("spec")
+ _specs[spec] = not _specs[spec]
+
+ Amr:RefreshShoppingUi()
+end
+
+local function onItemClick(widget)
+ local name = widget:GetUserData("itemName")
+ if name then
+ QueryAuctionItems(name)
+ end
+end
+
+function Amr:ShowShopWindow()
+ if not _frameShop then
+ _frameShop = AceGUI:Create("AmrUiFrame")
+ _frameShop:SetStatusTable(Amr.db.profile.shopWindow) -- window position is remembered in db
+ _frameShop:SetCallback("OnClose", onShopFrameClose)
+ _frameShop:SetLayout("None")
+ _frameShop:SetWidth(500)
+ _frameShop:SetHeight(500)
+ _frameShop:SetBorderColor(Amr.Colors.BorderBlue)
+ _frameShop:SetBackgroundColor(Amr.Colors.Bg)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(400)
+ lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White))
+ lbl:SetText(L.ShopTitle)
+ lbl:SetWordWrap(false)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetPoint("TOP", _frameShop.content, "TOP", 0, 30)
+ _frameShop:AddChild(lbl)
+
+ lbl:SetCallback("OnMouseDown", function(widget) _frameShop:StartMove() end)
+ lbl:SetCallback("OnMouseUp", function(widget) _frameShop:EndMove() end)
+
+ -- player picker
+ _cboPlayers = AceGUI:Create("AmrUiDropDown")
+ _cboPlayers:SetWidth(400)
+ _cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30)
+ _frameShop:AddChild(_cboPlayers)
+
+ -- spec pickers
+ _chk1 = AceGUI:Create("AmrUiCheckBox")
+ _chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20)
+ _chk1:SetUserData("spec", 1)
+ _chk1:SetCallback("OnClick", onSpecClick)
+ _frameShop:AddChild(_chk1)
+
+ _chk2 = AceGUI:Create("AmrUiCheckBox")
+ _chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0)
+ _chk2:SetUserData("spec", 2)
+ _chk2:SetCallback("OnClick", onSpecClick)
+ _frameShop:AddChild(_chk2)
+
+ _panelContent = AceGUI:Create("AmrUiPanel")
+ _panelContent:SetLayout("None")
+ _panelContent:SetTransparent()
+ _panelContent:SetPoint("TOPLEFT", _chk1.frame, "BOTTOMLEFT", 0, -10)
+ _panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT")
+ _frameShop:AddChild(_panelContent)
+
+ -- update shopping list data
+ local player = Amr:ExportCharacter()
+ Amr:UpdateShoppingData(player)
+
+ -- fill player list
+ local playerList = {}
+ for name, data in pairs(Amr.db.global.Shopping) do
+ table.insert(playerList, { text = name, value = name })
+ end
+ _cboPlayers:SetItems(playerList)
+
+ -- set default selected player
+ if not _selectedPlayer then
+ _selectedPlayer = player.Name .. "-" .. player.Realm
+ end
+ _cboPlayers:SelectItem(_selectedPlayer)
+
+ Amr:RefreshShoppingUi()
+
+ -- set event on dropdown after UI has been initially rendered
+ _cboPlayers:SetCallback("OnChange", onPlayerChange)
+ else
+ _frameShop:Show()
+ Amr:RefreshShoppingUi()
+ end
+
+ _frameShop:Raise()
+end
+
+-- helper to render a section of the shopping list
+local function renderShopSection(list, scroll, header)
+ if not list or next(list) == nil then return end
+
+ local w = 440
+
+ local panel = AceGUI:Create("AmrUiPanel")
+ panel:SetLayout("None")
+ panel:SetTransparent()
+ panel:SetWidth(w)
+ panel:SetHeight(40)
+ scroll:AddChild(panel)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(w)
+ lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
+ lbl:SetText(header)
+ lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT")
+ panel:AddChild(lbl)
+
+ for itemId, count in pairs(list) do
+ panel = AceGUI:Create("AmrUiPanel")
+ panel:SetLayout("None")
+ panel:SetTransparent()
+ panel:SetWidth(w)
+ panel:SetHeight(30)
+ scroll:AddChild(panel)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(35)
+ lbl:SetWordWrap(false)
+ lbl:SetFont(Amr.CreateFont("Bold", 20, Amr.Colors.White))
+ lbl:SetText(count .. "x")
+ lbl:SetPoint("LEFT", panel.content, "LEFT")
+ panel:AddChild(lbl)
+
+ local icon = AceGUI:Create("AmrUiIcon")
+ icon:SetBorderWidth(1)
+ icon:SetIconBorderColor(Amr.Colors.White)
+ icon:SetWidth(18)
+ icon:SetHeight(18)
+ icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0)
+ panel:AddChild(icon)
+
+ local btn = AceGUI:Create("AmrUiTextButton")
+ btn:SetWidth(w - 30 - 18 - 15)
+ btn:SetJustifyH("LEFT")
+ btn:SetWordWrap(false)
+ btn:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
+ btn:SetHoverFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
+ btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0)
+ btn:SetCallback("OnClick", onItemClick)
+ panel:AddChild(btn)
+
+ Amr.GetItemInfo(itemId, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)
+ -- set icon, name, and a tooltip
+ obj.itemIcon:SetIcon(texture)
+ obj.itemText:SetText(link:gsub("%[", ""):gsub("%]", ""))
+ obj.itemText:SetUserData("itemName", name)
+ Amr:SetItemTooltip(obj.itemText, link)
+ end, { itemIcon = icon, itemText = btn })
+ end
+
+end
+
+function Amr:RefreshShoppingUi()
+
+ _chk1:SetVisible(false)
+ _chk2:SetVisible(false)
+
+ _chk1:SetChecked(false)
+ _chk2:SetChecked(false)
+
+ -- clear out any previous data
+ _panelContent:ReleaseChildren()
+
+ -- render required gems for the selected player
+ local data = Amr.db.global.Shopping[_selectedPlayer]
+ if not data then
+ _panelContent:SetLayout("None")
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan))
+ lbl:SetText(L.ShopEmpty)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30)
+ _panelContent:AddChild(lbl)
+ else
+ -- set labels on checkboxes
+ if data.specs[1] then
+ local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[1]))
+ _chk1:SetText(name .. " " .. L.ShopSpecLabel)
+ _chk1:SetVisible(true)
+ _chk1:SetChecked(_specs[1])
+ end
+
+ if data.specs[2] then
+ local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[2]))
+ _chk2:SetText(name .. " " .. L.ShopSpecLabel)
+ _chk2:SetVisible(true)
+ _chk2:SetChecked(_specs[2])
+ end
+
+ local spec = 0
+ if not _specs[1] and not _specs[2] then
+ -- both unchecked, show nothing
+ else
+ -- both is 0, otherwise the one that is selected
+ if not _specs[1] or not _specs[2] then
+ spec = _specs[1] and 1 or 2
+ end
+
+ _panelContent:SetLayout("Fill")
+
+ local scroll = AceGUI:Create("AmrUiScrollFrame")
+ scroll:SetLayout("List")
+ _panelContent:AddChild(scroll)
+
+ renderShopSection(data.gems[spec], scroll, L.ShopHeaderGems)
+ renderShopSection(data.enchants[spec], scroll, L.ShopHeaderEnchants)
+ renderShopSection(data.materials[spec], scroll, L.ShopHeaderMaterials)
+ end
+ end
+
+end
+
+-- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize
+local function getShoppingData(player, gear, spec)
+
+ local ret = {
+ gems = {},
+ enchants = {},
+ materials = {}
+ }
+
+ -- used to prevent considering the same item twice
+ local usedItems = {}
+
+ for slotId, optimalItem in pairs(gear) do
+ local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
+ local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id]
+
+ -- find gem/enchant differences on the best-matching item
+
+ -- gems
+ if itemInfo and itemInfo.socketColors then
+ for i = 1, #itemInfo.socketColors do
+ local g = optimalItem.gemIds[i]
+ local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
+
+ if not isGemEquipped then
+ ret.gems[g] = ret.gems[g] and ret.gems[g] + 1 or 1
+ end
+ end
+ end
+
+ -- enchant
+ if optimalItem.enchantId and optimalItem.enchantId ~= 0 then
+ local e = optimalItem.enchantId
+ local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e
+
+ if not isEnchantEquipped then
+ ret.enchants[e] = ret.enchants[e] and ret.enchants[e] + 1 or 1
+ end
+ end
+ end
+
+ return ret
+end
+
+-- get the number of a specified item that the player currently owns
+local function getOwnedCount(itemId)
+ local ret = 0
+
+ local list = Amr.db.char.BagItemsAndCounts
+ if list and list[itemId] then
+ ret = ret + list[itemId]
+ end
+
+ list = Amr.db.char.BankItemsAndCounts
+ if list and list[itemId] then
+ ret = ret + list[itemId]
+ end
+
+ return ret
+end
+
+-- look at both gear sets and find stuff that a player needs to acquire to gem/enchant their gear
+function Amr:UpdateShoppingData(player)
+
+ -- 0 is combination of both specs, 1 is primary, 2 is secondary
+ local required = {
+ gems = {
+ [0] = {},
+ [1] = {},
+ [2] = {}
+ },
+ enchants = {
+ [0] = {},
+ [1] = {},
+ [2] = {}
+ },
+ materials = {
+ [0] = {},
+ [1] = {},
+ [2] = {}
+ },
+ specs = player.Specs
+ }
+
+ local enchantItemIdToId = {}
+
+ for spec, gear in pairs(Amr.db.char.GearSets) do
+ local obj = getShoppingData(player, gear, spec)
+ for k, v in pairs(obj.gems) do
+ local gemInfo = Amr.db.char.ExtraGemData[spec][k]
+ if gemInfo then
+ local prev = required.gems[spec][gemInfo.id]
+ required.gems[spec][gemInfo.id] = prev and prev + v or v
+
+ prev = required.gems[0][gemInfo.id]
+ required.gems[0][gemInfo.id] = prev and prev + v or v
+ end
+ end
+ for k, v in pairs(obj.enchants) do
+ local enchInfo = Amr.db.char.ExtraEnchantData[spec][k]
+ if enchInfo then
+ enchantItemIdToId[enchInfo.itemId] = k
+
+ local prev = required.enchants[spec][enchInfo.itemId]
+ required.enchants[spec][enchInfo.itemId] = prev and prev + v or v
+
+ prev = required.enchants[0][enchInfo.itemId]
+ required.enchants[0][enchInfo.itemId] = prev and prev + v or v
+ end
+ end
+ end
+
+ -- now subtract stuff the player already has, and generate a list of materials as well
+ for spec = 0, 2 do
+ -- now check if the player has any of the gems or enchants in their inventory, and subtract those
+ for itemId, count in pairs(required.gems[spec]) do
+ required.gems[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
+
+ if required.gems[spec][itemId] == 0 then
+ required.gems[spec][itemId] = nil
+ end
+ end
+
+ for itemId, count in pairs(required.enchants[spec]) do
+ -- look in both spec extra info cache
+ local e = enchantItemIdToId[itemId]
+ local enchInfo = nil
+ if Amr.db.char.ExtraEnchantData[1] then
+ enchInfo = Amr.db.char.ExtraEnchantData[1][e]
+ end
+ if not enchInfo then
+ if Amr.db.char.ExtraEnchantData[2] then
+ enchInfo = Amr.db.char.ExtraEnchantData[2][e]
+ end
+ end
+
+ if enchInfo then
+ required.enchants[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
+
+ if required.enchants[spec][itemId] == 0 then
+ required.enchants[spec][itemId] = nil
+ else
+ -- count up required materials
+ if enchInfo.materials then
+ local c = required.enchants[spec][itemId]
+ for k, v in pairs(enchInfo.materials) do
+ local prev = required.materials[spec][k]
+ required.materials[spec][k] = prev and prev + (v * c) or (v * c)
+ end
+ end
+ end
+ end
+ end
+
+ -- check if player has any of the materials already
+ for itemId, count in pairs(required.materials[spec]) do
+ required.materials[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
+
+ if required.materials[spec][itemId] == 0 then
+ required.materials[spec][itemId] = nil
+ end
+ end
+ end
+
+ Amr.db.global.Shopping[player.Name .. "-" .. player.Realm] = required
+end
+
+Amr:AddEventHandler("AUCTION_HOUSE_SHOW", function()
+ _isAhOpen = true
+ if Amr.db.profile.options.shopAh then
+ Amr:ShowShopWindow()
+ end
+end)
+
+Amr:AddEventHandler("AUCTION_HOUSE_CLOSED", function()
+ _isAhOpen = false
+ if Amr.db.profile.options.shopAh then
+ Amr:HideShopWindow()
+ end
+end)
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 TeamOptimizer.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TeamOptimizer.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,1405 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _panelSplash
+local _panelStartLoot
+local _lblStartLoot
+local _btnStartLoot
+local _scrollHistory
+local _tabs
+
+local _messagePrefixes = {
+ RosterRequestGear = "_TRR",
+ RosterGear = "_TRG",
+ ItemExportRequestGear = "_TLR",
+ ItemExportGear = "_TLG",
+ ItemExportLoot = "_TLL",
+ SyncRequest = "_TSR",
+ Sync = "_TSS"
+}
+
+Amr.LootMessagePrefixes = {
+ Start = "_TCS",
+ Roll = "_TCR",
+ Veto = "_TCV",
+ Rand = "_TCD",
+ Give = "_TCG",
+ Finish = "_TCF"
+}
+
+local function renderExportWindow(container, instructions, text)
+
+ local bg = Amr:RenderCoverChrome(container, 800, 450)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(750)
+ lbl:SetText(L.TeamExportHelp)
+ lbl:SetPoint("TOP", bg.content, "TOP", 0, -10)
+ bg:AddChild(lbl)
+
+ local lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetWidth(750)
+ lbl2:SetText(instructions)
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
+ bg:AddChild(lbl2)
+
+ local txt = AceGUI:Create("AmrUiTextarea")
+ txt:SetWidth(750)
+ txt:SetHeight(300)
+ txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -10)
+ txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
+ txt:SetText(text)
+ bg:AddChild(txt)
+
+ local btn = AceGUI:Create("AmrUiButton")
+ btn:SetText(L.TeamButtonExportClose)
+ btn:SetBackgroundColor(Amr.Colors.Green)
+ btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btn:SetWidth(120)
+ btn:SetHeight(28)
+ btn:SetPoint("TOPLEFT", txt.frame, "BOTTOMLEFT", 0, -10)
+ btn:SetCallback("OnClick", function(widget) Amr:HideCover() end)
+ bg:AddChild(btn)
+
+ return txt
+end
+
+local function renderImportWindow(container)
+
+ local bg = Amr:RenderCoverChrome(container, 700, 450)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(600)
+ lbl:SetText(L.TeamImportRankingsHeader)
+ lbl:SetPoint("TOP", bg.content, "TOP", 0, -10)
+ bg:AddChild(lbl)
+
+ local txt = AceGUI:Create("AmrUiTextarea")
+ txt:SetWidth(600)
+ txt:SetHeight(300)
+ txt:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
+ txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
+ bg:AddChild(txt)
+
+ local btnImportOk = AceGUI:Create("AmrUiButton")
+ btnImportOk:SetText(L.ImportButtonOk)
+ btnImportOk:SetBackgroundColor(Amr.Colors.Green)
+ btnImportOk:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnImportOk:SetWidth(120)
+ btnImportOk:SetHeight(28)
+ btnImportOk:SetPoint("TOPLEFT", txt.frame, "BOTTOMLEFT", 0, -10)
+ bg:AddChild(btnImportOk)
+
+ local btnImportCancel = AceGUI:Create("AmrUiButton")
+ btnImportCancel:SetText(L.ImportButtonCancel)
+ btnImportCancel:SetBackgroundColor(Amr.Colors.Green)
+ btnImportCancel:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnImportCancel:SetWidth(120)
+ btnImportCancel:SetHeight(28)
+ btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0)
+ btnImportCancel:SetCallback("OnClick", function(widget) Amr:HideCover() end)
+ bg:AddChild(btnImportCancel)
+
+ local lblErr = AceGUI:Create("AmrUiLabel")
+ lblErr:SetWidth(600)
+ lblErr:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Red))
+ lblErr:SetText("")
+ lblErr:SetPoint("TOPLEFT", btnImportOk.frame, "BOTTOMLEFT", 0, -20)
+ bg:AddChild(lblErr)
+
+ btnImportOk:SetCallback("OnClick", function(widget)
+ local msg = txt:GetText()
+ local err = Amr:ImportRankings(msg)
+ if err then
+ lblErr:SetText(err)
+ txt:SetFocus(true)
+ else
+ Amr:HideCover()
+ Amr:RefreshTeamUi()
+ end
+ end)
+
+ return txt
+end
+
+local function renderVersionWindow(container)
+
+ local windowWidth = 500
+ local lbl, lbl2
+ local bg, border = Amr:RenderCoverChrome(container, windowWidth, 600)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(windowWidth - 60)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
+ lbl:SetText(L.TeamVersionTitle)
+ lbl:SetPoint("TOP", bg.content, "TOP", 0, -10)
+ bg:AddChild(lbl)
+
+ if not IsInGroup() and not IsInRaid() then
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetWidth(windowWidth - 20)
+ lbl2:SetJustifyH("CENTER")
+ lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
+ lbl2:SetText(L.TeamVersionNoGroup)
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -25)
+ bg:AddChild(lbl2)
+ border:SetHeight(150)
+ else
+ local units = Amr:GetGroupUnitIdentifiers()
+
+ local missing = {}
+ local tooLow = {}
+
+ for i, unitId in ipairs(units) do
+ local realm, name = Amr:GetRealmAndName(unitId)
+ if realm then
+ local ver = Amr:GetAddonVersion(realm, name)
+ if ver == 0 then
+ table.insert(missing, { unitId, realm, name })
+ elseif ver < Amr.MIN_ADDON_VERSION then
+ table.insert(tooLow, { unitId, realm, name, ver })
+ end
+ end
+ end
+
+ if #missing == 0 and #tooLow == 0 then
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetWidth(windowWidth - 20)
+ lbl2:SetJustifyH("CENTER")
+ lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
+ lbl2:SetText(L.TeamVersionGood)
+ lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -25)
+ bg:AddChild(lbl2)
+ border:SetHeight(150)
+ else
+ local prev = lbl
+ local h = 0
+
+ -- helper to render a player name
+ local function renderItem(obj, showVer)
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(120)
+
+ local cls, clsEn = UnitClass(obj[1])
+ local color = clsEn and Amr.Colors.Classes[clsEn] or Amr.Colors.TextHeaderDisabled
+ lbl:SetFont(Amr.CreateFont("Regular", 14, color))
+
+ lbl:SetText(obj[3])
+ lbl:SetPoint("TOPLEFT", prev.frame, "BOTTOMLEFT", 0, -5)
+ bg:AddChild(lbl)
+ prev = lbl
+ h = h + lbl:GetHeight() + 5
+
+ if showVer then
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetWidth(60)
+ lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
+ lbl2:SetText("v" .. obj[4])
+ lbl2:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0)
+ bg:AddChild(lbl2)
+ end
+ end
+
+ if #missing > 0 then
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetWidth(180)
+ lbl2:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Red))
+ lbl2:SetText(L.TeamVersionMissing)
+ lbl2:SetJustifyH("CENTER")
+ lbl2:SetPoint("TOP", prev.frame, "BOTTOM", 0, -20)
+ bg:AddChild(lbl2)
+ h = h + lbl2:GetHeight() + 20
+
+ prev = lbl2
+ for i, obj in ipairs(missing) do
+ renderItem(obj)
+ end
+ end
+
+ if #tooLow > 0 then
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetWidth(180)
+ lbl2:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Gold))
+ lbl2:SetText(L.TeamVersionOld)
+ lbl2:SetJustifyH("CENTER")
+ lbl2:SetPoint("TOP", prev.frame, "BOTTOM", 0, -20)
+ bg:AddChild(lbl2)
+ h = h + lbl2:GetHeight() + 20
+
+ prev = lbl2
+ for i, obj in ipairs(tooLow) do
+ renderItem(obj, true)
+ end
+ end
+
+ border:SetHeight(h + 100)
+ end
+ end
+
+ local btn = AceGUI:Create("AmrUiButton")
+ btn:SetText(L.TeamButtonExportClose)
+ btn:SetBackgroundColor(Amr.Colors.Green)
+ btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btn:SetWidth(120)
+ btn:SetHeight(28)
+ btn:SetPoint("BOTTOM", bg.content, "BOTTOM", 0, 10)
+ btn:SetCallback("OnClick", function(widget) Amr:HideCover() end)
+ bg:AddChild(btn)
+end
+
+local function onVersionClick()
+ -- show a window with players who do not have the addon or too low a version
+ Amr:ShowCover(renderVersionWindow)
+end
+
+local function onExportRosterClick()
+
+ Amr:ShowCover(L.TeamExportRosterLoading)
+
+ Amr:ExportRosterAsync(function(txt)
+ Amr:HideCover()
+
+ if not txt then
+ Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk)
+ return
+ end
+
+ Amr:ShowCover(function(container)
+ local textbox = renderExportWindow(container, L.TeamExportRosterText, txt)
+ textbox:SetFocus(true)
+ end)
+ end)
+
+end
+
+local function onExportLootClick()
+
+ Amr:ShowCover(L.TeamExportRosterLoading)
+
+ Amr:ExportLootAsync(function(txt)
+ Amr:HideCover()
+
+ if txt == "NOGROUP" then
+ Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk)
+ return
+ elseif txt == "NOLOOT" then
+ Amr:ShowAlert(L.TeamAlertNoLoot, L.AlertOk)
+ return
+ else
+ Amr:ShowCover(function(container)
+ local textbox = renderExportWindow(container, L.TeamExportLootText, txt)
+ textbox:SetFocus(true)
+ end)
+ end
+ end)
+end
+
+local function onImportRankingsClick()
+ Amr:ShowCover(function(container)
+ local textbox = renderImportWindow(container)
+ textbox:SetFocus(true)
+ end)
+end
+
+local function renderTab(tab, container)
+
+ local lbl, lbl2
+
+ if tab == "Member" then
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(500)
+ lbl:SetFont(Amr.CreateFont("Regular", 24, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamMemberText)
+ lbl:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40)
+ container:AddChild(lbl)
+
+ -- if loot is still going on, show a button to re-show the loot window
+ if Amr.db.char.TeamOpt.LootInProgress then
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetWidth(500)
+ lbl2:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan))
+ lbl2:SetText(L.TeamMemberShowLootLabel)
+ lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -60)
+ container:AddChild(lbl2)
+
+ local btn = AceGUI:Create("AmrUiButton")
+ btn:SetWidth(180)
+ btn:SetHeight(26)
+ btn:SetBackgroundColor(Amr.Colors.Blue)
+ btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btn:SetText(L.TeamMemberShowLoot)
+ btn:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -10)
+ btn:SetCallback("OnClick", function(widget)
+ Amr:ShowLootWindow()
+ Amr:RefreshLootWindow()
+ Amr:RefreshLootRolls()
+ end)
+ container:AddChild(btn)
+ end
+
+ elseif tab == "Leader" then
+
+ local lblNum = AceGUI:Create("AmrUiLabel")
+ lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
+ lblNum:SetText("0.")
+ lblNum:SetWidth(40)
+ lblNum:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, -40)
+ container:AddChild(lblNum)
+
+ local btnVersion = AceGUI:Create("AmrUiButton")
+ btnVersion:SetText(L.TeamButtonVersionText)
+ btnVersion:SetBackgroundColor(Amr.Colors.Orange)
+ btnVersion:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnVersion:SetWidth(180)
+ btnVersion:SetHeight(26)
+ btnVersion:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
+ btnVersion:SetCallback("OnClick", onVersionClick)
+ container:AddChild(btnVersion)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamExportVersionLabel)
+ lbl:SetWidth(400)
+ lbl:SetPoint("TOPLEFT", btnVersion.frame, "TOPRIGHT", 20, 0)
+ container:AddChild(lbl)
+
+ lblNum = AceGUI:Create("AmrUiLabel")
+ lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
+ lblNum:SetText("1.")
+ lblNum:SetWidth(40)
+ lblNum:SetPoint("TOPRIGHT", btnVersion.frame, "BOTTOMLEFT", 0, -39)
+ container:AddChild(lblNum)
+
+ local btnRoster = AceGUI:Create("AmrUiButton")
+ btnRoster:SetText(L.TeamButtonExportRosterText)
+ btnRoster:SetBackgroundColor(Amr.Colors.Orange)
+ btnRoster:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnRoster:SetWidth(180)
+ btnRoster:SetHeight(26)
+ btnRoster:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
+ btnRoster:SetCallback("OnClick", onExportRosterClick)
+ container:AddChild(btnRoster)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamExportRosterLabel)
+ lbl:SetWidth(400)
+ lbl:SetPoint("TOPLEFT", btnRoster.frame, "TOPRIGHT", 20, 0)
+ container:AddChild(lbl)
+
+ lblNum = AceGUI:Create("AmrUiLabel")
+ lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
+ lblNum:SetText("2.")
+ lblNum:SetWidth(40)
+ lblNum:SetPoint("TOPRIGHT", btnRoster.frame, "BOTTOMLEFT", 0, -89)
+ container:AddChild(lblNum)
+
+ local btnLoot = AceGUI:Create("AmrUiButton")
+ btnLoot:SetText(L.TeamButtonExportLootText)
+ btnLoot:SetBackgroundColor(Amr.Colors.Orange)
+ btnLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnLoot:SetWidth(180)
+ btnLoot:SetHeight(26)
+ btnLoot:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
+ btnLoot:SetCallback("OnClick", onExportLootClick)
+ container:AddChild(btnLoot)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamExportLootLabel)
+ lbl:SetWidth(400)
+ lbl:SetPoint("TOPLEFT", btnLoot.frame, "TOPRIGHT", 20, 0)
+ container:AddChild(lbl)
+
+ lbl2 = AceGUI:Create("AmrUiLabel")
+ lbl2:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Blue))
+ lbl2:SetText(L.TeamExportLootLabel2)
+ lbl2:SetWidth(400)
+ lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -5)
+ container:AddChild(lbl2)
+
+ lblNum = AceGUI:Create("AmrUiLabel")
+ lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
+ lblNum:SetText("3.")
+ lblNum:SetWidth(40)
+ lblNum:SetPoint("TOPRIGHT", btnLoot.frame, "BOTTOMLEFT", 0, -89)
+ container:AddChild(lblNum)
+
+ local btnRank = AceGUI:Create("AmrUiButton")
+ btnRank:SetText(L.TeamButtonImportRankingsText)
+ btnRank:SetBackgroundColor(Amr.Colors.Green)
+ btnRank:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnRank:SetWidth(180)
+ btnRank:SetHeight(26)
+ btnRank:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
+ btnRank:SetCallback("OnClick", onImportRankingsClick)
+ container:AddChild(btnRank)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamImportRankingsLabel)
+ lbl:SetWidth(400)
+ lbl:SetPoint("TOPLEFT", btnRank.frame, "TOPRIGHT", 20, 0)
+ container:AddChild(lbl)
+
+ _panelStartLoot = AceGUI:Create("AmrUiPanel")
+ _panelStartLoot:SetLayout("None")
+ _panelStartLoot:SetBackgroundColor(Amr.Colors.Black, 0)
+ _panelStartLoot:SetPoint("TOPLEFT", lblNum.frame, "BOTTOMLEFT", 0, -90)
+ container:AddChild(_panelStartLoot)
+ _panelStartLoot:SetVisible(false)
+
+ lblNum = AceGUI:Create("AmrUiLabel")
+ lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
+ lblNum:SetText("4.")
+ lblNum:SetWidth(40)
+ lblNum:SetPoint("TOPLEFT", _panelStartLoot.content, "TOPLEFT")
+ _panelStartLoot:AddChild(lblNum)
+
+ _btnStartLoot = AceGUI:Create("AmrUiButton")
+ _btnStartLoot:SetText(L.TeamButtonStartLootText)
+ _btnStartLoot:SetBackgroundColor(Amr.Colors.Blue)
+ _btnStartLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ _btnStartLoot:SetWidth(180)
+ _btnStartLoot:SetHeight(26)
+ _btnStartLoot:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
+ _btnStartLoot:SetCallback("OnClick", function(widget)
+ if Amr.db.char.TeamOpt.LootInProgress then
+ Amr:ShowLootWindow()
+ Amr:RefreshLootWindow()
+ Amr:RefreshLootRolls()
+ else
+ Amr:StartLoot()
+ end
+ end)
+ _panelStartLoot:AddChild(_btnStartLoot)
+
+ _lblStartLoot = AceGUI:Create("AmrUiLabel")
+ _lblStartLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Text))
+ _lblStartLoot:SetWidth(400)
+ _lblStartLoot:SetPoint("LEFT", _btnStartLoot.frame, "RIGHT", 20, 0)
+ _panelStartLoot:AddChild(_lblStartLoot)
+ end
+
+ -- loot history shows on either tab
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamHistoryTitle)
+ lbl:SetWidth(280)
+ lbl:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", 0, -12)
+ container:AddChild(lbl)
+
+ local panelHistory = AceGUI:Create("AmrUiPanel")
+ panelHistory:SetLayout("Fill")
+ panelHistory:SetBackgroundColor(Amr.Colors.Black, 0.3)
+ panelHistory:SetPoint("TOPRIGHT", lbl.frame, "BOTTOMRIGHT", 0, -5)
+ panelHistory:SetPoint("BOTTOMLEFT", container.content, "BOTTOMRIGHT", -280, 0)
+ container:AddChild(panelHistory)
+
+ _scrollHistory = AceGUI:Create("AmrUiScrollFrame")
+ _scrollHistory:SetLayout("List")
+ panelHistory:AddChild(_scrollHistory)
+end
+
+local function renderHistory()
+ if not _scrollHistory then return end
+ _scrollHistory:ReleaseChildren()
+
+ -- history is list of objects with:
+ -- link, result, class, name
+
+ local history = Amr.db.char.TeamOpt.History
+ local historyWidth = 260
+
+ local emptyMsg = nil
+ if not IsInGroup() and not IsInRaid() then
+ emptyMsg = L.TeamHistoryNoGroup
+ elseif not history or #history == 0 then
+ emptyMsg = L.TeamHistoryEmpty
+ end
+
+ if emptyMsg then
+ local panel = AceGUI:Create("AmrUiPanel")
+ panel:SetLayout("None")
+ panel:SetBackgroundColor(Amr.Colors.Black, 0)
+ panel:SetWidth(historyWidth)
+ panel:SetHeight(30)
+ _scrollHistory:AddChild(panel)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(historyWidth)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
+ lbl:SetText(emptyMsg)
+ lbl:SetPoint("LEFT", panel.content, "LEFT", 8, 0)
+ panel:AddChild(lbl)
+ else
+ for i = #history, 1, -1 do
+ local obj = history[i]
+ local itemLink = obj.link
+
+ local panel = AceGUI:Create("AmrUiPanel")
+ panel:SetLayout("None")
+ panel:SetBackgroundColor(Amr.Colors.Black, 0)
+ panel:SetWidth(historyWidth)
+ panel:SetHeight(45)
+ _scrollHistory:AddChild(panel)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(historyWidth - 5)
+ lbl:SetWordWrap(false)
+ lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
+ lbl:SetPoint("TOPLEFT", panel.content, "TOPLEFT", 5, -5)
+ panel:AddChild(lbl)
+
+ Amr.GetItemInfo(itemLink, function(obj, name, link)
+ -- set item name, tooltip
+ obj:SetText(link:gsub("%[", ""):gsub("%]", ""))
+ Amr:SetItemTooltip(obj, link, "ANCHOR_BOTTOMRIGHT", 0, obj.frame:GetHeight())
+ end, lbl)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(historyWidth - 5)
+ lbl:SetWordWrap(false)
+ lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.White))
+
+ if obj.result == "Disenchant" then
+ lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextHeaderDisabled))
+ lbl:SetText(L.TeamLootOptionDisenchant)
+ else
+ local color = obj.class and Amr.Colors.Classes[obj.class] or Amr.Colors.TextHeaderDisabled
+ lbl:SetText((obj.result == "??" and "" or L["TeamLootOption" .. obj.result] .. ": ") .."|c" .. Amr.ColorToHex(color, 1) .. obj.name .. "|r")
+ end
+
+ lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT", 5, 8)
+ panel:AddChild(lbl)
+
+ local line = AceGUI:Create("AmrUiPanel")
+ line:SetBackgroundColor(Amr.Colors.Black, 1)
+ line:SetWidth(historyWidth)
+ line:SetHeight(1)
+ line:SetPoint("BOTTOM", panel.content, "BOTTOM")
+ panel:AddChild(line)
+ end
+ end
+end
+
+local function onTabSelected(container, event, group)
+ container:ReleaseChildren()
+
+ -- clear references to tab elements
+ _panelStartLoot = nil
+ _lblStartLoot = nil
+ _btnStartLoot = nil
+ _scrollHistory = nil
+
+ Amr.db.char.TeamOpt.Role = group
+ renderTab(group, container)
+ Amr:RefreshTeamUi()
+end
+
+-- renders the main UI for the Team Optimizer tab
+function Amr:RenderTabTeam(container)
+
+ -- splash screen to customize team optimizer ui for the user
+ if not Amr.db.char.TeamOpt.Role then
+ _panelSplash = AceGUI:Create("AmrUiPanel")
+ _panelSplash:SetLayout("None")
+ _panelSplash:SetBackgroundColor(Amr.Colors.Black, 0)
+ _panelSplash:SetPoint("TOPLEFT", container.content, "TOPLEFT")
+ _panelSplash:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
+ container:AddChild(_panelSplash)
+
+ local lblSplash = AceGUI:Create("AmrUiLabel")
+ lblSplash:SetWidth(800)
+ lblSplash:SetJustifyH("CENTER")
+ lblSplash:SetFont(Amr.CreateFont("Regular", 24, Amr.Colors.Text))
+ lblSplash:SetText(L.TeamSplashHeader)
+ lblSplash:SetPoint("TOP", _panelSplash.content, "TOP", 0, -40)
+ _panelSplash:AddChild(lblSplash)
+
+ local btn = AceGUI:Create("AmrUiButton")
+ btn:SetText(L.TeamTabLeaderText)
+ btn:SetBackgroundColor(Amr.Colors.Orange)
+ btn:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.White))
+ btn:SetWidth(280)
+ btn:SetHeight(60)
+ btn:SetPoint("TOPRIGHT", lblSplash.frame, "BOTTOM", -50, -50)
+ btn:SetCallback("OnClick", function(widget)
+ Amr.db.char.TeamOpt.Role = "Leader"
+ _panelSplash:SetVisible(false)
+ _tabs:SetVisible(true)
+ _tabs:SelectTab("Leader")
+ end)
+ _panelSplash:AddChild(btn)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(280)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamSplashLeaderLabel)
+ lbl:SetPoint("TOP", btn.frame, "BOTTOM", 0, -20)
+ _panelSplash:AddChild(lbl)
+
+ btn = AceGUI:Create("AmrUiButton")
+ btn:SetText(L.TeamTabMemberText)
+ btn:SetBackgroundColor(Amr.Colors.Orange)
+ btn:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.White))
+ btn:SetWidth(280)
+ btn:SetHeight(60)
+ btn:SetPoint("TOPLEFT", lblSplash.frame, "BOTTOM", 50, -50)
+ btn:SetCallback("OnClick", function(widget)
+ Amr.db.char.TeamOpt.Role = "Member"
+ _panelSplash:SetVisible(false)
+ _tabs:SetVisible(true)
+ _tabs:SelectTab("Member")
+ end)
+ _panelSplash:AddChild(btn)
+
+ lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(280)
+ lbl:SetJustifyH("CENTER")
+ lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
+ lbl:SetText(L.TeamSplashMemberLabel)
+ lbl:SetPoint("TOP", btn.frame, "BOTTOM", 0, -20)
+ _panelSplash:AddChild(lbl)
+ end
+
+ -- tabstrip
+ _tabs = AceGUI:Create("AmrUiTabGroup")
+ _tabs:SetLayout("None")
+ _tabs:SetTabs({
+ {text=L.TeamTabLeaderText, value="Leader", style="bold"},
+ {text=L.TeamTabMemberText, value="Member", style="bold"}
+ })
+ _tabs:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, -30)
+ _tabs:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
+ _tabs:SetCallback("OnGroupSelected", onTabSelected)
+ container:AddChild(_tabs)
+
+ local role = Amr.db.char.TeamOpt.Role
+
+ _tabs:SetVisible(not not role)
+ if role then
+ -- if a role has been chosen, select the proper tab (which will also refresh the UI)
+ _tabs:SelectTab(role)
+ else
+ -- no role, refresh the UI manually
+ self:RefreshTeamUi()
+ end
+
+end
+
+function Amr:ReleaseTabTeam()
+ _panelSplash = nil
+ _panelStartLoot = nil
+ _lblStartLoot = nil
+ _btnStartLoot = nil
+ _scrollHistory = nil
+ _tabs = nil
+end
+
+function Amr:RefreshTeamUi()
+
+ -- if rankings have been loaded, render the 'start loot' panel
+ if _panelStartLoot then
+ local rankString = Amr.db.global.TeamOpt.RankingString
+ if rankString then
+ _panelStartLoot:SetVisible(true)
+ _lblStartLoot:SetText(L.TeamStartLootLabel(#Amr.db.global.TeamOpt.Rankings))
+ _btnStartLoot:SetText(Amr.db.char.TeamOpt.LootInProgress and L.TeamButtonResumeLootText or L.TeamButtonStartLootText)
+ else
+ _panelStartLoot:SetVisible(false)
+ end
+ end
+
+ -- render loot history
+ renderHistory()
+end
+
+local function getItemIdsFromLinks(all, list)
+ for i, v in ipairs(list) do
+ local obj = Amr.ParseItemLink(v)
+ local id = Amr.GetItemUniqueId(obj)
+ if id then
+ table.insert(all, id)
+ end
+ end
+end
+
+-- update AllItems, used to determine when a new item is actually a new equippable item
+local function snapshotAllItems(data)
+
+ local all = {}
+ for k, v in pairs(data.Equipped[data.ActiveSpec]) do
+ local obj = Amr.ParseItemLink(v)
+ local id = Amr.GetItemUniqueId(obj)
+ if id then
+ table.insert(all, id)
+ end
+ end
+ getItemIdsFromLinks(all, data.BagItems)
+ getItemIdsFromLinks(all, data.BankItems)
+ getItemIdsFromLinks(all, data.VoidItems)
+
+ table.sort(all)
+ return all
+end
+
+local function sendGear(prefix, empty)
+
+ local region = Amr.RegionNames[GetCurrentRegion()]
+ local realm = GetRealmName()
+ local name = UnitName("player")
+
+ -- get all data, including inventory
+ local txt = "_"
+ if not empty then
+ local data = Amr:ExportCharacter()
+ txt = Amr.Serializer:SerializePlayerData(data, true)
+
+ -- snapshot items when gear is sent
+ Amr.db.char.TeamOpt.AllItems = snapshotAllItems(data)
+ end
+
+ local msg = string.format("%s\n%s\n%s\n%s\n%s", prefix, region, realm, name, txt)
+ Amr:SendAmrCommMessage(msg)
+end
+
+local function toPlayerKey(realm, name)
+ return name .. "-" .. realm
+end
+
+
+------------------------------------------------------------------------------------------------
+-- Loot Export
+------------------------------------------------------------------------------------------------
+
+-- prune out any characters no longer in the player's group
+local function pruneGearForItemExport()
+
+ local newInfo = {}
+ local units = Amr:GetGroupUnitIdentifiers()
+
+ for i, unitId in ipairs(units) do
+ local realm, name = Amr:GetRealmAndName(unitId)
+ if realm then
+ local key = toPlayerKey(realm, name)
+ newInfo[key] = Amr.db.global.TeamOpt.LootGear[key]
+ end
+ end
+
+ Amr.db.global.TeamOpt.LootGear = newInfo
+end
+
+local function scanMasterLoot()
+ -- only care if we are in a raid or group
+ if not IsInGroup() and not IsInRaid() then return end
+
+ -- we only care about the master looter
+ if not IsMasterLooter() then return end
+
+ -- guid of the unit being looted
+ local npcGuid = UnitGUID("target")
+ if not npcGuid then
+ -- this could wack shit out... but no raid bosses drop loot from containers right now, so should be fine
+ npcGuid = "container"
+ end
+
+ -- if we already have loot data for this unit, then we can ignore
+ if Amr.db.char.TeamOpt.LootGuid == npcGuid then return end
+
+ local loot = {}
+ for i = 1, GetNumLootItems() do
+ --local texture, item, quantity, quality, locked = GetLootSlotInfo(i)
+ local lootType = GetLootSlotType(i)
+ if lootType == 1 then
+ local link = GetLootSlotLink(i)
+ table.insert(loot, link)
+ end
+ end
+
+ Amr.db.char.TeamOpt.LootGuid = npcGuid
+ Amr.db.char.TeamOpt.Loot = loot
+
+ -- publish loot information to everyone else running the addon in case team optimizer user is not the master looter
+ local msg = _messagePrefixes.ItemExportLoot .. "\n" .. npcGuid .. "\n" .. table.concat(loot, "\n")
+ Amr:SendAmrCommMessage(msg)
+end
+
+local function onLootReceived(parts)
+
+ Amr.db.char.TeamOpt.LootGuid = parts[2]
+
+ local loot = {}
+ for i = 3, #parts do
+ table.insert(loot, parts[i])
+ end
+ Amr.db.char.TeamOpt.Loot = loot
+end
+
+local function onLeaveGroup()
+ -- if the current player is no longer in a group or raid, finish any looting in progress
+ Amr:FinishLoot(true)
+
+ -- clear loot when leave a group
+ Amr.db.char.TeamOpt.Loot = {}
+ Amr.db.char.TeamOpt.LootGuid = nil
+ Amr.db.global.TeamOpt.LootGear = {}
+end
+
+local function onGroupChanged()
+
+ if not IsInGroup() and not IsInRaid() then
+ onLeaveGroup()
+ end
+end
+
+
+local _lootExPlayersRemaining = 0
+local _lootExRoster = nil
+local _lootExCallback = nil
+
+local function serializeLootExport()
+ if not IsInGroup() and not IsInRaid() then return "NOGROUP" end
+
+ local loot = Amr.db.char.TeamOpt.Loot
+ if not loot or #loot == 0 then return "NOLOOT" end
+
+ local itemObjects = {}
+ for i, link in ipairs(loot) do
+ local obj = Amr.ParseItemLink(link)
+ if obj then
+ table.insert(itemObjects, obj)
+ end
+ end
+
+ -- DEBUG: just grab all currently equipped items
+ --[[
+ itemObjects = {}
+ local blah = Amr:ExportCharacter()
+ for k, v in pairs(blah.Equipped[blah.ActiveSpec]) do
+ local obj = Amr.ParseItemLink(v)
+ if obj then
+ table.insert(itemObjects, obj)
+ end
+ end
+ ]]
+
+ local parts = {}
+
+ -- unique ids of items
+ local lootPart = {}
+ for i, obj in ipairs(itemObjects) do
+ table.insert(lootPart, Amr.GetItemUniqueId(obj))
+ end
+ table.insert(parts, table.concat(lootPart, ";"))
+
+ -- gear for players who have gained loot since the last item import or roster export
+ pruneGearForItemExport()
+ local lootGear = Amr.db.global.TeamOpt.LootGear
+ for k, v in pairs(lootGear) do
+ table.insert(parts, v)
+ end
+
+ return table.concat(parts, "\n")
+end
+
+local function onLootExportCompleted()
+
+ -- fill in LootGear with just those players who have changed
+ Amr.db.global.TeamOpt.LootGear = _lootExRoster
+
+ if _lootExCallback then
+ local txt = serializeLootExport()
+ _lootExCallback(txt)
+ end
+
+ -- reset state
+ _lootExPlayersRemaining = 0
+ _lootExRoster = nil
+ _lootExCallback = nil
+end
+
+-- called when this player's gear info has been requested by someone exporting loot
+local function onGearForLootExportRequested()
+
+ local hasNewItem = false
+ local oldItems = Amr.db.char.TeamOpt.AllItems
+
+ if oldItems and #oldItems > 0 then
+ -- see if any new equippable items have been gained by comparing to the last snapshot
+ local data = Amr:ExportCharacter()
+ local allItems = snapshotAllItems(data)
+
+ if #oldItems ~= #allItems then
+ hasNewItem = true
+ else
+ -- go through items from front to back, if there are any that don't match then something has changed
+ for i = 1, #allItems do
+ local oldItem = oldItems[i]
+ local newItem = allItems[i]
+ if oldItem ~= newItem then
+ hasNewItem = true
+ break
+ end
+ end
+ end
+ end
+
+ -- whenever a new item is received, send out updated gear information that should be added to the next item export
+ sendGear(_messagePrefixes.ItemExportGear, not hasNewItem)
+end
+
+local function onGearForLootExportReceived(region, realm, name, data)
+ -- if I am not listening for incoming gear data for an item export, then ignore this message
+ if _lootExPlayersRemaining == 0 then return end
+
+ local key = toPlayerKey(realm, name)
+ if not data or data == "_" then
+ _lootExRoster[key] = nil
+ else
+ _lootExRoster[key] = data
+ end
+
+ _lootExPlayersRemaining = _lootExPlayersRemaining - 1
+ if _lootExPlayersRemaining <= 0 then
+ onLootExportCompleted()
+ end
+end
+
+-- Export the current loot, including any known gear data for players with the in-game addon, but only if it has changed since the last snapshot.
+-- This is asynchronous because it needs to wait for gear data to arrive from each player.
+function Amr:ExportLootAsync(callback)
+
+ if not IsInGroup() and not IsInRaid() then
+ callback("NOGROUP")
+ end
+
+ local loot = Amr.db.char.TeamOpt.Loot
+ if not loot or #loot == 0 then
+ callback("NOLOOT")
+ end
+
+ local playersNoGear = {}
+ _lootExPlayersRemaining = 0
+
+ local units = self:GetGroupUnitIdentifiers()
+ for i, unitId in ipairs(units) do
+ local realm, name = self:GetRealmAndName(unitId)
+ if realm then
+ local ver = self:GetAddonVersion(realm, name)
+ local key = toPlayerKey(realm, name)
+
+ if ver >= Amr.MIN_ADDON_VERSION then
+ _lootExPlayersRemaining = _lootExPlayersRemaining + 1
+ else
+ table.insert(playersNoGear, unitId)
+ end
+ end
+ end
+
+ _lootExRoster = {}
+ _lootExCallback = callback
+
+ if _lootExPlayersRemaining > 0 then
+ -- send a message to receive player data, when the last player is received onLootExportCompleted will be called
+ Amr:SendAmrCommMessage(_messagePrefixes.ItemExportRequestGear)
+ else
+ -- don't need to wait for anybody, just call immediately
+ onLootExportCompleted()
+ end
+end
+
+
+------------------------------------------------------------------------------------------------
+-- Roster Export
+------------------------------------------------------------------------------------------------
+
+local _rosterPlayersRemaining = 0
+local _roster = nil
+local _rosterCallback = nil
+
+local function onRosterCompleted()
+
+ if _rosterCallback then
+ -- serialize the roster
+ local parts = {}
+ for key, data in pairs(_roster) do
+ table.insert(parts, data)
+ end
+ local msg = table.concat(parts, "\n")
+
+ -- send to callback
+ _rosterCallback(msg)
+ end
+
+ -- reset state
+ _rosterPlayersRemaining = 0
+ _roster = nil
+ _rosterCallback = nil
+
+ -- clear out loot gear needed, an export will refresh everyone at the time of export
+ Amr.db.global.TeamOpt.LootGear = {}
+end
+
+-- called when this player's gear info has been requested by someone exporting the raid roster
+local function onGearForRosterRequested()
+
+ sendGear(_messagePrefixes.RosterGear)
+end
+
+local function onGearForRosterReceived(region, realm, name, data)
+ -- if I am not listening for incoming gear data for the roster, then ignore this message
+ if _rosterPlayersRemaining == 0 then return end
+
+ local key = toPlayerKey(realm, name)
+ _roster[key] = data
+
+ _rosterPlayersRemaining = _rosterPlayersRemaining - 1
+ if _rosterPlayersRemaining <= 0 then
+ onRosterCompleted()
+ end
+end
+
+-- Export the current roster, including any known gear data for players with the in-game addon.
+-- This is asynchronous because it needs to wait for gear data to arrive from each player.
+function Amr:ExportRosterAsync(callback)
+ if not IsInGroup() and not IsInRaid() then
+ callback()
+ return
+ end
+
+ local playersNoGear = {}
+ _rosterPlayersRemaining = 0
+
+ local units = self:GetGroupUnitIdentifiers()
+ for i, unitId in ipairs(units) do
+ local realm, name = self:GetRealmAndName(unitId)
+ if realm then
+ local ver = self:GetAddonVersion(realm, name)
+ local key = toPlayerKey(realm, name)
+
+ if ver >= Amr.MIN_ADDON_VERSION then
+ _rosterPlayersRemaining = _rosterPlayersRemaining + 1
+ else
+ table.insert(playersNoGear, unitId)
+ end
+ end
+ end
+
+ -- fill the roster with any players who can't send us data
+ _roster = {}
+ for i, unitId in ipairs(playersNoGear) do
+ local realm, name = self:GetRealmAndName(unitId)
+ if realm then
+ local key = toPlayerKey(realm, name)
+ local obj = {
+ Region = Amr.RegionNames[GetCurrentRegion()],
+ Realm = realm,
+ Name = name
+ }
+ _roster[key] = Amr.Serializer:SerializePlayerIdentity(obj)
+ end
+ end
+
+ _rosterCallback = callback
+
+ if _rosterPlayersRemaining > 0 then
+ -- send a message to receive player data, when the last player is received onRosterCompleted will be called
+ Amr:SendAmrCommMessage(_messagePrefixes.RosterRequestGear)
+ else
+ -- don't need to wait for anybody, just call immediately
+ onRosterCompleted()
+ end
+end
+
+
+------------------------------------------------------------------------------------------------
+-- Ranking Import
+------------------------------------------------------------------------------------------------
+
+-- helper to parse import item identifier format into an item object
+local function parseItemIdentifier(ident)
+
+ local parts = { strsplit(":", ident) }
+ local item = {}
+ item.id = tonumber(parts[1])
+ item.enchantId = 0
+ item.gemIds = { 0, 0, 0, 0 }
+ item.suffixId = math.abs(tonumber(parts[2]))
+ item.upgradeId = tonumber(parts[3])
+
+ if #parts > 3 then
+ item.bonusIds = {}
+ for b = 4, #parts do
+ table.insert(item.bonusIds, tonumber(parts[b]))
+ end
+ table.sort(item.bonusIds)
+ end
+
+ return item
+end
+
+function Amr:ParseRankingString(data)
+ local rankings = {}
+
+ local player = Amr:ExportCharacter()
+ local myUnitId = Amr:GetUnitId(player.Realm, player.Name)
+ local ml = IsMasterLooter()
+
+ local itemList = { strsplit("\n", data) }
+ for i = 1, #itemList do
+ local ranking = {}
+
+ local itemParts = { strsplit("_", itemList[i]) }
+
+ -- first part has the item identifier
+ ranking.item = parseItemIdentifier(itemParts[1])
+
+ -- second part has item info
+ local infoParts = { strsplit(";", itemParts[2]) }
+ ranking.itemInfo = {
+ slot = infoParts[1],
+ subclass = infoParts[2],
+ weaponType = infoParts[3],
+ armorType = infoParts[4]
+ }
+
+ local meInList = false
+
+ -- parse each ranking
+ ranking.ranks = {}
+ for j = 3, #itemParts do
+ local rankParts = { strsplit(";", itemParts[j]) }
+
+ local rank = {}
+ rank.realm = rankParts[1]
+ rank.name = rankParts[2]
+ rank.specId = tonumber(rankParts[3])
+ if rankParts[4] ~= "--" then
+ rank.equipped = parseItemIdentifier(rankParts[4])
+ end
+ rank.score = tonumber(rankParts[5])
+ rank.isEquipped = rankParts[6] == "t"
+ rank.notRanked = rankParts[7] == "t"
+ rank.offspec = rankParts[8] == "t"
+ rank.enchantingSkill = tonumber(rankParts[9])
+
+ table.insert(ranking.ranks, rank)
+
+ if myUnitId == Amr:GetUnitId(rank.realm, rank.name) then
+ meInList = true
+ rank.isMasterLooter = ml
+ end
+ end
+
+ -- if the current player is the master looter and he is not in the list, then add him at the end
+ if ml and not meInList then
+ local rank = {
+ realm = player.Realm,
+ name = player.Name,
+ specId = player.Specs[player.ActiveSpec],
+ equipped = "--",
+ score = 0,
+ isEquipped = false,
+ notRanked = true,
+ offspec = false,
+ enchantingSkill = 0,
+ isMasterLooter = true
+ }
+ table.insert(ranking.ranks, rank)
+ end
+
+ table.insert(rankings, ranking)
+ end
+
+ return rankings
+end
+
+-- import rankings from the website, save into the database, returns a string error if can't import for some reason
+function Amr:ImportRankings(data)
+
+ if not data or string.len(data) == 0 then
+ return L.ImportErrorEmpty
+ end
+
+ local success, rankings = pcall(Amr.ParseRankingString, self, data)
+
+ if not success then
+ return L.ImportErrorFormat
+ end
+
+ -- finish any looting in progress, effectively canceling it, user will have to press Start Loot again
+ Amr:FinishLoot()
+
+ -- save the rankings
+ Amr.db.global.TeamOpt.Rankings = rankings
+ Amr.db.global.TeamOpt.RankingString = data
+
+ -- clear loot gear needed on successful ranking import
+ Amr.db.global.TeamOpt.LootGear = {}
+end
+
+
+------------------------------------------------------------------------------------------------
+-- Loot Distribution
+------------------------------------------------------------------------------------------------
+
+function Amr:StartLoot()
+
+ if not IsInGroup() and not IsInRaid() then
+ Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk)
+ return
+ end
+
+ -- broadcast the loot data to everyone, this triggers the loot window to show
+ local msg = string.format("%s\n%s", Amr.LootMessagePrefixes.Start, Amr.db.global.TeamOpt.RankingString)
+ Amr:SendAmrCommMessage(msg)
+end
+
+function Amr:FinishLoot(clearHistory)
+
+ -- reset all state
+ Amr.db.char.TeamOpt.LootInProgress = false
+
+ -- don't reset these for now... only reset these if someone leaves a group
+ --Amr.db.char.TeamOpt.Loot = {}
+ --Amr.db.char.TeamOpt.LootGuid = nil
+ --Amr.db.global.TeamOpt.LootGear = {}
+
+ Amr.db.global.TeamOpt.Rankings = {}
+ Amr.db.global.TeamOpt.RankingString = nil
+
+ Amr.db.char.TeamOpt.Rolls = {}
+
+ if clearHistory then
+ Amr.db.char.TeamOpt.History = {}
+ end
+
+ -- close the loot window
+ Amr:HideLootWindow()
+
+ -- re-render the team optimizer UI
+ Amr:RefreshTeamUi()
+end
+
+
+------------------------------------------------------------------------------------------------
+-- Synchronization
+------------------------------------------------------------------------------------------------
+local _waitingForSync = false
+
+-- check if we need to synchronize
+local function checkSync()
+ -- if loot is in progress and this person is not the ML, send a request to synchronize on startup, this player may have missed some data
+ if not IsMasterLooter() and Amr.db.char.TeamOpt.LootInProgress then
+ _waitingForSync = true
+ Amr:SendAmrCommMessage(_messagePrefixes.SyncRequest)
+ end
+end
+
+-- send data to anyone who needs to synchronize their loot data: history, rolls, rankings
+local function sendSyncData()
+ -- only the master looter sends sync data to ensure that everyone gets the same stuff and we don't spam
+ if not IsMasterLooter() then return end
+
+ local msgParts = {}
+ table.insert(msgParts, _messagePrefixes.Sync)
+ table.insert(msgParts, Amr:Serialize(Amr.db.char.TeamOpt.History))
+ table.insert(msgParts, Amr:Serialize(Amr.db.char.TeamOpt.Rolls))
+ table.insert(msgParts, Amr:Serialize(Amr.db.global.TeamOpt.Rankings))
+
+ Amr:SendAmrCommMessage(table.concat(msgParts, "\n"))
+end
+
+local function receiveSyncData(parts)
+ if not _waitingForSync then return end
+ _waitingForSync = false
+
+ local success, obj = Amr:Deserialize(parts[2])
+ if success then
+ Amr.db.char.TeamOpt.History = obj
+ end
+
+ success, obj = Amr:Deserialize(parts[3])
+ if success then
+ Amr.db.char.TeamOpt.Rolls = obj
+ end
+
+ success, obj = Amr:Deserialize(parts[4])
+ if success then
+ Amr.db.global.TeamOpt.Rankings = obj
+ end
+
+ -- refresh any windows that may be visible
+ Amr:RefreshTeamUi()
+ Amr:RefreshLootWindow()
+ Amr:RefreshLootRolls()
+end
+
+
+function Amr:ProcessTeamMessage(message)
+
+ local parts = {}
+ for part in string.gmatch(message, "([^\n]+)") do
+ table.insert(parts, part)
+ end
+
+ local prefix = parts[1]
+
+ if prefix == _messagePrefixes.RosterRequestGear then
+ -- request for me to send my gear data
+ onGearForRosterRequested()
+ elseif prefix == _messagePrefixes.ItemExportRequestGear then
+ -- request for me to send my gear data
+ onGearForLootExportRequested()
+ elseif prefix == _messagePrefixes.ItemExportLoot then
+ -- the last loot that dropped
+ onLootReceived(parts)
+ elseif prefix == _messagePrefixes.SyncRequest then
+ sendSyncData()
+ elseif prefix == _messagePrefixes.Sync then
+ receiveSyncData(parts)
+ elseif prefix == Amr.LootMessagePrefixes.Start then
+ Amr:OnStartLootReceived(parts)
+ elseif prefix == Amr.LootMessagePrefixes.Roll then
+ Amr:OnLootRollReceived(parts)
+ elseif prefix == Amr.LootMessagePrefixes.Veto then
+ Amr:OnLootVetoReceived(parts)
+ elseif prefix == Amr.LootMessagePrefixes.Rand then
+ Amr:OnLootRandReceived(parts)
+ elseif prefix == Amr.LootMessagePrefixes.Give then
+ Amr:OnLootGiveReceived(parts)
+ elseif prefix == Amr.LootMessagePrefixes.Finish then
+ Amr:FinishLoot()
+ else
+ -- message will be of format: prefix\nregion\nrealm\nname\n[stuff]
+ local region = parts[2]
+ local realm = parts[3]
+ local name = parts[4]
+ local data = parts[5]
+
+ if prefix == _messagePrefixes.RosterGear then
+ -- receive gear data from someone
+ onGearForRosterReceived(region, realm, name, data)
+ elseif prefix == _messagePrefixes.ItemExportGear then
+ -- receive gear data for item export
+ onGearForLootExportReceived(region, realm, name, data)
+ end
+ end
+end
+
+function Amr:InitializeTeamOpt()
+
+ if not IsInGroup() and not IsInRaid() then
+ onLeaveGroup()
+ end
+
+ Amr:AddEventHandler("LOOT_OPENED", scanMasterLoot)
+ Amr:AddEventHandler("GROUP_ROSTER_UPDATE", onGroupChanged)
+
+ checkSync()
+end
diff -r 75431c084aa0 -r 01b63b8ed811 amr-constants.lua
--- a/amr-constants.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,323 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- item link format: |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:upgradeId:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r
-
--- get an object with all of the parts fo the item link format that we care about
-function AskMrRobot.parseItemLink(itemLink)
- if not itemLink then return nil end
-
- local str = string.match(itemLink, "|Hitem:([\-%d:]+)|")
- if not str then return nil end
-
- local parts = { strsplit(":", str) }
-
- local item = {}
- item.id = tonumber(parts[1])
- item.enchantId = tonumber(parts[2])
- item.gemIds = { tonumber(parts[3]), tonumber(parts[4]), tonumber(parts[5]), tonumber(parts[6]) }
- item.suffixId = math.abs(tonumber(parts[7])) -- convert suffix to positive number, that's what we use in our code
- --item.uniqueId = tonumber(parts[8])
- --item.level = tonumber(parts[9])
- item.upgradeId = tonumber(parts[10])
- --item.difficultyId = tonumber(parts[11])
-
- local numBonuses = tonumber(parts[12])
- if numBonuses and numBonuses > 0 then
- item.bonusIds = {}
- for i = 13, 12 + numBonuses do
- table.insert(item.bonusIds, tonumber(parts[i]))
- end
- table.sort(item.bonusIds)
- end
-
- return item
-end
-
--- item link format: |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:upgradeId:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r
-
-function AskMrRobot.createItemLink(itemObj)
-
- if itemObj == nil or itemObj.id == nil or itemObj.id == 0 then return nil end
-
- local parts = {}
- table.insert(parts, "item")
- table.insert(parts, itemObj.id)
- table.insert(parts, itemObj.enchantId)
- table.insert(parts, itemObj.gemIds[1])
- table.insert(parts, itemObj.gemIds[2])
- table.insert(parts, itemObj.gemIds[3])
- table.insert(parts, itemObj.gemIds[4])
-
- if itemObj.suffixId == 0 then
- table.insert(parts, 0)
- else
- table.insert(parts, -math.abs(itemObj.suffixId))
- end
-
- table.insert(parts, 0)
- table.insert(parts, UnitLevel("player"))
- table.insert(parts, itemObj.upgradeId)
- table.insert(parts, 0)
-
- if itemObj.bonusIds then
- table.insert(parts, #itemObj.bonusIds)
- for i,v in ipairs(itemObj.bonusIds) do
- table.insert(parts, v)
- end
- end
-
- return table.concat(parts, ":")
-end
-
-function AskMrRobot.createGemLink(gemId)
-
-end
-
--- convenience to get just the item id (or 0 if not a valid link) from an item link
-function AskMrRobot.getItemIdFromLink(itemLink)
- if not itemLink then return 0 end
- local parts = { strsplit(":", itemLink) }
- local id = tonumber(parts[2])
- return (id and id ~= 0 and id or 0)
-end
-
-function AskMrRobot.getItemUniqueId(item, noUpgrade)
- if item == nil then return "" end
- local ret = item.id .. ""
- if item.bonusIds then
- for i = 1, #item.bonusIds do
- ret = ret .. "b" .. item.bonusIds[i]
- end
- end
- if item.suffixId ~= 0 then
- ret = ret .. "s" .. item.suffixId
- end
- if not noUpgrade and item.upgradeId ~= 0 then
- ret = ret .. "u" .. item.upgradeId
- end
- return ret
-end
-
-AskMrRobot.instanceIds = {
- Auchindoun = 1182,
- BloodmaulSlagMines = 1175,
- GrimrailDepot = 1208,
- IronDocks = 1195,
- ShadowmoonBurialGrounds = 1176,
- Skyreach = 1209,
- TheEverbloom = 1279,
- UpperBlackrockSpire = 1358,
- Highmaul = 1228,
- BlackrockFoundry = 1205
-}
-
--- instances that we currently support logging for
-AskMrRobot.supportedInstanceIds = {
- --[1182] = true,
- --[1175] = true,
- --[1208] = true,
- --[1195] = true,
- --[1176] = true,
- --[1209] = true,
- --[1279] = true,
- --[1358] = true,
- [1228] = true,
- [1205] = true
-}
-
--- returns true if currently in a supported instance
-function AskMrRobot.IsSupportedInstance()
-
- local zone, _, difficultyIndex, _, _, _, _, instanceMapID = GetInstanceInfo()
- if AskMrRobot.supportedInstanceIds[tonumber(instanceMapID)] then
- return true
- else
- return false
- end
-end
-
-function AskMrRobot.IsSupportedInstanceId(instanceMapID)
- if AskMrRobot.supportedInstanceIds[tonumber(instanceMapID)] then
- return true
- else
- return false
- end
-end
-
-AskMrRobot.regionNames = {
- [1] = "US",
- [2] = "KR",
- [3] = "EU",
- [4] = "TW",
- [5] = "CN"
-}
-
-AskMrRobot.classIds = {
- ["NONE"] = 0,
- ["DEATHKNIGHT"] = 1,
- ["DRUID"] = 2,
- ["HUNTER"] = 3,
- ["MAGE"] = 4,
- ["MONK"] = 5,
- ["PALADIN"] = 6,
- ["PRIEST"] = 7,
- ["ROGUE"] = 8,
- ["SHAMAN"] = 9,
- ["WARLOCK"] = 10,
- ["WARRIOR"] = 11,
-}
-
-AskMrRobot.professionIds = {
- ["None"] = 0,
- ["Mining"] = 1,
- ["Skinning"] = 2,
- ["Herbalism"] = 3,
- ["Enchanting"] = 4,
- ["Jewelcrafting"] = 5,
- ["Engineering"] = 6,
- ["Blacksmithing"] = 7,
- ["Leatherworking"] = 8,
- ["Inscription"] = 9,
- ["Tailoring"] = 10,
- ["Alchemy"] = 11,
- ["Fishing"] = 12,
- ["Cooking"] = 13,
- ["First Aid"] = 14,
- ["Archaeology"] = 15
-}
-
-AskMrRobot.raceIds = {
- ["None"] = 0,
- ["BloodElf"] = 1,
- ["Draenei"] = 2,
- ["Dwarf"] = 3,
- ["Gnome"] = 4,
- ["Human"] = 5,
- ["NightElf"] = 6,
- ["Orc"] = 7,
- ["Tauren"] = 8,
- ["Troll"] = 9,
- ["Scourge"] = 10,
- ["Undead"] = 10,
- ["Goblin"] = 11,
- ["Worgen"] = 12,
- ["Pandaren"] = 13
-}
-
-AskMrRobot.factionIds = {
- ["None"] = 0,
- ["Alliance"] = 1,
- ["Horde"] = 2
-}
-
-AskMrRobot.specIds = {
- [250] = 1, -- DeathKnightBlood
- [251] = 2, -- DeathKnightFrost
- [252] = 3, -- DeathKnightUnholy
- [102] = 4, -- DruidBalance
- [103] = 5, -- DruidFeral
- [104] = 6, -- DruidGuardian
- [105] = 7, -- DruidRestoration
- [253] = 8, -- HunterBeastMastery
- [254] = 9, -- HunterMarksmanship
- [255] = 10, -- HunterSurvival
- [62] = 11, -- MageArcane
- [63] = 12, -- MageFire
- [64] = 13, -- MageFrost
- [268] = 14, -- MonkBrewmaster
- [270] = 15, -- MonkMistweaver
- [269] = 16, -- MonkWindwalker
- [65] = 17, -- PaladinHoly
- [66] = 18, -- PaladinProtection
- [70] = 19, -- PaladinRetribution
- [256] = 20, -- PriestDiscipline
- [257] = 21, -- PriestHoly
- [258] = 22, -- PriestShadow
- [259] = 23, -- RogueAssassination
- [260] = 24, -- RogueCombat
- [261] = 25, -- RogueSubtlety
- [262] = 26, -- ShamanElemental
- [263] = 27, -- ShamanEnhancement
- [264] = 28, -- ShamanRestoration
- [265] = 29, -- WarlockAffliction
- [266] = 30, -- WarlockDemonology
- [267] = 31, -- WarlockDestruction
- [71] = 32, -- WarriorArms
- [72] = 33, -- WarriorFury
- [73] = 34 -- WarriorProtection
-}
-
--- reverse map of our spec ID to the game's spec ID
-AskMrRobot.gameSpecIds = {}
-for k,v in pairs(AskMrRobot.specIds) do
- AskMrRobot.gameSpecIds[v] = k
-end
-
--- lookup from our socket color ID to string
-AskMrRobot.socketColorIds = {
- [0] = "Prismatic",
- [1] = "Red",
- [2] = "Yellow",
- [3] = "Blue",
- [4] = "Meta",
- [5] = "Cogwheel",
- [6] = "ShaTouched"
-}
-
--- map of game slot names to slot IDs (same both in our code and in-game)
-AskMrRobot.slotNameToId = {
- ["HeadSlot"] = 1,
- ["NeckSlot"] = 2,
- ["ShoulderSlot"] = 3,
- ["ChestSlot"] = 5,
- ["WaistSlot"] = 6,
- ["LegsSlot"] = 7,
- ["FeetSlot"] = 8,
- ["WristSlot"] = 9,
- ["HandsSlot"] = 10,
- ["Finger0Slot"] = 11,
- ["Finger1Slot"] = 12,
- ["Trinket0Slot"] = 13,
- ["Trinket1Slot"] = 14,
- ["BackSlot"] = 15,
- ["MainHandSlot"] = 16,
- ["SecondaryHandSlot"] = 17
-}
-
--- map of slot ID to display text
-AskMrRobot.slotDisplayText = {
- [1] = _G["HEADSLOT"],
- [2] = _G["NECKSLOT"],
- [3] = _G["SHOULDERSLOT"],
- [5] = _G["CHESTSLOT"],
- [6] = _G["WAISTSLOT"],
- [7] = _G["LEGSSLOT"],
- [8] = _G["FEETSLOT"],
- [9] = _G["WRISTSLOT"],
- [10] = _G["HANDSSLOT"],
- [11] = _G["FINGER0SLOT"] .. " 1",
- [12] = _G["FINGER1SLOT"] .. " 2",
- [13] = _G["TRINKET0SLOT"] .. " 1",
- [14] = _G["TRINKET1SLOT"] .. " 2",
- [15] = _G["BACKSLOT"],
- [16] = _G["MAINHANDSLOT"],
- [17] = _G["SECONDARYHANDSLOT"]
-}
-
--- all slot IDs that we care about, ordered in our standard display order
-AskMrRobot.slotIds = { 16, 17, 1, 2, 3, 15, 5, 9, 10, 6, 7, 8, 11, 12, 13, 14 }
-
--- cache slot orders to make slot sorting easier
-local _slotIdToOrder = {}
-for i = 1, #AskMrRobot.slotIds do
- _slotIdToOrder[AskMrRobot.slotIds[i]] = i
-end
-
--- given a table where the keys are slot IDs, sort in the standard slot order
-function AskMrRobot.sortSlots(t)
- return AskMrRobot.spairs(t, function(x, a, b)
- if a == nil and b == nil then return 0 end
- return _slotIdToOrder[a] < _slotIdToOrder[b]
- end)
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 localization/enUS.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/localization/enUS.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,375 @@
+--[[-------------------------------------------------------------------------------------------------------------
+Master Localization File (English)
+
+Instructions for Translators:
+1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German.
+2. At the top, replace "enUS" in the first code line with your locale.
+3. Change all the English strings in your file as appropriate.
+
+Note that a couple of the "strings" are functions that are provided variables. Feel free to modify these
+functions as necessary to output an appropriately worded statement in your language (but don't change the parameters).
+If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you.
+---------------------------------------------------------------------------------------------------------------]]
+
+-- replace enUS with your locale
+local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "enUS", true)
+
+if L then
+
+
+--[[----------------------------------------------------------------------
+General
+------------------------------------------------------------------------]]
+
+-- stat strings for e.g. displaying gem/enchant abbreviations, make as short as possible without being confusing/ambiguous
+L.StatsShort = {
+ ["Strength"] = "Str",
+ ["Agility"] = "Agi",
+ ["Intellect"] = "Int",
+ ["CriticalStrike"] = "Crit",
+ ["Haste"] = "Haste",
+ ["Mastery"] = "Mastery",
+ ["Multistrike"] = "Multi",
+ ["Versatility"] = "Vers",
+ ["BonusArmor"] = "Armor",
+ ["Spirit"] = "Spirit",
+ ["Dodge"] = "Dodge",
+ ["Parry"] = "Parry",
+ ["MovementSpeed"] = "Speed",
+ ["Avoidance"] = "Avoid",
+ ["Stamina"] = "Stam",
+ ["Armor"] = "Armor",
+ ["AttackPower"] = "AP",
+ ["SpellPower"] = "SP",
+ ["PvpResilience"] = "PvP Res",
+ ["PvpPower"] = "PvP Pow",
+}
+
+L.InstanceNames = {
+ [1228] = "Highmaul",
+ [1205] = "Blackrock Foundry",
+ [1182] = "Auchindoun",
+ [1175] = "Bloodmaul Slag Mines",
+ [1208] = "Grimrail Depot",
+ [1195] = "Iron Docks",
+ [1176] = "Shadowmoon Burial Grounds",
+ [1209] = "Skyreach",
+ [1279] = "The Everbloom",
+ [1358] = "Upper Blackrock Spire"
+}
+
+L.DifficultyNames = {
+ [17] = "LFR",
+ [14] = "Normal",
+ [15] = "Heroic",
+ [16] = "Mythic"
+}
+
+L.WeaponTypes = {
+ None = "None",
+ Axe = "Axe",
+ Mace = "Mace",
+ Sword = "Sword",
+ Fist = "Fist",
+ Dagger = "Dagger",
+ Staff = "Staff",
+ Polearm = "Polearm",
+ OffHand = "Off Hand",
+ Shield = "Shield",
+ Wand = "Wand",
+ Bow = "Bow",
+ Gun = "Gun",
+ Crossbow = "Crossbow"
+}
+
+L.ArmorTypes = {
+ None = "None",
+ Plate = "Plate",
+ Mail = "Mail",
+ Leather = "Leather",
+ Cloth = "Cloth"
+}
+
+L.OneHand = "One-Hand"
+L.TwoHand = "Two-Hand"
+L.OffHand = "Off Hand"
+
+
+--[[----------------------------------------------------------------------
+Main UI
+------------------------------------------------------------------------]]
+L.AlertOk = "OK"
+L.CoverCancel = "cancel"
+
+L.MinimapTooltip =
+[[Left Click to open the Ask Mr. Robot window.
+
+Right Click to change spec and equip your saved gear for that spec.
+
+Ctrl + Left Click to mark a fight as a wipe.]]
+
+L.MainStatusText = function(version, url)
+ return version .. " loaded. Documentation available at " .. url
+end
+
+L.TabExportText = "Export"
+L.TabGearText = "Gear"
+L.TabLogText = "Combat Logs"
+L.TabTeamText = "Team Optimizer"
+L.TabOptionsText = "Options"
+
+L.VersionChatTitle = "AMR Addon Version:"
+L.VersionChatNotInstalled = "NOT INSTALLED"
+L.VersionChatNotGrouped = "You are not in a group or raid!"
+
+
+--[[----------------------------------------------------------------------
+Export Tab
+------------------------------------------------------------------------]]
+L.ExportTitle = "Export Instructions"
+L.ExportHelp1 = "1. Copy the text below by pressing Ctrl+C (or Apple+C on a Mac)"
+L.ExportHelp2 = "2. Go to http://www.askmrrobot.com/wow/player and load your character"
+L.ExportHelp3 = "3. Press the green IMPORT (from addon) link just above your character name"
+L.ExportHelp4 = "4. Paste into the textbox on the website and press Import!"
+
+L.ExportSplashTitle = "Getting Started"
+L.ExportSplashSubtitle = "This is your first time using the new version of the addon. Do the following things to initialize your item database:"
+L.ExportSplash1 = "1. Activate each of your specs once and equip your latest gear for each spec"
+L.ExportSplash2 = "2. Open your bank and leave it open for at least two seconds"
+L.ExportSplash3 = "3. If you have gear in void storage, open it and leave it open for at least two seconds"
+L.ExportSplashClose = "Continue"
+
+
+--[[----------------------------------------------------------------------
+Gear Tab
+------------------------------------------------------------------------]]
+L.GearImportNote = "Click Import to paste data from the website."
+L.GearTabPrimary = "Primary Spec"
+L.GearTabSecondary = "Secondary Spec"
+L.GearBlank = "You have not loaded any gear for this spec yet."
+L.GearBlank2 = "Go to askmrrobot.com to optimize your gear, then use the Import button to the left."
+L.GearButtonEquip = function(spec)
+ -- spec 1 is primary, 2 is secondary
+ return string.format("Activate %s Spec and Equip Gear", spec == 1 and "Primary" or "Secondary")
+end
+L.GearButtonShop = "Show Shopping List"
+
+L.GearEquipErrorCombat = "Cannot change spec/gear while in combat!"
+L.GearEquipErrorEmpty = "No saved gear set could be found for the current spec."
+L.GearEquipErrorNotFound = "An item in your saved gear set could not be equipped."
+L.GearEquipErrorNotFound2 = "Try opening your bank and running this command again, or check your void storage."
+L.GearEquipErrorBagFull = "There is not enough room in your bags to equip your saved gear set."
+L.GearEquipErrorSoulbound = function(itemLink)
+ return itemLink .. " could not be equipped because it is not soulbound."
+end
+
+L.GearButtonImportText = "Import"
+L.GearButtonCleanText = "Clean Bags"
+
+L.GearTipTitle = "TIP!"
+L.GearTipText =
+[[In Options, you can enable automatic equipping of your gear sets whenever you change spec.
+
+Or, you can right click the minimap icon to switch spec and equip gear.
+
+OR! You can use slash commands:]]
+
+L.GearTipCommands =
+[[/amr equip [1 or 2]
+1 = primary
+2 = secondary
+no arg = toggle]]
+-- note to translators: the slash commands are literal and should stay as english
+
+
+--[[----------------------------------------------------------------------
+Import Dialog on Gear Tab
+------------------------------------------------------------------------]]
+L.ImportHeader = "Press Ctrl+V to paste data from the website into the box below."
+L.ImportButtonOk = "Import"
+L.ImportButtonCancel = "Cancel"
+
+L.ImportErrorEmpty = "The data string is empty."
+L.ImportErrorFormat = "The data string is not in the correct format."
+L.ImportErrorVersion = "The data string is from an old version of the addon. Please go to the website and generate a new one."
+L.ImportErrorChar = function(importChar, yourChar)
+ return "The data string is for " .. importChar .. ", but you are " .. yourChar .. "!"
+end
+L.ImportErrorRace = "It looks your race may have changed. Please go the website and re-optimize."
+L.ImportErrorFaction = "It looks your faction may have changed. Please go the website and re-optimize."
+L.ImportErrorLevel = "It looks your level may have changed. Please go the website and re-optimize."
+
+
+--[[----------------------------------------------------------------------
+Shopping List
+------------------------------------------------------------------------]]
+L.ShopTitle = "Shopping List"
+L.ShopEmpty = "There is no shopping list data for this player."
+L.ShopSpecLabel = "Spec"
+L.ShopHeaderGems = "Gems"
+L.ShopHeaderEnchants = "Enchants"
+L.ShopHeaderMaterials = "Enchanting Materials"
+
+
+--[[----------------------------------------------------------------------
+Combat Log Tab
+------------------------------------------------------------------------]]
+L.LogChatStart = "You are now logging combat, and Mr. Robot is logging character data for your raid."
+L.LogChatStop = "Combat logging has been stopped."
+
+L.LogChatWipe = function(wipeTime)
+ return "Manual wipe called at " .. wipeTime .. "."
+end
+L.LogChatUndoWipe = function(wipeTime)
+ return "Manual wipe at " .. wipeTime .. " was removed."
+end
+L.LogChatNoWipes = "There is no recent manual wipe to remove."
+
+L.LogButtonStartText = "Start Logging"
+L.LogButtonStopText = "Stop Logging"
+L.LogButtonReloadText = "Reload UI"
+L.LogButtonWipeText = "Wipe!"
+L.LogButtonUndoWipeText = "Undo Wipe"
+
+L.LogNote = "You are currently logging combat and gear data."
+L.LogReloadNote = "Either exit WoW entirely, or reload your UI just before uploading a log file."
+L.LogWipeNote = "The person uploading the log must be the one to use this wipe command."
+L.LogWipeNote2 = function(cmd)
+ return "'" .. cmd .. "' will also do this."
+end
+L.LogUndoWipeNote = "Last wipe called:"
+L.LogUndoWipeDate = function(day, timeOfDay)
+ return day .. " at " .. timeOfDay
+end
+
+L.LogAutoTitle = "Auto-Logging"
+L.LogAutoAllText = "Toggle All"
+
+L.LogInstructionsTitle = "Instructions!"
+L.LogInstructions =
+[[1.) Click Start Logging or enable Auto-Logging for your desired raids.
+
+2.) When you are ready to upload, exit World of Warcraft* or reload your UI.**
+
+3.) Launch the AMR Client to upload your log.
+
+
+*It is not necessary to exit WoW, but it is highly recommended. This will allow the AMR client to prevent your log file from getting very large.
+
+**The AMR addon collects extra data at the start of each encounter for all players in your raid with the AMR addon. Other players do not need to enable logging! They just need to have the addon installed and enabled. This data is only saved to disk if you exit WoW or reload your UI before uploading.
+]]
+
+
+--[[----------------------------------------------------------------------
+Team Optimizer Tab
+------------------------------------------------------------------------]]
+L.TeamTabLeaderText = "Loot Ranker"
+L.TeamTabMemberText = "Team Member"
+
+L.TeamSplashHeader = "How do you usually use the Team Optimizer?"
+L.TeamSplashLeaderLabel = "I am the person who uses askmrrobot.com to rank the loot for our group"
+L.TeamSplashMemberLabel = "Someone else handles using the Team Optimizer for me"
+
+L.TeamMemberText = "Just sit back and relax, everything is being handled by your team leaders."
+L.TeamMemberShowLootLabel = "Looting is in progress!"
+L.TeamMemberShowLoot = "Show Loot Window"
+
+L.TeamButtonVersionText = "Check for Addon"
+L.TeamButtonExportRosterText = "Export Roster"
+L.TeamButtonExportLootText = "Export Loot"
+L.TeamButtonExportClose = "Close"
+L.TeamButtonImportRankingsText = "Import Rankings"
+L.TeamButtonStartLootText = "Start Looting"
+L.TeamButtonResumeLootText = "Resume Looting"
+
+L.TeamExportVersionLabel = "The Team Optimizer is more accurate and easier to use if everyone in your group has the AskMrRobot addon."
+L.TeamExportRosterLabel = "Do this once at the beginning of your raid to initialize the Team Optimizer on the web. If someone joins or leaves your group during a raid, do it again and press 'Import without Reload' on the web to do a quick update of just the players that changed."
+L.TeamExportLootLabel = "Export all drops from the last looted boss and rank them all at once on the web. Rankings automatically account for loot won on previous bosses or bonus rolls!"
+L.TeamExportLootLabel2 = "Requires Master Loot"
+L.TeamImportRankingsLabel = "Import ranking data from askmrrobot.com so that you can easily view and distribute loot in-game."
+L.TeamStartLootLabel = function(numItems)
+ return numItems .. " items were imported."
+end
+
+L.TeamHistoryTitle = "Loot History"
+L.TeamHistoryNoGroup = "You are not in a group or raid."
+L.TeamHistoryEmpty = "No loot has been handed out yet."
+
+L.TeamVersionTitle = "Version Check"
+L.TeamVersionNoGroup = "You are not in a group or raid."
+L.TeamVersionGood = "Everyone in your group has the addon!"
+L.TeamVersionMissing = "NOT INSTALLED"
+L.TeamVersionOld = "OUT OF DATE"
+
+L.TeamExportRosterLoading = "Gathering player data, please wait..."
+
+L.TeamAlertNoGroup = "You are not in a group!"
+L.TeamAlertNoLoot = "Nothing has been master-looted recently in your party or raid!"
+
+L.TeamExportHelp = "Press Ctrl+C (or Apple+C on a Mac) to copy the text below."
+L.TeamExportRosterText = "Then go to the Team Optimizer on the website and paste into the roster import box."
+L.TeamExportLootText = "Then go to the Team Optimizer on the website and paste into the loot import box."
+-- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet
+
+L.TeamImportRankingsHeader = "Press Ctrl+V to paste data from the website into the box below."
+
+L.TeamLootOptionNeed = "Need"
+L.TeamLootOptionOff = "Off Spec"
+L.TeamLootOptionGreed = "Greed"
+L.TeamLootOptionPass = "Pass"
+L.TeamLootOptionDisenchant = "Disenchant"
+
+
+--[[----------------------------------------------------------------------
+Loot Window
+------------------------------------------------------------------------]]
+L.LootTitle = "Loot!"
+L.LootEmpty = "There is no loot to hand out right now!"
+L.LootHelpItems = "For each drop..."
+L.LootHelpRanks = "Choose an option:"
+L.LootHelpMaster = "Then master loot:"
+
+L.LootIneligible = "You are not eligible for this drop."
+
+L.LootRankHeaderRank = "Rank"
+L.LootRankHeaderScore = "Percent Upgrade"
+L.LootRankHeaderScoreDisenchant = "Player"
+
+-- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max
+L.LootRankHeaderNeed = "Need"
+L.LootRankHeaderOff = "Off"
+L.LootRankHeaderGreed = "Greed"
+L.LootRankHeaderPass = "Pass"
+L.LootRankHeaderRoll = "Roll"
+
+L.LootRankLabelOff = "off spec"
+L.LootRankLabelDisenchant = "disenchanter"
+L.LootRankLabelMasterLooter = "master looter"
+L.LootRankLabelNoAddon = "addon not running"
+
+L.LootMasterRollText = "Roll"
+L.LootMasterDisenchantText = "Disenchant/Sell"
+
+L.LootMasterRollLabel = "automatic /roll for everyone"
+L.LootMasterDisenchantLabel = "check this if no one wants this item"
+L.LootMasterGiveLoot = "Give Loot"
+L.LootMasterGiveDisenchant = "Give for Disenchant/Sell"
+
+L.LootMasterGiveFail = "Either this item or this master loot candidate could not be found :("
+
+
+--[[----------------------------------------------------------------------
+Options Tab
+------------------------------------------------------------------------]]
+L.OptionsHeaderGeneral = "General Options"
+
+L.OptionsHideMinimapName = "Hide minimap icon"
+L.OptionsHideMinimapDesc = "The minimap icon is for convenience, all actions can also be performed via slash commands or the UI."
+
+L.OptionsAutoGearName = "Automatically equip gear on spec change"
+L.OptionsAutoGearDesc = "Whenever you change spec (via the in-game UI, another addon, etc.), your imported AMR gear sets (on the Gear tab) will be automatically equipped."
+
+L.OptionsShopAhName = "Automatically show shopping list at auction house"
+L.OptionsShopAhDesc = "Whenever you open the auction house, automatically show the shopping list window. You can click on items in the shopping list to quickly search for them in the auction house."
+
+end
diff -r 75431c084aa0 -r 01b63b8ed811 localization/localization.de.lua
--- a/localization/localization.de.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +0,0 @@
---check locale
-if GetLocale() ~= "deDE" then return end
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- stat strings for e.g. displaying gem/enchant abbreviations
-L.AMR_STAT_SHORT_STRINGS = {
- ["Strength"] = "Stä",
- ["Agility"] = "Bew",
- ["Intellect"] = "Int",
- ["CriticalStrike"] = "Krit",
- ["Haste"] = "Tempo",
- ["Mastery"] = "Meisters",
- ["Multistrike"] = "Mehrfach",
- ["Versatility"] = "Viels",
- ["BonusArmor"] = "B-Rüstung",
- ["Spirit"] = "Wille",
- ["Dodge"] = "Ausw",
- ["Parry"] = "Parieren",
- ["MovementSpeed"] = "Geschw",
- ["Avoidance"] = "Verm",
- ["Stamina"] = "Ausd",
- ["Armor"] = "Rüstung",
- ["AttackPower"] = "AK",
- ["SpellPower"] = "ZM",
- ["PvpResilience"] = "PvP Abh",
- ["PvpPower"] = "PvP Macht",
-}
-
--- AskMrRobot.lua
-L.AMR_IMPORT_ERROR_EMPTY = "Der Importtext ist leer."
-L.AMR_IMPORT_ERROR_FORMAT = "Der Importtext hat ein ungültiges Format."
-L.AMR_IMPORT_ERROR_VERSION = "Der Importtext ist für eine alte Version des Addons. Bitte gehe auf die Webseite und generiere einen neuen."
-L.AMR_IMPORT_ERROR_CHAR = "Der Importtext ist für %s, aber du bist %s!"
-L.AMR_IMPORT_ERROR_RACE = "Es sieht so aus, als hätte sich deine Rasse geändert. Bitte gehe auf die Webseite und optimiere erneut."
-L.AMR_IMPORT_ERROR_FACTION = "Es sieht so aus, als hätte sich deine Fraktion geändert. Bitte gehe auf die Webseite und optimiere erneut."
-L.AMR_IMPORT_ERROR_LEVEL = "Es sieht so aus, als hätte sich dein Level geändert. Bitte gehe auf die Webseite und optimiere erneut."
-L.AMR_IMPORT_ERROR_SPEC = "Bitte ändere deine Spezialisierung auf %s um diese Optimierung zu sehen."
-L.AMR_IMPORT_ERROR_TALENT = "Es sieht so aus, als hätten sich deine Talente geändert. Bitte gehe auf die Webseite und optimiere erneut."
-L.AMR_IMPORT_ERROR_GLYPH = "Es sieht so aus, als hätten sich deine Glyphen geändert. Bitte gehe auf die Webseite und optimiere erneut."
---SlashCmdList.AMR
-L.AMR_SLASH_COMMAND_TEXT_1 = 'Verfügbare AskMrRobot Befehle:\n'
-L.AMR_SLASH_COMMAND_TEXT_2 = ' /amr show -- zeigt das Hauptfenster\n'
-L.AMR_SLASH_COMMAND_TEXT_3 = ' /amr hide -- versteckt das Hauptfenster\n'
-L.AMR_SLASH_COMMAND_TEXT_4 = ' /amr toggle -- schaltet das Hauptfenster um\n'
-L.AMR_SLASH_COMMAND_TEXT_5 = ' /amr wipe -- markiert einen Kampf als Wipe. Wird benutzt, um Ereignisse danach zu ignorieren\n'
-L.AMR_SLASH_COMMAND_TEXT_6 = ' /amr unwipe -- macht den letzten Wipe-Befehl rückgängig\n'
-L.AMR_SLASH_COMMAND_TEXT_7 = ' /amr export -- exportiert Taschen- und Bankinhalt (öffnet das Exportieren-Fenster)'
-
---AskMrRobotUi.lua
-
---createMainMenu
-L.AMR_UI_MENU_EXPORT = "Exportieren"
-L.AMR_UI_MENU_GEAR = "Ausrüstung laden"
-L.AMR_UI_MENU_COMBAT_LOG = "Kampflog"
-L.AMR_UI_MENU_HELP = "Hilfe"
-
-L.AMR_UI_BUTTON_IMPORT = "Import"
-L.AMR_UI_BUTTON_SUMMARY = "Übersicht"
-L.AMR_UI_BUTTON_GEMS = "Edelsteine"
-L.AMR_UI_BUTTON_ENCHANTS = "Verzauberungen"
-L.AMR_UI_BUTTON_SHOPPING_LIST = "Einkaufsliste"
-
---AskMrRobot.lua
-L.AMR_ON_EVENT_TOOLTIP = "Linksklick öffnet das Ask Mr. Robot Fenster.\n\nUmschalt + Linksklick um deinen Taschen- und Bankinhalt zu exportieren.\n\nStrg + Linksklick um einen Kampf als Wipe zu markieren."
-
---config.lua
---frame:SetScript
-L.AMR_CONFIG_EXIMPORT = "Mr. Robots Addon kann deine Gegenstandsinformationen auf seine Webseite exportieren, sowie die Optimierungen ins Spiel."
-L.AMR_CONFIG_CHECKBOX_MINIMAP_LABEL = "Zeige Minimap-Icon"
-L.AMR_CONFIG_CHECKBOX_MINIMAP_TOOLTIP_TITLE = "Minimap-Icon"
-L.AMR_CONFIG_CHECKBOX_MINIMAP_DESCRIPTION = "Zeige das Ask Mr. Robot Minimap-Icon."
-L.AMR_CONFIG_CHECKBOX_AUTOAH_LABEL = "Öffne Mr. Robots Einkaufsliste automatisch beim Auktionshaus"
-L.AMR_CONFIG_CHECKBOX_AUTOAH_TOOLTIP_TITLE = "Automatisches Öffnen Einkaufsliste"
-L.AMR_CONFIG_CHECKBOX_AUTOAH_DESCRIPTION = "Wenn auf deiner Einkaufsliste noch Posten offen sind, öffne Mr. Robots Einkaufsliste automatisch, wenn du das Auktionshaus besuchst."
-
---wait.lua
---wait
-L.AMR_WAIT_BAD_ARGUMENTS = "Bad Arguments to amr__wait"
---dir ui
---ui/CombatLogTab.lua
-L.AMR_COMBATLOGTAB_COMBAT_LOGGING = "Kampflog"
-L.AMR_COMBATLOGTAB_START_LOGGING = "Starte Kampflog"
-L.AMR_COMBATLOGTAB_CURRENTLY_LOGGING = "|c0000ff00Loggt derzeit|r"
-L.AMR_COMBATLOGTAB_STOP_LOGGING = "Stoppe Kampflog"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_SOO_LABEL = "Schlacht um Orgrimmar automatisch protokollieren"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_SOO_TOOLTIP_TITLE = "Automatisch Schlacht um Orgrimmar protokollieren"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_SOO_DESCRIPTION = "Das Kampflog wird automatisch gestartet, sobald du die Schlacht um Orgrimmar betrittst, und gestoppt, wenn du sie verlässt.\n\nBeachte, dass du andere Addons mit ähnlichen Funktionen deaktivieren solltest, um Konflikte zu vermeiden."
-L.AMR_COMBATLOGTAB_HEADLINE_OVER_BUTTON = "Charakterdaten"
-L.AMR_COMBATLOGTAB_SAVE_CHARACTER = "Daten speichern"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS = "ANWEISUNGEN"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_1 = "1. Benutze den Start/Stop-Button oder aktiviere 'Schlacht um Orgrimmar automatisch protokollieren'."
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_2 = "2. Wenn du bereit bist, deine Daten hochzuladen, drücke 'Daten speichern'. *"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_3 = "3. Beende World of Warcraft. **"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_4 = "4. Starte den Ask Mr. Robot Client und folge den Anweisungen. ***"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_5 = "|c00999999* Dies wird dein UI neu laden um sicher zu stellen, dass alle gesammelten Daten auf deine Festplatte gespeichert wurden. Dieser Schritt ist nicht notwendig, wenn du dich vor dem Hochladen ausloggst.|r"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_6 = "|c00999999** WoW zu beenden bevor du hochlädst ist optional, aber sehr zu empfehlen. Dies verhindert, dass das Kampflog übertrieben groß wird und damit den Vorgang verlangsamt.|r"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_7 = "|c00999999*** Du kannst das Programm hier herunterladen: |r |c003333ffhttp://www.askmrrobot.com/wow/combatlog/upload|r|c00999999.|r"
-L.AMR_COMBATLOGTAB_IS_LOGGING = "Du schreibst nun ein Kampflog und Mr. Robot speichert die Charakterdaten für den Raid."
-L.AMR_COMBATLOGTAB_STOPPED_LOGGING = "Kampflog wurde gestoppt."
-L.AMR_COMBATLOGTAB_INFIGHT = "Optionen im Kampf"
-L.AMR_COMBATLOGTAB_WIPE_1 = "|c00aaaaaaDeklariere einen Wipe |r|c00ffffffbevor|r|c00aaaaaa du absichtlich stirbst.|r"
-L.AMR_COMBATLOGTAB_WIPE_2 = "|c00aaaaaaDies wird absichtliche Tode und Schaden ignorieren.|r"
-L.AMR_COMBATLOGTAB_WIPE_3 = "|c00999999Diese Funktion muss von der Person benutzt werden, die das Kampflog aufzeichnet.|r"
-L.AMR_COMBATLOGTAB_SAVE_CHARACTER_INFO = "Während das Kampflog aufgezeichnet wird, werden Charakter- und Ausrüstungsdaten für alle Mitglieder des Raids, die dieses Addon installiert haben, aufgezeichnet. Nach dem Raid muss die Person, die das Kampflog aufgezeichnet hat, den Button drücken oder ausloggen, um die Daten zu speichern, damit sie hochgeladen werden können."
-L.AMR_COMBATLOGTAB_LASTWIPE = '|c00ff0000Letzter Wipe am %s um %s.|r'
-L.AMR_COMBATLOGTAB_WIPE_CHAT = "Es ist ein Wipe, sterbt!"
-L.AMR_COMBATLOGTAB_WIPE_MSG = "[AskMrRobot] Manueller Wipe ausgerufen um %s"
-L.AMR_COMBATLOGTAB_NOWIPES = '[AskMrRobot] Es gibt keinen manuellen Wipe zu entfernen'
-L.AMR_COMBATLOGTAB_UNWIPE_MSG = "[AskMrRobot] Manueller Wipe um %s wurde entfernt"
---ui/EnchantTab.lua
-L.AMR_ENCHANTTAB_ENCHANTS = "Verzauberungen"
-L.AMR_ENCHANTTAB_100_OPTIMAL = "Deine Verzauberungen sind 100% optimal!"
-L.AMR_ENCHANTTAB_SLOT = "Platz"
-L.AMR_ENCHANTTAB_CURRENT = "Aktuell"
-L.AMR_ENCHANTTAB_OPTIMIZED = "Optimiert"
-L.AMR_ENCHANTTAB_TESTSLOT = "TestSlot"
---ui/ExportTab.lua
-L.AMR_EXPORTTAB_EXPORT_TITLE = "Exportiere deinen Character für AskMrRobot.com"
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_1 = "1. Öffne deine Bank"
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_2 = "2. Kopiere den Text unterhalb mit Strg + C (oder Cmd + C auf einem Mac)"
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_3 = "3. Gehe auf |c00ffd100AskMrRobot.com|r und klicke auf den grünen '|c0000ff00Import from Addon|r' Button über deinem Charakternamen. Füge den Text in das Fenster ein, das sich öffnet."
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_NOTE = "Hinweis: Falls du etwas änderst, während dieses Fenster offen ist, dann klicke auf den Aktualisieren-Button unten, um einen neuen Export-Text zu erzeugen."
-L.AMR_EXPORTTAB_UPDATE = "Aktualisieren"
-
-
---ui/GemTab.lua
---popup autogem finished
-L.AMR_GEMTAB_FINISHED = "Mr. Robot ist fertig mit dem automatischen Sockeln. \rFalls Gegenstände nicht gesockelt wurden, hattest du vielleicht nicht genug Edelsteine. \rSollte dein Gürtel nicht gesockelt sein, fehlt eventuell die Gürtelschnalle."
-L.AMR_GEMTAB_BUTTON_OK = "Ok"
---popup autogem once
-L.AMR_GEMTAB_AUTOGEMMING_IN_PROGRESS = "Automatisches Sockeln läuft bereits."
---constructor
-L.AMR_GEMTAB_GEMS = "Edelsteine"
-L.AMR_GEMTAB_OPTIMAL = "Deine Edelsteine sind 100% optimal! Du bist nun wirklich, wirklich herausragend."
-L.AMR_GEMTAB_X_OPTIMIZE = "Du hast X Edelsteine zu optimieren"
-L.AMR_GEMTAB_AUTOGEM_BUTTON = "Auto Sockeln! (BETA)"
-L.AMR_GEMTAB_PREFER_PERFECT = "Bevorzuge Perfekte"
-L.AMR_GEMTAB_SLOT = "Platz"
-L.AMR_GEMTAB_CURRENT = "Aktuell"
-L.AMR_GEMTAB_OPTIMIZED = "Optimiert"
---Update
-L.AMR_GEMTAB_TO_OPTIMIZE = "Du hast %d \1244Edelstein:Edelsteine; zu optimieren"
---ui/HelpTab.lua
-L.AMR_HELPTAB_TITLE = "Hilfe"
-L.AMR_HELPTAB_LINK = "Besuche |c003333ffhttp://blog.askmrrobot.com/addon/|r für eine komplette Anleitung und um Fragen zu stellen.\r"
-L.AMR_HELPTAB_Q1 = "|c00999999Q:|r Das Arsenal aktualisiert meinen Charakter auf deiner Webseite nicht. Gibt es einen Workaround?"
-L.AMR_HELPTAB_A1 = "|c0066dd66A:|r Ja. Gehe in die |c00ffd100Exportieren|r Sektion dieses Addons. Kopiere den Text in der Box. Dann gehe auf unsere |c00ffd100Website|r, lade deinen Charakter und klicke auf den grünen '|c0000ff00Import (from addon)|r' Button über deinem Charakternamen. Füge den Text dort ein. Dieser Prozess bringt deinen derzeitigen ingame Charakter auf die Webseite!"
-L.AMR_HELPTAB_Q2 = "|c00999999Q:|r Muss ich zum Optimieren jedesmal einen neuen Text generieren?"
-L.AMR_HELPTAB_A2 = "|c0066dd66A:|r Ja. Gehe auf die |c00ffd100Website|r und klicke auf den grünen '|c0000ff00Update from Armory|r' Button über deinem Charakternamen, damit immer die aktuellsten Daten vorliegen. Optimiere deine Ausrüstung und klicke dann auf den blauen '|c0018C0F7Export to Addon|r' Button in der '|c00BF28D6Now What?|r' Sektion rechts, um deinen neuen Text zu bekommen. Kehre zu diesem |c00ffd100Addon|r zurück, gehe in den '|c00ffd100Ausrüstung laden|r' Tab und füge den Text in die Box ein."
-L.AMR_HELPTAB_Q3 = "|c00999999Q:|r Kann ich meine Einkaufsliste an einen anderen meiner Charaktere schicken?"
-L.AMR_HELPTAB_A3 = '|c0066dd66A:|r Ja, gehe in den Reiter Einkaufsliste und wähle "Post" als Option im Menü. Du kannst die Liste dann per Post verschicken.'
-L.AMR_HELPTAB_Q4 = "|c00999999Q:|r Ich bin gerade im Raid und habe einen Gegenstand bekommen. Kann ich diesen schnell optimieren?"
-L.AMR_HELPTAB_A4 = "|c0066dd66A:|r Ja! Schaue in diese Anleitung hier: |c003333ffhttp://blog.askmrrobot.com/addon#raid|r"
-L.AMR_HELPTAB_Q5 = "|c00999999Q:|r Wo ist das automatische Sockeln?"
-L.AMR_HELPTAB_A5 = "|c0066dd66A:|r Wir haben es vorrübergehend entfernt. Wir planen, es in WoD zurück zu bringen."
-L.AMR_HELPTAB_Q6 = "|c00999999Q:|r Ist Mr. Robot am aktuellen Stand?"
-L.AMR_HELPTAB_A6 = "|c0066dd66A:|r Ja! Für weitere Informationen, gehe auf \r|c003333ffhttp://blog.askmrrobot.com/2014/10/what-to-do-for-6-0-2/|r"
---ui/ImportTab.lua
---new
-L.AMR_IMPORTTAB_BUTTON = "Importieren"
-L.AMR_IMPORTTAB_TITLE = "Ein Ausrüstungsset von der Webseite laden"
-L.AMR_IMPORTTAB_INSTRUCTIONS_1 = "1. Klicke auf den blauen '|c0018C0F7Export to Addon|r' Button auf unserer |c00BF28D6Website|r. Er befindet sich rechts in der '|c33ffffffNow What?|r' Sektion. Kopiere den Text im Textfeld, das sich öffnet.\n|c00999999Zum Kopieren, drücke Strg + C (oder Cmd + C auf einem Mac)|r"
-L.AMR_IMPORTTAB_INSTRUCTIONS_2 = "2. Kehre dann zu diesem Fenster im |c00ffd100Addon|r zurück. Füge den Text in die Box unten ein und klicke auf den 'Importieren' Button.\n|c00999999Zum Einfügen, drücke Strg + V (oder Cmd + V auf einem Mac)|r"
-
---ui/ShoppingListTab
---popup mail
-L.AMR_SHOPPINGLISTTAB_OPEN_MAIL = "Du muss das Postfenster öffnen, damit dies funktioniert."
-L.AMR_SHOPPINGLISTTAB_BUTTON_OK = "Ok"
---new
-L.AMR_SHOPPINGLISTTAB_TITLE = "Einkaufsliste"
-L.AMR_SHOPPINGLISTTAB_BUTTON_SEND = "Abschicken!"
-L.AMR_SHOPPINGLISTTAB_ENCHANT_MATERIALS = "Verzauberungsmaterialien"
-L.AMR_SHOPPINGLISTTAB_ENCHANTS = "Verzauberungen"
-L.AMR_SHOPPINGLISTTAB_GEMS = "Edelsteine"
-L.AMR_SHOPPINGLISTTAB_INCLUDE = "Einfügen:"
-L.AMR_SHOPPINGLISTTAB_SEND_LIST_TO = "Sende Liste an"
-L.AMR_SHOPPINGLISTTAB_WHISPER_CHANNEL = "Flüstern oder an einen Kanal wie /raid oder /gilde senden."
-L.AMR_SHOPPINGLISTTAB_SEND_JEWELCRAFT_ENCHANTER = "An einen Juwelier oder Verzauberer senden"
-L.AMR_SHOPPINGLISTTAB_TOTAL = "Total"
-L.AMR_SHOPPINGLISTTAB_DONE = "EINKAUF ERLEDIGT!"
-L.AMR_SHOPPINGLISTTAB_A_ROBOTS_WISHLIST = "Es sei denn, du willst mir ein Geburtstagsgeschenk kaufen! Ich mag Titan Bolzen und Roboter Hunde... Oder war es Titan Hunde und Roboter Bolzen..."
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_FRIEND = "Flüstern"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_PARTY = "Gruppe"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_RAID = "Raid"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_GUILD = "Gilde"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_CHANNEL = "Kanal"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_MAIL = "Post"
-L.AMR_SHOPPINGLISTTAB_MAIL_ROBOT_MESSAGE = "Mr. Robot sagt, ich brauche folgendes, um meine Ausrüstung zu optimieren:\n"
-L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_GE = 'Anfrage für Edelsteine und Verzauberungen'
-L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_G = 'Anfrage für Edelsteine'
-L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_E = 'Anfrage für Verzauberungen'
-L.AMR_SHOPPINGLISTTAB_CHAT_ROBOT_MESSAGE = "Mr. Robot sagt ich brauche"
---ui/SummaryTab.lua
-L.AMR_SUMMARYTAB_TITLE = "Übersicht"
-L.AMR_SUMMARYTAB_NO_IMPORT = "Du hast keine Optimierungen importiert."
-L.AMR_SUMMARYTAB_GET_STARTED = 'Wechsel in den "Import"-Reiter um zu beginnen.'
-L.AMR_SUMMARYTAB_GO_UPGRADE = "Bitte werte die folgenden Gegenstände auf:"
-L.AMR_SUMMARYTAB_SLOT = "Platz"
-L.AMR_SUMMARYTAB_ITEM_NAME = "Gegenstandsname"
-L.AMR_SUMMARYTAB_OPTIMAL = "Glückwunsch! Du bist 100% optimal"
-L.AMR_SUMMARYTAB_LAST_IMPORT = "Letzter Import: ?\rDiese Optimierungen sind für ?"
-L.AMR_SUMMARYTAB_OPTIMIZATIONS_TO_GO = "Du hast ? Optimierungen durchzuführen:"
-L.AMR_SUMMARYTAB_GEMS_TO_GO = "? Edelsteine"
-L.AMR_SUMMARYTAB_ENCHANTS_TO_GO = "? Verzauberungen"
-L.AMR_SUMMARYTAB_VIEW_TABS = "Schaue in die Edelsteine und Verzauberungen Reiter, um die vorgeschlagenen Optimierungen zu sehen."
-L.AMR_SUMMARYTAB_GEMCOUNT = "%d \1244Edelstein:Edelsteine;"
-L.AMR_SUMMARYTAB_ENCHANTCOUNT = "%d \1244Verzauberung:Verzauberungen;"
-L.AMR_SUMMARYTAB_OPTIMIZATIONCOUNT = "Du hast %d \1244Optimierung:Optimierungen; zu machen:"
-L.AMR_SUMMARYTAB_LAST_IMPORT_1 = "Letzer Import: %s\rDiese Optimierungen sind für %s"
-L.AMR_SUMMARYTAB_LAST_IMPORT_2 = "Letzter Import: %s\rDiese Optimierungen sind für %s's..."
-L.AMR_SUMMARYTAB_LAST_IMPORT_PSPEC = "Primäre Spez.: %s"
-L.AMR_SUMMARYTAB_LAST_IMPORT_SSPEC = "Sekundäre Spez.: %s"
-L.AMR_SUMMARYTAB_DIFF_REALM = "einen anderen Server: %s"
-L.AMR_SUMMARYTAB_DIFF_TALENT = "andere Talente"
-L.AMR_SUMMARYTAB_DIFF_GLYPHS = "andere Glyphen"
-L.AMR_SUMMARYTAB_DIFF_GEAR = "Mr. Robot hat eine andere Ausrüstungszusammenstellung optimiert"
-L.AMR_SUMMARYTAB_DIFF_AND = "und"
-L.AMR_SUMMARYTAB_DIFF_PLEASE_EQ = ". Bitte rüste die folgenden Gegenstände aus, bevor du fortfährst."
-L.AMR_SUMMARYTAB_DIFF_CHECK_CHAR = "WARNUNG: Bitte überprüfe deinen Charackter, bevor du fortfährst:"
-L.AMR_SUMMARYTAB_DIFF_OPTIMIZED_FOR = "Mr. Robot optimierte für "
---showImportError
-L.AMR_SUMMARYTAB_IMPORT_NOT_WORK = 'Fehler! Dein Import ist fehlgeschlagen:\n\n%s'
---ui/RobotStamp.lua
-L.AMR_ROBOTSTAMP_TEXT = "MR. ROBOT PRÜFSIEGEL"
-L.AMR_ROBOTSTAMP_GEMS = "Deine Edelsteine sind 100% optimal! Du bist nun wirklich, wirklich herausragend."
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 localization/localization.en.lua
--- a/localization/localization.en.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-local _, AskMrRobot = ...
-AskMrRobot.L = {};
-local L = AskMrRobot.L;
-
-function AskMrRobot.format(fmt, ...)
- local args, order = {...}, {}
-
- local f = fmt:gsub('%%(%d+)%$', function(i)
- tinsert(order, args[tonumber(i)])
- return '%'
- end)
-
- return f:format(unpack(order))
-end
-
--- stat strings for e.g. displaying gem/enchant abbreviations
-L.AMR_STAT_SHORT_STRINGS = {
- ["Strength"] = "Str",
- ["Agility"] = "Agi",
- ["Intellect"] = "Int",
- ["CriticalStrike"] = "Crit",
- ["Haste"] = "Haste",
- ["Mastery"] = "Mastery",
- ["Multistrike"] = "Multi",
- ["Versatility"] = "Vers",
- ["BonusArmor"] = "Armor",
- ["Spirit"] = "Spirit",
- ["Dodge"] = "Dodge",
- ["Parry"] = "Parry",
- ["MovementSpeed"] = "Speed",
- ["Avoidance"] = "Avoid",
- ["Stamina"] = "Stam",
- ["Armor"] = "Armor",
- ["AttackPower"] = "AP",
- ["SpellPower"] = "SP",
- ["PvpResilience"] = "PvP Res",
- ["PvpPower"] = "PvP Pow",
-}
-
--- AskMrRobot.lua
-L.AMR_IMPORT_ERROR_EMPTY = "The data string is empty."
-L.AMR_IMPORT_ERROR_FORMAT = "The data string is not in the correct format."
-L.AMR_IMPORT_ERROR_VERSION = "The data string is from an old version of the addon. Please go to the website and generate a new one."
-L.AMR_IMPORT_ERROR_CHAR = "The data string is for %s, but you are %s!"
-L.AMR_IMPORT_ERROR_RACE = "It looks your race may have changed. Please go the website and re-optimize."
-L.AMR_IMPORT_ERROR_FACTION = "It looks your faction may have changed. Please go the website and re-optimize."
-L.AMR_IMPORT_ERROR_LEVEL = "It looks your level may have changed. Please go the website and re-optimize."
-L.AMR_IMPORT_ERROR_SPEC = "Please change your spec to %s to view this optimization."
-L.AMR_IMPORT_ERROR_TALENT = "It looks like your talents may have changed. Please go the website and re-optimize."
-L.AMR_IMPORT_ERROR_GLYPH = "It looks like your glyphs may have changed. Please go the website and re-optimize."
---SlashCmdList.AMR
-L.AMR_SLASH_COMMAND_TEXT_1 = 'Available AskMrRobot slash commands:\n'
-L.AMR_SLASH_COMMAND_TEXT_2 = ' /amr show -- show the main window\n'
-L.AMR_SLASH_COMMAND_TEXT_3 = ' /amr hide -- hide the main window\n'
-L.AMR_SLASH_COMMAND_TEXT_4 = ' /amr toggle -- toggle the main window\n'
-L.AMR_SLASH_COMMAND_TEXT_5 = ' /amr wipe -- logs a raid wipe. Used to ignore events in the fight after this point\n'
-L.AMR_SLASH_COMMAND_TEXT_6 = ' /amr unwipe -- undo the last wipe command\n'
-L.AMR_SLASH_COMMAND_TEXT_7 = ' /amr export -- export character, bag, and bank data (opens the export copy/paste window)'
-
---AskMrRobotUi.lua
-
---createMainMenu
-L.AMR_UI_MENU_EXPORT = "Export"
-L.AMR_UI_MENU_GEAR = "Load a Gear Set"
-L.AMR_UI_MENU_SETTINGS = "Settings"
-L.AMR_UI_MENU_COMBAT_LOG = "Combat Log"
-L.AMR_UI_MENU_HELP = "Help"
-
-L.AMR_UI_BUTTON_IMPORT = "Load"
-L.AMR_UI_BUTTON_SUMMARY = "Summary"
-L.AMR_UI_BUTTON_GEMS = "Gems"
-L.AMR_UI_BUTTON_ENCHANTS = "Enchants"
-L.AMR_UI_BUTTON_SHOPPING_LIST = "Shopping List"
-
---AskMrRobot.lua
-L.AMR_ON_EVENT_TOOLTIP = "Left Click to open the Ask Mr. Robot window.\n\nShift + Left Click to export your bag and bank data.\n\nCtrl + Left Click to mark a fight as a wipe."
-
---config.lua
---frame:SetScript
-L.AMR_CONFIG_EXIMPORT = "Mr. Robot's addon can export your item information to his website, and import your optimizations into the game."
-L.AMR_CONFIG_CHECKBOX_MINIMAP_LABEL = "Show minimap icon"
-L.AMR_CONFIG_CHECKBOX_MINIMAP_TOOLTIP_TITLE = "Minimap Icon"
-L.AMR_CONFIG_CHECKBOX_MINIMAP_DESCRIPTION = "Show the Ask Mr. Robot minimap icon."
-L.AMR_CONFIG_CHECKBOX_AUTOAH_LABEL = "Automatically show Mr. Robot's shopping list at the auction house"
-L.AMR_CONFIG_CHECKBOX_AUTOAH_TOOLTIP_TITLE = "Auto-Show Shopping List"
-L.AMR_CONFIG_CHECKBOX_AUTOAH_DESCRIPTION = "When your shopping list still has things left to buy, automatically show Mr. Robot's shopping list when you visit the auction house."
-
---wait.lua
---wait
-L.AMR_WAIT_BAD_ARGUMENTS = "Bad Arguments to amr__wait"
-
---dir ui
---ui/CombatLogTab.lua
-L.AMR_SETTINGSTAB_SETTINGS = "Settings"
-L.AMR_COMBATLOGTAB_COMBAT_LOGGING = "Combat Logging"
-L.AMR_COMBATLOGTAB_START_LOGGING = "Start Logging"
-L.AMR_COMBATLOGTAB_CURRENTLY_LOGGING = "|c0000ff00Currently Logging|r"
-L.AMR_COMBATLOGTAB_STOP_LOGGING = "Stop Logging"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_HM_LABEL = "Always log Highmaul"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_HM_TOOLTIP_TITLE = "Auto-Log Highmaul"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_HM_DESCRIPTION = "Automatically start logging when you enter Highmaul and stop when you leave Highmaul.\n\nNote that you should disable similar features in other addons to avoid conflicts."
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_BRF_LABEL = "Always log Blackrock Foundry"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_BRF_TOOLTIP_TITLE = "Auto-Log Blackrock Foundry"
-L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_BRF_DESCRIPTION = "Automatically start logging when you enter Blackrock Foundry and stop when you leave Blackrock Foundry.\n\nNote that you should disable similar features in other addons to avoid conflicts."
-L.AMR_COMBATLOGTAB_HEADLINE_OVER_BUTTON = "Save Characters"
-L.AMR_COMBATLOGTAB_SAVE_CHARACTER = "Save Character Data"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS = "INSTRUCTIONS"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_1 = "1. Use the Start/Stop buttons or check 'Always log Siege of Orgrimmar'."
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_2 = "2. When you are ready to upload, press 'Save Character Data'. *"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_3 = "3. Exit World of Warcraft. **"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_4 = "4. Launch the Ask Mr. Robot client and follow the instructions. ***"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_5 = "|c00999999* This will reload your UI to ensure that all collected data is saved to disk. This step is not necessary if you log out of the game before uploading.|r"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_6 = "|c00999999** Exiting WoW before uploading your combat log is optional, but highly recommended. This prevents your log file from getting ridiculously large and slowing down your uploads.|r"
-L.AMR_COMBATLOGTAB_INSTRUCTIONS_7 = "|c00999999*** You can download the client program at|r |c003333ffhttp://www.askmrrobot.com/wow/combatlog/upload|r|c00999999.|r"
-L.AMR_COMBATLOGTAB_IS_LOGGING = "You are now logging combat, and Mr. Robot is logging character data for your raid."
-L.AMR_COMBATLOGTAB_STOPPED_LOGGING = "Combat logging has been stopped."
-L.AMR_COMBATLOGTAB_INFIGHT = "In-Fight Options"
-L.AMR_COMBATLOGTAB_WIPE_1 = "|c00aaaaaaDeclare a wipe |r|c00ffffffbefore|r|c00aaaaaa you die on purpose.|r"
-L.AMR_COMBATLOGTAB_WIPE_2 = "|c00aaaaaaThis will ignore intentional deaths & damage.|r"
-L.AMR_COMBATLOGTAB_WIPE_3 = "|c00999999This feature must be used by the person logging combat.|r"
-L.AMR_COMBATLOGTAB_SAVE_CHARACTER_INFO = "While you are logging combat, character & gear data is collected for everyone in the raid who has this mod installed. At the end of the raid, the person logging needs to click the button to save the data so it can be uploaded."
-L.AMR_COMBATLOGTAB_LASTWIPE = '|c00ff0000Last wipe called on %s at %s.|r'
-L.AMR_COMBATLOGTAB_WIPE_CHAT = "It's a wipe, everybody die!"
-L.AMR_COMBATLOGTAB_WIPE_MSG = "[AskMrRobot] Manual wipe called at %s"
-L.AMR_COMBATLOGTAB_NOWIPES = '[AskMrRobot] There is no recent manual wipe to remove'
-L.AMR_COMBATLOGTAB_UNWIPE_MSG = "[AskMrRobot] Manual wipe at %s was removed"
---ui/EnchantTab.lua
-L.AMR_ENCHANTTAB_ENCHANTS = "Enchants"
-L.AMR_ENCHANTTAB_100_OPTIMAL = "Your enchants are 100% optimal!"
-L.AMR_ENCHANTTAB_SLOT = "Slot"
-L.AMR_ENCHANTTAB_CURRENT = "Current"
-L.AMR_ENCHANTTAB_OPTIMIZED = "Optimized"
-L.AMR_ENCHANTTAB_TESTSLOT = "TestSlot"
---ui/ExportTab.lua
-L.AMR_EXPORTTAB_EXPORT_TITLE = "Export your character to AskMrRobot.com"
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_1 = "1. Open your bank"
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_2 = "2. Copy the text below by pressing Ctrl+C (or Cmd+C on a Mac)"
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_3 = "3. Go to |c00ffd100AskMrRobot.com|r and click the green '|c0000ff00Import from Addon|r' button found just above your character name. Paste the text into the window that pops up."
-L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_NOTE = "NOTE: If you change something while this window is open, press the Update button below to generate a new export string."
-L.AMR_EXPORTTAB_UPDATE = "Update"
-
---ui/GemTab.lua
---popup autogem finished
-L.AMR_GEMTAB_FINISHED = "Mr. Robot finished auto-gemming. \rIf some items aren't gemmed, you may need to acquire more gems. \rIf your belt isn't gemmed, you may still need to buy a belt buckle."
-L.AMR_GEMTAB_BUTTON_OK = "Ok"
---popup autogem once
-L.AMR_GEMTAB_AUTOGEMMING_IN_PROGRESS = "Autogemming already in progress."
---constructor
-L.AMR_GEMTAB_GEMS = "Gems"
-L.AMR_GEMTAB_OPTIMAL = "Your gems are 100% optimal! You are truly, truly outrageous."
-L.AMR_GEMTAB_X_OPTIMIZE = "You have X gems to optimize"
-L.AMR_GEMTAB_AUTOGEM_BUTTON = "Auto Gem! (BETA)"
-L.AMR_GEMTAB_PREFER_PERFECT = "Prefer Perfect"
-L.AMR_GEMTAB_SLOT = "Slot"
-L.AMR_GEMTAB_CURRENT = "Current"
-L.AMR_GEMTAB_OPTIMIZED = "Optimized"
---Update
-L.AMR_GEMTAB_TO_OPTIMIZE = "You have %d \1244gem:gems; to optimize"
---ui/HelpTab.lua
-L.AMR_HELPTAB_TITLE = "Help"
-L.AMR_HELPTAB_LINK = "Visit |c003333ffhttp://blog.askmrrobot.com/addon/|r for a full tutorial and to ask questions.\r"
-L.AMR_HELPTAB_Q1 = "|c00999999Q:|r The armory won’t update my character on your website. Is there a workaround?"
-L.AMR_HELPTAB_A1 = "|c0066dd66A:|r Yes. Go to the |c00ffd100Export|r section of this addon. Copy the text in the box. Then go to our |c00ffd100website|r, load your character, and click the green '|c0000ff00Import (from addon)|r' button, found just above your character name. Paste the text there. That process takes a snapshot of your current in-game character and imports it to the website!"
-L.AMR_HELPTAB_Q2 = "|c00999999Q:|r Do I have to get a new text-string every time I need to optimize?"
-L.AMR_HELPTAB_A2 = "|c0066dd66A:|r Yes. Go to the |c00ffd100website|r and click the green '|c0000ff00Update from Armory|r' button found just above your character name, to make sure you have updated gear. Optimize your gear and then click the blue '|c0018C0F7Export to Addon|r' button found to the right of your gear, in the purple '|c00BF28D6Now What?|r section. Return to this |c00ffd100addon|r, go to the '|c00ffd100Load a Gear Set|r' tab and paste the text in the box."
-L.AMR_HELPTAB_Q3 = "|c00999999Q:|r Can I send my shopping list to an alt?"
-L.AMR_HELPTAB_A3 = '|c0066dd66A:|r Yes, go to the shopping list tab and select the "mail" option in the drop down. You can mail the list to your alt.'
-L.AMR_HELPTAB_Q4 = "|c00999999Q:|r I am in the middle of a raid and just won a piece of loot. Can I optimize really quickly?"
-L.AMR_HELPTAB_A4 = "|c0066dd66A:|r Yes! You'll want to read the tutorial on that here: \r|c003333ffhttp://blog.askmrrobot.com/addon#raid"
-L.AMR_HELPTAB_Q5 = "|c00999999Q:|r Where is auto gemming?"
-L.AMR_HELPTAB_A5 = "|c0066dd66A:|r We have temporarily removed it. We plan to bring it back for WoD"
-L.AMR_HELPTAB_Q6 = "|c00999999Q:|r Is Mr. Robot updated?"
-L.AMR_HELPTAB_A6 = "|c0066dd66A:|r Yes! For more info, go to \r|c003333ffhttp://blog.askmrrobot.com/2014/10/what-to-do-for-6-0-2/"
---ui/ImportTab.lua
---new
-L.AMR_IMPORTTAB_BUTTON = "Load Gear"
-L.AMR_IMPORTTAB_TITLE = "Load a gear set from the website"
-L.AMR_IMPORTTAB_INSTRUCTIONS_1 = "1. Click the blue '|c0018C0F7Send to Addon|r' button on our |c00BF28D6website|r. It's found on the right side in the '|c33ffffffNow What?|r' section. Copy the text in the box that pops up.|n|c00999999To copy, press ctrl + c (or cmd + c on a mac)|r"
-L.AMR_IMPORTTAB_INSTRUCTIONS_2 = "2. Then return to this window in the |c00ffd100addon|r. Paste the text in the box below, then click the 'Load Gear' button.|n|c00999999To paste, press ctrl + v in the window (or cmd + v on a mac)|r"
-
---ui/ShoppingListTab
---popup mail
-L.AMR_SHOPPINGLISTTAB_OPEN_MAIL = "You need to open the mail window for this to work"
-L.AMR_SHOPPINGLISTTAB_BUTTON_OK = "Ok"
---new
-L.AMR_SHOPPINGLISTTAB_TITLE = "Shopping List"
-L.AMR_SHOPPINGLISTTAB_BUTTON_SEND = "send it!"
-L.AMR_SHOPPINGLISTTAB_ENCHANT_MATERIALS = "Enchant Materials"
-L.AMR_SHOPPINGLISTTAB_ENCHANTS = "Enchants"
-L.AMR_SHOPPINGLISTTAB_GEMS = "Gems"
-L.AMR_SHOPPINGLISTTAB_INCLUDE = "Include:"
-L.AMR_SHOPPINGLISTTAB_SEND_LIST_TO = "Send list to"
-L.AMR_SHOPPINGLISTTAB_WHISPER_CHANNEL = "Whisper to a friend or send to a channel, like /raid or /guild."
-L.AMR_SHOPPINGLISTTAB_SEND_JEWELCRAFT_ENCHANTER = "Send to a Jewelcraft or Enchanter friend :)"
-L.AMR_SHOPPINGLISTTAB_TOTAL = "Total"
-L.AMR_SHOPPINGLISTTAB_DONE = "YOUR SHOPPING IS ALL DONE!"
-L.AMR_SHOPPINGLISTTAB_A_ROBOTS_WISHLIST = "Unless you want to buy me a birthday present! I like titanium bolts and robot dogs... Or was it titanium dogs and robot bolts..."
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_FRIEND = "a friend"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_PARTY = "party"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_RAID = "raid"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_GUILD = "guild"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_CHANNEL = "channel"
-L.AMR_SHOPPINGLISTTAB_DROPDOWN_MAIL = "mail"
-L.AMR_SHOPPINGLISTTAB_MAIL_ROBOT_MESSAGE = "Mr. Robot says I need the following to optimize my gear:\n"
-L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_GE = 'Request for gems and enchants'
-L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_G = 'Request for gems'
-L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_E = 'Request for enchants'
-L.AMR_SHOPPINGLISTTAB_CHAT_ROBOT_MESSAGE = "Mr. Robot says I need"
---ui/SummaryTab.lua
-L.AMR_SUMMARYTAB_TITLE = "Summary"
-L.AMR_SUMMARYTAB_NO_IMPORT = "You have no optimizations imported."
-L.AMR_SUMMARYTAB_GET_STARTED = 'Click the |c00ffd100Load|r tab to get started.'
-L.AMR_SUMMARYTAB_GO_UPGRADE = "Please upgrade the following items:"
-L.AMR_SUMMARYTAB_SLOT = "Slot"
-L.AMR_SUMMARYTAB_ITEM_NAME = "Item Name"
-L.AMR_SUMMARYTAB_OPTIMAL = "Congratulations! You are 100% optimal"
-L.AMR_SUMMARYTAB_LAST_IMPORT = "Last import: ?\rThese optimizations are for ?"
-L.AMR_SUMMARYTAB_OPTIMIZATIONS_TO_GO = "You have ? optimizations to make:"
-L.AMR_SUMMARYTAB_GEMS_TO_GO = "? gems"
-L.AMR_SUMMARYTAB_ENCHANTS_TO_GO = "? enchants"
-L.AMR_SUMMARYTAB_VIEW_TABS = "View the Gem and Enchant tabs for suggested optimizations."
-L.AMR_SUMMARYTAB_GEMCOUNT = "%d \1244gem:gems;"
-L.AMR_SUMMARYTAB_ENCHANTCOUNT = "%d \1244enchant:enchants;"
-L.AMR_SUMMARYTAB_OPTIMIZATIONCOUNT = "You have %d \1244optimization:optimizations; to make:"
-L.AMR_SUMMARYTAB_LAST_IMPORT_1 = "Last import: %s\rThese optimizations are for %s"
-L.AMR_SUMMARYTAB_LAST_IMPORT_2 = "Last import: %s\rThese optimizations are for %s's..."
-L.AMR_SUMMARYTAB_LAST_IMPORT_PSPEC = "Primary Spec - %s"
-L.AMR_SUMMARYTAB_LAST_IMPORT_SSPEC = "Secondary Spec - %s"
-L.AMR_SUMMARYTAB_DIFF_REALM = "a different realm: %s"
-L.AMR_SUMMARYTAB_DIFF_TALENT = "different talents"
-L.AMR_SUMMARYTAB_DIFF_GLYPHS = "different glyphs"
-L.AMR_SUMMARYTAB_DIFF_GEAR = "Mr. Robot optimized a different set of gear"
-L.AMR_SUMMARYTAB_DIFF_AND = "and"
-L.AMR_SUMMARYTAB_DIFF_PLEASE_EQ = ". Please equip the following items before proceeding with the optimizations."
-L.AMR_SUMMARYTAB_DIFF_CHECK_CHAR = "WARNING: Please check your character before proceeding:"
-L.AMR_SUMMARYTAB_DIFF_OPTIMIZED_FOR = "Mr. Robot optimized for "
---showImportError
-L.AMR_SUMMARYTAB_IMPORT_NOT_WORK = 'Error! Your import did not work:|n|n%s'
---ui/RobotStamp.lua
-L.AMR_ROBOTSTAMP_TEXT = "ROBOT STAMP OF APPROVAL"
-L.AMR_ROBOTSTAMP_GEMS = "Your gems are 100% optimal! You are truly, truly outrageous."
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 sort.lua
--- a/sort.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-local _, AskMrRobot = ...
-
-function AskMrRobot.spairs(t, order)
- -- collect the keys
- local keys = {}
- for k in pairs(t) do keys[#keys+1] = k end
-
- -- if order function given, sort by it by passing the table and keys a, b,
- -- otherwise just sort the keys
- if order then
- table.sort(keys, function(a,b) return order(t, a, b) end)
- else
- table.sort(keys)
- end
-
- -- return the iterator function
- local i = 0
- return function()
- i = i + 1
- if keys[i] then
- return keys[i], t[keys[i]]
- end
- end
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiButton.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiButton.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,120 @@
+--[[-----------------------------------------------------------------------------
+Button Widget
+Based on the AceGUI button, but with a custom look for AMR.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiButton", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local _G = _G
+local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
+
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function buttonOnClick(frame, ...)
+ AceGUI:ClearFocus()
+ PlaySound("igMainMenuOption")
+ frame.obj:Fire("OnClick", ...)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- restore default values
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetDisabled(false)
+ self:SetText()
+ self:SetBackgroundColor({R = 0, G = 0, B = 0})
+ self:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.White))
+ self.frame:ClearAllPoints()
+ end,
+
+ ["SetText"] = function(self, text)
+ self.frame:SetText(text)
+ end,
+
+ ["SetFont"] = function(self, font)
+ self.frame:SetNormalFontObject(font)
+ end,
+
+ -- color is an object with R, G, B
+ ["SetBackgroundColor"] = function(self, color)
+ self.texNormal:SetTexture(color.R, color.G, color.B, 1)
+ self.texPush:SetTexture(color.R, color.G, color.B, 1)
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ self.frame:SetAlpha(0.2)
+ else
+ self.frame:Enable()
+ self.frame:SetAlpha(1)
+ end
+ end,
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local name = "AmrUiButton" .. AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Button", name, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnClick", buttonOnClick)
+
+ Amr.DropShadow(frame)
+
+ -- normal bg color, can be set with SetBackgroundColor
+ local texNormal = frame:CreateTexture(nil, "BORDER")
+ texNormal:SetAllPoints(true)
+ frame:SetNormalTexture(texNormal)
+
+ -- gives a 1px down+right animation on press
+ local texPush = frame:CreateTexture(nil, "BORDER")
+ texPush:SetPoint("TOPLEFT", frame, "TOPLEFT", 1, -1)
+ texPush:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 1, -1)
+ frame:SetPushedTexture(texPush)
+
+ -- not perfect, but more or less achieves the effect of lightening the bg color slightly on hover
+ local texHigh = frame:CreateTexture(nil, "BORDER")
+ texHigh:SetTexture(1, 1, 1, 0.1)
+ texHigh:SetAllPoints(true)
+ frame:SetHighlightTexture(texHigh)
+
+ local widget = {
+ texNormal = texNormal,
+ texPush = texPush,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiCheckBox.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiCheckBox.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,126 @@
+--[[-----------------------------------------------------------------------------
+CheckBox Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiCheckBox", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local _G = _G
+local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
+
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function buttonOnClick(frame, ...)
+ AceGUI:ClearFocus()
+ PlaySound("igMainMenuOption")
+ frame.obj:Fire("OnClick", ...)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- restore default values
+ self:SetDisabled(false)
+ self:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
+ self:SetText()
+ self:SetChecked(false)
+ self.frame:ClearAllPoints()
+ end,
+
+ ["SetText"] = function(self, text)
+ self.label:SetText(text)
+ self.frame:SetWidth(16 + 6 + self.label:GetStringWidth())
+ end,
+
+ ["SetFont"] = function(self, font)
+ self.label:SetFontObject(font)
+ end,
+
+ ["SetChecked"] = function(self, checked)
+ -- not sure if WoW expects boolean type or not, too lazy to find out so just cast it
+ self.frame:SetChecked(not not checked)
+ end,
+
+ ["GetChecked"] = function(self)
+ return self.frame:GetChecked()
+ end,
+
+ ["GetWidth"] = function(self)
+ return self.frame:GetWidth()
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ else
+ self.frame:Enable()
+ end
+ end,
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local name = "AmrUiCheckBox" .. AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("CheckButton", name, UIParent)
+ frame:SetHeight(16)
+ frame:SetPushedTextOffset(0, 0)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnClick", buttonOnClick)
+
+ -- unchecked texture
+ local texNormal = frame:CreateTexture(nil, "BACKGROUND")
+ texNormal:SetWidth(16)
+ texNormal:SetHeight(16)
+ texNormal:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\check-off")
+ texNormal:SetPoint("LEFT", frame, "LEFT")
+ frame:SetNormalTexture(texNormal)
+
+ -- checked texture
+ local texCheck = frame:CreateTexture(nil, "BORDER")
+ texCheck:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\check-on")
+ texCheck:SetPoint("LEFT", frame, "LEFT")
+ frame:SetCheckedTexture(texCheck)
+
+ -- label
+ local lbl = frame:CreateFontString(nil, "ARTWORK")
+ lbl:SetJustifyV("MIDDLE")
+ lbl:SetPoint("LEFT", texNormal, "RIGHT", 8, 0)
+ frame:SetFontString(lbl)
+
+ local widget = {
+ texNormal = texNormal,
+ label = lbl,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiDropDown.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiDropDown.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,258 @@
+--[[-----------------------------------------------------------------------------
+Dropdown picker widget.
+Simple version that handles our limited needs.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiDropDown", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local _G = _G
+local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
+
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function buttonOnClick(frame, ...)
+ if frame.obj.list:IsVisible() then
+ frame.obj.list:Hide()
+ else
+ frame.obj.list:Show()
+ frame.obj:RenderItems()
+ end
+end
+
+local function buttonOnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function buttonOnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function itemOnClick(frame, ...)
+ frame.obj:SelectItem(frame.value)
+ frame.obj.list:Hide()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- restore default values
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetDisabled(false)
+ self:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
+ self:SelectItem()
+ self.frame:ClearAllPoints()
+ self.list:Hide()
+ end,
+
+ ["OnRelease"] = function(self)
+ self.itemlist = nil
+ for _, item in pairs(self.items) do
+ item:Hide()
+ end
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ self.frame:GetFontString():SetWidth(width)
+ self.list:SetWidth(width)
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ self.frame:GetFontString():SetHeight(height)
+ end,
+
+ ["SetFont"] = function(self, font)
+ self.frame:SetNormalFontObject(font)
+
+ local _, h = font:GetFont()
+ self.fontHeight = h
+ end,
+
+ ["SelectItem"] = function(self, value)
+ -- clear any current selection
+ self.frame:SetText()
+
+ if not self.itemlist or not value then return end
+
+ local found = nil
+ for i, obj in ipairs(self.itemlist) do
+ local wasSelected = obj.selected
+ obj.selected = obj.value == value
+
+ if obj.selected then
+ self.frame:SetText(obj.text)
+ if not wasSelected then
+ found = obj.value
+ end
+ end
+ end
+
+ -- redraw the list if it is open
+ if self.list:IsVisible() then
+ self:RenderItems()
+ end
+
+ -- only fires if selection actually changed
+ if found then
+ self:Fire("OnChange", found)
+ end
+ end,
+
+ -- the list of items to display, ordered list of objects with value, text, color, and selected properties, only supports single selection
+ ["SetItems"] = function(self, items)
+ self.itemlist = items
+ self:RenderItems()
+ end,
+
+ ["CreateItem"] = function(self, index)
+ local itemname = ("AmrUiDropDown%dItem%d"):format(self.num, index)
+
+ local item = CreateFrame("Button", itemname, self.list)
+ item.obj = self
+
+ item:SetHeight(24)
+ item:SetPoint("LEFT", self.list, "LEFT", 1, 0)
+ item:SetPoint("RIGHT", self.list, "RIGHT", -1, 0)
+
+ local txt = item:CreateFontString()
+ item:SetFontString(txt)
+ txt:SetPoint("LEFT", item, "LEFT", 4, 0)
+ txt:SetPoint("RIGHT", item, "RIGHT", -4, 0)
+ txt:SetJustifyH("LEFT")
+
+ item:SetPushedTextOffset(0, 0)
+
+ -- not perfect, but more or less achieves the effect of lightening the bg color slightly on hover
+ local texHigh = item:CreateTexture(nil, "BORDER")
+ texHigh:SetTexture(1, 1, 1, 0.1)
+ texHigh:SetAllPoints(true)
+ item:SetHighlightTexture(texHigh)
+
+ item:SetScript("OnClick", itemOnClick)
+
+ return item
+ end,
+
+ ["RenderItems"] = function(self)
+ if not self.itemlist then return end
+ if not self.list:IsVisible() then return end
+
+ local prev = nil
+ local h = 0
+
+ for i, obj in ipairs(self.itemlist) do
+ local item = self.items[i]
+ if not item then
+ item = self:CreateItem(i)
+ self.items[i] = item
+ end
+
+ item:SetNormalFontObject(Amr.CreateFont(obj.selected and "Bold" or "Regular", obj.selected and self.fontHeight + 2 or self.fontHeight, obj.color or Amr.Colors.White, 1))
+ item:SetText(obj.text)
+ item.value = obj.value
+
+ if prev then
+ item:SetPoint("TOP", prev, "BOTTOM")
+ else
+ item:SetPoint("TOP", self.list, "TOP", 0, -1)
+ end
+
+ h = h + item:GetHeight()
+ prev = item
+ end
+
+ self.list:SetHeight(h + 2)
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ else
+ self.frame:Enable()
+ end
+ end,
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ self.list:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local name = "AmrUiDropDown" .. num
+ local frame = CreateFrame("Button", name, UIParent)
+ frame:Hide()
+
+ local txt = frame:CreateFontString()
+ frame:SetFontString(txt)
+ txt:SetPoint("LEFT", frame, "LEFT", 4, 0)
+ txt:SetPoint("RIGHT", frame, "RIGHT", -24, 0)
+ txt:SetJustifyH("LEFT")
+
+ frame:SetPushedTextOffset(0, 0)
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", buttonOnEnter)
+ frame:SetScript("OnLeave", buttonOnLeave)
+ frame:SetScript("OnClick", buttonOnClick)
+
+ local border = Amr.CreateTexture(frame, Amr.Colors.BorderGray, 1, "BACKGROUND")
+ border:SetAllPoints()
+
+ local bg = Amr.CreateTexture(frame, Amr.Colors.BgInput, 1, "BORDER")
+ bg:SetPoint("TOPLEFT", frame, "TOPLEFT", 1, -1)
+ bg:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -1, 1)
+
+ local arrow = frame:CreateTexture(nil, "ARTWORK")
+ arrow:SetWidth(16)
+ arrow:SetHeight(16)
+ arrow:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconScrollDown")
+ arrow:SetPoint("RIGHT", frame, "RIGHT", -4, 0)
+
+ local list = CreateFrame("Frame", nil, frame)
+ list:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, 1)
+ list:Hide()
+
+ local listBorder = Amr.CreateTexture(list, Amr.Colors.BorderGray, 1, "BACKGROUND")
+ listBorder:SetAllPoints()
+
+ local listBg = Amr.CreateTexture(list, Amr.Colors.BgInput, 1, "BORDER")
+ listBg:SetPoint("TOPLEFT", list, "TOPLEFT", 1, -1)
+ listBg:SetPoint("BOTTOMRIGHT", list, "BOTTOMRIGHT", -1, 1)
+
+ local widget = {
+ num = num,
+ list = list,
+ items = {},
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ frame.obj = widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiFrame.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiFrame.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,216 @@
+--[[-----------------------------------------------------------------------------
+AmrUiFrame container
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiFrame", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs, assert, type = pairs, assert, type
+local wipe = table.wipe
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: CLOSE
+
+-- width of the frame border
+local _borderWidth = 1
+
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function buttonOnClick(frame)
+ PlaySound("gsTitleOptionExit")
+ frame.obj:Hide()
+end
+
+local function frameOnClose(frame)
+ frame.obj:Fire("OnClose")
+end
+
+local function frameOnMouseDown(frame)
+ AceGUI:ClearFocus()
+end
+
+local function titleOnMouseDown(frame)
+ frame.obj:StartMove()
+end
+
+local function titleOnMouseUp(frame)
+ frame.obj:EndMove()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetAutoAdjustHeight(false)
+ self.frame:SetParent(UIParent)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ self:ApplyStatus()
+ self:Show()
+ end,
+
+ ["OnRelease"] = function(self)
+ self.status = nil
+ wipe(self.localstatus)
+ end,
+
+ ["Hide"] = function(self)
+ self.frame:Hide()
+ end,
+
+ ["Show"] = function(self)
+ self.frame:Show()
+ end,
+
+ -- called to set an external table to store status in
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ self:ApplyStatus()
+ end,
+
+ ["ApplyStatus"] = function(self)
+ local status = self.status or self.localstatus
+ local frame = self.frame
+ frame:ClearAllPoints()
+ if status.top and status.left then
+ frame:SetPoint("TOP", UIParent, "BOTTOM", 0, status.top)
+ frame:SetPoint("LEFT", UIParent, "LEFT", status.left, 0)
+ else
+ frame:SetPoint("CENTER")
+ end
+ end,
+
+ -- color is an object with R, G, B
+ ["SetBackgroundColor"] = function(self, color)
+ self.bg:SetTexture(color.R, color.G, color.B, 1)
+ end,
+
+ ["SetBorderColor"] = function(self, color)
+ self.border:SetTexture(color.R, color.G, color.B, 1)
+ end,
+
+ ["Raise"] = function(self)
+ self.frame:Raise()
+ end,
+
+ ["StartMove"] = function(self)
+ self.frame:StartMoving()
+ AceGUI:ClearFocus()
+ end,
+
+ ["EndMove"] = function(self)
+ self.frame:StopMovingOrSizing()
+ local status = self.status or self.localstatus
+ status.top = self.frame:GetTop()
+ status.left = self.frame:GetLeft()
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ content.width = width
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ content.height = height
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame", "AmrUiFrame" .. num, UIParent)
+ frame:Hide()
+
+ -- make escape key close this window
+ table.insert(UISpecialFrames, frame:GetName())
+
+ frame:EnableMouse(true)
+ frame:SetMovable(true)
+ frame:SetResizable(false)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ frame:SetToplevel(true)
+ frame:SetScript("OnHide", frameOnClose)
+ frame:SetScript("OnMouseDown", frameOnMouseDown)
+
+ Amr.DropShadow(frame)
+
+ local border = frame:CreateTexture(nil, "BACKGROUND", nil, 1)
+ border:SetAllPoints(true)
+
+ local bg = frame:CreateTexture(nil, "BORDER")
+ bg:SetPoint("TOPLEFT", frame, "TOPLEFT", _borderWidth, -_borderWidth)
+ bg:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -_borderWidth, _borderWidth)
+
+ local btnClose = CreateFrame("Button", nil, frame)
+ btnClose:SetNormalFontObject(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btnClose:SetText("x")
+ btnClose:SetWidth(22)
+ btnClose:SetHeight(22)
+ btnClose:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
+ btnClose:SetScript("OnClick", buttonOnClick)
+
+ local lbl = btnClose:GetFontString()
+ lbl:ClearAllPoints()
+ lbl:SetPoint("TOP", btnClose, "TOP", -1, -2)
+
+ -- style the button similar to AmrUiButton
+ Amr.DropShadow(btnClose)
+
+ local tex = Amr.CreateTexture(btnClose, Amr.Colors.Red)
+ tex:SetAllPoints(true)
+ btnClose:SetNormalTexture(tex)
+
+ tex = Amr.CreateTexture(btnClose, Amr.Colors.Red)
+ tex:SetPoint("TOPLEFT", btnClose, "TOPLEFT", 1, -1)
+ tex:SetPoint("BOTTOMRIGHT", btnClose, "BOTTOMRIGHT", 1, -1)
+ btnClose:SetPushedTexture(tex)
+
+ tex = Amr.CreateTexture(btnClose, Amr.Colors.White, 0.1)
+ tex:SetAllPoints(true)
+ btnClose:SetHighlightTexture(tex)
+
+ -- title
+ local titleFrame = CreateFrame("Frame", nil, frame)
+ titleFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
+ titleFrame:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", 0, -40)
+ titleFrame:EnableMouse(true)
+ titleFrame:SetScript("OnMouseDown", titleOnMouseDown)
+ titleFrame:SetScript("OnMouseUp", titleOnMouseUp)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, frame)
+ content:SetPoint("TOPLEFT", titleFrame, "BOTTOMLEFT", 20, 0)
+ content:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -20, 20)
+
+ local widget = {
+ localstatus = {},
+ border = border,
+ bg = bg,
+ content = content,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ btnClose.obj, titleFrame.obj = widget, widget
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiIcon.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiIcon.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,148 @@
+--[[-----------------------------------------------------------------------------
+Icon Container
+Simple container widget that is an icon, and can optionally contain children.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiIcon", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Support Functions
+-------------------------------------------------------------------------------]]
+local function setIconBorderWidth(frame, icon, width)
+ icon:ClearAllPoints()
+ icon:SetPoint("TOPLEFT", frame, "TOPLEFT", width, -width)
+ icon:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -width, width)
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+
+local function frameOnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function frameOnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetAutoAdjustHeight(false)
+ self:SetWidth(64)
+ self:SetHeight(64)
+ self:SetBorderWidth(2)
+ self:HideIconBorder()
+ self:SetIcon(nil)
+ self:SetStrata("FULLSCREEN_DIALOG")
+ self:SetLevel(0)
+ self:SetVisible(true)
+ self.frame:ClearAllPoints()
+ self.icon:SetDesaturated(false)
+ end,
+
+ ["SetIcon"] = function(self, icon)
+ if not icon then
+ self.icon:SetTexture(0, 0, 0, 0)
+ else
+ self.icon:SetTexture(icon)
+ end
+ end,
+
+ ["SetBorderWidth"] = function(self, width)
+ setIconBorderWidth(self.frame, self.icon, width)
+ end,
+
+ ["SetIconBorderColor"] = function(self, color, a)
+ self.bg:SetTexture(color.R, color.G, color.B, a or 1)
+ end,
+
+ ["HideIconBorder"] = function(self)
+ self.bg:SetTexture(0, 0, 0, 0)
+ end,
+
+ ["SetStrata"] = function(self, strata)
+ self.frame:SetFrameStrata(strata)
+ end,
+
+ ["SetLevel"] = function(self, level)
+ self.frame:SetFrameLevel(level)
+ end,
+
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ frame:SetScript("OnEnter", frameOnEnter)
+ frame:SetScript("OnLeave", frameOnLeave)
+
+ local bg = frame:CreateTexture(nil, "BORDER")
+ bg:SetAllPoints()
+
+ local icon = frame:CreateTexture(nil, "ARTWORK", nil, 1)
+ icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+
+ local borderTop = frame:CreateTexture(nil, "ARTWORK", nil, 2)
+ borderTop:SetTexture(0, 0, 0, 1)
+ borderTop:SetHeight(1)
+ borderTop:SetPoint("TOPLEFT", icon, "TOPLEFT")
+ borderTop:SetPoint("TOPRIGHT", icon, "TOPRIGHT")
+
+ local borderRight = frame:CreateTexture(nil, "ARTWORK", nil, 2)
+ borderRight:SetTexture(0, 0, 0, 1)
+ borderRight:SetWidth(1)
+ borderRight:SetPoint("TOPRIGHT", icon, "TOPRIGHT")
+ borderRight:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT")
+
+ local borderBottom = frame:CreateTexture(nil, "ARTWORK", nil, 2)
+ borderBottom:SetTexture(0, 0, 0, 1)
+ borderBottom:SetHeight(1)
+ borderBottom:SetPoint("BOTTOMLEFT", icon, "BOTTOMLEFT")
+ borderBottom:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT")
+
+ local borderLeft = frame:CreateTexture(nil, "ARTWORK", nil, 2)
+ borderLeft:SetTexture(0, 0, 0, 1)
+ borderLeft:SetWidth(1)
+ borderLeft:SetPoint("TOPLEFT", icon, "TOPLEFT")
+ borderLeft:SetPoint("BOTTOMLEFT", icon, "BOTTOMLEFT")
+
+ local widget = {
+ bg = bg,
+ icon = icon,
+ frame = frame,
+ content = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiLabel.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiLabel.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,152 @@
+--[[-----------------------------------------------------------------------------
+Label Widget
+Displays text.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiLabel", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local max, select, pairs = math.max, select, pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+
+local function updateSize(self)
+ if self.resizing then return end
+ local frame = self.frame
+ local width = frame.width or frame:GetWidth() or 0
+ local label = self.label
+ local height
+
+ label:ClearAllPoints()
+ label:SetPoint("TOPLEFT")
+ label:SetWidth(width)
+ height = label:GetHeight()
+
+ self.resizing = true
+ frame:SetHeight(height)
+ frame.height = height
+ self.resizing = nil
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+
+local function frameOnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function frameOnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function frameOnMouseDown(frame, ...)
+ frame.obj:Fire("OnMouseDown", ...)
+end
+
+local function frameOnMouseUp(frame, ...)
+ frame.obj:Fire("OnMouseUp", ...)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- set the flag to stop constant size updates
+ self.resizing = true
+ -- height is set dynamically by the text size
+ self:SetWidth(200)
+ self:SetText()
+ self:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
+ self:SetJustifyH("LEFT")
+ self:SetJustifyV("MIDDLE")
+ self:SetWordWrap(true)
+ self:SetVisible(true)
+
+ -- reset the flag
+ self.resizing = nil
+ -- run the update explicitly
+ updateSize(self)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["OnWidthSet"] = function(self, width)
+ updateSize(self)
+ end,
+
+ ["GetHeight"] = function(self)
+ return self.frame:GetHeight()
+ end,
+
+ ["SetText"] = function(self, text)
+ self.label:SetText(text)
+ updateSize(self)
+ end,
+
+ ["SetFont"] = function(self, font)
+ self.label:SetFontObject(font)
+ updateSize(self)
+ end,
+
+ ["SetJustifyV"] = function(self, val)
+ self.label:SetJustifyV(val)
+ end,
+
+ ["SetJustifyH"] = function(self, val)
+ self.label:SetJustifyH(val)
+ end,
+
+ ["SetWordWrap"] = function(self, enable)
+ self.label:SetWordWrap(enable)
+ updateSize(self)
+ end,
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ frame:SetScript("OnEnter", frameOnEnter)
+ frame:SetScript("OnLeave", frameOnLeave)
+ frame:SetScript("OnMouseDown", frameOnMouseDown)
+ frame:SetScript("OnMouseUp", frameOnMouseUp)
+
+ local label = frame:CreateFontString(nil, "ARTWORK")
+ label:SetFontObject(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
+
+ -- create widget
+ local widget = {
+ label = label,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiPanel.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiPanel.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,93 @@
+--[[-----------------------------------------------------------------------------
+Panel Container
+Simple container widget that is just a panel that can have a background color
+and contains other widgets.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiPanel", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetAutoAdjustHeight(false)
+ self:SetWidth(300)
+ self:SetHeight(100)
+ self:SetBackgroundColor(Amr.Colors.Black)
+ self:SetStrata("FULLSCREEN_DIALOG")
+ self:SetLevel(0)
+ self:SetAlpha(1)
+ self:SetVisible(true)
+ self:EnableMouse(false)
+ self.frame:ClearAllPoints()
+ end,
+
+ ["SetBackgroundColor"] = function(self, color, a)
+ self.bg:SetTexture(color.R, color.G, color.B, a or 1)
+ end,
+
+ -- set a transparent bg to make this panel invisible
+ ["SetTransparent"] = function(self)
+ self:SetBackgroundColor(Amr.Colors.Black, 0)
+ end,
+
+ ["SetStrata"] = function(self, strata)
+ self.frame:SetFrameStrata(strata)
+ end,
+
+ ["SetLevel"] = function(self, level)
+ self.frame:SetFrameLevel(level)
+ end,
+
+ ["SetAlpha"] = function(self, a)
+ self.frame:SetAlpha(a)
+ end,
+
+ ["EnableMouse"] = function(self, enable)
+ self.frame:EnableMouse(enable)
+ end,
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local bg = frame:CreateTexture(nil, "BACKGROUND")
+ bg:SetAllPoints()
+
+ local widget = {
+ bg = bg,
+ frame = frame,
+ content = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiScrollFrame.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiScrollFrame.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,260 @@
+--[[-----------------------------------------------------------------------------
+ScrollFrame Container
+Plain container that scrolls its content and doesn't grow in height.
+Based on AceGUI ScrollFrame, but with a custom scrollbar look.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiScrollFrame", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs, assert, type = pairs, assert, type
+local min, max, floor, abs = math.min, math.max, math.floor, math.abs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function FixScrollOnUpdate(frame)
+ frame:SetScript("OnUpdate", nil)
+ frame.obj:FixScroll()
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function ScrollFrame_OnMouseWheel(frame, value)
+ frame.obj:MoveScroll(value)
+end
+
+local function ScrollFrame_OnSizeChanged(frame)
+ frame:SetScript("OnUpdate", FixScrollOnUpdate)
+end
+
+local function ScrollBar_OnScrollValueChanged(frame, value)
+ frame.obj:SetScroll(value)
+end
+
+local function ScrollBar_UpArrowClick(frame)
+ frame.obj:MoveScroll(50)
+end
+
+local function ScrollBar_DownArrowClick(frame)
+ frame.obj:MoveScroll(-50)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetScroll(0)
+ self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
+ end,
+
+ ["OnRelease"] = function(self)
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ self.scrollframe:SetPoint("BOTTOMRIGHT")
+ self.scrollbar:Hide()
+ self.uparrow:Hide()
+ self.downarrow:Hide()
+ self.scrollBarShown = nil
+ self.content.height, self.content.width = nil, nil
+ end,
+
+ ["SetScroll"] = function(self, value)
+ local status = self.status or self.localstatus
+ local viewheight = self.scrollframe:GetHeight()
+ local height = self.content:GetHeight()
+ local offset
+
+ if viewheight > height then
+ offset = 0
+ else
+ offset = floor((height - viewheight) / 1000.0 * value)
+ end
+ self.content:ClearAllPoints()
+ self.content:SetPoint("TOPLEFT", 0, offset)
+ self.content:SetPoint("TOPRIGHT", 0, offset)
+ status.offset = offset
+ status.scrollvalue = value
+ end,
+
+ ["MoveScroll"] = function(self, value)
+ local status = self.status or self.localstatus
+ local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight()
+
+ if self.scrollBarShown then
+ local diff = height - viewheight
+ local delta = 1
+ if value < 0 then
+ delta = -1
+ end
+ self.scrollbar:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
+ end
+ end,
+
+ ["FixScroll"] = function(self)
+ if self.updateLock then return end
+ self.updateLock = true
+ local status = self.status or self.localstatus
+ local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight()
+ local offset = status.offset or 0
+ local curvalue = self.scrollbar:GetValue()
+ -- Give us a margin of error of 2 pixels to stop some conditions that i would blame on floating point inaccuracys
+ -- No-one is going to miss 2 pixels at the bottom of the frame, anyhow!
+ if viewheight < height + 2 then
+ if self.scrollBarShown then
+ self.scrollBarShown = nil
+ self.scrollbar:Hide()
+ self.uparrow:Hide()
+ self.downarrow:Hide()
+ self.scrollbar:SetValue(0)
+ self.scrollframe:SetPoint("BOTTOMRIGHT")
+ self:DoLayout()
+ end
+ else
+ if not self.scrollBarShown then
+ self.scrollBarShown = true
+ self.scrollbar:Show()
+ self.uparrow:Show()
+ self.downarrow:Show()
+ self.scrollframe:SetPoint("BOTTOMRIGHT", -20, 0)
+ self:DoLayout()
+ end
+ local value = (offset / (viewheight - height) * 1000)
+ if value > 1000 then value = 1000 end
+ self.scrollbar:SetValue(value)
+ self:SetScroll(value)
+ if value < 1000 then
+ self.content:ClearAllPoints()
+ self.content:SetPoint("TOPLEFT", 0, offset)
+ self.content:SetPoint("TOPRIGHT", 0, offset)
+ status.offset = offset
+ end
+ end
+ self.updateLock = nil
+ end,
+
+ ["LayoutFinished"] = function(self, width, height)
+ self.content:SetHeight(height or 0 + 20)
+ self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
+ end,
+
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ if not status.scrollvalue then
+ status.scrollvalue = 0
+ end
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ content.width = width
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ content.height = height
+ end
+}
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ local num = AceGUI:GetNextWidgetNum(Type)
+
+ local scrollframe = CreateFrame("ScrollFrame", nil, frame)
+ scrollframe:SetPoint("TOPLEFT")
+ scrollframe:SetPoint("BOTTOMRIGHT")
+ scrollframe:EnableMouseWheel(true)
+ scrollframe:SetScript("OnMouseWheel", ScrollFrame_OnMouseWheel)
+ scrollframe:SetScript("OnSizeChanged", ScrollFrame_OnSizeChanged)
+
+ local scrollbar = CreateFrame("Slider", ("AmrUiScrollFrame%dScrollBar"):format(num), scrollframe)
+ scrollbar:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, -16)
+ scrollbar:SetPoint("BOTTOMLEFT", scrollframe, "BOTTOMRIGHT", 4, 16)
+ scrollbar:SetMinMaxValues(0, 1000)
+ scrollbar:SetValueStep(1)
+ scrollbar:SetValue(0)
+ scrollbar:SetWidth(16)
+ scrollbar:Hide()
+
+ local thumb = Amr.CreateTexture(scrollbar, Amr.Colors.Gray)
+ thumb:SetWidth(16)
+ thumb:SetHeight(32)
+ scrollbar:SetThumbTexture(thumb)
+
+ local uparrow = CreateFrame("Button", nil, frame)
+ uparrow:SetWidth(16)
+ uparrow:SetHeight(16)
+
+ local tex = uparrow:CreateTexture()
+ tex:SetWidth(16)
+ tex:SetHeight(16)
+ tex:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconScrollUp")
+ tex:SetPoint("LEFT", uparrow, "LEFT")
+ uparrow:SetNormalTexture(tex)
+
+ uparrow:SetPoint("BOTTOMRIGHT", scrollbar, "TOPRIGHT")
+ uparrow:SetScript("OnClick", ScrollBar_UpArrowClick)
+ uparrow:Hide()
+
+ local downarrow = CreateFrame("Button", nil, frame)
+ downarrow:SetWidth(16)
+ downarrow:SetHeight(16)
+
+ tex = downarrow:CreateTexture()
+ tex:SetWidth(16)
+ tex:SetHeight(16)
+ tex:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconScrollDown")
+ tex:SetPoint("LEFT", downarrow, "LEFT")
+ downarrow:SetNormalTexture(tex)
+
+ downarrow:SetPoint("TOPRIGHT", scrollbar, "BOTTOMRIGHT")
+ downarrow:SetScript("OnClick", ScrollBar_DownArrowClick)
+ downarrow:Hide()
+
+ -- set the script as the last step, so it doesn't fire yet
+ scrollbar:SetScript("OnValueChanged", ScrollBar_OnScrollValueChanged)
+
+ local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
+ scrollbg:SetPoint("TOPLEFT", scrollbar, "TOPLEFT", 0, 16)
+ scrollbg:SetPoint("BOTTOMRIGHT", scrollbar, "BOTTOMRIGHT", 0, -16)
+ scrollbg:SetTexture(0, 0, 0, 0.3)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, scrollframe)
+ content:SetPoint("TOPLEFT")
+ content:SetPoint("TOPRIGHT")
+ content:SetHeight(400)
+ scrollframe:SetScrollChild(content)
+
+ local widget = {
+ localstatus = { scrollvalue = 0 },
+ scrollframe = scrollframe,
+ uparrow = uparrow,
+ downarrow = downarrow,
+ scrollbar = scrollbar,
+ content = content,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ scrollframe.obj, scrollbar.obj, uparrow.obj, downarrow.obj = widget, widget, widget, widget
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiTabGroup.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiTabGroup.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,266 @@
+--[[-----------------------------------------------------------------------------
+AMR TabGroup Container
+Container that uses tabs on top to switch between groups.
+This is adapted from AceGUIContainer-TabGroup, but has a custom look.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiTabGroup", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs, ipairs, assert, type, wipe = pairs, ipairs, assert, type, wipe
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+
+local function tabSetSelected(frame, selected)
+ frame.selected = selected
+end
+
+local function tabSetDisabled(frame, disabled)
+ frame.disabled = disabled
+end
+
+local function buildTabsOnUpdate(frame)
+ local self = frame.obj
+ self:BuildTabs()
+ frame:SetScript("OnUpdate", nil)
+end
+
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function tabOnClick(frame)
+ if not (frame.selected or frame.disabled) then
+ PlaySound("igCharacterInfoTab")
+ frame.obj:SelectTab(frame.value)
+ end
+end
+
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self.tabSelector:Hide()
+ self.frame:ClearAllPoints()
+ end,
+
+ ["OnRelease"] = function(self)
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ self.tablist = nil
+ for _, tab in pairs(self.tabs) do
+ tab:Hide()
+ end
+ self.tabSelector:Hide()
+ end,
+
+ ["CreateTab"] = function(self, id, style)
+ local tabname = ("AmrUiTabGroup%dTab%d"):format(self.num, id)
+
+ local tab = CreateFrame("Button", tabname, self.border)
+ tab.obj = self
+ tab.id = id
+
+ if style == "bold" then
+ tab:SetNormalFontObject(Amr.CreateFont("Regular", 24, Amr.Colors.TextHeaderDisabled))
+ tab:SetHighlightFontObject(Amr.CreateFont("Regular", 24, Amr.Colors.TextHover))
+ else
+ tab:SetNormalFontObject(Amr.CreateFont("Regular", 28, Amr.Colors.Text))
+ tab:SetHighlightFontObject(Amr.CreateFont("Regular", 28, Amr.Colors.TextHover))
+ end
+
+ tab:SetScript("OnClick", tabOnClick)
+
+ tab.SetSelected = tabSetSelected
+ tab.SetDisabled = tabSetDisabled
+
+ return tab
+ end,
+
+ ["SetStatusTable"] = function(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ end,
+
+ ["SelectTab"] = function(self, value)
+ local status = self.status or self.localstatus
+
+ self.tabSelector:Hide()
+
+ local found
+ for i, v in ipairs(self.tabs) do
+ if v.value == value then
+ v:SetSelected(true)
+ found = true
+
+ -- show the tab selector under the proper tab
+ if v.style == "underline" then
+ self.tabSelector:SetWidth(v:GetWidth())
+ self.tabSelector:ClearAllPoints()
+ self.tabSelector:SetPoint("TOPLEFT", v, "BOTTOMLEFT", 0, -2)
+ self.tabSelector:Show()
+ v:SetNormalFontObject(Amr.CreateFont("Regular", 28, Amr.Colors.Text))
+ v:SetHighlightFontObject(Amr.CreateFont("Regular", 28, Amr.Colors.Text))
+ elseif v.style == "bold" then
+ v:SetNormalFontObject(Amr.CreateFont("Bold", 28, Amr.Colors.TextHeaderActive))
+ v:SetHighlightFontObject(Amr.CreateFont("Bold", 28, Amr.Colors.TextHeaderActive))
+ end
+ else
+ v:SetSelected(false)
+ if v.style == "bold" then
+ v:SetNormalFontObject(Amr.CreateFont("Regular", 24, Amr.Colors.TextHeaderDisabled))
+ v:SetHighlightFontObject(Amr.CreateFont("Regular", 24, Amr.Colors.TextHover))
+ else
+ v:SetNormalFontObject(Amr.CreateFont("Regular", 28, Amr.Colors.Text))
+ v:SetHighlightFontObject(Amr.CreateFont("Regular", 28, Amr.Colors.TextHover))
+ end
+ end
+ end
+ status.selected = value
+
+ -- call this to reposition after style change
+ self:BuildTabs()
+
+ if found then
+ self:Fire("OnGroupSelected",value)
+ end
+ end,
+
+ ["SetTabs"] = function(self, tabs)
+ self.tablist = tabs
+ self:BuildTabs()
+ end,
+
+ ["BuildTabs"] = function(self)
+ local status = self.status or self.localstatus
+ local tablist = self.tablist
+ local tabs = self.tabs
+
+ if not tablist then return end
+
+ local first = true
+ for i, v in ipairs(tablist) do
+ local tab = tabs[i]
+ if not tab then
+ tab = self:CreateTab(i)
+ tabs[i] = tab
+ end
+
+ local padding = 20
+ if v.style == "bold" then padding = 0 end
+
+ tab:Show()
+ tab:SetText(v.text)
+ tab:SetWidth(tab:GetTextWidth() + padding)
+ tab:SetHeight(tab:GetTextHeight())
+ tab:SetDisabled(v.disabled)
+ tab.value = v.value
+ tab.style = v.style or "underline"
+
+ tab:ClearAllPoints()
+ if first then
+ local firstOffset = 0
+ if tab.style == "bold" then firstOffset = 4 end
+ tab:SetPoint("BOTTOMLEFT", self.border, "TOPLEFT", firstOffset, 8)
+ first = false
+ else
+ tab:SetPoint("LEFT", tabs[i - 1], "RIGHT", 30, 0)
+ end
+ end
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ local content = self.content
+ local contentwidth = width - 60
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ self:BuildTabs(self)
+ self.frame:SetScript("OnUpdate", buildTabsOnUpdate)
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - (self.borderoffset + 23)
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end,
+
+ ["LayoutFinished"] = function(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight((height or 0) + (self.borderoffset + 23))
+ end,
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame",nil,UIParent)
+ frame:SetHeight(100)
+ frame:SetWidth(100)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local border = CreateFrame("Frame", nil, frame)
+ local borderoffset = 30
+ border:SetPoint("TOPLEFT", 0, -borderoffset)
+ border:SetPoint("BOTTOMRIGHT", 0, 3)
+
+ local line = border:CreateTexture(nil, "ARTWORK")
+ line:Hide()
+ line:SetTexture(1, 1, 1, 1)
+ line:SetHeight(4)
+
+ local content = CreateFrame("Frame", nil, border)
+ content:SetPoint("TOPLEFT", 0, 0)
+ content:SetPoint("BOTTOMRIGHT", 0, 0)
+
+ local widget = {
+ num = num,
+ frame = frame,
+ localstatus = {},
+ alignoffset = 18,
+ border = border,
+ borderoffset = borderoffset,
+ tabs = {},
+ tabSelector = line,
+ content = content,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiTextButton.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiTextButton.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,226 @@
+--[[-----------------------------------------------------------------------------
+Text Button Widget
+Based on the AceGUI button, but a custom look that just shows text.
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiTextButton", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local _G = _G
+local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
+
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function buttonOnClick(frame, ...)
+ AceGUI:ClearFocus()
+ PlaySound("igMainMenuOption")
+ frame.obj:Fire("OnClick", ...)
+end
+
+local function buttonOnEnter(frame)
+ frame.obj.bg:Hide()
+ frame.obj.hover:Show()
+ frame.obj:Fire("OnEnter")
+end
+
+local function buttonOnLeave(frame)
+ frame.obj.bg:Show()
+ frame.obj.hover:Hide()
+ frame.obj:Fire("OnLeave")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- restore default values
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetBackgroundColor(Amr.Colors.Black, 0)
+ self:SetHoverBackgroundColor(Amr.Colors.Black, 0)
+ self:SetDisabled(false)
+
+ self:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
+ self:SetHoverFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextHover))
+ self:SetText("")
+ self:SetWordWrap(true)
+ self:SetJustifyH("CENTER")
+ self:SetJustifyV("MIDDLE")
+ self:SetTextPadding()
+
+ self:SetSubtextFont(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
+ self:SetSubtext()
+ self:SetSubtextWordWrap(true)
+ self:SetSubtextJustifyH("CENTER")
+ self:SetSubtextJustifyV("MIDDLE")
+ self:SetSubtextPadding()
+
+ self.frame:ClearAllPoints()
+ self.bg:Show()
+ self.hover:Hide()
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ self.frame:GetFontString():SetWidth(width)
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ self.frame:GetFontString():SetHeight(height)
+ end,
+
+ ["SetBackgroundColor"] = function(self, color, alpha)
+ self.bg:SetTexture(color.R, color.G, color.B, alpha)
+ end,
+
+ ["SetBackgroundImage"] = function(self, image)
+ self.bg:SetTexture(image)
+ self.bg:SetDesaturated(false)
+ end,
+
+ ["SetHoverBackgroundColor"] = function(self, color, alpha)
+ self.hover:SetTexture(color.R, color.G, color.B, alpha)
+ end,
+
+ ["SetHoverBackgroundImage"] = function(self, image)
+ self.hover:SetTexture(image)
+ end,
+
+ ["SetText"] = function(self, text)
+ self.frame:SetText(text)
+ end,
+
+ ["SetWordWrap"] = function(self, enable)
+ self.frame:GetFontString():SetWordWrap(enable)
+ end,
+
+ ["SetJustifyH"] = function(self, val)
+ self.frame:GetFontString():SetJustifyH(val)
+ end,
+
+ ["SetJustifyV"] = function(self, val)
+ self.frame:GetFontString():SetJustifyV(val)
+ end,
+
+ ["SetTextPadding"] = function(self, top, right, bottom, left)
+ local f = self.frame:GetFontString()
+ f:ClearAllPoints()
+
+ if not top and not right and not bottom and not left then
+ f:SetPoint("CENTER")
+ end
+
+ if top then f:SetPoint("TOP", self.frame, "TOP", 0, -top) end
+ if right then f:SetPoint("RIGHT", self.frame, "RIGHT", -right, 0) end
+ if bottom then f:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, bottom) end
+ if left then f:SetPoint("LEFT", self.frame, "LEFT", left, 0) end
+ end,
+
+ ["SetFont"] = function(self, font)
+ self.frame:SetNormalFontObject(font)
+ end,
+
+ ["SetHoverFont"] = function(self, font)
+ self.frame:SetHighlightFontObject(font)
+ end,
+
+ ["SetSubtext"] = function(self, text)
+ self.subtxt:SetText(text)
+ if text then
+ self.subtxt:Show()
+ else
+ self.subtxt:Hide()
+ end
+ end,
+
+ ["SetSubtextWordWrap"] = function(self, enable)
+ self.subtxt:SetWordWrap(enable)
+ end,
+
+ ["SetSubtextJustifyH"] = function(self, val)
+ self.subtxt:SetJustifyH(val)
+ end,
+
+ ["SetSubtextJustifyV"] = function(self, val)
+ self.subtxt:SetJustifyV(val)
+ end,
+
+ ["SetSubtextPadding"] = function(self, top, right, bottom, left)
+ local f = self.subtxt
+ f:ClearAllPoints()
+ if top then f:SetPoint("TOP", self.frame, "TOP", 0, -top) end
+ if right then f:SetPoint("RIGHT", self.frame, "RIGHT", -right, 0) end
+ if bottom then f:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, bottom) end
+ if left then f:SetPoint("LEFT", self.frame, "LEFT", left, 0) end
+ end,
+
+ ["SetSubtextFont"] = function(self, font)
+ self.subtxt:SetFontObject(font)
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ else
+ self.frame:Enable()
+ end
+ end,
+
+ ["SetVisible"] = function(self, visible)
+ if visible then
+ self.frame:Show()
+ else
+ self.frame:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local name = "AmrUiTextButton" .. AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Button", name, UIParent)
+ frame:Hide()
+
+ local txt = frame:CreateFontString()
+ frame:SetFontString(txt)
+
+ local subtxt = frame:CreateFontString()
+ subtxt:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", buttonOnEnter)
+ frame:SetScript("OnLeave", buttonOnLeave)
+ frame:SetScript("OnClick", buttonOnClick)
+
+ local bg = frame:CreateTexture()
+ bg:SetAllPoints()
+
+ local hover = frame:CreateTexture()
+ hover:SetAllPoints()
+
+ local widget = {
+ bg = bg,
+ subtxt = subtxt,
+ hover = hover,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/AmrUiTextarea.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/AmrUiTextarea.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,200 @@
+--[[-----------------------------------------------------------------------------
+Textarea Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "AmrUiTextarea", 1
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+
+-- handles clicking in the scrollframe but below the bounds of the editbox (which can't be sized to same as scrollframe)
+local function scrollFrameMouseUp(self)
+ local editbox = self.obj.editbox
+ editbox:SetFocus()
+ editbox:SetCursorPosition(editbox:GetNumLetters())
+end
+
+local function scrollFrameVerticalScroll(self, offset)
+ local editbox = self.obj.editbox
+ editbox:SetHitRectInsets(0, 0, offset, editbox:GetHeight() - offset - self:GetHeight())
+end
+
+local function editboxCursorChanged(self, _, y, _, cursorHeight)
+ self, y = self.obj.scrollFrame, -y
+ local offset = self:GetVerticalScroll()
+ if y < offset then
+ self:SetVerticalScroll(y)
+ else
+ y = y + cursorHeight - self:GetHeight()
+ if y > offset then
+ self:SetVerticalScroll(y)
+ end
+ end
+end
+
+local function editboxEditFocusLost(self)
+ self:HighlightText(0, 0)
+ self.obj:Fire("OnEditFocusLost")
+end
+
+local function editboxEditFocusGained(frame)
+ AceGUI:SetFocus(frame.obj)
+ frame.obj:Fire("OnEditFocusGained")
+end
+
+local function editboxTextChanged(self, userInput)
+ if userInput then
+ self = self.obj
+ self:Fire("OnTextChanged", self.editbox:GetText())
+ end
+end
+
+local function editboxTextSet(self)
+ self:HighlightText()
+ self:SetCursorPosition(self:GetNumLetters())
+ self = self.obj
+ self:Fire("OnTextSet", self.editbox:GetText())
+end
+
+-- works for both the scrollframe and the editbox, handles e.g. dragging a spell link into the textarea
+local function onReceiveDrag(self)
+ local type, id, info = GetCursorInfo()
+ if type == "spell" then
+ info = GetSpellInfo(id, info)
+ elseif type ~= "item" then
+ return
+ end
+ ClearCursor()
+ self = self.obj
+ local editbox = self.editbox
+ if not editbox:HasFocus() then
+ editbox:SetFocus()
+ editbox:SetCursorPosition(editbox:GetNumLetters())
+ end
+ editbox:Insert(info)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- restore default values
+ self:SetWidth(200)
+ self:SetHeight(24)
+ self:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
+ self:SetText("")
+ self.frame:ClearAllPoints()
+ end,
+
+ ["SetText"] = function(self, text)
+ self.editbox:SetText(text)
+ end,
+
+ ["GetText"] = function(self)
+ return self.editbox:GetText()
+ end,
+
+ ["SetFont"] = function(self, font)
+ self.editbox:SetFontObject(font)
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.editbox:Disable()
+ else
+ self.editbox:Enable()
+ end
+ end,
+
+ ["OnWidthSet"] = function(self, width)
+ self.editbox:SetWidth(width)
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ self.editbox:SetHeight(height)
+ end,
+
+ ["ClearFocus"] = function(self)
+ self.editbox:ClearFocus()
+ self.frame:SetScript("OnShow", nil)
+ end,
+
+ ["SetFocus"] = function(self, highlight)
+ self.editbox:SetFocus()
+ if highlight then
+ self.editbox:HighlightText()
+ end
+ if not self.frame:IsShown() then
+ self.frame:SetScript("OnShow", function(frame)
+ frame.obj.editbox:SetFocus()
+ if highlight then
+ self.editbox:HighlightText()
+ end
+ frame:SetScript("OnShow", nil)
+ end)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local name = "AmrUiTextarea" .. AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("ScrollFrame", name, UIParent)
+ frame:Hide()
+
+ frame:SetScript("OnMouseUp", scrollFrameMouseUp)
+ frame:SetScript("OnReceiveDrag", onReceiveDrag)
+ frame:HookScript("OnVerticalScroll", scrollFrameVerticalScroll)
+
+ local editbox = CreateFrame("EditBox", name .. "Edit", frame)
+ editbox:SetAllPoints()
+ editbox:EnableMouse(true)
+ editbox:SetMultiLine(true)
+ editbox:SetAutoFocus(false)
+ editbox:SetCountInvisibleLetters(false)
+ editbox:SetTextInsets(4, 4, 4, 4)
+
+ editbox:SetScript("OnMouseDown", onReceiveDrag)
+ editbox:SetScript("OnReceiveDrag", onReceiveDrag)
+
+ editbox:SetScript("OnCursorChanged", editboxCursorChanged)
+ editbox:SetScript("OnEscapePressed", editbox.ClearFocus)
+ editbox:SetScript("OnEditFocusLost", editboxEditFocusLost)
+ editbox:SetScript("OnTextChanged", editboxTextChanged)
+ editbox:SetScript("OnTextSet", editboxTextSet)
+ editbox:SetScript("OnEditFocusGained", editboxEditFocusGained)
+
+ frame:SetScrollChild(editbox)
+
+ local border = frame:CreateTexture(nil, "BACKGROUND")
+ border:SetTexture(Amr.Colors.BorderGray.R, Amr.Colors.BorderGray.G, Amr.Colors.BorderGray.B, 1)
+ border:SetAllPoints(true)
+
+ local bg = frame:CreateTexture(nil, "BORDER")
+ bg:SetTexture(Amr.Colors.BgInput.R, Amr.Colors.BgInput.G, Amr.Colors.BgInput.B, 1)
+ bg:SetPoint("TOPLEFT", frame, "TOPLEFT", 1, -1)
+ bg:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -1, 1)
+
+ local widget = {
+ editbox = editbox,
+ scrollFrame = frame,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ editbox.obj, frame.obj = widget, widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff -r 75431c084aa0 -r 01b63b8ed811 ui/CombatLogTab.lua
--- a/ui/CombatLogTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,584 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the CombatLogTab class
-AskMrRobot.CombatLogTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
--- these are valid keys in AmrDb.LogData, all others will be deleted
-local _logDataKeys = {
- ["_logging"] = true,
- ["_autoLog"] = true,
- ["_lastZone"] = true,
- ["_lastDiff"] = true,
- ["_current2"] = true,
- ["_history2"] = true,
- ["_wipes"] = true,
- ["_lastWipe"] = true,
- ["_currentExtra"] = true,
- ["_historyExtra"] = true
-};
-
-local _undoButton = false
-
--- helper to create text for this tab
-local function CreateText(tab, font, relativeTo, xOffset, yOffset, text)
- local t = tab:CreateFontString(nil, "ARTWORK", font)
- t:SetPoint("TOPLEFT", relativeTo, "BOTTOMLEFT", xOffset, yOffset)
- t:SetPoint("RIGHT", tab, "RIGHT", -5, 0)
- t:SetWidth(t:GetWidth())
- t:SetJustifyH("LEFT")
- t:SetText(text)
-
- return t
-end
-
-local function newCheckbox(tab, label, tooltipTitle, description, onClick)
- local check = CreateFrame("CheckButton", "AmrCheck" .. label, tab, "InterfaceOptionsCheckButtonTemplate")
- check:SetScript("OnClick", function(self)
- PlaySound(self:GetChecked() and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff")
- onClick(self, self:GetChecked() and true or false)
- end)
- check.label = _G[check:GetName() .. "Text"]
- check.label:SetText(label)
- check.tooltipText = tooltipTitle
- check.tooltipRequirement = description
- return check
-end
-
-function AskMrRobot.CombatLogTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.CombatLogTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
- -- tab header
- local text = tab:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetText(L.AMR_COMBATLOGTAB_COMBAT_LOGGING)
-
- --scrollframe
- tab.scrollframe = AskMrRobot.ScrollFrame:new(nil, tab)
- tab.scrollframe:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30)
- tab.scrollframe:SetPoint("BOTTOMRIGHT", tab, "BOTTOMRIGHT", -30, 10)
-
- local content = tab.scrollframe.content
- content:SetHeight(730)
-
- local btn = CreateFrame("Button", "AmrCombatLogStart", content, "UIPanelButtonTemplate")
- btn:SetPoint("TOPLEFT", content, "TOPLEFT", 0, 0)
- btn:SetText("Start Logging")
- btn:SetWidth(120)
- btn:SetHeight(30)
- tab.btnStart = btn
-
- btn:SetScript("OnClick", function()
- tab:ToggleLogging()
- end)
-
-
- text = content:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- text:SetPoint("LEFT", btn, "RIGHT", 10, 0)
- tab.loggingStatus = text;
-
- local hmAutoChk = newCheckbox(content,
- L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_HM_LABEL,
- L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_HM_TOOLTIP_TITLE,
- L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_HM_DESCRIPTION,
- function(self, value)
- if value then
- AmrDb.LogData._autoLog[AskMrRobot.instanceIds.Highmaul] = "enabled"
- else
- AmrDb.LogData._autoLog[AskMrRobot.instanceIds.Highmaul] = "disabled"
- end
-
- AmrDb.LogData._lastZone = nil
- AmrDb.LogData._lastDiff = nil
- tab:UpdateAutoLogging()
- end
- )
- hmAutoChk:SetChecked(AmrDb.LogData._autoLog[AskMrRobot.instanceIds.Highmaul] == "enabled")
- hmAutoChk:SetPoint("TOPLEFT", btn, "BOTTOMLEFT", 0, -10)
- hmAutoChk:SetHeight(30)
-
- local brfAutoChk = newCheckbox(content,
- L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_BRF_LABEL,
- L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_BRF_TOOLTIP_TITLE,
- L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_BRF_DESCRIPTION,
- function(self, value)
- if value then
- AmrDb.LogData._autoLog[AskMrRobot.instanceIds.BlackrockFoundry] = "enabled"
- else
- AmrDb.LogData._autoLog[AskMrRobot.instanceIds.BlackrockFoundry] = "disabled"
- end
-
- AmrDb.LogData._lastZone = nil
- AmrDb.LogData._lastDiff = nil
- tab:UpdateAutoLogging()
- end
- )
- brfAutoChk:SetChecked(AmrDb.LogData._autoLog[AskMrRobot.instanceIds.BlackrockFoundry] == "enabled")
- brfAutoChk:SetPoint("TOPLEFT", hmAutoChk, "BOTTOMLEFT", 0, -10)
- brfAutoChk:SetHeight(30)
-
- local text = CreateText(content, "GameFontNormalLarge", brfAutoChk, 0, -20, L.AMR_COMBATLOGTAB_INFIGHT)
-
- btn = CreateFrame("Button", "AmrCombatLogWipe", brfAutoChk, "UIPanelButtonTemplate")
- btn:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -10)
- btn:SetText("Wipe")
- btn:SetWidth(70)
- btn:SetHeight(30)
- btn:SetScript("OnClick", function()
- tab:LogWipe()
- end)
-
- tab.btnWipe = btn
-
- local text2 = CreateText(content, "GameFontWhite", text, 80, -12, L.AMR_COMBATLOGTAB_WIPE_1)
- text2 = CreateText(content, "GameFontWhite", text2, 0, -2, L.AMR_COMBATLOGTAB_WIPE_2)
- text2 = CreateText(content, "GameFontWhite", text2, 0, -2, L.AMR_COMBATLOGTAB_WIPE_3)
-
- btn = CreateFrame("Button", "AmrCombatLogUnWipe", content, "UIPanelButtonTemplate")
- btn:SetPoint("LEFT", text, "LEFT", 0, 0)
- btn:SetPoint("TOP", text2, "BOTTOM", 0, -10)
- btn:SetText("Undo")
- btn:SetWidth(70)
- btn:SetHeight(30)
- btn:Hide() -- initially hidden
- btn:SetScript("OnClick", function()
- tab:LogUnwipe()
- end)
- tab.btnUnwipe = btn
-
- text = content:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- text:SetPoint("LEFT", btn, "LEFT", 80, 0)
- tab.lastWipeLabel = text
-
- text = CreateText(tab, "GameFontNormalLarge", btn, 0, -20, L.AMR_COMBATLOGTAB_HEADLINE_OVER_BUTTON)
-
- btn = CreateFrame("Button", "AmrCombatLogSaveCharData", content, "UIPanelButtonTemplate")
- btn:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -5)
- btn:SetText(L.AMR_COMBATLOGTAB_SAVE_CHARACTER)
- btn:SetWidth(150)
- btn:SetHeight(30)
-
- -- reload the UI will save character data to disk
- btn:SetScript("OnClick", ReloadUI)
-
- text = CreateText(content, "GameFontWhite", btn, 0, -15, L.AMR_COMBATLOGTAB_SAVE_CHARACTER_INFO)
-
- text = CreateText(content, "GameFontNormalLarge", text, 0, -30, L.AMR_COMBATLOGTAB_INSTRUCTIONS)
- text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_1)
- text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_2)
- text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_3)
- text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_4)
-
- text = CreateText(content, "GameFontNormalSmall", text, 0, -30, L.AMR_COMBATLOGTAB_INSTRUCTIONS_5)
- text = CreateText(content, "GameFontNormalSmall", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_6)
- text = CreateText(content, "GameFontNormalSmall", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_7)
-
- --[[
- btn = CreateFrame("Button", "AmrCombatLogTest", tab, "UIPanelButtonTemplate")
- btn:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -15)
- btn:SetText("Test")
- btn:SetWidth(120)
- btn:SetHeight(30)
-
- btn:SetScript("OnClick", function()
-
- local t = time()
- AskMrRobot.SaveAll()
- AskMrRobot.ExportToAddonChat(t)
- AskMrRobot.CombatLogTab.SaveExtras(t)
- end)
- ]]
-
- -- when we start up, ensure that logging is still enabled if it was enabled when they last used the addon
- if (AskMrRobot.CombatLogTab.IsLogging()) then
- SetCVar("advancedCombatLogging", 1)
- LoggingCombat(true)
- end
-
- -- if auto-logging is enabled, do a check when the addon is loaded to make sure that state is set correctly
- if tab:IsAutoLoggingEnabled() then
- tab:UpdateAutoLogging()
- end
-
- tab:SetScript("OnShow", function()
- tab:Update()
- end)
-
- return tab
-end
-
-function AskMrRobot.CombatLogTab.IsLogging()
- return AmrDb.LogData._logging == true
-end
-
-function AskMrRobot.CombatLogTab:StartLogging()
-
- local now = time()
- local oldDuration = 60 * 60 * 24 * 10
-
- -- archive the current logging session so that users don't accidentally blow away data before uploading it
- if AmrDb.LogData._current2 ~= nil then
- if not AmrDb.LogData._history2 then AmrDb.LogData._history2 = {} end
-
- -- add new entries
- for name, timeList in AskMrRobot.spairs(AmrDb.LogData._current2) do
- if not AmrDb.LogData._history2[name] then AmrDb.LogData._history2[name] = {} end
- for timestamp, dataString in AskMrRobot.spairs(timeList) do
- AmrDb.LogData._history2[name][timestamp] = dataString
- end
- end
-
- -- delete entries that are more than 10 days old
- for name, timeList in AskMrRobot.spairs(AmrDb.LogData._history2) do
- for timestamp, dataString in AskMrRobot.spairs(timeList) do
- if difftime(now, tonumber(timestamp)) > oldDuration then
- timeList[timestamp] = nil
- end
- end
-
- local count = 0
- for timestamp, dataString in pairs(timeList) do
- count = count + 1
- end
- if count == 0 then
- AmrDb.LogData._history2[name] = nil
- end
- end
- end
-
- -- same idea with extra info (auras, pets, whatever we end up adding to it)
- if AmrDb.LogData._currentExtra ~= nil then
- if not AmrDb.LogData._historyExtra then AmrDb.LogData._historyExtra = {} end
-
- -- add new entries
- for name, timeList in AskMrRobot.spairs(AmrDb.LogData._currentExtra) do
- if not AmrDb.LogData._historyExtra[name] then AmrDb.LogData._historyExtra[name] = {} end
- for timestamp, dataString in AskMrRobot.spairs(timeList) do
- AmrDb.LogData._historyExtra[name][timestamp] = dataString
- end
- end
-
- -- delete entries that are more than 10 days old
- for name, timeList in AskMrRobot.spairs(AmrDb.LogData._historyExtra) do
- for timestamp, dataString in AskMrRobot.spairs(timeList) do
- if difftime(now, tonumber(timestamp)) > oldDuration then
- timeList[timestamp] = nil
- end
- end
-
- local count = 0
- for timestamp, dataString in pairs(timeList) do
- count = count + 1
- end
- if count == 0 then
- AmrDb.LogData._historyExtra[name] = nil
- end
- end
- end
-
-
- -- delete _wipes entries that are more than 10 days old
- if AmrDb.LogData._wipes then
- local i = 1
- while i <= #AmrDb.LogData._wipes do
- local t = AmrDb.LogData._wipes[i]
- if difftime(now, t) > oldDuration then
- tremove(AmrDb.LogData._wipes, i)
- else
- i = i + 1
- end
- end
- end
-
- -- delete the _lastWipe if it is more than 10 days old
- if AmrDb.LogData._lastWipe and difftime(now, AmrDb.LogData._lastWipe) > oldDuration then
- AmrDb.LogData_lastWipe = nil
- end
-
- -- clean up old-style logging data from previous versions of the addon
- for k, v in AskMrRobot.spairs(AmrDb.LogData) do
- if not _logDataKeys[k] then
- AmrDb.LogData[k] = nil
- end
- end
-
- -- start a new logging session
- AmrDb.LogData._current2 = {}
- AmrDb.LogData._currentExtra = {}
- AmrDb.LogData._logging = true
-
- -- always enable advanced combat logging via our addon, gathers more detailed data for better analysis
- SetCVar("advancedCombatLogging", 1)
-
- LoggingCombat(true)
- self:Update()
-
- AskMrRobot.AmrUpdateMinimap()
-
- print(L.AMR_COMBATLOGTAB_IS_LOGGING)
-end
-
-function AskMrRobot.CombatLogTab:StopLogging()
- LoggingCombat(false)
- AmrDb.LogData._logging = false
- self:Update()
-
- AskMrRobot.AmrUpdateMinimap()
-
- print(L.AMR_COMBATLOGTAB_STOPPED_LOGGING)
-end
-
-function AskMrRobot.CombatLogTab:ToggleLogging()
- if AskMrRobot.CombatLogTab.IsLogging() then
- self:StopLogging()
- else
- self:StartLogging()
- end
-end
-
--- update the panel and state
-function AskMrRobot.CombatLogTab:Update()
- local isLogging = AskMrRobot.CombatLogTab.IsLogging()
-
- if isLogging then
- self.btnStart:SetText(L.AMR_COMBATLOGTAB_STOP_LOGGING)
- self.loggingStatus:SetText(L.AMR_COMBATLOGTAB_CURRENTLY_LOGGING)
- else
- self.btnStart:SetText(L.AMR_COMBATLOGTAB_START_LOGGING)
- self.loggingStatus:SetText("")
- end
-
- if AmrDb.LogData._lastWipe then
- self.lastWipeLabel:SetText(L.AMR_COMBATLOGTAB_LASTWIPE:format(date('%B %d', AmrDb.LogData._lastWipe), date('%I:%M %p', AmrDb.LogData._lastWipe)))
- self.btnUnwipe:Show()
- else
- self.lastWipeLabel:SetText("")
- self.btnUnwipe:Hide()
- end
-
-end
-
--- returns true if any auto-logging options are enabled
-function AskMrRobot.CombatLogTab:IsAutoLoggingEnabled()
- -- see if any auto-logging is enabled
- for k,v in pairs(AmrDb.LogData._autoLog) do
- if v == "enabled" then
- return true
- end
- end
- return false
-end
-
--- called to update logging state when auto-logging is enabled
-function AskMrRobot.CombatLogTab:UpdateAutoLogging()
-
- -- get the info about the instance
- --local zone, zonetype, difficultyIndex, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID = GetInstanceInfo()
- local zone, _, difficultyIndex, _, _, _, _, instanceMapID = GetInstanceInfo()
- --local difficulty = difficultyIndex
- -- Unless Blizzard fixes scenarios to not return nil, let's hardcode this into returning "scenario" -Znuff
- --if zonetype == nil and difficultyIndex == 1 then
- --zonetype = "scenario"
- --end
-
- if zone == AmrDb.LogData._lastZone and difficultyIndex == AmrDb.LogData._lastDiff then
- -- do nothing if the zone hasn't actually changed, otherwise we may override the user's manual enable/disable
- return
- end
-
- AmrDb.LogData._lastZone = zone
- AmrDb.LogData._lastDiff = difficultyIndex
-
- if self:IsAutoLoggingEnabled() then
- if AskMrRobot.IsSupportedInstanceId(instanceMapID) and AmrDb.LogData._autoLog[tonumber(instanceMapID)] == "enabled" then
- -- we are in a supported zone that we want to auto-log, turn logging on
- -- (supported check is probably redundant, but just in case someone has old settings lying around)
- if not AskMrRobot.CombatLogTab.IsLogging() then
- self:StartLogging()
- end
- else
- -- not in a zone that we want to auto-log, turn logging off
- if AskMrRobot.CombatLogTab.IsLogging() then
- self:StopLogging()
- end
- end
- end
-
-end
-
-local function RaidChatType()
- if UnitIsGroupAssistant("player") or UnitIsGroupLeader("player") then
- return "RAID_WARNING"
- else
- return "RAID"
- end
-end
-
--- used to store wipes to AmrDb.LogData so that we trim data after the wipe
-function AskMrRobot.CombatLogTab:LogWipe()
- local t = time()
- tinsert(AmrDb.LogData._wipes, t)
- AmrDb.LogData._lastWipe = t
-
- --if GetNumGroupMembers() > 0 then
- -- SendChatMessage(L.AMR_COMBATLOGTAB_WIPE_CHAT, RaidChatType())
- --end
- print(string.format(L.AMR_COMBATLOGTAB_WIPE_MSG, date('%I:%M %p', t)))
-
- self:Update()
-end
-
--- used to undo the wipe command
-function AskMrRobot.CombatLogTab:LogUnwipe()
- local t = AmrDb.LogData._lastWipe
- if not t then
- print(L.AMR_COMBATLOGTAB_NOWIPES)
- else
- tremove(AmrDb.LogData._wipes)
- AmrDb.LogData._lastWipe = nil
- print(string.format(L.AMR_COMBATLOGTAB_UNWIPE_MSG, date('%I:%M %p', t)))
- end
- self:Update()
-end
-
--- initialize the AmrDb.LogData variable
-function AskMrRobot.CombatLogTab.InitializeVariable()
- if not AmrDb.LogData then AmrDb.LogData = {} end
- if not AmrDb.LogData._autoLog then AmrDb.LogData._autoLog = {} end
-
- for k,v in pairs(AskMrRobot.supportedInstanceIds) do
- if not AmrDb.LogData._autoLog[k] then
- AmrDb.LogData._autoLog[k] = "disabled"
- end
- end
-
- AmrDb.LogData._wipes = AmrDb.LogData._wipes or {}
-end
-
-local function GetPlayerExtraData(data, unitId, petId)
-
- local guid = UnitGUID(unitId)
- if guid == nil then
- return
- end
-
- local fields = {}
-
- local buffs = {}
- for i=1,40 do
- local _,_,_,count,_,_,_,_,_,_,spellId = UnitAura(unitId, i, "HELPFUL")
- table.insert(buffs, spellId)
- end
- if #buffs == 0 then
- table.insert(fields, "_")
- else
- table.insert(fields, AskMrRobot.toCompressedNumberList(buffs))
- end
-
- local petGuid = UnitGUID(petId)
- if petGuid then
- table.insert(fields, guid .. "," .. petGuid)
- else
- table.insert(fields, '_')
- end
-
- local name = GetUnitName(unitId, true) -- GetRaidRosterInfo(rosterIndex)
- local realm = GetRealmName()
- local region = AskMrRobot.regionNames[GetCurrentRegion()]
- local splitPos = string.find(name, "-")
- if splitPos ~= nil then
- realm = string.sub(name, splitPos + 1)
- name = string.sub(name, 1, splitPos - 1)
- end
-
- data[region .. ":" .. realm .. ":" .. name] = table.concat(fields, ";")
-end
-
-function AskMrRobot.CombatLogTab.SaveExtras(timestamp)
-
- if not AskMrRobot.CombatLogTab.IsLogging() then
- return
- end
-
- local units = {}
- local petUnits = {}
-
- if IsInRaid() then
- for i = 1,40 do
- table.insert(units, "raid" .. i)
- table.insert(petUnits, "raidpet" .. i)
- end
- elseif IsInGroup() then
- table.insert(units, "player")
- table.insert(petUnits, "pet")
- for i = 1,4 do
- table.insert(units, "party" .. i)
- table.insert(petUnits, "partypet" .. i)
- end
- else
- return
- end
-
- local data = {}
- for i = 1,#units do
- GetPlayerExtraData(data, units[i], petUnits[i])
- end
-
- for name,val in pairs(data) do
- -- record aura stuff, we never check for duplicates, need to know it at each point in time
- if AmrDb.LogData._currentExtra[name] == nil then
- AmrDb.LogData._currentExtra[name] = {}
- end
- AmrDb.LogData._currentExtra[name][timestamp] = val
- end
-end
-
--- read a message sent to the addon channel with a player's info at the time an encounter started
-function AskMrRobot.CombatLogTab:ReadAddonMessage(message)
-
- -- message will be of format: timestamp\nregion\nrealm\nname\n[stuff]
- local parts = {}
- for part in string.gmatch(message, "([^\n]+)") do
- tinsert(parts, part)
- end
-
- local timestamp = parts[1]
- local name = parts[2] .. ":" .. parts[3] .. ":" .. parts[4]
- local data = parts[5]
-
- if (data == "done") then
- -- we have finished receiving this message; now process it to reduce the amount of duplicate data
- local setup = AmrDb.LogData._current2[name][timestamp]
-
- if (AmrDb.LogData._previousSetup == nil) then
- AmrDb.LogData._previousSetup = {}
- end
-
- local previousSetup = AmrDb.LogData._previousSetup[name]
-
- if (previousSetup == setup) then
- -- if the last-seen setup for this player is the same as the current one, we don't need this entry
- AmrDb.LogData._current2[name][timestamp] = nil
- else
- -- record the last-seen setup
- AmrDb.LogData._previousSetup[name] = setup
- end
-
- else
- -- concatenate messages with the same timestamp+name
- if (AmrDb.LogData._current2[name] == nil) then
- AmrDb.LogData._current2[name] = {}
- end
-
- if (AmrDb.LogData._current2[name][timestamp] == nil) then
- AmrDb.LogData._current2[name][timestamp] = data
- else
- AmrDb.LogData._current2[name][timestamp] = AmrDb.LogData._current2[name][timestamp] .. data
- end
- end
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/Components.lua
--- a/ui/Components.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-local _, AskMrRobot = ...
-
-local unresolvedItemIds = {}
-
--- Create a new class that inherits from a base class
-function AskMrRobot.inheritsFrom( baseClass )
-
- -- The following lines are equivalent to the SimpleClass example:
-
- -- Create the table and metatable representing the class.
- local new_class = { }
-
- -- Note that this function uses class_mt as an upvalue, so every instance
- -- of the class will share the same metatable.
- --
- -- function new_class:create(o)
- -- o = o or {}
- -- setmetatable( o, class_mt )
- -- return o
- -- end
-
- -- The following is the key to implementing inheritance:
-
- -- The __index member of the new class's metatable references the
- -- base class. This implies that all methods of the base class will
- -- be exposed to the sub-class, and that the sub-class can override
- -- any of these methods.
- --
- if baseClass then
- setmetatable( new_class, { __index = baseClass } )
- end
-
- return new_class
-end
-
-local itemInfoFrame = nil;
-
-local function onGetItemInfoReceived(arg1, arg2, arg3)
- -- since wow is awesome, it doesn't tell us *which* item id was just resolved, so we have to look at them all
- for itemId, callbacks in pairs(unresolvedItemIds) do
- -- attempt to get the item info AGAIN
- local a, b, c, d, e, f, g, h, i, j, k = GetItemInfo(itemId)
- -- if we got item info...
- if a then
- -- remove the callbacks from the list
- unresolvedItemIds[itemId] = nil
-
- -- call each callback
- for i = 1, #callbacks do
- callbacks[i](a, b, c, d, e, f, g, h, i, j, k)
- end
- end
- end
-end
-
-
-function AskMrRobot.RegisterItemInfoCallback(itemId, callback)
- if not itemId then
- return
- end
-
- if not itemInfoFrame then
- waitFrame = CreateFrame("Frame","WaitFrame", UIParent);
- waitFrame:RegisterEvent("GET_ITEM_INFO_RECEIVED")
- waitFrame:SetScript("OnEvent", onGetItemInfoReceived);
- end
-
-
- -- get the list of registered callbacks for this particular item
- local list = unresolvedItemIds[itemId]
- -- if there was a list, then just add the callback to the list
- if list then
- tinsert(list, callback)
- else
- -- there wasn't a list, so make a new one with this callback
- unresolvedItemIds[itemId] = { callback }
- end
-end
-
-
--- initialize the Frame class (inherit from a dummy frame)
-AskMrRobot.Frame = AskMrRobot.inheritsFrom(CreateFrame("Frame"))
-
--- Frame contructor
-function AskMrRobot.Frame:new(name, parentFrame, inheritsFrame)
- -- create a new frame (if one isn't supplied)
- local o = CreateFrame("Frame", name, parentFrame, inheritsFrame)
-
- -- use the Frame class
- setmetatable(o, { __index = AskMrRobot.Frame })
-
- -- return the instance of the Frame
- return o
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/EnchantLinkText.lua
--- a/ui/EnchantLinkText.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-local _, AskMrRobot = ...
-
-AskMrRobot.EnchantLinkText = AskMrRobot.inheritsFrom(AskMrRobot.ItemTooltipFrame)
-
-function AskMrRobot.EnchantLinkText:new(name, parent)
- local o = AskMrRobot.ItemTooltipFrame:new(name, parent)
-
- -- use the ItemLinkText class
- setmetatable(o, { __index = AskMrRobot.EnchantLinkText })
-
- -- the item text
- o.itemText = AskMrRobot.FontString:new(o, nil, "ARTWORK", "GameFontWhite")
- o.itemText:SetPoint("TOPLEFT")
- o.itemText:SetPoint("BOTTOMRIGHT")
- o.itemText:SetJustifyH("LEFT")
-
- return o
-end
-
-function AskMrRobot.EnchantLinkText:SetEnchantId(enchantId)
- --self.itemName = nil
- if enchantId and enchantId ~= 0 then
- local enchantData = AskMrRobot.ExtraEnchantData[enchantId];
- local spellId = enchantData and enchantData.spellId
- local link = nil
- if spellId then
- link = 'enchant:' .. spellId
- end
- self:SetItemLink(link)
- if enchantData then
- self.itemText:SetText(enchantData.text)
- else
- --self.itemText:SetText(enchantId)
- self.itemText:SetText('unknown')
- end
- -- if self.useSpellName then
- -- local spellName = spellId and select(1, GetSpellInfo(spellId))
- -- self.itemText:SetText(spellName)
- -- self.itemName = spellName
- -- else
- -- self.itemName = AskMrRobot.getEnchantName(enchantId)
- -- self.itemText:SetText(self.itemName)
- -- end
- else
- self:SetItemLink(nil)
- self.itemText:SetText('')
- end
-end
-
-function AskMrRobot.EnchantLinkText:SetFontSize(fontSize)
- self.itemText:SetFontSize(fontSize)
-end
-
-function AskMrRobot.EnchantLinkText:UseSpellName()
- self.useSpellName = true
-end
-
-AskMrRobot.EnchantLinkIconAndText = AskMrRobot.inheritsFrom(AskMrRobot.EnchantLinkText)
-
-function AskMrRobot.EnchantLinkIconAndText:new(name, parent)
- local o = AskMrRobot.EnchantLinkText:new(name, parent)
-
- -- use the EnchantLinkIconAndText class
- setmetatable(o, { __index = AskMrRobot.EnchantLinkIconAndText })
-
- o.iconFrame = AskMrRobot.Frame:new(nil, o)
- o.iconFrame:SetPoint("TOPLEFT", 0, 5)
- o.iconFrame:SetWidth(24)
- o.iconFrame:SetHeight(24)
-
- o.icon = o.iconFrame:CreateTexture(nil, "BACKGROUND")
- o.icon:SetPoint("TOPLEFT")
- o.icon:SetPoint("BOTTOMRIGHT")
-
- o.itemText:SetPoint("TOPLEFT", o.iconFrame, "TOPRIGHT", 4, -5)
-
- o:SetRoundBorder()
-
- return o
-end
-
-function AskMrRobot.EnchantLinkIconAndText:SetRoundBorder()
- self.iconFrame:SetBackdrop({edgeFile = "Interface\\AddOns\\AskMrRobot\\Media\\round-edge", edgeSize = 8})
-end
-
-function AskMrRobot.EnchantLinkIconAndText:SetSquareBorder()
- self.iconFrame:SetBackdrop({edgeFile = "Interface\\AddOns\\AskMrRobot\\Media\\square-edge", edgeSize = 8})
-end
-
-function AskMrRobot.EnchantLinkIconAndText:SetEnchantId(enchantId)
- AskMrRobot.EnchantLinkText.SetEnchantId(self, enchantId)
- if enchantId and enchantId ~= 0 then
- --local texture = AskMrRobot.getEnchantIcon(enchantId)
- --self.icon:SetTexture('Interface/Icons/' .. texture)
- local enchantData = AskMrRobot.ExtraEnchantData[enchantId];
- local spellId = enchantData and enchantData.spellId
- local link = nil
- if spellId then
- link = 'enchant:' .. spellId
- local _, _, icon = GetSpellInfo(spellId)
- if icon then
- self.icon:SetTexture(icon)
- end
- end
-
- self.iconFrame:Show()
- else
- self.iconFrame:Hide()
- end
-end
-
-function AskMrRobot.EnchantLinkIconAndText:SetFontSize(fontSize)
- self.itemText:SetFontSize(fontSize)
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/EnchantTab.lua
--- a/ui/EnchantTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
--- initialize the EnchantTab class
-AskMrRobot.EnchantTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
-function AskMrRobot.EnchantTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.EnchantTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
-
- local text = tab:CreateFontString("AmrEnchantsText1", "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetText(L.AMR_ENCHANTTAB_ENCHANTS)
-
- tab.stamp = AskMrRobot.RobotStamp:new(nil, tab)
- tab.stamp:Hide()
- tab.stamp.smallText:SetText(L.AMR_ENCHANTTAB_100_OPTIMAL)
- tab.stamp:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 2, -15)
- tab.stamp:SetPoint("RIGHT", tab, "RIGHT", -20, 0)
-
- tab.slotHeader = tab:CreateFontString("AmrBadEnchantSlotHeader", "ARTWORK", "GameFontNormal")
- tab.slotHeader:SetPoint("TOPLEFT", "AmrEnchantsText1", "BOTTOMLEFT", 0, -20)
- tab.slotHeader:SetText(L.AMR_ENCHANTTAB_SLOT)
- tab.slotHeader:SetWidth(90)
- tab.slotHeader:SetJustifyH("LEFT")
-
- tab.currentHeader = tab:CreateFontString("AmrBadEnchantCurrentHeader", "ARTWORK", "GameFontNormal")
- tab.currentHeader:SetText(L.AMR_ENCHANTTAB_CURRENT)
- tab.currentHeader:SetPoint("TOPLEFT", "AmrBadEnchantSlotHeader", "TOPLEFT", 100, 0)
- tab.currentHeader:SetWidth(120)
- tab.currentHeader:SetJustifyH("LEFT")
-
- tab.optimizedHeader = tab:CreateFontString("AmrBadEnchantOptimizedHeader", "ARTWORK", "GameFontNormal")
- tab.optimizedHeader:SetPoint("TOPLEFT", "AmrBadEnchantCurrentHeader", "TOPLEFT", 140, 0)
- tab.optimizedHeader:SetPoint("RIGHT", -30, 0)
- tab.optimizedHeader:SetText(L.AMR_ENCHANTTAB_OPTIMIZED)
- tab.optimizedHeader:SetJustifyH("LEFT")
-
- tab.badEnchantSlots = {}
- tab.badEnchantCurrent = {}
- tab.badEnchantOptimized = {}
-
- for i = 1, #AskMrRobot.slotIds do
- local itemText = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- itemText:SetPoint("TOPLEFT", "AmrBadEnchantSlotHeader", "TOPLEFT", 0, -26 * i)
- itemText:SetPoint("BOTTOMRIGHT", "AmrBadEnchantSlotHeader", "BOTTOMRIGHT", 0, -26 * i)
- itemText:SetJustifyH("LEFT")
- itemText:SetText(L.AMR_ENCHANTTAB_TESTSLOT)
- tinsert(tab.badEnchantSlots, itemText)
-
- itemText = AskMrRobot.EnchantLinkText:new(nil, tab)
- itemText:SetPoint("TOPLEFT", "AmrBadEnchantCurrentHeader", "TOPLEFT", 0, -26 * i)
- itemText:SetPoint("BOTTOMRIGHT", "AmrBadEnchantCurrentHeader", "BOTTOMRIGHT", 0, -26 * i)
- tinsert(tab.badEnchantCurrent, itemText)
-
- itemText = AskMrRobot.EnchantLinkIconAndText:new(nil, tab)
- itemText:SetPoint("TOPLEFT", "AmrBadEnchantOptimizedHeader", "TOPLEFT", 0, -26 * i)
- itemText:SetPoint("BOTTOMRIGHT", "AmrBadEnchantOptimizedHeader", "BOTTOMRIGHT", 0, -26 * i)
- tinsert(tab.badEnchantOptimized, itemText)
- end
-
- return tab
-end
-
-function AskMrRobot.EnchantTab:Update()
-
- local i = 1
-
- -- for all the bad items
- if AskMrRobot.ComparisonResult.enchants then
- for iSlot = 1, #AskMrRobot.slotIds do
- local slotId = AskMrRobot.slotIds[iSlot]
- local badEnchant = AskMrRobot.ComparisonResult.enchants[slotId]
- if badEnchant ~= nil then
- self.badEnchantSlots[i]:SetText(AskMrRobot.slotDisplayText[slotId])
- self.badEnchantSlots[i]:Show()
-
- self.badEnchantCurrent[i]:SetEnchantId(badEnchant.current)
-
- self.badEnchantOptimized[i]:SetEnchantId(badEnchant.optimized)
- i = i + 1
- end
- end
-
- end
-
- -- hide / show the headers
- if i == 1 then
- self.optimizedHeader:Hide()
- self.currentHeader:Hide()
- self.slotHeader:Hide()
- self.stamp:Show()
- else
- self.optimizedHeader:Show()
- self.currentHeader:Show()
- self.slotHeader:Show()
- self.stamp:Hide()
- end
-
- -- hide the remaining slots
- while i <= #self.badEnchantSlots do
- self.badEnchantSlots[i]:Hide()
- self.badEnchantCurrent[i]:SetEnchantId(nil)
- self.badEnchantOptimized[i]:SetEnchantId(nil)
- i = i + 1
- end
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/ExportTab.lua
--- a/ui/ExportTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the ExportTab class
-AskMrRobot.ExportTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
--- helper to create text for this tab
-local function CreateText(tab, font, relativeTo, xOffset, yOffset, text)
- local t = tab:CreateFontString(nil, "ARTWORK", font)
- t:SetPoint("TOPLEFT", relativeTo, "BOTTOMLEFT", xOffset, yOffset)
- t:SetPoint("RIGHT", tab, "RIGHT", -25, 0)
- t:SetWidth(t:GetWidth())
- t:SetJustifyH("LEFT")
- t:SetText(text)
-
- return t
-end
-
-function AskMrRobot.ExportTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.ExportTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
- local text = tab:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetText(L.AMR_EXPORTTAB_EXPORT_TITLE)
-
- -- copy/paste
- local text2 = CreateText(tab, "GameFontWhite", text, 0, -15, L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_1)
- text = CreateText(tab, "GameFontWhite", text2, 0, -15, L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_2)
-
- local txtExportString = CreateFrame("ScrollFrame", "AmrScrollFrame", tab, "InputScrollFrameTemplate")
- txtExportString:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 12, -10)
- txtExportString:SetPoint("RIGHT", -25, 0)
- txtExportString:SetWidth(txtExportString:GetWidth())
- txtExportString:SetHeight(50)
- txtExportString.EditBox:SetWidth(txtExportString:GetWidth())
- txtExportString.EditBox:SetMaxLetters(0)
- txtExportString.CharCount:Hide()
- tab.txtExportString = txtExportString
-
- txtExportString.EditBox:SetScript("OnEscapePressed", function()
- AskMrRobot.mainWindow:Hide()
- end)
-
- text = CreateText(tab, "GameFontWhite", txtExportString, -12, -20, L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_3)
-
- text = CreateText(tab, "GameFontWhite", text, 0, -10, L.AMR_EXPORTTAB_COPY_PASTE_EXPORT_NOTE)
-
- btn = CreateFrame("Button", "AmrUpdateExportString", tab, "UIPanelButtonTemplate")
- btn:SetPoint("TOPLEFT", text, "BOTTOMLEFT", -2, -10)
- btn:SetText(L.AMR_EXPORTTAB_UPDATE)
- btn:SetWidth(110)
- btn:SetHeight(30)
-
- btn:SetScript("OnClick", function()
- tab:Update()
- end)
-
- tab:SetScript("OnShow", function()
- tab:Update()
- end)
-
- return tab
-end
-
--- update the panel and state
-function AskMrRobot.ExportTab:Update()
- AskMrRobot.SaveAll()
- self.txtExportString.EditBox:SetText(AskMrRobot.ExportToString())
- self.txtExportString.EditBox:HighlightText()
- self.txtExportString.EditBox:SetFocus()
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/FontString.lua
--- a/ui/FontString.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-local _, AskMrRobot = ...
-
--- initialize the Frame class (inherit from a dummy frame)
-AskMrRobot.FontString = AskMrRobot.inheritsFrom(AskMrRobot.Frame:new():CreateFontString(nil, "ARTWORK", "GameFontNormal"))
-
--- Frame contructor
-function AskMrRobot.FontString:new(parentFrame, name, layer, style, fontSize)
-
- local o = parentFrame:CreateFontString(name, layer, style) -- create a new frame (if one isn't supplied)
-
- -- use the fontstring class
- setmetatable(o, { __index = AskMrRobot.FontString })
-
- if fontSize then
- o:SetFontSize(fontSize)
- end
-
- return o
-end
-
-function AskMrRobot.FontString:SetFontSize(fontSize)
- local file, _, flags = self:GetFont()
- self:SetFont(file, fontSize, flags)
-end
-
-function AskMrRobot.FontString:IncreaseFontSize(add)
- local file, fontSize, flags = self:GetFont()
- self:SetFont(file, fontSize + add, flags)
-end
-
-function AskMrRobot.SetFontSize(fontString, fontSize)
- local file, _, flags = fontString:GetFont()
- fontString:SetFont(file, fontSize, flags)
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/GearComparisonTab.lua
--- a/ui/GearComparisonTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,373 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the GearComparisonTab class
-AskMrRobot.GearComparisonTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
--- stores the results of the last gear comparison
-AskMrRobot.ComparisonResult = {
- items = {},
- gems = {},
- enchants = {}
-}
-
-local function createTabButton(tab, text, index)
- local button = CreateFrame("Button", "GearComparisonTab" .. index, tab, "TabButtonTemplate")
- if index == 1 then
- button:SetPoint("TOPLEFT")
- else
- button:SetPoint("LEFT", "GearComparisonTab" .. (index - 1), "RIGHT")
- end
-
- button:SetText(text)
- button:SetWidth(50)
- button:SetHeight(20)
- button:SetID(index)
- button:SetScript("OnClick", AskMrRobot.GearComparisonTab.tabButtonClick)
- return button
-end
-
-function AskMrRobot.GearComparisonTab:tabButtonClick()
- local tab = self:GetParent()
- local id = self:GetID()
- PanelTemplates_SetTab(tab, id)
- for i = 1, #tab.tabs do
- local t = tab.tabs[i]
- if t:GetID() == id then
- t:Show()
- else
- t:Hide()
- end
- end
-end
-
-function AskMrRobot.GearComparisonTab:new(parent)
-
- local tab = AskMrRobot.Frame:new("GearComparison", parent)
- setmetatable(tab, { __index = AskMrRobot.GearComparisonTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
- tab.initialized = false
-
- tab.tabButtons = {
- createTabButton(tab, L.AMR_UI_BUTTON_IMPORT, 1),
- createTabButton(tab, L.AMR_UI_BUTTON_SUMMARY, 2),
- createTabButton(tab, L.AMR_UI_BUTTON_GEMS, 3),
- createTabButton(tab, L.AMR_UI_BUTTON_ENCHANTS, 4),
- createTabButton(tab, L.AMR_UI_BUTTON_SHOPPING_LIST, 5)
- }
-
- PanelTemplates_SetNumTabs(tab, 5)
- PanelTemplates_SetTab(tab, 1)
- for i = 1, #tab.tabButtons do
- PanelTemplates_TabResize(tab.tabButtons[i], 0)
- end
-
- PanelTemplates_DisableTab(tab, 2)
- PanelTemplates_DisableTab(tab, 3)
- PanelTemplates_DisableTab(tab, 4)
- PanelTemplates_DisableTab(tab, 5)
-
- -- create the import tab
- tab.importTab = AskMrRobot.ImportTab:new(tab)
- tab.importTab:SetID(1)
-
- -- set the tab left of the tab
- tab.importTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30)
-
- tab.summaryTab = AskMrRobot.SummaryTab:new(tab)
- tab.summaryTab:SetID(2)
- tab.summaryTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30)
-
- tab.gemTab = AskMrRobot.GemTab:new(tab)
- tab.gemTab:SetID(3)
- tab.gemTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30)
-
- tab.enchantTab = AskMrRobot.EnchantTab:new(tab)
- tab.enchantTab:SetID(4)
- tab.enchantTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30)
-
- tab.shoppingTab = AskMrRobot.ShoppingListTab:new(tab)
- tab.shoppingTab:SetID(5)
- tab.shoppingTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30)
-
- tab.tabs = {tab.importTab, tab.summaryTab, tab.gemTab, tab.enchantTab, tab.shoppingTab}
-
- -- show the first tab
- tab.importTab:Show()
-
- -- setup the import button to run the import
- tab.importTab.button:SetScript("OnClick", function()
- tab:Import()
- tab.tabButtonClick(tab.tabButtons[2])
- end)
-
- tab:SetScript("OnShow", AskMrRobot.GearComparisonTab.OnShow)
-
- --tab:RegisterEvent("ITEM_PUSH")
- --tab:RegisterEvent("DELETE_ITEM_CONFIRM")
- tab:RegisterEvent("SOCKET_INFO_CLOSE")
- tab:RegisterEvent("SOCKET_INFO_UPDATE")
- tab:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
- tab:RegisterEvent("BAG_UPDATE")
-
- tab:SetScript("OnEvent", AskMrRobot.GearComparisonTab.OnEvent)
-
- return tab
-end
-
-function AskMrRobot.GearComparisonTab:On_SOCKET_INFO_CLOSE()
- if self.initialized then
- self:Import()
- end
-end
-
-function AskMrRobot.GearComparisonTab:On_SOCKET_INFO_UPDATE()
- if self.initialized then
- self:Import()
- end
-end
-
-function AskMrRobot.GearComparisonTab:On_PLAYER_SPECIALIZATION_CHANGED()
- if self.initialized then
- self:Import()
- end
-end
-
-function AskMrRobot.GearComparisonTab:On_BAG_UPDATE()
- if self.initialized then
- self:Import()
- end
-end
-
-function AskMrRobot.GearComparisonTab:On_ITEM_PUSH()
- if self.initialized then
- self:Import()
- end
-end
-
-function AskMrRobot.GearComparisonTab:On_DELETE_ITEM_CONFIRM()
- if self.initialized then
- self:Import()
- end
-end
-
-function AskMrRobot.GearComparisonTab:OnShow()
- if not self.initialized then
- self.initialized = true
-
- -- on first show, load the last import
- if AmrDb.LastCharacterImport and AmrDb.LastCharacterImport ~= "" then
- self.importTab:SetImportText(AmrDb.LastCharacterImport)
- self:Import()
- self.tabButtonClick(self.tabButtons[2])
- else
- self:Update()
- end
- else
- self:Update()
- end
-end
-
-function AskMrRobot.GearComparisonTab:Import()
-
- local err = AskMrRobot.ImportCharacter(self.importTab:GetImportText(), AskMrRobot.debug)
- -- goto the summary tab
- self.summaryTab:showImportError(err)
- PanelTemplates_EnableTab(self, 2)
- if err then
- PanelTemplates_DisableTab(self, 3)
- PanelTemplates_DisableTab(self, 4)
- PanelTemplates_DisableTab(self, 5)
- else
- PanelTemplates_EnableTab(self, 3)
- PanelTemplates_EnableTab(self, 4)
- PanelTemplates_EnableTab(self, 5)
- self:Update()
- end
-end
-
--- update the panel and state
-function AskMrRobot.GearComparisonTab:Update()
- -- update the comparison
- if self.summaryTab then
- AskMrRobot.GearComparisonTab.Compare()
- self.summaryTab:showBadItems()
- self.gemTab:Update()
- self.enchantTab:Update()
- self.shoppingTab:Update()
- end
-end
-
-
--- Helper for checking for swapped items, returns a number indicating how different two items are (0 means the same, higher means more different)
-local function countItemDifferences(item1, item2)
- if item1 == nil and item2 == nil then return 0 end
-
- -- different items (id + bonus ids + suffix, constitutes a different physical drop)
- if AskMrRobot.getItemUniqueId(item1, true) ~= AskMrRobot.getItemUniqueId(item2, true) then
- if item1 == nil or item2 == nil then return 1000 end
- -- do a check to deal with SoO item variants, see if we have duplicate ID information
- local info = AskMrRobot.ExtraItemData[item2.id]
- if info == nil then
- info = AskMrRobot.ExtraItemData[item1.id]
- if info == nil or info.duplicateId ~= item2.id then
- return 1000
- end
- elseif info.duplicateId ~= item1.id then
- return 1000
- end
- end
-
- -- different upgrade levels of the same item (only for older gear, player has control over upgrade level)
- if item1.upgradeId ~= item2.upgradeId then
- return 100
- end
-
- -- different gems
- local gemDiffs = 0
- for i = 1, 3 do
- if item1.gemIds[i] ~= item2.gemIds[i] then
- gemDiffs = gemDiffs + 1
- end
- end
-
- local enchantDiff = 0
- if item1.enchantId ~= item2.enchantId then
- enchantDiff = 1
- end
-
- return gemDiffs + enchantDiff
-end
-
-function AskMrRobot.GearComparisonTab:OnEvent(event, ...)
- local handler = self["On_" .. event]
- if handler then
- handler(self, ...)
- end
-end
-
--- modifies data2 such that differences between data1 and data2 in the two specified slots is the smallest
-local function checkSwappedItems(data1, data2, slot1, slot2)
- local diff = countItemDifferences(data1[slot1], data2[slot1]) + countItemDifferences(data1[slot2], data2[slot2])
- local swappedDiff
- if diff > 0 then
- swappedDiff = countItemDifferences(data1[slot1], data2[slot2]) + countItemDifferences(data1[slot2], data2[slot1])
- if swappedDiff < diff then
- local temp = data2[slot1]
- data2[slot1] = data2[slot2]
- data2[slot2] = temp
- end
- end
-end
-
--- compare the last import data to the player's current state
-function AskMrRobot.GearComparisonTab.Compare()
- if not AskMrRobot.ImportData then return end
-
- AskMrRobot.SaveAll()
-
- -- first parse the player's equipped gear into item objects
- local equipped = {}
- for k, v in pairs(AmrDb.Equipped[AmrDb.ActiveSpec]) do
- equipped[k] = AskMrRobot.parseItemLink(v)
- end
-
- -- swap finger/trinket in AskMrRobot.ImportData such that the number of differences is the smallest
- checkSwappedItems(equipped, AskMrRobot.ImportData, INVSLOT_FINGER1, INVSLOT_FINGER2)
- checkSwappedItems(equipped, AskMrRobot.ImportData, INVSLOT_TRINKET1, INVSLOT_TRINKET2)
-
- -- clear previous comparison result
- AskMrRobot.ComparisonResult = {
- items = {},
- gems = {},
- enchants = {}
- }
-
- local result = {
- items = {},
- gems = {},
- enchants = {}
- }
-
- -- determine specific differences
- for i,slotId in ipairs(AskMrRobot.slotIds) do
- local itemEquipped = equipped[slotId]
- local itemImported = AskMrRobot.ImportData[slotId]
-
- local itemsDifferent = AskMrRobot.getItemUniqueId(itemEquipped) ~= AskMrRobot.getItemUniqueId(itemImported)
- if itemsDifferent and itemEquipped ~= nil and itemImported ~= nil then
- -- do an extra check for old versions of SoO items, our server code always converts to new, equivalent version, but need to check backwards for the addon
- local info = AskMrRobot.ExtraItemData[itemImported.id]
- if info and info.duplicateId == itemEquipped.id then
- itemsDifferent = false
- end
- end
-
- if itemsDifferent then
- -- the items are different
- local needsUpgrade = false
- if itemEquipped and itemImported and itemEquipped.id == itemImported.id and itemImported.upgradeId > itemEquipped.upgradeId then
- needsUpgrade = true
- end
- result.items[slotId] = {
- current = itemEquipped,
- optimized = itemImported,
- needsUpgrade = needsUpgrade
- }
- elseif itemEquipped then
-
- if AskMrRobot.ExtraItemData[itemImported.id] and AskMrRobot.ExtraItemData[itemImported.id].socketColors then
-
- -- items are same, check for gem/enchant differences
- -- NOTE: we used to do a bunch of fancy gem checks, but we can ditch all that logic b/c WoD gems are much simpler (no socket bonuses, gem/socket colors to worry about)
- local hasBadGems = false
- for g = 1, #AskMrRobot.ExtraItemData[itemImported.id].socketColors do
- if not AskMrRobot.AreGemsCompatible(itemEquipped.gemIds[g], itemImported.gemIds[g]) then
- hasBadGems = true
- break
- end
- end
-
- if hasBadGems then
- result.gems[slotId] = {
- current = {},
- optimized = {}
- }
-
- for g = 1, #AskMrRobot.ExtraItemData[itemImported.id].socketColors do
- result.gems[slotId].current[g] = itemEquipped.gemIds[g]
- result.gems[slotId].optimized[g] = itemImported.gemIds[g]
- end
- end
- end
-
- if itemEquipped.enchantId ~= itemImported.enchantId then
- result.enchants[slotId] = {
- current = itemEquipped.enchantId,
- optimized = itemImported.enchantId
- }
- end
- end
- end
-
- -- only set the new result if it is completely successful
- AskMrRobot.ComparisonResult = result
-end
-
--- checks our extra gem information to see if the two gems are functionally equivalent
-function AskMrRobot.AreGemsCompatible(gemId1, gemId2)
- if gemId1 == gemId2 then return true end
-
- -- see if we have extra gem information
- local extraInfo = AskMrRobot.ExtraGemData[gemId1]
- if not extraInfo then
- extraInfo = AskMrRobot.ExtraGemData[gemId2]
- end
- if extraInfo == nil or extraInfo.identicalGroup == nil then return false end
-
- -- if identicalGroup contains both gem ids, they are equivalent
- return extraInfo.identicalGroup[gemId1] and extraInfo.identicalGroup[gemId2]
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/GemIcon.lua
--- a/ui/GemIcon.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-local _, AskMrRobot = ...
-
-local primaryGemTexture = "Interface\\ItemSocketingFrame\\UI-ItemSockets";
-local engineeringGemTexture = "Interface\\ItemSocketingFrame\\UI-EngineeringSockets";
-
-GEM_TYPE_INFO = {
- Yellow = {tex=primaryGemTexture, left=0, right=0.16796875, top=0.640625, bottom=0.80859375, r=0.97, g=0.82, b=0.29, OBLeft=0.7578125, OBRight=0.9921875, OBTop=0, OBBottom=0.22265625},
- Red = {tex=primaryGemTexture, left=0.1796875, right=0.34375, top=0.640625, bottom=0.80859375, r=1, g=0.47, b=0.47, OBLeft=0.7578125, OBRight=0.9921875, OBTop=0.4765625, OBBottom=0.69921875},
- Blue = {tex=primaryGemTexture,left=0.3515625, right=0.51953125, top=0.640625, bottom=0.80859375, r=0.47, g=0.67, b=1, OBLeft=0.7578125, OBRight=0.9921875, OBTop=0.23828125, OBBottom=0.4609375},
- Hydraulic = {tex=engineeringGemTexture, left=0.01562500, right=0.68750000, top=0.50000000, bottom=0.58398438, r=1, g=1, b=1, OBLeft=0.01562500, OBRight=0.93750000, OBTop=0.11132813, OBBottom=0.21679688},
- Cogwheel = {tex=engineeringGemTexture, left=0.01562500, right=0.68750000, top=0.41210938, bottom=0.49609375, r=1, g=1, b=1, OBLeft=0.01562500, OBRight=0.78125000, OBTop=0.31640625, OBBottom=0.40820313},
- Meta = {tex=primaryGemTexture, left=0.171875, right=0.3984375, top=0.40234375, bottom=0.609375, r=1, g=1, b=1, OBLeft=0.7578125, OBRight=0.9921875, OBTop=0, OBBottom=0.22265625},
- Prismatic = {tex=engineeringGemTexture, left=0.01562500, right=0.68750000, top=0.76367188, bottom=0.84765625, r=1, g=1, b=1, OBLeft=0.01562500, OBRight=0.68750000, OBTop=0.58789063, OBBottom=0.67187500}
-}
-
-AskMrRobot.GemIcon = AskMrRobot.inheritsFrom(AskMrRobot.ItemIcon)
-
--- item icon contructor
-function AskMrRobot.GemIcon:new(name, parent)
- -- create a new frame (if one isn't supplied)
- local o = AskMrRobot.ItemIcon:new(name, parent)
-
- -- use the ItemIcon class
- setmetatable(o, { __index = AskMrRobot.GemIcon })
-
- -- add the overlay for the
- o.openBracket = o:CreateTexture(nil, "ARTWORK")
- o.openBracket:SetPoint("TOPLEFT")
- o.openBracket:SetPoint("BOTTOMRIGHT")
-
- -- return the instance of the GemIcon
- return o
-end
-
-function AskMrRobot.GemIcon:UpdateGemStuff()
- local info = GEM_TYPE_INFO[self.color]
-
- if self.itemLink then
- -- hide the 2nd half of the empty gem icon
- self.openBracket:Hide()
-
- if info then
- self:SetBackdropBorderColor(info.r, info.g, info.b)
- end
- else
- if info then
- -- set the empty gem background texture
- self.itemIcon:SetTexture(info.tex)
- self.itemIcon:SetTexCoord(info.left, info.right, info.top, info.bottom)
-
- -- set the empty gem overlay
- self.openBracket:SetTexture(info.tex)
- self.openBracket:SetTexCoord(info.OBLeft, info.OBRight, info.OBTop, info.OBBottom)
- self.openBracket:Show()
-
- --hide the border (the empty gem icon has a border)
- self:SetBackdropBorderColor(0,0,0,0)
-
- -- set the empty gem background texture
- self.itemIcon:SetTexture(info.tex)
- self.itemIcon:SetTexCoord(info.left, info.right, info.top, info.bottom)
-
- -- set the empty gem overlay
- self.openBracket:SetTexture(info.tex)
- self.openBracket:SetTexCoord(info.OBLeft, info.OBRight, info.OBTop, info.OBBottom)
- self.openBracket:Show()
- else
- self.openBracket:Hide()
- end
-
- self:SetBackdropBorderColor(0,0,0,0)
- end
-
-end
-
-function AskMrRobot.GemIcon:SetItemLink(link)
- AskMrRobot.ItemIcon.SetItemLink(self, link)
- self:UpdateGemStuff()
-end
-
-function AskMrRobot.GemIcon:SetGemColor(color)
- self.color = color
- self:UpdateGemStuff()
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/GemTab.lua
--- a/ui/GemTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,208 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
-StaticPopupDialogs["AUTOGEM_FINISHED"] = {
- text = L.AMR_GEMTAB_FINISHED,
- button1 = L.AMR_GEMTAB_BUTTON_OK,
- timeout = 0,
- whileDead = true,
- hideOnEscape = true,
- preferredIndex = 3, -- avoid some UI taint, see http://www.wowace.com/announcements/how-to-avoid-some-ui-taint/
-}
-
-StaticPopupDialogs["AUTOGEM_ONCE"] = {
- text = L.AMR_GEMTAB_AUTOGEMMING_IN_PROGRESS,
- button1 = L.AMR_GEMTAB_BUTTON_OK,
- timeout = 0,
- whileDead = true,
- hideOnEscape = true,
- preferredIndex = 3, -- avoid some UI taint, see http://www.wowace.com/announcements/how-to-avoid-some-ui-taint/
-}
-
--- initialize the GemTab class
-AskMrRobot.GemTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
-local MAX_SLOTS = 4
-
--- GemTab contructor
-function AskMrRobot.GemTab:new(parent)
- -- create a new frame (if one isn't supplied)
- local tab = AskMrRobot.Frame:new(nil, parent)
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- -- use the GemTab class
- setmetatable(tab, { __index = AskMrRobot.GemTab })
- tab:Hide()
-
- local text = tab:CreateFontString("AmrGemsText1", "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetText(L.AMR_GEMTAB_GEMS)
-
- tab.stamp = AskMrRobot.RobotStamp:new(nil, tab)
- tab.stamp:Hide()
- tab.stamp.smallText:SetText(L.AMR_GEMTAB_OPTIMAL)
- tab.stamp:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 2, -15)
- tab.stamp:SetPoint("RIGHT", tab, "RIGHT", -20, 0)
-
- text = tab:CreateFontString("AmrGemsText2", "ARTWORK", "GameFontWhite")
- text:SetText(L.AMR_GEMTAB_X_OPTIMIZE)
- text:SetPoint("TOPLEFT", "AmrGemsText1", "BOTTOMLEFT", 0, -20)
- text:SetWidth(200)
- text:SetJustifyH("LEFT")
- tab.gemsTextToOptimize = text
-
- -- autogem button
- tab.button = CreateFrame("Button", "AmrAutoGemButton", tab, "UIPanelButtonTemplate")
- tab.button:SetPoint("TOP", "AmrGemsText1", "BOTTOM", 0, -16)
- tab.button:SetPoint("RIGHT", -40, 0)
- tab.button:SetText(L.AMR_GEMTAB_AUTOGEM_BUTTON)
- tab.button:SetWidth(150)
- tab.button:SetHeight(20)
- tab.button:SetScript("OnClick", function() tab:startAutoGem() end)
- tab.button:Hide()
-
- -- autogem checkbox button
- tab.usePerfectButton = CreateFrame("CheckButton", "AmrUsePerfectButton", tab, "ChatConfigCheckButtonTemplate")
- tab.preferPerfects = true
- tab.usePerfectButton:SetChecked(tab.preferPerfects)
- tab.usePerfectButton:SetPoint("TOPLEFT", "AmrAutoGemButton", "BOTTOMLEFT", 0, -4)
- tab.usePerfectButton:SetScript("OnClick", function () tab.preferPerfects = tab.usePerfectButton:GetChecked() end)
- local text3 = getglobal(tab.usePerfectButton:GetName() .. 'Text')
- text3:SetText(L.AMR_GEMTAB_PREFER_PERFECT)
- text3:SetWidth(150)
- text3:SetPoint("TOPLEFT", tab.usePerfectButton, "TOPRIGHT", 2, -4)
- tab.usePerfectButton:Hide()
-
- tab.gemSlotHeader = tab:CreateFontString("AmrBadGemSlot0", "ARTWORK", "GameFontNormal")
- tab.gemSlotHeader:SetPoint("TOPLEFT", "AmrGemsText2", "BOTTOMLEFT", 0, -20)
- tab.gemSlotHeader:SetText(L.AMR_GEMTAB_SLOT)
- tab.gemSlotHeader:SetWidth(90)
- tab.gemSlotHeader:SetJustifyH("LEFT")
- tab.gemSlotHeader:Hide()
- tab.gemCurrentHeader = tab:CreateFontString("AmrBadGemCurrent0_1", "ARTWORK", "GameFontNormal")
- tab.gemCurrentHeader:SetPoint("TOPLEFT", "AmrBadGemSlot0", "TOPLEFT", 88, 0)
- tab.gemCurrentHeader:SetWidth(110)
- tab.gemCurrentHeader:SetText(L.AMR_GEMTAB_CURRENT)
- tab.gemCurrentHeader:SetJustifyH("LEFT")
- tab.gemCurrentHeader:Hide()
- tab.gemOptimizedHeader = tab:CreateFontString("AmrBadGemOptimized0_1", "ARTWORK", "GameFontNormal")
- tab.gemOptimizedHeader:SetPoint("TOPLEFT", "AmrBadGemCurrent0_1", "TOPLEFT", 70, 0)
- tab.gemOptimizedHeader:SetPoint("RIGHT", -30, 0)
- tab.gemOptimizedHeader:SetText(L.AMR_GEMTAB_OPTIMIZED)
- tab.gemOptimizedHeader:SetJustifyH("LEFT")
- tab.gemOptimizedHeader:Hide()
-
- tab.fauxScroll = CreateFrame("ScrollFrame", "testme", tab, "FauxScrollFrameTemplate")
- tab.fauxScroll:SetPoint("BOTTOMRIGHT", -40, 15)
- tab.fauxScroll:SetPoint("TOPLEFT", "AmrBadGemSlot0", "BOTTOMLEFT", -12, -5)
- tab.fauxScroll.parent = tab
- tab.fauxScroll:SetScript("OnVerticalScroll", AskMrRobot.GemTab.OnVerticalScroll)
-
- tab.jewelPanels = {}
- for i = 1, MAX_SLOTS do
-
- tab.jewelPanels[i] = AskMrRobot.JewelPanel:new("AmrBadGemSlot" .. i, tab)
- if i == 1 then
- tab.jewelPanels[i]:SetPoint("TOPLEFT", "AmrBadGemSlot" .. (i-1), "BOTTOMLEFT", -12, -5)
- --tab.jewelPanels[i]:SetPoint("TOPLEFT")
- else
- tab.jewelPanels[i]:SetPoint("TOPLEFT", "AmrBadGemSlot" .. (i-1), "BOTTOMLEFT", 0, -5)
- end
- tab.jewelPanels[i]:SetPoint("RIGHT", -40, 0)
- end
-
- return tab
-end
-
-function AskMrRobot.GemTab:startAutoGem()
- if AskMrRobot.AutoGem(self.preferPerfects) == false then
- StaticPopup_Show("AUTOGEM_ONCE")
- end
-end
-
-function AskMrRobot.GemTab:Update()
- self.count = 0
-
- local i = 1
- local badGemTotal = 0
-
- if AskMrRobot.ComparisonResult.gems then
- for iSlot = 1, #AskMrRobot.slotIds do
- local slotId = AskMrRobot.slotIds[iSlot]
- local badGems = AskMrRobot.ComparisonResult.gems[slotId]
- if badGems ~= nil then
- self.count = self.count + 1
- if i <= MAX_SLOTS then
- self.jewelPanels[i]:Show()
- end
- for g = 1, #badGems.optimized do
- if not AskMrRobot.AreGemsCompatible(badGems.optimized[g], badGems.current[g]) then
- badGemTotal = badGemTotal + 1
- end
- end
- i = i + 1
- end
- end
- end
-
- self.gemsTextToOptimize:SetFormattedText(L.AMR_GEMTAB_TO_OPTIMIZE, badGemTotal)
-
- --hide/show the headers, depending on if we have any bad gems
- if self.count == 0 then
- self.gemSlotHeader:Hide()
- self.gemCurrentHeader:Hide()
- self.gemOptimizedHeader:Hide()
- self.gemsTextToOptimize:Hide()
- self.button:Hide()
- self.usePerfectButton:Hide()
- self.stamp:Show()
- else
- self.gemSlotHeader:Show()
- self.gemCurrentHeader:Show()
- self.gemOptimizedHeader:Show()
- self.gemsTextToOptimize:Show()
- --self.button:Show()
- --self.usePerfectButton:Show()
- self.stamp:Hide()
- end
-
- for i = self.count + 1, MAX_SLOTS do
- self.jewelPanels[i]:Hide()
- i = i + 1
- end
-
- AskMrRobot.GemTab.OnUpdate(self.fauxScroll, self.count, #self.jewelPanels, self.jewelPanels[1]:GetHeight())
-end
-
-function AskMrRobot.GemTab.OnVerticalScroll(scrollframe, offset)
- local self = scrollframe.parent
- FauxScrollFrame_OnVerticalScroll(self.fauxScroll, offset, self.jewelPanels[1]:GetHeight(), AskMrRobot.GemTab.OnUpdate)
-end
-
-function AskMrRobot.GemTab.OnUpdate(scrollframe)
- local self = scrollframe.parent
- FauxScrollFrame_Update(self.fauxScroll, self.count, #self.jewelPanels, self.jewelPanels[1]:GetHeight())
- local offset = FauxScrollFrame_GetOffset(scrollframe)
-
- local i = 1
- if AskMrRobot.ComparisonResult.gems then
- for iSlot = 1, #AskMrRobot.slotIds do
- local slotId = AskMrRobot.slotIds[iSlot]
- local badGems = AskMrRobot.ComparisonResult.gems[slotId]
- if badGems ~= nil then
- if offset > 0 then
- offset = offset - 1
- else
-
- if i > MAX_SLOTS then
- break
- end
-
- self.jewelPanels[i]:SetItemLink(AskMrRobot.slotDisplayText[slotId], AmrDb.Equipped[AmrDb.ActiveSpec][slotId])
- self.jewelPanels[i]:SetOptimizedGems(badGems.optimized, badGems.current)
- i = i + 1
- end
- end
- end
- end
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/HelpTab.lua
--- a/ui/HelpTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the HelpTab class
-AskMrRobot.HelpTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
-function AskMrRobot.HelpTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.HelpTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
- local text = tab:CreateFontString("AmrHelpText1", "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetText(L.AMR_HELPTAB_TITLE)
-
- local text2 = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- text2:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -10)
- text2:SetPoint("RIGHT", tab, "RIGHT", -25, -8)
- text2:SetWidth(text2:GetWidth())
- text2:SetJustifyH("LEFT")
- text2:SetText(L.AMR_HELPTAB_LINK)
-
- local answer = text2
- for i = 1, 6 do
- text = AskMrRobot.FontString:new(tab, nil, "ARTWORK", "GameFontWhite")
- text:SetPoint("TOPLEFT", answer, "BOTTOMLEFT", 0, -11)
- text:SetPoint("RIGHT", -18, 0)
- text:SetWidth(text2:GetWidth())
- text:SetJustifyH("LEFT")
- text:SetText(L['AMR_HELPTAB_Q' .. i])
- text:IncreaseFontSize(1)
- text:SetTextColor(1,1,1)
-
- answer = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- answer:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -4)
- answer:SetPoint("RIGHT", tab, "RIGHT", -18, 0)
- answer:SetWidth(text2:GetWidth())
- answer:SetJustifyH("LEFT")
- answer:SetText(L['AMR_HELPTAB_A' .. i])
- answer:SetTextColor(0.7,0.7,0.7)
- end
-
- return tab
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/ImportTab.lua
--- a/ui/ImportTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the ImportTab class
-AskMrRobot.ImportTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
-function AskMrRobot.ImportTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.ImportTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
- local text = tab:CreateFontString("AmrImportText1", "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetFormattedText(L.AMR_IMPORTTAB_TITLE)
-
- text = tab:CreateFontString("AmrImportText2", "ARTWORK", "GameFontWhite")
- text:SetPoint("TOPLEFT", "AmrImportText1", "BOTTOMLEFT", 0, -20)
- text:SetPoint("RIGHT", -15, 0)
- text:SetWidth(text:GetWidth())
- text:SetJustifyH("LEFT")
- text:SetText(L.AMR_IMPORTTAB_INSTRUCTIONS_1)
-
- text = tab:CreateFontString("AmrImportText3", "ARTWORK", "GameFontWhite")
- text:SetPoint("TOPLEFT", "AmrImportText2", "BOTTOMLEFT", 0, -10)
- text:SetPoint("RIGHT", -15, 0)
- text:SetWidth(text:GetWidth())
- text:SetJustifyH("LEFT")
- text:SetText(L.AMR_IMPORTTAB_INSTRUCTIONS_2)
-
- local scrollFrame = CreateFrame("ScrollFrame", "AmrImportScrollFrame", tab, "InputScrollFrameTemplate")
- scrollFrame:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 5, -10)
- scrollFrame:SetPoint("RIGHT", -30, 0)
- scrollFrame:SetWidth(430)
- scrollFrame:SetHeight(60);
- scrollFrame.EditBox:SetWidth(scrollFrame:GetWidth())
- scrollFrame.EditBox:SetMaxLetters(2400)
- scrollFrame.CharCount:Hide()
- scrollFrame.EditBox:SetFocus()
- scrollFrame.EditBox:HighlightText()
- tab.scrollFrame = scrollFrame
-
- tab.button = CreateFrame("Button", "AmrImportButton", tab, "UIPanelButtonTemplate")
- tab.button:SetText(L.AMR_IMPORTTAB_BUTTON)
- tab.button:SetWidth(100)
- tab.button:SetHeight(20)
- tab.button:SetPoint("BOTTOM", tab, "BOTTOM", 0, 20)
- scrollFrame:SetPoint("BOTTOM", tab.button, "TOP", 0, 15)
-
- tab:SetScript("OnShow", function()
- tab.scrollFrame.EditBox:HighlightText()
- tab.scrollFrame.EditBox:SetFocus()
- tab:Update()
- end)
-
- return tab
-end
-
-function AskMrRobot.ImportTab:GetImportText()
- return self.scrollFrame.EditBox:GetText()
-end
-
-function AskMrRobot.ImportTab:SetImportText(text)
- self.scrollFrame.EditBox:SetText(text)
- self.scrollFrame.EditBox:HighlightText()
-end
-
--- update the panel and state
-function AskMrRobot.ImportTab:Update()
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/ItemIcon.lua
--- a/ui/ItemIcon.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-local _, AskMrRobot = ...
-
--- initialize the ItemIcon class (inherit from a dummy frame)
-AskMrRobot.ItemIcon = AskMrRobot.inheritsFrom(AskMrRobot.ItemTooltipFrame)
-
--- item icon contructor
-function AskMrRobot.ItemIcon:new(name, parent)
- -- create a new frame (if one isn't supplied)
- local o = AskMrRobot.ItemTooltipFrame:new(name, parent)
-
- -- use the ItemIcon class
- setmetatable(o, { __index = AskMrRobot.ItemIcon })
-
- -- the item icon
- o.itemIcon = o:CreateTexture(nil, "BACKGROUND")
- o.itemIcon:SetPoint("TOPLEFT")
- o.itemIcon:SetPoint("BOTTOMRIGHT")
-
- -- return the instance of the ItemIcon
- return o
-end
-
-function AskMrRobot.ItemIcon:SetRoundBorder()
- self:SetBackdrop({edgeFile = "Interface\\AddOns\\AskMrRobot\\Media\\round-edge", edgeSize = 8})
-end
-
-function AskMrRobot.ItemIcon:SetSquareBorder()
- self:SetBackdrop({edgeFile = "Interface\\AddOns\\AskMrRobot\\Media\\square-edge", edgeSize = 8})
-end
-
--- set the item icon and tooltip from the specified item link
-function AskMrRobot.ItemIcon:SetItemLink(link)
- AskMrRobot.ItemTooltipFrame.SetItemLink(self, link)
- if link then
- self.itemIcon:SetTexture(GetItemIcon(AskMrRobot.getItemIdFromLink(link)))
- self.itemIcon:SetTexCoord(0, 1, 0, 1)
- else
- self.itemIcon:SetTexture(nil)
- end
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/ItemLinkText.lua
--- a/ui/ItemLinkText.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-local _, AskMrRobot = ...
-
-AskMrRobot.ItemLinkText = AskMrRobot.inheritsFrom(AskMrRobot.ItemTooltipFrame)
-
-function AskMrRobot.ItemLinkText:new(name, parent)
- local o = AskMrRobot.ItemTooltipFrame:new(name, parent)
-
- -- use the ItemLinkText class
- setmetatable(o, { __index = AskMrRobot.ItemLinkText })
-
- -- the item text
- o.itemText = AskMrRobot.FontString:new(o, nil, "ARTWORK", "GameFontWhite")
- o.itemText:SetPoint("TOPLEFT")
- o.itemText:SetPoint("BOTTOMRIGHT")
- o.itemText:SetJustifyH("LEFT")
-
- return o
-end
-
-function AskMrRobot.ItemLinkText:SetFormat(formatText)
- self.formatText = formatText
-end
-
-function AskMrRobot.ItemLinkText:SetItem(itemObj)
- -- blank/nil
- if itemObj == nil or itemObj.id == nil or itemObj.id == 0 then
- self.itemText:SetText("empty")
- self.itemText:SetTextColor(0.5,0.5,0.5)
- self:SetItemLink(nil)
- return
- end
-
- local itemName, itemLink = GetItemInfo(AskMrRobot.createItemLink(itemObj))
- self:SetItemLink(itemLink)
- if itemLink then
- self.itemName = itemName
- if self.formatText then
- self.itemText:SetFormattedText(self.formatText, itemLink:gsub("%[", ""):gsub("%]", ""))
- else
- self.itemText:SetText(itemLink:gsub("%[", ""):gsub("%]", ""))
- end
- else
- self.itemText:SetFormattedText("unknown (%d)", itemObj.id)
- self.itemText:SetTextColor(1,1,1)
- AskMrRobot.RegisterItemInfoCallback(itemObj.id, function(name, itemLink2)
- if self.formatText then
- self.itemText:SetFormattedText(self.formatText, itemLink2:gsub("%[", ""):gsub("%]", ""))
- else
- self.itemText:SetText(itemLink2:gsub("%[", ""):gsub("%]", ""))
- end
- self:SetItemLink(itemLink2)
- self.itemName = name
- end)
- end
-end
-
-function AskMrRobot.ItemLinkText:SetItemId(itemId)
-
- self:SetItem({
- id = itemId,
- enchantId = 0,
- gemIds = {0,0,0,0},
- suffixId = 0,
- upgradeId = 0
- })
-
-end
-
-function AskMrRobot.ItemLinkText:SetFontSize(fontSize)
- self.itemText:SetFontSize(fontSize)
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/ItemTooltipFrame.lua
--- a/ui/ItemTooltipFrame.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-local _, AskMrRobot = ...
-
--- initialize the ItemLink class
-AskMrRobot.ItemTooltipFrame = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
--- item link contructor
-function AskMrRobot.ItemTooltipFrame:new(name, parent)
- -- create a new frame
- local o = AskMrRobot.Frame:new(name, parent)
-
- -- use the ItemTooltipFrame class
- setmetatable(o, { __index = AskMrRobot.ItemTooltipFrame })
-
- o.tooltipShown = false
-
- -- initialize the enter/leave scripts for showing the tooltips
- o:SetScript("OnEnter", AskMrRobot.ItemTooltipFrame.OnEnterTooltipFrame)
- o:SetScript("OnLeave", AskMrRobot.ItemTooltipFrame.OnLeaveTooltipFrame)
-
- -- return the instance of the ItemTooltipFrame
- return o
-end
-
-function AskMrRobot.ItemTooltipFrame:OnEnterTooltipFrame()
- if self.itemLink then
- GameTooltip:SetOwner(self, "ANCHOR_CURSOR")
-
- GameTooltip:SetHyperlink(self.itemLink)
-
- GameTooltip:Show()
- self.tooltipShown = true
- end
-end
-
-function AskMrRobot.ItemTooltipFrame:OnLeaveTooltipFrame()
- GameTooltip:Hide()
- self.tooltipShown = false
-end
-
--- set the tooltip from the specified item link
-function AskMrRobot.ItemTooltipFrame:SetItemLink(link)
- if self.tooltipShown then
- GameTooltip:Hide()
- end
- self.itemLink = link
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/JewelPanel.lua
--- a/ui/JewelPanel.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-local _, AskMrRobot = ...
-
-local MAX_GEMS_PER_SLOT = 3
-
--- make the JewelPanel inherit from a dummy frame
-AskMrRobot.JewelPanel = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
--- JewelPanel constructor
-function AskMrRobot.JewelPanel:new (name, parent)
- -- create a new frame if one isn't supplied
- local o = AskMrRobot.Frame:new(name, parent)
-
- -- make the object a JewelPanel instanct
- setmetatable(o, { __index = AskMrRobot.JewelPanel})
-
- -- set the height and border of the newly created jewel frame
- o:SetHeight(95)
- o:SetBackdrop({edgeFile = "Interface/Tooltips/UI-Tooltip-Border", edgeSize = 16})
-
- -- setup the slot name
- o._slotName = o:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- o._slotName:SetPoint("TOPLEFT", 11, -10)
- o._slotName:SetWidth(80)
- o._slotName:SetJustifyH("LEFT")
-
- -- setup the item icon frame
- o._itemIcon = AskMrRobot.ItemIcon:new()
- o._itemIcon:SetParent(o)
- o._itemIcon:SetRoundBorder()
- o._itemIcon:SetPoint("TOPLEFT", 9, -32)
- o._itemIcon:SetWidth(48)
- o._itemIcon:SetHeight(48)
-
- -- initialize the current gems array
- o._currentGems = {}
- o._optimizedGemText = {}
- o._optimizedGemIcons = {}
- -- for each row of gems
- for i = 1, MAX_GEMS_PER_SLOT do
- -- create an item icon for the currently equiped gem
- local gemIcon = AskMrRobot.GemIcon:new(nil, o)
- gemIcon:SetPoint("TOPLEFT", 100, 18 - 27 * i)
- gemIcon:SetWidth(24)
- gemIcon:SetHeight(24)
- gemIcon:SetRoundBorder()
- o._currentGems[i] = gemIcon
-
- -- create an item icon for the optimized gem
- gemIcon = AskMrRobot.GemIcon:new(nil, o)
- gemIcon:SetPoint("TOPLEFT", 170, 18 - 27 * i)
- gemIcon:SetWidth(24)
- gemIcon:SetHeight(24)
- gemIcon:SetRoundBorder()
- o._optimizedGemIcons[i] = gemIcon
-
- -- create the optimized gem text
- local gemText = o:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- gemText:SetPoint("TOPLEFT", 200, 12 - 27 * i)
- gemText:SetPoint("RIGHT", -30)
- gemText:SetJustifyH("LEFT")
- o._optimizedGemText[i] = gemText
- end
-
- -- return the JewelPanel instance
- return o
-end
-
--- set the item link for this JewelPanel
--- this updates the item icon, the slot name, and the tooltip
-function AskMrRobot.JewelPanel:SetItemLink(slotName, itemLink)
- -- set the item icon and the tooltip
- self._itemIcon:SetItemLink(itemLink)
-
- if itemLink then
- local _, _, rarity = GetItemInfo(itemLink)
- if rarity then
- local r,g,b = GetItemQualityColor(rarity)
- self._itemIcon:SetBackdropBorderColor(r,g,b,1)
- else
- self._itemIcon:SetBackdropBorderColor(1,1,1,1)
- end
- else
- self._itemIcon:SetBackdropBorderColor(1,1,1,1)
- end
-
- -- set the slot name
- self._slotName:SetText(slotName)
-end
-
--- set the optimized gem information (array of {id, color, enchantId})
--- SetItemLink must be called first
-function AskMrRobot.JewelPanel:SetOptimizedGems(optimizedGems, showGems)
-
- -- get the item link
- local itemLink = self._itemIcon.itemLink
-
- if not itemLink then return end
-
- -- for all of the gem rows in this control
- local itemId = AskMrRobot.getItemIdFromLink(itemLink)
-
- local gemCount = 0
-
- for i = 1, MAX_GEMS_PER_SLOT do
- -- get the optimized text, optimized icon, and current icon for the row
- local text = self._optimizedGemText[i]
- local optimizedIcon = self._optimizedGemIcons[i]
- local currentIcon = self._currentGems[i]
-
- -- get the current gem in the specified slot
- local currentGemLink = select(2, GetItemGem(itemLink, i))
-
- -- if there is a gem to add (or remove)
- --if i <= #optimizedGems or currentGemLink then
- if i <= #optimizedGems or currentGemLink then
- local optimizedGemId = 0
- if optimizedGems[i] and optimizedGems[i] > 0 then
- optimizedGemId = AskMrRobot.ExtraGemData[optimizedGems[i]].id
- end
- --local currentGemId = AskMrRobot.ExtraGemData[showGems[i]].id
-
- -- set the current gem icon / tooltip
- currentIcon:SetItemLink(currentGemLink)
-
- local currentGemId = AskMrRobot.getItemIdFromLink(currentGemLink)
-
- local optimizedGemLink = nil
- if i <= #optimizedGems then
- -- make a link for the optimized gem
- optimizedGemLink = select(2, GetItemInfo(optimizedGemId))
-
- if not optimizedGemLink and optimizedGemId and itemId then
- AskMrRobot.RegisterItemInfoCallback(optimizedGemId, function(name, link)
- optimizedIcon:SetItemLink(link)
- end)
- end
- end
-
-
- local mismatched = not AskMrRobot.AreGemsCompatible(optimizedGems[i], showGems[i])
-
- --if showGems[i] and optimizedGems[i] and optimizedGems[i].color then
- --if test and optimizedGems[i] and optimizedGems[i].color then
- if mismatched and optimizedGems[i] and optimizedGems[i] > 0 then
- gemCount = gemCount + 1
- -- set the optimized gem text
- text:SetTextColor(1,1,1)
- --text:SetText(AskMrRobot.alternateGemName[optimizedGemId] or (optimizedGems[i] ~= 0 and AskMrRobot.getEnchantName(optimizedGems[i])) or GetItemInfo(optimizedGemId))
-
- text:SetText(AskMrRobot.ExtraGemData[optimizedGems[i]].text)
-
- currentIcon:Show()
-
- -- load the item image / tooltip
- optimizedIcon:SetItemLink(optimizedGemLink)
- optimizedIcon:Show()
- optimizedIcon:SetBackdropBorderColor(1,1,1)
- currentIcon:SetBackdropBorderColor(1,1,1)
- else
- --if optimizedGems[i] and optimizedGems[i].color then
- if optimizedGems[i] then
- text:SetText("no change")
- text:SetTextColor(0.5,0.5,0.5)
- currentIcon:Show()
- gemCount = gemCount + 1
- else
- text:SetText('')
- currentIcon:Hide()
- end
- optimizedIcon:SetItemLink(nil)
- optimizedIcon:Hide()
- end
-
- -- TODO highlight the socket color
- local socketColorId = AskMrRobot.ExtraItemData[itemId].socketColors[i]
- local socketName = AskMrRobot.socketColorIds[socketColorId];
- currentIcon:SetGemColor(optimizedGems[i] and socketName)
- optimizedIcon:SetGemColor(optimizedGems[i] and socketName)
-
- -- show the gem row
- text:Show()
- else
- -- hide the gem row
- text:Hide()
- optimizedIcon:Hide()
- currentIcon:Hide()
- end
- end
-
- local y1 = 0
- local y2 = 0
- if gemCount == 1 then
- y1 = 27
- elseif gemCount == 2 then
- y1 = 9
- y2 = 4
- end
-
- for i = 1, MAX_GEMS_PER_SLOT do
- self._optimizedGemText[i]:SetPoint("TOPLEFT", 200, 12 - (27 + y2) * i - y1)
- self._optimizedGemIcons[i]:SetPoint("TOPLEFT", 170, 18 - (27 + y2) * i - y1)
- self._currentGems[i]:SetPoint("TOPLEFT", 100, 18 - (27 + y2) * i - y1)
- end
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/RobotStamp.lua
--- a/ui/RobotStamp.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the RobotStamp class (inherit from Frame)
-AskMrRobot.RobotStamp = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
--- item icon contructor
-function AskMrRobot.RobotStamp:new(name, parent)
- -- create a new frame (if one isn't supplied)
- local o = AskMrRobot.Frame:new(name, parent)
-
- -- use the RobotStamp class
- setmetatable(o, { __index = AskMrRobot.RobotStamp })
-
- o:SetPoint("TOPLEFT", parent, "TOPLEFT")
- o:SetPoint("RIGHT", parent, "RIGHT")
- o:SetHeight(80);
-
- o.bigText = o:CreateFontString(nil, "ARTWORK", "GameFontNormalHuge")
- o.bigText:SetTextColor(7/255, 166/255, 11/255)
- local file, _, flags = o.bigText:GetFont()
- o.bigText:SetFont(file, 24, flags)
- o.bigText:SetText(L.AMR_ROBOTSTAMP_TEXT)
- o.bigText:SetPoint("TOPLEFT", o, "TOPLEFT", 15, -20)
- o.bigText:SetPoint("RIGHT", o, "RIGHT", -15, 0)
-
- o.smallText = o:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- o.smallText:SetText(AMR_ROBOTSTAMP_GEMS)
- o.smallText:SetPoint("TOPLEFT", o.bigText, "BOTTOMLEFT", 0, -7)
- o.smallText:SetPoint("RIGHT", o, "RIGHT", -15, 0)
- o.smallText:SetWidth(o.smallText:GetWidth())
- o.smallText:SetJustifyH("CENTER")
-
- o:SetBackdrop({edgeFile = "Interface\\AddOns\\AskMrRobot\\Media\\round-edge-big", edgeSize = 16})
- o:SetBackdropBorderColor(7/255, 166/255, 11/255)
-
- -- return the instance of the RobotStamp
- return o
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/ScrollFrame.lua
--- a/ui/ScrollFrame.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-local _, AskMrRobot = ...
-
--- initialize the ScrollFrame class (inherit from a dummy frame)
-AskMrRobot.ScrollFrame = AskMrRobot.inheritsFrom(CreateFrame("ScrollFrame"))
-
-function AskMrRobot.ScrollFrame:OnMouseWheel(value)
- local currentValue = self.scrollbar:GetValue()
- if value < 0 then
- currentValue = currentValue + 15
- else
- currentValue = currentValue - 15
- end
- self.scrollbar:SetValue(currentValue)
-end
-
-function AskMrRobot.ScrollFrame:RecalcScrollbar()
- self.scrollbar:SetMinMaxValues(0, self.content:GetHeight() * 100 / self:GetHeight())
-end
-
-function AskMrRobot.ScrollFrame:OnSizeChanged(width, height)
- self.content:SetWidth(width)
- self:RecalcScrollbar()
-end
-
--- ScrollFrame contructor
-function AskMrRobot.ScrollFrame:new(name, parentFrame)
- -- create a new frame (if one isn't supplied)
- local scrollframe = CreateFrame("ScrollFrame", name, parentFrame)
-
- -- use the ScrollFrame class
- setmetatable(scrollframe, { __index = AskMrRobot.ScrollFrame })
-
- scrollframe:EnableMouseWheel(true)
- scrollframe:SetScript("OnMouseWheel", AskMrRobot.ScrollFrame.OnMouseWheel)
- scrollframe:SetScript("OnSizeChanged", AskMrRobot.ScrollFrame.OnSizeChanged)
-
- local scrollbar = CreateFrame("Slider", nil, scrollframe, "UIPanelScrollBarTemplate" )
- scrollbar:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, -16)
- scrollbar:SetPoint("BOTTOMLEFT", scrollframe, "BOTTOMRIGHT", 4, 16)
- scrollbar:SetMinMaxValues(0, 100)
- scrollbar:SetValueStep(10)
- scrollbar.scrollStep = 10
- scrollbar:SetValue(0)
- scrollbar:SetWidth(16)
- scrollbar:SetScript("OnValueChanged",
- function (self, value)
- self:GetParent():SetVerticalScroll(value)
- end)
- scrollbar:Enable()
- scrollbar:SetOrientation("VERTICAL");
- scrollbar:Show()
- scrollframe.scrollbar = scrollbar
-
- --content frame
- local content = AskMrRobot.Frame:new(nil, scrollframe)
- scrollframe.content = content
-
- scrollframe:SetScrollChild(content)
-
- content:SetScript('OnSizeChanged', function(a, width, height)
- scrollframe:RecalcScrollbar()
- end)
-
- -- return the instance of the ScrollFrame
- return scrollframe
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/SettingsTab.lua
--- a/ui/SettingsTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the CombatLogTab class
-AskMrRobot.SettingsTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
--- helper to create text for this tab
-local function CreateText(tab, font, relativeTo, xOffset, yOffset, text)
- local t = tab:CreateFontString(nil, "ARTWORK", font)
- t:SetPoint("TOPLEFT", relativeTo, "BOTTOMLEFT", xOffset, yOffset)
- t:SetPoint("RIGHT", tab, "RIGHT", -5, 0)
- t:SetWidth(t:GetWidth())
- t:SetJustifyH("LEFT")
- t:SetText(text)
-
- return t
-end
-
-local function newCheckbox(tab, label, tooltipTitle, description, onClick)
- local check = CreateFrame("CheckButton", "AmrCheck" .. label, tab, "InterfaceOptionsCheckButtonTemplate")
- check:SetScript("OnClick", function(self)
- PlaySound(self:GetChecked() and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff")
- onClick(self, self:GetChecked() and true or false)
- end)
- check.label = _G[check:GetName() .. "Text"]
- check.label:SetText(label)
- check.tooltipText = tooltipTitle
- check.tooltipRequirement = description
- return check
-end
-
-function AskMrRobot.SettingsTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.SettingsTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
- -- tab header
- local text = tab:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetText(L.AMR_SETTINGSTAB_SETTINGS)
-
- --scrollframe
- tab.scrollframe = AskMrRobot.ScrollFrame:new(nil, tab)
- tab.scrollframe:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30)
- tab.scrollframe:SetPoint("BOTTOMRIGHT", tab, "BOTTOMRIGHT", -30, 10)
-
- local content = tab.scrollframe.content
- content:SetHeight(730)
-
- local autoPopup = newCheckbox(content,
- L.AMR_CONFIG_CHECKBOX_MINIMAP_LABEL,
- L.AMR_CONFIG_CHECKBOX_MINIMAP_TOOLTIP_TITLE,
- L.AMR_CONFIG_CHECKBOX_MINIMAP_DESCRIPTION,
- function(self, value)
- if AmrDb.Options.hideMapIcon then
- AmrDb.Options.hideMapIcon = false
- else
- AmrDb.Options.hideMapIcon = true
- end
- AskMrRobot.AmrUpdateMinimap();
- end
- )
- autoPopup:SetChecked(not AmrDb.Options.hideMapIcon)
- autoPopup:SetPoint("TOPLEFT", content, "TOPLEFT", 0, 0)
-
- local autoAh = newCheckbox(content,
- L.AMR_CONFIG_CHECKBOX_AUTOAH_LABEL,
- L.AMR_CONFIG_CHECKBOX_AUTOAH_TOOLTIP_TITLE,
- L.AMR_CONFIG_CHECKBOX_AUTOAH_DESCRIPTION,
- function(self, value)
- if AmrDb.Options.manualShowShop then
- AmrDb.Options.manualShowShop = false
- else
- AmrDb.Options.manualShowShop = true
- end
- end
- )
- autoAh:SetChecked(not AmrDb.Options.manualShowShop)
- autoAh:SetPoint("TOPLEFT", autoPopup, "BOTTOMLEFT", 0, -10)
-
- return tab
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/ShoppingListTab.lua
--- a/ui/ShoppingListTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,950 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
--- initialize the ShoppingListTab class
-AskMrRobot.ShoppingListTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
-StaticPopupDialogs["SHOPPING_TAB_PLEASE_OPEN"] = {
- text = L.AMR_SHOPPINGLISTTAB_OPEN_MAIL,
- button1 = L.AMR_SHOPPINGLISTTAB_BUTTON_OK,
- timeout = 0,
- whileDead = true,
- hideOnEscape = true,
- preferredIndex = 3, -- avoid some UI taint, see http://www.wowace.com/announcements/how-to-avoid-some-ui-taint/
-}
-
-function AskMrRobot.ShoppingListTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.ShoppingListTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
- tab:RegisterEvent("AUCTION_HOUSE_CLOSED")
- tab:RegisterEvent("AUCTION_HOUSE_SHOW")
- tab:RegisterEvent("MAIL_SHOW")
- tab:RegisterEvent("MAIL_CLOSED")
- tab:RegisterEvent("GET_ITEM_INFO_RECEIVED")
-
- tab.isAuctionHouseVisible = false
-
- tab:SetScript("OnEvent", function(...)
- tab:OnEvent(...)
- end)
-
- tab.shoppingListHeader = AskMrRobot.FontString:new(tab, nil, "ARTWORK", "GameFontNormalLarge")
- tab.shoppingListHeader:SetPoint("TOPLEFT", 0, -5)
- tab.shoppingListHeader:SetText(L.AMR_SHOPPINGLISTTAB_TITLE)
-
- tab.shoppingPanel = AskMrRobot.Frame:new(nil, tab)
- tab.shoppingPanel:SetPoint("TOPLEFT", tab.shoppingListHeader, "BOTTOMLEFT", 0, -10)
- tab.shoppingPanel:SetPoint("BOTTOMRIGHT", tab, "BOTTOMRIGHT", -20, 17)
-
-
- tab.sendButton = CreateFrame("Button", "AmrSendButton", tab.shoppingPanel, "UIPanelButtonTemplate")
- tab.sendButton:SetText(L.AMR_SHOPPINGLISTTAB_BUTTON_SEND)
- tab.sendButton:SetPoint("BOTTOMLEFT", 0, 0)
- tab.sendButton:SetHeight(25)
- tab.sendButton:SetNormalFontObject("GameFontNormalLarge")
- tab.sendButton:SetHighlightFontObject("GameFontHighlightLarge")
- tab.sendButton:SetWidth(150)
- tab.sendButton:SetScript("OnClick", function()
- tab:Send()
- end)
-
- tab.enchantMaterialsCheckbox = CreateFrame("CheckButton", "AmrEnchantMaterialsCheckbox", tab.shoppingPanel, "ChatConfigCheckButtonTemplate");
- tab.enchantMaterialsCheckbox:SetChecked(AmrDb.SendSettings.SendEnchantMaterials)
- tab.enchantMaterialsCheckbox:SetScript("OnClick", function () AmrDb.SendSettings.SendEnchantMaterials = tab.enchantMaterialsCheckbox:GetChecked() end)
- tab.enchantMaterialsCheckbox:SetPoint("TOPLEFT", tab.sendButton, "TOPLEFT", 0, 25)
- local text3 = getglobal(tab.enchantMaterialsCheckbox:GetName() .. 'Text')
- text3:SetFontObject("GameFontHighlightLarge")
- text3:SetText(L.AMR_SHOPPINGLISTTAB_ENCHANT_MATERIALS)
- text3:SetPoint("RIGHT", tab, "RIGHT", -20, 0)
- text3:SetPoint("TOPLEFT", tab.enchantMaterialsCheckbox, "TOPRIGHT", 2, -4)
-
-
- tab.enchantsCheckbox = CreateFrame("CheckButton", "AmrEnchantsCheckbox", tab.shoppingPanel, "ChatConfigCheckButtonTemplate");
- tab.enchantsCheckbox:SetChecked(AmrDb.SendSettings.SendEnchants)
- tab.enchantsCheckbox:SetScript("OnClick", function () AmrDb.SendSettings.SendEnchants = tab.enchantsCheckbox:GetChecked() end)
- tab.enchantsCheckbox:SetPoint("TOPLEFT", tab.sendButton, "TOPLEFT", 0, 50)
- local text2 = getglobal(tab.enchantsCheckbox:GetName() .. 'Text')
- text2:SetFontObject("GameFontHighlightLarge")
- text2:SetText(L.AMR_SHOPPINGLISTTAB_ENCHANTS)
- text2:SetPoint("RIGHT", tab, "RIGHT", -20, 0)
- text2:SetPoint("TOPLEFT", tab.enchantsCheckbox, "TOPRIGHT", 2, -4)
-
-
-
- tab.gemsCheckbox = CreateFrame("CheckButton", "AmrGemsCheckbox", tab.shoppingPanel, "ChatConfigCheckButtonTemplate");
- tab.gemsCheckbox:SetPoint("TOPLEFT", tab.sendButton, "TOPLEFT", 0, 75)
- tab.gemsCheckbox:SetChecked(AmrDb.SendSettings.SendGems)
- tab.gemsCheckbox:SetScript("OnClick", function () AmrDb.SendSettings.SendGems = tab.gemsCheckbox:GetChecked() end)
- local text = getglobal(tab.gemsCheckbox:GetName() .. 'Text')
- text:SetFontObject("GameFontHighlightLarge")
- text:SetText(L.AMR_SHOPPINGLISTTAB_GEMS)
- text:SetPoint("RIGHT", tab, "RIGHT", -20, 0)
- text:SetPoint("TOPLEFT", tab.gemsCheckbox, "TOPRIGHT", 2, -4)
-
-
- tab.sendMessage4 = AskMrRobot.FontString:new(tab.shoppingPanel, nil, "ARTWORK", "GameFontHighlightLarge")
- tab.sendMessage4:SetText(L.AMR_SHOPPINGLISTTAB_INCLUDE)
- tab.sendMessage4:SetPoint("TOPLEFT", tab.gemsCheckbox, "TOPLEFT", 0, 20)
-
-
- tab.sendMessage3 = AskMrRobot.FontString:new(tab.shoppingPanel, nil, "ARTWORK", "GameFontHighlightLarge")
- tab.sendMessage3:SetText(L.AMR_SHOPPINGLISTTAB_SEND_LIST_TO)
- tab.sendMessage3:SetPoint("TOPLEFT", tab.sendMessage4, "TOPLEFT", 0, 25)
-
-
- tab.sendMessage2 = AskMrRobot.FontString:new(tab.shoppingPanel, nil, "ARTWORK", "GameFontNormal")
- tab.sendMessage2:SetTextColor(.5,.5,.5)
- tab.sendMessage2:SetText(L.AMR_SHOPPINGLISTTAB_WHISPER_CHANNEL)
- tab.sendMessage2:SetPoint("TOPLEFT", tab.sendMessage3, "TOPLEFT", 0, 25)
-
-
- tab.sendMessage1 = AskMrRobot.FontString:new(tab.shoppingPanel, nil, "ARTWORK", "GameFontNormalLarge")
- tab.sendMessage1:SetTextColor(0,1,0)
- tab.sendMessage1:SetText(L.AMR_SHOPPINGLISTTAB_SEND_JEWELCRAFT_ENCHANTER)
- tab.sendMessage1:SetPoint("TOPLEFT", tab.sendMessage2, "TOPLEFT", 0, 25)
-
-
- tab.scrollFrame = CreateFrame("ScrollFrame", "AmrScrollFrame", tab.shoppingPanel, "UIPanelScrollFrameTemplate")
- tab.scrollFrame:SetPoint("TOPLEFT", 0, 0)
- tab.scrollFrame:SetPoint("RIGHT", -20, 0)
- tab.scrollFrame:SetPoint("BOTTOM", tab.sendMessage1, "TOP", 0, 10)
-
- tab.scrollParent = AskMrRobot.Frame:new(nil, tab.shoppingPanel)
- tab.scrollParent:SetPoint("TOPLEFT", 0, 0)
- tab.scrollParent:SetWidth(tab:GetWidth() - 20)
- tab.scrollParent:SetHeight(500)
- tab.scrollFrame:SetScrollChild(tab.scrollParent)
-
- -- magic to get the scrollbar to work with the scrollwheel...
- tab.scrollFrame:SetScript("OnMouseWheel", function(arg1, arg2)
- ScrollFrameTemplate_OnMouseWheel(arg1, arg2, arg1.ScrollBar)
- end)
-
- tab.gemsHeader = AskMrRobot.FontString:new(tab.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- tab.gemsHeader:SetText(L.AMR_SHOPPINGLISTTAB_GEMS)
- tab.gemsHeader:SetPoint("TOPLEFT", tab.scrollParent, "TOPLEFT", 0, 0)
-
- tab.gemsQuantityHeader = AskMrRobot.FontString:new(tab.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- tab.gemsQuantityHeader:SetText(L.AMR_SHOPPINGLISTTAB_TOTAL)
- tab.gemsQuantityHeader:SetPoint("TOPLEFT", tab.scrollParent, "TOPLEFT", 370, 0)
-
- tab.enchantsHeader = AskMrRobot.FontString:new(tab.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- tab.enchantsHeader:SetText(L.AMR_SHOPPINGLISTTAB_ENCHANTS)
-
- tab.enchantsQuantityHeader = AskMrRobot.FontString:new(tab.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- tab.enchantsQuantityHeader:SetText(L.AMR_SHOPPINGLISTTAB_TOTAL)
- tab.enchantsQuantityHeader:SetPoint("TOPLEFT", tab.enchantsHeader, "TOPLEFT", 370, 0)
-
- tab.enchantMaterialsHeader = AskMrRobot.FontString:new(tab.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- tab.enchantMaterialsHeader:SetText(L.AMR_SHOPPINGLISTTAB_ENCHANT_MATERIALS)
-
- tab.enchantMaterialsQuantityHeader = AskMrRobot.FontString:new(tab.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- tab.enchantMaterialsQuantityHeader:SetText(L.AMR_SHOPPINGLISTTAB_TOTAL)
- tab.enchantMaterialsQuantityHeader:SetPoint("TOPLEFT", tab.enchantMaterialsHeader, "TOPLEFT", 370, 0)
-
- tab.stamp = AskMrRobot.RobotStamp:new(nil, tab)
- tab.stamp:Hide()
- tab.stamp.bigText:SetText(L.AMR_SHOPPINGLISTTAB_DONE)
- tab.stamp.smallText:SetText(L.AMR_SHOPPINGLISTTAB_A_ROBOTS_WISHLIST)
- tab.stamp:SetPoint("TOPLEFT", tab.shoppingListHeader, "BOTTOMLEFT", 2, -15)
- tab.stamp:SetPoint("RIGHT", tab, "RIGHT", -30, 0)
- tab.stamp:SetHeight(92)
-
- tab.gemIcons = {}
- tab.gemLinks = {}
- tab.gemQuantity = {}
- tab.enchantIcons = {}
- tab.enchantLinks = {}
- tab.enchantQuantity = {}
- tab.enchantMaterialIcons = {}
- tab.enchantMaterialLinks = {}
- tab.enchantMaterialQuantity = {}
-
- -- Create the dropdown, and configure its appearance
- tab.dropDown = CreateFrame("FRAME", "AmrSendType", tab.shoppingPanel, "UIDropDownMenuTemplate")
- tab.dropDown:SetPoint("TOPLEFT", tab.sendMessage3, "TOPRIGHT", 0, 5)
- UIDropDownMenu_SetWidth(tab.dropDown, 140)
- UIDropDownMenu_SetText(tab.dropDown, AmrDb.SendSettings.SendToType)
-
- local text = getglobal(tab.dropDown:GetName() .. 'Text')
- text:SetFontObject("GameFontHighlightLarge")
-
- local AddButton = function(list, optionText)
- local info = UIDropDownMenu_CreateInfo()
- info.justifyH = "RIGHT"
- info.text = optionText
- info.checked = AmrDb.SendSettings.SendToType == optionText
- info.arg1 = optionText
- info.func = list.SetValue
- info.owner = list
- info.fontObject = "GameFontHighlightLarge"
- info.minWidth = 140
- return info
- end
-
- -- Create and bind the initialization function to the dropdown menu
- UIDropDownMenu_Initialize(tab.dropDown, function(self, level, menuList)
- UIDropDownMenu_AddButton(AddButton(self, L.AMR_SHOPPINGLISTTAB_DROPDOWN_FRIEND))
- UIDropDownMenu_AddButton(AddButton(self, L.AMR_SHOPPINGLISTTAB_DROPDOWN_PARTY))
- UIDropDownMenu_AddButton(AddButton(self, L.AMR_SHOPPINGLISTTAB_DROPDOWN_RAID))
- UIDropDownMenu_AddButton(AddButton(self, L.AMR_SHOPPINGLISTTAB_DROPDOWN_GUILD))
- UIDropDownMenu_AddButton(AddButton(self, L.AMR_SHOPPINGLISTTAB_DROPDOWN_CHANNEL))
- UIDropDownMenu_AddButton(AddButton(self, L.AMR_SHOPPINGLISTTAB_DROPDOWN_MAIL))
- end)
-
- function tab.dropDown:SetValue(newValue)
- AmrDb.SendSettings.SendToType = newValue
- -- Update the text; if we merely wanted it to display newValue, we would not need to do this
- UIDropDownMenu_SetText(tab.dropDown, AmrDb.SendSettings.SendToType)
- -- Because this is called from a sub-menu, only that menu level is closed by default.
- -- Close the entire menu with this next call
- CloseDropDownMenus()
- end
-
- tab.sendTo = CreateFrame("EditBox", "AmrSendTo", tab.shoppingPanel, "InputBoxTemplate" )
- tab.sendTo:SetPoint("TOPLEFT", tab.dropDown, "TOPRIGHT", 0, 0)
- tab.sendTo:SetPoint("RIGHT", 0, 0)
- tab.sendTo:SetHeight(30)
- tab.sendTo:SetText(AmrDb.SendSettings.SendTo or "")
- tab.sendTo:SetFontObject("GameFontHighlightLarge")
- tab.sendTo:SetAutoFocus(false)
- tab.sendTo:SetScript("OnChar", function()
- AmrDb.SendSettings.SendTo = tab.sendTo:GetText()
- end)
-
- tab.messageQueue = {}
-
- tab.itemNames = {}
- return tab
-end
-
--- display a gem icon in a row
--- gemInfo is {id, enchantId, color, count }
-function AskMrRobot.ShoppingListTab:SetGemIcon(row, gemInfo)
- -- get gem icon for the row
- local gemIcon = self.gemIcons[row]
-
- -- if we don't have one
- if gemIcon == nil then
- -- make one
- gemIcon = AskMrRobot.GemIcon:new(nil, self.scrollParent)
- self.gemIcons[row] = gemIcon
- gemIcon:SetScript("OnMouseDown", function()
- self:SearchForGem(row)
- end)
-
- -- position it
- local previous = self.gemsHeader
- if row > 1 then
- previous = self.gemIcons[row - 1]
- end
- gemIcon:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 0, -7)
-
- -- size it
- gemIcon:SetWidth(24)
- gemIcon:SetHeight(24)
-
- -- give it a nice border
- gemIcon:SetRoundBorder()
- end
-
- gemIcon:Show()
-
- -- make a link for the optimized gem
- gemLink = select(2, GetItemInfo(gemInfo.id))
-
- -- set the link (tooltip + icon)
- gemIcon:SetItemLink(gemLink)
- --gemIcon:SetGemColor(gemInfo.color)
- gemIcon:SetGemColor('Prismatic')
-
- -- if we didn't get one, its because WoW is slow
- if not gemLink and gemInfo.id then
- -- when WoW finally returns the link, set the icon / tooltip
- AskMrRobot.RegisterItemInfoCallback(gemInfo.id, function(name, link)
- gemIcon:SetItemLink(link)
- end)
- end
-
-end
-
-
--- display a gem icon in a row
--- gemInfo is {id, enchantId, color, count }
-function AskMrRobot.ShoppingListTab:SetGemText(row, gemInfo)
- -- get gem icon for the row
- local gemText = self.gemLinks[row]
-
- -- if we don't have one
- if gemText == nil then
- -- make one
- gemText = AskMrRobot.ItemLinkText:new(nil, self.scrollParent)
- self.gemLinks[row] = gemText
- gemText:SetScript("OnMouseDown", function()
- self:SearchForGem(row)
- end)
-
- -- position it
- local previous = self.gemsHeader
- if row > 1 then
- previous = self.gemIcons[row - 1]
- end
- gemText:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 30, -8)
- gemText:SetPoint("RIGHT", self, "RIGHT", -70, 0)
- gemText:SetHeight(18)
- gemText:SetFontSize(15)
- end
-
- gemText:Show()
-
- gemText:SetItemId(gemInfo.id)
-end
-
--- display a gem icon in a row
--- gemInfo is {id, enchantId, color, count }
-function AskMrRobot.ShoppingListTab:SetGemQuantity(row, qty, total)
- if qty > total then qty = total end
-
- -- get gem icon for the row
- local gemText = self.gemQuantity[row]
-
- -- if we don't have one
- if gemText == nil then
- -- make one
- gemText = AskMrRobot.FontString:new(self.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- self.gemQuantity[row] = gemText
-
- -- position it
- local previous = self.gemsHeader
- if row > 1 then
- previous = self.gemIcons[row - 1]
- end
- gemText:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 370, -8)
- gemText:SetHeight(18)
- gemText:SetFontSize(15)
- end
-
- gemText:SetText('' .. qty .. '/' .. total)
- if qty == total then
- gemText:SetTextColor(0,1,0)
- else
- gemText:SetTextColor(1,0,0)
- end
- gemText:Show()
-end
-
-
--- display an enchant icon in a row
-function AskMrRobot.ShoppingListTab:SetEnchantIcon(row, enchantId)
-
- -- get enchant icon for the row
- local enchantIcon = self.enchantIcons[row]
-
- -- if we don't have one
- if enchantIcon == nil then
- -- make one
- enchantIcon = AskMrRobot.EnchantLinkIconAndText:new(nil, self.scrollParent)
- self.enchantIcons[row] = enchantIcon
- enchantIcon:SetScript("OnMouseDown", function()
- self:SearchForEnchant(row)
- end)
-
- -- position it
- if row == 1 then
- enchantIcon:SetPoint("TOPLEFT", self.enchantsHeader, "BOTTOMLEFT", 0, -12)
- enchantIcon:SetPoint("RIGHT", self.scrollParent, "RIGHT", -30, 0)
- else
- enchantIcon:SetPoint("TOPLEFT", self.enchantIcons[row - 1], "BOTTOMLEFT", 0, -7)
- enchantIcon:SetPoint("RIGHT", self.scrollParent, "RIGHT", -30, 0)
- end
-
- -- size it
- enchantIcon:SetWidth(24)
- enchantIcon:SetHeight(24)
- enchantIcon:SetFontSize(15)
-
- -- give it a nice border
- enchantIcon:SetRoundBorder()
-
- enchantIcon:UseSpellName()
- end
-
- enchantIcon:SetEnchantId(enchantId)
-
- enchantIcon:Show()
-end
-
--- display a gem icon in a row
--- gemInfo is {id, enchantId, color, count }
-function AskMrRobot.ShoppingListTab:SetEnchantQuantity(row, qty, total)
- if qty > total then qty = total end
-
- -- get gem icon for the row
- local enchantText = self.enchantQuantity[row]
-
- -- if we don't have one
- if enchantText == nil then
- -- make one
- enchantText = AskMrRobot.FontString:new(self.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- self.enchantQuantity[row] = enchantText
-
- -- position it
- local previous = self.enchantsHeader
- if row > 1 then
- previous = self.enchantIcons[row - 1]
- end
- enchantText:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 370, -8)
- enchantText:SetHeight(18)
- enchantText:SetFontSize(15)
- end
-
- enchantText:SetText('' .. qty .. '/' .. total)
- if qty == total then
- enchantText:SetTextColor(0,1,0)
- else
- enchantText:SetTextColor(1,0,0)
- end
- enchantText:Show()
-end
-
-function AskMrRobot.ShoppingListTab:SearchForItem(itemName)
- if self.isAuctionHouseVisible then
- QueryAuctionItems(itemName, nil, nil, 0, 0, 0, 0, 0, 0, 0)
- end
-end
-
-function AskMrRobot.ShoppingListTab:SearchForGem(row)
- self:SearchForItem(self.gemLinks[row].itemName)
-end
-
-function AskMrRobot.ShoppingListTab:SearchForEnchant(row)
- self:SearchForItem(self.enchantIcons[row].itemName)
-end
-
-function AskMrRobot.ShoppingListTab:SearchForEnchantMaterial(row)
- self:SearchForItem(self.enchantMaterialLinks[row].itemName)
-end
-
-
--- display an enchant material icon in a row
-function AskMrRobot.ShoppingListTab:SetEnchantMaterialIcon(row, itemId)
- -- get enchant material icon for the row
- local materialIcon = self.enchantMaterialIcons[row]
-
- -- if we don't have one
- if materialIcon == nil then
- -- make one
- materialIcon = AskMrRobot.ItemIcon:new(nil, self.scrollParent)
- self.enchantMaterialIcons[row] = materialIcon
- materialIcon:SetScript("OnMouseDown", function()
- self:SearchForEnchantMaterial(row)
- end)
-
- -- position it
- local previous = self.enchantMaterialsHeader
- if row > 1 then
- previous = self.enchantMaterialIcons[row - 1]
- end
- materialIcon:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 0, -7)
-
- -- size it
- materialIcon:SetWidth(24)
- materialIcon:SetHeight(24)
-
- -- give it a nice border
- materialIcon:SetRoundBorder()
- end
-
- materialIcon:Show()
-
- -- make a link for the optimized gem
- local itemLink = select(2, GetItemInfo(itemId))
-
- materialIcon:SetItemLink(itemLink)
-
- -- if we didn't get one, its because WoW is slow
- if not itemLink and itemId then
- -- when WoW finally returns the link, set the icon / tooltip
- AskMrRobot.RegisterItemInfoCallback(itemId, function(name, link)
- materialIcon:SetItemLink(link)
- end)
- end
-end
-
-
--- display an enchant material link in a row
-function AskMrRobot.ShoppingListTab:SetEnchantMaterialLink(row, itemId)
- -- get gem icon for the row
- local materialLink = self.enchantMaterialLinks[row]
-
- -- if we don't have one
- if materialLink == nil then
- -- make one
- materialLink = AskMrRobot.ItemLinkText:new(nil, self.scrollParent)
- self.enchantMaterialLinks[row] = materialLink
- materialLink:SetScript("OnMouseDown", function()
- self:SearchForEnchantMaterial(row)
- end)
-
- -- position it
- local previous = self.enchantMaterialsHeader
- if row > 1 then
- previous = self.enchantMaterialIcons[row - 1]
- end
- materialLink:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 30, -8)
- materialLink:SetPoint("RIGHT", self, "RIGHT", -30, 0)
- materialLink:SetHeight(18)
- materialLink:SetFontSize(15)
- end
-
- materialLink:Show()
-
- materialLink:SetItemId(itemId)
- materialLink.itemId = itemId
-end
-
--- display a gem icon in a row
--- gemInfo is {id, enchantId, color, count }
-function AskMrRobot.ShoppingListTab:SetEnchantMaterialQuantity(row, qty, total)
- if qty > total then qty = total end
-
- -- get gem icon for the row
- local enchantText = self.enchantMaterialQuantity[row]
-
- -- if we don't have one
- if enchantText == nil then
- -- make one
- enchantText = AskMrRobot.FontString:new(self.scrollParent, nil, "ARTWORK", "GameFontNormalLarge")
- self.enchantMaterialQuantity[row] = enchantText
-
- -- position it
- local previous = self.enchantMaterialsHeader
- if row > 1 then
- previous = self.enchantMaterialIcons[row - 1]
- end
- enchantText:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 370, -8)
- enchantText:SetHeight(18)
- enchantText:SetFontSize(15)
- end
-
- enchantText:SetText('' .. qty .. '/' .. total)
- if qty == total then
- enchantText:SetTextColor(0,1,0)
- else
- enchantText:SetTextColor(1,0,0)
- end
- enchantText:Show()
-end
-
-function AskMrRobot.ShoppingListTab:HasStuffToBuy()
-
- local gemList, enchantList, enchantMaterials = self:CalculateItems()
-
- local count = 0
- for gemId, gemInfo in AskMrRobot.spairs(gemList) do
- count = count + gemInfo.total - gemInfo.count
- end
- for slot, enchant in AskMrRobot.spairs(enchantList) do
- count = count + enchant.total - enchant.count
- end
-
- return count > 0
-end
-
-function AskMrRobot.ShoppingListTab:CalculateItems()
- -- build a map of missing gem-enchant-ids -> {id, enchantid, count, total}
- local gemList = {}
-
- -- for each piece of gear that needs at least 1 gem changed
- for _, badGems in pairs(AskMrRobot.ComparisonResult.gems) do
- -- for each specified gem
- for g = 1, #badGems.optimized do
- local goodGemEnchantId = badGems.optimized[g]
- -- if AMR says to optimized this gem AND it does *NOT* match matches the current gem
- if goodGemEnchantId ~= 0 and not AskMrRobot.AreGemsCompatible(goodGemEnchantId, badGems.current[g]) then
- -- see if this gem is in our list of gems to optimize
- local gem = gemList[goodGemEnchantId]
- if gem == nil then
- -- if not, add it
- gemList[goodGemEnchantId] = {id = AskMrRobot.ExtraGemData[goodGemEnchantId].id, enchantId = goodGemEnchantId, count = 0, total = 1, compatibleGemIds = AskMrRobot.ExtraGemData[goodGemEnchantId].identicalItemGroup}
- else
- -- if so, increase the total requested for this
- gem.total = gem.total + 1
- end
- end
- end
- end
-
- local enchantList = {}
- for slot, enchantData in AskMrRobot.sortSlots(AskMrRobot.ComparisonResult.enchants) do
- local extraData = AskMrRobot.ExtraEnchantData[enchantData.optimized]
- local id = extraData and extraData.itemId or enchantData.optimized
- local qty = enchantList[id]
- if qty then
- qty.total = qty.total + 1
- else
- qty = { count = 0, total = 1, optimized = enchantData.optimized, itemId = extraData and extraData.itemId }
- enchantList[id] = qty
- end
- end
-
- local enchantMaterials = {}
- for slot, enchantData in pairs(AskMrRobot.ComparisonResult.enchants) do
- local extraData = AskMrRobot.ExtraEnchantData[enchantData.optimized]
- if extraData and extraData.materials then
- local itemId
- local count
- for itemId, count in pairs(extraData.materials) do
- if enchantMaterials[itemId] then
- enchantMaterials[itemId].total = enchantMaterials[itemId].total + count
- else
- enchantMaterials[itemId] = { count = 0, total = count }
- end
- end
- end
-
- end
-
- local bagItemsWithCounts = {}
- -- copy the bank items into a new table so we don't alter them
- if (AmrDb.BankItemsAndCounts) then
- for id, count in pairs(AmrDb.BankItemsAndCounts) do
- bagItemsWithCounts[id] = count
- end
- end
-
- -- add the items from the players bags
- AskMrRobot.ScanBags(bagItemsWithCounts)
-
- -- note: void storage can't hold stackable items, so don't worry about them
-
- --substract any inventory we already have in bags/bank
- for itemId, count in pairs(bagItemsWithCounts) do
- for _, gem in pairs(gemList) do
- if gem.compatibleGemIds[itemId] and gem.count < gem.total then
- local needed = gem.total - gem.count
- if count > needed then
- gem.count = gem.total
- -- only consume the number needed (subtract in case this is compatible with a different gem)
- count = count - needed
- else
- gem.count = gem.count + count
- count = 0
- end
- end
- end
- local material = enchantMaterials[itemId]
- if material then
- material.count = material.count + count
- end
- local enchant = enchantList[itemId]
- if enchant then
- enchant.count = enchant.count + count
- end
- end
-
- return gemList, enchantList, enchantMaterials
-end
-
-function AskMrRobot.ShoppingListTab:Update()
-
- local gemList, enchantList, enchantMaterials = self:CalculateItems()
-
- -- update the UI
- local lastControl = nil
- local row = 1
- for gemId, gemInfo in AskMrRobot.spairs(gemList) do
- self:SetGemIcon(row, gemInfo)
- self:SetGemText(row, gemInfo)
- self:SetGemQuantity(row, gemInfo.count, gemInfo.total)
- lastControl = self.gemIcons[row]
- row = row + 1
- end
-
- -- hide any extra gem icons
- for i = row, #self.gemIcons do
- self.gemIcons[i]:Hide()
- self.gemLinks[i]:Hide()
- self.gemQuantity[i]:Hide()
- end
-
- -- hide / show the gems header, and position the enchant headers
- if row > 1 then
- self.gemsHeader:Show()
- self.gemsQuantityHeader:Show()
- self.enchantsHeader:SetPoint("TOPLEFT", self.gemIcons[row - 1], "BOTTOMLEFT", 0, -15)
- else
- self.gemsHeader:Hide()
- self.gemsQuantityHeader:Hide()
- self.enchantsHeader:SetPoint("TOPLEFT", self.scrollParent, "TOPLEFT", 0, 0)
- end
-
- self.enchantNames = {}
- row = 1
- for slot, enchant in AskMrRobot.spairs(enchantList) do
- self:SetEnchantIcon(row, enchant.optimized)
- local row2 = row
- self.enchantIcons[row2].itemName = nil
- if not enchant.itemId then
- self.enchantIcons[row2].itemText:SetText("unknown")
- else
- self:GetItemName(enchant.itemId, function(name)
- self.enchantIcons[row2].itemName = name
- self.enchantIcons[row2].itemText:SetText(name)
- end)
- end
- self:SetEnchantQuantity(row, enchant.count, enchant.total)
- lastControl = self.enchantIcons[row]
- row = row + 1
- end
-
- -- hide any extra enchant icons
- for i = row, #self.enchantIcons do
- self.enchantIcons[i]:Hide()
- self.enchantQuantity[i]:Hide()
- end
-
- -- hide / show the enchants header, and position the enchant materials headers
- if row > 1 then
- self.enchantsHeader:Show()
- self.enchantsQuantityHeader:Show()
- self.enchantMaterialsHeader:SetPoint("TOPLEFT", self.enchantIcons[row - 1], "BOTTOMLEFT", 0, -15)
- else
- self.enchantsHeader:Hide()
- self.enchantsQuantityHeader:Hide()
- self.enchantMaterialsHeader:SetPoint("TOPLEFT", self.scrollParent, "TOPLEFT", 0, 0)
- end
-
- row = 1
- for itemId, count in AskMrRobot.spairs(enchantMaterials) do
- self:SetEnchantMaterialIcon(row, itemId)
- self:SetEnchantMaterialLink(row, itemId)
- self:SetEnchantMaterialQuantity(row, count.count, count.total)
- lastControl = self.enchantMaterialIcons[row]
- row = row + 1
- end
-
- for i = row, #self.enchantMaterialIcons do
- self.enchantMaterialIcons[i]:Hide()
- self.enchantMaterialLinks[i]:Hide()
- self.enchantMaterialQuantity[i]:Hide()
- end
-
- if row == 1 then
- self.enchantMaterialsHeader:Hide()
- self.enchantMaterialsQuantityHeader:Hide()
- else
- self.enchantMaterialsHeader:Show()
- self.enchantMaterialsQuantityHeader:Show()
- end
-
- -- fix up the scrollbar length
- if lastControl then
- local height = self.scrollParent:GetTop() - lastControl:GetBottom()
- self.scrollParent:SetHeight(height)
- if height < self.scrollFrame:GetHeight() then
- self.scrollFrame.ScrollBar:Hide()
- else
- self.scrollFrame:Show()
- self.scrollFrame.ScrollBar:Show()
- end
- self.stamp:Hide()
- self.shoppingPanel:Show()
- else
- self.scrollFrame.ScrollBar:Hide()
- self.shoppingPanel:Hide()
- self.stamp:Show()
- end
-end
-
-function AskMrRobot.ShoppingListTab:GetItemName(itemId, func)
- local name = GetItemInfo(itemId)
- if name then
- func(name)
- else
- tinsert(self.itemNames, { itemId = itemId, func = func })
- end
-end
-
-function AskMrRobot.ShoppingListTab:On_GET_ITEM_INFO_RECEIVED()
- for i = #self.itemNames, 1, -1 do
- local name = GetItemInfo(self.itemNames[i].itemId)
- if name then
- self.itemNames[i].func(name)
- tremove(self.itemNames, i)
- end
- end
-end
-
-function AskMrRobot.ShoppingListTab:OnEvent(frame, event, ...)
- local handler = self["On_" .. event]
- if handler then
- handler(self, ...)
- end
-end
-
-function AskMrRobot.ShoppingListTab:On_MAIL_SHOW()
- self.mailOpen = true
-end
-
-function AskMrRobot.ShoppingListTab:On_MAIL_CLOSED()
- self.mailOpen = nil
-end
-
-function AskMrRobot.ShoppingListTab:On_AUCTION_HOUSE_SHOW()
- self.isAuctionHouseVisible = true
-end
-
-function AskMrRobot.ShoppingListTab:On_AUCTION_HOUSE_CLOSED()
- self.isAuctionHouseVisible = false
-end
-
-function AskMrRobot.ShoppingListTab:sendMail()
-
- -- need mail window to be open for this to work
- if not self.mailOpen then
- StaticPopup_Show("SHOPPING_TAB_PLEASE_OPEN")
- return
- end
-
- local message = L.AMR_SHOPPINGLISTTAB_MAIL_ROBOT_MESSAGE
-
- local gemList, enchantList, enchantMaterials = self:CalculateItems()
-
- if AmrDb.SendSettings.SendGems then
- for k,v in pairs(gemList) do
- --exclude jewelcrafter gems
- --if not AskMrRobot.JewelcrafterGems[k] then
- local needed = v.total - v.count
- if needed > 0 then
- local itemName = GetItemInfo(v.id)
- if itemName then
- message = message .. "\n" .. needed .. "x " .. itemName
- end
- end
- --end
- end
- end
-
- if AmrDb.SendSettings.SendEnchants then
- for k,v in pairs(enchantList) do
- local needed = v.total - v.count
- if needed > 0 then
- local itemName = GetItemInfo(k)
- if itemName then
- message = message .. "\n" .. needed .. "x " .. itemName
- end
- end
- end
- end
-
- if AmrDb.SendSettings.SendEnchantMaterials then
- for k,v in pairs(enchantMaterials) do
- local needed = v.total - v.count
- if needed > 0 then
- local itemName = GetItemInfo(k)
- if itemName then
- message = message .. "\n" .. needed .. "x " .. itemName
- end
- end
- end
- end
-
- MailFrameTab_OnClick(nil, 2)
- if AmrDb.SendSettings.SendGems then
- if AmrDb.SendSettings.SendEnchants then
- SendMailSubjectEditBox:SetText(L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_GE)
- else
- SendMailSubjectEditBox:SetText(L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_G)
- end
- else
- SendMailSubjectEditBox:SetText(L.AMR_SHOPPINGLISTTAB_MAIL_SUBJECT_E)
- end
- SendMailNameEditBox:SetText(AmrDb.SendSettings.SendTo)
- SendMailBodyEditBox:SetText(message)
-end
-
-function AskMrRobot.ShoppingListTab:Send()
- local chatType = nil
- if AmrDb.SendSettings.SendToType == L.AMR_SHOPPINGLISTTAB_DROPDOWN_PARTY then
- chatType = "PARTY"
- elseif AmrDb.SendSettings.SendToType == L.AMR_SHOPPINGLISTTAB_DROPDOWN_GUILD then
- chatType = "GUILD"
- elseif AmrDb.SendSettings.SendToType == L.AMR_SHOPPINGLISTTAB_DROPDOWN_RAID then
- chatType = "RAID"
- elseif AmrDb.SendSettings.SendToType == L.AMR_SHOPPINGLISTTAB_DROPDOWN_CHANNEL then
- chatType = "CHANNEL"
- elseif AmrDb.SendSettings.SendToType == L.AMR_SHOPPINGLISTTAB_DROPDOWN_MAIL then
- self:sendMail()
- return
- else
- chatType = "WHISPER"
- end
-
- local message = L.AMR_SHOPPINGLISTTAB_CHAT_ROBOT_MESSAGE
- local count = 0
-
-
- local gemList, enchantList, enchantMaterials = self:CalculateItems()
-
- local items = {}
- if AmrDb.SendSettings.SendGems then
- for k,v in pairs(gemList) do
- --if not AskMrRobot.JewelcrafterGems[k] then
- local needed = v.total - v.count
- if needed > 0 then
- tinsert(items, {id = v.id, needed = needed})
- end
- --end
- end
- end
-
- if AmrDb.SendSettings.SendEnchants then
- for k,v in pairs(enchantList) do
- local needed = v.total - v.count
- if needed > 0 then
- tinsert(items, {id = k, needed = needed})
- end
- end
- end
-
- if AmrDb.SendSettings.SendEnchantMaterials then
- for k,v in pairs(enchantMaterials) do
- local needed = v.total - v.count
- if needed > 0 then
- tinsert(items, {id = k, needed = needed})
- end
- end
- end
-
- for i, entry in ipairs(items) do
- local _, link = GetItemInfo(entry.id)
- if link then
- message = message .. " " .. entry.needed .. "x " .. link
- count = count + 1
- if count == 2 then
- tinsert(self.messageQueue, {message = message, chatType = chatType, chatChannel = AmrDb.SendSettings.SendTo})
- count = 0
- message = L.AMR_SHOPPINGLISTTAB_CHAT_ROBOT_MESSAGE
- end
- end
- end
-
- if count > 0 then
- tinsert(self.messageQueue, {message = message, chatType = chatType, chatChannel = AmrDb.SendSettings.SendTo})
- end
-
- self:SendNextMessage()
-end
-
-function AskMrRobot.ShoppingListTab:SendNextMessage()
- while #self.messageQueue > 0 do
- local entry = self.messageQueue[1]
- table.remove(self.messageQueue, 1)
- SendChatMessage(entry.message, entry.chatType, nil, entry.chatChannel)
- end
-end
\ No newline at end of file
diff -r 75431c084aa0 -r 01b63b8ed811 ui/SummaryTab.lua
--- a/ui/SummaryTab.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,449 +0,0 @@
-local _, AskMrRobot = ...
-local L = AskMrRobot.L;
-
-AskMrRobot.SummaryTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
-
-function AskMrRobot.SummaryTab:new(parent)
-
- local tab = AskMrRobot.Frame:new(nil, parent)
- setmetatable(tab, { __index = AskMrRobot.SummaryTab })
- tab:SetPoint("TOPLEFT")
- tab:SetPoint("BOTTOMRIGHT")
- tab:Hide()
-
-
- tab.importDate = ""
-
- local text = tab:CreateFontString("AmrSummaryText1", "ARTWORK", "GameFontNormalLarge")
- text:SetPoint("TOPLEFT", 0, -5)
- text:SetFormattedText(L.AMR_SUMMARYTAB_TITLE)
-
- -- error text
- tab.errorText1 = tab:CreateFontString("AmrSummaryErrorText1", "ARTWORK", "GameFontRedLarge")
- tab.errorText1:SetPoint("TOPLEFT", "AmrSummaryText1", "BOTTOMLEFT", 0, -20)
- tab.errorText1:SetText(L.AMR_SUMMARYTAB_NO_IMPORT)
- tab.errorText1:SetPoint("RIGHT", -20, 0)
- tab.errorText1:SetWidth(tab.errorText1:GetWidth())
- tab.errorText1:SetWordWrap(true)
- tab.errorText1:SetJustifyH("LEFT")
-
- tab.errorText2 = tab:CreateFontString("AmrSummaryErrorText2", "ARTWORK", "GameFontWhite")
- tab.errorText2:SetPoint("TOPLEFT", "AmrSummaryErrorText1", "BOTTOMLEFT", 0, -15)
- tab.errorText2:SetPoint("RIGHT", -20, 0)
- tab.errorText2:SetWidth(tab.errorText2:GetWidth())
- tab.errorText2:SetJustifyH("LEFT")
- tab.errorText2:SetText(L.AMR_SUMMARYTAB_GET_STARTED)
-
- -- bad items
- tab.badItemSlots = {}
- tab.badItemNames = {}
-
- local itemText = tab:CreateFontString("AmrBadItemSlot0", "ARTWORK", "GameFontNormal")
- itemText:SetPoint("TOPLEFT", "AmrSummaryErrorText2", "BOTTOMLEFT", 0, -20)
- itemText:SetText("Slot")
- itemText:SetWidth(100)
- itemText:SetJustifyH("LEFT")
- itemText:Hide()
- tinsert(tab.badItemSlots, itemText)
-
- itemText = tab:CreateFontString("AmrBadItemName0", "ARTWORK", "GameFontNormal")
- itemText:SetPoint("TOPLEFT", "AmrBadItemSlot0", "TOPLEFT", 120, 0)
- itemText:SetPoint("RIGHT", -30, 0)
- itemText:SetText("Item Name")
- itemText:SetJustifyH("LEFT")
- itemText:Hide()
- tinsert(tab.badItemNames, itemText)
-
- itemText = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- itemText:SetPoint("LEFT", 0, 0)
- itemText:SetPoint("RIGHT", -30, 0)
- itemText:SetPoint("TOP", "AmrBadItemSlot0", "TOP", 0, 0 )
- itemText:SetText(L.AMR_SUMMARYTAB_GO_UPGRADE)
- itemText:SetJustifyH("LEFT")
- itemText:Hide()
- tab.upgradeInstructions = itemText
-
- itemText = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal")
- itemText:SetPoint("LEFT", "AmrBadItemSlot0", "LEFT", 0, -20)
- itemText:SetPoint("RIGHT", "AmrBadItemSlot0", "RIGHT", 0, -20)
- itemText:SetPoint("TOP", tab.upgradeInstructions, "BOTTOM", 0, -10 )
- itemText:SetHeight(20)
- itemText:SetText(L.AMR_SUMMARYTAB_SLOT)
- itemText:SetJustifyH("LEFT")
- itemText:Hide()
- tab.upgradeSlotHeader = itemText
-
- itemText = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal")
- itemText:SetPoint("LEFT", "AmrBadItemName0", "LEFT", 0, 0)
- itemText:SetPoint("RIGHT", "AmrBadItemName0", "RIGHT", 0, 0)
- itemText:SetPoint("TOP", tab.upgradeSlotHeader, "TOP", 0, 0)
- itemText:SetPoint("BOTTOM", tab.upgradeSlotHeader, "BOTTOM", 0, 0)
- itemText:SetText(L.AMR_SUMMARYTAB_ITEM_NAME)
- itemText:SetJustifyH("LEFT")
- itemText:Hide()
- tab.upgradeItemHeader = itemText
-
- for i = 1, #AskMrRobot.slotIds do
- local itemText = tab:CreateFontString("AmrBadItemSlot" .. i, "ARTWORK", "GameFontWhite")
- itemText:SetPoint("TOPLEFT", "AmrBadItemSlot" .. (i-1), "BOTTOMLEFT", 0, -5)
- itemText:SetPoint("TOPRIGHT", "AmrBadItemSlot" .. (i-1), "BOTTOMRIGHT", 0, -5)
- itemText:SetJustifyH("LEFT")
- itemText:Hide()
- tinsert(tab.badItemSlots, itemText)
-
- itemText = AskMrRobot.ItemLinkText:new(nil, tab)
- itemText:SetPoint("LEFT", "AmrBadItemName0", "TOPLEFT", 0, 0)
- itemText:SetPoint("RIGHT", "AmrBadItemName0", "BOTTOMRIGHT", 0, 0)
- itemText:SetPoint("TOP", "AmrBadItemSlot" .. i, 0, 0)
- itemText:SetPoint("BOTTOM", "AmrBadItemSlot" .. i, 0, 0)
- itemText:Hide()
- tinsert(tab.badItemNames, itemText)
- end
-
- tab.upgradeItemSlots = {}
- tab.upgradeItemNames = {}
- for i = 1, #AskMrRobot.slotIds do
- local itemText = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- if i == 1 then
- itemText:SetPoint("TOPLEFT", tab.upgradeSlotHeader, "BOTTOMLEFT", 0, -5)
- itemText:SetPoint("TOPRIGHT", tab.upgradeSlotHeader, "BOTTOMRIGHT", 0, -5)
- else
- itemText:SetPoint("TOPLEFT", tab.upgradeItemSlots[i-1], "BOTTOMLEFT", 0, -5)
- itemText:SetPoint("TOPRIGHT", tab.upgradeItemSlots[i-1], "BOTTOMRIGHT", 0, -5)
- end
- itemText:SetJustifyH("LEFT")
- itemText:Hide()
- tinsert(tab.upgradeItemSlots, itemText)
-
- itemText = AskMrRobot.ItemLinkText:new(nil, tab)
- itemText:SetFormat("|cff00ff00Upgrade|r %s")
- itemText:SetPoint("LEFT", tab.upgradeItemHeader, "LEFT", 0, 0)
- itemText:SetPoint("RIGHT", tab.upgradeItemHeader, "RIGHT", 0, 0)
- itemText:SetPoint("TOP", tab.upgradeItemSlots[i], 0, 0)
- itemText:SetPoint("BOTTOM", tab.upgradeItemSlots[i], 0, 0)
- itemText:Hide()
- tinsert(tab.upgradeItemNames, itemText)
- end
-
- tab.importInfo = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal")
- tab.importInfo:SetText(L.AMR_SUMMARYTAB_LAST_IMPORT)
- tab.importInfo:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -20)
- tab.importInfo:SetJustifyH("LEFT")
- tab.importInfo:Hide()
-
- tab.specIcon = tab:CreateTexture(nil, "BACKGROUND")
- tab.specIcon:SetPoint("TOPLEFT")
- tab.specIcon:SetWidth(64)
- tab.specIcon:SetHeight(64)
- tab.specIcon:SetPoint("TOPLEFT", tab.importInfo, "BOTTOMLEFT", 0, -10)
- tab.specIcon:Hide()
-
- tab.stamp = AskMrRobot.RobotStamp:new(nil, tab)
- tab.stamp:Hide()
- tab.stamp.smallText:SetText(L.AMR_SUMMARYTAB_OPTIMAL)
- tab.stamp:SetPoint("TOPLEFT", tab.specIcon, "BOTTOMLEFT", 2, -25)
- tab.stamp:SetPoint("RIGHT", tab, "RIGHT", -20, 0)
- tab.stamp:Hide()
-
- tab.specText = tab:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge")
- local Path, Size, Flags = tab.specText:GetFont()
- tab.specText:SetFont(Path, 24, Flags);
- tab.specText:SetPoint("LEFT", tab.specIcon, "RIGHT", 10, 0)
- tab.specText:SetText("?")
- tab.specText:Hide()
-
- tab.optimizationSummary = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal")
- tab.optimizationSummary:SetPoint("TOPLEFT", tab.specIcon, "BOTTOMLEFT", 0, -15)
- tab.optimizationSummary:SetText(L.AMR_SUMMARYTAB_OPTIMIZATIONS_TO_GO)
- tab.optimizationSummary:Hide()
-
- tab.gemCount = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- tab.gemCount:SetPoint("TOPLEFT", tab.optimizationSummary, "BOTTOMLEFT", 0, -15)
- tab.gemCount:SetText(L.AMR_SUMMARYTAB_GEMS_TO_GO)
- tab.gemCount:Hide()
-
- tab.enchantCount = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- tab.enchantCount:SetPoint("TOPLEFT", tab.gemCount, "BOTTOMLEFT", 0, -5)
- tab.enchantCount:SetText(L.AMR_SUMMARYTAB_ENCHANTS_TO_GO)
- tab.enchantCount:Hide()
-
- tab.instructions = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
- tab.instructions:SetPoint("TOPLEFT", tab.enchantCount, "BOTTOMLEFT", 0, -15)
- tab.instructions:SetText(L.AMR_SUMMARYTAB_VIEW_TABS)
- tab.instructions:SetPoint("RIGHT", -20, 0)
- tab.instructions:SetWidth(tab.instructions:GetWidth())
- tab.instructions:SetWordWrap(true)
- tab.instructions:SetJustifyH("LEFT")
- tab.instructions:Hide()
-
- return tab
-end
-
-function AskMrRobot.SummaryTab:getSpecIcon(specId)
- for i = 1, GetNumSpecializations() do
- local id, _, _, icon = GetSpecializationInfo(i)
- if id == specId then
- return icon
- end
- end
- return nil
-end
-
-function AskMrRobot.SummaryTab:showBadItems()
- local i = 2
-
- -- for all slots
- for iSlot = 1, #AskMrRobot.slotIds do
- local slotId = AskMrRobot.slotIds[iSlot]
- local badItem = AskMrRobot.ComparisonResult.items[slotId]
- if badItem ~= nil and not badItem.needsUpgrade then
- self.badItemSlots[i]:SetText(AskMrRobot.slotDisplayText[slotId])
- self.badItemSlots[i]:Show()
- if badItem.optimized then
- self.badItemNames[i]:SetItem(badItem.optimized)
- else
- self.badItemNames[i]:SetItem(nil)
- end
- self.badItemNames[i]:Show()
- i = i + 1
- end
- end
-
- -- for all the upgrade items
- local j = 1
- for iSlot = 1, #AskMrRobot.slotIds do
- local slotId = AskMrRobot.slotIds[iSlot]
- local badItem = AskMrRobot.ComparisonResult.items[slotId]
- if badItem ~= nil and badItem.needsUpgrade then
- self.upgradeItemSlots[j]:SetText(AskMrRobot.slotDisplayText[slotId])
- self.upgradeItemSlots[j]:Show()
- if badItem.optimized then
- self.upgradeItemNames[j]:SetItem(badItem)
- else
- self.upgradeItemNames[j]:SetItem(nil)
- end
- self.upgradeItemNames[j]:Show()
- j = j + 1
- end
- end
-
- -- hide / show the headers
- if i == 2 and j == 1 then
- self.badItemSlots[1]:Hide()
- self.badItemNames[1]:Hide()
- self:showImportError(nil)
- self.importInfo:Show()
- self.specIcon:Show()
- self.optimizationSummary:Show()
- self.gemCount:Show()
- self.enchantCount:Show()
- self.instructions:Show()
- self.specText:Show()
-
- local gemCount = 0
- for slotNum, badGems in pairs(AskMrRobot.ComparisonResult.gems) do
- --for k, v in pairs(badGems.badGems) do
- --gemCount = gemCount + 1
- --end
- for g = 1, 3 do
- if not AskMrRobot.AreGemsCompatible(badGems.current[g], badGems.optimized[g]) then
- gemCount = gemCount + 1
- end
- end
- end
-
- self.gemCount:SetFormattedText(L.AMR_SUMMARYTAB_GEMCOUNT, gemCount)
-
- local enchantCount = 0
- for slotNum, badEnchant in pairs(AskMrRobot.ComparisonResult.enchants) do
- enchantCount = enchantCount + 1
- end
-
- self.enchantCount:SetFormattedText(L.AMR_SUMMARYTAB_ENCHANTCOUNT, enchantCount)
-
- self.optimizationSummary:SetFormattedText(L.AMR_SUMMARYTAB_OPTIMIZATIONCOUNT, gemCount + enchantCount)
-
- if gemCount + enchantCount == 0 then
- self.stamp:Show()
- self.optimizationSummary:Hide()
- self.enchantCount:Hide()
- self.instructions:Hide()
- self.gemCount:Hide()
- else
- self.stamp:Hide()
- self.optimizationSummary:Show()
- self.enchantCount:Show()
- self.instructions:Show()
- self.gemCount:Show()
- end
-
- local activeSpecGroup = GetActiveSpecGroup()
-
- if activeSpecGroup == nil then
- self.importInfo:SetFormattedText(L.AMR_SUMMARYTAB_LAST_IMPORT_1, AmrDb.LastCharacterImportDate, UnitName("player"))
- else
- self.importInfo:SetFormattedText(L.AMR_SUMMARYTAB_LAST_IMPORT_2, AmrDb.LastCharacterImportDate, UnitName("player"))
- local spec = GetSpecialization(false, false, group);
- if spec then
- local _, name, _, icon = GetSpecializationInfo(spec);
- if activeSpecGroup == 1 then
- self.specText:SetFormattedText(L.AMR_SUMMARYTAB_LAST_IMPORT_PSPEC, name)
- else
- self.specText:SetFormattedText(L.AMR_SUMMARYTAB_LAST_IMPORT_SSPEC, name)
- end
- self.specIcon:SetTexture(icon)
- end
- end
-
-
- --local currentSpec = GetAmrSpecialization(GetActiveSpecGroup())
- --return (not currentSpec and not spec) or tostring(currentSpec) == spec
- else
- self.importInfo:Hide()
- self.specIcon:Hide()
- self.optimizationSummary:Hide()
- self.gemCount:Hide()
- self.enchantCount:Hide()
- self.instructions:Hide()
- self.specText:Hide()
- end
-
- if i == 2 then
- self.badItemSlots[1]:Hide()
- self.badItemNames[1]:Hide()
- else
- self.badItemSlots[1]:Show()
- self.badItemNames[1]:Show()
- local warnings = {}
- if self.badRealm then
- tinsert(warnings, L.AMR_SUMMARYTAB_DIFF_REALM:format(self.badRealm))
- end
- if self.badTalents then
- tinsert(warnings, L.AMR_SUMMARYTAB_DIFF_TALENT)
- end
- if self.badGlyphs then
- tinsert(warnings, L.AMR_SUMMARYTAB_DIFF_GLYPHS)
- end
- local message = L.AMR_SUMMARYTAB_DIFF_GEAR
- if #warnings > 0 then
- message = message .. " (".. L.AMR_SUMMARYTAB_DIFF_AND.." "
- for k = 1, #warnings do
- if k > 1 then
- message = message .. ', '
- end
- message = message .. warnings[k]
- end
- message = message .. ")"
- end
- message = message .. L.AMR_SUMMARYTAB_DIFF_PLEASE_EQ
- self:showImportWarning(L.AMR_SUMMARYTAB_DIFF_CHECK_CHAR, message)
- end
-
- if j == 1 then
- self.upgradeItemHeader:Hide()
- self.upgradeSlotHeader:Hide()
- self.upgradeInstructions:Hide()
- else
- if i == 2 then
- local warnings = {}
- if self.badRealm then
- tinsert(warnings, L.AMR_SUMMARYTAB_DIFF_REALM:format(self.badRealm))
- end
- if self.badTalents then
- tinsert(warnings, L.AMR_SUMMARYTAB_DIFF_TALENT)
- end
- if self.badGlyphs then
- tinsert(warnings, L.AMR_SUMMARYTAB_DIFF_GLYPHS)
- end
- local message = nil
- if #warnings > 0 then
- message = L.AMR_SUMMARYTAB_DIFF_OPTIMIZED_FOR
- for k = 1, #warnings do
- if k > 1 then
- message = message .. ', '
- end
- message = message .. warnings[k]
- end
- message = message .. "."
- end
- self:showImportWarning(L.AMR_SUMMARYTAB_DIFF_CHECK_CHAR, message)
- end
- self.upgradeItemHeader:Show()
- self.upgradeSlotHeader:Show()
- self.upgradeInstructions:Show()
- if i == 2 then
- self.upgradeInstructions:SetPoint("TOP", self.badItemSlots[1], "TOP", 0, 0)
- self.errorText2:Hide()
- else
- self.upgradeInstructions:SetPoint("TOP", self.badItemSlots[i], "BOTTOM", 0, -20)
- end
- end
-
- -- hide the remaining slots
- while i <= #self.badItemSlots do
- self.badItemSlots[i]:Hide()
- self.badItemNames[i]:Hide()
- i = i + 1
- end
-
- -- hide the remaining slots
- while j <= #self.upgradeItemSlots do
- self.upgradeItemSlots[j]:Hide()
- self.upgradeItemNames[j]:Hide()
- j = j + 1
- end
-end
-
-
-function AskMrRobot.SummaryTab:showImportError(text, text2)
- self.stamp:Hide()
- self.gemCount:Hide()
- self.instructions:Hide()
- self.enchantCount:Hide()
- self.optimizationSummary:Hide()
- if text then
- self.errorText1:Show()
- self.errorText1:SetText(L.AMR_SUMMARYTAB_IMPORT_NOT_WORK:format(text))
- self.errorText1:Show()
- self.errorText2:SetText(text2)
- self.errorText2:Show()
- self.importInfo:Hide()
- self.upgradeSlotHeader:Hide()
- self.upgradeItemHeader:Hide()
- self.upgradeInstructions:Hide()
- self.specIcon:Hide()
- self.specText:Hide()
- else
- self.errorText1:Hide()
- self.errorText2:Hide()
- self.importInfo:Show()
- end
- for i = 1, #self.badItemSlots do
- self.badItemSlots[i]:Hide()
- self.badItemNames[i]:Hide()
- end
- for i = 1, #self.upgradeItemSlots do
- self.upgradeItemSlots[i]:Hide()
- self.upgradeItemNames[i]:Hide()
- end
-end
-
-function AskMrRobot.SummaryTab:showImportWarning (text, text2)
- self.stamp:Hide()
- self.hasImportError = false
- self.gemCount:Hide()
- self.instructions:Hide()
- self.enchantCount:Hide()
- self.specIcon:Hide()
- self.specText:Hide()
- self.optimizationSummary:Hide()
- if text then
- self.errorText1:SetText(text)
- self.errorText1:Show()
- self.errorText2:SetText(text2)
- self.errorText2:Show()
- else
- self.errorText1:Hide()
- self.errorText2:Hide()
- end
-end
diff -r 75431c084aa0 -r 01b63b8ed811 ui/Ui.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/Ui.lua Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,396 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- used to make some stuff layer correctly
+Amr.FrameLevels = {
+ High = 100,
+ Highest = 125
+}
+
+-- standard colors used throughout the UI (in standard 0-255 RGB format, game uses 0-1 decimals, but we auto-convert it below)
+Amr.Colors = {
+ White = { R = 255, G = 255, B = 255 },
+ Black = { R = 0, G = 0, B = 0 },
+ Gray = { R = 153, G = 153, B = 153 },
+ Orange = { R = 201, G = 87, B = 1 },
+ Green = { R = 77, G = 134, B = 45 },
+ Blue = { R = 54, G = 172, B = 204 },
+ Red = { R = 204, G = 38, B = 38 },
+ Gold = { R = 255, G = 215, B = 0 },
+ BrightGreen = { R = 0, G = 255, B = 0 },
+ Text = { R = 255, G = 255, B = 255 },
+ TextHover = { R = 255, G = 255, B = 0 },
+ TextGray = { R = 120, G = 120, B = 120 },
+ TextHeaderActive = { R = 223, G = 134, B = 61 },
+ TextHeaderDisabled = { R = 188, G = 188, B = 188 },
+ TextTan = { R = 223, G = 192, B = 159 },
+ BorderBlue = { R = 26, G = 83, B = 98 },
+ BorderGray = { R = 96, G = 96, B = 96 },
+ Bg = { R = 41, G = 41, B = 41 },
+ BgInput = { R = 17, G = 17, B = 17 },
+ BarHigh = { R = 114, G = 197, B = 66 },
+ BarMed = { R = 255, G = 196, B = 36 },
+ BarLow = { R = 201, G = 87, B = 1 }
+}
+
+-- convert from common RGB to 0-1 RGB values
+for k,v in pairs(Amr.Colors) do
+ v.R = v.R / 255
+ v.G = v.G / 255
+ v.B = v.B / 255
+end
+
+-- get colors for classes from WoW's constants
+Amr.Colors.Classes = {}
+for k,v in pairs(RAID_CLASS_COLORS) do
+ Amr.Colors.Classes[k] = { R = v.r, G = v.g, B = v.b }
+end
+
+-- helper to take 0-1 value and turn into 2-digit hex value
+local function decToHex(num)
+ num = math.ceil(num * 255)
+ num = string.format("%X", num)
+ if string.len(num) == 1 then num = "0" .. num end
+ return num
+end
+
+function Amr.ColorToHex(color, alpha)
+ return decToHex(alpha) .. decToHex(color.R) .. decToHex(color.G) .. decToHex(color.B)
+end
+
+local function getFontPath(style)
+ return "Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\Ubuntu-" .. style .. ".ttf"
+end
+
+-- create a font with the specified style (Regular, Bold, Italic), size (pixels, max of 32), color (object with R, G, B), and alpha (if not specified, defaults to 1)
+function Amr.CreateFont(style, size, color, a)
+ local alpha = a or 1
+ local id = string.format("%s_%d_%f_%f_%f_%f", style, size, color.R, color.G, color.B, alpha)
+ local font = CreateFont(id)
+ font:SetFont(getFontPath(style), size)
+ font:SetTextColor(color.R, color.G, color.B, alpha)
+ return font
+end
+
+-- helper to create a solid texture from a color with R,G,B properties
+function Amr.CreateTexture(parent, color, alpha, layer)
+ local t = parent:CreateTexture(nil, layer or "ARTWORK")
+ t:SetTexture(color.R, color.G, color.B, alpha or 1)
+ return t
+end
+
+-- helper to create a cheater shadow without having to create custom images
+function Amr.DropShadow(frame)
+ local shadow = frame:CreateTexture(nil, "BACKGROUND")
+ shadow:SetTexture(0, 0, 0, 0.4)
+ shadow:SetPoint("TOPLEFT", frame, "TOPLEFT", 2, -2)
+ shadow:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 1, -1)
+
+ shadow = frame:CreateTexture(nil, "BACKGROUND")
+ shadow:SetTexture(0, 0, 0, 0.3)
+ shadow:SetPoint("TOPLEFT", frame, "TOPLEFT", 2, -2)
+ shadow:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 2, -2)
+
+ shadow = frame:CreateTexture(nil, "BACKGROUND")
+ shadow:SetTexture(0, 0, 0, 0.1)
+ shadow:SetPoint("TOPLEFT", frame, "TOPLEFT", 2, -2)
+ shadow:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 3, -3)
+end
+
+
+-- a layout that does nothing, just lets the children position themselves how they prefer
+AceGUI:RegisterLayout("None", function(content, children)
+ if content.obj.LayoutFinished then
+ content.obj:LayoutFinished(nil, nil)
+ end
+end)
+
+local _mainFrame = nil
+local _mainTabs = nil
+local _mainCover = nil
+local _activeTab = "Export"
+
+-- release everything when the UI is closed
+local function onMainFrameClose(widget)
+ AceGUI:Release(widget)
+ Amr["ReleaseTab" .. _activeTab](Amr)
+ _mainFrame = nil
+ _mainTabs = nil
+ _mainCover = nil
+end
+
+local function onMainTabSelected(container, event, group)
+ container:ReleaseChildren()
+ Amr["ReleaseTab" .. _activeTab](Amr)
+
+ _activeTab = group
+
+ -- each section defines its own render method in a separate file (options tab is defined in Core.lua, uses standard Ace config stuff to auto-generate)
+ Amr["RenderTab" .. group](Amr, container)
+end
+
+-- refresh the currently displayed tab
+function Amr:RefreshTab()
+ if not _mainTabs then return end
+
+ _mainTabs:ReleaseChildren()
+ Amr["ReleaseTab" .. _activeTab](Amr)
+ Amr["RenderTab" .. _activeTab](Amr, container)
+end
+
+local function createMainWindow()
+
+ local f = AceGUI:Create("AmrUiFrame")
+ f:SetStatusTable(Amr.db.profile.window) -- window position is remembered in db
+ f:SetCallback("OnClose", onMainFrameClose)
+ f:SetLayout("None")
+ f:SetWidth(1000)
+ f:SetHeight(700)
+ f:SetBorderColor(Amr.Colors.BorderBlue)
+ f:SetBackgroundColor(Amr.Colors.Bg)
+
+ -- some status text
+ local lblStatus = AceGUI:Create("AmrUiLabel")
+ lblStatus:SetWidth(900)
+ lblStatus:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
+ lblStatus:SetText("Ask Mr. Robot " .. L.MainStatusText("v" .. GetAddOnMetadata(Amr.ADDON_NAME, "Version"), "http://www.askmrrobot.com/wow/addon"))
+ lblStatus:SetJustifyH("CENTER")
+ lblStatus:SetWordWrap(false)
+ lblStatus:SetPoint("TOP", f.content, "BOTTOM")
+ f:AddChild(lblStatus)
+
+ -- create the main UI container
+ local c = AceGUI:Create("AmrUiPanel")
+ c:SetLayout("Fill")
+ c:SetBackgroundColor(Amr.Colors.Black, 0)
+ c:SetPoint("TOPLEFT", f.content, "TOPLEFT")
+ c:SetPoint("BOTTOMRIGHT", f.content, "BOTTOMRIGHT")
+ f:AddChild(c)
+
+ -- create the main tab strip
+ local t = AceGUI:Create("AmrUiTabGroup")
+ t:SetLayout("None")
+ t:SetTabs({
+ {text=L.TabExportText, value="Export"},
+ {text=L.TabGearText, value="Gear"},
+ {text=L.TabLogText, value="Log"},
+ {text=L.TabTeamText, value="Team"},
+ {text=L.TabOptionsText, value="Options"}
+ })
+ t:SetCallback("OnGroupSelected", onMainTabSelected)
+ c:AddChild(t)
+
+ -- create the cover/overlay container
+ c = AceGUI:Create("AmrUiPanel")
+ c:SetLayout("None")
+ c:EnableMouse(true)
+ c:SetBackgroundColor(Amr.Colors.Black, 0.75)
+ c:SetPoint("TOPLEFT", f.frame, "TOPLEFT")
+ c:SetPoint("BOTTOMRIGHT", f.frame, "BOTTOMRIGHT")
+ f:AddChild(c)
+
+ -- after adding, set cover to sit on top of everything, then hide it
+ c:SetStrata("FULLSCREEN_DIALOG")
+ c:SetLevel(Amr.FrameLevels.High)
+ c:SetVisible(false)
+
+ -- put standard cover ui elements (label, cancel button)
+ local coverMsg = AceGUI:Create("AmrUiLabel")
+ coverMsg:SetWidth(600)
+ coverMsg:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan))
+ coverMsg:SetJustifyH("MIDDLE")
+ coverMsg:SetJustifyV("MIDDLE")
+ coverMsg:SetText("")
+ coverMsg:SetPoint("CENTER", c.frame, "CENTER", 0, 20)
+ c:AddChild(coverMsg)
+
+ local coverCancel = AceGUI:Create("AmrUiTextButton")
+ coverCancel:SetWidth(200)
+ coverCancel:SetHeight(20)
+ coverCancel:SetText(L.CoverCancel)
+ coverCancel:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextHeaderDisabled))
+ coverCancel:SetHoverFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextHeaderActive))
+ coverCancel:SetPoint("CENTER", c.frame, "CENTER", 0, -20)
+ c:AddChild(coverCancel)
+
+ coverCancel:SetCallback("OnClick", function(widget)
+ Amr:HideCover()
+ end)
+
+ -- create cover content area for custom cover ui (sort of like a modal dialog)
+ local coverContent = AceGUI:Create("AmrUiPanel")
+ coverContent:SetLayout("None")
+ coverContent:SetBackgroundColor(Amr.Colors.Black, 0)
+ coverContent:SetPoint("TOPLEFT", c.frame, "TOPLEFT")
+ coverContent:SetPoint("BOTTOMRIGHT", c.frame, "BOTTOMRIGHT")
+ c:AddChild(coverContent)
+
+ _mainFrame = f
+ _mainTabs = t
+ _mainCover = {
+ panel = c,
+ content = coverContent,
+ label = coverMsg,
+ cancel = coverCancel
+ }
+end
+
+function Amr:ShowCover(msgOrRenderFunc, disableCancel)
+ if _mainCover then
+ _mainCover.panel:SetVisible(true)
+
+ if type(msgOrRenderFunc) == "function" then
+ _mainCover.label:SetText("")
+ _mainCover.cancel:SetVisible(false)
+
+ -- render custom content into the cover
+ msgOrRenderFunc(_mainCover.content)
+ else
+ -- standard loading/waiting message with optional cancel button
+ _mainCover.label:SetText(msgOrRenderFunc or "")
+ _mainCover.cancel:SetVisible(not disableCancel)
+ end
+ end
+end
+
+function Amr:HideCover()
+ if _mainCover then
+ _mainCover.panel:SetVisible(false)
+
+ -- release any custom content rendered into the cover
+ _mainCover.content:ReleaseChildren()
+ end
+end
+
+-- shows a "modal" alert over the main UI
+function Amr:ShowAlert(message, btnText)
+
+ Amr:ShowCover(function(container)
+ local border = AceGUI:Create("AmrUiPanel")
+ border:SetLayout("None")
+ border:SetBackgroundColor(Amr.Colors.BorderBlue)
+ border:SetWidth(400)
+ border:SetHeight(150)
+ border:SetPoint("CENTER", container.frame, "CENTER")
+ container:AddChild(border)
+
+ local bg = AceGUI:Create("AmrUiPanel")
+ bg:SetLayout("None")
+ bg:SetBackgroundColor(Amr.Colors.Bg)
+ bg:SetPoint("TOPLEFT", border.frame, "TOPLEFT", 1, -1)
+ bg:SetPoint("BOTTOMRIGHT", border.frame, "BOTTOMRIGHT", -1, 1)
+ border:AddChild(bg)
+
+ local lbl = AceGUI:Create("AmrUiLabel")
+ lbl:SetWidth(360)
+ lbl:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
+ lbl:SetJustifyH("CENTER")
+ lbl:SetText(message)
+ lbl:SetPoint("TOP", bg.content, "TOP", 0, -20)
+ bg:AddChild(lbl)
+
+ local btn = AceGUI:Create("AmrUiButton")
+ btn:SetBackgroundColor(Amr.Colors.Orange)
+ btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
+ btn:SetWidth(120)
+ btn:SetHeight(26)
+ btn:SetText(btnText)
+ btn:SetPoint("BOTTOM", bg.content, "BOTTOM", 0, 20)
+ bg:AddChild(btn)
+
+ btn:SetCallback("OnClick", function(widget)
+ Amr:HideCover()
+ end)
+ end)
+end
+
+-- toggle visibility of the UI
+function Amr:Toggle()
+ if not self:IsEnabled() then return end
+
+ if not _mainFrame then
+ self:Show()
+ else
+ self:Hide()
+ end
+end
+
+-- hide the UI if not already hidden
+function Amr:Hide()
+ if not self:IsEnabled() then return end
+ if not _mainFrame then return end
+
+ _mainFrame:Hide()
+end
+
+-- show the UI if not shown already, and display the last active tab
+function Amr:Show()
+ if not self:IsEnabled() then return end
+
+ if _mainFrame then
+ _mainFrame:Show()
+ else
+ createMainWindow()
+ end
+
+ -- show the active tab
+ _mainTabs:SelectTab(_activeTab)
+end
+
+-- show the UI if not shown already, and select the specified tab
+function Amr:ShowTab(tab)
+ if not self:IsEnabled() then return end
+
+ _activeTab = tab
+ self:Show()
+end
+
+----------------------------------------------------------------------------------------
+-- Tooltips
+----------------------------------------------------------------------------------------
+
+-- set an item tooltip on any AceGUI widget with OnEnter and OnLeave events
+function Amr:SetItemTooltip(obj, itemLink, anchor, x, y)
+ obj:SetUserData("ttItemLink", itemLink)
+ obj:SetCallback("OnEnter", function(widget)
+ local tooltipLink = widget:GetUserData("ttItemLink")
+ GameTooltip:SetOwner(widget.frame, anchor and anchor or "ANCHOR_CURSOR", x, y)
+ GameTooltip:SetHyperlink(tooltipLink)
+ end)
+ obj:SetCallback("OnLeave", function(widget)
+ GameTooltip:Hide()
+ end)
+end
+
+function Amr:SetSpellTooltip(obj, spellId, anchor, x, y)
+ obj:SetUserData("ttSpellId", spellId)
+ obj:SetCallback("OnEnter", function(widget)
+ local ttSpellId = widget:GetUserData("ttSpellId")
+ GameTooltip:SetOwner(widget.frame, anchor and anchor or "ANCHOR_CURSOR", x, y)
+ GameTooltip:SetSpellByID(ttSpellId)
+ end)
+ obj:SetCallback("OnLeave", function(widget)
+ GameTooltip:Hide()
+ end)
+end
+
+function Amr:RenderCoverChrome(container, width, height)
+
+ local border = AceGUI:Create("AmrUiPanel")
+ border:SetLayout("None")
+ border:SetBackgroundColor(Amr.Colors.BorderBlue)
+ border:SetWidth(width + 2)
+ border:SetHeight(height + 2)
+ border:SetPoint("CENTER", container.frame, "CENTER")
+ container:AddChild(border)
+
+ local bg = AceGUI:Create("AmrUiPanel")
+ bg:SetLayout("None")
+ bg:SetBackgroundColor(Amr.Colors.Bg)
+ bg:SetPoint("TOPLEFT", border.frame, "TOPLEFT", 1, -1)
+ bg:SetPoint("BOTTOMRIGHT", border.frame, "BOTTOMRIGHT", -1, 1)
+ border:AddChild(bg)
+
+ return bg, border
+end
diff -r 75431c084aa0 -r 01b63b8ed811 wait.lua
--- a/wait.lua Tue Feb 24 21:50:13 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-local _, AskMrRobot = ...
-
-local waitTable = {};
-local waitFrame = nil;
-
-function AskMrRobot.wait(delay, func, ...)
- if(type(delay)~="number" or type(func)~="function") then
- print(AMR_WAIT_BAD_ARGUMENTS);
- return false;
- end
- if(waitFrame == nil) then
- waitFrame = CreateFrame("Frame","WaitFrame", UIParent);
- waitFrame:SetScript("OnUpdate",function (self,elapse)
- local count = #waitTable;
- local i = 1;
- while(i<=count) do
- local waitRecord = tremove(waitTable,i);
- local d = tremove(waitRecord,1);
- local f = tremove(waitRecord,1);
- local p = tremove(waitRecord,1);
- if(d>elapse) then
- tinsert(waitTable,i,{d-elapse,f,p});
- i = i + 1;
- else
- count = count - 1;
- f(unpack(p));
- end
- end
- end);
- end
- tinsert(waitTable,{delay,func,{...}});
- return true;
-end