Compare with Previous | Blame | View Log
local BUTTON_HEIGHT, ICON_SIZE, GAP, TEXT_OFFSET, MAX_ENTRIES = 15, 13, 10, 5 local f = CreateFrame( "Frame", "AraBrokerGuildFriends", UIParent ) local t = CreateFrame"Frame" local dontShow, block, horde, config, isGuild, tip, tipShown = true local defaultConfig = { showGuildNotes = true, showGuildName = true, sortType = "class", sortDESC = false, scale = 1 } local guildEntries, friendEntries, motd, slider, nbEntries = {}, {} local sliderValue, hasSlider, ShowTablet = 0 local RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS local L = {} local format, strfind, GetGuildRosterInfo, GetFriendInfo, CLASS_BUTTONS, GetDifficultyColor = format, strfind, GetGuildRosterInfo, GetFriendInfo, CLASS_BUTTONS, GetQuestDifficultyColor local orgReloadUI = ReloadUI ReloadUI = function(...) config.reloading = true orgReloadUI(...) end local tables = {} local new = function( ... ) local table = tremove(tables) or {} for i=1, select("#",...) do table[i] = select(i,...) end return table end local del = function( table ) tables[ #tables + 1 ] = wipe(table) end local friendOnline, friendOffline = gsub(ERR_FRIEND_ONLINE_SS,"\124Hplayer:%%s\124h%[%%s%]\124h",""), gsub(ERR_FRIEND_OFFLINE_S,"%%s","") function f:CHAT_MSG_SYSTEM( event, msg ) if strfind( msg, friendOnline ) or strfind( msg, friendOffline ) then ShowFriends() end end function f:FRIENDLIST_UPDATE() for k,v in next,friendEntries do del(v) friendEntries[k]=nil end local total, online = GetNumFriends(), 0 for i = 1, total do local name, level, class, zone, connected, status, note = GetFriendInfo(i) if connected then if online == 0 then friendEntries[1] = new( nil, "Name", "", "Lv", "Zone", "Notes" ) end online = online + 1 friendEntries[online+1] = new( name, status ~= "" and format("%s %s", tostring(status), tostring(name)) or name, L[class], level, zone or "Unknown", note and format("[%s]",note) or "-" ) end end f.FriendsBlock.text = format(config.hideTotalFriends and " %d" or " %d/%d", online, total) if not isGuild and block then f:FriendsOnEnter(block) end end function f:GUILD_ROSTER_UPDATE() dontShow = not(isGuild and block) f.GuildOnEnter(block) end function f:PLAYER_GUILD_UPDATE(event, unit) if unit and unit ~= "player" then return end return IsInGuild() and GuildRoster() end local hordeZones = "Orgrimmar,Undercity,Thunder Bluff,Silvermoon City,Durotar,Tirisfal Glades,The Barrens,Silverpine Forest,Mulgore,The Sepulcher,Eversong Woods,Ghostlands," local allianceZones = "Ironforge,Stormwind City,Darnassus,Azuremyst Isle,Bloodmyst Isle,Darkshore,Dun Morogh,Loch Modan,Wetlands,Elwynn Forest,Redridge Mountains,Westfall,Teldrassil,Duskwood,Darshire,Thelsamar,Deeprun Tram,The Exodar,Auberdine,Menethil Harbor,Theramore Isle," local function GetZoneColor(zone) if strfind( hordeZones , zone.."," ) then if horde then return 0,1,0 else return 1,0,0 end end if strfind( allianceZones, zone.."," ) then if horde then return 1,0,0 else return 0,1,0 end end return 1,1,0 end local function ShowBlockHints() local showBelow = UIParent:GetHeight()-f:GetTop()*f:GetScale() < f:GetBottom()*f:GetScale() tip:SetOwner(f, "ANCHOR_NONE") tip:SetPoint(showBelow and "TOP" or "BOTTOM", f, showBelow and "BOTTOM" or "TOP") tip:AddLine"Hints [|cffffffffBlock|r]" tip:AddLine("|cffff8020Click|r to open panel.", .2,1,.2) tip:AddLine("|cffff8020Ctrl+Click|r to toggle hints.", .2,1,.2) tip:AddLine("|cffff8020Shift+RightClick|r to toggle total number.", .2,1,.2) if isGuild then tip:AddLine("|cffff8020Shift+Click|r to toggle guild name.", .2,1,.2) tip:AddLine("|cffff8020RightClick|r to toggle note column.", .2,1,.2) else tip:AddLine("|cffff8020EverythingElse|r to add a friend.", .2,1,.2) end tip:Show() end local function ShowHints(btn) if not config.hideHints and btn and btn.unit then local showBelow = UIParent:GetHeight()-f:GetTop()*f:GetScale() < f:GetBottom()*f:GetScale() tip:SetOwner(f, "ANCHOR_NONE") tip:SetPoint(showBelow and "TOP" or "BOTTOM", f, showBelow and "BOTTOM" or "TOP") tip:AddLine"Hints" tip:AddLine("|cffff8020Click|r to whisper.", .2,1,.2) tip:AddLine("|cffff8020Alt+Click|r to invite.", .2,1,.2) tip:AddLine("|cffff8020Shift+Click|r to query guild.", .2, 1, .2) if not isGuild or CanEditPublicNote() then tip:AddLine("|cffff8020Ctrl+Click|r to edit note.", .2, 1, .2) end if isGuild then if CanEditOfficerNote() then tip:AddLine("|cffff8020Ctrl+RightClick|r to edit officer note.", .2, 1, .2) end tip:AddLine("|cffff8020RightClick|r to sort closest column.", .2, 1, .2) else tip:AddLine("|cffff8020MiddleClick|r to remove friend.", .2, 1, .2) end tip:AddLine("|cffff8020Ctrl+MouseWheel|r to resize tooltip.", .2, 1, .2) tip:Show() tipShown = true end end local highlight = f:CreateTexture() highlight:SetTexture"Interface\\QuestFrame\\UI-QuestTitleHighlight" highlight:SetBlendMode"ADD" highlight:SetAlpha(0) local function Menu_OnEnter(b) if b and b.index then highlight:SetAllPoints(b) if b.index > 1 then highlight:SetAlpha(1) ShowHints(b) end end end local function Menu_OnLeave(b) highlight:ClearAllPoints() tip:Hide() tipShown = false if b and b.index and b.index > 1 then highlight:SetAlpha(0) end if not MouseIsOver(f) then sliderValue = 0 block = nil f:Hide() end end local function CreateFS( index, parent, justify, anchor ) local fs = parent:CreateFontString( nil, "OVERLAY", "SystemFont_Shadow_Med1" ) fs:SetJustifyH( justify ) fs:SetTextColor( 1, index == 1 and .8 or 1, 0 ) if anchor then fs:SetPoint( "LEFT", anchor, "RIGHT", GAP, 0 ) end return fs end local function EditMOTD() f:Hide() block = nil GuildMOTDEditButton:Click() end local cols, colspace = { "class", "name", "level", "zone", "note", "rank" }, { ICON_SIZE } local function OnGuildmateClick( self, button ) if not( self and self.unit ) then return end if isGuild and button ~= "LeftButton" and not IsModifierKeyDown() then -- sort columns local prevDist = GetCursorPosition() / self:GetEffectiveScale() - self:GetLeft() local colpos, dist = colspace[1] - prevDist for i=2, #colspace do colpos = colpos + colspace[i] + (i==2 and TEXT_OFFSET or GAP) dist = abs( colpos - colspace[i]*.5 ) if dist > prevDist then dist, prevDist = i-1, -1 break else prevDist = dist end end colpos = prevDist < 0 and dist or #colspace colpos = not config.showGuildNotes and colpos == 5 and "rank" or cols[colpos] if config.sortType == colpos then config.sortDESC = not config.sortDESC else config.sortType, config.sortDESC = colpos, false end SortGuildRoster( colpos ) elseif button == "MiddleButton" and not isGuild then RemoveFriend( self.unit ) elseif IsAltKeyDown() then InviteUnit( self.unit ) elseif IsControlKeyDown() then if not isGuild then FriendsFrame.NotesID = self.index - 1 StaticPopup_Show( "SET_FRIENDNOTE", self.unit ) elseif button == "LeftButton" and CanEditPublicNote() or button ~= "LeftButton" and CanEditOfficerNote() then SetGuildRosterSelection( guildEntries[self.index][8] ) StaticPopup_Show( button == "LeftButton" and "SET_GUILDPLAYERNOTE" or "SET_GUILDOFFICERNOTE" ) end else SetItemRef("player:"..self.unit, "|Hplayer:"..self.unit.."|h["..self.unit.."]|h", "LeftButton") end end local function Scroll(self, delta) if IsControlKeyDown() then config.scale = config.scale - delta * .05 return ShowTablet(tipShown, isGuild) end slider:SetValue( sliderValue - delta * (IsModifierKeyDown() and 10 or 3) ) end local buttons = setmetatable( { }, { __index = function( table, key ) local button = CreateFrame( "Button", nil, f ) table[key] = button button.index = key button:SetNormalFontObject(GameFontNormal) button:RegisterForClicks"AnyUp" button:SetScript( "OnEnter", Menu_OnEnter ) button:SetScript( "OnLeave", Menu_OnLeave ) button:EnableMouseWheel(true) button:SetScript( "OnMouseWheel", Scroll) button.fontName = CreateFS( key, button, "LEFT" ) if key == 0 then motd = button motd.fontName:SetJustifyV"TOP" motd.fontName:SetPoint( "TOPLEFT", motd, "TOPLEFT" ) motd:SetPoint( "TOPLEFT", f, "TOPLEFT", GAP, -GAP ) else button:SetHeight( BUTTON_HEIGHT ) button.icon = button:CreateTexture() button.icon:SetWidth( ICON_SIZE ) button.icon:SetHeight( ICON_SIZE ) button.icon:SetPoint( "LEFT", button, "LEFT" ) button.fontName:SetPoint( "LEFT", button.icon, "RIGHT", TEXT_OFFSET, 0 ) button.fontLevel = CreateFS( key, button, "CENTER", button.fontName ) button.fontZone = CreateFS( key, button, "CENTER", button.fontLevel ) button.fontNotes = CreateFS( key, button, "CENTER", button.fontZone ) button.fontRank = CreateFS( key, button, "RIGHT", button.fontNotes ) end return button end } ) local function SetButtonData( index, name, class, level, zone, notes, rank, inGroup, isGrouped ) local button = buttons[index] button.fontName:SetText( name or "" ) if class then if index > 1 then local color = RAID_CLASS_COLORS[class] button.fontName:SetTextColor( color.r, color.g, color.b ) if inGroup then button.icon:SetTexture(isGrouped and "Interface\\Buttons\\UI-CheckBox-Check" or "") button.icon:SetTexCoord(.15,.85,.15,.85) else button.icon:SetTexture"Interface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes" local offset, left, right, bottom, top = 0.025, unpack( CLASS_BUTTONS[class] ) button.icon:SetTexCoord( left+offset, right-offset, bottom+offset, top-offset ) end color = GetDifficultyColor(level) button.fontLevel:SetTextColor( color.r, color.g, color.b ) button.fontZone:SetTextColor( GetZoneColor(zone or "#") ) end button.fontLevel:SetText( level or "" ) button.fontZone:SetText( zone or "" ) button.fontNotes:SetText( notes or "" ) button.fontRank:SetText( rank or "" ) return button, button.fontName:GetStringWidth(), button.fontLevel:GetStringWidth(), button.fontZone:GetStringWidth(), button.fontNotes:GetStringWidth(), button.fontRank:GetStringWidth() end return button, button.fontName:GetStringWidth() end local function UpdateScrollButtons(nbEntries) local btn = buttons[1] for i=2, #buttons do buttons[i]:Hide() end for i=2, nbEntries do button = buttons[sliderValue+i] button:SetPoint("TOPLEFT", btn, "TOPLEFT", 0, BUTTON_HEIGHT*(1-i)) button:Show() end end ShowTablet = function( self, _isGuild ) f:Show() isGuild = _isGuild f:SetScale(config.scale) MAX_ENTRIES = floor( WorldFrame:GetHeight() / ( BUTTON_HEIGHT * f:GetEffectiveScale() ) ) - (config.hideHints and 6 or 12) slider:SetHeight(BUTTON_HEIGHT*MAX_ENTRIES) local entries = isGuild and guildEntries or friendEntries local hasEntries = #entries > 0 hasSlider = #entries > MAX_ENTRIES if not hasEntries then entries[1] = new( nil, isGuild and ERR_GUILD_PLAYER_NOT_IN_GUILD or "No friends online.", "" ) end if hasSlider then slider:SetMinMaxValues( 0, #entries - MAX_ENTRIES ) slider:SetValue(sliderValue) slider:Show() else slider:Hide() end nbEntries = math.min( MAX_ENTRIES, #entries ) local nameC, levelC, zoneC, notesC, rankC = 0, 0, 0, 0, 0 local nameW, levelW, zoneW, notesW, rankW local hideNotes = isGuild and not config.showGuildNotes local extraHeight, button = 0 local isGrouped = GetNumRaidMembers()>0 and UnitInRaid or GetNumPartyMembers()>0 and UnitInParty or nil for i = 1, #entries do local entry = entries[i] button, nameW, levelW, zoneW, notesW, rankW = SetButtonData( i, entry[2], entry[3], entry[4], entry[5], entry[6], entry[7], isGrouped, isGrouped and entry[1] and isGrouped(entry[1]) ) button.unit = entry[1] button:SetScript( "OnClick", OnGuildmateClick ) if nameW > nameC then nameC = nameW end if levelW and levelW>0 then if levelW > levelC then levelC = levelW end if zoneW > zoneC then zoneC = zoneW end if notesW > notesC then notesC = notesW end if rankW > rankC then rankC = rankW end if hideNotes then button.fontNotes:Hide() else button.fontNotes:Show() end button.fontRank:SetPoint( "LEFT", hideNotes and button.fontZone or button.fontNotes, "RIGHT", GAP, 0 ) end end if hideNotes then notesC = -GAP end local maxWidth = ICON_SIZE + TEXT_OFFSET + nameC + levelC + zoneC + notesC + rankC + GAP * (isGuild and 4 or 3) if isGuild and hasEntries then SetButtonData( 0, "|cffffffffMOTD:|r "..GetGuildRosterMOTD() ) if CanEditMOTD() then motd:SetScript( "OnClick", EditMOTD ) end motd.fontName:SetWidth( maxWidth ) extraHeight = motd.fontName:GetHeight() + BUTTON_HEIGHT motd:SetWidth( maxWidth ) motd:SetHeight( extraHeight ) motd:Show() buttons[1]:SetPoint( "TOPLEFT", motd, "BOTTOMLEFT" ) else buttons[0]:Hide() buttons[1]:SetPoint( "TOPLEFT", f, "TOPLEFT", GAP, -GAP ) end UpdateScrollButtons(nbEntries) for i=1, #entries do button = buttons[i] button:SetWidth( maxWidth ) button.fontName:SetWidth(nameC) button.fontLevel:SetWidth(levelC) button.fontZone:SetWidth(zoneC) button.fontNotes:SetWidth(notesC) button.fontRank:SetWidth(rankC) end colspace[2], colspace[3], colspace[4], colspace[5], colspace[6] = nameC, levelC, zoneC, notesC, rankC if hasSlider then slider:SetPoint("TOPRIGHT", buttons[2], "TOPRIGHT", 19 + TEXT_OFFSET, BUTTON_HEIGHT*.5) end f:SetWidth( maxWidth + GAP*2 + (hasSlider and 16 + TEXT_OFFSET*2 or 0) ) f:SetHeight( BUTTON_HEIGHT * nbEntries + extraHeight + GAP*2 ) block = block or self Menu_OnEnter(block) local showBelow = select( 2, block:GetCenter() ) > ( UIParent:GetHeight() / 2 ) f:ClearAllPoints() f:SetPoint( showBelow and "TOP" or "BOTTOM", block, showBelow and "BOTTOM" or "TOP" ) if not (tipShown or config.hideHints) then ShowBlockHints() end end function f:FriendsOnEnter() if InCombatLockdown() then f:Show() return Menu_OnEnter(f) end return ShowTablet( self, false ) end function f:GuildOnEnter() if not dontShow and InCombatLockdown() then f:Show() return Menu_OnEnter(f) end for k, v in next, guildEntries do del(v) guildEntries[k]=nil end for i=1, GetNumGuildMembers(true) do local name, rank, _, level, class, zone, note, offnote, connected, status = GetGuildRosterInfo(i) if connected then if #guildEntries == 0 then guildEntries[1] = new( nil, "Name", "", "Lv", "Zone", "Notes", "Rank" ) end local notes = note ~= "" and (offnote ~= "" and format( "[%s] - \124cffff9944[%s]\124r", note, offnote ) or format( "[%s]", note )) or (offnote ~= "" and format( "\124cffff9944[%s]\124r", offnote ) or "-") guildEntries[#guildEntries+1] = new( name, status == "" and name or format( "%s %s", status, name ), L[class], level, zone, notes, rank, i ) end end f.GuildBlock.text = IsInGuild() and format(config.hideGuildTotal and" %s%d"or" %s%d/%d", config.showGuildName and GetGuildInfo"player" and GetGuildInfo"player"..": " or "", #guildEntries-1, GetNumGuildMembers(true) ) or "No Guild" if dontShow then dontShow = false else return ShowTablet(self, true) end end local ldb = LibStub("LibDataBroker-1.1") f.GuildBlock = ldb:NewDataObject( "|cFFFFB366Ara|r Guild", { type = "data source", text = GUILD, icon = "Interface\\AddOns\\Ara_Broker_Guild_Friends\\guild.tga", OnEnter = f.GuildOnEnter, OnLeave = Menu_OnLeave, OnClick = function(self, button) if button == "LeftButton" then if IsShiftKeyDown() and IsInGuild() then config.showGuildName = not config.showGuildName f.GuildBlock.text = format( " %s%d/%d", config.showGuildName and GetGuildInfo"player"..": " or "", #guildEntries-1, GetNumGuildMembers(true) ) elseif IsControlKeyDown() then config.hideHints = not config.hideHints ShowTablet( block, isGuild ) else ToggleFriendsFrame(3) end elseif button == "RightButton" and IsShiftKeyDown() then config.hideGuildTotal = not config.hideGuildTotal f:GuildOnEnter() elseif not InCombatLockdown() then config.showGuildNotes = not config.showGuildNotes ShowTablet( block, true ) end end, } ) f.FriendsBlock = ldb:NewDataObject( "|cFFFFB366Ara|r Friends", { type = "data source", text = FRIENDS, icon = "Interface\\AddOns\\Ara_Broker_Guild_Friends\\friends.tga", OnEnter = f.FriendsOnEnter, OnLeave = Menu_OnLeave, OnClick = function(self, btn) if IsControlKeyDown() then config.hideHints = not config.hideHints ShowTablet( block, isGuild ) elseif IsShiftKeyDown() then config.hideTotalFriends = not config.hideTotalFriends f:FRIENDLIST_UPDATE() elseif IsModifierKeyDown() or btn~="LeftButton" then StaticPopup_Show"ADD_FRIEND" else ToggleFriendsFrame(1) end end, } ) local orgGuildRoster = GuildRoster GuildRoster = function(...) t.guildTimer = 0 return orgGuildRoster(...) end local orgShowFriends = ShowFriends ShowFriends = function(...) t.friendTimer = 0 return orgShowFriends(...) end local function OnUpdate( self, elapsed ) t.guildTimer = t.guildTimer + elapsed if t.guildTimer > 15 then if IsInGuild() then GuildRoster() else t.guildTimer = 0 end end t.friendTimer = t.friendTimer + elapsed if t.friendTimer > 15 then ShowFriends() end end function f:ADDON_LOADED( event, addon ) if addon ~= "Ara_Broker_Guild_Friends" then return else f:UnregisterEvent(event) end AraBrokerGuildFriendsDB = AraBrokerGuildFriendsDB or defaultConfig config = AraBrokerGuildFriendsDB for k, v in next, defaultConfig do if config[k]==nil then config[k]=v end end if config.reloading then config.reloading = nil else SortGuildRoster(config.sortType) if config.sortDESC then SortGuildRoster(config.sortType) end end for eng, loc in next, LOCALIZED_CLASS_NAMES_MALE do L[loc] = eng end for eng, loc in next, LOCALIZED_CLASS_NAMES_FEMALE do L[loc] = eng end tip = GameTooltip horde = UnitFactionGroup"player" == "Horde" if Skinner then Skinner:applySkin(self) else f:SetBackdrop( { bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Tooltips\\UI-Tooltip-Border", tile=true, tileSize=12, edgeSize=12, insets = { left=2, right=2, top=2, bottom=2 } } ) f:SetBackdropColor( 0, 0, 0, .75 ) end f.red, f.green, f.blue, f.alpha = f:GetBackdropColor() f:SetFrameStrata"TOOLTIP" f:SetClampedToScreen(true) f:EnableMouse(true) t:SetScript( "OnUpdate", OnUpdate ) f:SetScript( "OnEnter", Menu_OnEnter ) f:SetScript( "OnLeave", Menu_OnLeave ) f:RegisterEvent"GUILD_ROSTER_UPDATE" f:RegisterEvent"PLAYER_GUILD_UPDATE" f:RegisterEvent"FRIENDLIST_UPDATE" f:RegisterEvent"CHAT_MSG_SYSTEM" slider = CreateFrame("Slider", nil, f) slider:SetWidth(16) slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal") slider:SetBackdrop( { bgFile = "Interface\\Buttons\\UI-SliderBar-Background", edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", edgeSize = 8, tile = true, tileSize = 8, insets = {left = 3, right = 3, top = 6, bottom = 6} } ) slider:SetValueStep(1) slider:SetScript( "OnLeave", Menu_OnLeave ) slider:SetScript( "OnValueChanged", function(self, value) if hasSlider then sliderValue = value if block then UpdateScrollButtons(MAX_ENTRIES) end end end ) ShowFriends() if IsInGuild(buttons[0]) then GuildRoster() else t.guildTimer = 0 end f:GuildOnEnter() f.ADDON_LOADED = nil end f:RegisterEvent"ADDON_LOADED" f:SetScript( "OnEvent", function(self, event, ...) return self[event](self, event, ...) end ) f:SetScript( "OnShow", function(self) f:SetBackdropColor( InCombatLockdown() and f.red+.25 or f.red, f.green, f.blue, f.alpha) end )