map markers, search, export\import like retail, some bug fixes (#21)

* fix middle button on click map

* fix pvp buttons

* map markers search dev tools and fixes

* fixes and loc

* loc

* fix

* typo

* typo

* fix
This commit is contained in:
fxpw(Toxa)
2023-01-07 12:46:08 +03:00
committed by GitHub
parent 8826ee51bf
commit f097d91fb1
16 changed files with 4507 additions and 296 deletions
+3 -2
View File
@@ -58,6 +58,7 @@ E.myLocalizedClass, E.myclass = UnitClass("player") -- On Ascension, this is al
E.myLocalizedRace, E.myrace = UnitRace("player") E.myLocalizedRace, E.myrace = UnitRace("player")
E.myname = UnitName("player") E.myname = UnitName("player")
E.myrealm = GetRealmName() E.myrealm = GetRealmName()
E.mynameRealm = format('%s - %s', E.myname, E.myrealm) -- contains spaces/dashes in realm (for profile keys)
E.version = GetAddOnMetadata("ElvUI", "Version") E.version = GetAddOnMetadata("ElvUI", "Version")
E.wowpatch, E.wowbuild = GetBuildInfo() E.wowpatch, E.wowbuild = GetBuildInfo()
E.wowbuild = tonumber(E.wowbuild) E.wowbuild = tonumber(E.wowbuild)
@@ -802,8 +803,8 @@ function E:UpdateAll(ignoreInstall)
Chat:UpdateAnchors() Chat:UpdateAnchors()
end end
DataBars:EnableDisable_ExperienceBar() DataBars:ExperienceBar_Toggle()
DataBars:EnableDisable_ReputationBar() DataBars:ReputationBar_Toggle()
DataBars:UpdateDataBarDimensions() DataBars:UpdateDataBarDimensions()
DataTexts:LoadDataTexts() DataTexts:LoadDataTexts()
+286 -267
View File
@@ -1,104 +1,195 @@
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB local E, L, V, P, G = unpack(select(2,...))
local D = E:GetModule("Distributor") local D = E:GetModule('Distributor')
local LibCompress = E.Libs.Compress local NP = E:GetModule('NamePlates')
local LibBase64 = E.Libs.Base64 local LibDeflate = E.Libs.Deflate
local _G = _G
local tonumber, type, gsub, pairs, pcall, loadstring = tonumber, type, gsub, pairs, pcall, loadstring
local len, format, split, strmatch = strlen, format, strsplit, strmatch
--Lua functions
local loadstring = loadstring
local pcall = pcall
local setfenv = setfenv
local tonumber = tonumber
local type = type
local format, gsub, len, split, sub = string.format, string.gsub, string.len, string.split, string.sub
--WoW API / Variables
local CreateFrame = CreateFrame local CreateFrame = CreateFrame
local GetNumRaidMembers, UnitInRaid = GetNumRaidMembers, UnitInRaid local IsInRaid, UnitInRaid = IsInRaid, UnitInRaid
local GetNumPartyMembers, UnitInParty = GetNumPartyMembers, UnitInParty local IsInGroup, UnitInParty = IsInGroup, UnitInParty
-- local LE_PARTY_CATEGORY_HOME = LE_PARTY_CATEGORY_HOME
-- local LE_PARTY_CATEGORY_INSTANCE = LE_PARTY_CATEGORY_INSTANCE
local ACCEPT, CANCEL, YES, NO = ACCEPT, CANCEL, YES, NO local ACCEPT, CANCEL, YES, NO = ACCEPT, CANCEL, YES, NO
-- GLOBALS: ElvDB, ElvPrivateDB
---------------------------------- local EXPORT_PREFIX = '!E1!' -- also in Options StyleFilters
-- CONSTANTS local REQUEST_PREFIX = 'ELVUI_REQUEST'
---------------------------------- local REPLY_PREFIX = 'ELVUI_REPLY'
local TRANSFER_PREFIX = 'ELVUI_TRANSFER'
local TRANSFER_COMPLETE_PREFIX = 'ELVUI_COMPLETE'
local REQUEST_PREFIX = "ELVUI_REQUEST" -- Set compression
local REPLY_PREFIX = "ELVUI_REPLY" LibDeflate.compressLevel = { level = 5 }
local TRANSFER_PREFIX = "ELVUI_TRANSFER"
local TRANSFER_COMPLETE_PREFIX = "ELVUI_COMPLETE"
local ACECOMMPREFIXES = {
[TRANSFER_PREFIX.."\001"] = true,
[TRANSFER_PREFIX.."\002"] = true,
[TRANSFER_PREFIX.."\003"] = true,
}
-- The active downloads -- The active downloads
local Downloads = {} local Downloads = {}
local Uploads = {} local Uploads = {}
--Keys that should not be exported
D.blacklistedKeys = {
profile = {
gridSize = true,
general = {
cropIcon = true,
numberPrefixStyle = true
},
chat = {
hideVoiceButtons = true
},
bags = {
shownBags = true
}
},
private = {},
global = {
profileCopy = true,
general = {
AceGUI = true,
UIScale = true,
locale = true,
version = true,
eyefinity = true,
ultrawide = true,
disableTutorialButtons = true,
allowDistributor = true
},
chat = {
classColorMentionExcludedNames = true
},
datatexts = {
newPanelInfo = true,
settings = {
Currencies = {
tooltipData = true
}
}
},
nameplates = {
filters = true
},
unitframe = {
aurafilters = true,
aurawatch = true,
newCustomText = true,
}
},
}
--Keys that auto or user generated tables.
D.GeneratedKeys = {
profile = {
convertPages = true,
movers = true,
actionbar = {},
nameplates = { -- this is supposed to have an 's' because yeah, oh well
filters = true
},
datatexts = {
panels = true,
},
unitframe = {
units = {} -- required for the scope below for customTexts
}
},
private = {
theme = true,
install_complete = true
},
global = {
datatexts = {
customPanels = true,
customCurrencies = true
},
unitframe = {
AuraBarColors = true,
aurafilters = true,
aurawatch = true
},
nameplates = {
filters = true
}
}
}
do
local units = D.GeneratedKeys.profile.unitframe.units
for unit in pairs(P.unitframe.units) do
units[unit] = {customTexts = true}
end
for i = 1, 10 do
D.GeneratedKeys.profile.actionbar['bar'..i] = { paging = true }
end
end
function D:Initialize() function D:Initialize()
self.Initialized = true self.Initialized = true
self:RegisterComm(REQUEST_PREFIX)
self:RegisterEvent("CHAT_MSG_ADDON")
self.statusBar = CreateFrame("StatusBar", "ElvUI_Download", E.UIParent) D:UpdateSettings()
E:RegisterStatusBar(self.statusBar)
self.statusBar:CreateBackdrop("Default") self.statusBar = CreateFrame('StatusBar', 'ElvUI_Download', E.UIParent)
self.statusBar:CreateBackdrop()
self.statusBar:SetStatusBarTexture(E.media.normTex) self.statusBar:SetStatusBarTexture(E.media.normTex)
self.statusBar:SetStatusBarColor(0.95, 0.15, 0.15) self.statusBar:SetStatusBarColor(0.95, 0.15, 0.15)
self.statusBar:Size(250, 18) self.statusBar:Size(250, 18)
self.statusBar.text = self.statusBar:CreateFontString(nil, "OVERLAY") self.statusBar.text = self.statusBar:CreateFontString(nil, 'OVERLAY')
self.statusBar.text:FontTemplate() self.statusBar.text:FontTemplate()
self.statusBar.text:Point("CENTER") self.statusBar.text:Point('CENTER')
self.statusBar:Hide() self.statusBar:Hide()
E:RegisterStatusBar(self.statusBar)
end
function D:UpdateSettings()
if E.global.general.allowDistributor then
self:RegisterComm(REQUEST_PREFIX)
self:RegisterEvent('CHAT_MSG_ADDON')
else
self:UnregisterComm(REQUEST_PREFIX)
self:UnregisterEvent('CHAT_MSG_ADDON')
end
end end
-- Used to start uploads -- Used to start uploads
function D:Distribute(target, otherServer, isGlobal) function D:Distribute(target, otherServer, isGlobal)
local profileKey, data local profileKey, data
if not isGlobal then if not isGlobal then
if ElvDB.profileKeys then profileKey = ElvDB.profileKeys and ElvDB.profileKeys[E.mynameRealm]
profileKey = ElvDB.profileKeys[E.myname.." - "..E.myrealm]
end
data = ElvDB.profiles[profileKey] data = ElvDB.profiles[profileKey]
else else
profileKey = "global" profileKey = 'global'
data = ElvDB.global data = ElvDB.global
end end
if not data or not profileKey then return end if not data then return end
data = E:RemoveTableDuplicates(data, isGlobal and G or P)
local serialData = self:Serialize(data) local serialData = self:Serialize(data)
local length = len(serialData) local length = len(serialData)
local message = format("%s:%d:%s", profileKey, length, target) local message = format('%s:%d:%s', profileKey, length, target)
Uploads[profileKey] = { Uploads[profileKey] = {serialData = serialData, target = target}
serialData = serialData,
target = target,
}
if otherServer then if otherServer then
local numParty, numRaid = GetNumPartyMembers(), GetNumRaidMembers() if IsInRaid() and UnitInRaid('target') then
if numRaid > 0 and UnitInRaid("target") then self:SendCommMessage(REQUEST_PREFIX, message, 'RAID')
self:SendCommMessage(REQUEST_PREFIX, message, "RAID") elseif IsInGroup() and UnitInParty('target') then
elseif numParty > 0 and UnitInParty("target") then self:SendCommMessage(REQUEST_PREFIX, message, 'PARTY')
self:SendCommMessage(REQUEST_PREFIX, message, "PARTY")
else else
E:Print(L["Must be in group with the player if he isn't on the same server as you."]) E:Print(L["Must be in group with the player if he isn't on the same server as you."])
return return
end end
else else
self:SendCommMessage(REQUEST_PREFIX, message, "WHISPER", target) self:SendCommMessage(REQUEST_PREFIX, message, 'WHISPER', target)
end end
self:RegisterComm(REPLY_PREFIX) self:RegisterComm(REPLY_PREFIX)
E:StaticPopup_Show("DISTRIBUTOR_WAITING") E:StaticPopup_Show('DISTRIBUTOR_WAITING')
end end
function D:CHAT_MSG_ADDON(_, prefix, message, _, sender) function D:CHAT_MSG_ADDON(_, prefix, message, _, sender)
if not ACECOMMPREFIXES[prefix] or not Downloads[sender] then return end if prefix ~= TRANSFER_PREFIX or not Downloads[sender] then return end
local cur = len(message) local cur = len(message)
local max = Downloads[sender].length local max = Downloads[sender].length
Downloads[sender].current = Downloads[sender].current + cur Downloads[sender].current = Downloads[sender].current + cur
@@ -110,29 +201,21 @@ function D:CHAT_MSG_ADDON(_, prefix, message, _, sender)
self.statusBar:SetValue(Downloads[sender].current) self.statusBar:SetValue(Downloads[sender].current)
end end
function D:UpdateSendProgress(sentBytes, totalBytes)
self.statusBar:SetValue(sentBytes)
if sentBytes == totalBytes then
E:StaticPopupSpecial_Hide(self.statusBar)
end
end
function D:OnCommReceived(prefix, msg, dist, sender) function D:OnCommReceived(prefix, msg, dist, sender)
if prefix == REQUEST_PREFIX then if prefix == REQUEST_PREFIX then
local profile, length, sendTo = split(":", msg) local profile, length, sendTo = split(':', msg)
if dist ~= "WHISPER" and sendTo ~= E.myname then if dist ~= 'WHISPER' and sendTo ~= E.myname then
return return
end end
if self.statusBar:IsShown() then if self.statusBar:IsShown() then
self:SendCommMessage(REPLY_PREFIX, profile..":NO", dist, sender) self:SendCommMessage(REPLY_PREFIX, profile..':NO', dist, sender)
return return
end end
local textString = format(L["%s is attempting to share the profile %s with you. Would you like to accept the request?"], sender, profile) local textString = format(L["%s is attempting to share the profile %s with you. Would you like to accept the request?"], sender, profile)
if profile == "global" then if profile == 'global' then
textString = format(L["%s is attempting to share his filters with you. Would you like to accept the request?"], sender) textString = format(L["%s is attempting to share his filters with you. Would you like to accept the request?"], sender)
end end
@@ -143,44 +226,40 @@ function D:OnCommReceived(prefix, msg, dist, sender)
self.statusBar:SetValue(0) self.statusBar:SetValue(0)
self.statusBar.text:SetFormattedText(L["Data From: %s"], sender) self.statusBar.text:SetFormattedText(L["Data From: %s"], sender)
E:StaticPopupSpecial_Show(self.statusBar) E:StaticPopupSpecial_Show(self.statusBar)
self:SendCommMessage(REPLY_PREFIX, profile..":YES", dist, sender) self:SendCommMessage(REPLY_PREFIX, profile..':YES', dist, sender)
end, end,
OnCancel = function() OnCancel = function()
self:SendCommMessage(REPLY_PREFIX, profile..":NO", dist, sender) self:SendCommMessage(REPLY_PREFIX, profile..':NO', dist, sender)
end, end,
button1 = ACCEPT, button1 = ACCEPT,
button2 = CANCEL, button2 = CANCEL,
timeout = 32, timeout = 30,
whileDead = 1, whileDead = 1,
hideOnEscape = 1 hideOnEscape = 1,
} }
E:StaticPopup_Show("DISTRIBUTOR_RESPONSE")
E:StaticPopup_Show('DISTRIBUTOR_RESPONSE')
Downloads[sender] = { Downloads[sender] = {
current = 0, current = 0,
length = tonumber(length), length = tonumber(length),
profile = profile profile = profile,
} }
self:RegisterComm(TRANSFER_PREFIX) self:RegisterComm(TRANSFER_PREFIX)
elseif prefix == REPLY_PREFIX then elseif prefix == REPLY_PREFIX then
self:UnregisterComm(REPLY_PREFIX) self:UnregisterComm(REPLY_PREFIX)
E:StaticPopup_Hide("DISTRIBUTOR_WAITING") E:StaticPopup_Hide('DISTRIBUTOR_WAITING')
local profileKey, response = split(":", msg)
if response == "YES" then
self.statusBar:SetMinMaxValues(0, len(Uploads[profileKey].serialData))
self.statusBar:SetValue(0)
self.statusBar.text:SetFormattedText(L["Data To: %s"], sender)
E:StaticPopupSpecial_Show(self.statusBar)
local profileKey, response = split(':', msg)
if response == 'YES' then
self:RegisterComm(TRANSFER_COMPLETE_PREFIX) self:RegisterComm(TRANSFER_COMPLETE_PREFIX)
self:SendCommMessage(TRANSFER_PREFIX, Uploads[profileKey].serialData, dist, Uploads[profileKey].target, "BULK", self.UpdateSendProgress, self) self:SendCommMessage(TRANSFER_PREFIX, Uploads[profileKey].serialData, dist, Uploads[profileKey].target)
Uploads[profileKey] = nil
else else
E:StaticPopup_Show("DISTRIBUTOR_REQUEST_DENIED") E:StaticPopup_Show('DISTRIBUTOR_REQUEST_DENIED')
Uploads[profileKey] = nil
end end
Uploads[profileKey] = nil
elseif prefix == TRANSFER_PREFIX then elseif prefix == TRANSFER_PREFIX then
self:UnregisterComm(TRANSFER_PREFIX) self:UnregisterComm(TRANSFER_PREFIX)
E:StaticPopupSpecial_Hide(self.statusBar) E:StaticPopupSpecial_Hide(self.statusBar)
@@ -191,7 +270,7 @@ function D:OnCommReceived(prefix, msg, dist, sender)
if success then if success then
local textString = format(L["Profile download complete from %s, would you like to load the profile %s now?"], sender, profileKey) local textString = format(L["Profile download complete from %s, would you like to load the profile %s now?"], sender, profileKey)
if profileKey == "global" then if profileKey == 'global' then
textString = format(L["Filter download complete from %s, would you like to apply changes now?"], sender) textString = format(L["Filter download complete from %s, would you like to apply changes now?"], sender)
else else
if not ElvDB.profiles[profileKey] then if not ElvDB.profiles[profileKey] then
@@ -206,10 +285,10 @@ function D:OnCommReceived(prefix, msg, dist, sender)
maxLetters = 127, maxLetters = 127,
OnAccept = function(popup) OnAccept = function(popup)
ElvDB.profiles[popup.editBox:GetText()] = data ElvDB.profiles[popup.editBox:GetText()] = data
E.Libs.AceAddon:GetAddon("ElvUI").data:SetProfile(popup.editBox:GetText()) E.Libs.AceAddon:GetAddon('ElvUI').data:SetProfile(popup.editBox:GetText())
E:UpdateAll(true) E:UpdateAll(true)
-- E:StaggeredUpdateAll()
Downloads[sender] = nil Downloads[sender] = nil
E:StaticPopup_Show("CONFIG_RL")
end, end,
OnShow = function(popup) popup.editBox:SetText(profileKey) popup.editBox:SetFocus() end, OnShow = function(popup) popup.editBox:SetText(profileKey) popup.editBox:SetFocus() end,
timeout = 0, timeout = 0,
@@ -219,8 +298,8 @@ function D:OnCommReceived(prefix, msg, dist, sender)
preferredIndex = 3 preferredIndex = 3
} }
E:StaticPopup_Show("DISTRIBUTOR_CONFIRM") E:StaticPopup_Show('DISTRIBUTOR_CONFIRM')
self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, "COMPLETE", dist, sender) self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, 'COMPLETE', dist, sender)
return return
end end
end end
@@ -228,11 +307,12 @@ function D:OnCommReceived(prefix, msg, dist, sender)
E.PopupDialogs.DISTRIBUTOR_CONFIRM = { E.PopupDialogs.DISTRIBUTOR_CONFIRM = {
text = textString, text = textString,
OnAccept = function() OnAccept = function()
if profileKey == "global" then if profileKey == 'global' then
E:CopyTable(ElvDB.global, data) E:CopyTable(ElvDB.global, data)
E:UpdateAll(true) E:UpdateAll(true)
-- E:StaggeredUpdateAll()
else else
E.Libs.AceAddon:GetAddon("ElvUI").data:SetProfile(profileKey) E.Libs.AceAddon:GetAddon('ElvUI').data:SetProfile(profileKey)
end end
Downloads[sender] = nil Downloads[sender] = nil
end, end,
@@ -242,123 +322,89 @@ function D:OnCommReceived(prefix, msg, dist, sender)
button1 = YES, button1 = YES,
button2 = NO, button2 = NO,
whileDead = 1, whileDead = 1,
hideOnEscape = 1 hideOnEscape = 1,
} }
E:StaticPopup_Show("DISTRIBUTOR_CONFIRM") E:StaticPopup_Show('DISTRIBUTOR_CONFIRM')
self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, "COMPLETE", dist, sender) self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, 'COMPLETE', dist, sender)
else else
E:StaticPopup_Show("DISTRIBUTOR_FAILED") E:StaticPopup_Show('DISTRIBUTOR_FAILED')
self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, "FAILED", dist, sender) self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, 'FAILED', dist, sender)
end end
elseif prefix == TRANSFER_COMPLETE_PREFIX then elseif prefix == TRANSFER_COMPLETE_PREFIX then
self:UnregisterComm(TRANSFER_COMPLETE_PREFIX) self:UnregisterComm(TRANSFER_COMPLETE_PREFIX)
if msg == "COMPLETE" then if msg == 'COMPLETE' then
E:StaticPopup_Show("DISTRIBUTOR_SUCCESS") E:StaticPopup_Show('DISTRIBUTOR_SUCCESS')
else else
E:StaticPopup_Show("DISTRIBUTOR_FAILED") E:StaticPopup_Show('DISTRIBUTOR_FAILED')
end end
end end
end end
--Keys that should not be exported
local blacklistedKeys = {
profile = {
general = {
numberPrefixStyle = true,
}
},
private = {},
global = {
general = {
UIScale = true,
locale = true,
eyefinity = true,
ignoreScalePopup = true
},
chat = {
classColorMentionExcludedNames = true
},
unitframe = {
spellRangeCheck = true
}
}
}
local function GetProfileData(profileType) local function GetProfileData(profileType)
if not profileType or type(profileType) ~= "string" then if not profileType or type(profileType) ~= 'string' then
E:Print("Bad argument #1 to 'GetProfileData' (string expected)") E:Print('Bad argument #1 to "GetProfileData" (string expected)')
return return
end end
local profileKey local profileData, profileKey = {}
local profileData = {} if profileType == 'profile' then
if profileType == "profile" then
if ElvDB.profileKeys then
profileKey = ElvDB.profileKeys[E.myname.." - "..E.myrealm]
end
--Copy current profile data --Copy current profile data
profileKey = ElvDB.profileKeys and ElvDB.profileKeys[E.mynameRealm]
profileData = E:CopyTable(profileData, ElvDB.profiles[profileKey]) profileData = E:CopyTable(profileData, ElvDB.profiles[profileKey])
--This table will also hold all default values, not just the changed settings. --This table will also hold all default values, not just the changed settings.
--This makes the table huge, and will cause the WoW client to lock up for several seconds. --This makes the table huge, and will cause the WoW client to lock up for several seconds.
--We compare against the default table and remove all duplicates from our table. The table is now much smaller. --We compare against the default table and remove all duplicates from our table. The table is now much smaller.
profileData = E:RemoveTableDuplicates(profileData, P) profileData = E:RemoveTableDuplicates(profileData, P, D.GeneratedKeys.profile)
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.profile) profileData = E:FilterTableFromBlacklist(profileData, D.blacklistedKeys.profile)
elseif profileType == "private" then elseif profileType == 'private' then
local privateProfileKey = E.myname.." - "..E.myrealm local privateKey = ElvPrivateDB.profileKeys and ElvPrivateDB.profileKeys[E.mynameRealm]
profileKey = "private" profileData = E:CopyTable(profileData, ElvPrivateDB.profiles[privateKey])
profileData = E:RemoveTableDuplicates(profileData, V, D.GeneratedKeys.private)
profileData = E:CopyTable(profileData, ElvPrivateDB.profiles[privateProfileKey]) profileData = E:FilterTableFromBlacklist(profileData, D.blacklistedKeys.private)
profileData = E:RemoveTableDuplicates(profileData, V) profileKey = 'private'
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.private) elseif profileType == 'global' then
elseif profileType == "global" then
profileKey = "global"
profileData = E:CopyTable(profileData, ElvDB.global) profileData = E:CopyTable(profileData, ElvDB.global)
profileData = E:RemoveTableDuplicates(profileData, G) profileData = E:RemoveTableDuplicates(profileData, G, D.GeneratedKeys.global)
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.global) profileData = E:FilterTableFromBlacklist(profileData, D.blacklistedKeys.global)
elseif profileType == "filters" then profileKey = 'global'
profileKey = "filters" elseif profileType == 'filters' then
profileData.unitframe = {} profileData.unitframe = {}
profileData.unitframe.aurafilters = {} profileData.unitframe.aurafilters = E:CopyTable({}, ElvDB.global.unitframe.aurafilters)
profileData.unitframe.aurafilters = E:CopyTable(profileData.unitframe.aurafilters, ElvDB.global.unitframe.aurafilters) profileData.unitframe.aurawatch = E:CopyTable({}, ElvDB.global.unitframe.aurawatch)
profileData.unitframe.buffwatch = {} profileData = E:RemoveTableDuplicates(profileData, G, D.GeneratedKeys.global)
profileData.unitframe.buffwatch = E:CopyTable(profileData.unitframe.buffwatch, ElvDB.global.unitframe.buffwatch) profileKey = 'filters'
profileData = E:RemoveTableDuplicates(profileData, G) elseif profileType == 'styleFilters' then
elseif profileType == "styleFilters" then profileKey = 'styleFilters'
profileKey = "styleFilters"
profileData.nameplates = {} profileData.nameplates = {}
profileData.nameplates.filters = {} profileData.nameplates.filters = E:CopyTable({}, ElvDB.global.nameplates.filters)
profileData.nameplates.filters = E:CopyTable(profileData.nameplates.filters, ElvDB.global.nameplates.filters) NP:StyleFilterClearDefaults(profileData.nameplates.filters)
profileData = E:RemoveTableDuplicates(profileData, G) profileData = E:RemoveTableDuplicates(profileData, G, D.GeneratedKeys.global)
end end
return profileKey, profileData return profileKey, profileData
end end
local function GetProfileExport(profileType, exportFormat) local function GetProfileExport(profileType, exportFormat)
local profileExport, exportString
local profileKey, profileData = GetProfileData(profileType) local profileKey, profileData = GetProfileData(profileType)
local profileExport
if not profileKey or not profileData or (profileData and type(profileData) ~= "table") then if not profileKey or not profileData or (profileData and type(profileData) ~= 'table') then
E:Print("Error getting data from 'GetProfileData'") E:Print('Error getting data from "GetProfileData"')
return return
end end
if exportFormat == "text" then if exportFormat == 'text' then
local serialData = D:Serialize(profileData) local serialData = D:Serialize(profileData)
exportString = D:CreateProfileExport(serialData, profileType, profileKey) local exportString = D:CreateProfileExport(serialData, profileType, profileKey)
local compressedData = LibCompress:Compress(exportString) local compressedData = LibDeflate:CompressDeflate(exportString, LibDeflate.compressLevel)
local encodedData = LibBase64:Encode(compressedData) local printableString = LibDeflate:EncodeForPrint(compressedData)
profileExport = encodedData profileExport = printableString and format('%s%s', EXPORT_PREFIX, printableString) or nil
elseif exportFormat == "luaTable" then elseif exportFormat == 'luaTable' then
exportString = E:TableToLuaString(profileData) local exportString = E:TableToLuaString(profileData)
profileExport = D:CreateProfileExport(exportString, profileType, profileKey) profileExport = D:CreateProfileExport(exportString, profileType, profileKey)
elseif exportFormat == "luaPlugin" then elseif exportFormat == 'luaPlugin' then
profileExport = E:ProfileTableToPluginFormat(profileData, profileType) profileExport = E:ProfileTableToPluginFormat(profileData, profileType)
end end
@@ -366,91 +412,67 @@ local function GetProfileExport(profileType, exportFormat)
end end
function D:CreateProfileExport(dataString, profileType, profileKey) function D:CreateProfileExport(dataString, profileType, profileKey)
local returnString return (profileType == 'profile' and format('%s::%s::%s', dataString, profileType, profileKey)) or format('%s::%s', dataString, profileType)
if profileType == "profile" then
returnString = format("%s::%s::%s", dataString, profileType, profileKey)
else
returnString = format("%s::%s", dataString, profileType)
end
return returnString
end end
function D:GetImportStringType(dataString) function D:GetImportStringType(dataString)
local stringType = "" return (strmatch(dataString, '^'..EXPORT_PREFIX) and 'Deflate') or (strmatch(dataString, '^{') and 'Table') or ''
if LibBase64:IsBase64(dataString) then
stringType = "Base64"
elseif sub(dataString, 1, 1) == "{" then --Basic check to weed out obviously wrong strings
stringType = "Table"
end
return stringType
end end
function D:Decode(dataString) function D:Decode(dataString)
local profileInfo, profileType, profileKey, profileData local profileInfo, profileType, profileKey, profileData
local stringType = self:GetImportStringType(dataString) local stringType = self:GetImportStringType(dataString)
if stringType == "Base64" then if stringType == 'Deflate' then
local decodedData = LibBase64:Decode(dataString) local data = gsub(dataString, '^'..EXPORT_PREFIX, '')
local decompressedData, decompressedMessage = LibCompress:Decompress(decodedData) local decodedData = LibDeflate:DecodeForPrint(data)
local decompressed = LibDeflate:DecompressDeflate(decodedData)
if not decompressedData then if not decompressed then
E:Print("Error decompressing data:", decompressedMessage) E:Print('Error decompressing data.')
return return
end end
local serializedData, success local serializedData, success
serializedData, profileInfo = E:SplitString(decompressedData, "^^::") -- "^^" indicates the end of the AceSerializer string serializedData, profileInfo = E:SplitString(decompressed, '^^::') -- '^^' indicates the end of the AceSerializer string
if not profileInfo then if not profileInfo then
E:Print("Error importing profile. String is invalid or corrupted!") E:Print('Error importing profile. String is invalid or corrupted!')
return return
end end
serializedData = format("%s%s", serializedData, "^^") --Add back the AceSerializer terminator serializedData = format('%s%s', serializedData, '^^') --Add back the AceSerializer terminator
profileType, profileKey = E:SplitString(profileInfo, "::") profileType, profileKey = E:SplitString(profileInfo, '::')
success, profileData = D:Deserialize(serializedData) success, profileData = D:Deserialize(serializedData)
if not success then if not success then
E:Print("Error deserializing:", profileData) E:Print('Error deserializing:', profileData)
return return
end end
elseif stringType == "Table" then elseif stringType == 'Table' then
local profileDataAsString local profileDataAsString
profileDataAsString, profileInfo = E:SplitString(dataString, "}::") -- "}::" indicates the end of the table profileDataAsString, profileInfo = E:SplitString(dataString, '}::') -- '}::' indicates the end of the table
if not profileInfo then if not profileInfo then
E:Print("Error extracting profile info. Invalid import string!") E:Print('Error extracting profile info. Invalid import string!')
return return
end end
if not profileDataAsString then if not profileDataAsString then
E:Print("Error extracting profile data. Invalid import string!") E:Print('Error extracting profile data. Invalid import string!')
return return
end end
profileDataAsString = format("%s%s", profileDataAsString, "}") --Add back the missing "}" profileDataAsString = format('%s%s', profileDataAsString, '}') --Add back the missing '}'
profileDataAsString = gsub(profileDataAsString, "\124\124", "\124") --Remove escape pipe characters profileDataAsString = gsub(profileDataAsString, '\124\124', '\124') --Remove escape pipe characters
profileType, profileKey = E:SplitString(profileInfo, "::") profileType, profileKey = E:SplitString(profileInfo, '::')
local func, err = loadstring(format("%s %s", "return", profileDataAsString)) local profileMessage
local profileToTable = loadstring(format('%s %s', 'return', profileDataAsString))
if profileToTable then profileMessage, profileData = pcall(profileToTable) end
if func then if profileMessage and (not profileData or type(profileData) ~= 'table') then
setfenv(func, {}) -- execute code in an empty environment E:Print('Error converting lua string to table:', profileMessage)
local success, res = pcall(func)
if success then
profileData = res
else
err = res
end
end
if err then
E:Print("Error converting lua string to table:", err)
return return
end end
end end
@@ -459,52 +481,46 @@ function D:Decode(dataString)
end end
local function SetImportedProfile(profileType, profileKey, profileData, force) local function SetImportedProfile(profileType, profileKey, profileData, force)
D.profileType = nil if profileType == 'profile' then
D.profileKey = nil profileData = E:FilterTableFromBlacklist(profileData, D.blacklistedKeys.profile) --Remove unwanted options from import
D.profileData = nil
if profileType == "profile" then
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.profile) --Remove unwanted options from import
if not ElvDB.profiles[profileKey] or force then if not ElvDB.profiles[profileKey] or force then
if force and E.data.keys.profile == profileKey then if force and E.data.keys.profile == profileKey then
--Overwriting an active profile doesn't update when calling SetProfile --Overwriting an active profile doesn't update when calling SetProfile
--So make it look like we use a different profile --So make it look like we use a different profile
local tempKey = profileKey.."_Temp" E.data.keys.profile = profileKey..'_Temp'
E.data.keys.profile = tempKey
end end
ElvDB.profiles[profileKey] = profileData ElvDB.profiles[profileKey] = profileData
--Calling SetProfile will now update all settings correctly --Calling SetProfile will now update all settings correctly
E.data:SetProfile(profileKey) E.data:SetProfile(profileKey)
else else
D.profileType = profileType E:StaticPopup_Show('IMPORT_PROFILE_EXISTS', nil, nil, {profileKey = profileKey, profileType = profileType, profileData = profileData})
D.profileKey = profileKey
D.profileData = profileData
E:StaticPopup_Show("IMPORT_PROFILE_EXISTS")
return
end end
elseif profileType == "private" then elseif profileType == 'private' then
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.private) --Remove unwanted options from import local privateKey = ElvPrivateDB.profileKeys and ElvPrivateDB.profileKeys[E.mynameRealm]
local pfKey = ElvPrivateDB.profileKeys[E.myname.." - "..E.myrealm] if privateKey then
ElvPrivateDB.profiles[pfKey] = profileData profileData = E:FilterTableFromBlacklist(profileData, D.blacklistedKeys.private) --Remove unwanted options from import
E:StaticPopup_Show("IMPORT_RL") ElvPrivateDB.profiles[privateKey] = profileData
elseif profileType == "global" then E:StaticPopup_Show('IMPORT_RL')
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.global) --Remove unwanted options from import end
elseif profileType == 'global' then
profileData = E:FilterTableFromBlacklist(profileData, D.blacklistedKeys.global) --Remove unwanted options from import
E:CopyTable(ElvDB.global, profileData) E:CopyTable(ElvDB.global, profileData)
E:StaticPopup_Show("IMPORT_RL") E:StaticPopup_Show('IMPORT_RL')
elseif profileType == "filters" then elseif profileType == 'filters' then
E:CopyTable(ElvDB.global.unitframe, profileData.unitframe) E:CopyTable(ElvDB.global.unitframe, profileData.unitframe)
elseif profileType == "styleFilters" then E:UpdateUnitFrames()
E:CopyTable(ElvDB.global.nameplates, profileData.nameplates) elseif profileType == 'styleFilters' then
E:CopyTable(ElvDB.global.nameplates, profileData.nameplates or profileData.nameplate)
E:UpdateNamePlates()
end end
--Update all ElvUI modules
E:UpdateAll(true)
end end
function D:ExportProfile(profileType, exportFormat) function D:ExportProfile(profileType, exportFormat)
if not profileType or not exportFormat then if not profileType or not exportFormat then
E:Print("Bad argument to 'ExportProfile' (string expected)") E:Print('Bad argument to "ExportProfile" (string expected)')
return return
end end
@@ -516,12 +532,12 @@ end
function D:ImportProfile(dataString) function D:ImportProfile(dataString)
local profileType, profileKey, profileData = self:Decode(dataString) local profileType, profileKey, profileData = self:Decode(dataString)
if not profileData or type(profileData) ~= "table" then if not profileData or type(profileData) ~= 'table' then
E:Print("Error: something went wrong when converting string to table!") E:Print('Error: something went wrong when converting string to table!')
return return
end end
if profileType and ((profileType == "profile" and profileKey) or profileType ~= "profile") then if profileType and ((profileType == 'profile' and profileKey) or profileType ~= 'profile') then
SetImportedProfile(profileType, profileKey, profileData) SetImportedProfile(profileType, profileKey, profileData)
end end
@@ -532,28 +548,28 @@ E.PopupDialogs.DISTRIBUTOR_SUCCESS = {
text = L["Your profile was successfully recieved by the player."], text = L["Your profile was successfully recieved by the player."],
whileDead = 1, whileDead = 1,
hideOnEscape = 1, hideOnEscape = 1,
button1 = OKAY button1 = _G.OKAY,
} }
E.PopupDialogs.DISTRIBUTOR_WAITING = { E.PopupDialogs.DISTRIBUTOR_WAITING = {
text = L["Profile request sent. Waiting for response from player."], text = L["Profile request sent. Waiting for response from player."],
whileDead = 1, whileDead = 1,
hideOnEscape = 1, hideOnEscape = 1,
timeout = 35 timeout = 20,
} }
E.PopupDialogs.DISTRIBUTOR_REQUEST_DENIED = { E.PopupDialogs.DISTRIBUTOR_REQUEST_DENIED = {
text = L["Request was denied by user."], text = L["Request was denied by user."],
whileDead = 1, whileDead = 1,
hideOnEscape = 1, hideOnEscape = 1,
button1 = OKAY button1 = _G.OKAY,
} }
E.PopupDialogs.DISTRIBUTOR_FAILED = { E.PopupDialogs.DISTRIBUTOR_FAILED = {
text = L["Lord! It's a miracle! The download up and vanished like a fart in the wind! Try Again!"], text = L["Lord! It's a miracle! The download up and vanished like a fart in the wind! Try Again!"],
whileDead = 1, whileDead = 1,
hideOnEscape = 1, hideOnEscape = 1,
button1 = OKAY button1 = _G.OKAY,
} }
E.PopupDialogs.DISTRIBUTOR_RESPONSE = {} E.PopupDialogs.DISTRIBUTOR_RESPONSE = {}
@@ -566,20 +582,21 @@ E.PopupDialogs.IMPORT_PROFILE_EXISTS = {
hasEditBox = 1, hasEditBox = 1,
editBoxWidth = 350, editBoxWidth = 350,
maxLetters = 127, maxLetters = 127,
OnAccept = function(self) OnAccept = function(self, data)
local profileType = D.profileType SetImportedProfile(data.profileType, self.editBox:GetText(), data.profileData, true)
local profileKey = self.editBox:GetText()
local profileData = D.profileData
SetImportedProfile(profileType, profileKey, profileData, true)
end, end,
EditBoxOnTextChanged = function(self) EditBoxOnTextChanged = function(self)
if self:GetText() == "" then if self:GetText() == '' then
self:GetParent().button1:Disable() self:GetParent().button1:Disable()
else else
self:GetParent().button1:Enable() self:GetParent().button1:Enable()
end end
end, end,
OnShow = function(self) self.editBox:SetText(D.profileKey) self.editBox:SetFocus() end, OnShow = function(self, data)
self.editBox:SetText(data.profileKey)
self.editBox:SetFocus()
end,
timeout = 0,
whileDead = 1, whileDead = 1,
hideOnEscape = true, hideOnEscape = true,
preferredIndex = 3 preferredIndex = 3
@@ -589,14 +606,16 @@ E.PopupDialogs.IMPORT_RL = {
text = L["You have imported settings which may require a UI reload to take effect. Reload now?"], text = L["You have imported settings which may require a UI reload to take effect. Reload now?"],
button1 = ACCEPT, button1 = ACCEPT,
button2 = CANCEL, button2 = CANCEL,
OnAccept = ReloadUI, OnAccept = _G.ReloadUI,
timeout = 0,
whileDead = 1, whileDead = 1,
hideOnEscape = false, hideOnEscape = false,
preferredIndex = 3 preferredIndex = 3
} }
local function InitializeCallback() local function InitializeCallback()
D:Initialize() D:Initialize()
end end
E:RegisterModule(D:GetName(), InitializeCallback) E:RegisterModule(D:GetName(), InitializeCallback)
+10 -3
View File
@@ -61,6 +61,7 @@ do
AddOn:AddLib("EP", "LibElvUIPlugin-1.0") AddOn:AddLib("EP", "LibElvUIPlugin-1.0")
AddOn:AddLib("LSM", "LibSharedMedia-3.0") AddOn:AddLib("LSM", "LibSharedMedia-3.0")
AddOn:AddLib("ACL", "AceLocale-3.0-ElvUI") AddOn:AddLib("ACL", "AceLocale-3.0-ElvUI")
AddOn:AddLib('ACH', 'LibAceConfigHelper')
AddOn:AddLib("LAB", "LibActionButton-1.0-ElvUI") AddOn:AddLib("LAB", "LibActionButton-1.0-ElvUI")
AddOn:AddLib("LAI", "LibAuraInfo-1.0-ElvUI", true) AddOn:AddLib("LAI", "LibAuraInfo-1.0-ElvUI", true)
AddOn:AddLib("LBF", "LibButtonFacade", true) AddOn:AddLib("LBF", "LibButtonFacade", true)
@@ -70,6 +71,7 @@ do
AddOn:AddLib("SpellRange", "SpellRange-1.0") AddOn:AddLib("SpellRange", "SpellRange-1.0")
AddOn:AddLib("ItemSearch", "LibItemSearch-1.2-ElvUI") AddOn:AddLib("ItemSearch", "LibItemSearch-1.2-ElvUI")
AddOn:AddLib("Compress", "LibCompress") AddOn:AddLib("Compress", "LibCompress")
AddOn:AddLib('Deflate', 'LibDeflate')
AddOn:AddLib("Base64", "LibBase64-1.0-ElvUI") AddOn:AddLib("Base64", "LibBase64-1.0-ElvUI")
AddOn:AddLib("Translit", "LibTranslit-1.0") AddOn:AddLib("Translit", "LibTranslit-1.0")
-- added on ElvUI_OptionsUI load: AceGUI, AceConfig, AceConfigDialog, AceConfigRegistry, AceDBOptions -- added on ElvUI_OptionsUI load: AceGUI, AceConfig, AceConfigDialog, AceConfigRegistry, AceDBOptions
@@ -104,11 +106,16 @@ AddOn.Tooltip = AddOn:NewModule("Tooltip","AceTimer-3.0","AceHook-3.0","AceEvent
AddOn.TotemBar = AddOn:NewModule("Totems","AceEvent-3.0") AddOn.TotemBar = AddOn:NewModule("Totems","AceEvent-3.0")
AddOn.UnitFrames = AddOn:NewModule("UnitFrames","AceTimer-3.0","AceEvent-3.0","AceHook-3.0") AddOn.UnitFrames = AddOn:NewModule("UnitFrames","AceTimer-3.0","AceEvent-3.0","AceHook-3.0")
AddOn.WorldMap = AddOn:NewModule("WorldMap","AceHook-3.0","AceEvent-3.0","AceTimer-3.0") AddOn.WorldMap = AddOn:NewModule("WorldMap","AceHook-3.0","AceEvent-3.0","AceTimer-3.0")
AddOn.MapMarkers = AddOn:NewModule("MapMarkers","AceHook-3.0","AceComm-3.0","AceSerializer-3.0")
do do
local arg2, arg3 = "([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1" local a,b,c = '','([%(%)%.%%%+%-%*%?%[%^%$])','%%%1'
function AddOn:EscapeString(str) function AddOn:EscapeString(s) return gsub(s,b,c) end
return gsub(str, arg2, arg3)
local d = {'|[TA].-|[ta]','|c[fF][fF]%x%x%x%x%x%x','|r','^%s+','%s+$'}
function AddOn:StripString(s, ignoreTextures)
for i = ignoreTextures and 2 or 1, #d do s = gsub(s,d[i],a) end
return s
end end
end end
@@ -0,0 +1,170 @@
local LibStub = _G.LibStub
local MAJOR, MINOR = 'LibAceConfigHelper', 7
local ACH = LibStub:NewLibrary(MAJOR, MINOR)
local LSM = LibStub('LibSharedMedia-3.0')
if not ACH then return end
local type, pairs = type, pairs
local function insertWidth(opt, width)
if type(width) == 'number' and width > 5 then
opt.customWidth = width
else
opt.width = width
end
end
local function insertConfirm(opt, confirm)
local confirmType = type(confirm)
if confirmType == 'boolean' then
opt.confirm = true
elseif confirmType == 'string' then
opt.confirm = true
opt.confirmText = confirm
elseif confirmType == 'function' then
opt.confirm = confirm
end
end
function ACH:Color(name, desc, order, alpha, width, get, set, disabled, hidden)
local optionTable = { type = 'color', name = name, desc = desc, order = order, hasAlpha = alpha, get = get, set = set, disabled = disabled, hidden = hidden }
if width then insertWidth(optionTable, width) end
return optionTable
end
function ACH:Description(name, order, fontSize, image, imageCoords, imageWidth, imageHeight, width, hidden)
local optionTable = { type = 'description', name = name or '', order = order, fontSize = fontSize, image = image, imageCoords = imageCoords, imageWidth = imageWidth, imageHeight = imageHeight, hidden = hidden }
if width then insertWidth(optionTable, width) end
return optionTable
end
function ACH:Execute(name, desc, order, func, image, confirm, width, get, set, disabled, hidden)
local optionTable = { type = 'execute', name = name, desc = desc, order = order, func = func, image = image, get = get, set = set, disabled = disabled, hidden = hidden }
if width then insertWidth(optionTable, width) end
if confirm then insertConfirm(optionTable, confirm) end
return optionTable
end
function ACH:Group(name, desc, order, childGroups, get, set, disabled, hidden, func)
return { type = 'group', childGroups = childGroups, name = name, desc = desc, order = order, set = set, get = get, hidden = hidden, disabled = disabled, func = func, args = {} }
end
function ACH:Header(name, order, get, set, hidden)
return { type = 'header', name = name or '', order = order, get = get, set = set, hidden = hidden }
end
function ACH:Input(name, desc, order, multiline, width, get, set, disabled, hidden, validate)
local optionTable = { type = 'input', name = name, desc = desc, order = order, multiline = multiline, get = get, set = set, disabled = disabled, hidden = hidden, validate = validate }
if width then insertWidth(optionTable, width) end
return optionTable
end
function ACH:Select(name, desc, order, values, confirm, width, get, set, disabled, hidden)
local optionTable = { type = 'select', name = name, desc = desc, order = order, values = values or {}, get = get, set = set, disabled = disabled, hidden = hidden }
if width then insertWidth(optionTable, width) end
if confirm then insertConfirm(optionTable, confirm) end
return optionTable
end
function ACH:MultiSelect(name, desc, order, values, confirm, width, get, set, disabled, hidden)
local optionTable = { type = 'multiselect', name = name, desc = desc, order = order, values = values or {}, get = get, set = set, disabled = disabled, hidden = hidden }
if width then insertWidth(optionTable, width) end
if confirm then insertConfirm(optionTable, confirm) end
return optionTable
end
function ACH:Toggle(name, desc, order, tristate, confirm, width, get, set, disabled, hidden)
local optionTable = { type = 'toggle', name = name, desc = desc, order = order, tristate = tristate, get = get, set = set, disabled = disabled, hidden = hidden }
if width then insertWidth(optionTable, width) end
if confirm then insertConfirm(optionTable, confirm) end
return optionTable
end
-- Values are the following: key = value
-- min - min value
-- max - max value
-- softMin - 'soft' minimal value, used by the UI for a convenient limit while allowing manual input of values up to min/max
-- softMax - 'soft' maximal value, used by the UI for a convenient limit while allowing manual input of values up to min/max
-- step - step value: 'smaller than this will break the code' (default=no stepping limit)
-- bigStep - a more generally-useful step size. Support in UIs is optional.
-- isPercent (boolean) - represent e.g. 1.0 as 100%, etc. (default=false)
function ACH:Range(name, desc, order, values, width, get, set, disabled, hidden)
local optionTable = { type = 'range', name = name, desc = desc, order = order, get = get, set = set, disabled = disabled, hidden = hidden }
if width then insertWidth(optionTable, width) end
if values and type(values) == 'table' then
for key, value in pairs(values) do
optionTable[key] = value
end
end
return optionTable
end
function ACH:Spacer(order, width, hidden)
local optionTable = { name = ' ', type = 'description', order = order, hidden = hidden }
if width then insertWidth(optionTable, width) end
return optionTable
end
local function SharedMediaSelect(controlType, name, desc, order, values, width, get, set, disabled, hidden)
local optionTable = { type = 'select', dialogControl = controlType, name = name, desc = desc, order = order, values = values, get = get, set = set, disabled = disabled, hidden = hidden }
if width then insertWidth(optionTable, width) end
return optionTable
end
function ACH:SharedMediaFont(name, desc, order, width, get, set, disabled, hidden)
return SharedMediaSelect('LSM30_Font', name, desc, order, function() return LSM:HashTable('font') end, width, get, set, disabled, hidden)
end
function ACH:SharedMediaSound(name, desc, order, width, get, set, disabled, hidden)
return SharedMediaSelect('LSM30_Sound', name, desc, order, function() return LSM:HashTable('sound') end, width, get, set, disabled, hidden)
end
function ACH:SharedMediaStatusbar(name, desc, order, width, get, set, disabled, hidden)
return SharedMediaSelect('LSM30_Statusbar', name, desc, order, function() return LSM:HashTable('statusbar') end, width, get, set, disabled, hidden)
end
function ACH:SharedMediaBackground(name, desc, order, width, get, set, disabled, hidden)
return SharedMediaSelect('LSM30_Background', name, desc, order, function() return LSM:HashTable('background') end, width, get, set, disabled, hidden)
end
function ACH:SharedMediaBorder(name, desc, order, width, get, set, disabled, hidden)
return SharedMediaSelect('LSM30_Border', name, desc, order, function() return LSM:HashTable('border') end, width, get, set, disabled, hidden)
end
local FontFlagValues = {
NONE = 'None',
OUTLINE = 'Outline',
THICKOUTLINE = 'Thick',
MONOCHROME = '|cffaaaaaaMono|r',
MONOCHROMEOUTLINE = '|cffaaaaaaMono|r Outline',
MONOCHROMETHICKOUTLINE = '|cffaaaaaaMono|r Thick',
}
function ACH:FontFlags(name, desc, order, width, get, set, disabled, hidden)
local optionTable = { type = 'select', name = name, desc = desc, order = order, get = get, set = set, disabled = disabled, hidden = hidden, values = FontFlagValues }
if width then insertWidth(optionTable, width) end
return optionTable
end
+19
View File
@@ -0,0 +1,19 @@
zlib License
(C) 2018-2021 Haoqian He
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
File diff suppressed because it is too large Load Diff
+4
View File
@@ -12,6 +12,10 @@
<Include file="Ace3\AceComm-3.0\AceComm-3.0.xml"/> <Include file="Ace3\AceComm-3.0\AceComm-3.0.xml"/>
<Include file="Ace3\AceSerializer-3.0\AceSerializer-3.0.xml"/> <Include file="Ace3\AceSerializer-3.0\AceSerializer-3.0.xml"/>
<Script file="LibSharedMedia-3.0\LibSharedMedia-3.0.lua"/> <Script file="LibSharedMedia-3.0\LibSharedMedia-3.0.lua"/>
<!-- -->
<Script file='LibAceConfigHelper\LibAceConfigHelper.lua'/>
<Script file='LibDeflate\LibDeflate.lua'/>
<!-- -->
<Script file="LibSimpleSticky\LibSimpleSticky.lua"/> <Script file="LibSimpleSticky\LibSimpleSticky.lua"/>
<Script file="LibSpellRange-1.0\LibSpellRange-1.0.lua"/> <Script file="LibSpellRange-1.0\LibSpellRange-1.0.lua"/>
<Script file="HealPredict\healpredict.lua"/> <Script file="HealPredict\healpredict.lua"/>
+1
View File
@@ -1,4 +1,5 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/"> <Ui xmlns="http://www.blizzard.com/wow/ui/">
<Script file="Minimap.lua"/> <Script file="Minimap.lua"/>
<Script file="Worldmap.lua"/> <Script file="Worldmap.lua"/>
<Script file="MapMarkers.lua"/>
</Ui> </Ui>
+190
View File
@@ -0,0 +1,190 @@
local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
-- local MM = {}
local MM = E:GetModule("MapMarkers")
local ElvUI_AllMarkers = {};
local ElvUI_ShowedMarkers = {};
local debug = false
-- local _print = print
local dprint = function(...)
if debug then print(...) end
end
local IgnoreList = {};
-- LibStub("AceComm-3.0"):Embed(MM);
-- LibStub("AceSerializer-3.0"):Embed(MM);
local coords = {0.499, 0.751, 0.276, 0.484};
-- local mapX,mapY = WorldMapButton:GetSize()
local prefix = "ElvUI_Marker";
local texturePath = "Interface\\AddOns\\ElvUI\\Media\\Textures\\RaidIcons";
local SYNC_INFO = "|Hplayer:%1$s|h[%1$s]|h Places a marker on the map " ..
": \n|Helvm:show:%2$s:%3$s:%4$s:nil|h|cff3588ff[Show Location]|r|h |Helvm:ignore:%1$s|h|cff3588ff[Ignore markers from: %1$s]|r|h"
function MM:SendMark(text, distribution)
MM:SendCommMessage(prefix, text, distribution or "RAID");
end
local pname = UnitName("player");
function MM:ReseiveMark(text, distribution, target)
local success,mapid, x, y, who = MM:Deserialize(text);
mapid = tonumber(mapid)
if success and mapid and x and y and who ~= pname and not IgnoreList[who] then
MM:PrintMarkInfo(mapid, x, y, who);
end
end
function MM:PrintMarkInfo(mapid, x, y, who)
DEFAULT_CHAT_FRAME:AddMessage(string.format(SYNC_INFO,who,mapid,x,y), 0.41, 0.8, 0.94);
end
function MM:HideAll()
for i = 1, #ElvUI_ShowedMarkers do
ElvUI_ShowedMarkers[i]:Hide();
end
table.wipe(ElvUI_ShowedMarkers);
end
function MM:ShowMark(i,mapid)
local marker = ElvUI_AllMarkers[mapid][i];
if marker.showed then
marker:Show()
table.insert(ElvUI_ShowedMarkers, marker);
elseif not marker.showed then
marker:Hide()
end
end
function MM:CreateMark(mapid,IsSendedMark,x,y)
mapid = mapid or GetCurrentMapAreaID();
mapid = tonumber(mapid)
if not WorldMapFrame:IsShown() and IsSendedMark then
ToggleFrame(WorldMapFrame)
SetMapByID(mapid-1)
end
local curX, curY = GetCursorPosition();
local scale = WorldMapDetailFrame:GetEffectiveScale();
local width, height = WorldMapDetailFrame:GetSize();
local centerX, centerY = WorldMapDetailFrame:GetCenter();
local adjustedX = (curX / scale - (centerX - (width * 0.5))) / width;
local adjustedY = (centerY + (height * 0.5) - curY / scale) / height;
x = IsSendedMark and x or adjustedX * 100;
y = IsSendedMark and y or adjustedY * 100;
ElvUI_AllMarkers[mapid] = ElvUI_AllMarkers[mapid] or {};
local lastIndex = #ElvUI_AllMarkers[mapid];
ElvUI_AllMarkers[mapid][lastIndex+1] = CreateFrame("Frame", "Marker"..mapid..lastIndex+1, WorldMapDetailFrame);
local marker = ElvUI_AllMarkers[mapid][lastIndex+1];
marker.name = "Marker"..mapid..lastIndex;
marker.index = lastIndex+1;
marker.x = x;
marker.y = y;
marker.Texture = marker:CreateTexture();
marker.Texture:SetTexture(texturePath);
marker.Texture:SetAllPoints();
marker.Texture:SetTexCoord(unpack(coords))
marker:SetPoint("CENTER", WorldMapDetailFrame, "TOPLEFT", (x / 100) * WorldMapDetailFrame:GetWidth(), (-y / 100) * WorldMapDetailFrame:GetHeight());
marker:SetWidth(31);
marker:SetHeight(31);
marker:SetFrameStrata("DIALOG");
marker:EnableMouse(true);
marker:HookScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
GameTooltip:SetText("SHIFT + ПКМ убрать метку", 1, 1, 1)
GameTooltip:Show()
end)
marker:SetScript("OnLeave", function()
GameTooltip:Hide()
end)
marker:SetScript("OnMouseDown",function(self,click)
if IsShiftKeyDown() and click == "MiddleButton" then
self:Hide();
self.showed = false;
MM:RefreshAll();
end
end)
marker.showed = true;
table.insert(ElvUI_ShowedMarkers, marker);
marker:Show();
if IsSendedMark then
local mname = GetMapName(mapid) or (mapid .." (mapid)")
print(format("Map marker added in %s at (%.1f, %.1f)", mname, x, y))
else
MM:SendMark(MM:Serialize(mapid, x, y, pname));
end
end
function MM:RefreshAll()
MM:HideAll();
local mapid = GetCurrentMapAreaID();
mapid = tonumber(mapid)
ElvUI_AllMarkers[mapid] = ElvUI_AllMarkers[mapid] or {};
local leng = #ElvUI_AllMarkers[mapid];
if leng and leng > 0 then
for i = 1, leng do
MM:ShowMark(i,mapid);
end
end
end
local function createMark(self, link)
local _,_, mapid, x, y = strsplit(":", link);
mapid = tonumber(mapid)
MM:CreateMark(mapid,true,x,y);
end
local function AddToIgnore(self,link)
local _,_, name = strsplit(":", link);
-- MM:CreateMark(mapid,true,x,y)
IgnoreList[name] = true;
print(name.." was added to ignored markers list.");
end
function MM:Initialize()
if not E.db.general.mapMarkers.enable then return end
local _SetItemRef = SetItemRef
function SetItemRef(link, textref, button, chatFrame)
if link:match("elvm:show") then
createMark(chatFrame,link);
elseif link:match("elvm:ignore") then
AddToIgnore(chatFrame,link);
else
_SetItemRef(link, textref, button, chatFrame);
end
end
WorldMapButton:RegisterForClicks("LeftButtonDownm", "RightButtonDown","MiddleButtonDown");
WorldMapButton:HookScript("OnClick",function(self,click)
if click == "MiddleButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
MM:CreateMark(nil,false);
end
end)
local function EventHandler(self, event, ...)
MM:RefreshAll();
end
MM.imFrame = CreateFrame("Frame",nil,UIParent);
MM.imFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
MM.imFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
MM.imFrame:RegisterEvent("WORLD_MAP_UPDATE");
MM.imFrame:RegisterEvent("WORLD_MAP_NAME_UPDATE");
MM.imFrame:SetScript("OnEvent", EventHandler);
if E.db.general.mapMarkers.showRaidMarkers then
MM:RegisterComm(prefix, MM.ReseiveMark);
end
end
local function InitializeCallback()
MM:Initialize()
end
E:RegisterInitialModule(MM:GetName(), InitializeCallback)
-7
View File
@@ -95,13 +95,6 @@ local menuList = {
ToggleFrame(AscensionLFGFrame) ToggleFrame(AscensionLFGFrame)
end end
}, },
-- {
-- text = LOOKING_FOR_RAID,
-- notCheckable = 1,
-- func = function()
-- ToggleFrame(LFRParentFrame)
-- end
-- },
{ {
text = MAINMENU_BUTTON, text = MAINMENU_BUTTON,
notCheckable = 1, notCheckable = 1,
+17 -9
View File
@@ -14,11 +14,6 @@ function D:ModifyErrorFrame()
local Orig_ScriptErrorsFrame_Update = ScriptErrorsFrame_Update local Orig_ScriptErrorsFrame_Update = ScriptErrorsFrame_Update
ScriptErrorsFrame_Update = function(...) ScriptErrorsFrame_Update = function(...)
if GetCVarBool("scriptErrors") ~= 1 then
Orig_ScriptErrorsFrame_Update(...)
return
end
-- Sometimes the locals table does not have an entry for an index, which can cause an argument #6 error -- Sometimes the locals table does not have an entry for an index, which can cause an argument #6 error
-- in Blizzard_DebugTools.lua:430 and then cause a C stack overflow, this will prevent that -- in Blizzard_DebugTools.lua:430 and then cause a C stack overflow, this will prevent that
local index = ScriptErrorsFrame.index local index = ScriptErrorsFrame.index
@@ -32,8 +27,10 @@ function D:ModifyErrorFrame()
Orig_ScriptErrorsFrame_Update(...) Orig_ScriptErrorsFrame_Update(...)
-- Stop text highlighting again if GetCVarBool("scriptErrors") == 1 then
ScriptErrorsFrameScrollFrameText:HighlightText(0, 0) -- Stop text highlighting again
ScriptErrorsFrameScrollFrameText:HighlightText(0, 0)
end
end end
-- Unhighlight text when focus is hit -- Unhighlight text when focus is hit
@@ -42,9 +39,9 @@ function D:ModifyErrorFrame()
end) end)
ScriptErrorsFrame:Size(500, 300) ScriptErrorsFrame:Size(500, 300)
ScriptErrorsFrameScrollFrame:Size(ScriptErrorsFrame:GetWidth() - 45, ScriptErrorsFrame:GetHeight() - 71) ScriptErrorsFrameScrollFrame:Size(455, 229)
ScriptErrorsFrameScrollFrameText:Width(ScriptErrorsFrameScrollFrame:GetWidth()) ScriptErrorsFrameScrollFrameText:Width(455)
local BUTTON_WIDTH = 75 local BUTTON_WIDTH = 75
local BUTTON_HEIGHT = 24 local BUTTON_HEIGHT = 24
@@ -62,6 +59,17 @@ function D:ModifyErrorFrame()
end) end)
ScriptErrorsFrame.firstButton = firstButton ScriptErrorsFrame.firstButton = firstButton
-- add reload button
local reloadButton = CreateFrame("Button", nil, ScriptErrorsFrame, "UIPanelButtonTemplate")
reloadButton:SetPoint("LEFT", firstButton, "LEFT", -30, 0)
reloadButton:SetText("R")
reloadButton:SetHeight(BUTTON_HEIGHT)
reloadButton:SetWidth(30)
reloadButton:SetScript("OnClick", function()
ReloadUI()
end)
ScriptErrorsFrame.reloadButton = reloadButton
-- Also add a Last button for errors -- Also add a Last button for errors
local lastButton = CreateFrame("Button", nil, ScriptErrorsFrame, "UIPanelButtonTemplate") local lastButton = CreateFrame("Button", nil, ScriptErrorsFrame, "UIPanelButtonTemplate")
lastButton:SetPoint("BOTTOMLEFT", ScriptErrorsFrame.next, "BOTTOMRIGHT", BUTTON_SPACING, 0) lastButton:SetPoint("BOTTOMLEFT", ScriptErrorsFrame.next, "BOTTOMRIGHT", BUTTON_SPACING, 0)
+4
View File
@@ -84,6 +84,10 @@ P.general = {
} }
} }
}, },
mapMarkers = {
enable = true,
showRaidMarkers = true,
},
threat = { threat = {
enable = true, enable = true,
position = "RIGHTCHAT", position = "RIGHTCHAT",
+7 -7
View File
@@ -2,7 +2,7 @@ local E = unpack(ElvUI) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalD
local D = E:GetModule("Distributor") local D = E:GetModule("Distributor")
local _, Engine = ... local _, Engine = ...
Engine[1] = {} Engine[1] = {Blank = function() return '' end }
Engine[2] = E.Libs.ACL:GetLocale("ElvUI", E.global.general.locale or "enUS") Engine[2] = E.Libs.ACL:GetLocale("ElvUI", E.global.general.locale or "enUS")
local C, L = Engine[1], Engine[2] local C, L = Engine[1], Engine[2]
@@ -48,7 +48,7 @@ E.Options.args = {
width = "full" width = "full"
}, },
RepositionWindow = { RepositionWindow = {
order = 2, order = 3,
type = "execute", type = "execute",
name = L["Reposition Window"], name = L["Reposition Window"],
desc = L["Reset the size and position of this frame."], desc = L["Reset the size and position of this frame."],
@@ -58,7 +58,7 @@ E.Options.args = {
end end
}, },
ToggleTutorial = { ToggleTutorial = {
order = 3, order = 4,
type = "execute", type = "execute",
name = L["Toggle Tutorials"], name = L["Toggle Tutorials"],
customWidth = 150, customWidth = 150,
@@ -68,7 +68,7 @@ E.Options.args = {
end end
}, },
Install = { Install = {
order = 4, order = 5,
type = "execute", type = "execute",
name = L["Install"], name = L["Install"],
customWidth = 100, customWidth = 100,
@@ -79,7 +79,7 @@ E.Options.args = {
end end
}, },
ResetAllMovers = { ResetAllMovers = {
order = 5, order = 6,
type = "execute", type = "execute",
name = L["Reset Anchors"], name = L["Reset Anchors"],
customWidth = 150, customWidth = 150,
@@ -89,7 +89,7 @@ E.Options.args = {
end end
}, },
ToggleAnchors = { ToggleAnchors = {
order = 6, order = 7,
type = "execute", type = "execute",
name = L["Toggle Anchors"], name = L["Toggle Anchors"],
customWidth = 150, customWidth = 150,
@@ -99,7 +99,7 @@ E.Options.args = {
end end
}, },
LoginMessage = { LoginMessage = {
order = 7, order = 8,
type = "toggle", type = "toggle",
name = L["Login Message"], name = L["Login Message"],
customWidth = 150, customWidth = 150,
+2 -1
View File
@@ -24,4 +24,5 @@ UnitFrames.lua
DataBars.lua DataBars.lua
Maps.lua Maps.lua
ModuleControl.lua ModuleControl.lua
Tags.lua Tags.lua
Search.lua
+31
View File
@@ -120,6 +120,37 @@ E.Options.args.maps = {
min = -200, max = 200, step = 1 min = -200, max = 200, step = 1
} }
} }
},
spacer2 = {
order = 6,
type = "description",
name = "\n"
},
mapMarkersGroup = {
order = 7,
type = "group",
name = L["Map Markers"],
guiInline = true,
disabled = function() return not WM.Initialized end,
args = {
enable = {
order = 1,
type = "toggle",
name = L["Enable"],
desc = L["Map Markers on map"],
get = function(info) return E.db.general.mapMarkers[info[#info]] end,
set = function(info, value) E.db.general.mapMarkers[info[#info]] = value E:StaticPopup_Show("PRIVATE_RL") end
},
showRaidMarkers = {
order = 2,
type = "toggle",
name = L["Reseive Raid Markers from raid"],
desc = L["Reseive Raid Markers from raid"],
get = function(info) return E.db.general.mapMarkers[info[#info]] end,
set = function(info, value) E.db.general.mapMarkers[info[#info]] = value E:StaticPopup_Show("PRIVATE_RL") end,
disabled = function() return not E.db.general.mapMarkers.enable end,
},
}
} }
} }
}, },
+227
View File
@@ -0,0 +1,227 @@
local E, _, V, P, G = unpack(ElvUI)
local C, L = unpack(select(2, ...))
local ACH = E.Libs.ACH
local SearchText = ''
local gsub = gsub
local wipe = wipe
local next = next
local type = type
local pcall = pcall
local pairs = pairs
local ipairs = ipairs
local gmatch = gmatch
local tinsert = tinsert
local strtrim = strtrim
local strfind = strfind
local strjoin = strjoin
local strlower = strlower
local strmatch = strmatch
local strsplit = strsplit
local start = 100
local depth = start + 2
local inline = depth - 1
local results, entries = {}, {}
local sep = ' |cFF888888>|r '
local searchCache = {}
local blockOption = {
filters = true,
info = true,
plugins = true,
search = true,
tagGroup = true,
modulecontrol = true,
profiles = true
}
local typeInvalid = {
description = true,
header = true
}
local typeValue = {
multiselect = true,
select = true,
}
local nameIndex = {
[L["General"]] = 1,
[L["Global"]] = 0
}
E.Options.args.search = ACH:Group(L["Search"], nil, 4)
local Search = E.Options.args.search.args
local EditBox = ACH:Input(L["Search"], nil, 0, nil, 1.5, function() return SearchText end, function(_, value) C:Search_ClearResults() if strmatch(value, '%S+') then SearchText = strtrim(strlower(value)) C:Search_Config() C:Search_AddResults() end end)
-- Search.editbox = EditBox
E.Options.args.searchEditBox = EditBox
E.Options.args.searchEditBox.order = 2
local Header = {
order = 1,
type = "header",
name = "Enter configuration options to search for in the top left of the options window.\nResults will be shown on the left under the \"Search\" category",
width = "full"
}
local Header2 = {
order = 2,
type = "header",
name = "The results will be shown under the search category on the left",
width = "full"
}
Search.header = Header
Search.header2 = Header2
-- E.Options.args.searchEditBox.height = 20
-- local WhatsNew = ACH:Execute(L["Whats New"], nil, 1, function() C:Search_ClearResults() C:Search_Config(nil, nil, nil, true) C:Search_AddResults() end, nil, nil, nil, nil, nil, nil, function() return SearchText ~= '' or next(searchCache) end)
-- Search.whatsNew = WhatsNew
function C:Search_DisplayResults(groups, section)
if groups.entries then
groups.entries.section = section
end
local index = groups.index or start
groups.index = nil
for name, group in pairs(groups) do
if name ~= 'entries' then
local sub = ACH:Group(name, nil, nameIndex[name] or index, 'tab')
sub.inline = index == inline
section[name] = sub
C:Search_DisplayResults(group, sub.args)
end
end
if groups.entries then
C:Search_DisplayButtons(groups.entries)
end
end
function C:Search_ButtonFunc()
if self.option then
E.Libs.AceConfigDialog:SelectGroup('ElvUI', strsplit(',', self.option.location))
end
end
function C:Search_DisplayButtons(buttons)
local section = buttons.section
buttons.section = nil
for _, data in next, buttons do
local button = ACH:Execute(data.clean, nil, nil, C.Search_ButtonFunc, nil, nil, 1.5)
button.location = data.location
section[data.name] = button
end
end
function C:Search_AddButton(location, name)
local group, index, clean = results, start, name
for groupName in gmatch(name, '(.-)'..sep) do
if index > depth then break end
-- button name
clean = gsub(clean, '^' .. E:EscapeString(groupName) .. sep, '')
-- sub groups
if not group[groupName] then group[groupName] = { index = index } end
group = group[groupName]
index = index + 1
end
-- sub buttons
local count, entry = (entries.count or 0) + 1, { name = name, clean = clean, location = location }
entries.count, entries[count] = count, entry
-- linking
if not group.entries then group.entries = {} end
group.entries[count] = entry
end
function C:Search_AddResults()
wipe(results)
wipe(entries)
for location, names in pairs(searchCache) do
if type(names) == 'table' then
for _, name in ipairs(names) do
C:Search_AddButton(location, name)
end
else
C:Search_AddButton(location, names)
end
end
C:Search_DisplayResults(results, Search)
end
function C:Search_ClearResults()
wipe(searchCache)
wipe(Search)
Search.header = Header
Search.header2 = Header2
-- Search.editbox = EditBox
-- Search.whatsNew = WhatsNew
SearchText = ''
end
function C:Search_FindText(text, whatsNew)
if whatsNew then
return strfind(text, E.NewSign, nil, true)
else
return strfind(strlower(E:StripString(text)), SearchText, nil, true)
end
end
function C:Search_GetReturn(value, ...)
if type(value) == 'function' then
local success, arg1 = pcall(value, ...)
if success then
return arg1
end
else
return value
end
end
function C:Search_Config(tbl, loc, locName, whatsNew)
if not whatsNew and SearchText == '' then return end
for option, infoTable in pairs(tbl or E.Options.args) do
if not (blockOption[option] or infoTable.hidden or typeInvalid[infoTable.type]) then
local location, locationName = loc and (infoTable.type == 'group' and not infoTable.inline and strjoin(',', loc, option) or loc) or option
local name = C:Search_GetReturn(infoTable.name, option)
if type(name) == 'string' then -- bad apples
locationName = locName and (strmatch(name, '%S+') and strjoin(sep, locName, name) or locName) or name
if C:Search_FindText(name, whatsNew) then
if not searchCache[location] then
searchCache[location] = locationName
elseif type(searchCache[location]) == 'table' then
tinsert(searchCache[location], locationName)
else
searchCache[location] = { searchCache[location], locationName }
end
else
local values = (typeValue[infoTable.type] and not infoTable.dialogControl) and C:Search_GetReturn(infoTable.values, option)
if values then
for _, subName in next, values do
if type(subName) == 'string' and C:Search_FindText(subName, whatsNew) then
searchCache[location] = locationName
break -- only need one
end
end
end
end
end
-- process objects (sometimes without a locationName)
if type(infoTable) == 'table' and infoTable.args then
C:Search_Config(infoTable.args, location, locationName, whatsNew)
end
end
end
end