Version 0.2

- handle SendChatMessage ordering if addon Misspelled is also installed
- break long texts in chunks of 255 length
- debug flag and messages
master 0.2.0
Lothar Buchholz 4 years ago
parent 6ff629284a
commit 418e6cf446

@ -1,2 +1,23 @@
# Grichelde # Grichelde Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] Version 1.0 - 2020-05-27
First version to be released
### Added
- slash commands
- Options GUI
- store settings globally or per character
- added translations
## Version 0.2 - 2020-05-25
### Added
- added debug flag and messages
### Fixed
- handle SendChatMessage ordering if addon Misspelled is also installed
- break long texts in chunks of 255 length
## Version 0.1 - 2020-05-24
### Added
- bootstrap addon with Ace3 from [Misspelled](https://www.curseforge.com/wow/addons/misspelled)

@ -1,13 +1,11 @@
--[[-------------------------------------------------------------------------- --[[---------------------------------------------------------------------------
Grichelde Grichelde
Copyright 2020 Teilzeit-Jedi <tj@teilzeit-jedi.de> Copyright 2020 Teilzeit-Jedi <tj@teilzeit-jedi.de>
based on Misspelled developed by Nathan Pieper - nrpieper (@) gmail (dot) com based on Misspelled developed by Nathan Pieper - nrpieper (@) gmail (dot) com
This code freely distributed for your use in any GPL compliant project. This code freely distributed for your use in any GPL compliant project.
You may obtain a copy of the License at See conditions in the LICENSE file attached.
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
@ -17,107 +15,141 @@
--------------------------------------------------------------------------]] -- --------------------------------------------------------------------------]] --
local Grichelde = {} local AddonName, AddonTable = ...
Grichelde = LibStub("AceAddon-3.0"):NewAddon("Grichelde", "AceEvent-3.0", "AceHook-3.0")
Grichelde.Version = "0.1.0" local Grichelde = LibStub("AceAddon-3.0"):NewAddon("Grichelde", "AceEvent-3.0", "AceHook-3.0")
Grichelde.version = GetAddOnMetadata(AddonName, "Version")
Grichelde.build = GetAddOnMetadata(AddonName, "X-Build") or "UNKNOWN"
local AceGUI = LibStub("AceGUI-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("Grichelde", true) local L = LibStub("AceLocale-3.0"):GetLocale("Grichelde", true)
local Grichelde_Debug = false
-- faster function lookups by mapping to local refs -- faster function lookups by mapping to local refs
local string_find = string.find local string_find = string.find
local string_gsub = string.gsub local string_gsub = string.gsub
local string_len = string.len local string_len = string.len
local string_rep = string.rep local string_rep = string.rep
local string_sub = string.sub local string_sub = string.sub
local tostring = string.tostring
local tContains = tContains
local strtrim = strtrim local strtrim = strtrim
local pairs = table.pairs local strmatch = strmatch
local ipairs = table.ipairs local tostring = tostring
local tInsert = table.insert
local tContains = tContains
local pairs = pairs
local ipairs = ipairs
local Grichelde_Saved_CTL_SendChatMessage local Grichelde_Hooks = {}
local Grichelde_CTL_hookedversion = 0
local Grichelde_ChatTypes = { "SAY", "EMOTE", "YELL", "PARTY", "GUILD", "OFFICER", "RAID", "RAID_WARNING", "INSTANCE_CHAT", "BATTLEGROUND", "WHISPER" } --local Grichelde_ChatTypes = { "SAY", "EMOTE", "YELL", "PARTY", "GUILD", "OFFICER", "RAID", "RAID_WARNING", "INSTANCE_CHAT", "BATTLEGROUND", "WHISPER" }
local Grichelde_ChatTypes = { "SAY", "EMOTE", "YELL", "PARTY", "GUILD" }
local Grichelde_ChatCommands = { "/s", "/e", "/me", "/y", "/p", "/pl", "/g", "/o", "/raid", "/rl", "/rw", "/i", "bg", "/w", "/r", "/tt" } local Grichelde_ChatCommands = { "/s", "/e", "/me", "/y", "/p", "/pl", "/g", "/o", "/raid", "/rl", "/rw", "/i", "bg", "/w", "/r", "/tt" }
-- do not replace these patterns -- do not replace these patterns
local Grichelde_IgnorePatterns = { local Grichelde_IgnorePatterns = {
"|[Cc]%x%x%x%x%x%x%x%x.-|r", -- colored items (or links) "|[Cc]%x%x%x%x%x%x%x%x.-|r", -- colored items (or links)
"|H.-|h", -- item links (http://www.wowwiki.com/ItemLink) "|H.-|h", -- item links (http://www.wowwiki.com/ItemLink)
"|T.-|t", -- textures "|T.-|t", -- textures
"|n", -- newline "|n", -- newline
"{rt[1-8]}", -- rumbered raid target icons "{rt[1-8]}", -- rumbered raid target icons
"{Star}", -- named raid target icon 1 "{Star}", -- named raid target icon 1
"{Circle}", -- named raid target icon 2 "{Circle}", -- named raid target icon 2
"{Coin}", -- named raid target icon 2 "{Coin}", -- named raid target icon 2
"{Diamond}", -- named raid target icon 3 "{Diamond}", -- named raid target icon 3
"{Triangle}", -- named raid target icon 4 "{Triangle}", -- named raid target icon 4
"{Moon}", -- named raid target icon 5 "{Moon}", -- named raid target icon 5
"{Square}", -- named raid target icon 6 "{Square}", -- named raid target icon 6
"{Cross}", -- named raid target icon 7 "{Cross}", -- named raid target icon 7
"{X}", -- named raid target icon 7 "{X}", -- named raid target icon 7
"{Skull}" -- named raid target icon 8 "{Skull}" -- named raid target icon 8
} }
function Grichelde:OnInitialize() function Grichelde:OnInitialize()
-- print("Grichelde was loaded")
-- Build Interface Options window -- Build Interface Options window
self:CreateInterfaceOptions() --self:CreateInterfaceOptions()
-- Watch for WIM and Prat to Load, then integrate -- Watch for WIM and Prat to Load, then integrate
Grichelde:RegisterEvent("ADDON_LOADED") self:RegisterEvent("ADDON_LOADED", "HookIntoForOtherChatAddons")
end
function Grichelde:OnEnable()
-- Hook in before message is sent to replace all character occurrences where replacements have been defined in the options
self:RawHook("SendChatMessage", true)
if (Misspelled) then
print("Misspelled detected, Grichelde will have any messsage being cleansed")
end
-- tell the world we are listening
print(L.AddonLoaded)
end
-- hooks for removing any misspelled word highlighting in the text before the chat message is sent function Grichelde:OnDisable()
-- The WoW client will disconnect if you attempt to send a color tags in a chat message. self:Unhook("SendChatMessage")
Grichelde:RawHook("SendChatMessage", true)
end end
--- @param event string --- @param event string
--- @param addonName string --- @param addonName string
function Grichelde:ADDON_LOADED(event, addonName) function Grichelde:HookIntoForOtherChatAddons(event, addonName)
if event == "ADDON_LOADED" and addonName == "WIM" then if event == "ADDON_LOADED" then
WIM.RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnEnterPressed", Grichelde.EditBox_OnEnterPressed) if 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 -- If available use the WIM API
-- into ChatThrottleLib to be on the safe side. if (WIM.RegisterPreSendFilterText) then -- avoid error if WIM not up to date.
WIM.RegisterPreSendFilterText(function(text)
if (WIM.RegisterPreSendFilterText) then -- avoid error if WIM not up to date. return Grichelde:CheckAndReplace(text)
WIM.RegisterPreSendFilterText(function(text) end)
return Grichelde:ReplaceText(text) else
end) -- WIM sends its chat messages via the API ChatThrottleLib, which itself hooks the default SendChatMessage api
else -- many times before Grichelde will. ChatThrottleLib might potentially load before Grichelde, so we just hook
if (ChatThrottleLib and Grichelde_CTL_hookedversion < ChatThrottleLib.version) then -- into ChatThrottleLib to be on the safe side.
Grichelde_Saved_CTL_SendChatMessage = ChatThrottleLib.SendChatMessage
if (ChatThrottleLib) then
function ChatThrottleLib:SendChatMessage(prio, prefix, text, ...) Grichelde_Hooks["ChatThrottleLib"] = ChatThrottleLib.SendChatMessage
text = Grichelde:ReplaceText(text)
-- print("Grichelde Hooked ChatThrottleLib_SendChatMessaged called") function ChatThrottleLib:SendChatMessage(prio, prefix, text, ...)
return Grichelde_Saved_CTL_SendChatMessage(ChatThrottleLib, prio, prefix, text, ...) Grichelde:DebugPrint("ChatThrottleLib:SendChatMessage : Hook called")
local replacedText = Grichelde:CheckAndReplace(text)
return Grichelde_Hooks["ChatThrottleLib"](ChatThrottleLib, prio, prefix, replacedText, ...)
end
end end
Grichelde_CTL_hookedversion = ChatThrottleLib.version
end end
end end
end end
end end
--- Before a chat message is sent, check if replacement is required and replace the text accordingly. --- Before af chat message is sent, check if replacement is required and replace the text accordingly.
--- @param message string --- @param message string
--- @param type string --- @param type string
--- @param language string --- @param language string
--- @param channel string --- @param channel string
function Grichelde:SendChatMessage(message, type, language, channel, ...) function Grichelde:SendChatMessage(message, type, language, channel, ...)
local text = strtrim(message) local replacedText = self:CheckAndReplace(message, type)
self:DebugPrint("SendChatMessage : replacedText: " .. replacedText)
-- Send text in chunks if length exceeds 255 bytes after replacement
local chunks = self:SplitText(replacedText)
self:DebugPrint("SendChatMessage : #chunks: " .. #chunks)
if (Grichelde:CheckReplacement(message, type)) then for _, chunk in ipairs(chunks) do
text = Grichelde:ReplaceText(text) self.hooks["SendChatMessage"](chunk, type, language, channel, ...);
end end
end
function Grichelde:CheckAndReplace(message, type)
local text = message
if (Misspelled) then
self:DebugPrint("Misspelled detected: cleansing message")
text = Misspelled:RemoveHighlighting(text)
end
text = strtrim(text)
self.hooks["SendChatMessage"](text, type, language, channel, ...); if (self:CheckReplacement(text, type)) then
text = self:ReplaceText(text)
end
return text
end end
function Grichelde:CheckReplacement(text, type) function Grichelde:CheckReplacement(text, type)
@ -125,20 +157,41 @@ function Grichelde:CheckReplacement(text, type)
-- check type -- check type
if (not tContains(Grichelde_ChatTypes, type)) then if (not tContains(Grichelde_ChatTypes, type)) then
self:DebugPrint("CheckReplacement : skip channel type")
return false return false
end end
-- don't replace slash commands except chat related commands -- don't replace slash commands except chat related commands
if string_sub(text, 1, 1) == "/" then if string_sub(text, 1, 1) == "/" then
local firstWord, _ = Grichelde:GetNextWord(text) local firstWord, _ = self:SplitOnFirstMatch(text)
if (firstWord == nil or not tContains(Grichelde_ChatCommands, firstWord)) then if (firstWord == nil or not tContains(Grichelde_ChatCommands, firstWord)) then
self:DebugPrint("CheckReplacement : ignore slash command")
return false return false
end end
end end
-- in any other case
return true return true
end end
--- Checks if the text starts with a preversable ignore pattern, such as itemLinks, textures or raid target icons
--- and returns the end location of the match, or 0 if no pattern was found
--- @param text string
--- @return number
function Grichelde:CheckForPreversableText(text)
self:DebugPrint("CheckForPreversableText : text is " .. 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
self:DebugPrint("CheckForPreversableText : Found ignore pattern " .. pattern .. " at (" .. pos1 .. "," .. pos2 .. ")")
return pos2
end
end
self:DebugPrint("CheckForPreversableText : no ignore pattern found")
return 0
end
--- Replaces all character occurrences for which replacements have been defined in the options, --- Replaces all character occurrences for which replacements have been defined in the options,
--- while preserving any itemLinks or textures. (http://www.wowwiki.com/ItemLink) --- while preserving any itemLinks or textures. (http://www.wowwiki.com/ItemLink)
--- @param text string --- @param text string
@ -148,8 +201,9 @@ function Grichelde:ReplaceText(text)
local newText = text local newText = text
-- don't replace non-chat related slash commands -- don't replace non-chat related slash commands
local firstWord, line = Grichelde:GetNextWord(text) local firstWord, line = self:SplitOnFirstMatch(text)
if (tContains(Grichelde_ChatCommands, firstWord)) then if (firstWord ~= nil and tContains(Grichelde_ChatCommands, firstWord)) then
self:DebugPrint("ReplaceText : Found slash command " .. (firstWord + "") )
-- skip chat slash command -- skip chat slash command
finalText = finalText .. firstWord .. ' ' finalText = finalText .. firstWord .. ' '
newText = line newText = line
@ -158,22 +212,28 @@ function Grichelde:ReplaceText(text)
local current = 1 local current = 1
local lastStart = 1 local lastStart = 1
while current < string_len(newText) do while current <= string_len(newText) do
local currentChar = string_sub(newText, current, current) local currentChar = string_sub(newText, current, current)
self:DebugPrint("current/char : " .. current .. "," .. currentChar)
if currentChar ~= '|' and currentChar ~= '{' then if currentChar ~= '|' and currentChar ~= '{' then
current = current + 1 current = current + 1
else else
-- lookahead-check for itemLinks, textures and raid target icons -- lookahead-check for itemLinks, textures and raid target icons
local textAhead = string_sub(newText, current) local textAhead = string_sub(newText, current)
local posEnd = Grichelde:CheckForPreversableText(textAhead) local posEnd = self:CheckForPreversableText(textAhead)
if posEnd > 0 then if posEnd > 0 then
self:DebugPrint("ReplaceText : Found an ignore pattern")
local textBehind = string_sub(newText, lastStart, current - 1) local textBehind = string_sub(newText, lastStart, current - 1)
local replacement = Grichelde:ReplaceCharacters(textBehind) local replacement = self:ReplaceCharacters(textBehind)
local preservedText = string_sub(textAhead, 1, posEnd) local preservedText = string_sub(textAhead, 1, posEnd)
finalText = finalText .. replacement .. preservedText finalText = finalText .. replacement .. preservedText
current = current + posEnd current = current + posEnd
lastStart = current lastStart = current
self:DebugPrint("ReplaceText : restarting at " .. lastStart)
else else
-- no corresponding end was found to start pattern, continue loop with next char -- no corresponding end was found to start pattern, continue loop with next char
current = current + 1 current = current + 1
@ -181,29 +241,14 @@ function Grichelde:ReplaceText(text)
end end
end end
-- cleanup to the end -- cleanup remaining text to the end
local remainingText = newText local remainingText = string_sub(newText, lastStart)
if lastStart ~= 1 then local replacement = self:ReplaceCharacters(remainingText)
remainingText = string_sub(newText, lastStart)
end
local replacement = Grichelde:ReplaceCharacters(remainingText)
finalText = finalText .. replacement finalText = finalText .. replacement
return finalText
end
--- Checks if the text starts with a preversable ignore pattern, such as itemLinks, textures or raid target icons self:DebugPrint("ReplaceText : replaced \"" .. text .. "\"")
--- @param text string self:DebugPrint("ReplaceText : with \"" .. finalText .. "\"")
--- @return number return finalText
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 end
--- Replaces all character occurrences for which replacements have been defined in the options --- Replaces all character occurrences for which replacements have been defined in the options
@ -217,258 +262,82 @@ function Grichelde:ReplaceCharacters(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")
replacement = string_gsub(replacement, "T", "K") replacement = string_gsub(replacement, "T", "K")
self:DebugPrint("ReplaceCharacters : replaced \"" .. text .. "\" with \"" .. replacement .. "\"")
return replacement return replacement
end end
--- Splits a long text in longest possible chunks of <= 255 length, split at last available space
--[[ Interface Options Window ]] -- --- @param text string
function Grichelde:CreateInterfaceOptions() --- @return table
local cfgFrame = CreateFrame("FRAME", nil, UIParent) function Grichelde:SplitText(text)
cfgFrame.name = "Grichelde" local chunks = {}
local splitText = text
local cfgFrameHeader = cfgFrame:CreateFontString("OVERLAY", nil, "GameFontNormalLarge") local textSize = string_len(splitText)
cfgFrameHeader:SetPoint("TOPLEFT", 15, -15)
cfgFrameHeader:SetText(self.Version) while textSize > 255 do
local chunk = string_sub(splitText, 1, 255)
local cfgAutoSelectDict = CreateFrame("CHECKBUTTON", "Misspelled_cfgAutoSelectDict", cfgFrame, "InterfaceOptionsCheckButtonTemplate") local remaining = ""
Misspelled_cfgAutoSelectDict:SetPoint("TOPLEFT", 20, -40)
Misspelled_cfgAutoSelectDictText:SetText(L["Auto Select Dictionary to Load"]) -- special case: if space is the start of the next chunk, don't split this chunk
Misspelled_cfgAutoSelectDict:SetChecked(Misspelled_DB.AutoSelectDictionary) if string_sub(splitText, 256, 256) ~= ' ' then
Misspelled_cfgAutoSelectDict:SetScript("OnClick", function(self) -- split at last space, don't assign directly as nil might be returned
if self:GetChecked() then local left, right = self:SplitOnLastMatch(chunk)
PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON if left ~= nil then
else chunk = left
PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF end
end if right ~= nil then
Misspelled_DB.AutoSelectDictionary = not Misspelled_DB.AutoSelectDictionary remaining = right
--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 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 self:DebugPrint("SplitText : chunk: " .. chunk )
Misspelled_cfgDictdeDE:Disable()
Misspelled_cfgDictenGB:Disable() tInsert(chunks, chunk)
Misspelled_cfgDictenUS:Disable() splitText = remaining .. string_sub(splitText, 256)
Misspelled_cfgDictesES:Disable() textSize = string_len(splitText)
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 end
InterfaceOptions_AddCategory(cfgFrame) -- pickup remaining text < 255
end self:DebugPrint("SplitText : last chunk: " .. splitText)
tInsert(chunks, splitText)
return chunks
end
-- split first word of a text line -- split first word of a text line
function Grichelde:GetNextWord(line) function Grichelde:SplitOnFirstMatch(text, start)
-- need to add a trailing separator to catch the last value. self:DebugPrint("SplitOnFirstMatch : text: " .. text .. ", start: " .. self:EmptyIfNil(start))
local wordPattern = "[%s%c]*(%w+)[%s%c]*" local pos = 1
local s, e = string_find(line, wordPattern) if start ~= nil then pos = start end
local word = string_sub(line, s, e) local left, right = strmatch(text, "^.- .+", pos)
local rest = string_sub(line, e) self:DebugPrint("SplitOnFirstMatch : left: " .. self:EmptyIfNil(left) .. ", right: " .. self:EmptyIfNil(right))
return word, rest return left, right
end
function Grichelde:SplitOnLastMatch(text, start)
self:DebugPrint("SplitOnLastMatch : text: " .. text .. ", start: " .. self:EmptyIfNil(start))
local pos = 1
if start ~= nil then pos = start end
local left, right = strmatch(text, ".+ .-$", pos)
self:DebugPrint("SplitOnLastMatch : left: " .. self:EmptyIfNil(left) .. ", right: " .. self:EmptyIfNil(right))
return left, right
end
function Grichelde:DebugPrint(message)
if (Grichelde_Debug) then
print(GRAY_FONT_COLOR_CODE .. "Grichelde:" .. FONT_COLOR_CODE_CLOSE .. " " .. message)
end
end
function Grichelde:EmptyIfNil(value)
if value == nil then return "" end
return tostring(value)
end end
function Grichelde:tprint(t, indent, done) function Grichelde:tprint(t, indent, done)
-- in case we run it standalone -- in case we run it standalone
local Note = Note or print local Note = Note or print
-- local Tell = Tell or io.write
-- show strings differently to distinguish them from numbers -- show strings differently to distinguish them from numbers
local function show(val) local function show(val)
@ -487,14 +356,10 @@ function Grichelde:tprint(t, indent, done)
if type(value) == "table" and not done[value] then if type(value) == "table" and not done[value] then
done[value] = true done[value] = true
Note(show(key), ":"); Note(show(key), ":");
Grichelde:tprint(value, indent + 2, done) self:tprint(value, indent + 2, done)
else else
print(show(key), "=") print(show(key), "=")
print(show(value)) print(show(value))
end end
end end
end end
function Grichelde:print(...)
SELECTED_DOCK_FRAME:AddMessage(...)
end

@ -11,7 +11,9 @@
## X-Curse-Project-ID: 385480 ## X-Curse-Project-ID: 385480
## X-Category: Chat/Communication ## X-Category: Chat/Communication
## X-Credits: Teilzeit-Jedi, Nathan Pieper ## X-Credits: Teilzeit-Jedi, Nathan Pieper
## X-Embeds: Ace3
## OptionalDeps: Ace3
## SavedVariables: GrichseldeOptions ## SavedVariables: GrichseldeOptions
## SavedVariablesPerCharacter: GrichseldeCharOptions ## SavedVariablesPerCharacter: GrichseldeCharOptions

@ -1,2 +1,11 @@
# Grichelde # Grichelde - Text replacer
Grichelde replaces characters you entered in a chatbox with any replacment text you specified in the addon options.
Intentionally started as a helper addon for a roleplaying friend, Grichelde can be used for much more, like
* fixing your common spelling errors :)
* replacing toon names with their nick names
* write out abbreviations for you
* create hilarious moments during roleplay
<!-- Did you ever had an Undead without a jaw and wondered how it might sound like if s/he'd actually speak? -->

@ -4,6 +4,7 @@
<Script file="libs\LibStub\LibStub.lua"/> <Script file="libs\LibStub\LibStub.lua"/>
<Include file="libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/> <Include file="libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
<Include file="libs\AceAddon-3.0\AceAddon-3.0.xml"/> <Include file="libs\AceAddon-3.0\AceAddon-3.0.xml"/>
<Include file="libs\AceConsole-3.0\AceConsole-3.0.xml"/>
<Include file="libs\AceGUI-3.0\AceGUI-3.0.xml"/> <Include file="libs\AceGUI-3.0\AceGUI-3.0.xml"/>
<Include file="libs\AceEvent-3.0\AceEvent-3.0.xml" /> <Include file="libs\AceEvent-3.0\AceEvent-3.0.xml" />
<Include file="libs\AceLocale-3.0\AceLocale-3.0.xml" /> <Include file="libs\AceLocale-3.0\AceLocale-3.0.xml" />

@ -0,0 +1,250 @@
--- **AceConsole-3.0** provides registration facilities for slash commands.
-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
-- to your addons individual needs.
--
-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
-- and can be accessed directly, without having to explicitly call AceConsole itself.\\
-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
-- make into AceConsole.
-- @class file
-- @name AceConsole-3.0
-- @release $Id: AceConsole-3.0.lua 1202 2019-05-15 23:11:22Z nevcairiel $
local MAJOR,MINOR = "AceConsole-3.0", 7
local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
if not AceConsole then return end -- No upgrade needed
AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
-- Lua APIs
local tconcat, tostring, select = table.concat, tostring, select
local type, pairs, error = type, pairs, error
local format, strfind, strsub = string.format, string.find, string.sub
local max = math.max
-- WoW APIs
local _G = _G
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
local tmp={}
local function Print(self,frame,...)
local n=0
if self ~= AceConsole then
n=n+1
tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
end
for i=1, select("#", ...) do
n=n+1
tmp[n] = tostring(select(i, ...))
end
frame:AddMessage( tconcat(tmp," ",1,n) )
end
--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
-- @paramsig [chatframe ,] ...
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
-- @param ... List of any values to be printed
function AceConsole:Print(...)
local frame = ...
if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
return Print(self, frame, select(2,...))
else
return Print(self, DEFAULT_CHAT_FRAME, ...)
end
end
--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
-- @paramsig [chatframe ,] "format"[, ...]
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
-- @param format Format string - same syntax as standard Lua format()
-- @param ... Arguments to the format string
function AceConsole:Printf(...)
local frame = ...
if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
return Print(self, frame, format(select(2,...)))
else
return Print(self, DEFAULT_CHAT_FRAME, format(...))
end
end
--- Register a simple chat command
-- @param command Chat command to be registered WITHOUT leading "/"
-- @param func Function to call when the slash command is being used (funcref or methodname)
-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
function AceConsole:RegisterChatCommand( command, func, persist )
if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end
if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk
local name = "ACECONSOLE_"..command:upper()
if type( func ) == "string" then
SlashCmdList[name] = function(input, editBox)
self[func](self, input, editBox)
end
else
SlashCmdList[name] = func
end
_G["SLASH_"..name.."1"] = "/"..command:lower()
AceConsole.commands[command] = name
-- non-persisting commands are registered for enabling disabling
if not persist then
if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
AceConsole.weakcommands[self][command] = func
end
return true
end
--- Unregister a chatcommand
-- @param command Chat command to be unregistered WITHOUT leading "/"
function AceConsole:UnregisterChatCommand( command )
local name = AceConsole.commands[command]
if name then
SlashCmdList[name] = nil
_G["SLASH_" .. name .. "1"] = nil
hash_SlashCmdList["/" .. command:upper()] = nil
AceConsole.commands[command] = nil
end
end
--- Get an iterator over all Chat Commands registered with AceConsole
-- @return Iterator (pairs) over all commands
function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
local function nils(n, ...)
if n>1 then
return nil, nils(n-1, ...)
elseif n==1 then
return nil, ...
else
return ...
end
end
--- Retreive one or more space-separated arguments from a string.
-- Treats quoted strings and itemlinks as non-spaced.
-- @param str The raw argument string
-- @param numargs How many arguments to get (default 1)
-- @param startpos Where in the string to start scanning (default 1)
-- @return Returns arg1, arg2, ..., nextposition\\
-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
function AceConsole:GetArgs(str, numargs, startpos)
numargs = numargs or 1
startpos = max(startpos or 1, 1)
local pos=startpos
-- find start of new arg
pos = strfind(str, "[^ ]", pos)
if not pos then -- whoops, end of string
return nils(numargs, 1e9)
end
if numargs<1 then
return pos
end
-- quoted or space separated? find out which pattern to use
local delim_or_pipe
local ch = strsub(str, pos, pos)
if ch=='"' then
pos = pos + 1
delim_or_pipe='([|"])'
elseif ch=="'" then
pos = pos + 1
delim_or_pipe="([|'])"
else
delim_or_pipe="([| ])"
end
startpos = pos
while true do
-- find delimiter or hyperlink
local ch,_
pos,_,ch = strfind(str, delim_or_pipe, pos)
if not pos then break end
if ch=="|" then
-- some kind of escape
if strsub(str,pos,pos+1)=="|H" then
-- It's a |H....|hhyper link!|h
pos=strfind(str, "|h", pos+2) -- first |h
if not pos then break end
pos=strfind(str, "|h", pos+2) -- second |h
if not pos then break end
elseif strsub(str,pos, pos+1) == "|T" then
-- It's a |T....|t texture
pos=strfind(str, "|t", pos+2)
if not pos then break end
end
pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
else
-- found delimiter, done with this arg
return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
end
end
-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
return strsub(str, startpos), nils(numargs-1, 1e9)
end
--- embedding and embed handling
local mixins = {
"Print",
"Printf",
"RegisterChatCommand",
"UnregisterChatCommand",
"GetArgs",
}
-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
-- @param target target object to embed AceBucket in
function AceConsole:Embed( target )
for k, v in pairs( mixins ) do
target[v] = self[v]
end
self.embeds[target] = true
return target
end
function AceConsole:OnEmbedEnable( target )
if AceConsole.weakcommands[target] then
for command, func in pairs( AceConsole.weakcommands[target] ) do
target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
end
end
end
function AceConsole:OnEmbedDisable( target )
if AceConsole.weakcommands[target] then
for command, func in pairs( AceConsole.weakcommands[target] ) do
target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
end
end
end
for addon in pairs(AceConsole.embeds) do
AceConsole:Embed(addon)
end

@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceConsole-3.0.lua"/>
</Ui>

@ -1,5 +1,5 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ <Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd"> ..\FrameXML\UI.xsd">
<Script file="localization\localisation.lua"/> <Script file="localisation\enUS.lua"/>
<Script file="localization\localisation-de.lua"/> <Script file="localisation\deDE.lua"/>
</Ui> </Ui>

@ -0,0 +1,14 @@
local L = LibStub('AceLocale-3.0'):NewLocale('Grichelde', 'deDE')
if not L then return end
-- system messages
L.AddonLoaded = 'Grichelde hilft Euch jetzt bei euren Sprachschwierigkeiten.'
-- profiles
--L.ProfileCreated = 'Neues Profil "%s" erstellt'
--L.ProfileLoaded = 'Profil auf "%s" festgelegt'
--L.ProfileDeleted = 'Profil "%s" gel\195\182scht'
--L.ProfileCopied = 'Einstellungen von "%s" kopiert'
--L.ProfileReset = 'Profil "%s" zur\195\188ckgesetzt'
--L.CantDeleteCurrentProfile = 'Das aktuelle Profil kann nicht gel\195\182scht werden'
--L.InvalidProfile = 'Ung\195\188ltiges Profil "%s"'

@ -0,0 +1,5 @@
local L = LibStub('AceLocale-3.0'):NewLocale('Grichelde', 'enUS', true)
if not L then return end
-- system messages
L.AddonLoaded = 'Grichelde now helps you with your spelling disabilities.'

@ -1,22 +0,0 @@
--[[
Localization.lua
Translations for Dominos
German language
--]]
local L = LibStub('AceLocale-3.0'):NewLocale('Grichelde', 'deDE')
if not L then return end
--system messages
L.NewPlayer = 'Neues Profil f\195\188r %s erstellt'
L.Updated = 'Aktualisiert auf v%s'
--profiles
L.ProfileCreated = 'Neues Profil "%s" erstellt'
L.ProfileLoaded = 'Profil auf "%s" festgelegt'
L.ProfileDeleted = 'Profil "%s" gel\195\182scht'
L.ProfileCopied = 'Einstellungen von "%s" kopiert'
L.ProfileReset = 'Profil "%s" zur\195\188ckgesetzt'
L.CantDeleteCurrentProfile = 'Das aktuelle Profil kann nicht gel\195\182scht werden'
L.InvalidProfile = 'Ung\195\188ltiges Profil "%s"'
Loading…
Cancel
Save