---------------------------------------------------------
-- Module declaration
local HandyNotes = LibStub ( " AceAddon-3.0 " ) : GetAddon ( " HandyNotes " )
local HN = HandyNotes : NewModule ( " HandyNotes " , " AceEvent-3.0 " , " AceConsole-3.0 " )
local L = LibStub ( " AceLocale-3.0 " ) : GetLocale ( " HandyNotes " , false )
local HBD = LibStub ( " HereBeDragons-2.0 " )
local PIN_DRAG_SCALE = 1.2
local WoWRetail = ( WOW_PROJECT_ID == WOW_PROJECT_MAINLINE )
---------------------------------------------------------
-- Our db upvalue and db defaults
local db
local dbdata
local defaults = {
global = {
[ " * " ] = { } , -- ["mapFile"] = {[coord] = {note data}, [coord] = {note data}}
} ,
profile = {
icon_scale = 1.0 ,
icon_alpha = 1.0 ,
} ,
}
---------------------------------------------------------
-- Localize some globals
local next = next
local wipe = wipe
local GameTooltip = GameTooltip
---------------------------------------------------------
-- Constants
-- An addon can replace this table or add to it directly, but keep in mind
-- notes are currently stored with the index number of the chosen icon.
HN.icons = {
[ 1 ] = { text = RAID_TARGET_1 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0 , tCoordRight = 0.25 , tCoordTop = 0 , tCoordBottom = 0.25 } , -- Star
[ 2 ] = { text = RAID_TARGET_2 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0.25 , tCoordRight = 0.5 , tCoordTop = 0 , tCoordBottom = 0.25 } , -- Circle
[ 3 ] = { text = RAID_TARGET_3 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0.5 , tCoordRight = 0.75 , tCoordTop = 0 , tCoordBottom = 0.25 } , -- Diamond
[ 4 ] = { text = RAID_TARGET_4 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0.75 , tCoordRight = 1 , tCoordTop = 0 , tCoordBottom = 0.25 } , -- Triangle
[ 5 ] = { text = RAID_TARGET_5 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0 , tCoordRight = 0.25 , tCoordTop = 0.25 , tCoordBottom = 0.5 } , -- Moon
[ 6 ] = { text = RAID_TARGET_6 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0.25 , tCoordRight = 0.5 , tCoordTop = 0.25 , tCoordBottom = 0.5 } , -- Square
[ 7 ] = { text = RAID_TARGET_7 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0.5 , tCoordRight = 0.75 , tCoordTop = 0.25 , tCoordBottom = 0.5 } , -- Cross
[ 8 ] = { text = RAID_TARGET_8 , icon = " Interface \\ TargetingFrame \\ UI-RaidTargetingIcons " , tCoordLeft = 0.75 , tCoordRight = 1 , tCoordTop = 0.25 , tCoordBottom = 0.5 } , -- Skull
[ 9 ] = { text = MINIMAP_TRACKING_AUCTIONEER , icon = " Interface \\ Minimap \\ Tracking \\ Auctioneer " } ,
[ 10 ] = { text = MINIMAP_TRACKING_BANKER , icon = " Interface \\ Minimap \\ Tracking \\ Banker " } ,
[ 11 ] = { text = MINIMAP_TRACKING_BATTLEMASTER , icon = " Interface \\ Minimap \\ Tracking \\ BattleMaster " } ,
[ 12 ] = { text = MINIMAP_TRACKING_FLIGHTMASTER , icon = " Interface \\ Minimap \\ Tracking \\ FlightMaster " } ,
[ 13 ] = { text = MINIMAP_TRACKING_INNKEEPER , icon = " Interface \\ Minimap \\ Tracking \\ Innkeeper " } ,
[ 14 ] = { text = MINIMAP_TRACKING_MAILBOX , icon = " Interface \\ Minimap \\ Tracking \\ Mailbox " } ,
[ 15 ] = { text = MINIMAP_TRACKING_REPAIR , icon = " Interface \\ Minimap \\ Tracking \\ Repair " } ,
[ 16 ] = { text = MINIMAP_TRACKING_STABLEMASTER , icon = " Interface \\ Minimap \\ Tracking \\ StableMaster " } ,
[ 17 ] = { text = MINIMAP_TRACKING_TRAINER_CLASS , icon = " Interface \\ Minimap \\ Tracking \\ Class " } ,
[ 18 ] = { text = MINIMAP_TRACKING_TRAINER_PROFESSION , icon = " Interface \\ Minimap \\ Tracking \\ Profession " } ,
[ 19 ] = { text = MINIMAP_TRACKING_TRIVIAL_QUESTS , icon = " Interface \\ Minimap \\ Tracking \\ TrivialQuests " } ,
[ 20 ] = { text = MINIMAP_TRACKING_VENDOR_AMMO , icon = " Interface \\ Minimap \\ Tracking \\ Ammunition " } ,
[ 21 ] = { text = MINIMAP_TRACKING_VENDOR_FOOD , icon = " Interface \\ Minimap \\ Tracking \\ Food " } ,
[ 22 ] = { text = MINIMAP_TRACKING_VENDOR_POISON , icon = " Interface \\ Minimap \\ Tracking \\ Poisons " } ,
[ 23 ] = { text = MINIMAP_TRACKING_VENDOR_REAGENT , icon = " Interface \\ Minimap \\ Tracking \\ Reagents " } ,
[ 24 ] = { text = FACTION_ALLIANCE , icon = " Interface \\ TargetingFrame \\ UI-PVP-Alliance " ,
tCoordLeft = 0.05 , tCoordRight = 0.65 , tCoordTop = 0 , tCoordBottom = 0.6 } ,
[ 25 ] = { text = FACTION_HORDE , icon = " Interface \\ TargetingFrame \\ UI-PVP-Horde " ,
tCoordLeft = 0.05 , tCoordRight = 0.65 , tCoordTop = 0 , tCoordBottom = 0.6 } ,
[ 26 ] = { text = FACTION_STANDING_LABEL4 , icon = " Interface \\ TargetingFrame \\ UI-PVP-FFA " ,
tCoordLeft = 0.05 , tCoordRight = 0.65 , tCoordTop = 0 , tCoordBottom = 0.6 } ,
[ 27 ] = { text = ARENA , icon = " Interface \\ PVPFrame \\ PVP-ArenaPoints-Icon " } ,
[ 28 ] = { text = L [ " Portal " ] , icon = " Interface \\ Icons \\ Spell_Arcane_PortalDalaran " } ,
}
---------------------------------------------------------
-- Plugin Handlers to HandyNotes
local HNHandler = { }
function HNHandler : OnEnter ( mapID , coord )
if ( self : GetCenter ( ) > UIParent : GetCenter ( ) ) then -- compare X coordinate
GameTooltip : SetOwner ( self , " ANCHOR_LEFT " )
else
GameTooltip : SetOwner ( self , " ANCHOR_RIGHT " )
end
local title = dbdata [ mapID ] [ coord ] . title
local desc = dbdata [ mapID ] [ coord ] . desc
if title == " " and desc == " " then title = L [ " (No Title) " ] end
if title == " " and desc ~= " " then title = desc desc = nil end
GameTooltip : SetText ( title )
GameTooltip : AddLine ( desc , nil , nil , nil , true )
GameTooltip : Show ( )
end
function HNHandler : OnLeave ( mapID , coord )
GameTooltip : Hide ( )
end
local function deletePin ( button , mapID , coord )
local HNEditFrame = HN.HNEditFrame
if HNEditFrame.coord == coord and HNEditFrame.mapID == mapID then
HNEditFrame : Hide ( )
end
dbdata [ mapID ] [ coord ] = nil
HN : SendMessage ( " HandyNotes_NotifyUpdate " , " HandyNotes " )
end
local function editPin ( button , mapID , coord )
local HNEditFrame = HN.HNEditFrame
HNEditFrame.x , HNEditFrame.y = HandyNotes : getXY ( coord )
HNEditFrame.coord = coord
HNEditFrame.mapID = mapID
HNEditFrame : Hide ( ) -- Hide first to trigger the OnShow handler
HNEditFrame : Show ( )
end
local function addTomTomWaypoint ( button , mapID , coord )
if TomTom then
local x , y = HandyNotes : getXY ( coord )
TomTom : AddWaypoint ( mapID , x , y , {
title = dbdata [ mapID ] [ coord ] . title ,
persistent = nil ,
minimap = true ,
world = true
} )
end
end
do
local isMoving = false
local movingPinScale = nil
local info = { }
local clickedMapID = nil
local clickedCoord = nil
local function generateMenu ( button , level )
if ( not level ) then return end
for k in pairs ( info ) do info [ k ] = nil end
if ( level == 1 ) then
-- Create the title of the menu
info.isTitle = 1
info.text = L [ " HandyNotes " ]
info.notCheckable = 1
local t = HN.icons [ dbdata [ clickedMapID ] [ clickedCoord ] . icon ]
info.icon = t.icon
info.tCoordLeft = t.tCoordLeft
info.tCoordRight = t.tCoordRight
info.tCoordTop = t.tCoordTop
info.tCoordBottom = t.tCoordBottom
UIDropDownMenu_AddButton ( info , level )
-- Edit menu item
info.disabled = nil
info.isTitle = nil
info.notCheckable = nil
info.icon = nil
info.tCoordLeft = nil
info.tCoordRight = nil
info.tCoordTop = nil
info.tCoordBottom = nil
info.text = L [ " Edit Handy Note " ]
info.func = editPin
info.arg1 = clickedMapID
info.arg2 = clickedCoord
UIDropDownMenu_AddButton ( info , level )
-- Delete menu item
info.text = L [ " Delete Handy Note " ]
info.func = deletePin
info.arg1 = clickedMapID
info.arg2 = clickedCoord
UIDropDownMenu_AddButton ( info , level )
if TomTom then
info.text = L [ " Add this location to TomTom waypoints " ]
info.func = addTomTomWaypoint
info.arg1 = clickedMapID
info.arg2 = clickedCoord
UIDropDownMenu_AddButton ( info , level )
end
-- Close menu item
info.text = CLOSE
info.func = function ( ) CloseDropDownMenus ( ) end
info.arg1 = nil
info.arg2 = nil
info.notCheckable = 1
UIDropDownMenu_AddButton ( info , level )
-- Add the dragging hint
info.isTitle = 1
info.func = nil
info.text = L [ " |cFF00FF00Hint: |cffeda55fCtrl+Shift+LeftDrag|cFF00FF00 to move a note " ]
UIDropDownMenu_AddButton ( info , level )
end
end
local HandyNotes_HandyNotesDropdownMenu = CreateFrame ( " Frame " , " HandyNotes_HandyNotesDropdownMenu " )
HandyNotes_HandyNotesDropdownMenu.displayMode = " MENU "
HandyNotes_HandyNotesDropdownMenu.initialize = generateMenu
function HNHandler : OnClick ( button , down , mapID , coord )
if button == " RightButton " and not down then
clickedMapID = mapID
clickedCoord = coord
ToggleDropDownMenu ( 1 , nil , HandyNotes_HandyNotesDropdownMenu , self , 0 , 0 )
elseif button == " LeftButton " and down and IsControlKeyDown ( ) and IsShiftKeyDown ( ) then
-- Only move if we're viewing the same map as the icon's map
if mapID == WorldMapFrame : GetMapID ( ) then
isMoving = true
movingPinScale = self : GetScale ( )
local x , y = self : GetCenter ( )
local s = self : GetEffectiveScale ( ) / UIParent : GetEffectiveScale ( )
self : ClearAllPoints ( )
self : SetParent ( UIParent )
self : SetScale ( PIN_DRAG_SCALE )
self : SetPoint ( " CENTER " , UIParent , " BOTTOMLEFT " , x * s / PIN_DRAG_SCALE , y * s / PIN_DRAG_SCALE )
self : SetFrameStrata ( " TOOLTIP " )
self : StartMoving ( )
end
elseif isMoving and not down then
isMoving = false
self : StopMovingOrSizing ( )
local x , y = self : GetCenter ( )
if movingPinScale then
x = x * PIN_DRAG_SCALE / movingPinScale
y = y * PIN_DRAG_SCALE / movingPinScale
self : SetScale ( movingPinScale )
movingPinScale = nil
end
local s = self : GetEffectiveScale ( ) / WorldMapFrame.ScrollContainer . Child : GetEffectiveScale ( )
x = x * s - WorldMapFrame.ScrollContainer . Child : GetLeft ( )
y = y * s - WorldMapFrame.ScrollContainer . Child : GetTop ( )
self : ClearAllPoints ( )
self : SetParent ( WorldMapFrame.ScrollContainer . Child )
self : SetPoint ( " CENTER " , WorldMapFrame.ScrollContainer . Child , " TOPLEFT " , x / self : GetScale ( ) , y / self : GetScale ( ) )
self : SetFrameStrata ( " TOOLTIP " )
self : SetUserPlaced ( false )
-- Get the new coordinate
x = x / WorldMapFrame.ScrollContainer . Child : GetWidth ( )
y = - y / WorldMapFrame.ScrollContainer . Child : GetHeight ( )
-- Move the button back into the map if it was dragged outside
if x < 0.001 then x = 0.001 end
if x > 0.999 then x = 0.999 end
if y < 0.001 then y = 0.001 end
if y > 0.999 then y = 0.999 end
local newCoord = HandyNotes : getCoord ( x , y )
-- Search in 4 directions till we find an unused coord
local count = 0
local zoneData = dbdata [ mapID ]
while true do
if not zoneData [ newCoord + count ] then
zoneData [ newCoord + count ] = zoneData [ coord ]
break
elseif not zoneData [ newCoord - count ] then
zoneData [ newCoord - count ] = zoneData [ coord ]
break
elseif not zoneData [ newCoord + count * 10000 ] then
zoneData [ newCoord + count * 10000 ] = zoneData [ coord ]
break
elseif not zoneData [ newCoord - count * 10000 ] then
zoneData [ newCoord - count * 10000 ] = zoneData [ coord ]
break
end
count = count + 1
end
dbdata [ mapID ] [ coord ] = nil
HN : SendMessage ( " HandyNotes_NotifyUpdate " , " HandyNotes " )
end
end
end
do
local tablepool = setmetatable ( { } , { __mode = ' k ' } )
-- This is a custom iterator we use to iterate over every node in a given zone
local function iter ( t , prestate )
if not t then return end
local data = t.data
local state , value = next ( data , prestate )
if value then
return state , nil , HN.icons [ value.icon ] , db.icon_scale , db.icon_alpha
end
wipe ( t )
tablepool [ t ] = true
end
-- This is a funky custom iterator we use to iterate over every zone's nodes
-- in a given continent + the continent itself
local function iterCont ( t , prestate )
if not t then return end
local zone = t.C [ t.Z ]
local data = dbdata [ zone ]
local state , value
while zone do
if data then -- Only if there is data for this zone
state , value = next ( data , prestate )
while state do -- Have we reached the end of this zone?
if value.cont or zone == t.contId then -- Show on continent?
return state , zone , HN.icons [ value.icon ] , db.icon_scale , db.icon_alpha
end
state , value = next ( data , state ) -- Get next data
end
end
-- Get next zone
t.Z = next ( t.C , t.Z )
zone = t.C [ t.Z ]
data = dbdata [ zone ]
prestate = nil
end
wipe ( t )
tablepool [ t ] = true
end
function HNHandler : GetNodes2 ( uiMapId , minimap )
local C = HandyNotes : GetContinentZoneList ( uiMapId ) -- Is this a continent?
if C then
local tbl = next ( tablepool ) or { }
tablepool [ tbl ] = nil
tbl.C = C
tbl.Z = next ( C )
tbl.contId = uiMapId
return iterCont , tbl , nil
else -- It is a zone
local tbl = next ( tablepool ) or { }
tablepool [ tbl ] = nil
tbl.data = dbdata [ uiMapId ]
return iter , tbl , nil
end
end
end
---------------------------------------------------------
-- HandyNotes core
function HN . OnCanvasClicked ( mapCanvas , button , cursorX , cursorY )
local self = HN
if button == " RightButton " and IsAltKeyDown ( ) and not IsControlKeyDown ( ) and not IsShiftKeyDown ( ) then
local coord = HandyNotes : getCoord ( cursorX , cursorY )
local x , y = HandyNotes : getXY ( coord )
-- Pass the data to the edit note frame
local HNEditFrame = self.HNEditFrame
HNEditFrame.x = x
HNEditFrame.y = y
HNEditFrame.coord = coord
HNEditFrame.mapID = mapCanvas : GetMapID ( )
HNEditFrame : Hide ( ) -- Hide first to trigger the OnShow handler
HNEditFrame : Show ( )
return true
end
return false
end
-- Function to create a note where the player is
function HN : CreateNoteHere ( arg1 )
local mapID , x , y
if arg1 ~= " " then
-- Coordinates entered
x , y = string.match ( strtrim ( arg1 ) , " ([%d.]+)[, ]+([%d.]+) " )
x , y = tonumber ( x ) , tonumber ( y )
if x and y and x > 1 and x < 100 and y > 1 and y < 100 then
-- Normalize coordinates to between 0 and 1
x , y = x / 100 , y / 100
end
if not x or not y or x <= 0 or x >= 1 or y <= 0 or y >= 1 then
self : Print ( L [ " Syntax: " ] .. " /hnnew [x, y] " )
return
end
mapID = HBD : GetPlayerZone ( )
else
-- No coordinates entered, get the coordinates of player
x , y , mapID = HBD : GetPlayerZonePosition ( )
end
if mapID and x and y then
local coord = HandyNotes : getCoord ( x , y )
x , y = HandyNotes : getXY ( coord )
-- Pass the data to the edit note frame
local HNEditFrame = self.HNEditFrame
HNEditFrame.x = x
HNEditFrame.y = y
HNEditFrame.coord = coord
HNEditFrame.mapID = mapID
HNEditFrame : Hide ( ) -- Hide first to trigger the OnShow handler
HNEditFrame : Show ( )
else
self : Print ( L [ " ERROR_CREATE_NOTE1 " ] )
end
end
---------------------------------------------------------
-- Options table
local options = {
type = " group " ,
name = L [ " HandyNotes " ] ,
desc = L [ " HandyNotes " ] ,
get = function ( info ) return db [ info.arg ] end ,
set = function ( info , v )
db [ info.arg ] = v
HN : SendMessage ( " HandyNotes_NotifyUpdate " , " HandyNotes " )
end ,
args = {
desc = {
name = L [ " These settings control the look and feel of the HandyNotes icons. " ] ,
type = " description " ,
order = 0 ,
} ,
icon_scale = {
type = " range " ,
name = L [ " Icon Scale " ] ,
desc = L [ " The scale of the icons " ] ,
min = 0.25 , max = 2 , step = 0.01 ,
arg = " icon_scale " ,
order = 10 ,
} ,
icon_alpha = {
type = " range " ,
name = L [ " Icon Alpha " ] ,
desc = L [ " The alpha transparency of the icons " ] ,
min = 0 , max = 1 , step = 0.01 ,
arg = " icon_alpha " ,
order = 20 ,
} ,
} ,
}
---------------------------------------------------------
-- Addon initialization, enabling and disabling
function HN : OnInitialize ( )
-- Set up our database
self.db = LibStub ( " AceDB-3.0 " ) : New ( " HandyNotes_HandyNotesDB " , defaults )
db = self.db . profile
dbdata = self.db . global
-- migrate data, if neccessary
local HBDMigrate = LibStub ( " HereBeDragons-Migrate " )
local migration = { }
for zone in pairs ( dbdata ) do
if type ( zone ) == " string " then
migration [ zone ] = true
end
end
for zone in pairs ( migration ) do
local data = dbdata [ zone ]
dbdata [ zone ] = nil
local stripped_zone = zone : gsub ( " _terrain%d+$ " , " " )
for coord , info in pairs ( data ) do
local newzone = HBDMigrate : GetUIMapIDFromMapFile ( stripped_zone , info.level )
if newzone then
info.level = nil
if not dbdata [ newzone ] then
dbdata [ newzone ] = { }
end
dbdata [ newzone ] [ coord ] = info
end
end
end
HNData = dbdata
-- Initialize our database with HandyNotes
HandyNotes : RegisterPluginDB ( " HandyNotes " , HNHandler , options )
--WorldMapMagnifyingGlassButton:SetText(WorldMapMagnifyingGlassButton:GetText() .. L["\nAlt+Right Click To Add a HandyNote"])
if WoWRetail then
-- Work-around for taint from canvas click handlers
self.ClickHandlerFrame = CreateFrame ( " Frame " , nil , WorldMapFrame.ScrollContainer )
self.ClickHandlerFrame : SetAllPoints ( )
self.ClickHandlerFrame . UpdateCapture = function ( f )
-- only Alt and no other keys, then show our capture frame
if IsAltKeyDown ( ) and not IsControlKeyDown ( ) and not IsShiftKeyDown ( ) then
f : Show ( )
else
f : Hide ( )
end
end
self.ClickHandlerFrame : RegisterEvent ( " MODIFIER_STATE_CHANGED " )
self.ClickHandlerFrame : SetScript ( " OnEvent " , self.ClickHandlerFrame . UpdateCapture )
-- fallback check OnUpdate. It'll only run while the capture frame is visible, and hide if the modifiers change
self.ClickHandlerFrame : SetScript ( " OnUpdate " , self.ClickHandlerFrame . UpdateCapture )
-- always pass through buttons other then right click
self.ClickHandlerFrame : SetPassThroughButtons ( " LeftButton " , " MiddleButton " , " Button4 " , " Button5 " )
self.ClickHandlerFrame : UpdateCapture ( )
self.ClickHandlerFrame : SetScript ( " OnMouseUp " , function ( f , button )
HN.OnCanvasClicked ( WorldMapFrame , button , WorldMapFrame.ScrollContainer : NormalizeUIPosition ( WorldMapFrame.ScrollContainer : GetCursorPosition ( ) ) )
end )
else
WorldMapFrame : AddCanvasClickHandler ( self.OnCanvasClicked )
end
-- Slash command
self : RegisterChatCommand ( " hnnew " , " CreateNoteHere " )
end
function HN : OnEnable ( )
end
function HN : OnDisable ( )
end