From 44dd7ac8eba8f4927efea920b8b10af3672e9b39 Mon Sep 17 00:00:00 2001 From: Lothar Buchholz Date: Tue, 9 Jun 2020 02:28:31 +0200 Subject: [PATCH] Version 0.8.0-beta - handle replacement via slash command - emote and ooc detection - de-/activate single mappings - move buttons and mappings are disabled when already at top/bottom or deactivated - minimap button is darkened when addon is disabled - help tab with examples - right-click on minimap button quickly de-/activates replacements - fixed mapping to raid warning, instance and battleground chats - localized raid target markers - capital % substitutions - incorrect consolidation - Umlaut and accent replacements --- .pkgmeta | 2 + CHANGELOG.md | 18 +- Grichelde.lua | 34 ++- Grichelde.toc | 11 +- GricheldeChat.lua | 511 +++++++++++++++++++++++++--------- GricheldeConstants.lua | 172 +++++++++++- GricheldeDatabase.lua | 14 +- GricheldeMinimap.lua | 107 +++++++ GricheldeOptions.lua | 482 +++++++++++++++++++++----------- GricheldeUpgrade.lua | 34 ++- GricheldeUtils.lua | 42 +-- README.md | 50 +++- libs.xml | 12 +- localisation/deDE.lua | 128 ++++++++- localisation/enUS.lua | 126 ++++++++- twitch/channels-de.png | Bin 110212 -> 74029 bytes twitch/channels-en.png | Bin 113875 -> 71749 bytes twitch/examples-de.png | Bin 0 -> 84788 bytes twitch/examples-en.png | Bin 0 -> 82999 bytes twitch/image-descriptions.txt | 40 +++ twitch/minimap-icon.png | Bin 13856 -> 13660 bytes twitch/profiles-de.png | Bin 131112 -> 92304 bytes twitch/profiles-en.png | Bin 129552 -> 88736 bytes twitch/replacements-de.png | Bin 113620 -> 195215 bytes twitch/replacements-en.png | Bin 103429 -> 187574 bytes twitch/show-mappings.png | Bin 61978 -> 75470 bytes 26 files changed, 1383 insertions(+), 400 deletions(-) create mode 100644 GricheldeMinimap.lua create mode 100644 twitch/examples-de.png create mode 100644 twitch/examples-en.png create mode 100644 twitch/image-descriptions.txt diff --git a/.pkgmeta b/.pkgmeta index 273c52d..33ccf2d 100644 --- a/.pkgmeta +++ b/.pkgmeta @@ -4,6 +4,8 @@ enable-nolib-creation: no externals: libs/LibStub: https://repos.wowace.com/wow/libstub/tags/1.0 + libs/LibDataBroker: https://github.com/tekkub/libdatabroker-1-1 + libs/LibDBIcon: https://repos.curseforge.com/wow/libdbicon-1-0/trunk/LibDBIcon-1.0 libs/CallbackHandler-1.0: https://repos.wowace.com/wow/callbackhandler/trunk/CallbackHandler-1.0 libs/AceAddon-3.0: https://repos.wowace.com/wow/ace3/trunk/AceAddon-3.0 libs/AceConfig-3.0: https://repos.wowace.com/wow/ace3/trunk/AceConfig-3.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 33972f0..d82340d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,17 +3,29 @@ 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). -## [Upcoming] Version 0.8.0-beta - 2020-06-08 +## Version 0.8.0-beta - 2020-06-14 [Feature Complete] ### Added - handle replacement via slash command -- emote detection +- emote and ooc detection +- de-/activate single mappings +- move buttons are disabled when already at top/bottom or replacements were deactivated +- minimap button is darkened when addon is disabled +- help tab with examples +### Changed +- right-click on minimap button quickly de-/activates replacements +### Fixed +- mapping to raid warning, instance and battleground chats +- localized raid target markers +- capital % substitutions +- incorrect consolidation +- Umlaut and accent replacements ## Version 0.7.2-beta - 2020-06-08 ### Added - minimap button ### Changed - graphical move arrows -###Fixed +### Fixed - crash on matches with 0-width ## Version 0.7.1-beta - 2020-06-07 diff --git a/Grichelde.lua b/Grichelde.lua index 54b6fe0..9d7b019 100644 --- a/Grichelde.lua +++ b/Grichelde.lua @@ -1,6 +1,7 @@ --[[--------------------------------------------------------------------------- Grichelde - Text Replacer + Copyright 2020 Teilzeit-Jedi This addon is distributed in the hope that it will be useful, @@ -19,39 +20,46 @@ local _G = _G -- initialize addon local Grichelde = LibStub("AceAddon-3.0"):NewAddon(AddonTable, AddonName, "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0") -Grichelde.L = LibStub("AceLocale-3.0"):GetLocale("Grichelde", true) Grichelde.version = GetAddOnMetadata(AddonName, "Version") Grichelde.build = GetAddOnMetadata(AddonName, "X-Build") or "Experimental" Grichelde.classic = _G.WOW_PROJECT_ID == _G.WOW_PROJECT_CLASSIC Grichelde.logLevel = 0 -- cannot reference Grichelde.LOG_LEVELs here as they are loaded afterwards -- publish to global env -_G.Grichelde = Grichelde +_G[AddonName] = Grichelde -- Ace3 callbacks function Grichelde:OnInitialize() + self.L = LibStub("AceLocale-3.0"):GetLocale(self.name, true) + -- Build Interface Options window self.db = self:LoadDatabase() self:UpgradeDatabase() +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) self.options, self.dialog = self:SetupOptions() self:RefreshOptions("OnProfileChanged") self.ldb, self.icon = self:MinimapButton() - self:SetupSlashCommands() -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) -- tell the world we are listening - self:Print(self.L.AddonLoaded, self.COLOR_CODES.PREFIX .. self.L.AddonName .. " " .. self.L.VersionAbbr .. self.version .. self.COLOR_CODES.CLOSE) + if self.db.profile.enabled then + local namePlusVersion = self:Format(self.L.AddonNamePlusVersion, self.L.AddonName, self.version) + self:Print(self.L.AddonLoaded, self.COLOR_CODES.PREFIX .. namePlusVersion .. self.COLOR_CODES.CLOSE) + end end function Grichelde:OnDisable() self:Unhook("SendChatMessage") + self:CloseOptions() + self:HideMinimapButton() + self:UnregisterChatCommand("grichelde") + self:UnregisterChatCommand("gri") end --- Register slash commands 'gri' and 'grichelde' @@ -60,19 +68,21 @@ function Grichelde:SetupSlashCommands() self:RegisterChatCommand("gri", "HandleSlashCommand") end -function Grichelde:HandleSlashCommand(input) +function Grichelde:HandleSlashCommand(input, ...) -- Show the GUI if no input is supplied, otherwise handle the chat input. if self.functions.nilOrEmpty(input) then - self:OpenOptions() + self:ToggleOptions() else -- handle slash ourselves self:DebugPrint("Handle slash command: " .. input) if input == "mappings" then self:ToogleMappings() elseif input == "options" then - self:PrintOptions() + self:ToggleOptions() elseif input == "profile" then self:PrintProfile() + else + self:SendChatMessageOverride(input, ...) end end end \ No newline at end of file diff --git a/Grichelde.toc b/Grichelde.toc index 1a1b823..20c5629 100644 --- a/Grichelde.toc +++ b/Grichelde.toc @@ -3,7 +3,7 @@ ## Title: Grichelde ## Notes: Replaces characters from the chat box ## Notes-de: Ersetzt eingegebene Zeichen in der Chat-Zeile -## Version: 0.7.2-beta +## Version: 0.8.0-beta ## Author: Teilzeit-Jedi ## eMail: tj@teilzeit-jedi.de @@ -12,18 +12,21 @@ ## X-License: GPLv3 ## X-Category: Chat/Communication ## X-Credits: Teilzeit-Jedi -## X-Embeds: Ace3 +## X-Embeds: LibStub, CallbackHandler, Ace3, LibDataBroker, LibDBIcon -## OptionalDeps: Ace3, LibDataBroker, LibIcon +## OptionalDeps: LibStub, CallbackHandler, Ace3, LibDataBroker, LibDBIcon ## SavedVariables: GricheldeDB libs.xml -localisation.xml Grichelde.lua GricheldeConstants.lua + +localisation.xml + GricheldeUtils.lua GricheldeDatabase.lua GricheldeUpgrade.lua GricheldeOptions.lua +GricheldeMinimap.lua GricheldeChat.lua diff --git a/GricheldeChat.lua b/GricheldeChat.lua index 96c8a08..88b527d 100644 --- a/GricheldeChat.lua +++ b/GricheldeChat.lua @@ -2,44 +2,132 @@ local _G = _G local Grichelde = _G.Grichelde -local IsAddOnLoaded, nilOrEmpty, ipairs, spairs, tContains, tFilter, tInsert, tConcat, find, sub, isUpper, isLower, toUpper, toLower, trim, length - = Grichelde.functions.IsAddOnLoaded, Grichelde.functions.nilOrEmpty, Grichelde.functions.ipairs, Grichelde.functions.spairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, - Grichelde.functions.find, Grichelde.functions.sub, Grichelde.functions.isUpper, Grichelde.functions.isLower, Grichelde.functions.toUpper, Grichelde.functions.toLower, Grichelde.functions.trim, Grichelde.functions.length +local IsAddOnLoaded, assert, nilOrEmpty, pairs, ipairs, spairs, tContains, tFilter, tInsert, tConcat, tSize, tIsEmpty, find, sub, isUpper, isLower, toUpper, toLower, trim, length, lenUtf8 + = Grichelde.functions.IsAddOnLoaded, Grichelde.functions.assert, Grichelde.functions.nilOrEmpty, Grichelde.functions.pairs, Grichelde.functions.ipairs, Grichelde.functions.spairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, Grichelde.functions.tSize, Grichelde.functions.tIsEmpty, + Grichelde.functions.find, Grichelde.functions.sub, Grichelde.functions.isUpper, Grichelde.functions.isLower, Grichelde.functions.toUpper, Grichelde.functions.toLower, Grichelde.functions.trim, Grichelde.functions.length, Grichelde.functions.lenUtf8 + +local function cleanseMessage(this, message) + local text = message or "" + if (IsAddOnLoaded("Misspelled")) then + this:DebugPrint("Misspelled detected: cleansing message") + text = _G.Misspelled:RemoveHighlighting(message) + end + return trim(text) +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 replacedText = self:CheckAndReplace(message, type) +function Grichelde:SendChatMessage(message, type, ...) + local text = cleanseMessage(self, message) + if (self:CheckReplacementAllowed(text, type)) then + text = self:ReplaceText(text) + end + + self:SendChunkifiedChatMessage(text, type, ...) +end + +--- Always replaces the text accoording to the configuration, even if activation or channel was disabled. +--- This is used the the override slash command: "/gri /emote text to replace". +--- NOTE: type and channel (in case of whispers) are determined from the message text. +-- @param message string +-- @param type string +function Grichelde:SendChatMessageOverride(message, ...) + local text = cleanseMessage(self, message) + + local chatType, lang, channel = text, DEFAULT_CHAT_FRAME.editBox.chatType or "SAY", DEFAULT_CHAT_FRAME.editBox.languageID + local msg, type, chan = self:CheckAndExtractMessageTypeTarget(text) + + if msg ~= nil then + msg = self:ReplaceText(msg) + + if type ~= nil then + self:SendChunkifiedChatMessage(msg, type, lang, chan, ...) + else + self:SendChunkifiedChatMessage(msg, chatType, lang, channel, ...) + end + else + -- suppress invalid messages/channels/targets + end +end +--- Send text in chunks if length exceeds 255 bytes after replacement. +function Grichelde:SendChunkifiedChatMessage(message, ...) + if length(message) > 255 then + local chunks = self:SplitText(message) + self:DebugPrint("SendChatMessage : #chunks:", #chunks) - -- Send text in chunks if length exceeds 255 bytes after replacement - local chunks = self:SplitText(replacedText) - self:DebugPrint("SendChatMessage : #chunks:", #chunks) + for _, chunk in ipairs(chunks) do + self.hooks["SendChatMessage"](chunk, ...); + end + else + self.hooks["SendChatMessage"](message, ...); + end +end - for _, chunk in ipairs(chunks) do - self.hooks["SendChatMessage"](chunk, type, language, channel, ...); +local function IsOneBigEmote(this, text) + local firstWord, _ = this:SplitOnFirstMatch(text) + assert(firstWord ~= nil, "firstWord is never nil") + + -- emote detection + local isOneBigEmote = false + -- scheme *emote* + if sub(firstWord, 1, 1) == "<" then + -- search for emote end + local _, emoteEnd = find(text, "%>", 2) + isOneBigEmote = (emoteEnd == length(text)) end + if not isOneBigEmote and sub(firstWord, 1, 1) == "*" then + -- search for emote end + local _, emoteEnd = find(text, "%*", 2) + isOneBigEmote = (emoteEnd == length(text)) + end + -- scheme **emote** + if not isOneBigEmote and sub(firstWord, 1, 2) == "**" then + -- search for emote end + local _, emoteEnd = find(text, "%*%*", 3) + isOneBigEmote = (emoteEnd == length(text)) + end + + -- the whole text is one big emote + return isOneBigEmote end -function Grichelde:CheckAndReplace(message, type) - local text = message - if (self:CheckReplacement(text, type)) then - if (IsAddOnLoaded("Misspelled")) then - self:DebugPrint("Misspelled detected: cleansing message") - text = _G.Misspelled:RemoveHighlighting(text) +--[[ +--- Detect OOC in text, patterns are (( ooc )) or ooc: +local function IsOoc(this, text) + local firstWord, _ = this:SplitOnFirstMatch(text) + assert(firstWord ~= nil, "firstWord is never nil") + + -- scheme: (( ooc )) + if sub(firstWord, 1, 2) == "((" then + -- search for emote end + local _, oocEnd = find(text, "%)%)", 3) + if (oocEnd == length(text)) then + this:TracePrintPrint("IsOoc : skip ((ooc))", text) + return true end - text = self:ReplaceText(trim(text)) end - return text + -- scheme: ooc: + if sub(firstWord, 1, 4) == "ooc:" then + this:TracePrint("IsOoc : skip ooc:", text) + return true + end + + return false end +]] -function Grichelde:CheckReplacement(text, channel) - -- skip if not enabled +--- Checks if a message can be replaced according to configuration. +-- @return boolean +function Grichelde:CheckReplacementAllowed(text, channel) + self:DebugPrint("CheckReplacementAllowed : text:", text) + + -- skip if disabled if (not self.db.profile.enabled) then - self:DebugPrint("CheckReplacement : disabled") + self:DebugPrint("CheckReplacementAllowed : disabled") return false end @@ -49,35 +137,139 @@ function Grichelde:CheckReplacement(text, channel) end -- skip if wrong channel + local chan = self:ConvertBlizzTypeToOption(channel) local allowedChannels = tFilter(self.db.profile.channels, - function(_,v) return v == true end, - function(k,_) return k end + function(_, k, v) return k == chan and v == true end, + function(_, k, _) return k end ) - self:DebugPrint("CheckReplacement : allowed channels:", tConcat(allowedChannels, ", ")) - local type = self:ConvertBlizChannelToType(channel) - if (type == nil or not tContains(allowedChannels, type)) then - self:DebugPrint("CheckReplacement : skip channel type:", type) + self:DebugPrint("CheckReplacementAllowed : allowed channels:") + self:DebugPrint(allowedChannels) + + if tIsEmpty(allowedChannels) then + self:DebugPrint("CheckReplacementAllowed : skip channel type:", chan) return false end - -- don't replace slash commands except chat related commands - if sub(text, 1, 1) == "/" then - local firstWord, _ = self:SplitOnFirstMatch(text) - -- todo: adapt allowed slash commands - if (firstWord == nil or not tContains(self.slashCommands, firstWord)) then - self:DebugPrint("CheckReplacement : ignore slash command") - return false - end + local firstWord, _ = self:SplitOnFirstMatch(text) + assert(firstWord ~= nil, "firstWord is never nil") + + -- don't replace slash commands + if sub(firstWord, 1, 1) == "/" then + self:DebugPrint("CheckReplacementAllowed : skip other slash commands:", firstWord) + return false + end + + -- emote detection + if IsOneBigEmote(self, text) then + self:DebugPrint("CheckReplacementAllowed : one big emote") + return self.db.profile.channels.emote end - -- in any other case + -- in any other case, treat as ordinary text or emote return true end -function Grichelde:ConvertBlizChannelToType(channel) - local type = toLower(channel) - self:DebugPrint("ConvertBlizChannelToType : convert %s to %s", channel, type) - return type +--- Checks if the text from the Grichelde slash command can be replaced and if so +--- returns the replacable text, the chat type and target (player or channel) from the message text. +--- This is used the the override slash command: "/gri /emote text to replace". +-- @param text (string) the whole message +-- @return message (string) stripped message +-- @return type (string) chat type +-- @return channel (string|number) channel number for whispers +function Grichelde:CheckAndExtractMessageTypeTarget(text) + self:DebugPrint("CheckAndExtractMessageTypeTarget : text:", text) + + -- skip if no further text + if nilOrEmpty(text) then + return nil -- dont send text at all + end + + -- first word should be a chat command + if sub(text, 1, 1) == "/" then + -- extract chat command + local chatCmd, targetAndText = self:SplitOnFirstMatch(text) + assert(chatCmd ~= nil, "chatCmd is never nil") + + local type = tFilter(self.SUPPORTED_CHAT_COMMANDS, + function(_, k, _) return chatCmd == k end + ) + self:DebugPrint("CheckAndExtractMessageTypeTarget : determined type:") + self:DebugPrint(type) + + if not tIsEmpty(type) then + -- valid /chattype + if type[1] == "WHISPER" then + -- special reply handling + if "/r" == chatCmd or "/reply" == chatCmd then + -- reuse last type and target if possible + local lastTold, lastToldType = ChatEdit_GetLastToldTarget() + self:DebugPrint("CheckAndExtractMessageTypeTarget : lastTell, lastTellType =", lastTold, lastToldType) + return targetAndText, lastToldType or type[1], lastTold + elseif "/tt" == chatCmd then + -- determine target from game world selection + if (not UnitExists("target") or not UnitIsPlayer("target")) then + self:ErrorPrint(self.L.Error_InvalidWhisperTarget) + return nil -- dont send text at all + end + + local target = UnitName("target"); + if target == nil then + self:ErrorPrint(self.L.Error_InvalidWhisperTarget) + return nil -- dont send text at all + end + + -- eventually we found our target + self:DebugPrint("CheckAndExtractMessageTypeTarget : target:", target) + return targetAndText, type[1], target + else + -- determine target from text + local target, msg = self:SplitOnFirstMatch(targetAndText) + if target == nil then + self:ErrorPrint(self.L.Error_InvalidWhisperTarget) + return nil -- dont send text at all + end + + -- eventually we found our target + self:DebugPrint("CheckAndExtractMessageTypeTarget : determined target:", target) + return msg, type[1], target + end + else + -- all other chat types + return targetAndText, type[1], nil + end + else + -- if not a valid chat command, try as a numbered channel + local _, _, channelNumber = find(chatCmd, "^/(%d+)") + if channelNumber ~= nil then + local channelId = GetChannelName(channelNumber) + if channelId ~= nil then + return targetAndText, "CHANNEL", channelId + end + end + + -- ignore any other slash commands + self:ErrorPrint(self.L.Error_InvalidChannel) + return nil -- dont send text at all + end + elseif IsOneBigEmote(self, text) then + self:DebugPrint("CheckAndExtractMessageTypeTarget : determined EMOTE type") + return text, "EMOTE", nil + else + -- in any other case, treat as ordinary text, assume default type and channel + return text + end +end + +function Grichelde:ConvertBlizzTypeToOption(channel) + local option = tFilter(self.BLIZZ_TYPE_TO_OPTIONS, + function(_, k, _) return channel == k end + ) + if not tIsEmpty(option) then + self:DebugPrint("ConvertBlizzTypeToOption : convert %s to %s", channel, option[1]) + return option[1] + else + return nil + end end --- Replaces all character occurrences for which replacements have been defined in the options, @@ -85,18 +277,9 @@ end -- @param text string -- @return string function Grichelde:ReplaceText(text) - local lookAheads = {'|', '{', '%'} - local finalText = "" + local lookAheads = {'|', '{', '%', '*', '<', '(', 'o'} 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:", firstWord ) - -- skip chat slash command - finalText = finalText .. firstWord .. ' ' - newText = line - end + local finalText = "" local current = 1 local lastStart = 1 @@ -108,8 +291,7 @@ function Grichelde:ReplaceText(text) if ( not tContains(lookAheads, currentChar)) then current = current + 1 else - - -- lookahead-check for itemLinks, textures and raid target icons + -- lookahead-check for all preservable patterns (itemLinks, textures, emotes, ooc, etc.) local textAhead = sub(newText, current) local posEnd = self:CheckForPreversableText(textAhead) if posEnd > 0 then @@ -141,52 +323,64 @@ function Grichelde:ReplaceText(text) 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 +--- Checks if the text starts with a preversable ignore pattern, such as itemLinks, textures, raid target icons, +--- emotes, ooc 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 - "|K.-|k", -- Battle.net - "|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 + self:TracePrint("CheckForPreversableText : text:", text) + + -- Calling find on ever pattern might be inefficient but its way less code than marching over every character + for _, pattern in ipairs(Grichelde.IGNORE_PATTERNS_CASE_SENSITIVE) 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 + + -- emote detection + for _, pattern in ipairs(Grichelde.EMOTE_PATTERNS) do + local pos1, pos2 = find(text, pattern) + if pos1 == 1 and pos2 ~= nil then + local emote = sub(text, pos1, pos2) + if (not self.db.profile.channels.emote) then + self:DebugPrint("CheckForPreversableText : Found emote \"%s\" but preserved it", emote) + return pos2 + else + self:DebugPrint("CheckForPreversableText : Found emote \"%s\" at (%d, %d)", emote, pos1, pos2) + end + end + end + + -- %-substitutions + local lowerText = toLower(text) + for _, pattern in ipairs(Grichelde.IGNORE_PATTERNS_CASE_INSENSITIVE) do + local pos1, pos2 = find(lowerText, 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 + + -- Localized raid target markers + for _, localizedRT in ipairs(Grichelde.LOCALIZED_RAID_TARGETS) do + local translation = toLower(self.L["IgnorePattern_" .. localizedRT]) + local locPattern = "{" .. translation .. "}" + self:TracePrint("CheckForPreversableText : locPattern:", locPattern) + local pos1, pos2 = find(lowerText, locPattern) + if pos1 == 1 and pos2 ~= nil then + self:DebugPrint("CheckForPreversableText : Found localized raid target marker \"%s\" at (%d, %d)", locPattern, pos1, pos2) + return pos2 + end + end + + -- ooc: detection remaing text is treated as ooc completely! + if sub(lowerText, 1, 4) == "ooc:" then + self:DebugPrint("CheckForPreversableText : ooc for remaing text") + return length(text) + end + self:DebugPrint("CheckForPreversableText : no ignore pattern found") return 0 end @@ -207,7 +401,7 @@ function Grichelde:ReplaceCharacters(text) local before = result local search = replTable.searchText - if not nilOrEmpty(search) then + if not nilOrEmpty(search) and replTable.active then local replace = replTable.replaceText consolidate[replName] = {} @@ -218,11 +412,11 @@ function Grichelde:ReplaceCharacters(text) local oldResult = result local pos1, pos2 = find(oldResult, search, pos) + self:TracePrint("ReplaceCharacters : pos1: %d, pos2: %d", pos1, pos2) while (pos1 and pos2 and pos1 <= pos2) do - self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) local pre = sub(result, 1, pos1 - 1 + offset) local post = sub(result, pos2 + 1 + offset) - self:TracePrint("pre: %s, post: %s", pre, post) + self:TracePrint("ReplaceCharacters : pre: %s, post: %s", pre, post) -- actual replacement result = pre .. replace .. post @@ -233,9 +427,21 @@ function Grichelde:ReplaceCharacters(text) tInsert(consolidate[replName], pos1 + offset) end + -- update previous consolidate markers + local diff = length(replace) - length(search) + for key, posList in pairs(consolidate) do + if key ~= replName then + for i, pc in ipairs(posList) do + if pos1 < pc then + consolidate[key][i] = consolidate[key][i] + diff + end + end + end + end + -- replacement text can lengthen or shorten the resulting text -- after replacement result and lowerResult can have different sizes - offset = offset + length(replace) - length(search) + offset = offset + diff -- update values for next iteration pos = pos2 + 1 pos1, pos2 = find(oldResult, search, pos) @@ -247,58 +453,70 @@ function Grichelde:ReplaceCharacters(text) local lowerSearch = toLower(search) local pos1, pos2 = find(lowerResult, lowerSearch, pos) + self:TracePrint("ReplaceCharacters : pos1: %d, pos2: %d", pos1, pos2) while (pos1 and pos2 and pos1 <= pos2) do - self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) local pre = sub(result, 1, pos1 - 1 + offset) local match = sub(result, pos1 + offset, pos2 + offset) local post = sub(result, pos2 + 1 + offset) - self:TracePrint("pre: %s, match: %s, post: %s", pre, match, post) + self:TracePrint("ReplaceCharacters : pre: %s, match: %s, post: %s", pre, match, post) -- keep cases + local utf8, uc, tc = nil, 0, 0 local repl = "" local lastCase = nil for p = pos1, pos2 do - self:TracePrint("p: %d", p) + self:TracePrint("ReplaceCharacters : p: %d", p) local c = sub(match, p - pos1 + 1, p - pos1 + 1) - local r = sub(replace, p - pos1 + 1, p - pos1 + 1) or "" - - if (isUpper(c)) then -- all UPPER-CASE letter - lastCase = true - repl = repl .. toUpper(r) - elseif (isLower(match)) then -- all lower_case letter - lastCase = false - repl = repl .. toLower(r) - else -- no letter - lastCase = nil - repl = repl .. r + -- put together umlaut or accent + if utf8 ~= nil then + c = utf8 .. c + utf8 = nil + end + + -- if not umlaut or accent + if c ~= "\195" then + local r = sub(replace, p - pos1 + 1 - uc + tc, p - pos1 + 1 - uc + tc) or "" + if r == "\195" then + r = sub(replace, p - pos1 + 1 - uc + tc, p - pos1 + 1 - uc + tc + 1) or "" + tc = tc + 1 + end + self:TracePrint("ReplaceCharacters : character: %s, %s", c, r) + + if (isUpper(c)) then -- UPPER-CASE letter + lastCase = true + repl = repl .. toUpper(r) + elseif (isLower(c)) then -- lower_case letter + lastCase = false + repl = repl .. toLower(r) + else -- no letter + lastCase = nil + repl = repl .. r + end + else + -- handle UTF8 characters + utf8 = c + uc = uc + 1 end - self:TracePrint("character: %s, %s", c, r) end - self:TracePrint("length %d > %d", length(replace), pos2 - pos1 + 1) - if (length(replace) > pos2 - pos1 + 1) then - local remainingReplace = sub(replace, pos2 - pos1 + 2) + self:TracePrint("ReplaceCharacters : length %d > %d", length(replace), pos2 - pos1 + 1 - uc + tc) + if (length(replace) > pos2 - pos1 + 1 - uc + tc) then + local remainingReplace = sub(replace, pos2 - pos1 + 2 - uc + tc) local nextLetter = sub(post, 1, 1) + self:TracePrint("ReplaceCharacters : rest: %s, n: %s, lastCase: %s", remainingReplace, nextLetter, lastCase) - self:TracePrint("rest: %s, n: %s, lastCase: %s", remainingReplace, nextLetter, lastCase) - - if (isUpper(nextLetter)) then - if lastCase == nil or lastCase == false then - repl = repl .. remainingReplace - else + if lastCase == nil then + if (isUpper(nextLetter)) then repl = repl .. toUpper(remainingReplace) - end - elseif (isLower(nextLetter)) then - if lastCase == nil or lastCase == true then - repl = repl .. remainingReplace - else + elseif (isLower(nextLetter)) then repl = repl .. toLower(remainingReplace) + else + repl = repl .. remainingReplace end + elseif lastCase == false then + repl = repl .. toLower(remainingReplace) else - -- no letter - if lastCase == nil then - repl = repl .. remainingReplace - elseif lastCase == false then + if (isLower(nextLetter)) then repl = repl .. toLower(remainingReplace) else repl = repl .. toUpper(remainingReplace) @@ -308,16 +526,30 @@ function Grichelde:ReplaceCharacters(text) -- actual replacement result = pre .. repl .. post - self:DebugPrint("result: %s", result) + self:DebugPrint("ReplaceCharacters : result: %s", result) -- remember positions for consolidate if replTable.consolidate then tInsert(consolidate[replName], pos1 + offset) + self:TracePrint("consolidate[" .. replName .. "] is:") + self:TracePrint(consolidate[replName]) + end + + -- update previous consolidate markers + local diff = length(repl) - length(lowerSearch) + for key, posList in pairs(consolidate) do + if key ~= replName then + for i, pc in ipairs(posList) do + if pos1 < pc then + consolidate[key][i] = consolidate[key][i] + diff + end + end + end end -- replacement text can be longer or shorter the resulting text -- after replacement result and lowerResult can have different sizes - offset = offset + length(repl) - length(lowerSearch) + offset = offset + diff -- update values for next iteration pos = pos2 + 1 pos1, pos2 = find(lowerResult, lowerSearch, pos) @@ -327,18 +559,21 @@ function Grichelde:ReplaceCharacters(text) if before ~= result then self:DebugPrint("ReplaceCharacters : replaced \"%s\" with \"%s\"", before, result) end + if replTable.consolidate then + self:DebugPrint("consolidate[" .. replName .. "] is:") + self:DebugPrint(consolidate[replName]) + end else - self:DebugPrint("ReplaceCharacters : Skip replacement for empty mapping") + self:DebugPrint("ReplaceCharacters : Skip replacement for %s", replName) end end -- consolidation is done last for replName, replTable in spairs(replacements) do local before = result - --local search = replTable.searchText local search = replTable.searchText - if not nilOrEmpty(search) then + if not nilOrEmpty(search) and replTable.active then local replace = replTable.replaceText local lowerResult = toLower(result) local offset = 0 @@ -350,20 +585,20 @@ function Grichelde:ReplaceCharacters(text) for _, pos1 in spairs(consolidate[replName]) do local pos2 = pos1 + length(replace) - 1 - self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) - local match = sub(lowerResult, pos1, pos2) + self:TracePrint("ReplaceCharacters : pos1: %d, pos2: %d", pos1, pos2) + local match = toLower(replace) local next = sub(lowerResult, pos2 + 1, pos2 + 1 + pos2 - pos1) - self:TracePrint("match: %s, next: %s", match, next) + self:TracePrint("ReplaceCharacters : match: %s, next: %s", match, next) local _, p2 = find(next, "^" .. match) - self:TracePrint("p2: %d", p2) + self:TracePrint("ReplaceCharacters : p2: %d", p2) if (p2) then result = sub(result, 1, pos2 + offset) .. sub(result, pos2 + 1 + p2 + offset) -- consolidation will shorten the resulting text offset = offset + length(result) - length(lowerResult) end - self:DebugPrint("result: %s", result) + self:DebugPrint("ReplaceCharacters : result: %s", result) end end @@ -371,7 +606,7 @@ function Grichelde:ReplaceCharacters(text) self:DebugPrint("ReplaceCharacters : consolidate \"%s\" with \"%s\"", before, result) end else - self:DebugPrint("ReplaceCharacters : Skip consolidation for empty mapping") + self:DebugPrint("ReplaceCharacters : Skip consolidation for %s", replName) end end diff --git a/GricheldeConstants.lua b/GricheldeConstants.lua index 5f34dc9..3db054c 100644 --- a/GricheldeConstants.lua +++ b/GricheldeConstants.lua @@ -8,10 +8,16 @@ Grichelde.LOG_LEVEL.DEBUG = 1 Grichelde.LOG_LEVEL.TRACE = 2 Grichelde.MAPPING_OFFSET = 10 +Grichelde.MINIMAP_ENABLED = 1.0 +Grichelde.MINIMAP_DARKENDED = 0.5 Grichelde.ICONS = {} Grichelde.ICONS.MOVE_UP = "Interface\\MainMenuBar\\UI-MainMenu-ScrollUpButton-Up" +Grichelde.ICONS.MOVE_UP_DISABLED = "Interface\\MainMenuBar\\UI-MainMenu-ScrollUpButton-Disabled" Grichelde.ICONS.MOVE_DOWN = "Interface\\MainMenuBar\\UI-MainMenu-ScrollDownButton-Up" +Grichelde.ICONS.MOVE_DOWN_DISABLED = "Interface\\MainMenuBar\\UI-MainMenu-ScrollDownButton-Disabled" +Grichelde.ICONS.DELETE = "Interface\\Buttons\\UI-Panel-MinimizeButton-Up" +Grichelde.ICONS.DELETE_DISABLED = "Interface\\Buttons\\UI-Panel-MinimizeButton-Disabled" -- colors: Grichelde.COLORS = {} @@ -35,7 +41,109 @@ Grichelde.COLOR_CODES.LIGHTYELLOW = _G.LIGHTYELLOW_FONT_COLOR_CODE or "|cffffff9 Grichelde.COLOR_CODES.ORANGE = _G.ORANGE_FONT_COLOR_CODE or "|cffff7f3f" 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.SLASH_COMMANDS = { "gri", "grichelde" } + +Grichelde.SUPPORTED_CHAT_COMMANDS = { + ["/s"] = "SAY", + ["/say"] = "SAY", + ["/sprechen"] = "SAY", + ["/c"] = "CHANNEL", + ["/csay"] = "CHANNEL", + ["/e"] = "EMOTE", + ["/em"] = "EMOTE", + ["/me"] = "EMOTE", + ["/emote"] = "EMOTE", + ["/y"] = "YELL", + ["/yell"] = "YELL", + ["/sh"] = "YELL", + ["/shout"] = "YELL", + ["/schreien"] = "YELL", + ["/sch"] = "YELL", + ["/p"] = "PARTY", + ["/party"] = "PARTY", + ["/gruppe"] = "PARTY", + ["/pl"] = "PARTY", + ["/partyleader"] = "PARTY", + ["/g"] = "GUILD", + ["/gc"] = "GUILD", + ["/guild"] = "GUILD", + ["/gilde"] = "GUILD", + ["/o"] = "OFFICER", + ["/osay"] = "OFFICER", + ["/officer"] = "OFFICER", + ["/offizier"] = "OFFICER", + ["/raid"] = "RAID", + ["/rsay"] = "RAID", + ["/rl"] = "RAID", + ["/rsay"] = "RAID", + ["/raidleader"] = "RAID", + ["/schlachtzug"] = "RAID", + ["/rw"] = "RAID_WARNING", + ["/raidwarning"] = "RAID_WARNING", + ["/i"] = "INSTANCE_CHAT", + ["/instance"] = "INSTANCE_CHAT", + ["/instanz"] = "INSTANCE_CHAT", + ["/bg"] = "BATTLEGROUND", + ["/battleground"] = "BATTLEGROUND", + ["/schlachfeld"] = "BATTLEGROUND", + ["/w"] = "WHISPER", + ["/whisper"] = "WHISPER", + ["/t"] = "WHISPER", + ["/tell"] = "WHISPER", + ["/send"] = "WHISPER", + ["/tt"] = "WHISPER", + ["/r"] = "WHISPER", + ["/reply"] = "WHISPER", + ["/fl\195\188stern"] = "WHISPER", + ["/antworten"] = "WHISPER", +} + +Grichelde.BLIZZ_TYPE_TO_OPTIONS = { + ["SAY"] = "say", + ["EMOTE"] = "emote", + ["YELL"] = "yell", + ["PARTY"] = "party", + ["GUILD"] = "guild", + ["OFFICER"] = "officer", + ["RAID"] = "raid", + ["RAID_WARNING"] = "raidWarning", + ["INSTANCE"] = "instance", + ["BATTLEGROUND"] = "battleground", + ["WHISPER"] = "whisper", +} + +-- do not replace these patterns +Grichelde.IGNORE_PATTERNS_CASE_SENSITIVE = { + "|[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 + "|K.-|k", -- Battle.net + "|n", -- newline + + "%(%(.-%)%)", -- (( ooc )) +} + +-- for separate emote detection +Grichelde.EMOTE_PATTERNS = { + "%*.-%*", -- emotes * + "%*%*.-%*%*", -- emotes ** + "%<.-%>" -- emotes < > +} + +Grichelde.IGNORE_PATTERNS_CASE_INSENSITIVE = { + "{rt[1-8]}", -- rumbered raid target icons, localized raid targets are handled differently + "%%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 +} + +Grichelde.LOCALIZED_RAID_TARGETS = { "Star", "Circle", "Diamond", "Triangle", "Moon", "Square", "Cross", "Skull" } local function nilOrEmpty(s) return s == nil or s:trim() == "" @@ -68,12 +176,25 @@ local function spairs(t, orderFunc) end end -local function tFilter(t, cond, extr) +local function tFilter(t, condition, extract) 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) + local cond = false + if condition then + local t = Grichelde.functions.type(condition) + if t == "function" then + cond = condition(t, key, value) + elseif t == "string" or t == "number" then + cond = (value == condition) + end + end + + if cond then + local val = value + if extract and Grichelde.functions.type(extract) == "function" then + val = extract(t, key, value) + end + Grichelde.functions.tInsert(filtered, val) end end return filtered @@ -90,6 +211,11 @@ local function tSize(t) return size end +local function tIsEmpty(t) + if (not t) then return true end + return Grichelde.functions.tNext(t) == nil +end + local function tClone(orig) local orig_type = Grichelde.functions.type(orig) local copy @@ -107,7 +233,7 @@ local function tClone(orig) end local function isChar(word) - return Grichelde.functions.find(word, "%a+") + return Grichelde.functions.find(word, "[%z\65-\90\97-\122\195-\197][\128-\191]?") end local function isNumber(digit) @@ -128,9 +254,34 @@ local function isCapital(word) and Grichelde.functions.isLower(Grichelde.functions.sub(word,2)) end +local function color(color, text) + return color .. text .. Grichelde.COLOR_CODES.CLOSE +end + +local function cPrefix(text) + return Grichelde.functions.color(Grichelde.COLOR_CODES.PREFIX, text) +end + +local function cYellow(text) + return Grichelde.functions.color(Grichelde.COLOR_CODES.NORMAL, text) +end + +local function cGray(text) + return Grichelde.functions.color(Grichelde.COLOR_CODES.GRAY, text) +end + +local function cDarkgray(text) + return Grichelde.functions.color(Grichelde.COLOR_CODES.DARKGRAY, text) +end + +local function cOrange(text) + return Grichelde.functions.color(Grichelde.COLOR_CODES.ORANGE, text) +end + -- faster function lookups by mapping to local refs Grichelde.functions = {} Grichelde.functions.IsAddOnLoaded = _G.IsAddOnLoaded +Grichelde.functions.assert = _G.assert Grichelde.functions.type = _G.type Grichelde.functions.print = _G.print Grichelde.functions.nilOrEmpty = nilOrEmpty @@ -142,9 +293,11 @@ Grichelde.functions.tFilter = tFilter Grichelde.functions.tInsert = _G.table.insert Grichelde.functions.tConcat = _G.table.concat Grichelde.functions.tSize = tSize +Grichelde.functions.tIsEmpty = tIsEmpty Grichelde.functions.tSort = _G.table.sort Grichelde.functions.tClone = tClone Grichelde.functions.tNext = _G.next +Grichelde.functions.tWipe = _G.wipe Grichelde.functions.setmetatable = _G.setmetatable Grichelde.functions.getmetatable = _G.getmetatable Grichelde.functions.select = _G.select @@ -162,10 +315,17 @@ Grichelde.functions.isNumber = isNumber Grichelde.functions.isUpper = isUpper Grichelde.functions.isLower = isLower Grichelde.functions.isCapital = isCapital +Grichelde.functions.color = color +Grichelde.functions.cPrefix = cPrefix +Grichelde.functions.cYellow = cYellow +Grichelde.functions.cGray = cGray +Grichelde.functions.cDarkgray = cDarkgray +Grichelde.functions.cOrange = cOrange 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.lenUtf8 = _G.strlenutf8 Grichelde.functions.toString = _G.tostring Grichelde.functions.toNumber = _G.tonumber Grichelde.functions.max = _G.math.max diff --git a/GricheldeDatabase.lua b/GricheldeDatabase.lua index b66a148..d59cff6 100644 --- a/GricheldeDatabase.lua +++ b/GricheldeDatabase.lua @@ -24,6 +24,7 @@ function Grichelde:GetDefaultConfig() }, replacements = { ["**"] = { + active = true, order = 9999, searchText = "", replaceText = "", @@ -34,23 +35,12 @@ function Grichelde:GetDefaultConfig() order = 10, searchText = "s", replaceText = "ch", - exactCase = false, - consolidate = true, }, replacement_11 = { order = 11, searchText = "t", replaceText = "ck", - exactCase = false, - consolidate = true, }, - replacement_12 = { - order = 12, - searchText = "Zark", - replaceText = "toter Schamane", - exactCase = false, - consolidate = true, - } } } } @@ -156,4 +146,4 @@ function Grichelde:ReorderReplacements() self:DebugPrint("ReorderReplacements : sorted table") self:DebugPrint(self.db.profile.replacements) -end \ No newline at end of file +end diff --git a/GricheldeMinimap.lua b/GricheldeMinimap.lua new file mode 100644 index 0000000..d2512ba --- /dev/null +++ b/GricheldeMinimap.lua @@ -0,0 +1,107 @@ +-- read namespace from global env +local _G = _G +local Grichelde = _G.Grichelde + +--- add Minimap button +function Grichelde:MinimapButton() + local function clickHandler(_, button) + if button == 'LeftButton' then + self:ToggleOptions() + elseif button == 'RightButton' then + self:ToggleActivation() + end + end + + local function updateTooltip(tooltip) + if not tooltip or not tooltip.AddLine then return end + + local tooltipTitle = self:Format(self.L.Minimap_Tooltip_Enabled, self.L.AddonName) + if not self.db.profile.enabled then + tooltipTitle = self:Format(self.L.Minimap_Tooltip_Disabled, self.L.AddonName) + end + + tooltip:SetText(tooltipTitle, + Grichelde.COLORS.HIGHLIGHT.r, Grichelde.COLORS.HIGHLIGHT.g, Grichelde.COLORS.HIGHLIGHT.b, Grichelde.COLORS.HIGHLIGHT.a + ) + + tooltip:AddDoubleLine(self.L.Minimap_Tooltip_Options_Left, self.L.Minimap_Tooltip_Options_Right, + Grichelde.COLORS.GREEN.r, Grichelde.COLORS.GREEN.g, Grichelde.COLORS.GREEN.b, Grichelde.COLORS.GREEN.a, + Grichelde.COLORS.NORMAL.r, Grichelde.COLORS.NORMAL.g, Grichelde.COLORS.NORMAL.b, Grichelde.COLORS.NORMAL.a + ) + tooltip:AddDoubleLine(self.L.Minimap_Tooltip_Mappings_Left, self.L.Minimap_Tooltip_Mappings_Right, + Grichelde.COLORS.GREEN.r, Grichelde.COLORS.GREEN.g, Grichelde.COLORS.GREEN.b, Grichelde.COLORS.GREEN.a, + Grichelde.COLORS.NORMAL.r, Grichelde.COLORS.NORMAL.g, Grichelde.COLORS.NORMAL.b, Grichelde.COLORS.NORMAL.a + ) + end + + local darkened = Grichelde.MINIMAP_ENABLED + if not self.db.profile.enabled then + darkened = Grichelde.MINIMAP_DARKENDED + end + + local ldb = LibStub("LibDataBroker-1.1"):NewDataObject(self.name, { + type = "launcher", + text = self.AddonName, + icon = "Interface\\Icons\\Spell_Holy_Silence", + --icon = ([[Interface\Addons\%s\%s]]):format(self.name, self.name), + OnClick = clickHandler, + OnRightClick = function() self:ShowMappings() end, + OnTooltipShow = updateTooltip, + iconR = darkened, + iconG = darkened, + iconB = darkened + }) + + local icon = LibStub("LibDBIcon-1.0") + self:DebugPrint("MinimapButton : hidden: ", self.db.profile.minimapButton.hide) + icon:Register(self.name, ldb, self.db.profile.minimapButton) + + return ldb, icon +end + +function Grichelde:ToggleMinimapButton() + self.db.profile.minimapButton.hide = not self.db.profile.minimapButton.hide + self:DebugPrint("ToggleMinimapButton : hidden: ", self.db.profile.minimapButton.hide) + + if self.db.profile.minimapButton.hide then + self:HideMinimapButton() + else + self:ShowMinimapButton() + end +end + +function Grichelde:ShowMinimapButton() + if self.icon then + self.icon:Show(self.name) + end +end + +function Grichelde:HideMinimapButton() + if self.icon then + self.icon:Hide(self.name) + end +end + +function Grichelde:ToggleActivation() + self.db.profile.enabled = not self.db.profile.enabled + -- refresh option UI if open at the moment + self.dialog:SelectGroup(self.name, "enabled") + + local formatString = self.L.AddonLoaded + local darkened = Grichelde.MINIMAP_ENABLED + if not self.db.profile.enabled then + formatString = self.L.AddonUnloaded + darkened = Grichelde.MINIMAP_DARKENDED + end + + if self.dialog ~= nil and self.dialog.OpenFrames[self.name] ~= nil then + local namePlusVersion = self:Format(self.L.AddonNamePlusVersion, self.L.AddonName, self.version) + local statusText = self:Format(formatString, namePlusVersion) + self.dialog.OpenFrames[self.name]:SetStatusText(statusText) + end + + self.ldb.iconR = darkened + self.ldb.iconG = darkened + self.ldb.iconB = darkened +end + diff --git a/GricheldeOptions.lua b/GricheldeOptions.lua index 5e505ea..e0395da 100644 --- a/GricheldeOptions.lua +++ b/GricheldeOptions.lua @@ -2,8 +2,10 @@ local _G = _G local Grichelde = _G.Grichelde -local nilOrEmpty, pairs, find, match, toString, toNumber - = Grichelde.functions.nilOrEmpty, Grichelde.functions.pairs, Grichelde.functions.find, Grichelde.functions.match, Grichelde.functions.toString, Grichelde.functions.toNumber +local nilOrEmpty, pairs, tContains, tWipe, find, match, color, toString, toNumber + = Grichelde.functions.nilOrEmpty, Grichelde.functions.pairs, Grichelde.functions.tContains, Grichelde.functions.tWipe, Grichelde.functions.find, Grichelde.functions.match, Grichelde.functions.color, Grichelde.functions.toString, Grichelde.functions.toNumber + +local selectedExample = 1 function Grichelde:CreateOptionsUI() return { @@ -20,6 +22,7 @@ function Grichelde:CreateOptionsUI() type = "toggle", name = self.L.Options_Enabled_Name, desc = self:Format(self.L.Options_Enabled_Desc, self.L.AddonName), + set = function() self:ToggleActivation() end, disabled = false, }, minimapButton = { @@ -27,25 +30,62 @@ function Grichelde:CreateOptionsUI() type = "toggle", name = self.L.Options_Minimap_Button_Name, desc = self.L.Options_Minimap_Button_Desc, - set = function(info, val) self:ToogleMinimapButton(info, val) end, - get = function(info) return not self.db.profile.minimapButton.hide end, + set = function(_, _) self:ToggleMinimapButton() end, + get = function(_) return not self.db.profile.minimapButton.hide end, disabled = false, }, - channels = { + + replacements = { order = 2, type = "group", + name = self.L.Options_Replacements_Group_Name, + desc = self.L.Options_Replacements_Group_Desc, + args = { + 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:AddEmptyMapping(info) end, + }, + deleteAll = { + order = 1, + type = "execute", + confirm = true, + confirmText = self.L.Options_Replacements_DeleteAll_ConfirmText, + name = self.L.Options_Replacements_DeleteAll_Name, + desc = self.L.Options_Replacements_DeleteAll_Desc, + func = function(info) self:DeleteAllMappings(info) end, + }, + header = { + order = 3, + type = "description", + name = self.L.Options_Replacements_Header, + }, + spacer1 = { + order = 4, + type = "header", + name = "", + }, + }, + }, + + channels = { + order = 3, + type = "group", name = self.L.Options_Channels_Group_Name, desc = self:Format(self.L.Options_Channels_Group_Desc, self.L.AddonName), args = { header = { order = 1, type = "description", - name = self.L.Options_Channels_Header + name = self.L.Options_Channels_Header, }, - spacer = { + spacer2 = { order = 2, type = "header", - name = "" + name = "", }, say = { order = 10, @@ -113,45 +153,106 @@ function Grichelde:CreateOptionsUI() name = self.L.Options_Channel_Whisper_Name, desc = self:Format(self.L.Options_Channel_Whisper_Desc, self.L.AddonName), }, - } + }, }, - replacements = { - order = 3, + help = { + order = -1, type = "group", - name = self.L.Options_Replacements_Group_Name, - desc = self.L.Options_Replacements_Group_Desc, + childGroups = "tab", + name = self.L.Options_Help_Group_Name, + desc = self.L.Options_Help_Group_Desc, + disabled = false, args = { - 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:AddEmptyMapping(info) end + basics = { + order = 10, + type = "group", + name = self.L.Options_Help_Tab_Basics_Name, + desc = self.L.Options_Help_Tab_Basics_Desc, + args = { + paragraph1 = { + order = 1, + type = "description", + name = self.L.Options_Help_Basics, + fontSize = "medium", + }, + }, }, - deleteAll = { - order = 1, - type = "execute", - confirm = true, - confirmText = self.L.Options_Replacements_DeleteAll_ConfirmText, - name = self.L.Options_Replacements_DeleteAll_Name, - desc = self.L.Options_Replacements_DeleteAll_Desc, - func = function(info) self:DeleteAllMappings(info) end + expert = { + order = 11, + type = "group", + name = self.L.Options_Help_Tab_Expert_Name, + desc = self.L.Options_Help_Tab_Expert_Desc, + args = { + paragraph4 = { + order = 1, + type = "description", + name = self.L.Options_Help_Expert, + fontSize = "medium", + }, + }, }, - header = { - order = 3, - type = "description", - name = self.L.Options_Replacements_Header + examples = { + order = 12, + type = "group", + name = self.L.Options_Help_Tab_Examples_Name, + desc = self.L.Options_Help_Tab_Examples_Desc, + args = { + note = { + order = 1, + type = "description", + name = self.L.Options_Help_Examples_Note, + }, + header = { + order = 2, + type = "description", + name = self.L.Options_Help_Examples0_Header, + fontSize = "medium", + width = 2.5, + }, + dropDown = { + order = 3, + type = "select", + name = "", + --width = 1, + values = { + self.L.Options_Help_Examples1_Select, + self.L.Options_Help_Examples2_Select, + self.L.Options_Help_Examples3_Select, + self.L.Options_Help_Examples4_Select, + self.L.Options_Help_Examples5_Select, + self.L.Options_Help_Examples6_Select, +-- self.L.Options_Help_Examples7_Select, + }, + set = function(info, val) selectedExample = val end, + get = function(_) + self.options.args.help.args.examples.args.header.name = self.L["Options_Help_Examples" .. selectedExample .. "_Header"] + self.options.args.help.args.examples.args.example.name = self.L["Options_Help_Examples" .. selectedExample .. "_Text"] + self.dialog:SelectGroup(self.name, "help", "examples", "header.name") + return selectedExample + end, + }, + spacer3 = { + order = 4, + type = "header", + name = "", + }, + example = { + order = 5, + type = "description", + name = self.L.Options_Help_Examples0_Text, + fontSize = "medium", + }, + }, }, - spacer = { - order = 4, - type = "header", - name = "" + disclaimer = { + order = 20, + type = "description", + name = self.L.Options_Help_Disclaimer, }, - } - } - } + }, + }, + }, } end @@ -162,69 +263,89 @@ function Grichelde:CreateMapping(offset) name = function(info) return self:MappingName(info) end, desc = self.L.Options_Mapping_Group_Desc, childGroups = "tree", + disabled = function(info) return not self:IsMappingActive(info) end, args = { - searchText = { - order = 0, - type = "input", - name = self.L.Options_Mapping_SearchText_Name, - desc = self.L.Options_Mapping_SearchText_Desc, - }, - replaceText = { - order = 1, - type = "input", - name = self.L.Options_Mapping_ReplaceText_Name, - desc = self.L.Options_Mapping_ReplaceText_Desc, - }, - exactCase = { - order = 2, - type = "toggle", - name = self.L.Options_Mapping_ExactCase_Name, - desc = self.L.Options_Mapping_ExactCase_Desc, - width = "full", - }, - consolidate = { - order = 3, - type = "toggle", - name = self.L.Options_Mapping_Consolidate_Name, - desc = self.L.Options_Mapping_Consolidate_Desc, - width = "full" - }, moveUp = { - order = 10, + order = 0, type = "execute", - -- name = self.L.Options_Mapping_MoveUp_Name, name = "", desc = self.L.Options_Mapping_MoveUp_Desc, - image = Grichelde.ICONS.MOVE_UP, + image = function(info) return self:GetMoveUpImage(info) end, width = 0.15, - func = function(info) self:MoveUp(info) end + func = function(info) self:MoveUp(info) end, }, moveDown = { - order = 11, + order = 1, type = "execute", - -- name = self.L.Options_Mapping_MoveDown_Name, name = "", desc = self.L.Options_Mapping_MoveDown_Desc, - image = Grichelde.ICONS.MOVE_DOWN, + image = function(info) return self:GetMoveDownImage(info) end, width = 0.15, - func = function(info) self:MoveDown(info) end + func = function(info) self:MoveDown(info) end, }, - spacer = { - order = 18, +--[[ + spacer4 = { + order = 2, type = "description", name = "", - width = 1.2, + width = 0.1, + }, +]] + active = { + order = 3, + type = "toggle", + name = self.L.Options_Mapping_Enabled_Name, + desc = self.L.Options_Mapping_Enabled_Desc, + width = 2.2, }, delete = { - order = 19, + order = 4, type = "execute", confirm = true, confirmText = self.L.Options_Mapping_Delete_ConfirmText, - name = self.L.Options_Mapping_Delete_Name, + name = "", desc = self.L.Options_Mapping_Delete_Desc, - width = 0.5, - func = function(info) self:DeleteMapping(info) end + image = function(info) return self:GetDeleteImage(info) end, + width = 0.1, + func = function(info) self:DeleteMapping(info) end, + }, + + searchText = { + order = 10, + type = "input", + name = self.L.Options_Mapping_SearchText_Name, + desc = self.L.Options_Mapping_SearchText_Desc, + }, + replaceText = { + order = 11, + type = "input", + name = self.L.Options_Mapping_ReplaceText_Name, + desc = self.L.Options_Mapping_ReplaceText_Desc, + }, + + exactCase = { + order = 20, + type = "toggle", + name = self.L.Options_Mapping_ExactCase_Name, + desc = self.L.Options_Mapping_ExactCase_Desc, + width = "full", }, + consolidate = { + order = 21, + type = "toggle", + name = self.L.Options_Mapping_Consolidate_Name, + desc = self.L.Options_Mapping_Consolidate_Desc, + width = "full", + }, +--[[ + stopOnMatch = { + order = 22, + type = "toggle", + name = self.L.Options_Mapping_StopOnMatch_Name, + desc = self.L.Options_Mapping_StopOnMatch_Desc, + width = "full", + } +]] } } end @@ -233,6 +354,7 @@ function Grichelde:SetupOptions() -- add DB-backed profiles to UI options local options = self:CreateOptionsUI() options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) + options.args.profiles.order = 8 options.args.profiles.disabled = false -- Adding options to blizzard frame @@ -245,16 +367,17 @@ end function Grichelde:RefreshOptions(event) self:DebugPrint("RefreshOptions : event:", event) + local currentProfile = color(Grichelde.COLOR_CODES.GREEN, self.db:GetCurrentProfile()) if event == "OnNewProfile" then - self:PrefixedPrint(self.L.Profiles_Created, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Profiles_Created, currentProfile) elseif event == "OnProfileChanged" then - self:PrefixedPrint(self.L.Profiles_Loaded, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Profiles_Loaded, currentProfile) elseif event == "OnProfileDeleted" then - self:PrefixedPrint(self.L.Profiles_Deleted, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Profiles_Deleted, currentProfile) elseif event == "OnProfileCopied" then - self:PrefixedPrint(self.L.Profiles_Copied, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Profiles_Copied, currentProfile) elseif event == "OnProfileReset" then - self:PrefixedPrint(self.L.Profiles_Reset, self.COLOR_CODES.GREEN .. self.db:GetCurrentProfile() .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Profiles_Reset, currentProfile) else self:DebugPrint("Refreshing Profile %s on options change: %s", self.db:GetCurrentProfile(), event) end @@ -263,53 +386,9 @@ function Grichelde:RefreshOptions(event) self:RefreshReplacements(self.db.profile.replacements) end ---- add Minimap button -function Grichelde:MinimapButton() - local function toggleOptionsUI(_, button) - if button == 'LeftButton' then - self:ToggleOptions() - elseif button == 'RightButton' then - self:ToogleMappings() - end - end - - local function tooltip(tooltip) - if not tooltip or not tooltip.AddLine then return end - tooltip:SetText(self.L.AddonName, - Grichelde.COLORS.HIGHLIGHT.r, Grichelde.COLORS.HIGHLIGHT.g, Grichelde.COLORS.HIGHLIGHT.b, Grichelde.COLORS.HIGHLIGHT.a - ) - - tooltip:AddDoubleLine(self.L.Options_Minimap_Tooltip_Options_Left, self.L.Options_Minimap_Tooltip_Options_Right, - Grichelde.COLORS.GREEN.r, Grichelde.COLORS.GREEN.g, Grichelde.COLORS.GREEN.b, Grichelde.COLORS.GREEN.a, - Grichelde.COLORS.NORMAL.r, Grichelde.COLORS.NORMAL.g, Grichelde.COLORS.NORMAL.b, Grichelde.COLORS.NORMAL.a - ) - tooltip:AddDoubleLine(self.L.Options_Minimap_Tooltip_Mappings_Left, self.L.Options_Minimap_Tooltip_Mappings_Right, - Grichelde.COLORS.GREEN.r, Grichelde.COLORS.GREEN.g, Grichelde.COLORS.GREEN.b, Grichelde.COLORS.GREEN.a, - Grichelde.COLORS.NORMAL.r, Grichelde.COLORS.NORMAL.g, Grichelde.COLORS.NORMAL.b, Grichelde.COLORS.NORMAL.a - ) - end - - local ldb = LibStub("LibDataBroker-1.1"):NewDataObject(self.name, { - type = "launcher", - text = self.AddonName, - icon = "Interface\\Icons\\Spell_Holy_Silence", - --icon = ([[Interface\Addons\%s\%s]]):format(self.name, self.name), - OnClick = toggleOptionsUI, - OnRightClick = function() self:ShowMappings() end, - OnTooltipShow = tooltip, - }) - - local icon = LibStub("LibDBIcon-1.0") - self:DebugPrint("MinimapButton : hidden: ", self.db.profile.minimapButton.hide) - icon:Register(self.name, ldb, self.db.profile.minimapButton) - - return ldb, icon -end - - function Grichelde:ToggleOptions() - self:DebugPrint("MinimapButton : options open: ", self.dialog.OpenFrames[self.name]) - if self.dialog.OpenFrames[self.name] then + self:DebugPrint("ToggleOptions : options open: ", not not self.dialog.OpenFrames[self.name]) + if self.dialog ~= nil and self.dialog.OpenFrames[self.name] ~= nil then self:CloseOptions() else self:OpenOptions() @@ -317,29 +396,78 @@ function Grichelde:ToggleOptions() end function Grichelde:OpenOptions() - self.dialog:Open(self.name) + if self.dialog ~= nil then + self.dialog:Open(self.name) + + local formatString = self.L.AddonLoaded + if not self.db.profile.enabled then + formatString = self.L.AddonUnloaded + end + + local namePlusVersion = self:Format(self.L.AddonNamePlusVersion, self.L.AddonName, self.version) + local statusText = self:Format(formatString, namePlusVersion) + self.dialog.OpenFrames[self.name]:SetStatusText(statusText) + end end function Grichelde:CloseOptions() - self.dialog:Close(self.name) + if self.dialog ~= nil then + self.dialog:Close(self.name) + end end +--- If all replacements were disabled +-- @return (boolean) function Grichelde:IsDisabled(info) - if info.option.type == "group" then + if info and info.option.type == "group" then return false end return not self.db.profile.enabled end +--- If all replacements were disabled +-- @return (boolean) +function Grichelde:IsMappingActive(info) + self:TracePrint("IsMappingActive : info") + for i = 0, #info do + self:TracePrint("%d = %s", i, info[i]) + end + + if info and info.option.type == "group" then + return true + end + if not self.db.profile.enabled then + return false + end + + local replacements = self.db.profile.replacements or {} + local currentName = info[2] + local uiElem = info[3] + + self:DebugPrint("IsMappingActive : \"%s\"", currentName) + self:DebugPrint(replacements[currentName]) + + if (tContains({"moveUp", "moveDown", "active", "delete"}, uiElem)) then + return true + else + return not not replacements[currentName].active + end +end + function Grichelde:MappingName(info) -- self:TracePrint("MappingName : info") -- self:TracePrint(info) local option = self.db.profile.replacements[info[2]] if nilOrEmpty(option.searchText) and nilOrEmpty(option.replaceText) then - return self.L.Options_Mapping_EmptyMapping + return color(Grichelde.COLOR_CODES.GRAY, self.L.Options_Mapping_EmptyMapping) else - return self:Format(self.L.Options_Mapping_Group_Name, option.searchText or "", option.replaceText or "") + local name = self:Format(self.L.Options_Mapping_Group_Name, option.searchText or "", option.replaceText or "") + if option.active == true then + return name + else + return color(Grichelde.COLOR_CODES.GRAY, name) + end end end @@ -473,6 +601,66 @@ function Grichelde:MoveDown(info) end end +function Grichelde:GetMoveUpImage(info) + self:TracePrint("GetMoveUpImage : info") + for i = 0, #info do + self:TracePrint("%d = %s", i, info[i]) + end + + local currentName = info[2] + self:DebugPrint("GetMoveUpImage : \"%s\"", currentName) + + local _, replNumber = self:SplitOnFirstMatch(currentName, "_") + local currentOrder = toNumber(replNumber) + + if (self:IsMappingActive(info) and currentOrder > Grichelde.MAPPING_OFFSET ) then + return Grichelde.ICONS.MOVE_UP + else + return Grichelde.ICONS.MOVE_UP_DISABLED + end +end + +function Grichelde:GetMoveDownImage(info) + self:TracePrint("GetMoveDownImage : info") + for i = 0, #info do + self:TracePrint("%d = %s", i, info[i]) + end + + local currentName = info[2] + self:DebugPrint("GetMoveDownImage : \"%s\"", currentName) + + local _, replNumber = self:SplitOnFirstMatch(currentName, "_") + local currentOrder = toNumber(replNumber) + + local maxRepl = Grichelde.MAPPING_OFFSET + local replacements = self.db.profile.replacements or {} + for replName, _ in pairs(replacements) do + local num = match(replName, "^replacement_(%d+)") + if num and maxRepl < toNumber(num) then + maxRepl = toNumber(num) + end + end + + if (self:IsMappingActive(info) and currentOrder < maxRepl) then + return Grichelde.ICONS.MOVE_DOWN + else + return Grichelde.ICONS.MOVE_DOWN_DISABLED + end +end + +function Grichelde:GetDeleteImage(info) + self:TracePrint("GetDeleteImage : info") + for i = 0, #info do + self:TracePrint("%d = %s", i, info[i]) + end + + if (self:IsMappingActive(info)) then + return Grichelde.ICONS.DELETE + else + return Grichelde.ICONS.DELETE_DISABLED + end +end + function Grichelde:DeleteMapping(info) self:TracePrint("DeleteMapping : info") for i = 0, #info do @@ -495,28 +683,8 @@ function Grichelde:DeleteAllMappings() self:DebugPrint("DeleteAllMappings") -- do NOT set self.db.profile.replacements = {} it will break defaults - for replName, _ in pairs(self.db.profile.replacements or {}) do - self.db.profile.replacements[replName] = nil - end + tWipe(self.db.profile.replacements) self:AddEmptyMapping() self:RefreshOptions("DeleteAllMappings") end - -function Grichelde:ToogleMinimapButton(info, val) - self:TracePrint("ToogleMinimapButton : info") - for i = 0, #info do - self:TracePrint("%d = %s", i, info[i]) - end - - self.db.profile.minimapButton.hide = not val - self:DebugPrint("ToogleMinimapButton : hidden: ", self.db.profile.minimapButton.hide) - - if self.db.profile.minimapButton.hide then - self.icon:Hide(self.name); - else - self.icon:Show(self.name); - end -end - - diff --git a/GricheldeUpgrade.lua b/GricheldeUpgrade.lua index c91ff88..6bf8c32 100644 --- a/GricheldeUpgrade.lua +++ b/GricheldeUpgrade.lua @@ -2,10 +2,10 @@ local _G = _G local Grichelde = _G.Grichelde -local pairs, find, toNumber = Grichelde.functions.pairs, Grichelde.functions.find, Grichelde.functions.toNumber +local pairs, find, color, cOrange, toNumber = Grichelde.functions.pairs, Grichelde.functions.find, Grichelde.functions.color, Grichelde.functions.cOrange, Grichelde.functions.toNumber function Grichelde:Upgrade_To_v060() - self:PrefixedPrint(self.L.Upgrade_ToVersion, Grichelde.COLOR_CODES.ORANGE .. "0.6.0" .. Grichelde.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Upgrade_ToVersion, cOrange("0.6.0")) local replacements = self.db.profile.replacements or {} self:DebugPrint("Upgrade_To_v060 : old database") @@ -22,10 +22,10 @@ function Grichelde:Upgrade_To_v060() end function Grichelde:Upgrade_To_v070() - self:PrefixedPrint(self.L.Upgrade_ToVersion, Grichelde.COLOR_CODES.ORANGE .. "0.7.0" .. Grichelde.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Upgrade_ToVersion, cOrange("0.7.0")) local replacements = self.db.profile.replacements or {} - self:DebugPrint("Upgrade_To_v070 : old database") + self:DebugPrint("Upgrade_To_v070 : old replacements") self:DebugPrint(replacements) for _, replTable in pairs(replacements) do @@ -33,13 +33,13 @@ function Grichelde:Upgrade_To_v070() replTable["ignoreCase"] = nil end - self:DebugPrint("Upgrade_To_v070 : new database") + self:DebugPrint("Upgrade_To_v070 : new replacements") self:DebugPrint(replacements) return 0, 7, 0 end function Grichelde:Upgrade_To_v072() - self:PrefixedPrint(self.L.Upgrade_ToVersion, Grichelde.COLOR_CODES.ORANGE .. "0.7.2" .. Grichelde.COLOR_CODES.CLOSE) + self:PrefixedPrint(self.L.Upgrade_ToVersion, cOrange("0.7.2")) self:DebugPrint("Upgrade_To_v072 : old database") self:DebugPrint(self.db.profile) @@ -51,6 +51,22 @@ function Grichelde:Upgrade_To_v072() return 0, 7, 2 end +function Grichelde:Upgrade_To_v080() + self:PrefixedPrint(self.L.Upgrade_ToVersion, cOrange("0.8.0")) + + local replacements = self.db.profile.replacements or {} + self:DebugPrint("Upgrade_To_v080 : old replacements") + self:DebugPrint(replacements) + + for _, replTable in pairs(replacements) do + replTable["active"] = true + end + + self:DebugPrint("Upgrade_To_v080 : new replacements") + self:DebugPrint(self.db.profile) + return 0, 8, 0 +end + function Grichelde:UpgradeDatabase() local dbVersion = self.db.global.version or "0.0.0" self:DebugPrint("Database version:", dbVersion) @@ -76,6 +92,10 @@ function Grichelde:UpgradeDatabase() major, minor, patch = self:Upgrade_To_v072(dbVersion) end end + if minor < 8 then + upgrade = upgrade + 1 + major, minor, patch = self:Upgrade_To_v080(dbVersion) + end end if upgrade == 0 then @@ -83,7 +103,7 @@ function Grichelde:UpgradeDatabase() else if not error then self.db.global.version = self.version - self:PrefixedPrint(Grichelde.COLOR_CODES.GREEN .. self.L.Upgrade_Successful .. Grichelde.COLOR_CODES.CLOSE) + self:PrefixedPrint(color(Grichelde.COLOR_CODES.GREEN, self.L.Upgrade_Successful)) end end end \ No newline at end of file diff --git a/GricheldeUtils.lua b/GricheldeUtils.lua index 1c6dd92..20c6fcf 100644 --- a/GricheldeUtils.lua +++ b/GricheldeUtils.lua @@ -2,8 +2,8 @@ local _G = _G local Grichelde = _G.Grichelde -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 +local type, print, pairs, tSize, select, unpack, find, color, cGray, cDarkgray, cPrefix, 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.color, Grichelde.functions.cGray, Grichelde.functions.cDarkgray, Grichelde.functions.cPrefix, Grichelde.functions.format, Grichelde.functions.rep, Grichelde.functions.toString -- show strings differently to distinguish them from numbers local function plainValue(val) @@ -60,7 +60,7 @@ local function tPrint(val, indent, known, printFunc) end end --- split at first word of a text line +--- Splits at first word of a text line function Grichelde:SplitOnFirstMatch(text, delimPattern, start) if text == nil then return nil end local pattern = "^(.-)" .. (delimPattern or " " ) .."(.*)" @@ -71,7 +71,7 @@ function Grichelde:SplitOnFirstMatch(text, delimPattern, start) return left or text, right end --- split at last word of a text line +--- Splits at last word of a text line function Grichelde:SplitOnLastMatch(text, delimPattern, start) local pattern = "(.*)" .. (delimPattern or " ") .. "(.-)$" local pos = start or 1 @@ -81,10 +81,10 @@ function Grichelde:SplitOnLastMatch(text, delimPattern, start) return left, right or text end --- split at last word of a text line +--- Splits 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) + local pos1, pos2, left, right = find(text, pattern, 1) + self:DebugPrint("TestMatch : pos1: %d, pos2: %d, left: %s, right: %s", pos1, pos2, left, right) end function Grichelde:Format(message, ...) @@ -117,18 +117,22 @@ function Grichelde:Print(...) end function Grichelde:PrefixedPrint(...) - print(self.COLOR_CODES.PREFIX .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + print(cPrefix(self.L.AddonName) .. ":", self:Format(...)) +end + +function Grichelde:ErrorPrint(...) + print(cPrefix(self.L.AddonName) .. ": " .. color(self.COLOR_CODES.RED, self:Format(...))) end function Grichelde:DebugPrint(obj, ...) self:LogPrint(Grichelde.LOG_LEVEL.DEBUG, function(...) - print(self.COLOR_CODES.GRAY .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + print(cGray(self.L.AddonName) .. ":", self:Format(...)) end, obj, ...) end function Grichelde:TracePrint(obj, ...) self:LogPrint(Grichelde.LOG_LEVEL.TRACE, function(...) - print(self.COLOR_CODES.DARKGRAY .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + print(cDarkgray(self.L.AddonName) .. ":", self:Format(...)) end, obj, ...) end @@ -166,29 +170,29 @@ end --- Print UI options to chat frame function Grichelde:PrintOptions() - self:PrefixedPrint(self.COLOR_CODES.PREFIX .. self.L.Debug_Options .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(cPrefix(self.L.Debug_Options)) self:LogPrint(-1, function(...) - print(self.COLOR_CODES.PREFIX .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + print(cPrefix(self.L.AddonName) .. ":", self:Format(...)) end, self.options.args.replacements.args) end ---- Print DB +--- Print current DB profile function Grichelde:PrintProfile() - self:PrefixedPrint(self.COLOR_CODES.PREFIX .. self.L.Debug_Profile .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(cPrefix(self.L.Debug_Profile)) self:LogPrint(-1, function(...) - print(self.COLOR_CODES.PREFIX .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + print(cPrefix(self.L.AddonName) .. ":", self:Format(...)) end, self.db.profile) end --- Print DB replacements to chat frame function Grichelde:PrintMappings() - self:PrefixedPrint(self.COLOR_CODES.PREFIX .. self.L.Debug_Mappings .. self.COLOR_CODES.CLOSE) + self:PrefixedPrint(cPrefix(self.L.Debug_Mappings)) self:LogPrint(-1, function(...) - print(self.COLOR_CODES.PREFIX .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + print(cPrefix(self.L.AddonName) .. ":", self:Format(...)) end, self.db.profile.replacements) end ---- Open UI windows with replacements in it +--- Open window with DB replacements in it function Grichelde:ToogleMappings() local AceGUI = LibStub("AceGUI-3.0") @@ -230,7 +234,7 @@ function Grichelde:ToogleMappings() tPrint(replacements, 0, {}, function(s) text = text .. s .. "|n" end) configBox:SetText(text) configBox:SetFullWidth(true) - configBox:SetFullHeight(true) + --configBox:SetFullHeight(true) configBox:DisableButton(true) --configBox:SetDisabled(true) configBox:SetNumLines(50); diff --git a/README.md b/README.md index c139a2e..b4e0cb7 100644 --- a/README.md +++ b/README.md @@ -8,27 +8,57 @@ Intentionally started as a helper addon for a roleplaying friend, Grichelde can * write out abbreviations for you * create hilarious moments during roleplay +## Disclaimer +#### No Warranty +The addon is provided "AS IS" and comes without warranty of any kind of function or correctness (for more details, consult the GPL 3 license). +Also the author is not held responsible for any risk or damage the addon or its use might cause, especially lost of progress or data due to crashes of the WoW client. + +#### Respect others +This addon does not encourange or intend to hurt or to tease people with speaking disabilities or language disorders. +The responsibility rest on the user completely. Please use the features with care and respect to other players. + ## FAQ -### How do I start +#### Where do I start? Grichelde is active right from the start with default mappings. To open the options UI, either left-click on the new minimap icon, or type `/gri` or `/grichelde` in the chatbox. All mappings and channels can be configured there. -### My replacement is not taken. +#### Does it replace only letters but also whole words? + +Grichelde is capable of handling both, even whole sentences can be replaced. +Only slash commands, item links, textures, placeholders and ooc-markers are excluded from replacement. + +#### My replacement is not taken. After entering a search or replacement text, you see a button "Okay" next to yout input. This is **not** a validation message, -but the save button for text. This is a rectriction from the UI library and can be seen in other addons as well. +but the save button for text. This is a restriction from the UI library and can be seen in other addons as well. Please click on "Okay" button to save the input permanently. -### I get errors, what should I do? +If it still does not work or gives you errors, please read the next question. + +#### I have to disable the Addon frequently. Is there a more elegant solution + +Actually there are two solutions: +1. A right-click on the minimap button will disable Grichelde instantly. Right-click a second time will activate it again. Easy, isn't it? +2. You can disable Grichelde permanently and forcefully replace the sentence in the chatbox. I call it "Grichelde-On-Demand" :) + In the chatbox put `/gri` or `/grichelde` in front of your typed text, you can also include the target channel, + i.e. `/gri /guild hello there` and Grichelde will apply the active replacements even if guild channel or Grichelde was disabled. + +#### I get errors, what should I do? Please report your errors here. Make a screenshot or copy both the error message as well as your recent mappings. -You can bring up a small windows with your mapping by right-clicking the minimap icon or entering the "/gri mappings" command. +You can bring up a small windows with your mapping by entering the `/gri mappings` command. + +#### Why that strange name? + +Grichelde is the name of an undead rogue without a jaw, who was played in RP sessions with a guild member. +She started replacing "s" and "t" letters manually for each line in the chat, which became cumbersome over time. +(If you ever wondered how an Undead without a jaw sounds like, its really hilarious, you should try it.) +Without spelling errors, "Griselde" in German is an old-fashioned female first name. -### Why that strange name? +#### I'm a pro. Does it support regular expressions? -Grichelde is the name of the undead rogue without a jaw, that was played in RP session with my friend. -She started replacing "s" and "t" letters manually for each line in the chat, which is cumbersome over time. -(If you wondered how an Undead without a jaw sounds like, its really hilarious.) Without spelling errors, -"Griselde" in German would be a old-fashioned female first name. +This is actually an unofficial feature. In general the searchText is passed in as Lua, so yes regex can be used in lookups. +There are two caveats: first, Lua does not support PCRE syntax, as it would bloat Lua's simplicity and performance (read [here](http://www.lua.org/pil/20.1.html) why). +Secondly, Grichelde does not support capture groups in the replacement text, so matches get lost. \ No newline at end of file diff --git a/libs.xml b/libs.xml index d8f4fbc..dc0c907 100644 --- a/libs.xml +++ b/libs.xml @@ -2,16 +2,16 @@ ..\FrameXML\UI.xsd">