comparison Bar.lua @ 257:920d17851a93 stable

Merge 1.1 beta 4 to stable
author Flick
date Tue, 12 Apr 2011 16:06:31 -0700
parents 65f2805957a0
children 36a29870bf34
comparison
equal deleted inserted replaced
210:b2b105747466 257:920d17851a93
1 local addonName, addonTable = ...
2 local ReAction = addonTable.ReAction
3 local L = ReAction.L
4 local LKB = ReAction.LKB
5 local _G = _G
6 local CreateFrame = CreateFrame
7 local floor = math.floor
8 local fmod = math.fmod
9 local format = string.format
10 local tfetch = addonTable.tfetch
11 local tbuild = addonTable.tbuild
12 local fieldsort = addonTable.fieldsort
13
14 local LSG = LibStub("ReAction-LibShowActionGrid-1.0")
15
16 ---- Secure snippets ----
17 local _reaction_init =
18 [[
19 anchorKeys = newtable("point","relPoint","x","y")
20
21 state = nil
22 set_state = nil
23 state_override = nil
24 unit_exists = nil
25
26 showAll = false
27 hidden = false
28
29 defaultAlpha = 1.0
30 defaultScale = 1.0
31 defaultAnchor = newtable()
32
33 activeStates = newtable()
34 settings = newtable()
35 extensions = newtable()
36 ]]
37
38 local _reaction_refresh =
39 [[
40 local oldState = state
41 state = state_override or set_state or state
42
43 local hide = nil
44 if state then
45 local settings = settings[state]
46 if settings then
47 -- show/hide
48 hide = settings.hide
49 -- re-anchor
50 local old_anchor = activeStates.anchor
51 activeStates.anchor = settings.anchorEnable and state
52 if old_anchor ~= activeStates.anchor or not set_state then
53 if activeStates.anchor then
54 if settings.anchorPoint then
55 self:ClearAllPoints()
56 local f = self:GetAttribute("frameref-anchor-"..state)
57 if f then
58 self:SetPoint(settings.anchorPoint, f, settings.anchorRelPoint, settings.anchorX, settings.anchorY)
59 end
60 end
61 elseif defaultAnchor.point then
62 self:ClearAllPoints()
63 self:SetPoint(defaultAnchor.point, defaultAnchor.frame,
64 defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y)
65 end
66 end
67 -- re-scale
68 local old_scale = activeStates.scale
69 activeStates.scale = settings.enableScale and state
70 if old_scale ~= activeStates.scale or not set_state then
71 self:SetScale(activeStates.scale and settings.scale or defaultScale)
72 end
73 -- alpha
74 local old_alpha = activeStates.alpha
75 activeStates.alpha = settings.enableAlpha and state
76 if old_alpha ~= activeStates.alpha or not set_state then
77 self:SetAlpha(activeStates.alpha and settings.alpha or defaultAlpha)
78 end
79 end
80 end
81
82 -- hide if state or unit_exists says to
83 hide = not showAll and (hide or unithide)
84 if hide ~= hidden then
85 hidden = hide
86 if hide then
87 self:Hide()
88 else
89 self:Show()
90 end
91 end
92
93 for _, attr in pairs(extensions) do
94 control:RunAttribute(attr)
95 end
96
97 control:ChildUpdate()
98
99 if showAll then
100 control:CallMethod("UpdateHiddenLabel", state and settings[state] and settings[state].hide)
101 end
102
103 if oldState ~= state then
104 control:CallMethod("StateRefresh", state)
105 end
106 ]]
107
108 local _onstate_reaction = -- function( self, stateid, newstate )
109 [[
110 set_state = newstate
111 ]] .. _reaction_refresh
112
113 local _onstate_showgrid = -- function( self, stateid, newstate )
114 [[
115 control:ChildUpdate(stateid,newstate)
116 control:CallMethod("UpdateShowGrid")
117 ]]
118
119 local _onstate_unitexists = -- function( self, stateid, newstate )
120 [[
121 unithide = not newstate or newstate == "hide"
122 ]] .. _reaction_refresh
123
124 local _onclick = -- function( self, button, down )
125 [[
126 if state_override == button then
127 state_override = nil -- toggle
128 else
129 state_override = button
130 end
131 ]] .. _reaction_refresh
132
133 -- For reference
134 -- the option field names must match the field names of the options table, below
135 local stateProperties = {
136 hide = true,
137 --keybindState = true, TODO: broken
138 anchorEnable = true,
139 anchorFrame = true,
140 anchorPoint = true,
141 anchorRelPoint = true,
142 anchorX = true,
143 anchorY = true,
144 enableScale = true,
145 scale = true,
146 enableAlpha = true,
147 alpha = true,
148 }
149
150
151
152 ---- Bar class ----
153 local Bar = { }
154 local frameList = { }
155
156 ReAction.Bar = Bar -- export to ReAction
157
158 function Bar:New( name, config, buttonClass )
159 if type(config) ~= "table" then
160 error("ReAction.Bar: config table required")
161 end
162
163 -- create new self
164 self = setmetatable(
165 {
166 config = config,
167 name = name,
168 buttons = { },
169 buttonClass = buttonClass,
170 width = config.width or 480,
171 height = config.height or 40,
172 },
173 {__index = self} )
174
175 -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is
176 -- not mouse-clickable by the user.
177 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
178 name = name and "ReAction-"..name
179 local f = name and frameList[name]
180 if not f then
181 f = CreateFrame("Button", name, parent, "SecureHandlerStateTemplate, SecureHandlerClickTemplate")
182 if name then
183 frameList[name] = f
184 end
185 end
186 f:SetFrameStrata("MEDIUM")
187 f:SetWidth(self.width)
188 f:SetHeight(self.height)
189 f:SetAlpha(config.alpha or 1.0)
190 f:Show()
191 f:EnableMouse(false)
192 f:SetClampedToScreen(true)
193 LSG:AddFrame(f)
194
195 -- secure handlers
196 f:Execute(_reaction_init)
197 f:SetAttribute("_onstate-reaction", _onstate_reaction)
198 f:SetAttribute("_onstate-showgrid", _onstate_showgrid)
199 f:SetAttribute("_onstate-unitexists", _onstate_unitexists)
200 f:SetAttribute("_onclick", _onclick)
201
202 -- secure handler CallMethod()s
203 f.UpdateShowGrid = function() self:UpdateShowGrid() end
204 f.StateRefresh = function() self:RefreshControls() end
205 f.UpdateHiddenLabel = function(f,hidden) self:SetLabelSubtext(hidden and L["Hidden"]) end
206
207 -- Override the default frame accessor to provide strict read-only access
208 function self:GetFrame()
209 return f
210 end
211
212 self:ApplyAnchor()
213 self:SetConfigMode(ReAction:GetConfigMode())
214 self:SetKeybindMode(ReAction:GetKeybindMode())
215
216 if ReAction.LBF then
217 local g = ReAction.LBF:Group(L["ReAction"], self.name)
218 self.config.ButtonFacade = self.config.ButtonFacade or {
219 skinID = "Blizzard",
220 backdrop = true,
221 gloss = 0,
222 colors = {},
223 }
224 local c = self.config.ButtonFacade
225 g:Skin(c.skinID, c.gloss, c.backdrop, c.colors)
226 self.LBFGroup = g
227 end
228
229 ReAction.RegisterCallback(self, "OnConfigModeChanged")
230
231 buttonClass:SetupBar(self)
232 self:ApplyStates()
233
234 return self
235 end
236
237 function Bar:Destroy()
238 local f = self:GetFrame()
239 self:CleanupStates()
240 for idx, b in self:IterateButtons() do
241 b:Destroy()
242 end
243 f:UnregisterAllEvents()
244 self:ShowControls(false)
245 ReAction.UnregisterAllCallbacks(self)
246 LKB.UnregisterAllCallbacks(self)
247 if self.LBFGroup then
248 self.LBFGroup:Delete(true)
249 end
250 LSG:RemoveFrame(f)
251 f:SetParent(UIParent)
252 f:ClearAllPoints()
253 f:Hide()
254 end
255
256 --
257 -- Events
258 --
259
260 function Bar:OnConfigModeChanged(event, mode)
261 self:SetConfigMode(mode)
262 end
263
264 --
265 -- Accessors
266 --
267
268 function Bar:GetName()
269 return self.name
270 end
271
272 -- only ReAction:RenameBar() should call this function. Calling from any other
273 -- context will desync the bar list in the ReAction class.
274 function Bar:SetName(name)
275 if self.LBFGroup then
276 -- LBF doesn't offer a method of renaming a group, so delete and remake the group.
277 local c = self.config.ButtonFacade
278 local g = ReAction.LBF:Group(L["ReAction"], name)
279 for idx, b in self:IterateButtons() do
280 self.LBFGroup:RemoveButton(b:GetFrame(), true)
281 g:AddButton(b:GetFrame())
282 end
283 self.LBFGroup:Delete(true)
284 self.LBFGroup = g
285 self.LBFGroup:Skin(c.skinID, c.gloss, c.backdrop, c.colors)
286 end
287 self.name = name
288 if self.overlay then
289 self.overlay:SetLabel(self.name)
290 end
291 end
292
293 function Bar:GetFrame()
294 -- this method is included for documentation purposes. It is overridden
295 -- for each object in the :New() method.
296 error("Invalid Bar object: used without initialization")
297 end
298
299 function Bar:GetButton(idx)
300 return self.buttons[idx]
301 end
302
303 function Bar:GetButtonClass()
304 return self.buttonClass
305 end
306
307 function Bar:GetConfig()
308 return self.config
309 end
310
311 function Bar:GetAnchor()
312 local c = self.config
313 return (c.point or "CENTER"),
314 (c.anchor or self:GetFrame():GetParent():GetName()),
315 (c.relpoint or c.point or "CENTER"),
316 (c.x or 0),
317 (c.y or 0)
318 end
319
320 function Bar:SetAnchor(point, frame, relativePoint, x, y)
321 local c = self.config
322 c.point = point or c.point
323 c.anchor = frame or c.anchor
324 c.relpoint = relativePoint or c.relpoint
325 c.x = x or c.x
326 c.y = y or c.y
327 self:ApplyAnchor()
328 end
329
330 function Bar:GetSize()
331 local f = self:GetFrame()
332 return f:GetWidth(), f:GetHeight()
333 end
334
335 function Bar:SetSize(w,h)
336 local f = self:GetFrame()
337 self.config.width = w
338 self.config.height = h
339 f:SetWidth(w)
340 f:SetHeight(h)
341 end
342
343 function Bar:GetButtonSize()
344 local w = self.config.btnWidth or 32
345 local h = self.config.btnHeight or 32
346 -- TODO: get from modules?
347 return w,h
348 end
349
350 function Bar:SetButtonSize(w,h)
351 if w > 0 and h > 0 then
352 self.config.btnWidth = w
353 self.config.btnHeight = h
354 end
355 end
356
357 function Bar:GetNumButtons()
358 local r,c = self:GetButtonGrid()
359 return r*c
360 end
361
362 function Bar:GetButtonGrid()
363 local cfg = self.config
364 local r = cfg.btnRows or 1
365 local c = cfg.btnColumns or 1
366 local s = cfg.spacing or 4
367 return r,c,s
368 end
369
370 function Bar:SetButtonGrid(r,c,s)
371 if r > 0 and c > 0 and s > 0 then
372 local cfg = self.config
373 cfg.btnRows = r
374 cfg.btnColumns = c
375 cfg.spacing = s
376 end
377 self.buttonClass:SetupBar(self)
378 end
379
380 function Bar:GetAlpha()
381 return self.config.alpha or 1.0
382 end
383
384 function Bar:SetAlpha(value)
385 self.config.alpha = value
386 self:GetFrame():SetAlpha(value or 1.0)
387 self:UpdateDefaultStateAlpha()
388 end
389
390 function Bar:IterateButtons()
391 -- iterator returns idx, button, but does NOT iterate in index order
392 return pairs(self.buttons)
393 end
394
395 --
396 -- Methods
397 --
398
399 function Bar:SetConfigMode(mode)
400 self:SetSecureData("showAll",mode)
401 self:ShowControls(mode)
402 for idx, b in self:IterateButtons() do
403 b:ShowGridTemp(mode)
404 b:UpdateActionIDLabel(mode)
405 end
406 end
407
408 function Bar:SetKeybindMode(mode)
409 self:SetSecureData("showAll",mode)
410 for idx, b in self:IterateButtons() do
411 b:SetKeybindMode(mode)
412 end
413 end
414
415 function Bar:ApplyAnchor()
416 local f = self:GetFrame()
417 local c = self.config
418 local p = c.point
419
420 f:SetWidth(c.width)
421 f:SetHeight(c.height)
422 f:ClearAllPoints()
423
424 if p then
425 local a = f:GetParent()
426 if c.anchor then
427 local bar = ReAction:GetBar(c.anchor)
428 if bar then
429 a = bar:GetFrame()
430 else
431 a = _G[c.anchor]
432 end
433 end
434 local fr = a or f:GetParent()
435 f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0)
436 else
437 f:SetPoint("CENTER")
438 end
439
440 self:UpdateDefaultStateAnchor()
441 end
442
443 function Bar:ClipNButtons( n )
444 local cfg = self.config
445 local r = cfg.btnRows or 1
446 local c = cfg.btnColumns or 1
447
448 cfg.btnRows = ceil(n/c)
449 cfg.btnColumns = min(n,c)
450 end
451
452 function Bar:AddButton(idx, button)
453 local f = self:GetFrame()
454
455 self.buttons[idx] = button
456
457 -- Store a properly wrapped reference to the child frame as an attribute
458 -- (accessible via "frameref-btn#")
459 f:SetFrameRef(format("btn%d",idx), button:GetFrame())
460
461 -- button constructors are responsible for calling SkinButton
462 end
463
464 function Bar:RemoveButton(button)
465 local idx = button:GetIndex()
466 if idx then
467 self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil)
468 self.buttons[idx] = nil
469 end
470 if self.LBFGroup then
471 self.LBFGroup:RemoveButton(button:GetFrame(),true)
472 end
473 end
474
475 function Bar:PlaceButton(button, baseW, baseH)
476 local idx = button:GetIndex()
477 if idx then
478 local r, c, s = self:GetButtonGrid()
479 local bh, bw = self:GetButtonSize()
480 local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
481 local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s)
482 local scale = bw/baseW
483 local b = button:GetFrame()
484
485 b:ClearAllPoints()
486 b:SetPoint("TOPLEFT",x/scale,y/scale)
487 b:SetScale(scale)
488 end
489 end
490
491 function Bar:SkinButton( button, data )
492 if self.LBFGroup then
493 self.LBFGroup:AddButton(button:GetFrame(), data)
494 end
495 end
496
497 function Bar:UpdateShowGrid()
498 for idx, button in self:IterateButtons() do
499 button:UpdateShowGrid()
500 end
501 end
502
503 function Bar:ShowControls(show)
504 if show then
505 if not self.overlay then
506 self.overlay = Bar.Overlay:New(self) -- see Overlay.lua
507 end
508 self.overlay:Show()
509 self:RefreshSecureState()
510 elseif self.overlay then
511 self.overlay:Hide()
512 end
513 end
514
515 function Bar:RefreshControls()
516 if self.overlay and self.overlay:IsShown() then
517 self.overlay:RefreshControls()
518 end
519 end
520
521 function Bar:SetLabelSubtext(text)
522 if self.overlay then
523 self.overlay:SetLabelSubtext(text)
524 end
525 end
526
527 --
528 -- Secure state functions
529 --
530
531 function Bar:GetSecureState()
532 local env = GetManagedEnvironment(self:GetFrame())
533 return env and env.state
534 end
535
536 function Bar:GetStateProperty(state, propname)
537 return tfetch(self:GetConfig(), "states", state, propname)
538 end
539
540 function Bar:SetStateProperty(state, propname, value)
541 local s = tbuild(self:GetConfig(), "states", state)
542 s[propname] = value
543 self:SetSecureStateData(state, propname, value)
544 end
545
546 function Bar:ApplyStates()
547 local states = tfetch(self:GetConfig(), "states")
548 if states then
549 self:SetStateDriver(states)
550 end
551 end
552
553 function Bar:CleanupStates()
554 self:SetStateDriver(nil)
555 end
556
557 function Bar:RefreshSecureState()
558 self:GetFrame():Execute(_reaction_refresh)
559 end
560
561 -- usage: SetSecureData(globalname, [tblkey1, tblkey2, ...], value)
562 function Bar:SetSecureData( ... )
563 local n = select('#',...)
564 if n < 2 then
565 error("ReAction.Bar:SetSecureData() requires at least 2 arguments")
566 end
567 local f = self:GetFrame()
568 f:SetAttribute("data-depth",n-1)
569 f:SetAttribute("data-value",select(n,...))
570 for i = 1, n-1 do
571 local key = select(i,...)
572 if key == nil then
573 error("ReAction.Bar:SetSecureData() - nil table key in argument list (#"..i..")")
574 end
575 f:SetAttribute("data-key-"..i, key)
576 end
577 f:Execute(
578 [[
579 local n = self:GetAttribute("data-depth")
580 if n > 0 then
581 local value = self:GetAttribute("data-value")
582 local t = _G
583 for i = 1, n do
584 local key = self:GetAttribute("data-key-"..i)
585 if not key then return end
586 if not t[key] then
587 t[key] = newtable()
588 end
589 if i == n then
590 t[key] = value
591 else
592 t = t[key]
593 end
594 end
595 end
596 ]])
597 self:RefreshSecureState()
598 end
599
600 function Bar:SetSecureStateData( state, key, value )
601 self:SetSecureData("settings",state,key,value)
602 end
603
604 -- sets a snippet to be run as an extension to _onstate-reaction
605 function Bar:SetSecureStateExtension( id, snippet )
606 if id == nil then
607 error("ReAction.Bar:SetSecureStateExtension() requires an id")
608 end
609 local f = self:GetFrame()
610 f:SetAttribute("input-secure-ext-id",id)
611 f:SetAttribute("secure-ext-"..id,snippet)
612 f:Execute(
613 [[
614 local id = self:GetAttribute("input-secure-ext-id")
615 if id then
616 extensions[id] = self:GetAttribute("secure-ext-"..id) or nil
617 end
618 ]])
619 self:RefreshSecureState()
620 end
621
622 function Bar:SetFrameRef( name, refFrame )
623 if refFrame then
624 local _, explicit = refFrame:IsProtected()
625 if not explicit then
626 refFrame = nil
627 end
628 end
629 if refFrame then
630 self:GetFrame():SetFrameRef(name,refFrame)
631 else
632 self:GetFrame():SetAttribute("frameref-"..name,nil)
633 end
634 end
635
636 function Bar:SetStateDriver( states )
637 if states then
638 for state, props in pairs(states) do
639 self:SetSecureStateData(state, "active_", true) -- make sure there's a 'settings' field for this state
640 for propname, value in pairs(props) do
641 if propname == "anchorFrame" then
642 self:SetFrameRef("anchor-"..state, _G[value])
643 elseif propname == "rule" then
644 -- do nothing
645 else
646 self:SetSecureStateData(state, propname, value)
647 end
648 end
649 end
650 end
651 local rule = states and self:BuildStateRule(states)
652 if rule then
653 RegisterStateDriver(self:GetFrame(),"reaction",rule)
654 elseif self.statedriver then
655 UnregisterStateDriver(self:GetFrame(),"reaction")
656 end
657 self.statedriver = rule
658 self:BuildStateKeybinds(states)
659 self:RefreshSecureState()
660 end
661
662 -- pass unit=nil to set up the unit elsewhere, if you want something more complex
663 function Bar:RegisterUnitWatch( unit, enable )
664 local f = self:GetFrame()
665 if unit then
666 f:SetAttribute("unit",unit)
667 end
668 if enable then
669 if not self.unitwatch then
670 RegisterUnitWatch(self:GetFrame(),true)
671 end
672 elseif self.unitwatch then
673 UnregisterUnitWatch(self:GetFrame())
674 end
675 self.unitwatch = enable
676 self:RefreshSecureState()
677 end
678
679 function Bar:SetStateKeybind( key, state )
680 local f = self:GetFrame()
681 local binds = self.statebinds
682 if not binds then
683 binds = { }
684 self.statebinds = binds
685 end
686
687 -- clear the old binding, if any
688 if binds[state] then
689 SetOverrideBinding(f, false, binds[state], nil)
690 end
691
692 if key then
693 SetOverrideBindingClick(f, false, key, f:GetName(), state) -- state name is virtual mouse button
694 end
695 binds[state] = key
696 end
697
698 function Bar:GetStateKeybind( state )
699 if self.statebinds and state then
700 return self.statebinds[state]
701 end
702 end
703
704 function Bar:UpdateDefaultStateAnchor()
705 local point, frame, relPoint, x, y = self:GetAnchor()
706 local f = self:GetFrame()
707 f:SetAttribute("defaultAnchor-point",point)
708 f:SetAttribute("defaultAnchor-relPoint",relPoint)
709 f:SetAttribute("defaultAnchor-x",x)
710 f:SetAttribute("defaultAnchor-y",y)
711 self:SetFrameRef("defaultAnchor",_G[frame or "UIParent"])
712 f:Execute([[
713 for _, k in pairs(anchorKeys) do
714 defaultAnchor[k] = self:GetAttribute("defaultAnchor-"..k)
715 end
716 defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor")
717 ]])
718 end
719
720 function Bar:UpdateDefaultStateAlpha()
721 local f = self:GetFrame()
722 f:SetAttribute("defaultAlpha",self:GetAlpha())
723 f:Execute([[
724 defaultAlpha = self:GetAttribute("defaultAlpha")
725 ]])
726 end
727
728 ---- secure state driver rules ----
729
730 local playerClass = select(2, UnitClass("player"))
731 local function ClassFilter(...)
732 for i = 1, select('#',...) do
733 if playerClass == select(i,...) then
734 return false
735 end
736 end
737 return true
738 end
739
740 local ruleformats = {
741 stealth = { format = "stealth", filter = ClassFilter("ROGUE","DRUID") },
742 nostealth = { format = "nostealth", filter = ClassFilter("ROGUE","DRUID") },
743 shadowdance = { format = "bonusbar:2", filter = ClassFilter("ROGUE") },
744 shadowform = { format = "form:1", filter = ClassFilter("PRIEST") },
745 noshadowform = { format = "noform", filter = ClassFilter("PRIEST") },
746 battle = { format = "stance:1", filter = ClassFilter("WARRIOR") },
747 defensive = { format = "stance:2", filter = ClassFilter("WARRIOR") },
748 berserker = { format = "stance:3", filter = ClassFilter("WARRIOR") },
749 caster = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") },
750 bear = { format = "form:1", filter = ClassFilter("DRUID") },
751 cat = { format = "form:3", filter = ClassFilter("DRUID") },
752 tree = { format = "form:5", filter = ClassFilter("DRUID") },
753 moonkin = { format = "form:5", filter = ClassFilter("DRUID") },
754 demon = { format = "form:2", filter = ClassFilter("WARLOCK") },
755 nodemon = { format = "noform", filter = ClassFilter("WARLOCK") },
756 pet = { format = "pet" },
757 nopet = { format = "nopet" },
758 harm = { format = "@target,harm" },
759 help = { format = "@target,help" },
760 notarget = { format = "@target,noexists" },
761 focusharm = { format = "@focus,harm" },
762 focushelp = { format = "@focus,help" },
763 nofocus = { format = "@focus,noexists" },
764 raid = { format = "group:raid" },
765 party = { format = "group:party" },
766 solo = { format = "nogroup" },
767 combat = { format = "combat" },
768 nocombat = { format = "nocombat" },
769 possess = { format = "@vehicle,noexists,bonusbar:5" },
770 vehicle = { format = "@vehicle,exists,bonusbar:5" },
771 }
772
773 function Bar.InitRuleFormats()
774 local forms = { }
775 for i = 1, GetNumShapeshiftForms() do
776 local _, name = GetShapeshiftFormInfo(i)
777 forms[name] = i;
778 end
779 -- use 9 if not found since 9 is never a valid stance/form
780 local defensive = forms[GetSpellInfo(71)] or 9
781 local berserker = forms[GetSpellInfo(2458)] or 9
782 local bear = forms[GetSpellInfo(5487)] or 9
783 local aquatic = forms[GetSpellInfo(1066)] or 9
784 local cat = forms[GetSpellInfo(768)] or 9
785 local travel = forms[GetSpellInfo(783)] or 9
786 local tree = forms[GetSpellInfo(33891)] or 9
787 local moonkin = forms[GetSpellInfo(24858)] or 9
788 local flight = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9
789
790 ruleformats.defensive.format = "stance:"..defensive
791 ruleformats.berserker.format = "stance:"..berserker
792 ruleformats.caster.format = format("form:0/%d/%d/%d", aquatic, travel, flight)
793 ruleformats.bear.format = "form:"..bear
794 ruleformats.cat.format = "form:"..cat
795 ruleformats.tree.format = "form:"..tree
796 ruleformats.moonkin.format = "form:"..moonkin
797 end
798
799 function Bar:BuildStateRule(states)
800 -- states is a table :
801 -- states[statename].rule = {
802 -- order = #,
803 -- type = "default"/"custom"/"any"/"all",
804 -- values = { ... }, -- keys of ruleformats[]
805 -- custom = "...",
806 -- }
807 local rules = { }
808 local default
809
810 for idx, state in ipairs(fieldsort(states, "rule", "order")) do
811 local c = states[state].rule
812 local type = c.type
813 if type == "default" then
814 default = default or state
815 elseif type == "custom" then
816 if c.custom then
817 -- strip out all spaces from the custom rule
818 table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
819 end
820 elseif type == "any" or type == "all" then
821 if c.values then
822 local clauses = { }
823 for key, value in pairs(c.values) do
824 if ruleformats[key] and not ruleformats[key].filter then
825 table.insert(clauses, ruleformats[key].format)
826 end
827 end
828 if #clauses > 0 then
829 local sep = (type == "any") and "][" or ","
830 table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
831 end
832 end
833 end
834 end
835 -- make sure that the default, if any, is last
836 if default then
837 table.insert(rules, default)
838 end
839 return table.concat(rules,";")
840 end
841
842 function Bar:BuildStateKeybinds( states )
843 if states then
844 for name, state in pairs(states) do
845 local rule = tfetch(state, "rule")
846 if rule and rule.type == "keybind" then
847 self:SetStateKeybind(rule.keybind, name)
848 else
849 self:SetStateKeybind(nil, name) -- this clears an existing keybind
850 end
851 end
852 end
853 end
854