annotate Devian.lua @ 99:7d94df3804a7

- Console drag buttons for resizing - While dragging a console frame, other frames are ghosted out - Dropdown menu includes Toggle, Pin, and MinMax
author Nenue
date Thu, 27 Oct 2016 06:11:04 -0400
parents 33bc8baba858
children 790dca545f1d
rev   line source
Nenue@50 1 --- Devian - Devian.lua
Nenue@32 2 -- @file-author@
Nenue@32 3 -- @project-revision@ @project-hash@
Nenue@32 4 -- @file-revision@ @file-hash@
Nenue@66 5
Nenue@91 6 --GLOBALS: Devian, DevianLoadMessage, DEVIAN_WORKSPACE, DEVIAN_PNAME, DEVIAN_PID
Nenue@91 7
Nenue@91 8 DEVIAN_WORKSPACE = false
Nenue@91 9 DEVIAN_PNAME = 'Dvn'
Nenue@91 10 DEVIAN_PID = 0
Nenue@79 11
Nenue@66 12 local ADDON, D = ...
Nenue@79 13 local MAJOR, MINOR = 'Devian-2.0', 'r@project-revision@'
Nenue@98 14 local D_INITIALIZED
Nenue@98 15 local next = next
Nenue@72 16 local sub, GetTime, print, _G = string.sub, GetTime, print, _G
Nenue@72 17 local format, setmetatable, getprinthandler, setprinthandler = string.format, setmetatable, getprinthandler, setprinthandler
Nenue@79 18 local tinsert, tremove, rawset = tinsert, tremove, rawset
Nenue@66 19 local currentProfile
Nenue@66 20 local playerName = UnitName("player")
Nenue@66 21 local playerRealm = playerName .. '-' .. GetRealmName()
Nenue@66 22 local num_dock_tabs = 0
Nenue@93 23 local charStates ={}
Nenue@79 24
Nenue@98 25 local registeredTags = {}
Nenue@98 26
Nenue@98 27
Nenue@98 28 DevianCore = {}
Nenue@98 29
Nenue@98 30 function DevianCore:OnLoad ()
Nenue@98 31 self:RegisterEvent('ADDON_LOADED')
Nenue@98 32 self:RegisterEvent('PLAYER_LOGIN')
Nenue@98 33 self:SetShown(true)
Nenue@98 34 end
Nenue@98 35
Nenue@98 36 function DevianCore:OnEvent(event, arg)
Nenue@98 37 if event == 'ADDON_LOADED' or event == 'PLAYER_LOGIN' then
Nenue@98 38 --print(event, arg, DevianDB)
Nenue@98 39 if (arg == 'Devian') and not D_INITIALIZED then
Nenue@98 40 D_INITIALIZED = true
Nenue@98 41 self:Initialize()
Nenue@98 42 self:UnregisterAllEvents()
Nenue@98 43 end
Nenue@98 44 end
Nenue@98 45 end
Nenue@98 46
Nenue@79 47 DevianLoadMessage = setmetatable({}, {
Nenue@79 48 __call = function(t, msg)
Nenue@79 49 rawset(t, #t+1, msg)
Nenue@79 50 end,
Nenue@79 51 __index = function(t)
Nenue@79 52 return #t
Nenue@79 53 end
Nenue@79 54 })
Nenue@79 55
Nenue@66 56 --@debug@
Nenue@66 57 D.debugmode = true
Nenue@66 58 --@end-debug@
Nenue@66 59 D.print = function(...)
Nenue@66 60 if currentProfile and not currentProfile.workspace then
Nenue@98 61 return nop
Nenue@66 62 end
Nenue@66 63
Nenue@66 64 if D.debugmode then
Nenue@66 65 return print('Dvn', ...)
Nenue@66 66 else
Nenue@93 67 return nop
Nenue@66 68 end
Nenue@0 69 end
Nenue@66 70 local print = D.print
Nenue@66 71
Nenue@66 72 D.L = setmetatable({}, {
Nenue@66 73 __index= function(t,k)
Nenue@98 74 return tostring(k)
Nenue@66 75 end,
Nenue@66 76 __call = function(t,k,...)
Nenue@98 77 return format((t[k]) , ...)
Nenue@66 78 end
Nenue@66 79 })
Nenue@66 80 D.oldprint = getprinthandler()
Nenue@66 81 if not _G.oldprint then _G.oldprint = D.oldprint end
Nenue@66 82
Nenue@47 83 local pairs, tostring, tonumber, ipairs, type = pairs, tostring, tonumber, ipairs, type
Nenue@66 84 local max, rand, format, print = max, math.random, string.format, print
Nenue@47 85 local insert, wipe, concat = table.insert, table.wipe, table.concat
Nenue@47 86 local select, unpack = select, unpack
Nenue@47 87 local GetNumAddOns, GetAddOnInfo, GetAddOnEnableState, EnableAddOn = GetNumAddOns, GetAddOnInfo, GetAddOnEnableState, EnableAddOn
Nenue@47 88 local UnitName, DisableAddOn = UnitName, DisableAddOn
Nenue@66 89
Nenue@98 90 local db, L
Nenue@13 91 local defaults = {
Nenue@66 92 global = {{}, {}},
Nenue@66 93 default_channel = {
Nenue@55 94 signature = 'Main',
Nenue@98 95 x = 100, y = -200,
Nenue@55 96 height = 500, width = 600,
Nenue@66 97 enabled = true},
Nenue@66 98 current_profile = 1,
Nenue@66 99 main_profile = 1,
Nenue@66 100 last_profile = 1,
Nenue@66 101 profilesName = {},
Nenue@66 102 profiles = {
Nenue@45 103 },
Nenue@33 104 font = [[Interface\Addons\Devian\font\SourceCodePro-Regular.ttf]], -- font info
Nenue@13 105 fontsize = 13,
Nenue@13 106 fontoutline = 'NONE',
Nenue@45 107
Nenue@45 108 headergrad = {'VERTICAL', 0, 0, 0, 1,
Nenue@45 109 1, 0.1, 0.1, 1}, -- header info
Nenue@33 110 headerdrop = {1,1,1,1},
Nenue@45 111 headerblend = 'BLEND',
Nenue@33 112 headeralpha = 1,
Nenue@45 113 headerfontcolor = {1,1,1,1},
Nenue@45 114
Nenue@45 115 backdrop = {1,1,1,1}, -- background frame info
Nenue@56 116 backgrad = {'VERTICAL', 0, 0, 0, .75, 0,0,0, .65},
Nenue@55 117 backblend = 'BLEND',
Nenue@45 118 backalpha = 1,
Nenue@45 119 backborder = {.5,.5,.5,1},
Nenue@45 120 backheader = {.25,.25,.25,1},
Nenue@45 121
Nenue@45 122 frontdrop = {1,1,1,1}, -- foreground frame info
Nenue@56 123 frontgrad = {'VERTICAL', 0, 0, 0, 1, 0,0,0, 0.95},
Nenue@55 124 frontblend = 'BLEND',
Nenue@28 125 frontalpha = 1,
Nenue@45 126 frontborder = {.07,.47,1,1},
Nenue@45 127 frontheader = {1,1,1,1},
Nenue@33 128 tagcolor = {}, -- tag color repository
Nenue@36 129 workspace = 2, -- current profile
Nenue@47 130 last_workspace = 2, -- default workspace to alternate with when just "/dvn" is issued
Nenue@47 131
Nenue@47 132 dock_onshow_fade_time = 2.5,
Nenue@47 133 dock_onshow_fade_from = 1,
Nenue@47 134 dock_onshow_fade_to = 0.2,
Nenue@47 135
Nenue@47 136 dock_alpha_on = 1,
Nenue@47 137 dock_alpha_off = 0.2,
Nenue@47 138 dock_fade_in = 0.15,
Nenue@47 139 dock_fade_out = 0.45,
Nenue@47 140 dock_button_alpha_on = 1,
Nenue@47 141 dock_button_alpha_off = 0.2,
Nenue@50 142 dock_button_fade_in = 0.075,
Nenue@55 143 dock_button_fade_out = 0.075,
Nenue@47 144
Nenue@55 145 movement_fade = true,
Nenue@55 146 movement_fade_time = 0.15,
Nenue@60 147 movement_fade_from = 1,
Nenue@60 148 movement_fade_to = 0,
Nenue@60 149 movement_translation_x = 25,
Nenue@60 150 movement_translation_y = 25,
Nenue@13 151 }
Nenue@9 152
Nenue@66 153 D.console = {}
Nenue@66 154 D.max_channel = 0
Nenue@0 155
Nenue@67 156 D.InWorkspace = function ()
Nenue@67 157 return db.profiles[db.current_profile].workspace
Nenue@67 158 end
Nenue@67 159
Nenue@86 160 local profileTemplate = {
Nenue@86 161 name = function(id, name) return name end,
Nenue@86 162 workspace = function(id, name) return (id ~= 1) end,
Nenue@86 163 current_channel = 1,
Nenue@86 164 default_channel = 1,
Nenue@86 165 num_channels = 1,
Nenue@86 166 max_channel = 1, -- the highest created channel id
Nenue@86 167 enabled = true, -- allow enabled consoles to appear
Nenue@86 168 channels = {
Nenue@86 169 {
Nenue@86 170 index = 1,
Nenue@86 171 signature = 'Main',
Nenue@98 172 x = 100, y = -200,
Nenue@86 173 height = 500, width = 600,
Nenue@86 174 enabled = true
Nenue@86 175 }
Nenue@86 176 },
Nenue@86 177 loadouts = {},
Nenue@86 178 global = {},
Nenue@86 179 tags = {},
Nenue@86 180 char = {
Nenue@86 181 [playerRealm] = {}
Nenue@86 182 },
Nenue@86 183 unlisted = {}
Nenue@86 184 }
Nenue@86 185
Nenue@86 186 --- Applies complex template tables
Nenue@86 187 -- If he value is a function, then it will invoke f(...) and use whatever gets returned
Nenue@86 188 function D.DeepCopy(src, dest, ...)
Nenue@86 189
Nenue@86 190 for k,v in pairs(src) do
Nenue@86 191 if not dest[k] then
Nenue@93 192 --oldprint('Rebuilding conf value', k)
Nenue@86 193 if type(v) == 'table' then
Nenue@86 194 dest[k] = {}
Nenue@86 195 D.DeepCopy(v, dest[k], ...)
Nenue@86 196
Nenue@86 197 else
Nenue@86 198 if type(v) == 'function' then
Nenue@86 199 v = v(...)
Nenue@86 200 end
Nenue@86 201 dest[k] = v
Nenue@86 202 end
Nenue@86 203 end
Nenue@86 204 end
Nenue@86 205 end
Nenue@86 206
Nenue@98 207 D.FixProfile = function(forced)
Nenue@98 208
Nenue@98 209 local numChannels = 0
Nenue@98 210 local minChannel = 400
Nenue@98 211 local sortedChannels = {}
Nenue@98 212 local sortedTags = {}
Nenue@98 213 local maxChannel = 0
Nenue@98 214 for k,v in pairs(currentProfile.channels) do
Nenue@98 215 numChannels = numChannels + 1
Nenue@98 216 maxChannel = max(tonumber(k), maxChannel)
Nenue@98 217 minChannel = min(tonumber(k), minChannel)
Nenue@98 218 tinsert(sortedChannels, v)
Nenue@98 219 end
Nenue@98 220 if (maxChannel > numChannels) or forced then
Nenue@98 221 oldprint('fixing channels data')
Nenue@98 222 table.sort(sortedChannels, function(a,b)
Nenue@98 223 return (b.index > a.index)
Nenue@98 224 end)
Nenue@98 225 for i, info in ipairs(sortedChannels) do
Nenue@99 226
Nenue@99 227 info.tags = info.tags or {}
Nenue@98 228 for tag, tagSet in pairs(currentProfile.tags) do
Nenue@98 229 for _, index in pairs(tagSet) do
Nenue@98 230 if index == info.index then
Nenue@98 231 sortedTags[tag] = sortedTags[tag] or {}
Nenue@98 232 sortedTags[tag][i] = i
Nenue@99 233 tinsert(info.tags, tag)
Nenue@98 234 end
Nenue@98 235 end
Nenue@98 236 end
Nenue@99 237 print('Set tags:', table.concat(info.tags, ', '))
Nenue@98 238
Nenue@98 239 info.index = i
Nenue@98 240 end
Nenue@98 241 currentProfile.channels = sortedChannels
Nenue@98 242 currentProfile.tags = sortedTags
Nenue@98 243 else
Nenue@98 244 minChannel = 2
Nenue@98 245 end
Nenue@98 246 currentProfile.lastUpdateFix = MINOR
Nenue@98 247 end
Nenue@98 248
Nenue@66 249 D.Profile = function (id, name)
Nenue@66 250
Nenue@66 251 if name and not id and db.profilesName[name] then
Nenue@66 252 id = db.profilesName[name]
Nenue@66 253 print('ID located by name, |cFF00FF00'..name..'|r is |cFFFFFF00'.. id..'|r')
Nenue@66 254 end
Nenue@66 255
Nenue@66 256 if not id or not db.profiles[id] then
Nenue@66 257 if not id then
Nenue@66 258 id = #db.profiles+1
Nenue@66 259 print('Generated profile ID: |cFFFFFF00'.. id .. '|r')
Nenue@66 260 end
Nenue@66 261
Nenue@66 262 if not name or db.profilesName[name] then
Nenue@66 263 local newName = name or (id == 1 and 'Main' or 'Profile')
Nenue@66 264 local prefix = newName
Nenue@66 265 local i = 2
Nenue@66 266 while db.profilesName[newName] do
Nenue@66 267 i = i + 1
Nenue@66 268 newName = prefix .. i
Nenue@66 269 end
Nenue@66 270 name = newName
Nenue@66 271 print('Generated profile name: |cFF00FF00'..newName..'|r')
Nenue@66 272 end
Nenue@86 273
Nenue@86 274
Nenue@86 275
Nenue@66 276 print('Creating profile')
Nenue@66 277 db.profilesName[name] = id
Nenue@86 278 db.profiles[id] = {}
Nenue@66 279 end
Nenue@66 280
Nenue@86 281
Nenue@86 282
Nenue@66 283 D.currentProfile = db.profiles[id]
Nenue@66 284 currentProfile = D.currentProfile
Nenue@86 285
Nenue@86 286 D.DeepCopy(profileTemplate, currentProfile, id, name)
Nenue@86 287
Nenue@86 288
Nenue@66 289 currentProfile.char[playerRealm] = currentProfile.char[playerRealm] or {}
Nenue@66 290 if currentProfile.workspace then
Nenue@79 291 DEVIAN_WORKSPACE = true
Nenue@91 292 DEVIAN_PNAME = currentProfile.name
Nenue@89 293 DEVIAN_PID = id
Nenue@66 294 setprinthandler(D.Message)
Nenue@66 295 else
Nenue@79 296 DEVIAN_WORKSPACE = false
Nenue@93 297 DEVIAN_PNAME = nil
Nenue@93 298 print = nop
Nenue@66 299 end
Nenue@89 300 DEVIAN_PID =id
Nenue@66 301
Nenue@98 302
Nenue@98 303 -- Attempt to fix bad data
Nenue@98 304 --@debug@
Nenue@98 305 MINOR = 70100
Nenue@98 306 --@end-debug@
Nenue@98 307 if (currentProfile.lastUpdateFix or 0) < MINOR then
Nenue@98 308 D.FixProfile(true)
Nenue@98 309 end
Nenue@98 310
Nenue@98 311
Nenue@66 312 D.unlisted = currentProfile.unlisted
Nenue@66 313 D.channels = currentProfile.channels
Nenue@66 314 D.tags = currentProfile.tags
Nenue@66 315 D.channelinfo = currentProfile.channels
Nenue@66 316 D.char = currentProfile.char[playerRealm]
Nenue@66 317 D.global = currentProfile.global
Nenue@66 318 D.num_channels = currentProfile.num_channels
Nenue@66 319 D.enabled = currentProfile.enabled
Nenue@66 320 D.sig = {}
Nenue@66 321 D.sigID = {}
Nenue@66 322 D.IDsig = {}
Nenue@72 323 D.dock = _G.DevianDock
Nenue@66 324 D.dock.buttons = D.dock.buttons or {}
Nenue@66 325
Nenue@98 326
Nenue@98 327
Nenue@66 328 return id, name
Nenue@66 329 end
Nenue@66 330
Nenue@66 331 local targetGlobal, targetChar
Nenue@86 332 D.Command = function (cmd)
Nenue@66 333 local list_id, scan_func, reload
Nenue@86 334
Nenue@14 335 local args = {}
Nenue@86 336 if cmd then
Nenue@86 337 local i, j = 0, 0
Nenue@86 338 repeat
Nenue@86 339 i, j = cmd:find("%S+", j+1)
Nenue@86 340 if i and j then
Nenue@86 341 tinsert(args, cmd:sub(i, j))
Nenue@86 342 end
Nenue@86 343
Nenue@86 344 until not(i or j)
Nenue@14 345 end
Nenue@14 346 local mode, tag, dest = unpack(args)
Nenue@0 347
Nenue@86 348
Nenue@14 349 -- no args, toggle ui
Nenue@86 350 if mode == 'rc' then
Nenue@93 351 return D:ResetChannels(tag)
Nenue@86 352 elseif mode == 'stack' then
Nenue@14 353 return D:StackFrames()
Nenue@14 354 elseif mode == 'grid' then
Nenue@14 355 return D:DistributeFrames()
Nenue@14 356 elseif mode == 'tag' then -- tagging
Nenue@93 357 return D:Tag(tag, dest)
Nenue@73 358 elseif mode == 'new' then
Nenue@93 359 return D:New(tag)
Nenue@93 360 elseif mode == 'dock' then
Nenue@93 361 D.db.dockPoint = tag
Nenue@93 362 return D:UpdateDock()
Nenue@73 363 elseif mode == 'remove' then
Nenue@93 364 return D:Remove(tag)
Nenue@89 365 elseif mode ~= nil then
Nenue@89 366 -- profile selector or save command
Nenue@66 367 if mode == 'save' then
Nenue@66 368 list_id = tonumber(tag)
Nenue@89 369 else
Nenue@89 370 list_id = tonumber(mode)
Nenue@89 371 end
Nenue@89 372
Nenue@89 373 if not list_id then
Nenue@89 374 if db.profilesName[tostring(list_id)] then
Nenue@89 375 list_id = db.profilesName[tostring(list_id)]
Nenue@89 376 else
Nenue@89 377
Nenue@89 378 D:Print(L('Unable to resolve profile ID/name', list_id, dest))
Nenue@89 379 return
Nenue@66 380 end
Nenue@89 381 end
Nenue@89 382
Nenue@89 383
Nenue@89 384 if mode == 'save' then
Nenue@66 385 D.Profile(list_id, dest)
Nenue@66 386 scan_func = D.Save
Nenue@89 387
Nenue@89 388
Nenue@89 389 local name = currentProfile.name
Nenue@89 390 if dest then
Nenue@89 391 dest = dest:gsub("$%s+", ''):gsub("%s+^", '')
Nenue@89 392 if dest then
Nenue@89 393 if name then
Nenue@89 394 db.profilesName[name] = nil
Nenue@89 395 end
Nenue@89 396 db.profiles[list_id].name = dest
Nenue@89 397 db.profilesName[dest] = list_id
Nenue@92 398
Nenue@92 399 name = dest
Nenue@89 400 end
Nenue@89 401 end
Nenue@89 402
Nenue@89 403
Nenue@89 404 D:Print("Profile |cFFFFFF00".. list_id .."|r:|cFF00FFFF".. name .."|r saved.")
Nenue@66 405 else
Nenue@32 406
Nenue@89 407 if db.profiles[list_id] then
Nenue@89 408 D.LoadMessage ("Switched profiles.")
Nenue@89 409 if list_id ~= db.main_profile then
Nenue@89 410 db.last_profile = list_id
Nenue@89 411 end
Nenue@89 412 db.current_profile = list_id
Nenue@89 413 scan_func = D.Load
Nenue@89 414 else
Nenue@89 415 return D:PrintHelp()
Nenue@89 416 end
Nenue@36 417
Nenue@0 418 end
Nenue@32 419 elseif mode == nil then
Nenue@66 420 list_id = (db.current_profile ~= db.main_profile) and db.main_profile or db.last_profile
Nenue@77 421 D.LoadMessage ("Switched between main and recent profile ("..db.current_profile..' and '..list_id..')')
Nenue@66 422 db.current_profile = list_id
Nenue@66 423 scan_func = D.Load
Nenue@32 424 else
Nenue@89 425
Nenue@89 426
Nenue@32 427 return D:PrintHelp()
Nenue@0 428 end
Nenue@77 429
Nenue@77 430 if not db.profiles[list_id] then
Nenue@77 431 db.profiles[list_id] = {global = {}, char = {} }
Nenue@77 432 D.LoadMessage ("Starting profile #|cFF00FFFF".. list_id..'|r')
Nenue@77 433 end
Nenue@67 434 if not db.profiles[list_id].char[playerRealm] then
Nenue@67 435 db.profiles[list_id].char[playerRealm] = {}
Nenue@67 436 end
Nenue@67 437
Nenue@66 438 targetGlobal = db.profiles[list_id].global
Nenue@66 439 targetChar = db.profiles[list_id].char[playerRealm]
Nenue@32 440
Nenue@67 441
Nenue@66 442 if scan_func then
Nenue@93 443 wipe(charStates)
Nenue@66 444 for id, name, enableState, globalState in D.Addons() do
Nenue@66 445 scan_func(id, name, enableState, globalState)
Nenue@0 446 end
Nenue@0 447 end
Nenue@0 448
Nenue@66 449 if scan_func == D.Load then
Nenue@72 450 _G.ReloadUI()
Nenue@93 451 if AddonList_Update then
Nenue@93 452 AddonList_Update()
Nenue@93 453 end
Nenue@93 454 elseif (scan_func == D.Save) then
Nenue@93 455 print('reckoning')
Nenue@93 456 local updated = {}
Nenue@93 457 for addon, newState in pairs(charStates) do
Nenue@93 458 for character, addons in pairs(db.profiles[list_id].char) do
Nenue@93 459 if addons[addon] then
Nenue@93 460 print(addon, addons[addon], '::', newState)
Nenue@93 461 if (addons[addon] ~= newState) then
Nenue@93 462 addons[addon] = newState
Nenue@93 463 updated[character] = (updated[character] or 0) + 1
Nenue@93 464 end
Nenue@93 465
Nenue@93 466 end
Nenue@93 467
Nenue@93 468 end
Nenue@93 469 end
Nenue@93 470 for character, numAddons in pairs(updated) do
Nenue@93 471 print(character, numAddons, 'settings')
Nenue@93 472 end
Nenue@93 473
Nenue@66 474 end
Nenue@93 475
Nenue@93 476
Nenue@66 477 D.Profile(db.current_profile)
Nenue@66 478 end
Nenue@66 479
Nenue@66 480 D.Addons = function()
Nenue@66 481 local playername = UnitName("player")
Nenue@66 482 return function(n, i)
Nenue@66 483 if i >= n then
Nenue@66 484 return nil
Nenue@66 485 end
Nenue@66 486
Nenue@66 487 i = i + 1
Nenue@66 488 local name = GetAddOnInfo(i)
Nenue@66 489 local enableState, globalState = GetAddOnEnableState(playername, i), GetAddOnEnableState(nil, i)
Nenue@66 490 return i, name, enableState, globalState
Nenue@66 491 end, GetNumAddOns(), 0
Nenue@66 492 end
Nenue@66 493
Nenue@66 494 D.Load = function(id, name, charState, globalState)
Nenue@93 495 print('load', tostring(name), tostring(charState), tostring(globalState))
Nenue@93 496 if targetGlobal[name] == 2 then
Nenue@93 497 EnableAddOn(id, true)
Nenue@93 498 elseif targetChar[name] == 2 then
Nenue@93 499 EnableAddOn(id, playerName)
Nenue@93 500 elseif targetGlobal[name] == 0 then
Nenue@93 501 DisableAddOn(id, true)
Nenue@93 502 elseif targetChar[name] == 0 then
Nenue@93 503 DisableAddOn(id, playerName)
Nenue@93 504 end
Nenue@93 505
Nenue@93 506 if not (targetChar[name] or targetGlobal[name]) then
Nenue@66 507 tinsert(D.unlisted, name)
Nenue@66 508 end
Nenue@93 509
Nenue@66 510 end
Nenue@93 511
Nenue@93 512
Nenue@66 513 D.Save = function(id, name, charState, globalState)
Nenue@93 514 if (charState ~= 0) or (globalState ~= 0) then
Nenue@93 515 print('save', id, name, playerRealm .. ': '.. charState, 'All: '.. globalState)
Nenue@93 516 end
Nenue@93 517
Nenue@66 518 targetGlobal[name] = globalState
Nenue@66 519 targetChar[name] = charState
Nenue@93 520
Nenue@93 521 -- if enabling/disabling globally
Nenue@93 522 if globalState ~= 1 then
Nenue@93 523 charStates[name] = globalState
Nenue@93 524 end
Nenue@66 525 end
Nenue@57 526
Nenue@98 527 D.UpdateTags = function()
Nenue@98 528 wipe(registeredTags)
Nenue@99 529 for index, channel in ipairs(D.channels) do
Nenue@99 530 for _, tag in ipairs(channel.tags) do
Nenue@99 531 registeredTags[tag] = registeredTags[tag] or {}
Nenue@99 532 tinsert(registeredTags[tag], D.console[index])
Nenue@98 533 end
Nenue@98 534 end
Nenue@98 535 end
Nenue@98 536
Nenue@99 537 D.Tag = function(self, tag, id)
Nenue@66 538 local sig
Nenue@99 539 if tag and id then
Nenue@66 540 --@debug@
Nenue@66 541 --print(tag, dest)
Nenue@66 542 --@end-debug@
Nenue@66 543
Nenue@66 544 -- convert to ID
Nenue@99 545 local channel, sig
Nenue@99 546 if tonumber(id) == nil then
Nenue@99 547 sig = id
Nenue@99 548 if D.sigID[id] then
Nenue@99 549 id = D.sigID[id]
Nenue@99 550 channel = D.channels[id]
Nenue@66 551 end
Nenue@57 552 else
Nenue@99 553 id = tonumber(id)
Nenue@99 554 channel = D.channels[id]
Nenue@32 555 end
Nenue@66 556
Nenue@99 557 -- if channel is still nil, create one
Nenue@99 558 if not channel then
Nenue@99 559 id = #D.channels + 1
Nenue@99 560 D:Print(L('New channel created', (sig and (id..':'..sig)) or id))
Nenue@99 561 channel = D:GetOrCreateChannel(id, sig)
Nenue@66 562 else
Nenue@99 563 sig = channel.signature
Nenue@66 564 end
Nenue@66 565 --@debug@
Nenue@66 566 --print('3 tag,dest,channel.sig=',tag, dest, channel.signature)--@end-debug@
Nenue@66 567
Nenue@66 568 if not currentProfile.tags[tag] then -- no tag table?
Nenue@99 569 currentProfile.tags[tag] = {}
Nenue@66 570 end
Nenue@66 571
Nenue@99 572 local existingTag = tContains(channel.tags, tag)
Nenue@99 573 if existingTag then -- is tag set?
Nenue@99 574
Nenue@99 575 for i, tag in ipairs(channel.tags) do
Nenue@99 576 if tag == tag then
Nenue@99 577 tremove(channel.tags, i)
Nenue@99 578 D:Print(L('Tag removed from channel', tag, channel.index, channel.signature))
Nenue@99 579 break
Nenue@99 580 end
Nenue@99 581 end
Nenue@66 582 else
Nenue@99 583 tinsert(channel.tags, tag)
Nenue@98 584 D:Print(L('Tag added to channel', tag, channel.index, channel.signature))
Nenue@66 585 end
Nenue@98 586 D.UpdateTags()
Nenue@98 587 DevianDock:Update()
Nenue@66 588 else
Nenue@66 589 D:Print(L['Command tag help'])
Nenue@0 590 end
Nenue@0 591 end
Nenue@0 592
Nenue@86 593 D.ResetChannels = function(self, profile)
Nenue@86 594 currentProfile.current_channel = 1
Nenue@86 595 currentProfile.primary_channel = 1
Nenue@86 596 currentProfile.channels = {}
Nenue@86 597 D.DeepCopy(profileTemplate.channels, currentProfile.channels)
Nenue@86 598 currentProfile.tags = {}
Nenue@86 599 D.LoadMessage('Profile reset.')
Nenue@86 600 ReloadUI()
Nenue@86 601 end
Nenue@86 602
Nenue@73 603 D.New = function(self, tag)
Nenue@73 604 if tag and not self.sigID[tag] then
Nenue@73 605 local id = D.max_channel + 1
Nenue@73 606 D.SetChannel(tag, id)
Nenue@73 607 end
Nenue@73 608 end
Nenue@0 609
Nenue@73 610 D.Remove = function(self, dest)
Nenue@73 611 dest = D.sigID[dest] or tonumber(dest)
Nenue@73 612 if D.console[dest] and D.channels[dest] then
Nenue@73 613 for tag, tagDest in pairs(D.tags) do
Nenue@73 614 for i = #tagDest, 0 do
Nenue@73 615 -- work downward so we aren't skipping entries
Nenue@73 616 if tagDest[i] == dest then
Nenue@73 617 tremove(tagDest, i)
Nenue@73 618 end
Nenue@73 619 end
Nenue@73 620 end
Nenue@73 621 D.console[dest]:Hide()
Nenue@73 622 D.channels[dest] = nil
Nenue@73 623 tremove(D.console, dest)
Nenue@93 624 D.dock.buttons[dest]:SetShown(false)
Nenue@73 625 D:UpdateDock()
Nenue@73 626 D:Print('Removed channel #'..dest)
Nenue@73 627 end
Nenue@73 628 end
Nenue@13 629
Nenue@77 630 --- Queue up a message to appear after UI reload
Nenue@77 631 function D.LoadMessage(msg)
Nenue@77 632
Nenue@77 633 tinsert(_G.DevianLoadMessage, msg)
Nenue@77 634 end
Nenue@77 635
Nenue@18 636 --- Creates a Devian-style output.
Nenue@18 637 -- The first argument describes the channel to output on, and the remaining arguments are concatenated in a manner similar to default print()
Nenue@18 638 -- This becomes the print handler when development mode is active. The original print() function is assigned to oldprint().
Nenue@18 639 -- @param Tag, signature, or numeric index of the channel to output on. Defaults to primary channel.
Nenue@18 640 -- @param ... Output contents.
Nenue@98 641 local default_sendq = {}
Nenue@66 642 function D.Message(prefix, ...)
Nenue@67 643 if not currentProfile.workspace then
Nenue@23 644 return D.oldprint(prefix, ...)
Nenue@23 645 end
Nenue@98 646 local print = D.oldprint
Nenue@57 647 prefix = tostring(prefix)
Nenue@57 648 if prefix == nil then
Nenue@55 649 prefix = 'nil*'
Nenue@1 650 end
Nenue@9 651
Nenue@98 652 local sendq = default_sendq
Nenue@30 653 local tag, id, tagged
Nenue@13 654 local byName = true
Nenue@98 655
Nenue@98 656 if registeredTags[prefix] then
Nenue@98 657 sendq = registeredTags[prefix]
Nenue@98 658 else
Nenue@98 659 if D.sig[prefix] then
Nenue@98 660 sendq[D.sig[prefix].index] = D.sig[prefix]
Nenue@98 661 elseif not tagged then
Nenue@98 662 sendq[1] = D.console[D.primary_channel]
Nenue@18 663 end
Nenue@18 664 end
Nenue@18 665
Nenue@98 666
Nenue@18 667
Nenue@9 668 -- color me timbers
Nenue@9 669 local pcolor
Nenue@18 670 if (not db.tagcolor[prefix]) and byName then
Nenue@86 671 -- numbers, use white
Nenue@86 672 if prefix:match('^%d+%.%d+') then
Nenue@73 673 pcolor = 'FFFFFF'
Nenue@73 674 else
Nenue@73 675 local c = {
Nenue@73 676 rand(64,255), rand(64,255), rand(64,255)
Nenue@73 677 }
Nenue@73 678 if c[1] > 223 and c[2] > 223 and c[3] > 223 then
Nenue@73 679 c[rand(1,3)] = rand(64,223)
Nenue@73 680 end
Nenue@73 681 db.tagcolor[prefix] = format('%02X%02X%02X', unpack(c))
Nenue@73 682 pcolor = db.tagcolor[prefix]
Nenue@0 683 end
Nenue@73 684 else
Nenue@73 685 pcolor = db.tagcolor[prefix]
Nenue@0 686 end
Nenue@0 687
Nenue@55 688 local buffer = {}
Nenue@0 689 for i = 1, select('#',...) do
Nenue@0 690 local var = select(i, ...)
Nenue@0 691
Nenue@0 692 if type(var) == 'table' then
Nenue@20 693 if type(var.GetName) == 'function' then
Nenue@72 694 var = '[table:'..tostring(var:GetName())..']'
Nenue@20 695 else
Nenue@32 696 var = '<'..tostring(var)..'>'
Nenue@20 697 end
Nenue@20 698
Nenue@0 699 elseif type(var) == 'boolean' then
Nenue@75 700 var = var and 'true' or 'false'
Nenue@0 701 elseif type(var) == 'function' then
Nenue@72 702 var = '['..tostring(var)..']'
Nenue@0 703 elseif type(var) == 'nil' then
Nenue@75 704 var = 'nil'
Nenue@60 705 else
Nenue@60 706 var = tostring(var)
Nenue@0 707 end
Nenue@0 708
Nenue@47 709 insert(buffer, var)
Nenue@0 710 end
Nenue@47 711 local message = concat(buffer, ' ')
Nenue@18 712 for id, channel in pairs(sendq) do
Nenue@55 713 if channel.width < 250 then
Nenue@72 714 prefix = sub(prefix, 0,2)
Nenue@55 715 end
Nenue@98 716 channel.out:AddMessage('|cFF'.. pcolor..prefix ..'|r ' .. message, 0.8, 0.8, 0.8)
Nenue@98 717 if channel.Dock and not channel.Dock.newMessage then
Nenue@98 718 channel.Dock.newMessage = true
Nenue@98 719 channel.Dock.caption.pulse:Play()
Nenue@98 720 channel.Dock:Update()
Nenue@58 721 end
Nenue@18 722 end
Nenue@47 723 wipe(buffer)
Nenue@0 724 end
Nenue@0 725
Nenue@14 726
Nenue@32 727
Nenue@14 728
Nenue@32 729 function D:PrintHelp()
Nenue@32 730 D:Print("|cFFFFFF00/dvn|r",
Nenue@32 731 "\n |cFFFFFF00<number>|r - Loads a saved addon list. List 1 is treated as a gameplay profile and consoles will be disabled by default.")
Nenue@47 732 D:Print("|cFFFFFF00/dvc|r [<key>, ...]", "- Hides and show consoles. A list of channel keys can be passed to specify which ones get toggled.")
Nenue@32 733
Nenue@32 734 D:Print("|cFFFFFF00/resetdvn|r", "- Resets all but profile data SavedVariables.")
Nenue@32 735 D:Print("|cFFFFFF00/cleandvn|r", "- Fully resets SavedVariables, profiles and all.")
Nenue@32 736 end
Nenue@32 737
Nenue@66 738 local blocked = {profiles = true, debugmode = true}
Nenue@66 739 D.SetDefaults = function()
Nenue@72 740 local DevianDB = _G.DevianDB
Nenue@66 741 for k,v in pairs(DevianDB) do
Nenue@66 742 if not blocked[k] then
Nenue@66 743 DevianDB[k] = nil
Nenue@60 744 end
Nenue@60 745 end
Nenue@66 746 for k,v in pairs(defaults) do
Nenue@66 747 if not blocked[k] then
Nenue@66 748 DevianDB[k] = v
Nenue@60 749 end
Nenue@60 750 end
Nenue@73 751
Nenue@77 752 D.LoadMessage "Non-user SavedVars have been reset."
Nenue@72 753 _G.ReloadUI()
Nenue@60 754 end
Nenue@66 755 D.SetDefaultsAll = function ()
Nenue@72 756 _G.DevianDB = nil
Nenue@77 757 D.LoadMessage "All SavedVars wiped."
Nenue@72 758 _G.ReloadUI()
Nenue@66 759 end
Nenue@73 760
Nenue@73 761 D.UnsetColors = function()
Nenue@73 762 db.tagcolor = {}
Nenue@73 763 D:Print('Tag color cache cleared.')
Nenue@73 764 end
Nenue@45 765
Nenue@33 766
Nenue@89 767 function D.ConsoleCommand (cmd)
Nenue@89 768 D:Console_Toggle(cmd)
Nenue@89 769 end
Nenue@87 770
Nenue@66 771
Nenue@98 772 function DevianCore:Initialize()
Nenue@81 773 L = D.L
Nenue@13 774
Nenue@33 775 -- pull defaults
Nenue@98 776 if not DevianDB then
Nenue@98 777 DevianDB = defaults
Nenue@9 778 end
Nenue@98 779 D.db = _G.DevianDB
Nenue@9 780 db = _G.DevianDB
Nenue@0 781
Nenue@66 782 ---
Nenue@77 783 if #_G.DevianLoadMessage >= 1 then
Nenue@77 784 for i, msg in ipairs(_G.DevianLoadMessage) do
Nenue@77 785 D:Print(msg)
Nenue@77 786 end
Nenue@77 787
Nenue@77 788 table.wipe(_G.DevianLoadMessage)
Nenue@0 789 end
Nenue@0 790
Nenue@66 791
Nenue@89 792 -- commands
Nenue@89 793 local cmdlist = {
Nenue@89 794 ['dvn'] = "Command",
Nenue@89 795 ['devian'] = "Command",
Nenue@89 796 ['dvc'] = "ConsoleCommand",
Nenue@89 797 ['dvncolors'] = "UnsetColors",
Nenue@89 798 ['cleandvn'] = "SetDefaultsAll",
Nenue@89 799 ['resetdvn'] = "SetDefaults",
Nenue@89 800 }
Nenue@89 801 for cmd, func in pairs(cmdlist) do
Nenue@89 802 local CMD = cmd:upper()
Nenue@89 803 _G['SLASH_' .. CMD .. '1'] = "/"..cmd
Nenue@89 804
Nenue@89 805 if type(func == 'string') then
Nenue@93 806 --print('SLASH_' .. CMD .. '1','/'.. cmd, func)
Nenue@89 807 SlashCmdList[CMD] = D[func]
Nenue@89 808 else
Nenue@93 809 --print('SLASH_' .. CMD .. '1','/'.. cmd, func)
Nenue@89 810 SlashCmdList[CMD] = func
Nenue@89 811 end
Nenue@89 812 end
Nenue@89 813
Nenue@66 814 --- initialize the current profile
Nenue@66 815 local id, name = D.Profile(db.current_profile or 1)
Nenue@66 816 D:Print('Using profile |cFFFFFF00'.. id ..'|r: |cFF00FF00'..currentProfile.name..'|r')
Nenue@66 817 if currentProfile.workspace then
Nenue@66 818 D:Print('Workspace: '.. (#currentProfile.channels) .. ' channels, ' .. #currentProfile.tags .. ' tags.')
Nenue@86 819 if D.channels[currentProfile.default_channel] then
Nenue@86 820 D:Print('Default channel: |cFFFFFF00'..currentProfile.default_channel..'|r: |cFF00FFFF'.. D.channels[currentProfile.default_channel].signature..'|r')
Nenue@86 821 end
Nenue@0 822 end
Nenue@13 823
Nenue@86 824
Nenue@98 825
Nenue@98 826 for index, cinfo in ipairs(D.channels) do
Nenue@98 827 --oldprint(index, cinfo.signature)
Nenue@66 828 if not D.primary_channel then
Nenue@98 829 D.primary_channel = index
Nenue@14 830 end
Nenue@98 831 D:GetOrCreateChannel(index)
Nenue@98 832 D.num_channels = #D.channels
Nenue@32 833 end
Nenue@86 834 D.primary_channel = D.primary_channel or 1
Nenue@66 835 D.max_channel = max(D.max_channel, currentProfile.max_channel)
Nenue@66 836 if currentProfile.max_channel < D.max_channel then
Nenue@66 837 for i = currentProfile.max_channel, D.max_channel do
Nenue@66 838 D.console[i]:Hide()
Nenue@47 839 end
Nenue@36 840 end
Nenue@36 841
Nenue@98 842 D.UpdateTags()
Nenue@98 843
Nenue@66 844 if currentProfile.workspace then
Nenue@66 845 if D.console[currentProfile.current_channel] then
Nenue@69 846 --print('bringing', D.console[currentProfile.current_channel].signature, 'to the front')
Nenue@66 847 D.console[currentProfile.current_channel]:ToFront()
Nenue@66 848 -- bring the current channel to the front
Nenue@47 849 end
Nenue@98 850 DevianDock:Update()
Nenue@36 851 end
Nenue@98 852
Nenue@98 853 print('devian')
Nenue@66 854 end
Nenue@89 855
Nenue@98 856 function D:Print (...)
Nenue@98 857 local msg = '|cFF00FF44Devian|r:'
Nenue@98 858 for i = 1, select('#', ...) do
Nenue@98 859 msg = msg .. ' ' .. tostring(select(i, ...))
Nenue@98 860 end
Nenue@98 861 DEFAULT_CHAT_FRAME:AddMessage(msg)
Nenue@98 862 end
Nenue@98 863
Nenue@98 864 function D:GetActiveChannel()
Nenue@98 865 return D.console[currentProfile.current_channel]
Nenue@98 866 end
Nenue@98 867
Nenue@98 868 function D:GetOrCreateChannel(id, name)
Nenue@99 869 id = id or (#D.channels + 1)
Nenue@99 870
Nenue@98 871 local info = D.channels[id]
Nenue@99 872 if not info then
Nenue@99 873 --print('new channel')
Nenue@99 874 name = name or ('Channel ' .. id)
Nenue@99 875 info = {
Nenue@99 876 index = id,
Nenue@99 877 signature = name,
Nenue@99 878 tags = {}
Nenue@99 879 }
Nenue@99 880 D.DeepCopy(defaults.default_channel, info)
Nenue@99 881 D.channels[id] = info
Nenue@99 882 elseif not info.tags then
Nenue@99 883 -- fix old data?
Nenue@99 884 info.tags = {info.signature}
Nenue@99 885 oldprint(D.db)
Nenue@99 886 for tag, tagSet in pairs(D.tags) do
Nenue@99 887 for _, index in pairs(tagSet) do
Nenue@99 888 if index == id then
Nenue@99 889 tinsert(info.tags, tag)
Nenue@99 890 end
Nenue@99 891 end
Nenue@99 892 end
Nenue@99 893 end
Nenue@99 894
Nenue@98 895 local frame = D.console[id]
Nenue@98 896 if not frame then
Nenue@98 897
Nenue@99 898 if DEVIAN_WORKSPACE then
Nenue@99 899 D:Print('Adding '.. (info.index) .. ':' .. (info.signature) .. ' (|cFF00FFFF'.. concat(info.tags, '|r; |cFF00FFFF')..'|r)')
Nenue@99 900 end
Nenue@98 901 frame = CreateFrame('Frame', 'DevianConsole'..id, Devian, 'DevianConsoleTemplate')
Nenue@98 902 frame:SetID(id)
Nenue@98 903 D.console[id] = frame
Nenue@99 904 D.sigID[info.signature] = id
Nenue@98 905 end
Nenue@98 906
Nenue@98 907 frame:Setup(info)
Nenue@98 908 return frame
Nenue@98 909 end