You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Grichelde/Grichelde.lua

500 lines
20 KiB
Lua

--[[--------------------------------------------------------------------------
Grichelde
Copyright 2020 Teilzeit-Jedi <tj@teilzeit-jedi.de>
based on Misspelled developed by Nathan Pieper - nrpieper (@) gmail (dot) com
This code freely distributed for your use in any GPL compliant project.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------]] --
local Grichelde = {}
Grichelde = LibStub("AceAddon-3.0"):NewAddon("Grichelde", "AceEvent-3.0", "AceHook-3.0")
Grichelde.Version = "0.1.0"
local AceGUI = LibStub("AceGUI-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("Grichelde", true)
-- faster function lookups by mapping to local refs
local string_find = string.find
local string_gsub = string.gsub
local string_len = string.len
local string_rep = string.rep
local string_sub = string.sub
local tostring = string.tostring
local tContains = tContains
local strtrim = strtrim
local pairs = table.pairs
local ipairs = table.ipairs
local Grichelde_Saved_CTL_SendChatMessage
local Grichelde_CTL_hookedversion = 0
local Grichelde_ChatTypes = { "SAY", "EMOTE", "YELL", "PARTY", "GUILD", "OFFICER", "RAID", "RAID_WARNING", "INSTANCE_CHAT", "BATTLEGROUND", "WHISPER" }
local Grichelde_ChatCommands = { "/s", "/e", "/me", "/y", "/p", "/pl", "/g", "/o", "/raid", "/rl", "/rw", "/i", "bg", "/w", "/r", "/tt" }
-- do not replace these patterns
local Grichelde_IgnorePatterns = {
"|[Cc]%x%x%x%x%x%x%x%x.-|r", -- colored items (or links)
"|H.-|h", -- item links (http://www.wowwiki.com/ItemLink)
"|T.-|t", -- textures
"|n", -- newline
"{rt[1-8]}", -- rumbered raid target icons
"{Star}", -- named raid target icon 1
"{Circle}", -- named raid target icon 2
"{Coin}", -- named raid target icon 2
"{Diamond}", -- named raid target icon 3
"{Triangle}", -- named raid target icon 4
"{Moon}", -- named raid target icon 5
"{Square}", -- named raid target icon 6
"{Cross}", -- named raid target icon 7
"{X}", -- named raid target icon 7
"{Skull}" -- named raid target icon 8
}
function Grichelde:OnInitialize()
-- print("Grichelde was loaded")
-- Build Interface Options window
self:CreateInterfaceOptions()
-- Watch for WIM and Prat to Load, then integrate
Grichelde:RegisterEvent("ADDON_LOADED")
-- hooks for removing any misspelled word highlighting in the text before the chat message is sent
-- The WoW client will disconnect if you attempt to send a color tags in a chat message.
Grichelde:RawHook("SendChatMessage", true)
end
--- @param event string
--- @param addonName string
function Grichelde:ADDON_LOADED(event, addonName)
if event == "ADDON_LOADED" and addonName == "WIM" then
WIM.RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnEnterPressed", Grichelde.EditBox_OnEnterPressed)
-- WIM sends its chat messages via the API ChatThrottleLib, which itself hooks the default SendChatMessage api
-- many times before Grichelde will. ChatThrottleLib might potentially load before Grichelde, so we just hook
-- into ChatThrottleLib to be on the safe side.
if (WIM.RegisterPreSendFilterText) then -- avoid error if WIM not up to date.
WIM.RegisterPreSendFilterText(function(text)
return Grichelde:ReplaceText(text)
end)
else
if (ChatThrottleLib and Grichelde_CTL_hookedversion < ChatThrottleLib.version) then
Grichelde_Saved_CTL_SendChatMessage = ChatThrottleLib.SendChatMessage
function ChatThrottleLib:SendChatMessage(prio, prefix, text, ...)
text = Grichelde:ReplaceText(text)
-- print("Grichelde Hooked ChatThrottleLib_SendChatMessaged called")
return Grichelde_Saved_CTL_SendChatMessage(ChatThrottleLib, prio, prefix, text, ...)
end
Grichelde_CTL_hookedversion = ChatThrottleLib.version
end
end
end
end
--- Before a chat message is sent, check if replacement is required and replace the text accordingly.
--- @param message string
--- @param type string
--- @param language string
--- @param channel string
function Grichelde:SendChatMessage(message, type, language, channel, ...)
local text = strtrim(message)
if (Grichelde:CheckReplacement(message, type)) then
text = Grichelde:ReplaceText(text)
end
self.hooks["SendChatMessage"](text, type, language, channel, ...);
end
function Grichelde:CheckReplacement(text, type)
-- todo: globally disabled?
-- check type
if (not tContains(Grichelde_ChatTypes, type)) then
return false
end
-- don't replace slash commands except chat related commands
if string_sub(text, 1, 1) == "/" then
local firstWord, _ = Grichelde:GetNextWord(text)
if (firstWord == nil or not tContains(Grichelde_ChatCommands, firstWord)) then
return false
end
end
return true
end
--- Replaces all character occurrences for which replacements have been defined in the options,
--- while preserving any itemLinks or textures. (http://www.wowwiki.com/ItemLink)
--- @param text string
--- @return string
function Grichelde:ReplaceText(text)
local finalText = ""
local newText = text
-- don't replace non-chat related slash commands
local firstWord, line = Grichelde:GetNextWord(text)
if (tContains(Grichelde_ChatCommands, firstWord)) then
-- skip chat slash command
finalText = finalText .. firstWord .. ' '
newText = line
end
local current = 1
local lastStart = 1
while current < string_len(newText) do
local currentChar = string_sub(newText, current, current)
if currentChar ~= '|' and currentChar ~= '{' then
current = current + 1
else
-- lookahead-check for itemLinks, textures and raid target icons
local textAhead = string_sub(newText, current)
local posEnd = Grichelde:CheckForPreversableText(textAhead)
if posEnd > 0 then
local textBehind = string_sub(newText, lastStart, current - 1)
local replacement = Grichelde:ReplaceCharacters(textBehind)
local preservedText = string_sub(textAhead, 1, posEnd)
finalText = finalText .. replacement .. preservedText
current = current + posEnd
lastStart = current
else
-- no corresponding end was found to start pattern, continue loop with next char
current = current + 1
end
end
end
-- cleanup to the end
local remainingText = newText
if lastStart ~= 1 then
remainingText = string_sub(newText, lastStart)
end
local replacement = Grichelde:ReplaceCharacters(remainingText)
finalText = finalText .. replacement
return finalText
end
--- Checks if the text starts with a preversable ignore pattern, such as itemLinks, textures or raid target icons
--- @param text string
--- @return number
function Grichelde:CheckForPreversableText(text)
-- Calling find on ever pattern might be inefficient but its way less code.
for _, pattern in ipairs(Grichelde_IgnorePatterns) do
local pos1, pos2 = string_find(text, pattern)
if pos1 == 1 and pos2 ~= nil then
return pos2
end
end
return 0
end
--- Replaces all character occurrences for which replacements have been defined in the options
--- @param text string
--- @return string
function Grichelde:ReplaceCharacters(text)
-- todo: read from options
-- todo: case (in)sensitivity
local replacement = text
replacement = string_gsub(replacement, "s", "ch")
replacement = string_gsub(replacement, "S", "Ch")
replacement = string_gsub(replacement, "t", "k")
replacement = string_gsub(replacement, "T", "K")
return replacement
end
--[[ Interface Options Window ]] --
function Grichelde:CreateInterfaceOptions()
local cfgFrame = CreateFrame("FRAME", nil, UIParent)
cfgFrame.name = "Grichelde"
local cfgFrameHeader = cfgFrame:CreateFontString("OVERLAY", nil, "GameFontNormalLarge")
cfgFrameHeader:SetPoint("TOPLEFT", 15, -15)
cfgFrameHeader:SetText(self.Version)
local cfgAutoSelectDict = CreateFrame("CHECKBUTTON", "Misspelled_cfgAutoSelectDict", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgAutoSelectDict:SetPoint("TOPLEFT", 20, -40)
Misspelled_cfgAutoSelectDictText:SetText(L["Auto Select Dictionary to Load"])
Misspelled_cfgAutoSelectDict:SetChecked(Misspelled_DB.AutoSelectDictionary)
Misspelled_cfgAutoSelectDict:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
Misspelled_DB.AutoSelectDictionary = not Misspelled_DB.AutoSelectDictionary
--Toggle the sub options
if Misspelled_DB.AutoSelectDictionary == true then
Misspelled_cfgDictdeDE:Disable()
Misspelled_cfgDictenGB:Disable()
Misspelled_cfgDictenUS:Disable()
Misspelled_cfgDictesES:Disable()
Misspelled_cfgDictfrFR:Disable()
Misspelled_cfgDictruRU:Disable()
Misspelled_cfgDictitIT:Disable()
else
Misspelled_cfgDictdeDE:Enable()
Misspelled_cfgDictenGB:Enable()
Misspelled_cfgDictenUS:Enable()
Misspelled_cfgDictesES:Enable()
Misspelled_cfgDictfrFR:Enable()
Misspelled_cfgDictruRU:Enable()
Misspelled_cfgDictitIT:Enable()
if Misspelled_DB.LoadDictionary == nil or #Misspelled_DB.LoadDictionary == 0 then
Misspelled_DB.LoadDictionary = "enUS"
Misspelled_cfgDictenUS:setChecked(true)
end
end
end)
local cfgDictdeDE = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictdeDE", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgDictdeDE:SetPoint("TOPLEFT", 40, -64)
Misspelled_cfgDictdeDEText:SetText("deDE")
Misspelled_cfgDictdeDE:SetChecked(Misspelled_DB.LoadDictionary == "deDE")
Misspelled_cfgDictdeDE:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "deDE"
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictenGB = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictenGB", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgDictenGB:SetPoint("TOPLEFT", 40, -88)
Misspelled_cfgDictenGBText:SetText("enGB")
Misspelled_cfgDictenGB:SetChecked(Misspelled_DB.LoadDictionary == "enGB")
Misspelled_cfgDictenGB:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "enGB"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictenUS = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictenUS", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgDictenUS:SetPoint("TOPLEFT", 40, -112)
Misspelled_cfgDictenUSText:SetText("enUS")
Misspelled_cfgDictenUS:SetChecked(Misspelled_DB.LoadDictionary == "enUS")
Misspelled_cfgDictenUS:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "enUS"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictesES = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictesES", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgDictesES:SetPoint("TOPLEFT", 40, -136)
Misspelled_cfgDictesESText:SetText("esES")
Misspelled_cfgDictesES:SetChecked(Misspelled_DB.LoadDictionary == "esES")
Misspelled_cfgDictesES:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "esES"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictfrFR = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictfrFR", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgDictfrFR:SetPoint("TOPLEFT", 40, -160)
Misspelled_cfgDictfrFRText:SetText("frFR")
Misspelled_cfgDictfrFR:SetChecked(Misspelled_DB.LoadDictionary == "frFR")
Misspelled_cfgDictfrFR:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "frFR"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictruRU = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictruRU", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgDictruRU:SetPoint("TOPLEFT", 40, -184)
Misspelled_cfgDictruRUText:SetText("ruRU")
Misspelled_cfgDictruRU:SetChecked(Misspelled_DB.LoadDictionary == "ruRU")
Misspelled_cfgDictruRU:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "ruRU"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictitIT = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictitIT", cfgFrame, "InterfaceOptionsCheckButtonTemplate")
Misspelled_cfgDictitIT:SetPoint("TOPLEFT", 40, -208)
Misspelled_cfgDictitITText:SetText("itIT")
Misspelled_cfgDictitIT:SetChecked(Misspelled_DB.LoadDictionary == "itIT")
Misspelled_cfgDictitIT:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "itIT"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
else
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
--Edit User Dictionary Button
local cfgEditUserDict = CreateFrame("Button", "EdutUserDictButton", cfgFrame, "UIPanelButtonTemplate")
cfgEditUserDict:SetPoint("TOPLEFT", 20, -287)
cfgEditUserDict:SetText(L["Edit User Dictionary..."])
cfgEditUserDict:SetWidth(200)
cfgEditUserDict:SetHeight(24)
cfgEditUserDict:SetScript("OnClick", function(self)
--PlaySound("igMainMenuOptionCheckBoxOn")
Misspelled:EditUserDict()
end)
--set options on startup
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
if Misspelled_DB.LoadDictionary == "deDE" then
Misspelled_cfgDictdeDE:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "enGB" then
Misspelled_cfgDictenGB:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "enUS" then
Misspelled_cfgDictenUS:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "esES" then
Misspelled_cfgDictesES:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "frFR" then
Misspelled_cfgDictfrFR:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "ruRU" then
Misspelled_cfgDictruRU:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "itIT" then
Misspelled_cfgDictitIT:SetChecked(true)
end
if Misspelled_DB.AutoSelectDictionary == true then
Misspelled_cfgDictdeDE:Disable()
Misspelled_cfgDictenGB:Disable()
Misspelled_cfgDictenUS:Disable()
Misspelled_cfgDictesES:Disable()
Misspelled_cfgDictfrFR:Disable()
Misspelled_cfgDictruRU:Disable()
Misspelled_cfgDictitIT:Disable()
else
Misspelled_cfgDictdeDE:Enable()
Misspelled_cfgDictenGB:Enable()
Misspelled_cfgDictenUS:Enable()
Misspelled_cfgDictesES:Enable()
Misspelled_cfgDictfrFR:Enable()
Misspelled_cfgDictruRU:Enable()
Misspelled_cfgDictitIT:Enable()
end
InterfaceOptions_AddCategory(cfgFrame)
end
-- split first word of a text line
function Grichelde:GetNextWord(line)
-- need to add a trailing separator to catch the last value.
local wordPattern = "[%s%c]*(%w+)[%s%c]*"
local s, e = string_find(line, wordPattern)
local word = string_sub(line, s, e)
local rest = string_sub(line, e)
return word, rest
end
function Grichelde:tprint(t, indent, done)
-- in case we run it standalone
local Note = Note or print
-- show strings differently to distinguish them from numbers
local function show(val)
if type(val) == "string" then
return '"' .. val .. '"'
else
return tostring(val)
end
end
-- entry point here
done = done or {}
indent = indent or 0
for key, value in pairs(t) do
print(string_rep(" ", indent)) -- indent it
if type(value) == "table" and not done[value] then
done[value] = true
Note(show(key), ":");
Grichelde:tprint(value, indent + 2, done)
else
print(show(key), "=")
print(show(value))
end
end
end
function Grichelde:print(...)
SELECTED_DOCK_FRAME:AddMessage(...)
end