Want more features on Pastebin? Sign Up, it's FREE!
Guest

Ara_Broker_Tradeskills

By: Uggers on Oct 6th, 2012  |  syntax: Lua  |  size: 53.25 KB  |  views: 6  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. -- TODO: auto tracking using CHAT_MSG_SKILL/CHAT_MSG_SYSTEM or auto tracking on panel selection
  2. -- TODO: save/restore skill filters if window is opened
  3. local addonName, L = ...
  4.  
  5. local meta = { __index = function(t,k) return k end }
  6. L = setmetatable( L, meta )
  7.  
  8. local f = CreateFrame("Frame", "AraSkills", UIParent) -- main frame
  9. local g = CreateFrame("Frame", nil, f) -- alt frame 1
  10. local h = CreateFrame("Frame", nil, f) -- alt frame 2
  11. local e = CreateFrame"Frame" -- OnUpdate if config.displayCDs
  12. local ex = CreateFrame"Frame" -- bucketed skill tracking
  13. local configMenu, nbEntries, UpdateTablet
  14. local config, realm, char, options, c
  15. local CDs, shortcuts, workingFrame, prevAltSkill
  16. local baseScript, SetTracking
  17.  
  18. local prevIndex = 0
  19. local info = {}
  20. local playerName, realmName = GetUnitName"player", GetRealmName()
  21. local scanner = CreateFrame("GameTooltip", "ABTS_GT", nil, "GameTooltipTemplate")
  22. scanner:SetOwner(UIParent, "ANCHOR_NONE")
  23. local fmtReagents = _G.SPELL_REAGENTS
  24.  
  25. local BUTTON_HEIGHT, ICON_SIZE, GAP, TEXT_OFFSET = 17, 15, 10, 4
  26. local PANEL, PROFESSION, COMBAT_SKILL, LINK, SECONDARY, SPELL_ID = 1, 2, 4, 8, 16, 256
  27. local MAX_SKILL_VALUE = 300 + 75 * GetAccountExpansionLevel()
  28. local defaultColors, colors = { header={1,1,1}, activeName={1,.82,0}, infoName={.6,.5,0}, selected={.9,0.45,.1}, highlight={1,.8,0}, ownCD={1,.82,0}, foreignCD={.6,.5,0} }
  29. local AUTO = "AUTO"
  30.  
  31. local bit_band, date, tonumber, wipe, ipairs, tremove, GetProfessionInfo, GetSpellInfo, GetTradeSkillInfo, GetTradeSkillRecipeLink, tip =
  32.         bit.band, date, tonumber, wipe, ipairs, tremove, GetProfessionInfo, GetSpellInfo, GetTradeSkillInfo, GetTradeSkillRecipeLink
  33.  
  34. local skills = {
  35.         [L.Alchemy]=            PROFESSION +  2259*SPELL_ID + PANEL + LINK,
  36.         [L.Archaeology]=        PROFESSION + 78670*SPELL_ID + PANEL + SECONDARY,
  37.         [L.Blacksmithing]=      PROFESSION +  2018*SPELL_ID + PANEL + LINK,
  38.         [L.Cooking]=            PROFESSION +  2550*SPELL_ID + PANEL + LINK + SECONDARY, -- campfire
  39.         [L.Enchanting]=         PROFESSION +  7411*SPELL_ID + PANEL + LINK, -- d/e
  40.         [L.Engineering]=        PROFESSION +  4036*SPELL_ID + PANEL + LINK,
  41.         [L["First Aid"]]=       PROFESSION +  3273*SPELL_ID + PANEL + LINK + SECONDARY,
  42.         [L.Fishing]=            PROFESSION + 131490*SPELL_ID + SECONDARY,
  43.         ["Herbalism"] =         PROFESSION +  2366*SPELL_ID, -- track herbs
  44.         [L.Inscription]=        PROFESSION + 45357*SPELL_ID + PANEL + LINK, -- mill
  45.         [L.Jewelcrafting]=      PROFESSION + 25229*SPELL_ID + PANEL + LINK, -- prospect
  46.         [L.Leatherworking]=     PROFESSION +  2108*SPELL_ID + PANEL + LINK,
  47. --      [L.Lockpicking]=        PROFESSION +  1804*SPELL_ID + SECONDARY,
  48.         [L.Runeforging]=        PROFESSION + 53428*SPELL_ID + PANEL + SECONDARY,
  49.         [L.Skinning]=           PROFESSION +  8613*SPELL_ID,
  50.         [L.Smelting]=           PROFESSION +  2580*SPELL_ID + PANEL, -- track minerals (icons: 2575=mining, 2580=smelting)
  51.         [L.Tailoring]=          PROFESSION +  3908*SPELL_ID + PANEL + LINK,
  52.         None =                  0
  53. }
  54. local altNames = setmetatable( {
  55.         [L.Mining] =            L.Smelting,
  56.         [L.RuneforgingDN] =     L.Runeforging,
  57. }, meta )
  58.  
  59.  
  60. local revAlt = setmetatable( {}, meta )
  61. for k,v in next, altNames do revAlt[v] = k end
  62.  
  63. local groupIcons = {
  64.         ["Transmutes"] = "Interface\\Icons\\Spell_Nature_ElementalPrecision_2" }
  65. local groupIcons = {
  66.         ["Facets of Research"] = "Interface\\Icons\\inv_misc_gem_variety_02" }
  67.  
  68.  
  69.  
  70. local HOUR, DAY, MIDNIGHT = 3600, 86400, -1
  71. local TRANSMUTE_G1 = { skill= L.Alchemy, CD= MIDNIGHT, group= "Transmutes" }
  72. local JCRESEARCH_G1 = { skill= L.Jewelcrafting, CD= MIDNIGHT, group= "Facets of Research" }
  73.  
  74. local watchedCDs = {
  75.         [11479]= TRANSMUTE_G1, -- Iron to Gold
  76.         [11480]= TRANSMUTE_G1, -- Mithril to Truesilver
  77.         [17559]= TRANSMUTE_G1, -- Air to Fire
  78.         [17566]= TRANSMUTE_G1, -- Earth to Life
  79.         [17561]= TRANSMUTE_G1, -- Earth to Water
  80.         [17560]= TRANSMUTE_G1, -- Fire to Earth
  81.         [17565]= TRANSMUTE_G1, -- Life to Earth
  82.         [17563]= TRANSMUTE_G1, -- Undeath to Earth
  83.         [17562]= TRANSMUTE_G1, -- Water to Air
  84.         [17564]= TRANSMUTE_G1, -- Water to Undeath
  85.         [28566]= TRANSMUTE_G1, -- Primal Air to Fire
  86.         [28585]= TRANSMUTE_G1, -- Primal Earth to Life
  87.         [28567]= TRANSMUTE_G1, -- Primal Earth to Water
  88.         [28568]= TRANSMUTE_G1, -- Primal Fire to Earth
  89.         [28583]= TRANSMUTE_G1, -- Primal Fire to Mana
  90.         [28584]= TRANSMUTE_G1, -- Primal Life to Earth
  91.         [28582]= TRANSMUTE_G1, -- Primal Mana to Fire
  92.         [28580]= TRANSMUTE_G1, -- Primal Shadow to Water
  93.         [28569]= TRANSMUTE_G1, -- Primal Water to Air
  94.         [28581]= TRANSMUTE_G1, -- Primal Water to Shadow
  95.         [53777]= TRANSMUTE_G1, -- Eternal Air to Earth
  96.         [52776]= TRANSMUTE_G1, -- Eternal Air to Water
  97.         [53781]= TRANSMUTE_G1, -- Eternal Earth to Air
  98.         [53782]= TRANSMUTE_G1, -- Eternal Earth to Shadow
  99.         [53775]= TRANSMUTE_G1, -- Eternal Fire to Life
  100.         [53774]= TRANSMUTE_G1, -- Eternal Fire to Water
  101.         [53773]= TRANSMUTE_G1, -- Eternal Life to Fire
  102.         [53771]= TRANSMUTE_G1, -- Eternal Life to Shadow
  103.         [54020]= TRANSMUTE_G1, -- Eternal Might
  104.         [53779]= TRANSMUTE_G1, -- Eternal Shadow to Earth
  105.         [52780]= TRANSMUTE_G1, -- Eternal Shadow to Life
  106.         [53783]= TRANSMUTE_G1, -- Eternal Water to Air
  107.         [53784]= TRANSMUTE_G1, -- Eternal Water to Fire
  108.  
  109.         [66658]= TRANSMUTE_G1, -- Ametrine
  110.         [66659]= TRANSMUTE_G1, -- Cardinal Ruby
  111.         [66662]= TRANSMUTE_G1, -- Dreadstone
  112.         [66664]= TRANSMUTE_G1, -- Eye of Zul
  113.         [66660]= TRANSMUTE_G1, -- King's Amber
  114.         [66663]= TRANSMUTE_G1, -- Majestic Zircon
  115.  
  116.         [28027]= { skill= L.Enchanting,         CD= 2*DAY },            -- Prismatic Sphere
  117.         [28028]= { skill= L.Enchanting,         CD= 2*DAY },            -- Void Sphere
  118.         [47280]= { skill= L.Jewelcrafting,      CD=   MIDNIGHT },       -- Brilliant Glass
  119.         [62242]= { skill= L.Jewelcrafting,      CD=   MIDNIGHT },       -- Icy Prism
  120.         [56005]= { skill= L.Tailoring,          CD= 6*DAY+20*HOUR },    -- Glacial Bag
  121.         [61288]= { skill= L.Inscription,        CD=   MIDNIGHT },       -- Minor Glyph RS
  122.         [61177]= { skill= L.Inscription,        CD=   MIDNIGHT },       -- Major Glyph RS
  123.         [60893]= { skill= L.Alchemy,            CD= 3*MIDNIGHT },       -- Alchemy Research
  124.         [44717]= { skill= "Item",               CD= 3*DAY },            -- Disgusting Jar (ripe jar: 44718)
  125.         [39878]= { skill= "Item",               CD= 3*DAY },            -- Mysterious Egg (cracked egg: 39883)
  126.  
  127.  
  128.         -------------------------------------- CATACLYSM ---------------------------------
  129.  
  130.  
  131.         [78866]= TRANSMUTE_G1, -- Living Elements
  132. --      [80237]= TRANSMUTE_G1, -- Shadowspirit Diamond (meta)
  133. --      [80245]= TRANSMUTE_G1, -- Inferno Ruby
  134. --      [80246]= TRANSMUTE_G1, -- Ocean Sapphire
  135. --      [80247]= TRANSMUTE_G1, -- Amberjewel
  136. --      [80248]= TRANSMUTE_G1, -- Demonseye
  137. --      [80250]= TRANSMUTE_G1, -- Ember Topaz
  138. --      [80251]= TRANSMUTE_G1, -- Dream Emerald
  139.  
  140.         [80243]= TRANSMUTE_G1, -- Truegold
  141.         [80244]= TRANSMUTE_G1, -- Pyrium Bar
  142.  
  143.         [73478]= { skill= L.Jewelcrafting,      CD=       20*HOUR },    -- Fire Prism
  144.         [89244]= { skill= L.Inscription,        CD=   MIDNIGHT },       -- Forged Documents (alliance)
  145.         [86654]= { skill= L.Inscription,        CD=   MIDNIGHT },       -- Forged Documents (horde)
  146.         [75141]= { skill= L.Tailoring,          CD= 6*DAY+20*HOUR },    -- Dream of Skywall
  147.         [75142]= { skill= L.Tailoring,          CD= 6*DAY+20*HOUR },    -- Dream of Deepholm
  148. --      [94743]= { skill= L.Tailoring,          CD=  },         -- Dream of Destruction
  149.         [75144]= { skill= L.Tailoring,          CD= 6*DAY+20*HOUR },    -- Dream of Hyjal
  150.         [75145]= { skill= L.Tailoring,          CD= 6*DAY+20*HOUR },    -- Dream of Ragnaros
  151.         [75146]= { skill= L.Tailoring,          CD= 6*DAY+20*HOUR },    -- Dream of Azshara
  152.  
  153.         -------------------------------------- MISTS OF PANDARIA ---------------------------------
  154.  
  155.  
  156. --      [114781]= TRANSMUTE_G1, -- Primal Diamond (Meta)
  157. --      [114784]= TRANSMUTE_G1, -- Primordial Ruby
  158. --      [114766]= TRANSMUTE_G1, -- River's Heart
  159. --      [114767]= TRANSMUTE_G1, -- Wild Jade
  160. --      [114776]= TRANSMUTE_G1, -- Vermilion Onyx
  161. --      [114777]= TRANSMUTE_G1, -- Imperial Amethyst
  162. --      [114778]= TRANSMUTE_G1, -- Sun's Radiance
  163.  
  164. --      [114783]= TRANSMUTE_G1, -- Trillium Bar
  165.         [114780]= TRANSMUTE_G1, -- Living Steel
  166.  
  167.         [131593]= JCRESEARCH_G1,        -- River's Heart
  168.         [131688]= JCRESEARCH_G1,        -- Wild Jade   
  169.         [131695]= JCRESEARCH_G1,        -- Sun's Radiance
  170.         [131691]= JCRESEARCH_G1,        -- Imperial Amethyst   
  171.         [131690]= JCRESEARCH_G1,        -- Vermilion Onyx      
  172.         [131686]= JCRESEARCH_G1,        -- Primordial Ruby
  173.  
  174.         [125557]= { skill= L.Tailoring,         CD=   MIDNIGHT },       -- Imperial Silk
  175.        
  176.         [112996]= { skill= L.Inscription,       CD=   MIDNIGHT },       -- Scroll of Wisdom    
  177.  
  178. }
  179.  
  180. local watchedIndex, watchedSpell, watchedID, watchedIcon
  181. local orgDoTradeSkill = _G.DoTradeSkill
  182.  
  183. function DoTradeSkill(index, ...)
  184.         watchedID = tonumber( (GetTradeSkillRecipeLink(index)):match"enchant:(%d+)" )
  185.         if watchedCDs[watchedID] then
  186.                 watchedIndex, watchedSpell, watchedIcon = index, GetTradeSkillInfo(index), GetTradeSkillIcon(index)
  187.                 f:RegisterEvent"UNIT_SPELLCAST_SUCCEEDED"
  188.                 f:RegisterEvent"UNIT_SPELLCAST_STOP"
  189.         end
  190.         return orgDoTradeSkill(index, ...)
  191. end
  192.  
  193.  
  194. local function CheckReagents(...)
  195.         local maxQuantity = 999
  196.         for i=1, select("#",...) do
  197.                 local reagent = select(i,...)
  198.                 local item, count = reagent:match"(.+).%((%d+)%)$" --TEXT_MODE_A_STRING_VALUE_TYPE "%s (%s)"
  199.                 if not count then item, count = reagent, 1 end
  200.                 local quantity = GetItemCount(item) / count
  201.                 if quantity < maxQuantity then maxQuantity = quantity end
  202.                 if maxQuantity == 0 then return 0 end
  203.         end
  204.         return floor(maxQuantity)
  205. end
  206.  
  207. local function GetMaxFeasible(spellID)
  208.         local quantity = 0
  209.         scanner:ClearLines()
  210.         scanner:SetHyperlink("spell:"..spellID)
  211.         for i=2, scanner:NumLines() do
  212.                 local text = _G[("ABTS_GTTextLeft%i"):format(i)]:GetText()
  213.                 if text:find(fmtReagents) then
  214.                         text = text:sub(#fmtReagents+1)
  215.                         quantity = CheckReagents( (","):split( text:gsub(", ",","), 8 ) )
  216.                         break
  217.                 end
  218.         end
  219.         return quantity
  220. end
  221.  
  222. function f:UNIT_SPELLCAST_STOP(event, unit, spell)
  223.         if unit ~= "player" or spell ~= watchedSpell then return end
  224.         f:UnregisterEvent"UNIT_SPELLCAST_SUCCEEDED"
  225.         f:UnregisterEvent"UNIT_SPELLCAST_STOP"
  226.         if event ~= "UNIT_SPELLCAST_SUCCEEDED" then return end
  227.         local cd, ownGroup = watchedCDs[watchedID]
  228.         local duration = cd.CD
  229.         if duration < 0 then -- reset after X midnight
  230.                 local hours, minutes = GetGameTime()
  231.                 duration = (-duration-1)*DAY + (24-hours)*HOUR - minutes*60 - 30
  232.         end
  233.         char[watchedID] = time() + duration
  234.         if cd.group then
  235.                 ownGroup = cd.group .. "-" .. playerName
  236.                 config.names[ownGroup] = watchedID
  237.         end
  238.         config.icons[ownGroup or watchedID] = watchedIcon
  239.         if config.displayCDs then e.timer = 0 end
  240.         if f:IsShown() then UpdateTablet() end
  241. end
  242. f.UNIT_SPELLCAST_SUCCEEDED = f.UNIT_SPELLCAST_STOP
  243.  
  244. local new, del
  245.  
  246. do
  247.         local tables = setmetatable( {}, { __mode = "k" } )
  248.  
  249.         new = function(...)
  250.                 local t = next(tables)
  251.                 if t then tables[t] = nil else t = {} end
  252.                 for i = 1, select("#", ...), 2 do
  253.                         local k, v = select(i, ...)
  254.                         t[k] = v
  255.                 end
  256.                 return t
  257.         end
  258.  
  259.         new1 = function(...)
  260.                 local t = next(tables)
  261.                 if t then tables[t] = nil else t = {} end
  262.                 for i = 1, select("#", ...) do t[i] = select(i,...) end
  263.                 return t
  264.         end
  265.  
  266.         del = function(t)
  267.                 tables[wipe(t)] = true
  268.         end
  269.  
  270. end
  271.  
  272. local getProfessions
  273. do
  274.         local p = {}
  275.         getProfessions = function()
  276.                 del(p)
  277.                 p = new1( GetProfessions() )
  278.                 return p
  279.         end
  280. end
  281.  
  282. local md, mx, xx, dd = 20*HOUR, 3*DAY+20*HOUR, { DAY, HOUR, 60, 1 }, { L.d, L.h, L.m, L.s }
  283. local function SecondsToTime(s)
  284.         local p = s<md and s/md*.5 or s<mx and (s-md)/(mx-md)*.5+.5 or 1
  285.         local i = s>=DAY and 1 or s>=HOUR and 2 or 3
  286.         return ("|cff%.2x%.2x40%i%s %.2i%s|r"):format(
  287.                 p<.5 and 64+p*382 or 255, p<.5 and 255 or 446-p*382,
  288.                 floor(s/xx[i]), dd[i], floor((s%xx[i])/xx[i+1]), dd[i+1] )
  289. end
  290.  
  291. local function ColorCurMax(cur,max,isProfession)
  292.         local pMax = isProfession and min(max/MAX_SKILL_VALUE,1) or 1
  293.         local pCur = isProfession and 1-min(sqrt(max-cur)/10,1) or 2/sqrt(max-cur+4)
  294.         return ("|cff%.2x%.2x00%.3i|r / |cff%.2x%.2x00%.3i|r"):format(
  295.                 pCur<.5 and 255 or 510-pCur*510, pCur<.5 and pCur*510 or 255, cur,
  296.                 pMax<.5 and 255 or 510-pMax*510, pMax<.5 and pMax*510 or 255, max )
  297. end
  298.  
  299. local function SortSkills(a, b)
  300.         if not a.isProfession == not b.isProfession then
  301.                 if a.primary == b.primary then
  302.                         return a.displayName < b.displayName
  303.                 else    return a.primary end
  304.         else return a.isProfession end
  305. end
  306.  
  307. local function SortCooldowns(a, b)
  308.         if a.group and a.group == b.group then
  309.                 return a.timeLeft > b.timeLeft
  310.         else    return a.name < b.name end
  311. end
  312.  
  313. local function SortShortcuts(a, b)
  314.         if a.skill == b.skill then
  315.                 return a.name < b.name
  316.         else    return a.skill < b.skill end
  317. end
  318.  
  319. local function SortAltSkills(a, b)
  320.         local primA = bit_band( skills[a] or SECONDARY, SECONDARY ) == 0
  321.         local primB = bit_band( skills[b] or SECONDARY, SECONDARY ) == 0
  322.         if primA == primB then
  323.                 return a < b
  324.         else    return primA end
  325. end
  326.  
  327. local function SortAlts(a, b)
  328.         return a.name < b.name
  329. end
  330.  
  331.  
  332. local function UpdateText()
  333.         local skill = char.track and char.trackedSkill ~= AUTO and char.trackedSkill or char.skill
  334.         f.block.icon = skill and select(3, GetSpellInfo( skills[ altNames[skill] ] / SPELL_ID ) ) or "Interface\\Minimap\\Tracking\\Profession"
  335.         if char.track and skill then
  336.                 f.block.text = ColorCurMax( char.curSkills[skill], char.maxSkills[skill], bit_band( skills[skill], PROFESSION ) > 0 )
  337.         elseif not config.displayCDs then
  338.                 f.block.text = char.skill or _G.TRADE_SKILLS
  339.         end
  340. end
  341.  
  342. local orgAbandonSkill = _G.AbandonSkill
  343. function AbandonSkill(...)
  344.         if char.skill == GetProfessionInfo(...) then
  345.                 char.skill = nil
  346.                 SetTracking()
  347.         end
  348.         return orgAbandonSkill(...)
  349. end
  350.  
  351. local highlight = g:CreateTexture()
  352. highlight:SetTexture"Interface\\QuestFrame\\UI-QuestLogTitleHighlight"
  353. highlight:SetBlendMode"ADD"
  354. highlight:SetAlpha(0)
  355.  
  356. local function ShowBlockHints()
  357.         if config.hideTips or not f:IsShown() then return end
  358.         tip:SetOwner(f, "ANCHOR_NONE")
  359.         local showRight = UIParent:GetWidth()/config.scale-f:GetLeft() < f:GetRight() and "LEFT" or "RIGHT"
  360.         local showBelow = UIParent:GetHeight()/config.scale-f:GetTop() < f:GetBottom() and "TOP" or "BOTTOM"
  361.         tip:SetPoint(showBelow..showRight, f, (showBelow == "TOP" and "BOTTOM" or "TOP")..showRight )
  362.         tip:AddLine(L["Hints [|cffffffffBlock|r]"])
  363.         tip:AddLine(L["|cffff8020Click|r to toggle panel."], 0.2, 1, 0.2)
  364.         tip:AddLine(L["|cffff8020Right-Click|r to show config menu."], 0.2, 1, 0.2)
  365.         tip:Show()
  366. end
  367.  
  368. local function Block_OnEnter(frame)
  369.         CloseDropDownMenus()
  370.         f:Show()
  371.         f.onBlock = true
  372.         f:ClearAllPoints()
  373.         local showBelow = select(2, frame:GetCenter()) > UIParent:GetHeight() / 2
  374.         f:SetPoint(showBelow and "TOP" or "BOTTOM", frame, showBelow and "BOTTOM" or "TOP")
  375.         highlight:SetVertexColor(unpack(colors.highlight))
  376.         if Skinner then
  377.                 Skinner:applySkin(f)
  378. --      else
  379. --              f:SetBackdropColor( unpack( colors.background ) )
  380. --              f:SetBackdropBorderColor( unpack( colors.border ) )
  381.         end
  382.         UpdateTablet()
  383.         ShowBlockHints()
  384. end
  385.  
  386. local function IsMouseOut()
  387.         return not (f.onBlock or f:IsMouseOver() or g:IsShown() and g:IsMouseOver() or h:IsShown() and h:IsMouseOver())
  388. end
  389.  
  390. local function Block_OnLeave(frame)
  391.         f.onBlock = nil
  392.         tip:Hide()
  393.         if IsMouseOut() then f:Hide() end
  394. end
  395.  
  396. --local function ToggleSFX()
  397. --      if not config.silent then return end
  398. --      if not f.soundFlag then
  399. --              f.soundFlag = GetCVar"Sound_EnableSFX"
  400. --              SetCVar("Sound_EnableSFX", 0)
  401. --      else
  402. --              SetCVar("Sound_EnableSFX", f.soundFlag)
  403. --              f.soundFlag = nil
  404. --      end
  405. --end
  406.  
  407. --local function CloseTradeSkill()
  408. --      ToggleSFX()
  409. --      _G.CloseTradeSkill()
  410. --      ToggleSFX()
  411. --end
  412.  
  413.  
  414. -- actions:     scan, link, craft, point, open (toggle)
  415. -- args:        spellID (required for: craft, point), craftAll (option for: craft (shortcuts))
  416. local function CastSpellByName(skillName)
  417. --      ToggleSFX()
  418.         if f.action == "point" or f.craftAll or f.action ~= "open" and TradeSkillFrame and TradeSkillFrame:IsVisible() then
  419.                 f.keepOpen = true
  420.         --      if f.action == "point" then print("action = point") end -- DEBUG
  421.                 CloseTradeSkill()
  422.         end
  423.         f:RegisterEvent"TRADE_SKILL_SHOW"
  424.         _G.CastSpellByName(skillName)
  425. --      if f.action == "point" then print("action blanked") end -- DEBUG
  426.         prevAltSkill, f.action, f.spellID, f.craftAll, f.keepOpen = nil
  427. --      ToggleSFX()
  428. end
  429.  
  430.  
  431. local function Tradeskill_OnClick(button, click)
  432.         if IsShiftKeyDown() and bit_band( skills[button.skill], LINK ) > 0 then
  433.                 f.action = "link"
  434.         elseif click == "RightButton" then
  435.                 if bit_band( skills[button.skill], PANEL ) == 0 then return end
  436.                 f.action = "scan"
  437.         elseif click == "MiddleButton" then
  438.                 char.show[button.skill] = not char.show[button.skill]
  439.                 return UpdateTablet()
  440.         else
  441.                 local hasPanel = bit_band( skills[button.skill], PANEL ) > 0
  442.  
  443.                 if (char.track and char.trackedSkill == AUTO or hasPanel) and button.skill ~= L.Runeforging then
  444.                         char.skill = button.skill
  445.                         UpdateText()
  446.  
  447.                         button.fontLeft:SetTextColor(unpack(colors.selected))
  448.                         if f.selected and f.selected ~= button then
  449.                                 local skill = skills[f.selected.skill]
  450.                                 local color = skill and bit_band( skill, PANEL ) == 0 and colors.infoName or colors.activeName
  451.                                 f.selected.fontLeft:SetTextColor(unpack(color))
  452.                         end
  453.                         f.selected = button
  454.                 end
  455.                 if not hasPanel then return end
  456.                 f.action = "open"
  457.         end
  458.         CastSpellByName(button.skill)
  459. end
  460.  
  461. local function Cooldown_OnClick(button, click)
  462.         local group = watchedCDs[button.spellID].group
  463.         if click == "MiddleButton" then
  464.                 local char = realm[button.owner]
  465.                 if group then -- remove group
  466.                         for k, v in next, watchedCDs do
  467.                                 if v.group == group then char[k] = nil end
  468.                         end
  469.                 else
  470.                         char[button.spellID] = nil
  471.                 end
  472.                 UpdateTablet()
  473.         elseif button.skill == "Item" then
  474.                 return
  475.         elseif click == "LeftButton" and IsControlKeyDown() then
  476.                 f.alias, f.aliasID = button.fontLeft:GetText():gsub( " %[%d+%]", "" ), group or button.spellID
  477.                 StaticPopup_Show("ABTS_SET_ALIAS", config.names[button.spellID] or group)
  478.         else
  479.                 f.action = (button.ready and button.hasReagents and click == "LeftButton" and not IsModifierKeyDown()) and "craft" or "point"
  480.                 f.spellID = button.spellID
  481.                 CastSpellByName(button.skill)
  482.         end
  483. end
  484.  
  485. local function Shortcut_OnClick(button, click)
  486.         if IsControlKeyDown() then
  487.                 f.alias, f.aliasID = button.fontLeft:GetText(), button.spellID
  488.                 StaticPopup_Show("ABTS_SET_ALIAS", config.names[button.spellID])
  489.         elseif click == "MiddleButton" then
  490.                 char.shortcuts[button.spellID] = nil
  491.                 UpdateTablet()
  492.         else
  493.                 f.action = (click ~= "RightButton" and button.hasReagents) and "craft" or "point"
  494.                 f.craftAll = click ~= "LeftButton" and click ~= "RightButton" or IsShiftKeyDown()
  495.                 f.spellID = button.spellID
  496.                 CastSpellByName(button.skill)
  497.         end
  498. end
  499.  
  500. local function Link_OnClick(button, click)
  501.         if not button.link then return end
  502.         if IsModifierKeyDown() then
  503.                 if not ChatEdit_InsertLink(button.link) then
  504.                         ChatEdit_GetLastActiveWindow():Show()
  505.                         ChatFrame_OpenChat(" "..button.link)
  506.                 end
  507.         elseif click == "LeftButton" then
  508.                 if prevAltSkill == button.owner..button.skill and TradeSkillFrame and TradeSkillFrame:IsVisible() then
  509.                         return CloseTradeSkill()
  510.                 end
  511.                 prevAltSkill = button.owner..button.skill
  512.         --      ToggleSFX()
  513.                 SetItemRef(button.link:match"|H([^|]+)")
  514.         --      ToggleSFX()
  515.         end
  516. end
  517.  
  518. local function ProfessionsBook_OnClick()
  519.         ProfessionsBook:ShowFrame()
  520. end
  521.  
  522. local function Alt_OnEnter(self)
  523.         if self and self.light then
  524.                 highlight:SetAllPoints(self)
  525.                 highlight:SetAlpha(1)
  526.  
  527.                 if config.hideTips then return end
  528.                 local hasLink = bit_band( skills[self.skill], LINK ) > 0
  529.                 if not( self.link or hasLink ) then return end
  530.  
  531.                 tip:SetOwner(g, "ANCHOR_NONE")
  532.                 local showRight = f:GetCenter() > UIParent:GetWidth()/config.scale/2 and "RIGHT" or "LEFT"
  533.                 local showBelow = select(2, g:GetCenter()) > UIParent:GetHeight()/config.scale/2 and "TOP" or "BOTTOM"
  534.                 tip:SetPoint(showBelow..showRight, self:GetParent(), (showBelow == "TOP" and "BOTTOM" or "TOP")..showRight )
  535.  
  536.                 tip:AddLine(L.Hints)
  537.                 if self.link then
  538.                         tip:AddLine(L["|cffff8020Click|r to toggle panel."], .2,1,.2)
  539.                         tip:AddLine(L["|cffff8020Shift+Click|r to link in chat."], .2,1,.2)
  540.                 elseif hasLink then
  541.                         tip:AddLine(L["No link available. Open a character\ntradeskill panel to be able to access\nit from your other characters."], 1, .6, .2)
  542.                 end
  543.                 tip:Show()
  544.         end
  545. end
  546.  
  547.  
  548. local function Alt_OnLeave(self)
  549.         highlight:ClearAllPoints()
  550.         if self and self.light then
  551.                 highlight:SetAlpha(0)
  552.                 tip:Hide()
  553.         end
  554.         if IsMouseOut() then f:Hide() end
  555. end
  556.  
  557. local function Menu_OnEnter(self)
  558.         if self and self.light then
  559.                 highlight:SetAllPoints(self)
  560.                 highlight:SetAlpha(1)
  561.                 if config.hideTips or self.skill == "None" then return end
  562.                 tip:SetOwner(f, "ANCHOR_NONE")
  563.                 local showRight = f:GetCenter() > UIParent:GetWidth()/config.scale/2 and "LEFT" or "RIGHT"
  564.                 local showBelow = select(2, f:GetCenter()) > UIParent:GetHeight()/config.scale/2 and "TOP" or "BOTTOM"
  565.                 tip:SetPoint(showBelow..showRight, f, (showBelow == "TOP" and "BOTTOM" or "TOP")..showRight )
  566.                 tip:AddLine(L.Hints)
  567.                 if self.shortcut then
  568.                         tip:AddLine(L["|cffff8020Click|r to craft."], .2,1,.2)
  569.                         tip:AddLine(L["|cffff8020Shift+Click|r to craft all."], .2,1,.2)
  570.                         tip:AddLine(L["|cffff8020Control+Click|r to set an alias."], .2,1,.2)
  571.                         tip:AddLine(L["|cffff8020Right-Click|r to show in panel."], .2,1,.2) -- "to show craft" would be better
  572.                 elseif self.spellID then
  573.                         tip:AddDoubleLine(L["Belongs to:"], self.owner, 1,1,1,1,1,1)
  574.                         if self.skill ~= "Item" then
  575.                                 if self.owner == playerName and self.ready then
  576.                                         tip:AddLine(L["|cffff8020Click|r to craft."], .2,1,.2)
  577.                                         tip:AddLine(L["|cffff8020Right-Click|r to show CD in panel."], .2,1,.2)
  578.                                 elseif char.show[watchedCDs[self.spellID].skill] ~= nil then
  579.                                         tip:AddLine(L["|cffff8020Click|r to show CD in panel."], .2,1,.2) -- "to show CD"
  580.                                 end
  581.                                 tip:AddLine(L["|cffff8020Control+Click|r to set an alias."], .2,1,.2)
  582.                         end
  583.                 else
  584.                         local hasPanel = bit_band( skills[self.skill], PANEL ) > 0
  585.                         local hasLink = bit_band( skills[self.skill], LINK ) > 0
  586.                         if hasPanel then tip:AddLine(L["|cffff8020Click|r to toggle panel."], .2,1,.2) end
  587.                         if hasLink then tip:AddLine(L["|cffff8020Shift+Click|r to link in chat."], .2,1,.2) end
  588.                         if hasPanel then tip:AddLine(L["|cffff8020Right-Click|r to scan for CDs."], .2,1,.2)
  589.                         elseif char.track and char.trackedSkill == AUTO then tip:AddLine(L["|cffff8020Click|r to track."], .2, 1, .2) end
  590.                 end
  591.                 tip:AddLine(L["|cffff8020Middle-Click|r to remove from list."], .2,1,.2)
  592.                 tip:Show()
  593.         end
  594. end
  595.  
  596. local function Menu_OnLeave(self)
  597.         highlight:ClearAllPoints()
  598.         if self and self.light then
  599.                 highlight:SetAlpha(0)
  600.                 tip:Hide()
  601.         end
  602.         if IsMouseOut() then f:Hide() end
  603. end
  604.  
  605. local function OnMouseWheel(self, delta)
  606.         if not IsControlKeyDown() then return end
  607.         config.scale = config.scale - delta * 0.05
  608.         UpdateTablet()
  609. end
  610.  
  611. local metatable = { __index = function(table, key)
  612.         local button = CreateFrame("Button", nil, workingFrame)
  613.         rawset(table, key, button)
  614.         button:RegisterForClicks"AnyUp"
  615.         button:SetHeight(BUTTON_HEIGHT)
  616.         button:SetPoint("TOPLEFT", workingFrame, "TOPLEFT", GAP, BUTTON_HEIGHT * (1-key) - GAP)
  617.         button:SetScript("OnEnter", workingFrame == f and Menu_OnEnter or Alt_OnEnter)
  618.         button:SetScript("OnLeave", workingFrame == f and Menu_OnLeave or Alt_OnLeave)
  619.         button:SetScript("OnMouseWheel", OnMouseWheel)
  620.         button.icon = button:CreateTexture(nil, "OVERLAY")
  621.         button.icon:SetWidth(ICON_SIZE) button.icon:SetHeight(ICON_SIZE)
  622.         button.icon:SetPoint("LEFT", button, "LEFT")
  623.         button.fontLeft = button:CreateFontString(nil, "OVERLAY", "SystemFont_Shadow_Med1")
  624.         button.fontLeft:SetPoint("LEFT", button, "LEFT", ICON_SIZE + TEXT_OFFSET, 0)
  625.         button.fontRight = button:CreateFontString(nil, "OVERLAY", "SystemFont_Shadow_Med1")
  626.         button.fontRight:SetPoint("RIGHT", button, "RIGHT")
  627.         return button
  628. end }
  629.  
  630. local buttons = setmetatable( {}, metatable )
  631. local altButtons = setmetatable( {}, metatable )
  632. local altButtons2 = setmetatable( {}, metatable )
  633.  
  634. local function AddEntry( buttons, icon, leftText, leftTextColor, rightText, rightTextColor, highlight )
  635.         nbEntries = nbEntries + 1
  636.         local button = buttons[nbEntries]
  637.         button.icon:SetTexture(icon or "")
  638.         if icon then button.icon:SetTexCoord(0.075, 0.925, 0.075, 0.925) end
  639.         button.fontLeft:SetText(leftText or "")
  640.         if leftTextColor then button.fontLeft:SetTextColor(unpack(leftTextColor)) end
  641.         button.fontRight:SetText(rightText or "")
  642.         if rightTextColor then button.fontRight:SetTextColor(unpack(rightTextColor)) end
  643.         button.light = highlight
  644.         button:Show()
  645.         return button, ICON_SIZE + TEXT_OFFSET + (leftText and button.fontLeft:GetStringWidth() or 0), rightText and button.fontRight:GetStringWidth() or 0
  646. end
  647.  
  648.  
  649. local function UpdateCDs()
  650.         local readyCDs = 0
  651.         local temp = new()
  652.         local dups = new()
  653.         local now = time()
  654.  
  655.         for i, v in next, CDs do
  656.                 del(v)
  657.                 CDs[i] = nil
  658.         end
  659.  
  660.         for char, data in next, realm do
  661.                 for spellID, expireTime in next, data do
  662.                         if type(spellID)=="number" then
  663.                                 local cd = watchedCDs[spellID]
  664.                                 if cd then
  665.                                         temp[#temp+1] = new(
  666.                                                 "name", cd.group and (config.aliases[cd.group] or cd.group) or
  667.                                                         config.aliases[spellID] or
  668.                                                         config.names[spellID] or
  669.                                                         GetSpellInfo(spellID),
  670.                                                 "timeLeft", expireTime-now,
  671.                                                 "char", char,
  672.                                                 "spellID", spellID,
  673.                                                 "skill", cd.skill,
  674.                                                 "group", cd.group,
  675.                                                 "ownGroup", cd.group and cd.group.."-"..char or nil)
  676.                                 end
  677.                         end
  678.                 end
  679.         end
  680.         sort(temp, SortCooldowns)
  681.  
  682.         for i=1, #temp do
  683.                 local cd = temp[i]
  684.                 if not dups[cd.ownGroup] then
  685.                         CDs[#CDs+1] = cd
  686.                         temp[i] = nil
  687.                         if cd.timeLeft <= 0 then readyCDs = readyCDs + 1 end
  688.                 end
  689.                 if cd.group then
  690.                         dups[cd.ownGroup] = true
  691.                 end
  692.         end
  693.  
  694.         for i, v in next, temp do
  695.                 if v then
  696.                         del(v)
  697.                         temp[i] = nil
  698.                 end
  699.         end
  700.         del(temp)
  701.         del(dups)
  702.  
  703.         if config.displayCDs and not char.track then
  704.                 f.block.text = (" %i/%i CD|4:s;"):format(readyCDs, #CDs)
  705.         end
  706. end
  707.  
  708. SetTracking = function (self, name, dontUpdate)
  709.         char.track = name ~= nil
  710.         if name then char.trackedSkill = name end
  711.         if not char.track then
  712.                 f:UnregisterEvent"CHAT_MSG_SKILL"
  713.                 f:UnregisterEvent"CHAT_MSG_SYSTEM"
  714.                 if dontUpdate then return end
  715.                 if config.displayCDs then UpdateCDs() else UpdateText() end
  716.         else
  717.                 if name ~= AUTO then char.skill = name end
  718.                 f:RegisterEvent"CHAT_MSG_SKILL"
  719.                 f:RegisterEvent"CHAT_MSG_SYSTEM"
  720.                 if not dontUpdate then UpdateText() end
  721.         end
  722. end
  723.  
  724.  
  725. local function ShowAlts()
  726.         workingFrame = g
  727.         local buttons = altButtons
  728.         nbEntries = 0
  729.         local firstEntry, button = true
  730.         local itemLeftWidth, itemRightWidth, colRightWidth, colLeftWidth = 0, 0, 0, 0
  731.         local list, subList = new(), new()
  732.  
  733.         for charName, charData in next, realm do
  734.                 if charName ~= playerName and charData.curSkills and next(charData.curSkills) then
  735.                         list[#list+1] = new("name",charName,"curSkills",charData.curSkills,"maxSkills",charData.maxSkills,"links",charData.links)
  736.                 end
  737.         end
  738.         sort(list,SortAlts)
  739.  
  740.         local maxEntries = type(config.breakMode)=="number" and 0 or floor( (UIParent:GetHeight()/config.scale -GAP*2) / BUTTON_HEIGHT - (config.hideTips and 3 or 6) / config.scale )
  741.         local newColAt = 0
  742.         for i, char in ipairs(list) do
  743.                 local firstCharEntry = true
  744.                 for skill, value in next, char.maxSkills do
  745.                         if value > 1 then subList[#subList+1] = skill end
  746.                 end
  747.                 sort(subList, SortAltSkills)
  748.                 if maxEntries == 0 and (i>config.breakMode) or maxEntries ~= 0 and (nbEntries+#subList+1 > maxEntries) then
  749.                         maxEntries, nbEntries, newColAt, workingFrame, buttons, firstEntry = 99, 0, nbEntries, h, altButtons2, true
  750.                 end
  751.                 for _, skill in ipairs(subList) do
  752.                         local bitSkill = skills[skill]
  753.                         if bitSkill and
  754.                                 (not config.panelOnly or bit_band(bitSkill, LINK) > 0) and
  755.                                 (not config.primaryOnly or bit_band(bitSkill, SECONDARY) == 0)
  756.                         then
  757.                                 if firstCharEntry then
  758.                                         if firstEntry then firstEntry=false else AddEntry(buttons) end
  759.                                         AddEntry(buttons, "", char.name, colors.header)
  760.                                 end
  761.                                 firstCharEntry = false
  762.  
  763.                                 local hasLink = config.panelOnly or bit_band(bitSkill, LINK) > 0
  764.                                 local color = hasLink and colors.activeName or colors.infoName
  765.                                 local icon = select(3,GetSpellInfo(floor(bitSkill/SPELL_ID)))
  766.                                 local strValues = ColorCurMax( char.curSkills[skill], char.maxSkills[skill], true )
  767.  
  768.                                 button, itemLeftWidth, itemRightWidth = AddEntry( buttons,
  769.                                         icon, revAlt[skill], color, strValues, nil, hasLink)
  770.                                 button:SetScript("OnClick", Link_OnClick)
  771.                                 button.link = char.links and char.links[skill]
  772.                                 button.skill, button.owner = skill, char.name
  773.                                 if itemLeftWidth > colLeftWidth then colLeftWidth = itemLeftWidth + GAP end
  774.                                 if itemRightWidth > colRightWidth then colRightWidth = itemRightWidth end
  775.                         end
  776.                 end
  777.                 wipe(subList)
  778.         end
  779.         del(list)
  780.         del(subList)
  781.         g:Show()
  782.         if newColAt > 0 then h:Show() else h:Hide() newColAt = nbEntries end
  783.         if _G.Skinner then
  784.                 _G.Skinner:applySkin(g)
  785.                 if newColAt ~= nbEntries then _G.Skinner:applySkin(h) end
  786.         end
  787.  
  788.         local horiz = f:GetCenter() > UIParent:GetWidth()/config.scale/2 and "RIGHT" or "LEFT"
  789.         local verti = (f:GetPoint()=="TOP"and f:GetTop() or f:GetBottom()) > UIParent:GetHeight()*.5 and "TOP" or "BOTTOM"
  790.  
  791.         local maxWidth = colLeftWidth + colRightWidth
  792.  
  793.         g:SetWidth( maxWidth + GAP*2 )
  794.         g:SetHeight(BUTTON_HEIGHT * newColAt + GAP*2)
  795.  
  796.         g:ClearAllPoints()
  797.         g:SetPoint(verti..horiz, f, verti..(horiz=="LEFT"and"RIGHT"or"LEFT"))
  798.  
  799.         for i, bt in next, altButtons do
  800.                 if i<=newColAt then bt:SetWidth(maxWidth) else bt:Hide() end
  801.         end
  802.         if newColAt ~= nbEntries then
  803.                 for i, bt in next, altButtons2 do
  804.                         if i <= nbEntries then bt:SetWidth(maxWidth) else bt:Hide() end
  805.                 end
  806.                 h:SetWidth( maxWidth + GAP*2 )
  807.                 h:SetHeight(BUTTON_HEIGHT * nbEntries + GAP*2)
  808.                 h:ClearAllPoints()
  809.                 h:SetPoint(verti..horiz, g, verti..(horiz=="LEFT"and"RIGHT"or"LEFT"))
  810.         end
  811.  
  812.         if nbEntries == 0 then g:Hide() end
  813. end
  814.  
  815.  
  816. UpdateTablet = function(self)
  817.         workingFrame = f
  818.  
  819.         f:SetScale(config.scale)
  820.         nbEntries = 0
  821.  
  822.         local button, selectedSkillExists
  823.         local itemLeftWidth, itemRightWidth, colRightWidth, colLeftWidth, skillWidth, cdWidth = 0, 0, 0, 0
  824.  
  825.         UpdateCDs()
  826.         local showCDs, showProfessions, showCombatSkills, showShortcuts = min(#CDs,1), 0, 0, next(char.shortcuts) and 1 or 0
  827.         for k,v in next,f.skills do del(v) f.skills[k]=nil end
  828.  
  829. --      ExpandSkillHeader(0)
  830. --      TradeSkillCollapseAllButton:Click()
  831.         for _, i in next, getProfessions() do
  832.                 local displayName, icon, value, valueMax = GetProfessionInfo(i)
  833.                 if displayName then
  834.                         local skillName = altNames[displayName]
  835.                         local skill = skills[skillName]
  836.                         if skill then
  837.                                 local hasPanel, isProfession = bit_band(skill,PANEL)>0, bit_band(skill,PROFESSION)>0
  838.                                 if char.show[skillName] == nil then char.show[skillName] = hasPanel end
  839.                                 local show = (not isProfession or not char.hideProfessions) and char.show[skillName]
  840.                                 if show then if isProfession then showProfessions=1 else showCombatSkills=1 end end
  841.                                 f.skills[#f.skills+1] = new(
  842.                                         "displayName",  displayName,
  843.                                         "skillName",    skillName,
  844.                                         "isProfession", isProfession and floor(skill/SPELL_ID),
  845.                                         "hasPanel",     hasPanel,
  846.                                         "color",        skillName==char.skill and colors.selected or hasPanel and colors.activeName or colors.infoName,
  847.                                         "strValue",     valueMax <= 1 and "" or ColorCurMax(value,valueMax,isProfession),
  848.                                         "show",         show,
  849.                                         "primary",      bit_band(skill,SECONDARY) == 0)
  850.                                 if isProfession then char.curSkills[skillName], char.maxSkills[skillName] = value, valueMax end
  851.                         end
  852.                 end
  853.         end
  854.         if GetSpellInfo(L.Runeforging) then
  855.                 local skillName = L.Runeforging
  856.                 if char.show[skillName] == nil then char.show[skillName] = true end
  857.                 f.skills[#f.skills+1] = new(
  858.                         "displayName",  altNames[L.RuneforgingDN] or skillName,
  859.                         "skillName",    skillName,
  860.                         "isProfession", 53428,
  861.                         "hasPanel",     true,
  862.                         "color",        skillName==char.skill and colors.selected or colors.activeName,
  863.                         "strValue",     "",
  864.                         "show",         not char.hideProfessions and char.show[skillName],
  865.                         "primary",      false)
  866.         end
  867.  
  868.         local nbHeaders = showProfessions + showCombatSkills + showCDs + showShortcuts
  869.  
  870.         sort(f.skills, SortSkills)
  871.         for i, v in ipairs(f.skills) do
  872.                 if v.show then
  873.                         if nbEntries == 0 and showProfessions == 1 and nbHeaders > 1 then
  874.                                 AddEntry(buttons, "", _G.TRADE_SKILLS, colors.header)
  875.                         end
  876.                         local texture = select(3,GetSpellInfo(v.isProfession)) or ""
  877.                         button, itemLeftWidth, itemRightWidth = AddEntry( buttons,
  878.                                 texture, v.displayName, v.color, v.strValue,
  879.                                 nil, true)
  880.                         button:SetScript("OnClick", Tradeskill_OnClick)
  881.                         button.skill, button.spellID, button.shortcut = v.skillName
  882.                         if v.color == colors.selected then f.selected = button end
  883.                         if itemLeftWidth > colLeftWidth then colLeftWidth = itemLeftWidth end
  884.                         if itemRightWidth > colRightWidth then colRightWidth = itemRightWidth end
  885.                 end
  886.         end
  887.         skillWidth = colLeftWidth + colRightWidth
  888.         colRightWidth, colLeftWidth = 0, 0
  889.  
  890.         -- Cooldowns
  891.         for index, data in ipairs(CDs) do
  892.                 local group = data.group
  893.                 if index == 1 then
  894.                         if nbEntries>0 then AddEntry(buttons) end
  895.                         if nbHeaders>1 then AddEntry(buttons, "", L.Cooldowns, colors.header) end
  896.                 end
  897.  
  898.                 local missingReagents = data.char == playerName and GetMaxFeasible(data.spellID) == 0
  899.  
  900.                 button, itemLeftWidth, itemRightWidth = AddEntry( buttons,
  901.                         config.icons[data.ownGroup or data.spellID] or
  902.                         config.names[data.ownGroup] and select(3,GetSpellInfo(config.names[data.ownGroup])) or
  903.                         groupIcons[group], data.name,
  904.                         data.char == playerName and colors.ownCD or colors.foreignCD,
  905.                         data.timeLeft<=0 and (missingReagents and L["|cffff4040Reagents!|r"] or L["|cff20ff20Ready!|r"]) or SecondsToTime(data.timeLeft),
  906.                         nil, true )
  907.                 button:SetScript("OnClick", Cooldown_OnClick)
  908.                 button.skill, button.spellID, button.ready, button.owner, button.hasReagents, button.shortcut = data.skill, group and config.names[data.ownGroup] or data.spellID, data.timeLeft<=0, data.char, not missingReagents
  909.                 if itemLeftWidth > colLeftWidth then colLeftWidth = itemLeftWidth end
  910.                 if itemRightWidth > colRightWidth then colRightWidth = itemRightWidth end
  911.         end
  912.         cdWidth = colLeftWidth + colRightWidth
  913.         colLeftWidth = 0
  914.  
  915.         -- Shortcuts
  916.         local shortcuts = new()
  917.         for spellID, skill in next, char.shortcuts do
  918.                 shortcuts[#shortcuts+1] = new(
  919.                         "skill", skill,
  920.                         "name", config.aliases[spellID] or config.names[spellID],
  921.                         "spellID", spellID)
  922.         end
  923.         sort(shortcuts, SortShortcuts)
  924.         local firstShortcut = true
  925.         for _, shortcut in ipairs(shortcuts) do
  926.                 if firstShortcut then
  927.                         firstShortcut = false
  928.                         if nbEntries>0 then AddEntry(buttons) end
  929.                         if nbHeaders>1 then AddEntry(buttons, "", L.Shortcuts, colors.header) end
  930.                 end
  931.  
  932.                 local quantity = GetMaxFeasible(shortcut.spellID)
  933.  
  934.                 button, itemLeftWidth = AddEntry( buttons,
  935.                         config.icons[shortcut.spellID],
  936.                         shortcut.name .. ( quantity==0 and " [|cffff4040" or " [|cffffffff" ) .. quantity .. "|r]",
  937.                         colors.activeName, nil, nil, true )
  938.                 if itemLeftWidth > colLeftWidth then colLeftWidth = itemLeftWidth end
  939.                 button:SetScript("OnClick", Shortcut_OnClick)
  940.                 button.skill, button.spellID, button.ready, button.shortcut, button.hasReagents = shortcut.skill, shortcut.spellID, true, true, quantity > 0
  941.         end
  942.         del(shortcuts)
  943.  
  944.         -- ProfessionsBook
  945.         if ProfessionsBook then
  946.                 if nbEntries>0 then AddEntry(buttons) end
  947.                 if nbHeaders>1 then AddEntry(buttons, "", L["Miscellaneous"], colors.header) end
  948.                 button, itemLeftWidth = AddEntry( buttons, "Interface\\Spellbook\\Spellbook-Icon", "ProfessionsBook", colors.activeName, nil, nil, true )
  949.                 button:SetScript("OnClick", ProfessionsBook_OnClick)
  950.                 button.skill, button.spellID, button.ready, button.shortcut = "None"
  951.         end
  952.  
  953.         local maxWidth = math.max(skillWidth, cdWidth, colLeftWidth, itemLeftWidth) + GAP
  954.  
  955.         for i, bt in next, buttons do
  956.                 if i<=nbEntries then bt:SetWidth(maxWidth) else bt:Hide() end
  957.         end
  958.         f:SetWidth(maxWidth + GAP*2)
  959.         f:SetHeight(BUTTON_HEIGHT * nbEntries + GAP*2)
  960.  
  961.         local isHidden = nbEntries == 0 or IsMouseOut()
  962.         if isHidden or config.hideAlts then
  963.                 if isHidden then f:Hide() end
  964.                 g:SetWidth(0)
  965.                 return h:SetWidth(0)
  966.         end
  967.         ShowAlts()
  968. end
  969.  
  970.  
  971. local function Block_OnClick(self, click)
  972.         if click == "LeftButton" then
  973.                 local skill = skills[char.skill]
  974.                 if skill and bit_band( skill, PANEL ) > 0 then f.action = "open"; CastSpellByName(char.skill) end
  975.         elseif click == "RightButton" then
  976.                 f:Hide() tip:Hide()
  977.                 if not configMenu then f:SetupConfigMenu() end
  978.                 configMenu.scale = UIParent:GetScale()
  979.                 ToggleDropDownMenu(1, nil, configMenu, self, 0, 0)
  980.         end
  981. end
  982.  
  983.  
  984. f.block = LibStub("LibDataBroker-1.1"):NewDataObject("|cFFFFB366Ara|r Tradeskills", {
  985.         type = "data source",
  986.         text = _G.TRADESKILLS,
  987.         icon = "",
  988.         iconCoords = { 0.075, 0.925, 0.075, 0.925 },
  989.         OnEnter = Block_OnEnter,
  990.         OnLeave = Block_OnLeave,
  991.         OnClick = Block_OnClick,
  992. } )
  993.  
  994.  
  995. function f:TRADE_SKILL_SHOW()
  996.         f:UnregisterEvent"TRADE_SKILL_SHOW"
  997.         if f.SetHooks then f:SetHooks() end
  998.         if IsTradeSkillLinked() then return end
  999.  
  1000.         char.links[ altNames[ GetTradeSkillLine() ] ] = GetTradeSkillListLink()
  1001.  
  1002.         local action = f.action
  1003.         if not action or action == "open" then return end
  1004.         if action == "link" then
  1005.                 local link = GetTradeSkillListLink()
  1006.                 if not ChatEdit_InsertLink(link) then
  1007.                         ChatEdit_GetLastActiveWindow():Show()
  1008.                         ChatFrame_OpenChat(" "..link)
  1009.                 end
  1010.                 if not f.keepOpen then CloseTradeSkill() end
  1011.                 return
  1012.         end
  1013.  
  1014.         ExpandTradeSkillSubClass(0)
  1015.         SetTradeSkillCategoryFilter(0,1,1)
  1016.         SetTradeSkillInvSlotFilter(0,1,1)
  1017.         TradeSkillOnlyShowMakeable(false)
  1018.         for i=1, GetNumTradeSkills() do
  1019.                 local itemName, itemType, quantity = GetTradeSkillInfo(i)
  1020.                 if itemType ~= "header" then
  1021.                         local spellID = tonumber( GetTradeSkillRecipeLink(i):match"enchant:(%d+)")
  1022.                         if spellID == f.spellID and (f.action == "point" or f.action == "craft") then
  1023.                                 TradeSkillFrame_SetSelection(i)
  1024.                                 if f.action == "craft" and not GetTradeSkillCooldown(i) then
  1025.                                         if f.craftAll then
  1026.                                                 TradeSkillInputBox:SetNumber(quantity)
  1027.                                                 TradeSkillCreateButton:Click()
  1028.                                         else
  1029.                                                 DoTradeSkill(i)
  1030.                                         end
  1031.                                 end
  1032.                                 TradeSkillListScrollFrame:SetVerticalScroll(max(i-5,0)*TradeSkillSkill1:GetHeight())
  1033.                                 TradeSkillFrame_Update()
  1034.                         --      if f.action == "point" then print("pointed --> done") end -- DEBUG
  1035.                                 break
  1036.                         end
  1037.                         local cd = watchedCDs[spellID]
  1038.                         if cd and f.action == "scan" then
  1039.                                 local timeLeft = GetTradeSkillCooldown(i) or 0
  1040.                                 if timeLeft>0 or config.addReadyCD then
  1041.                                         char[spellID] = time() + timeLeft
  1042.                                         if not cd.group then
  1043.                                                 config.icons[spellID] = GetTradeSkillIcon(i)
  1044.                                                 config.names[spellID] = itemName
  1045.                                         end
  1046.                                 end
  1047.                         end
  1048.                 end
  1049.         end
  1050.  
  1051.         if not f.keepOpen then CloseTradeSkill() end
  1052.         if f.action == "scan" and f:IsShown() then UpdateTablet() end
  1053. end
  1054.  
  1055.  
  1056. function f:SetAlias(alias)
  1057.         config.aliases[self.aliasID] = alias ~= "" and alias or nil
  1058.         if f:IsShown() then UpdateTablet()end
  1059. end
  1060.  
  1061. function f:PLAYER_LOGOUT()
  1062.         for _, i in next, getProfessions() do
  1063.                 local skillName, icon, value, valueMax, skill = GetProfessionInfo(i)
  1064.                 if skillName then
  1065.                         skill = skills[skillName]
  1066.                         if skill then
  1067.                                 if bit_band(skill,PROFESSION) == 0 then break end
  1068.                                 char.curSkills[skillName], char.maxSkills[skillName] = value, valueMax
  1069.                         end
  1070.                 end
  1071.         end
  1072. end
  1073.  
  1074. local function Error_OnHide()
  1075.         BasicScriptErrors:SetScript("OnHide", baseScript)
  1076.         StaticPopup_Show"ABTS_SET_SCALE"
  1077. end
  1078.  
  1079. ------------------------------[[  options  ]]------------------------------
  1080.  
  1081. local function DisplayCD_OnUpdate(self, elapsed)
  1082.         self.timer = self.timer - elapsed
  1083.         if self.timer > 0 then return end
  1084.         self.timer = 60
  1085.         UpdateCDs()
  1086. end
  1087.  
  1088. function f:SetupConfigMenu()
  1089.         configMenu = CreateFrame("Frame", "AraSkillsDD")
  1090.         configMenu.displayMode = "MENU"
  1091.  
  1092.         local function RemoveChar(self, char)
  1093.                 realm[char] = nil
  1094.                 print( "|cFFFFB366Ara|cffffffffBrokerTradeskills: "..(L["|cff8080ff%s|r has been removed."]):format(char) )
  1095.         end
  1096.  
  1097.         local function SetRadioOption(bt, var, val, checked)
  1098.                 config[var] = val
  1099.                 local p = bt:GetName():sub(1, 19)
  1100.                 for i = 1, bt:GetParent().numButtons do
  1101.                         if _G[p..i] == bt then
  1102.                                 _G[p..i.."Check"]:Show()
  1103.                         else
  1104.                                 _G[p..i.."Check"]:Hide()
  1105.                                 _G[p..i.."UnCheck"]:Show()
  1106.                         end
  1107.                 end
  1108.         end
  1109.  
  1110.         local function SetOption(self, t, v, c) t[v] = not t[v] end
  1111.         local function ColorPickerChange()      c[1], c[2], c[3] = ColorPickerFrame:GetColorRGB() end
  1112.         local function ColorPickerCancel(prev)  c[1], c[2], c[3] = unpack(prev) end
  1113.         local function OpenColorPicker(self, col)
  1114.                 c = colors[col]
  1115.                 ColorPickerFrame.func = ColorPickerChange
  1116.                 ColorPickerFrame.cancelFunc = ColorPickerCancel
  1117.                 ColorPickerFrame.previousValues = {unpack(c)}
  1118.                 ColorPickerFrame:SetColorRGB( c[1], c[2], c[3] )
  1119.                 ColorPickerFrame:Show()
  1120.         end
  1121.  
  1122.         options = {
  1123.                 { text = ("|cFFFFB366Ara|r Broker Tradeskills (%s)"):format( GetAddOnMetadata(addonName, "Version") ), isTitle = true },
  1124.                 { text = _G.TRADE_SKILLS, submenu = "isProfession", scope=char, var="hideProfessions", inv=true },
  1125.                 { text = L["Tracking..."], submenu = "track", checked=function() return char.track end, func=
  1126.                         function()
  1127.                                 char.track = not char.track
  1128.                                 SetTracking(nil, char.track and char.trackedSkill or nil)
  1129.                         end },
  1130.                 { text = L["Display number of Ready CD"], func=
  1131.                         function()
  1132.                                 config.displayCDs = not config.displayCDs
  1133.                                 e.timer = 0
  1134.                                 e:SetScript("OnUpdate", config.displayCDs and DisplayCD_OnUpdate or nil)
  1135.                                 if not config.displayCDs then return UpdateText() end
  1136.                         end, checked=function() return config.displayCDs end },
  1137.                 { text = L["Include |cff20ff20Ready!|r CDs when scanning"], scope=config, var="addReadyCD" },
  1138.         --      { text = L["Silently open/close panels"], scope=config, var="silent" },
  1139.                 { text = L["Show alt. skills"], scope=config, var="hideAlts", inv=true, submenu = {
  1140.                         { text = L["Primary only"], scope=config, var="primaryOnly" },
  1141.                         { text = L["With a craft window only"], scope=config, var="panelOnly" }, } },
  1142.                 { text = L["Alt list break mode"], submenu = {
  1143.                         { text = L.Auto, radio="breakMode", val="auto" },
  1144.                         { text = L["After 5th"], radio="breakMode", val=5 }, } },
  1145.                 { text = L["Remove an alt."], submenu="alts" },
  1146.                 { text = L["Tooltip size"], submenu = {
  1147.                         { text =  "90%", radio="scale", val = .9 },
  1148.                         { text = "100%", radio="scale", val = 1 },
  1149.                         { text = "110%", radio="scale", val = 1.1 },
  1150.                         { text = "120%", radio="scale", val = 1.2 },
  1151.                         { text = L["Custom..."], radio="scaleX", func=function() StaticPopup_Show"ABTS_SET_SCALE" end }, } },
  1152.                 { text = L.Colors, submenu = {
  1153.                         { text = L["Header"], color = "header" },
  1154.                         { text = L["Interactive skill name"], color = "activeName" },
  1155.                         { text = L["Last selected skill"], color = "selected" },
  1156.                         { text = L["Informative skill name"], color = "infoName" },
  1157.                         { text = L["Player CD"], color = "ownCD" },
  1158.                         { text = L["Other player CD"], color = "foreignCD" },
  1159.                         { text = L["Highlight"], color = "highlight" },
  1160.                         { text = "|cffaaaaff"..L["Restore default colors"], func=function() CloseDropDownMenus() for k, v in next, defaultColors do colors[k][1], colors[k][2], colors[k][3] = v[1], v[2], v[3] end end }, } },
  1161.                 { text = L["Show hints"], scope=config, var="hideTips", inv=true },
  1162.         }
  1163.         configMenu.initialize = function(self, level)
  1164.                 if not level then return end
  1165.                 local options = level>1 and UIDROPDOWNMENU_MENU_VALUE or options
  1166.                 if options == "alts" then
  1167.                         for char in next, realm do
  1168.                                 if char ~= playerName then
  1169.                                         info = wipe(info)
  1170.                                         info.text, info.notCheckable = char, true
  1171.                                         info.func, info.arg1 = RemoveChar, char
  1172.                                         UIDropDownMenu_AddButton(info, level)
  1173.                                 end
  1174.                         end
  1175.                         return
  1176.                 elseif options == "track" then
  1177.                         info = wipe(info)
  1178.                         info.text = L.Auto
  1179.                         info.func, info.arg1 = SetTracking, AUTO
  1180.                         info.checked = char.trackedSkill == AUTO
  1181.                         UIDropDownMenu_AddButton(info, level)
  1182.  
  1183.                         for i, s in ipairs(f.skills) do
  1184.                                 if s.isProfession then
  1185.                                         info = wipe(info)
  1186.                                         info.text = ("|cff%.2x%.2x%.2x%s|r"):format( s.color[1]*255, s.color[2]*255, s.color[3]*255, s.displayName )
  1187.                                         info.func, info.arg1 = SetTracking, s.skillName
  1188.                                         if char.trackedSkill ~= AUTO then
  1189.                                                 info.checked = s.skillName == char.trackedSkill
  1190.                                         end
  1191.                                         info.keepShownOnClick = true
  1192.                                         UIDropDownMenu_AddButton(info, level)
  1193.                                 end
  1194.                         end
  1195.                         return
  1196.                 end
  1197.                 local custom = type(options)~="table"
  1198.                 for i, v in ipairs(custom and f.skills or options) do
  1199.                         if not custom or v[options] then
  1200.                                 info = wipe(info)
  1201.                                 info.text = custom and ("|cff%.2x%.2x%.2x%s|r"):format( v.color[1]*255, v.color[2]*255, v.color[3]*255, v.displayName ) or v.text or v.ftext()
  1202.                                 info.isTitle, info.value, info.hasArrow = v.isTitle, v.submenu, v.submenu ~= nil
  1203.                                 if custom then
  1204.                                         info.isNotRadio = true
  1205.                                         info.checked = char.show[v.skillName]
  1206.                                         info.func, info.arg1, info.arg2 = SetOption, char.show, v.skillName
  1207.                                 elseif v.radio then
  1208.                                         if v.radio == "scaleX" then
  1209.                                                 info.checked = config.scale ~= .9 and config.scale ~= 1 and config.scale ~= 1.1 and config.scale ~= 1.2
  1210.                                                 info.func = v.func
  1211.                                                 if info.checked then
  1212.                                                         info.text = ("%s (%i%%)"):format(info.text, config.scale*100)
  1213.                                                 end
  1214.                                         else
  1215.                                                 info.checked = config[v.radio] == v.val
  1216.                                                 info.func, info.arg1, info.arg2 = SetRadioOption, v.radio, v.val
  1217.                                         end
  1218.                                 elseif v.scope then
  1219.                                         info.checked = v.inv and not v.scope[v.var] or not v.inv and v.scope[v.var]
  1220.                                         info.func, info.arg1, info.arg2, info.tooltipText = SetOption, v.scope, v.var, v.inv
  1221.                                         info.isNotRadio = true
  1222.                                 else
  1223.                                         info.notCheckable = not v.checked
  1224.                                         if v.color then
  1225.                                                 info.hasColorSwatch, info.notCheckable = true, true
  1226.                                                 info.r, info.g, info.b = unpack(colors[v.color])
  1227.                                                 info.func, info.arg1 = OpenColorPicker, v.color
  1228.                                                 info.padding = 10
  1229.                                         else
  1230.                                                 info.func, info.checked = v.func, v.checked
  1231.                                                 info.isNotRadio = true
  1232.                                         end
  1233.                                 end
  1234.  
  1235.                                 info.keepShownOnClick = info.func
  1236.                                 if level==1 and info.notCheckable then
  1237.                                         info.text = ("|Tx:%i|t%s"):format(25/self.scale, info.text)
  1238.                                 end
  1239.                                 UIDropDownMenu_AddButton(info, level)
  1240.                                 if level==1 and info.notCheckable and info.hasArrow then
  1241.                                         local frame = _G[("DropDownList1Button%i"):format(DropDownList1.numButtons)]
  1242.                                         frame:SetPoint("TOPLEFT", 11, select(5,frame:GetPoint())) --
  1243.                                 end
  1244.                         end
  1245.                 end
  1246.         end
  1247.  
  1248.         local function SetCustomScale(dialog)
  1249.                 local val = tonumber( dialog.editBox:GetText():match"(%d+)" )
  1250.                 if not val or val<70 or val>200 then
  1251.                         baseScript = BasicScriptErrors:GetScript"OnHide"
  1252.                         BasicScriptErrors:SetScript("OnHide",Error_OnHide)
  1253.                         BasicScriptErrorsText:SetText(L["Invalid scale.\nShould be a number between 70 and 200%"])
  1254.                         return BasicScriptErrors:Show()
  1255.                 end
  1256.                 config.scale = val/100
  1257.         end
  1258.  
  1259.         local dialog = {
  1260.                 text = L["Set tooltip scale.\nEnter a value between 70 and 200 (%%)."],
  1261.                 maxLetters = 12,
  1262.                 OnAccept = SetCustomScale,
  1263.                 OnShow = function(self) CloseDropDownMenus() self.editBox:SetText(config.scale*100) self.editBox:SetFocus() end,
  1264.                 EditBoxOnEnterPressed = function(self) local p=self:GetParent() SetCustomScale(p) p:Hide() end,
  1265.         }
  1266.         for k, v in next, StaticPopupDialogs.ADD_IGNORE do -- ADD_MUTE
  1267.                 if not dialog[k] then dialog[k] = v end
  1268.         end
  1269.         StaticPopupDialogs.ABTS_SET_SCALE = dialog
  1270.  
  1271.         f.SetupConfigMenu = nil
  1272. end
  1273.  
  1274. local function InitAltFrame(g, backdrop)
  1275.         g:SetFrameStrata"TOOLTIP"
  1276.         g:SetScript("OnEnter", Alt_OnEnter)
  1277.         g:SetScript("OnLeave", Alt_OnLeave)
  1278.         g:SetScript("OnMouseWheel", OnMouseWheel)
  1279.  
  1280.         g:SetBackdrop(backdrop)
  1281.         g:SetBackdropColor(.1, .1, .1, .85)
  1282.         g:SetBackdropBorderColor(.3, .3, .3, .9)
  1283. end
  1284.  
  1285. local function Tablet_OnHide(f)
  1286.         g:Hide() g:SetWidth(0)
  1287.         h:Hide() h:SetWidth(0)
  1288. end
  1289.  
  1290. function f:AdjustGroupHandling()
  1291.         local groups = new()
  1292.         for char, data in next, realm do
  1293.                 for id, timer in next, data do
  1294.                         if type(id)=="number" then
  1295.                                 local cd = watchedCDs[id]
  1296.                                 if cd then
  1297.                                         if cd.group then
  1298.                                                 local ownGroup = cd.group.."-"..char
  1299.                                                 config.icons[ownGroup] = config.icons[ownGroup] or config.icons[cd.group] or groupIcons[cd.group]
  1300.                                                 config.names[ownGroup] = config.names[ownGroup] or config.names[cd.group]
  1301.                                                 if not watchedCDs[ config.names[ownGroup] ] then config.names[ownGroup] = nil end
  1302.                                                 groups[cd.group] = true
  1303.                                         end
  1304.                                 else
  1305.                                         data[id] = nil
  1306.                                 end
  1307.                         end
  1308.                 end
  1309.         end
  1310.         for group in next, groups do
  1311.                 config.icons[group], config.names[group] = nil
  1312.         end
  1313.         del(groups)
  1314.         f.AdjustGroupHandling = nil
  1315. end
  1316.  
  1317.  
  1318. function f:ADDON_LOADED(event, addon)
  1319.         if addon ~= addonName then return end
  1320.  
  1321.         AraTradeskillsDB = AraTradeskillsDB or {icons={},names={},aliases={},colors=defaultColors}
  1322.         config = AraTradeskillsDB
  1323.         if not config.scale then config.scale = 1 end
  1324.         if not config.breakMode then config.breakMode = "auto" end -- r38
  1325.         if not config[realmName] then config[realmName] = {} end
  1326.         realm = config[realmName]
  1327.         if not realm[playerName] then realm[playerName] = {show={}} end
  1328.         char = realm[playerName]
  1329.         if not char.shortcuts then char.shortcuts = {} end -- r14
  1330.         if not char.curSkills then char.curSkills, char.maxSkills = {}, {} end
  1331.         if not char.links then char.links = {} end -- r25
  1332.         f.AdjustGroupHandling() -- r30
  1333.         if char.tracking then
  1334.                 char.trackedSkill, char.track, char.tracking = char.tracking, true
  1335.         end
  1336.         if char.trackedSkill == nil then char.trackedSkill = AUTO end
  1337.         colors = config.colors
  1338.  
  1339.         CDs = new()
  1340.         if config.displayCDs then
  1341.                 e.timer = 0
  1342.                 e:SetScript("OnUpdate", DisplayCD_OnUpdate)
  1343.         end
  1344.  
  1345.         tip = _G.GameTooltip
  1346.  
  1347.         local popup = {
  1348.                 text = L["Set an alias for \"%s\".\nLeave blank to reset."],
  1349.                 maxLetters = 32,
  1350.                 OnAccept = function(self) AraSkills:SetAlias(self.editBox:GetText()) end,
  1351.                 OnShow = function(self) self.editBox:SetText(AraSkills.alias) self.editBox:SetFocus() end,
  1352.                 EditBoxOnEnterPressed = function(self)
  1353.                         local parent = self:GetParent()
  1354.                         AraSkills:SetAlias(parent.editBox:GetText())
  1355.                         parent:Hide()
  1356.                 end,
  1357.         }
  1358.         for k, v in next, StaticPopupDialogs.SET_FRIENDNOTE do--SET_FRIENDNOTE do
  1359.                 if not popup[k] then popup[k] = v end
  1360.         end
  1361.         StaticPopupDialogs.ABTS_SET_ALIAS = popup
  1362.  
  1363.         self:Hide()
  1364.         self.skills = {}
  1365.         self:SetFrameStrata"TOOLTIP"
  1366.         self:SetClampedToScreen(true)
  1367.         self:SetScript("OnEnter", Menu_OnEnter)
  1368.         self:SetScript("OnLeave", Menu_OnLeave)
  1369.         self:SetScript("OnHide", Tablet_OnHide)
  1370.         self:SetScript("OnMouseWheel", OnMouseWheel)
  1371.         self:RegisterEvent"TRADE_SKILL_SHOW"
  1372.         self:RegisterEvent"PLAYER_LOGOUT"
  1373.         self:RegisterEvent"CHAT_MSG_LOOT"
  1374.         SetTracking(nil, char.track and char.trackedSkill or nil, true)
  1375.  
  1376.         local backdrop = { bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Tooltips\\UI-Tooltip-Border",
  1377.                 tile=false, tileSize=0, edgeSize=14, insets = { left=3, right=3, top=3, bottom=3 } }
  1378.         self:SetBackdrop(backdrop)
  1379.         self:SetBackdropColor(.1, .1, .1, .85)
  1380.         self:SetBackdropBorderColor(.3, .3, .3, .9)
  1381.  
  1382.         InitAltFrame(g, backdrop)
  1383.         InitAltFrame(h, backdrop)
  1384.  
  1385.         if IsLoggedIn() then UpdateText() else
  1386.                 f.PLAYER_ENTERING_WORLD = UpdateText
  1387.                 f:RegisterEvent"PLAYER_ENTERING_WORLD"
  1388.         end
  1389.         self:UnregisterEvent(event)
  1390.         self.ADDON_LOADED = nil
  1391. end
  1392.  
  1393. local matchSkillValue = ERR_SKILL_UP_SI:gsub("%%s","(.+)"):gsub("%%d","(%%d+)"):gsub("%%%d+%$s","(.+)"):gsub("%%%d+%$d","(.+)")
  1394.  
  1395. function f:CHAT_MSG_SKILL(event,msg)
  1396.         if not char.track then return end
  1397.         local skill, value = msg:match(matchSkillValue)
  1398.         if value and char.track and (char.trackedSkill == altNames[skill] or char.skill == altNames[skill]) then
  1399.                 char.curSkills[skill] = value
  1400.                 return UpdateText()
  1401.         end
  1402. end
  1403.  
  1404.  
  1405. local function UpdateTrackedSkill(self, elapsed)
  1406.         ex.timer = ex.timer - elapsed
  1407.         if ex.timer > 0 then return else ex:SetScript("OnUpdate", nil) end
  1408. --      ExpandSkillHeader(0)
  1409.         for _, i in next, getProfessions() do
  1410.                 local skillName, icon, value, valueMax = GetProfessionInfo(i)
  1411.                 if skillName and (char.trackedSkill == AUTO and skillName == char.skill or skillName == char.trackedSkill) then
  1412.                         char.maxSkills[skillName] = valueMax
  1413.                         return UpdateText()
  1414.                 end
  1415.         end
  1416. end
  1417.  
  1418. local matchSkillRankUp = ERR_LEARN_ABILITY_S:gsub("%%s", "(.+)")
  1419.  
  1420. function f:CHAT_MSG_SYSTEM(event,msg)
  1421.         if not msg:match(matchSkillRankUp) then return end
  1422.         ex.timer = 1
  1423.         ex:SetScript("OnUpdate", UpdateTrackedSkill)
  1424. end
  1425.  
  1426.  
  1427. local matchPushedItem = LOOT_ITEM_PUSHED_SELF:gsub("(%%s)", "(.+)") -- LOOT_ITEM_SELF for corpse loot
  1428.  
  1429. function f:CHAT_MSG_LOOT(event, msg)
  1430.         local item = msg:match(matchPushedItem)
  1431.         if not item then return end
  1432.         local name, id, cd, _, _, _, _, _, _, texture = GetItemInfo(item)
  1433.         if not name then return end
  1434.         id = tonumber( id:match"(%d+):" )
  1435.         cd = watchedCDs[id]
  1436.         if not cd or cd.skill ~= "Item" then return end
  1437.         char[id] = time() + cd.CD
  1438.         config.names[id] = name
  1439.         config.icons[id] = texture
  1440.         UpdateCDs()
  1441. end
  1442.  
  1443. function f:SetHooks()
  1444.         LoadAddOn"Blizzard_TradeSkillUI"
  1445.         local b = TradeSkillSkillIcon
  1446.         local TS_OnEnter = b:GetScript"OnEnter"
  1447.         local TS_OnLeave = b:GetScript"OnLeave"
  1448.         local TS_OnClick = b:GetScript"OnClick"
  1449.  
  1450.         local OrgSetItem = GameTooltip.SetTradeSkillItem
  1451.         local function NewSetItem(...)
  1452.                 OrgSetItem(...)
  1453.                 tip:AddLine("\n"..L["|cffff8040Click|r to create a shortcut."], .2, 1, .2)
  1454.                 tip:Show()
  1455.         end
  1456.  
  1457.         b:SetScript("OnEnter", function(self, ...)
  1458.                 if not IsTradeSkillLinked() then tip.SetTradeSkillItem = NewSetItem end
  1459.                 TS_OnEnter(self, ...)
  1460.         end)
  1461.         b:SetScript("OnLeave", function(...)
  1462.                 TS_OnLeave(...)
  1463.                 if not IsTradeSkillLinked() then tip.SetTradeSkillItem = OrgSetItem end
  1464.         end)
  1465.         b:SetScript("OnClick", function(self,button,...)
  1466.                 TS_OnClick(self,button,...)
  1467.                 if not IsTradeSkillLinked() and not IsModifierKeyDown() and button == "LeftButton" then
  1468.                         local index = TradeSkillFrame.selectedSkill
  1469.                         local spellID = tonumber( GetTradeSkillRecipeLink(index):match"enchant:(%d+)" )
  1470.                         local skill = GetTradeSkillLine()
  1471.                         char.shortcuts[spellID] = altNames[skill]
  1472.                         config.names[spellID] = GetTradeSkillInfo(index)
  1473.                         config.icons[spellID] = GetTradeSkillIcon(index)
  1474.                         print("|cFFFFB366Ara|cffffff00BrokerTradeskills: "..(L["|cff8080ff%s|r|cffffff00 added to shortcuts."]:format( config.names[spellID] )))
  1475.                 end
  1476.         end)
  1477.  
  1478.         f.SetHooks = nil
  1479. end
  1480.  
  1481. f:SetScript("OnEvent", function(self, event, ...) return self[event](self, event, ...) end)
  1482. f:RegisterEvent"ADDON_LOADED"
clone this paste RAW Paste Data