Version 0.5.0

- add replacements via options UI
- restructure debug functions
master 0.5.0
Lothar Buchholz 4 years ago
parent cc26683328
commit ecd6e5c340

@ -3,12 +3,12 @@ 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-28
## [Unreleased] Version 1.0 - 2020-06-02
### Added
- case sensitivity
- consolidate consecutive matches
## Version 0.5.0 - 2020-05-31
## Version 0.5.0 - 2020-06-01
### Added
- add replacements via options UI
- handle replacement via slash command

@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------]] --
-----------------------------------------------------------------------------]]
-- read namespace from global env
local AddonName, AddonTable = ...
@ -34,8 +34,12 @@ _G.Grichelde = Grichelde
-- Ace3 callbacks
function Grichelde:OnInitialize()
-- Build Interface Options window
self:LoadDatabase()
self:SetupOptions()
self.db = self:LoadDatabase()
self.options, self.dialog = self:SetupOptions()
self:RefreshOptions("OnProfileChanged")
self:DebugPrint(self.db.profile)
self:SetupSlashCommands()
-- Watch for WIM and Prat to Load, then integrate
@ -51,22 +55,21 @@ function Grichelde:OnEnable()
end
-- tell the world we are listening
self:DebugPrint(self.L.AddonLoaded, self.L.AddonName)
self:Print(self.L.AddonLoaded, self.COLOR_CODES.PREFIX .. self.L.AddonName .. " " .. self.L.VersionAbbr .. self.version .. self.COLOR_CODES.CLOSE)
end
function Grichelde:OnDisable()
self:Unhook("SendChatMessage")
end
--- register 'grichelde' and 'gri' slash commands
--- Register slash commands 'gri' and 'grichelde'
function Grichelde:SetupSlashCommands()
local function HandleSlashCommand(input)
-- Show the GUI if no input is supplied, otherwise handle the chat input.
if not input or input:trim() == "" then
if self.functions.nilOrEmpty(input) then
LibStub("AceConfigDialog-3.0"):Open(self.name)
else
-- handle slash ourselves
self:Print(self.L.AddonName .. " Version " .. self.version)
self:Print("Handle slash command: " .. input)
end
end
@ -75,8 +78,9 @@ function Grichelde:SetupSlashCommands()
self:RegisterChatCommand("gri", HandleSlashCommand)
end
--- @param event string
--- @param addonName string
--- Hook into WIM to catch whisper sending event.
-- @param event string
-- @param addonName string
function Grichelde:HookIntoForOtherChatAddons(event, addonName)
if event == "ADDON_LOADED" then
if addonName == "WIM" then

@ -3,7 +3,7 @@
## Title: Grichelde
## Notes: Replaces characters you type in the chat box
## Notes-de: Ersetzt eingegebene Zeichen in der Chat-Zeile
## Version: 0.4.0
## Version: 0.5.0
## Author: Teilzeit-Jedi
## eMail: tj@teilzeit-jedi.de
@ -22,5 +22,6 @@ localisation.xml
Grichelde.lua
GricheldeConstants.lua
GricheldeUtils.lua
GricheldeChat.lua
GricheldeDatabase.lua
GricheldeOptions.lua
GricheldeChat.lua

@ -2,22 +2,22 @@
local _G = _G
local Grichelde = _G.Grichelde
local ipairs, tContains, tFilter, tInsert, tConcat, find, sub, gsub, match, toLower, trim, length
= Grichelde.functions.ipairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, Grichelde.functions.find, Grichelde.functions.sub, Grichelde.functions.gsub, Grichelde.functions.match, Grichelde.functions.toLower, Grichelde.functions.trim, Grichelde.functions.length
local nilOrEmpty, ipairs, tContains, tFilter, tInsert, tConcat, find, sub, gsub, toLower, trim, length
= Grichelde.functions.nilOrEmpty, Grichelde.functions.ipairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, Grichelde.functions.find, Grichelde.functions.sub, Grichelde.functions.gsub, Grichelde.functions.toLower, Grichelde.functions.trim, Grichelde.functions.length
--- 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
-- @param message string
-- @param type string
-- @param language string
-- @param channel string
function Grichelde:SendChatMessage(message, type, language, channel, ...)
local replacedText = self:CheckAndReplace(message, type)
self:DebugPrint("SendChatMessage : replacedText: " .. replacedText)
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)
self:DebugPrint("SendChatMessage : #chunks:", #chunks)
for _, chunk in ipairs(chunks) do
self.hooks["SendChatMessage"](chunk, type, language, channel, ...);
@ -37,20 +37,26 @@ function Grichelde:CheckAndReplace(message, type)
end
function Grichelde:CheckReplacement(text, channel)
-- skip if not enabled
if (not self.db.profile.enabled) then
self:DebugPrint("CheckReplacement : disabled")
return false
end
-- check channel type
-- skip if no text
if nilOrEmpty(text) then
return false
end
-- skip if wrong channel
local allowedChannels = tFilter(self.db.profile.channels,
function(_,v) return v == true end,
function(k,_) return k end
)
self:DebugPrint("CheckReplacement : allowed channels: %s", tConcat(allowedChannels, ", "))
self:DebugPrint("CheckReplacement : allowed channels:", tConcat(allowedChannels, ", "))
local type = self:ConvertBlizChannelToType(channel)
if (type ~= nil and not tContains(allowedChannels, type)) then
self:DebugPrint("CheckReplacement : skip channel type %s", type)
self:DebugPrint("CheckReplacement : skip channel type:", type)
return false
end
@ -74,56 +80,19 @@ function Grichelde:ConvertBlizChannelToType(channel)
return type
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)
-- do not replace these patterns
local 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
}
-- Calling find on ever pattern might be inefficient but its way less code.
for _, pattern in ipairs(ignorePatterns) do
local pos1, pos2 = find(text, pattern)
if pos1 == 1 and pos2 ~= nil then
self:DebugPrint("CheckForPreversableText : Found ignore pattern \"%s\" at (%d, %d)", pattern, 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,
--- while preserving any itemLinks or textures. (http://www.wowwiki.com/ItemLink)
--- @param text string
--- @return string
-- @param text string
-- @return string
function Grichelde:ReplaceText(text)
local lookAheads = {'|', '{', '%'}
local finalText = ""
local newText = text
-- don't replace non-chat related slash commands
local firstWord, line = self:SplitOnFirstMatch(text)
if (firstWord ~= nil and tContains(self.slashCommands, firstWord)) then
self:DebugPrint("ReplaceText : Found slash command %s", firstWord )
self:DebugPrint("ReplaceText : Found slash command:", firstWord )
-- skip chat slash command
finalText = finalText .. firstWord .. ' '
newText = line
@ -136,7 +105,7 @@ function Grichelde:ReplaceText(text)
local currentChar = sub(newText, current, current)
self:DebugPrint("current/char : %s,%s", current, currentChar)
if currentChar ~= '|' and currentChar ~= '{' then
if ( not tContains(lookAheads, currentChar)) then
current = current + 1
else
@ -153,7 +122,7 @@ function Grichelde:ReplaceText(text)
finalText = finalText .. replacement .. preservedText
current = current + posEnd
lastStart = current
self:DebugPrint("ReplaceText : restarting at " .. lastStart)
self:DebugPrint("ReplaceText : restarting at", lastStart)
else
-- no corresponding end was found to start pattern, continue loop with next char
current = current + 1
@ -166,14 +135,64 @@ function Grichelde:ReplaceText(text)
local replacement = self:ReplaceCharacters(remainingText)
finalText = finalText .. replacement
self:DebugPrint("ReplaceText : replaced \"" .. text .. "\"")
self:DebugPrint("ReplaceText : with \"" .. finalText .. "\"")
self:DebugPrint("ReplaceText : replaced \"%s\"", text)
self:DebugPrint("ReplaceText : with \"%s\"", finalText)
return finalText
end
--- Checks if the text starts with a preversable ignore pattern, such as itemLinks, textures, raid target icons or
--- %-substitutons 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:", text)
-- do not replace these patterns
local 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
"%%n", -- player's name
"%%z", -- player's currnt zone
"%%sz", -- player's current sub-zone
"%%loc", -- player's map coordinates
"%%t", -- name of target
"%%f", -- name of focus target
"%%m", -- name of mouseover unit
"%%p", -- name of player pet
"%%tt" -- name of player's target's target
}
-- Calling find on ever pattern might be inefficient but its way less code.
for _, pattern in ipairs(ignorePatterns) do
local pos1, pos2 = find(text, pattern)
if pos1 == 1 and pos2 ~= nil then
self:DebugPrint("CheckForPreversableText : Found ignore pattern \"%s\" at (%d, %d)", pattern, 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
--- @param text string
--- @return string
-- @param text string
-- @return string
function Grichelde:ReplaceCharacters(text)
-- todo: read from options
-- todo: case (in)sensitivity
@ -189,12 +208,12 @@ function Grichelde:ReplaceCharacters(text)
end
--- Splits a long text in longest possible chunks of <= 255 length, split at last available space
--- @param text string
--- @return table
-- @param text string
-- @return table
function Grichelde:SplitText(text)
local chunks = {}
local splitText = text
local textSize = length(splitText)
local textSize = length(splitText or "")
while textSize > 255 do
local chunk = sub(splitText, 1, 255)
@ -212,7 +231,7 @@ function Grichelde:SplitText(text)
end
end
self:DebugPrint("SplitText : chunk: " .. chunk )
self:DebugPrint("SplitText : chunk:", chunk)
tInsert(chunks, chunk)
splitText = remaining .. sub(splitText, 256)
@ -220,26 +239,8 @@ function Grichelde:SplitText(text)
end
-- pickup remaining text < 255
self:DebugPrint("SplitText : last chunk: " .. splitText)
self:DebugPrint("SplitText : last chunk:", splitText)
tInsert(chunks, splitText)
return chunks
end
-- split at first word of a text line
function Grichelde:SplitOnFirstMatch(text, start)
self:DebugPrint("SplitOnFirstMatch : text: %s, start: %d", text, start)
local pos = start or 1
local left, right = match(text, "^.- .+", pos)
self:DebugPrint("SplitOnFirstMatch : left: %s, right: %s", left, right)
return left or text, right
end
-- split at last word of a text line
function Grichelde:SplitOnLastMatch(text, start)
self:DebugPrint("SplitOnLastMatch : text: %s, start: %d", text, start)
local pos = start or 1
local left, right = match(text, ".+ .-$", pos)
self:DebugPrint("SplitOnLastMatch : left: %s, right: %s", left, right)
return left, right or text
end

@ -4,40 +4,6 @@ local Grichelde = _G.Grichelde
-- upvalues and constants
-- faster function lookups by mapping to local refs
Grichelde.functions = {}
Grichelde.functions.type = _G.type
Grichelde.functions.print = _G.print
Grichelde.functions.pairs = _G.pairs
Grichelde.functions.ipairs = _G.ipairs
Grichelde.functions.tContains = _G.tContains
Grichelde.functions.tFilter = function(t, cond, extr)
local filtered = {}
for key, value in Grichelde.functions.pairs(t) do
if cond(key, value) then
local val = extr(key, value)
Grichelde.functions.tInsert(filtered, #filtered + 1, val)
end
end
return filtered
end
Grichelde.functions.tInsert = _G.table.insert
Grichelde.functions.tConcat = _G.table.concat
Grichelde.functions.select = _G.select
Grichelde.functions.unpack = _G.unpack
Grichelde.functions.find = _G.string.find
Grichelde.functions.sub = _G.string.sub
Grichelde.functions.gsub = _G.string.gsub
Grichelde.functions.match = _G.strmatch
Grichelde.functions.join = _G.strjoin
Grichelde.functions.toLower = _G.strlower
Grichelde.functions.toUpper = _G.strupper
Grichelde.functions.format = _G.string.format
Grichelde.functions.rep = _G.string.rep
Grichelde.functions.trim = _G.strtrim
Grichelde.functions.length = _G.string.len
Grichelde.functions.toString = _G.tostring
-- colors:
Grichelde.COLOR_CODES = {}
Grichelde.COLOR_CODES.PREFIX = "|c00FFAA00"
@ -54,41 +20,105 @@ Grichelde.COLOR_CODES.CLOSE = _G.FONT_COLOR_CODE_CLOSE or "|r";
Grichelde.slashCommands = { "/s", "/say", "/e", "/em", "/me", "/emote", "/y", "/yell", "/sh", "/shout", "/p", "/party", "/pl", "/partyleader", "/g", "/gc", "/guild", "/o", "/osay", "/officer", "/raid", "/rsay", "/rl", "/raidleader", "/rw", "/raidwarning", "/i", "/instance", "/bg", "/battleground", "/w", "/whisper", "/t", "/tell", "/send", "/r", "/reply" }
Grichelde.defaultConfig = {
global = {},
profile = {
enabled = true,
channels = {
["*"] = false,
say = true,
emote = false,
yell = true,
party = true,
partyLeader = true,
guild = true,
officer = true,
},
replacements = {
["**"] = {
searchText = "",
replaceText = "",
caseSensitive = false,
consolidate = true,
},
replacement_0 = {
order = 1,
searchText = "s",
replaceText = "ch",
caseSensitive = false,
consolidate = true,
},
replacement_1 = {
order = 2,
searchText = "t",
replaceText = "ck",
caseSensitive = false,
consolidate = true,
}
}
}
}
local function nilOrEmpty(s)
return s == nil or s:trim() == ""
end
local function spairs(t , orderFunc)
-- collect the keys
local sortedKeys = {}
-- for every non-nil value
for key, _ in Grichelde.functions.pairs(t) do
Grichelde.functions.tInsert(sortedKeys, key)
end
if orderFunc then
Grichelde.functions.tSort(sortedKeys, function(a, b) return orderFunc(sortedKeys, a, b) end)
else
Grichelde.functions.tSort(sortedKeys)
end
-- return the iterator function
local it = 0
return function()
it = it + 1
if sortedKeys[it] then
return sortedKeys[it], t[sortedKeys[it]]
end
end
end
local function tFilter(t, cond, extr)
local filtered = {}
for key, value in Grichelde.functions.pairs(t) do
if cond(key, value) then
local val = extr(key, value)
Grichelde.functions.tInsert(filtered, #filtered + 1, val)
end
end
return filtered
end
local function tSize(t)
local size = 0
if (t) then
-- for every non-nil value
for _, _ in Grichelde.functions.pairs(t) do
size = size + 1
end
end
return size
end
local function tClone(orig)
local orig_type = Grichelde.functions.type(orig)
local copy
if orig_type == 'table' then
copy = {}
-- for every non-nil value
for orig_key, orig_value in Grichelde.functions.tNext, orig, nil do
copy[tClone(orig_key)] = tClone(orig_value)
end
Grichelde.functions.setmetatable(copy, tClone(Grichelde.functions.getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
-- faster function lookups by mapping to local refs
Grichelde.functions = {}
Grichelde.functions.type = _G.type
Grichelde.functions.print = _G.print
Grichelde.functions.nilOrEmpty = nilOrEmpty
Grichelde.functions.pairs = _G.pairs
Grichelde.functions.ipairs = _G.ipairs
Grichelde.functions.spairs = spairs
Grichelde.functions.tContains = _G.tContains
Grichelde.functions.tFilter = tFilter
Grichelde.functions.tInsert = _G.table.insert
Grichelde.functions.tConcat = _G.table.concat
Grichelde.functions.tSize = tSize
Grichelde.functions.tSort = _G.table.sort
Grichelde.functions.tClone = tClone
Grichelde.functions.tNext = _G.next
Grichelde.functions.setmetatable = _G.setmetatable
Grichelde.functions.getmetatable = _G.getmetatable
Grichelde.functions.select = _G.select
Grichelde.functions.unpack = _G.unpack
Grichelde.functions.find = _G.string.find
Grichelde.functions.sub = _G.string.sub
Grichelde.functions.gsub = _G.string.gsub
Grichelde.functions.match = _G.strmatch
Grichelde.functions.join = _G.strjoin
Grichelde.functions.split = _G.strsplit
Grichelde.functions.toLower = _G.strlower
Grichelde.functions.toUpper = _G.strupper
Grichelde.functions.format = _G.string.format
Grichelde.functions.rep = _G.string.rep
Grichelde.functions.trim = _G.strtrim
Grichelde.functions.length = _G.string.len
Grichelde.functions.toString = _G.tostring
Grichelde.functions.toNumber = _G.tonumber
Grichelde.functions.max = _G.math.max
Grichelde.functions.min = _G.math.min

@ -0,0 +1,122 @@
-- read namespace from global env
local _G = _G
local Grichelde = _G.Grichelde
local pairs, ipairs, tInsert, tSort, unpack, join, toString
= Grichelde.functions.pairs, Grichelde.functions.ipairs, Grichelde.functions.tInsert, Grichelde.functions.tSort, Grichelde.functions.unpack, Grichelde.functions.join, Grichelde.functions.toString
local defaultConfig = {
global = {},
profile = {
enabled = true,
channels = {
["*"] = false,
say = true,
emote = false,
yell = true,
party = true,
guild = true,
officer = true,
},
replacements = {
["**"] = {
order = 9999,
searchText = "",
replaceText = "",
caseSensitive = false,
consolidate = true,
},
replacement_0 = {
order = 5,
searchText = "s",
replaceText = "ch",
caseSensitive = false,
consolidate = true,
},
replacement_1 = {
order = 9,
searchText = "t",
replaceText = "ck",
caseSensitive = false,
consolidate = true,
}
}
}
}
function Grichelde:LoadDatabase()
local db = LibStub("AceDB-3.0"):New(self.name .."DB", defaultConfig, true)
db.RegisterCallback(self, "OnNewProfile", "RefreshOptions")
db.RegisterCallback(self, "OnProfileChanged", "RefreshOptions")
db.RegisterCallback(self, "OnProfileDeleted", "RefreshOptions")
db.RegisterCallback(self, "OnProfileCopied", "RefreshOptions")
db.RegisterCallback(self, "OnProfileReset", "RefreshOptions")
return db
end
function Grichelde:SyncToDatabase(info, val)
local option = self.db.profile
local path = 1
while (path < #info) do
option = option[info[path]] -- or nil
path = path + 1
end
local optionPath = join(".", unpack(info, 1, #info))
self:DebugPrint("change option \"%s\" from %s to %s", optionPath, toString(option[info[path]]), toString(val))
option[info[path]] = val
end
function Grichelde:ReadFromDatabase(info)
local option = self.db.profile
local path = 1
while (path <= #info) do
option = option[info[path]] -- or nil
path = path + 1
end
local optionPath = join(".", unpack(info, 1, #info))
self:DebugPrint("read option \"%s\": %s", optionPath, toString(option))
return option
end
--- Sorts a replacements table by order sub-field.
--- Usually called with with self.db.profile.replacements
-- @param replacementsTable table
-- @return table
function Grichelde:ReorderReplacements(replacementsTable)
local replacements = replacementsTable or {}
local sortedByOrder = {}
for replName, _ in pairs(replacements) do
tInsert(sortedByOrder, replName)
end
tSort(sortedByOrder) -- lexicographical order will do for non-nil values
--[[tSort(sortedByOrder, function(a, b)
self:DebugPrint("ReorderReplacements : sort ", a, b)
if a then
if b then
return a < b
else
return a
end
else
return b
end
end)]]
self:DebugPrint("ReorderReplacements : sortedByOrder")
self:DebugPrint(sortedByOrder)
local sortedReplacements = {}
local index = 0
for _, replName in ipairs(sortedByOrder) do
sortedReplacements["replacement_"..index] = replacements[replName]
sortedReplacements["replacement_"..index].order = index
index = index + 1
end
--self:DebugPrint("ReorderReplacements : sorted table")
--self:DebugPrint(sortedReplacements)
return sortedReplacements
end

@ -2,7 +2,8 @@
local _G = _G
local Grichelde = _G.Grichelde
local unpack, join, toString = Grichelde.functions.unpack, Grichelde.functions.join, Grichelde.functions.toString
local nilOrEmpty, pairs, tSize, unpack, find, join, toString, toNumber
= Grichelde.functions.nilOrEmpty, Grichelde.functions.pairs, Grichelde.functions.tSize, Grichelde.functions.unpack, Grichelde.functions.find, Grichelde.functions.join, Grichelde.functions.toString, Grichelde.functions.toNumber
function Grichelde:CreateOptionsUI()
return {
@ -52,56 +53,44 @@ function Grichelde:CreateOptionsUI()
name = self.L.Options_Channels_ChannelParty_Name,
desc = self:Format(self.L.Options_Channels_ChannelParty_Desc, self.L.AddonName),
},
partyLeader = {
order = 4,
type = "toggle",
name = self.L.Options_Channels_ChannelPartyLeader_Name,
desc = self:Format(self.L.Options_Channels_ChannelPartyLeader_Desc, self.L.AddonName),
},
guild = {
order = 5,
order = 4,
type = "toggle",
name = self.L.Options_Channels_ChannelGuild_Name,
desc = self:Format(self.L.Options_Channels_ChannelGuild_Desc, self.L.AddonName),
},
officer = {
order = 6,
order = 5,
type = "toggle",
name = self.L.Options_Channels_ChannelOfficer_Name,
desc = self:Format(self.L.Options_Channels_ChannelOfficer_Desc, self.L.AddonName),
},
raid = {
order = 7,
order = 6,
type = "toggle",
name = self.L.Options_Channels_ChannelRaid_Name,
desc = self:Format(self.L.Options_Channels_ChannelRaid_Desc, self.L.AddonName),
},
raidLeader = {
order = 8,
type = "toggle",
name = self.L.Options_Channels_ChannelRaidLeader_Name,
desc = self:Format(self.L.Options_Channels_ChannelRaidLeader_Desc, self.L.AddonName),
},
raidWarning = {
order = 9,
order = 7,
type = "toggle",
name = self.L.Options_Channels_ChannelRaidWarning_Name,
desc = self:Format(self.L.Options_Channels_ChannelRaidWarning_Desc, self.L.AddonName),
},
instance = {
order = 10,
order = 8,
type = "toggle",
name = self.L.Options_Channels_ChannelInstance_Name,
desc = self:Format(self.L.Options_Channels_ChannelInstance_Desc, self.L.AddonName),
},
battleground = {
order = 11,
order = 9,
type = "toggle",
name = self.L.Options_Channels_ChannelBattleground_Name,
desc = self:Format(self.L.Options_Channels_ChannelBattleground_Desc, self.L.AddonName),
},
whisper = {
order = 12,
order = 10,
type = "toggle",
name = self.L.Options_Channels_ChannelWhisper_Name,
desc = self:Format(self.L.Options_Channels_ChannelWhisper_Desc, self.L.AddonName),
@ -118,56 +107,19 @@ function Grichelde:CreateOptionsUI()
add = {
order = 0,
type = "execute",
confirm = false,
name = self.L.Options_Replacements_Add_Name,
desc = self.L.Options_Replacements_Add_Desc,
func = function(info) self:AddReplacement(info) end
},
deleteAll = {
order = 0,
order = 1,
type = "execute",
confirm = "",
confirm = true,
confirmText = self.L.Options_Replacements_DeleteAll_ConfirmText,
name = self.L.Options_Replacements_DeleteAll_Name,
desc = self.L.Options_Replacements_DeleteAll_Desc,
},
replacement_0 = {
order = 0,
type = "group",
name = self.L.Options_Replacement_Group_Name,
desc = self.L.Options_Replacement_Group_Desc,
childGroups = "tree",
args = {
searchText = {
order = 0,
type = "input",
name = self.L.Options_Replacement_SearchText_Name,
desc = self.L.Options_Replacement_SearchText_Desc,
},
replaceText = {
order = 1,
type = "input",
name = self.L.Options_Replacement_ReplaceText_Name,
desc = self.L.Options_Replacement_ReplaceText_Desc,
},
caseSensitive = {
order = 2,
type = "toggle",
name = self.L.Options_Replacement_CaseSensitive_Name,
desc = self.L.Options_Replacement_CaseSensitive_Desc,
},
consolidate = {
order = 3,
type = "toggle",
name = self.L.Options_Replacement_Consolidate_Name,
desc = self.L.Options_Replacement_Consolidate_Desc,
width = 2
},
deleteAll = {
order = 4,
type = "execute",
confirm = "",
name = self.L.Options_Replacement_Delete_Name,
desc = self.L.Options_Replacement_Delete_Desc,
},
}
func = function(info) self:DeleteAllReplacements(info) end
}
}
}
@ -175,64 +127,177 @@ function Grichelde:CreateOptionsUI()
}
end
function Grichelde:LoadDatabase()
-- Called when the addon is loaded
self.db = LibStub("AceDB-3.0"):New(self.name .."DB", self.defaultConfig, true)
-- todo: is this really needed?
self.db.RegisterCallback(self, "OnProfileChanged", function (event)
self:PrefixedPrint(self.L.Profiles_Loaded, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
end)
self.db.RegisterCallback(self, "OnProfileCopied", function(event)
self:PrefixedPrint(self.L.Profiles_Copied, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
end)
self.db.RegisterCallback(self, "OnProfileReset", function(event)
self:PrefixedPrint(self.L.Profiles_Reset, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
end)
function Grichelde:CreateReplacement(offset)
return {
order = offset or 9999,
type = "group",
name = function(info) return self:MappingName(info) end,
desc = self.L.Options_Replacement_Group_Desc,
childGroups = "tree",
args = {
searchText = {
order = 0,
type = "input",
name = self.L.Options_Replacement_SearchText_Name,
desc = self.L.Options_Replacement_SearchText_Desc,
},
replaceText = {
order = 1,
type = "input",
name = self.L.Options_Replacement_ReplaceText_Name,
desc = self.L.Options_Replacement_ReplaceText_Desc,
},
caseSensitive = {
order = 2,
type = "toggle",
name = self.L.Options_Replacement_CaseSensitive_Name,
desc = self.L.Options_Replacement_CaseSensitive_Desc,
},
consolidate = {
order = 3,
type = "toggle",
name = self.L.Options_Replacement_Consolidate_Name,
desc = self.L.Options_Replacement_Consolidate_Desc,
width = 2
},
delete = {
order = 4,
type = "execute",
confirm = true,
confirmText = self.L.Options_Replacements_Delete_ConfirmText,
name = self.L.Options_Replacement_Delete_Name,
desc = self.L.Options_Replacement_Delete_Desc,
func = function(info) self:DeleteReplacement(info) end
},
}
}
end
function Grichelde:SetupOptions()
-- add DB-backed profiles to UI options
self.options = self:CreateOptionsUI()
self.options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
self.options.args.profiles.disabled = false
local activeProfile = self.db:GetCurrentProfile()
self:PrefixedPrint(self.L.Profiles_Loaded, self.COLOR_CODES.GREEN .. activeProfile .. self.COLOR_CODES.CLOSE)
self:tPrint(self.db.profile)
local options = self:CreateOptionsUI()
options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
options.args.profiles.disabled = false
-- Adding options to blizzard frame
LibStub("AceConfig-3.0"):RegisterOptionsTable(self.name, self.options)
LibStub("AceConfigDialog-3.0"):AddToBlizOptions(self.name, self.L.AddonName)
LibStub("AceConfig-3.0"):RegisterOptionsTable(self.name, options)
local dialog = LibStub("AceConfigDialog-3.0")
dialog:AddToBlizOptions(self.name, self.L.AddonName)
return options, dialog
end
function Grichelde:SyncToDatabase(info, val)
function Grichelde:IsDisabled(info)
if info.option.type == "group" then
return false
end
return not self.db.profile.enabled
end
function Grichelde:MappingName(info)
local option = self.db.profile
local path = 1
while ( path < #info) do
option = option[info[path]] -- or nil
while (path <= #info) do
option = option[info[path]]
path = path + 1
end
local optionPath = join(".", unpack(info, 1, #info))
self:DebugPrint("change option \"%s\" from %s to %s", optionPath, toString(option[info[path]]), toString(val))
option[info[path]] = val
if nilOrEmpty(option.searchText) and nilOrEmpty(option.replaceText) then
return self.L.Options_Replacement_EmptyMapping
else
return self:Format(self.L.Options_Replacement_Group_Name, option.searchText or "", option.replaceText or "")
end
end
function Grichelde:RefreshOptions(event)
self:DebugPrint("RefreshOptions : event:", event)
if event == "OnNewProfile" then
self:PrefixedPrint(self.L.Profiles_Created, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
elseif event == "OnProfileChanged" then
self:PrefixedPrint(self.L.Profiles_Loaded, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
elseif event == "OnProfileDeleted" then
self:PrefixedPrint(self.L.Profiles_Deleted, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
elseif event == "OnProfileCopied" then
self:PrefixedPrint(self.L.Profiles_Copied, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
elseif event == "OnProfileReset" then
self:PrefixedPrint(self.L.Profiles_Reset, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE)
else
self:DebugPrint("Refreshing Profile %s on options change: %s", self.db:GetCurrentProfile(), event)
end
self:RefreshReplacements(self:ReorderReplacements(self.db.profile.replacements))
end
function Grichelde:ReadFromDatabase(info)
--- Create UI options for rhe given replacement table (from DB).
--- Usually called with with self.db.profile.replacements
-- @param replacementsTable
function Grichelde:RefreshReplacements(replacementsTable)
--self:DebugPrint("RefreshReplacements : DB table:")
--self:DebugPrint(replacementsTable)
-- remove all previous replacements from options (not DB), except header and buttons
local replacements = self.options.args.replacements.args or {}
for k, _ in pairs(replacements) do
if k and find(k, "^replacement_") then
replacements[k] = nil
end
end
for replName, _ in pairs(replacementsTable or {}) do
local _, replNumber = self:SplitOnFirstMatch(replName, "_")
replacements[replName] = self:CreateReplacement(toNumber(replNumber))
end
--self:DebugPrint("RefreshReplacements : UI options:")
--self:DebugPrint(replacements)
self.dialog:ConfigTableChanged(nil, self.name)
end
function Grichelde:AddReplacement()
local replacements = self.db.profile.replacements
local maxRepl = tSize(replacements)
local newMapping = "replacement_" .. maxRepl
self:DebugPrint("AddReplacement : new replacement key:", newMapping)
-- setting replacements[newMapping] = {} will deactivate defaults
replacements[newMapping].order = maxRepl
--self:DebugPrint("AddReplacements : all DB entries:")
--self:DebugPrint(replacements)
--self.db.profile.replacements = replacements
self:RefreshOptions("AddReplacement " .. newMapping)
self.dialog:SelectGroup(self.name, "replacements", newMapping)
end
function Grichelde:DeleteReplacement(info)
self:DebugPrint("DeleteReplacement")
local option = self.db.profile
local path = 1
while (path <= #info) do
option = option[info[path]] -- or nil
while (path < #info - 1) do
option = option[info[path]]
--self:DebugPrint(option)
path = path + 1
end
local optionPath = join(".", unpack(info, 1, #info))
self:DebugPrint("read option \"%s\": %s", optionPath, toString(option))
return option
self:DebugPrint("delete option \"%s\": %s", optionPath, toString(option[info[path]]))
option[info[path]] = nil
self:RefreshOptions("DeleteReplacement " .. info[path])
local _, replNumber = self:SplitOnFirstMatch(info[path], "_")
local newMapping = "replacement_" .. toNumber(replNumber - 1)
self.dialog:SelectGroup(self.name, "replacements", newMapping)
end
function Grichelde:IsDisabled(info)
if info.option.type == "group" then
return false
function Grichelde:DeleteAllReplacements()
self:DebugPrint("DeleteAllReplacements : ")
--self.db.profile.replacements = {}
for replName, _ in pairs(self.db.profile.replacements or {}) do
self.db.profile.replacements[replName] = nil
end
return not self.db.profile.enabled
end
self:AddReplacement()
self:RefreshOptions("DeleteAllReplacements")
end

@ -2,58 +2,17 @@
local _G = _G
local Grichelde = _G.Grichelde
local type, print, pairs, select, unpack, format, rep, toString
= Grichelde.functions.type, Grichelde.functions.print, Grichelde.functions.pairs, Grichelde.functions.select, Grichelde.functions.unpack, Grichelde.functions.format, Grichelde.functions.rep, Grichelde.functions.toString
function Grichelde:Format(message, ...)
local msg = message
local l = select("#", ...)
if l > 0 then
-- sanitize nil values in vararg
local packed = { ... }
for i = 1, l do
packed[i] = toString(packed[i]) or "nil"
end
-- print("packed = ", packed)
-- self:tPrint(packed)
-- cannot assign unpacked to a vararg variable and print it for debug
msg = format(message, unpack(packed))
end
return msg or "nil"
end
function Grichelde:Print(...)
print(self:Format(...))
end
local function prefixedPrint(colorCode, prefix, endClose, ...)
print(colorCode .. prefix .. endClose .. ": " .. ...)
end
function Grichelde:PrefixedPrint(...)
prefixedPrint(self.COLOR_CODES.PREFIX, self.L.AddonName, self.COLOR_CODES.CLOSE, self:Format(...))
end
function Grichelde:DebugPrint(...)
if (self.debug) then
prefixedPrint(self.COLOR_CODES.GRAY, self.L.AddonName, self.COLOR_CODES.CLOSE, self:Format(...))
end
end
local function tLen(t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
end
local type, print, pairs, tSize, select, unpack, find, format, rep, toString
= Grichelde.functions.type, Grichelde.functions.print, Grichelde.functions.pairs, Grichelde.functions.tSize, Grichelde.functions.select, Grichelde.functions.unpack, Grichelde.functions.find, Grichelde.functions.format, Grichelde.functions.rep, Grichelde.functions.toString
-- show strings differently to distinguish them from numbers
function Grichelde:PlainValue(val)
local function plainValue(val)
if val == nil then
return "<nil>"
elseif type(val) == "string" then
return '"' .. val .. '"'
elseif type(val) == "table" then
if tLen(val) > 0 then
if tSize(val) > 0 then
return toString(val)
else
return "{}"
@ -64,40 +23,134 @@ function Grichelde:PlainValue(val)
end
--- Prints any value to default channel, do NOT return a string.
function Grichelde:tPrint(val, indent, known)
if (not self.debug) then return end
local function tPrint(val, indent, known, printFunc)
local printF = printFunc or print
indent = indent or 0
known = known or {}
if val == nil then
print(rep(" ", indent) .. "<nil>")
printF(rep(" ", indent) .. "<nil>")
elseif type(val) == "string" then
print(rep(" ", indent) .. "\"" .. val .. "\"")
printF(rep(" ", indent) .. "\"" .. val .. "\"")
elseif type(val) == "table" then
if tLen(val) > 0 then
if tSize(val) > 0 then
for key, value in pairs(val) do
if value == nil then
print(rep(" ", indent) .. self:PlainValue(key) .. "= <nil>")
printF(rep(" ", indent) .. plainValue(key) .. "= <nil>")
elseif type(value) == "table" then
print(rep(" ", indent) .. self:PlainValue(key) .. "= {")
if tLen(value) > 0 then
printF(rep(" ", indent) .. plainValue(key) .. "= {")
if tSize(value) > 0 then
if not known[value] then
self:tPrint(value, indent + 4, known)
tPrint(value, indent + 4, known, printF)
known[value] = true
else
print("<known table> " .. self:PlainValue(value))
printF("<known table> " .. plainValue(value))
end
end
print(rep(" ", indent) .. "}")
printF(rep(" ", indent) .. "}")
else
print(rep(" ", indent) .. self:PlainValue(key) .. " = " .. self:PlainValue(value))
printF(rep(" ", indent) .. plainValue(key) .. " = " .. plainValue(value))
end
end
else
print(rep(" ", indent) .. "{}")
printF(rep(" ", indent) .. "{}")
end
else
print(rep(" ", indent) .. toString(val))
printF(rep(" ", indent) .. toString(val))
end
end
-- split at first word of a text line
function Grichelde:SplitOnFirstMatch(text, delimPattern, start)
if text == nil then return nil end
local pattern = "^(.-)" .. (delimPattern or " " ) .."(.*)"
local pos = start or 1
self:DebugPrint("SplitOnFirstMatch : text: %s, pattern: %s, start: %d", text, pattern, start)
local _, _, left, right = find(text, pattern, pos)
self:DebugPrint("SplitOnFirstMatch : left: %s, right: %s", left, right)
return left or text, right
end
-- split at last word of a text line
function Grichelde:SplitOnLastMatch(text, delimPattern, start)
local pattern = "(.*)" .. (delimPattern or " ") .. "(.-)$"
local pos = start or 1
self:DebugPrint("SplitOnLastMatch : text: %s, pattern: %s, start: %d", text, pattern, start)
local _, _, left, right = find(text, pattern, pos)
self:DebugPrint("SplitOnLastMatch : left: %s, right: %s", left, right)
return left, right or text
end
-- split at last word of a text line
function Grichelde:TestMatch(text, pattern)
local _, _, left, right = find(text, pattern, 1)
self:DebugPrint("TestMatch : left: %s, right: %s", left, right)
end
function Grichelde:Format(message, ...)
if ( not message ) then
return "<nil>"
elseif type(message) == "string" then
if ( not find(message, "%%")) then
return message, ...
else
local l = select("#", ...)
if l > 0 then
-- sanitize nil values in vararg
local packed = { ... }
for i = 1, l do
packed[i] = toString(packed[i]) or "nil"
end
-- print("packed = ", packed)
-- self:tPrint(packed)
-- cannot assign unpacked to a vararg variable and print it for debug
-- Manually set count as unpack() stops on nil (bug with #table)
return format(message, unpack(packed, 1, l))
end
end
end
end
--- deprecated
function Grichelde:Print(...)
print(self:Format(...))
end
function Grichelde:PrefixedPrint(...)
print(self.COLOR_CODES.PREFIX .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...))
end
function Grichelde:DebugPrint(obj, ...)
local function prefixedDebugPrint(...)
print(self.COLOR_CODES.GRAY .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...))
end
if (self.debug) then
if obj == nil then
prefixedDebugPrint("<nil>")
else
if type(obj) == "string" then
local l = select("#", ...)
if ( l == 0 or not find(obj, "%%")) then
prefixedDebugPrint(obj, ...)
else
-- sanitize nil values in vararg
local packed = { ... }
for i = 1, l do
packed[i] = toString(packed[i]) or "nil"
end
-- print("packed = ", packed)
-- self:tPrint(packed)
-- cannot assign unpacked to a vararg variable and print it for debug
local fmtMsg = format(obj, unpack(packed, 1, l)) -- manually set count as unpack() stops on nil (bug with #table)
prefixedDebugPrint(fmtMsg)
end
elseif type(obj) == "table" then
tPrint(obj, 0, {}, prefixedDebugPrint)
else
prefixedDebugPrint(plainValue(obj))
end
end
end
end

@ -1,15 +1,15 @@
<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="libs\LibStub\LibStub.lua"/>
<Include file="libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
<Include file="libs\AceAddon-3.0\AceAddon-3.0.xml"/>
<Include file="libs\AceConfig-3.0\AceConfig-3.0.xml"/>
<Include file="libs\AceConsole-3.0\AceConsole-3.0.xml"/>
<Include file="libs\AceDB-3.0\AceDB-3.0.xml"/>
<Include file="libs\AceDBOptions-3.0\AceDBOptions-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\AceLocale-3.0\AceLocale-3.0.xml" />
<Include file="libs\AceHook-3.0\AceHook-3.0.xml"/>
<Script file="Libs\LibStub\LibStub.lua"/>
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
<Include file="Libs\AceDBOptions-3.0\AceDBOptions-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\AceLocale-3.0\AceLocale-3.0.xml" />
<Include file="Libs\AceHook-3.0\AceHook-3.0.xml"/>
</Ui>

@ -3,6 +3,7 @@ if not L then return end
-- system messages
L.AddonName = "Grichelde"
L.VersionAbbr = "v"
L.AddonLoaded = "%s hilft Euch jetzt bei euren Sprachschwierigkeiten."
L.Addon_Detected_Misspelled = "Das Addon 'Misspelled' wurde erkannt und alle Nachrichten werden automatisch bereinigt."
L.Addon_Detected_WIM = "Das Addon 'WIM' wurde erkannt und alle Flüsternnachrichten aus IM-Fenster werden behandelt."
@ -58,9 +59,11 @@ L.Options_Replacements_Add_Name = "Hinzu"
L.Options_Replacements_Add_Desc = "F\195\188gt eine neue Zuordnung hinzu."
L.Options_Replacements_DeleteAll_Name = "Alle L\195\182schen"
L.Options_Replacements_DeleteAll_Desc = "L\195\182scht alle Zuweisungen."
L.Options_Replacements_DeleteAll_ConfirmText="Wirklich ALLE Zuweisungen l\195\182schen?"
L.Options_Replacement_Group_Name = "Ersetzung"
L.Options_Replacement_Group_Name = "%s => %s"
L.Options_Replacement_Group_Desc = "Dieses Vorkommen wird in den aktivierten Kan\195\164len ersetzt."
L.Options_Replacement_EmptyMapping = "(keine)"
L.Options_Replacement_SearchText_Name = "Suchtext:"
L.Options_Replacement_SearchText_Desc = "Dieser Text wird in der Chateingabe gesucht."
L.Options_Replacement_ReplaceText_Name = "Ersetzung:"
@ -71,3 +74,4 @@ L.Options_Replacement_Consolidate_Name = "Fa\195\159e aufeinanderfolgende Treffe
L.Options_Replacement_Consolidate_Desc = "Wenn durch die Ersetzung die Zeichenfolge mehrfach hintereinander steht,|nfasse sie zu einem Vorkommen zusammen."
L.Options_Replacement_Delete_Name = "L\195\182schen"
L.Options_Replacement_Delete_Desc = "L\195\182scht diese Zuweisung."
L.Options_Replacements_Delete_ConfirmText="Diese Zuweisung l\195\182schen?"

@ -3,9 +3,10 @@ if not L then return end
-- system messages
L.AddonName = "Grichelde"
L.VersionAbbr = "v"
L.AddonLoaded = "%s now helps you with your spelling disabilities."
L.Addon_Detected_Misspelled = "Addon 'Misspelled' has been detected and any messsage will be cleansed automatically."
L.Addon_Detected_WIM = "Das Addon 'WIM' has been detected and any whispers will be handled from IM windows."
L.Addon_Detected_Misspelled = "The Addon 'Misspelled' has been detected and any messsage will be cleansed automatically."
L.Addon_Detected_WIM = "The Addon 'WIM' has been detected and any whispers will be handled from IM windows."
-- profiles
@ -56,12 +57,14 @@ L.Options_Channels_ChannelWhisper_Desc = "Activates %s in channel \"Whisper\"."
L.Options_Replacements_Group_Name = "Replacements"
L.Options_Replacements_Group_Desc = "These lookups will be replaced in activated channels."
L.Options_Replacements_Add_Name = "Add"
L.Options_Replacements_Add_Desc = "Add a new replacement mapping."
L.Options_Replacements_Add_Desc = "Adds a new replacement mapping."
L.Options_Replacements_DeleteAll_Name = "Delete All"
L.Options_Replacements_DeleteAll_Desc = "Delete all replacement mappings."
L.Options_Replacements_DeleteAll_Desc = "Deletes all replacement mappings."
L.Options_Replacements_DeleteAll_ConfirmText = "Do you really want to delete ALL replacement mappings?"
L.Options_Replacement_Group_Name = "Mapping"
L.Options_Replacement_Group_Name = "%s => %s"
L.Options_Replacement_Group_Desc = "This lookup will be replaced in activated channels."
L.Options_Replacement_EmptyMapping = "(none)"
L.Options_Replacement_SearchText_Name = "Search for:"
L.Options_Replacement_SearchText_Desc = "This text is looked up in your chat input box."
L.Options_Replacement_ReplaceText_Name = "Replacement:"
@ -71,4 +74,5 @@ L.Options_Replacement_CaseSensitive_Desc = "Will not replace occurrences if case
L.Options_Replacement_Consolidate_Name = "consolidate consecutive matches"
L.Options_Replacement_Consolidate_Desc = "If after the replacement a text sequence is repeated|ndirectly after another, treat them as one occurrence."
L.Options_Replacement_Delete_Name = "Delete"
L.Options_Replacement_Delete_Desc = "Delete this replacement mapping."
L.Options_Replacement_Delete_Desc = "Deletes this replacement mapping."
L.Options_Replacements_Delete_ConfirmText = "Delete this replacement mapping?"
Loading…
Cancel
Save