local _G = getfenv(0)local abacus = AceLibrary("Abacus-2.0")local LSM3 = LibStub("LibSharedMedia-3.0")local ipairs = _G.ipairslocal tonumber = _G.tonumberlocal unpack = _G.unpacklocal strformat = _G.string.formatlocal strutf8len = _G.string.utf8lenEBB_Bar = AceLibrary("AceOO-2.0").Class()local EBB_Bar = EBB_Barlocal ElkBuffBarsfunction EBB_Bar:ToString() return "EBB_Bar"endfunction EBB_Bar.prototype:init(arg) EBB_Bar.super.prototype.init(self) if not ElkBuffBars then ElkBuffBars = _G.ElkBuffBars end self.frames = {}endfunction EBB_Bar.prototype:Reset() self.frames.container:Hide() self.frames.container:ClearAllPoints() self.layout = nil self.data = nil self:SetParent()endfunction EBB_Bar.prototype:GetContainer() return self.frames.containerendfunction EBB_Bar.prototype:SetParent(parent) if self.frames.container then self.frames.container:SetParent(parent and parent.frames.container or UIParent) end self.parent = parentendfunction EBB_Bar.prototype:OnClick(button) if button == "LeftButton" then if IsAltKeyDown() then self.parent:ToggleAnchor() end if IsShiftKeyDown() and ElkBuffBars.db.profile.shiftannounce then local sendChatChannel = "" if GetNumPartyMembers() > 0 then sendChatChannel = "PARTY" else sendChatChannel = "SAY" end if not (sendChatChannel == "SAY" and not ElkBuffBars.db.profile.announceoutofparty) then if(self.data.timeleft>0 and self.data.realtype ~= "TRACKING") then SendChatMessage("I have " .. self.data.name .. ": " .. self:GetDataString("TIMELEFT") .. " remaining!", sendChatChannel) else SendChatMessage("I have " .. self.data.name .. "!", sendChatChannel) end end end elseif button == "RightButton" then if self.parent.layout.target ~= "player" then return end if self.data.realtype == "BUFF" or self.data.realtype == "DEBUFF" then CancelPlayerBuff(self.data.id) elseif self.data.realtype == "TENCH" then CancelItemTempEnchantment(self.data.id - 15) elseif self.data.realtype == "TRACKING" then if ( GameTooltip:GetOwner() == this ) then GameTooltip:Hide(); end ToggleDropDownMenu(1, nil, MiniMapTrackingDropDown, this, 0, -5) end endendfunction EBB_Bar.prototype:OnEnter(this) local realtype = self.data.realtype if self.layout.tooltipanchor == "default" then GameTooltip_SetDefaultAnchor(GameTooltip, this) else GameTooltip:SetOwner(this, self.layout.tooltipanchor) end if self.parent.layout.target == "player" then if realtype == "BUFF" or realtype == "DEBUFF" then GameTooltip:SetPlayerBuff(self.data.id) elseif realtype == "TENCH" then GameTooltip:SetInventoryItem("player", self.data.id) elseif realtype == "TRACKING" then GameTooltip:SetTracking() end else if realtype == "BUFF" then GameTooltip:SetUnitBuff(self.parent.layout.target, self.data.id) elseif realtype == "DEBUFF" then GameTooltip:SetUnitDebuff(self.parent.layout.target, self.data.id) end endendfunction EBB_Bar.prototype:OnLeave() GameTooltip:Hide()endfunction EBB_Bar.prototype:OnUpdate(elapsed) local data = self.data local frames = self.frames local layout = self.layout local total_elapsed = self.total_elapsed + elapsed local fraction = (data.timeleft - total_elapsed) / data.timemax if fraction > 1 then ElkBuffBars:Print(string.format("|cffff0000[EBB] ERROR:|r %d - %s - %d / %d", data.id, data.name, data.timeleft, data.timemax)) fraction = 0 end local barwidth = self.barwidth_total * fraction if barwidth + .2 < self.barwidth then -- this could be a visual change if barwidth < 1 then frames.bar:Hide() frames.container:SetScript("OnUpdate", nil) else frames.bar:SetWidth(barwidth) if layout.barright then frames.bar:SetTexCoord(fraction, 1, 0, 1) else frames.bar:SetTexCoord(0, fraction, 0, 1) end end self.barwidth = barwidth end self.total_elapsed = total_elapsedendfunction EBB_Bar.prototype:UpdateLayout(layout) if layout then self.layout = layout else layout = self.layout if not layout then return end end local frames = self.frames-- container if not frames.container then frames.container = CreateFrame("button", nil, UIParent) frames.container:SetFrameStrata("BACKGROUND") frames.container.bar = self frames.container:RegisterForClicks("LeftButtonUp", "RightButtonUp") frames.container:SetScript("OnClick", function(this, button) this.bar:OnClick(button) end ) frames.container:SetScript("OnEnter", function(this) this.bar:OnEnter(this) end ) frames.container:SetScript("OnLeave", function(this) this.bar:OnLeave() end ) end if layout.clickthrough then frames.container:EnableMouse(false) else frames.container:EnableMouse(true) end frames.container:SetHeight(layout.height) frames.container:SetWidth(layout.width) frames.container:SetAlpha(layout.alpha) local leftoffset = 0 local rightoffset = 0-- icon if layout.icon then if not frames.icon then frames.icon = frames.container:CreateTexture(nil, "BACKGROUND") end frames.icon:ClearAllPoints() frames.icon:SetHeight(layout.height) frames.icon:SetWidth(layout.height) if layout.icon == "LEFT" then leftoffset = layout.height frames.icon:SetPoint("LEFT", frames.container) end if layout.icon == "RIGHT" then rightoffset = -layout.height frames.icon:SetPoint("RIGHT", frames.container) end frames.icon:Show() else if frames.icon then frames.icon:Hide() end end-- iconcount if layout.icon and layout.iconcount then if not frames.iconcount then frames.iconcount = frames.container:CreateFontString(nil, "OVERLAY") end frames.iconcount:ClearAllPoints() frames.iconcount:SetPoint(layout.iconcountanchor, frames.icon, layout.iconcountanchor, (string.find(layout.iconcountanchor, "LEFT") and 3) or (string.find(layout.iconcountanchor, "RIGHT") and -3) or 0, (string.find(layout.iconcountanchor, "TOP") and -3) or (string.find(layout.iconcountanchor, "BOTTOM") and 3) or 0) frames.iconcount:SetFont(LSM3:Fetch("font", layout.iconcountfont), layout.iconcountfontsize, "OUTLINE") frames.iconcount:SetTextColor(layout.iconcountcolor[1], layout.iconcountcolor[2], layout.iconcountcolor[3], 1) frames.iconcount:Show() else if frames.iconcount then frames.iconcount:Hide() end end-- iconborder if layout.icon then if not frames.iconborder then frames.iconborder = frames.container:CreateTexture(nil, "OVERLAY") end frames.iconborder:ClearAllPoints() frames.iconborder:SetPoint("TOPLEFT", frames.icon) frames.iconborder:SetPoint("BOTTOMRIGHT", frames.icon) frames.iconborder:Show() else if frames.iconborder then frames.iconborder:Hide() end end-- bar if layout.bgbar then if not frames.bgbar then frames.bgbar = frames.container:CreateTexture(nil, "BACKGROUND") end frames.bgbar:ClearAllPoints() frames.bgbar:SetPoint("TOPLEFT", frames.container, "TOPLEFT", leftoffset, 0) frames.bgbar:SetPoint("BOTTOMRIGHT", frames.container, "BOTTOMRIGHT", rightoffset, 0) frames.bgbar:SetTexture(LSM3:Fetch("statusbar", layout.bartexture)) frames.bgbar:Show() else if frames.bgbar then frames.bgbar:Hide() end end if layout.bar then if not frames.bar then frames.bar = frames.container:CreateTexture(nil, "ARTWORK") end frames.bar:ClearAllPoints() if layout.barright then frames.bar:SetPoint("TOPRIGHT", frames.container, "TOPRIGHT", rightoffset, 0) frames.bar:SetPoint("BOTTOMRIGHT", frames.container, "BOTTOMRIGHT", rightoffset, 0) else frames.bar:SetPoint("TOPLEFT", frames.container, "TOPLEFT", leftoffset, 0) frames.bar:SetPoint("BOTTOMLEFT", frames.container, "BOTTOMLEFT", leftoffset, 0) end frames.bar:SetWidth(0) frames.bar:SetTexture(LSM3:Fetch("statusbar", layout.bartexture)) frames.bar:Show() else if frames.bar then frames.bar:Hide() end end local padding = layout.padding-- textTL if layout.textTL then if not frames.textTL then frames.textTL = frames.container:CreateFontString(nil, "OVERLAY") end frames.textTL:ClearAllPoints() frames.textTL:SetPoint("TOPLEFT", frames.container, "TOPLEFT", leftoffset + padding, -padding) frames.textTL:SetFontObject(GameFontNormal) frames.textTL:SetFont(LSM3:Fetch("font", layout.textTLfont), layout.textTLfontsize) frames.textTL:SetTextColor(layout.textTLcolor[1], layout.textTLcolor[2], layout.textTLcolor[3], 1) if not layout.textTR then frames.textTL:SetPoint("TOPRIGHT", frames.container, "TOPRIGHT", rightoffset - padding, -padding) frames.textTL:SetJustifyH(layout.textTLalign) else frames.textTL:SetJustifyH("LEFT") end if not layout.textBL then frames.textTL:SetPoint("BOTTOMLEFT", frames.container, "BOTTOMLEFT", leftoffset + padding, padding) frames.textTL:SetJustifyV("CENTER") else frames.textTL:SetJustifyV("TOP") end frames.textTL:Show() else if frames.textTL then frames.textTL:Hide() end end-- textTR if layout.textTR then if not frames.textTR then frames.textTR = frames.container:CreateFontString(nil, "OVERLAY") end frames.textTR:ClearAllPoints() frames.textTR:SetPoint("TOPRIGHT", frames.container, "TOPRIGHT", rightoffset - padding, -padding) frames.textTR:SetFontObject(GameFontNormal) frames.textTR:SetFont(LSM3:Fetch("font", layout.textTRfont), layout.textTRfontsize) frames.textTR:SetTextColor(layout.textTRcolor[1], layout.textTRcolor[2], layout.textTRcolor[3], 1) frames.textTR:SetJustifyH("RIGHT") if not layout.textBL then frames.textTR:SetPoint("BOTTOMRIGHT", frames.container, "BOTTOMRIGHT", rightoffset - padding, padding) frames.textTR:SetJustifyV("CENTER") else frames.textTR:SetJustifyV("TOP") end if layout.textTL then frames.textTR:SetPoint("TOPLEFT", frames.textTL, "TOPLEFT", 10, 0) end frames.textTR:Show() else if frames.textTR then frames.textTR:Hide() end end-- textBL if layout.textTL and layout.textBL then if not frames.textBL then frames.textBL = frames.container:CreateFontString(nil, "OVERLAY") end frames.textBL:ClearAllPoints() frames.textBL:SetPoint("BOTTOMLEFT", frames.container, "BOTTOMLEFT", leftoffset + padding, padding) frames.textBL:SetFontObject(GameFontNormal) frames.textBL:SetFont(LSM3:Fetch("font", layout.textBLfont), layout.textBLfontsize) frames.textBL:SetTextColor(layout.textBLcolor[1], layout.textBLcolor[2], layout.textBLcolor[3], 1) if not layout.textBR then frames.textBL:SetPoint("BOTTOMRIGHT", frames.container, "BOTTOMRIGHT", rightoffset - padding, padding) frames.textBL:SetJustifyH(layout.textBLalign) else frames.textBL:SetJustifyH("LEFT") end frames.textBL:Show() else if frames.textBL then frames.textBL:Hide() end end-- textBR if layout.textTL and layout.textBR then if not frames.textBR then frames.textBR = frames.container:CreateFontString(nil, "OVERLAY") end frames.textBR:ClearAllPoints() frames.textBR:SetPoint("BOTTOMRIGHT", frames.container, "BOTTOMRIGHT", rightoffset - padding, padding) frames.textBR:SetFontObject(GameFontNormal) frames.textBR:SetFont(LSM3:Fetch("font", layout.textBRfont), layout.textBRfontsize) frames.textBR:SetTextColor(layout.textBRcolor[1], layout.textBRcolor[2], layout.textBRcolor[3], 1) frames.textBR:SetJustifyH("RIGHT") if layout.textBL then frames.textBR:SetPoint("BOTTOMLEFT", frames.textBL, "BOTTOMLEFT", 10, 0) end frames.textBR:Show() else if frames.textBR then frames.textBR:Hide() end end-- precomputations self.barwidth_total = layout.width - leftoffset + rightoffset -- rightoffset is <= 0 self.barwidth_padded = self.barwidth_total - 2 * layout.padding self.trdwidth = self.barwidth_padded / 3endfunction EBB_Bar.prototype:UpdateData(data) if data then self.data = data else data = self.data if not data then return end end local frames = self.frames local layout = self.layout if layout.icon then frames.icon:SetTexture(data.icon) if data.type == "DEBUFF" then frames.iconborder:SetTexture("Interface\\Buttons\\UI-Debuff-Overlays") frames.iconborder:SetTexCoord(0.296875, 0.5703125, 0, 0.515625) local debuffcolor if ( data.debufftype ) then debuffcolor = DebuffTypeColor[data.debufftype] else debuffcolor = DebuffTypeColor["none"] end frames.iconborder:SetVertexColor(debuffcolor.r, debuffcolor.g, debuffcolor.b) frames.iconborder:Show() elseif data.type == "TENCH" then frames.iconborder:SetTexture("Interface\\Buttons\\UI-TempEnchant-Border") frames.iconborder:SetTexCoord(0, 1, 0, 1) frames.iconborder:Show() else frames.iconborder:Hide() end if layout.iconcount then if data.maxcharges then frames.iconcount:SetText(data.charges) frames.iconcount:Show() else frames.iconcount:Hide() end end end if layout.bar then if data.untilcancelled then if layout.timelessfull then frames.bar:SetWidth(self.barwidth_total) frames.bar:Show() else frames.bar:SetWidth(0) frames.bar:Hide() end frames.container:SetScript("OnUpdate", nil) else local fraction = data.timeleft / data.timemax if fraction > 1 then ElkBuffBars:Print(string.format("|cffff0000[EBB] ERROR:|r %d - %s - %d / %d", data.id, data.name, data.timeleft, data.timemax)) fraction = 0 end local barwidth = self.barwidth_total * fraction self.barwidth = barwidth if barwidth < 1 then frames.bar:Hide() frames.container:SetScript("OnUpdate", nil) else frames.bar:SetWidth(barwidth) if layout.barright then frames.bar:SetTexCoord(fraction, 1, 0, 1) else frames.bar:SetTexCoord(0, fraction, 0, 1) end frames.bar:Show() end self.total_elapsed = 0 if data.id >= 0 then -- no scripts for Blessing of Demonstration frames.container:SetScript("OnUpdate", function(this, elapsed) this.bar:OnUpdate(elapsed) end) end end local color = layout["barcolor"] if data.type == "DEBUFF" and layout.debufftypecolor then local debuffcolor if ( data.debufftype ) then debuffcolor = DebuffTypeColor[data.debufftype] else debuffcolor = DebuffTypeColor["none"] end color = {debuffcolor.r, debuffcolor.g, debuffcolor.b, color[4]} end frames.bar:SetVertexColor(unpack(color)) else frames.container:SetScript("OnUpdate", nil) end if layout.bgbar then frames.bgbar:SetVertexColor(unpack(layout["barbgcolor"])) end self:UpdateText()endlocal romandigits = { {["r"] = "M", ["a"] = 1000}, {["r"] = "CM", ["a"] = 900}, {["r"] = "D", ["a"] = 500}, {["r"] = "CD", ["a"] = 400}, {["r"] = "C", ["a"] = 100}, {["r"] = "XC", ["a"] = 90}, {["r"] = "L", ["a"] = 50}, {["r"] = "XL", ["a"] = 40}, {["r"] = "X", ["a"] = 10}, {["r"] = "IX", ["a"] = 9}, {["r"] = "V", ["a"] = 5}, {["r"] = "IV", ["a"] = 4}, {["r"] = "I", ["a"] = 1} }local arabic_to_roman = setmetatable({}, {__index=function(self,arabic) arabic = tonumber(arabic) if not arabic then return nil end local roman = "" for i,v in ipairs(romandigits) do while arabic >= v.a do arabic = arabic - v.a roman = roman .. v.r end end self[arabic] = roman return romanend})function EBB_Bar.prototype:GetDataString(datatype) if datatype == "NAME" then return self:GetNameString() end if datatype == "NAMERANK" then return self:GetNameString()..(self.data.rank and (" "..arabic_to_roman[self.data.rank]) or "") end if datatype == "NAMECOUNT" then return self:GetNameString()..((self.data.maxcharges) and " x"..self.data.charges or "") end if datatype == "NAMERANKCOUNT" then return self:GetNameString()..(self.data.rank and (" "..arabic_to_roman[self.data.rank]) or "")..((self.data.maxcharges) and " x"..self.data.charges or "") end if datatype == "RANK" then return self.data.rank and arabic_to_roman[self.data.rank] or "" end if datatype == "COUNT" then return ((self.data.maxcharges) and "x"..self.data.charges or "") end if datatype == "TIMELEFT" then return self:GetTimeString(self.data.timeleft, self.layout.timeformat) end if datatype == "DEBUFFTYPE" then return self.data.debufftype end return "???"endfunction EBB_Bar.prototype:GetNameString() local layout = self.layout local name = self.data.name if layout.abbreviate_name > 0 and strutf8len(name) > layout.abbreviate_name then return ElkBuffBars.ShortName[name] else return name endendfunction EBB_Bar.prototype:GetTimeString(time, format) if self.data.untilcancelled then return "" end if format == "DEFAULT" then return strformat(SecondsToTimeAbbrev(time)) end if format == "EXTENDED" then return abacus:FormatDurationExtended(time) end if format == "FULL" then return abacus:FormatDurationFull(time) end if format == "SHORT" then return abacus:FormatDurationShort(time) end if format == "CONDENSED" then return abacus:FormatDurationCondensed(time) end return "???"endfunction EBB_Bar.prototype:UpdateText() local frames = self.frames local layout = self.layout if layout.textTL then frames.textTL:SetText(self:GetDataString(layout.textTL)) end if layout.textTR then frames.textTR:SetText(self:GetDataString(layout.textTR)) end if layout.textTL and self.layout.textBL then frames.textBL:SetText(self:GetDataString(layout.textBL)) end if layout.textTL and self.layout.textBR then frames.textBR:SetText(self:GetDataString(layout.textBR)) end self:UpdateTextWidth()endfunction EBB_Bar.prototype:UpdateTimeleft() local data = self.data local frames = self.frames local layout = self.layout -- new data.timeleft was set, reset time elapsed since last setting self.total_elapsed = 0 if layout.textTL == "TIMELEFT" then frames.textTL:SetText(self:GetDataString("TIMELEFT")) end if layout.textTR == "TIMELEFT" then frames.textTR:SetText(self:GetDataString("TIMELEFT")) end if layout.textTL and layout.textBL == "TIMELEFT" then frames.textBL:SetText(self:GetDataString("TIMELEFT")) end if layout.textTL and layout.textBR == "TIMELEFT" then frames.textBR:SetText(self:GetDataString("TIMELEFT")) end self:UpdateTextWidth()endfunction EBB_Bar.prototype:UpdateTextWidth() local frames = self.frames local layout = self.layout local trdwidth = self.trdwidth if layout.textTL and layout.textTR then local TLwidth = frames.textTL:GetStringWidth() + 5 local TRwidth = frames.textTR:GetStringWidth() + 5 if TLwidth < trdwidth then frames.textTL:SetWidth(TLwidth) elseif TRwidth < trdwidth then frames.textTL:SetWidth(self.barwidth_padded - TRwidth) else frames.textTL:SetWidth(trdwidth + (TLwidth * trdwidth)/(TLwidth + TRwidth)) end end if layout.textTL and layout.textBL and layout.textBR then local BLwidth = frames.textBL:GetStringWidth() + 5 local BRwidth = frames.textBR:GetStringWidth() + 5 if BLwidth < trdwidth then frames.textBL:SetWidth(BLwidth) elseif BRwidth < trdwidth then frames.textBL:SetWidth(self.barwidth_padded - BRwidth) else frames.textBL:SetWidth(trdwidth + (BLwidth * trdwidth)/(BLwidth + BRwidth)) end endend