vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[ ItemRack 1.975 7/27/06 Gello ]]--
2  
3 --[[ SavedVariables ]]--
4  
5 ItemRack_Users = {} -- per-user bar settings (position, orientation, scale, locked status, bar contents)
6  
7 ItemRack_Settings = { -- These settings are for all users:
8 TooltipFollow = "OFF", -- whether tooltip shows at pointer or default position
9 CooldownNumbers = "OFF", -- whether cooldowns show a number overlay
10 Soulbound = "OFF", -- whether menu limits items to soulbound only
11 Bindings = "OFF", -- whether key bindings is displayed
12 MenuShift = "OFF", -- whether Shift key needs held to open the menu
13 Notify = "OFF", -- whether to notify when cooldowns finished on used items
14 ShowEmpty = "ON", -- whether to show empty slot in the menu
15 FlipMenu = "OFF", -- whether to display menu on the opposite side
16 RightClick = "OFF", -- whether right click sends to second slot
17 TinyTooltip = "OFF", -- whether to only display name, durability and cooldown in tooltip
18 ShowTooltips = "ON", -- whether to display tooltip at all
19 RotateMenu = "OFF", -- whether menu is rotated (temporary setting)
20 ShowIcon = "ON", -- whether to show the minimap button
21 DisableToggle = "ON", -- whether left-clicking disables the minimap button
22 FlipBar = "OFF", -- whether control appears on bottom or right bar grows other direction
23 EnableEvents = "OFF", -- whether automated event scrips run
24 CompactList = "OFF", -- whether saved sets list is compacted or not
25 NotifyThirty = "ON", -- whether notify happens at 30 seconds
26 ShowAllEvents = "OFF", -- whether to show all classes' events
27 AllowHidden = "OFF", -- whether to hide menu items with ALT+click
28 LargeFont = "OFF", -- whether event script font is large or small
29 SquareMinimap = "OFF", -- whether minimap button should go around square minimap
30 BigCooldown = "OFF", -- whether cooldown numbers are huge (like Cooldown Count)
31 SetLabels = "ON", -- whether labels show on set icons
32 AutoToggle = "OFF", -- whether sets automatically toggle when chosen
33 }
34  
35 -- all event scripts are stored globally in this saved variable. Defaults are in localization.lua
36 ItemRack_Events = {}
37  
38 ItemRack_Version = 1.975
39  
40 --[[ Local Variables ]]--
41  
42 -- some mount textures share non-mount buff textures, if you run across one put it here
43 local problem_mounts = {
44 ["Interface\\Icons\\Ability_Mount_PinkTiger"] = 1,
45 ["Interface\\Icons\\Ability_Mount_WhiteTiger"] = 1,
46 ["Interface\\Icons\\Spell_Nature_Swiftness"] = 1,
47 ["Interface\\Icons\\INV_Misc_Foot_Kodo"] = 1,
48 ["Interface\\Icons\\Ability_Mount_JungleTiger"] =1,
49 }
50  
51 local current_events_version = 1.975 -- use to control when to upgrade events
52  
53 -- defaults for ItemRack_Users
54 local ItemRackOpt_Defaults = {
55 MainOrient = "HORIZONTAL", -- direction of main bar, "HORIZONTAL" or "VERTICAL", menu orient always opposite
56 MainScale = 1, -- scale 0-1 of main bar and menu bar
57 XPos = 400, -- left position of main bar
58 YPos = 350, -- top position of main bar
59 Locked = "OFF", -- lock status of main bar (for all intents menu always locked)
60 Visible = "ON" -- whether the bar should be drawn on the screen
61 }
62  
63 local user = "default" -- "Gello of Hyjal" or "Gello of Deathwing ", defined at PLAYER_LOGIN
64  
65 ItemRack = {}
66  
67 ItemRack.FrameToScale = nil -- holds frame being scaled for ScaleUpdate
68  
69 ItemRack.BaggedItems = {} -- table containing items in bags to show in menu
70 ItemRack.NumberOfItems = 0 -- number of items in the menu
71 ItemRack.MaxItems = 30 -- maximum number of items that can display in the menu
72 ItemRack.InvOpen = nil -- which inventory slot has its menu open
73  
74 ItemRack.TooltipOwner = nil -- (this) when tooltip created
75 ItemRack.TooltipType = nil -- "BAG" or "INVENTORY"
76 ItemRack.TooltipBag = nil -- bag number
77 ItemRack.TooltipSlot = nil -- bag or inventory slot number
78  
79 ItemRack.CurrentTime = GetTime()
80  
81 ItemRack.MainDock = "" -- "TOPLEFT" "BOTTOMRIGHT" etc
82 ItemRack.MenuDock = ""
83  
84 ItemRack.Queue = {} -- [slot]="ItemName" if an item in queue, ie [13]="Arcanite Dragonling", [slot]=nil for no item in queue
85 ItemRack.NotifyList = {} -- ["item name"] = { bag=0-4, slot=1-x, inv=0-19, hadcooldown=true/nil }
86 ItemRack.AmmoCounts = {} -- ["Heavy Shot"]=200, ["Thorium Arrow"]=982, etc
87 ItemRack.TrinketsPaired = false -- true if the two trinkets are next to each other
88  
89 ItemRack.KeyBindingsSettled = false
90  
91 ItemRack.MenuDockedTo = nil -- "SET" for set window, "CHARACTERSHEET" for PaperDollFrame, nil for rack
92  
93 ItemRack.SelectedEvent = 0 -- index in list of event selected
94  
95 ItemRack.CanWearOneHandOffHand = nil -- whether player can wear one-hand in offhand (warrior, rogue, hunter)
96  
97 ItemRack.Buffs = {} -- table indexed by buff names, whether a buff is on or not
98 ItemRack.BankedItems = {} -- items in the bank indexed by itemID
99 ItemRack.BankSlots = { -1,5,6,7,8,9,10 }
100  
101 local eventList = {} -- note the departure from using the table for local values -- mod will be rewritten to a consistent style
102 local eventListSize = 1
103  
104 local scratchTable = { {}, {} } -- for secondary sorts
105 local scratchTableSize = { 1, 1 }
106  
107 --[[ reference tables ]]--
108  
109 ItemRack.OptInfo = {
110 ["ItemRack_Control_Rotate"] = { text=ItemRackText.CONTROL_ROTATE_TEXT, tooltip=ItemRackText.CONTROL_ROTATE_TOOLTIP },
111 ["ItemRack_Control_Lock"] = { text=ItemRackText.CONTROL_LOCK_TEXT, tooltip=ItemRackText.CONTROL_LOCK_TOOLTIP },
112 ["ItemRack_Control_Options"] = { text=ItemRackText.CONTROL_OPTIONS_TEXT, tooltip=ItemRackText.CONTROL_OPTIONS_TOOLTIP },
113 ["ItemRack_Opt_TooltipFollow"] = { text=ItemRackText.OPT_TOOLTIPFOLLOW_TEXT, tooltip=ItemRackText.OPT_TOOLTIPFOLLOW_TOOLTIP, type="Check", info="TooltipFollow" },
114 ["ItemRack_Opt_CooldownNumbers"] = { text=ItemRackText.OPT_COOLDOWNNUMBERS_TEXT, tooltip=ItemRackText.OPT_COOLDOWNNUMBERS_TOOLTIP, type="Check", info="CooldownNumbers" },
115 ["ItemRack_Opt_Soulbound"] = { text=ItemRackText.OPT_SOULBOUND_TEXT, tooltip=ItemRackText.OPT_SOULBOUND_TOOLTIP, type="Check", info="Soulbound" },
116 ["ItemRack_Opt_Bindings"] = { text=ItemRackText.OPT_BINDINGS_TEXT, tooltip=ItemRackText.OPT_BINDINGS_TOOLTIP, type="Check", info="Bindings" },
117 ["ItemRack_Opt_MenuShift"] = { text=ItemRackText.OPT_MENUSHIFT_TEXT, tooltip=ItemRackText.OPT_MENUSHIFT_TOOLTIP, type="Check", info="MenuShift" },
118 ["ItemRack_Opt_Close"] = { text=ItemRackText.OPT_CLOSE_TEXT, tooltip=ItemRackText.OPT_CLOSE_TOOLTIP },
119 ["ItemRack_InvFrame_Resize"] = { text=ItemRackText.INVFRAME_RESIZE_TEXT, tooltip=ItemRackText.INVFRAME_RESIZE_TOOLTIP },
120 ["ItemRack_Opt_ShowEmpty"] = { text=ItemRackText.OPT_SHOWEMPTY_TEXT, tooltip=ItemRackText.OPT_SHOWEMPTY_TOOLTIP, type="Check", info="ShowEmpty" },
121 ["ItemRack_Opt_FlipMenu"] = { text=ItemRackText.OPT_FLIPMENU_TEXT, tooltip=ItemRackText.OPT_FLIPMENU_TOOLTIP, type="Check", info="FlipMenu" },
122 ["ItemRack_Opt_RightClick"] = { text=ItemRackText.OPT_RIGHTCLICK_TEXT, tooltip=ItemRackText.OPT_RIGHTCLICK_TOOLTIP, type="Check", info="RightClick" },
123 ["ItemRack_Opt_TinyTooltip"] = { text=ItemRackText.OPT_TINYTOOLTIP_TEXT, tooltip=ItemRackText.OPT_TINYTOOLTIP_TOOLTIP, type="Check", info="TinyTooltip" },
124 ["ItemRack_Opt_ShowTooltips"] = { text=ItemRackText.OPT_SHOWTOOLTIPS_TEXT, tooltip=ItemRackText.OPT_SHOWTOOLTIPS_TOOLTIP, type="Check", info="ShowTooltips" },
125 ["ItemRack_Opt_Notify"] = { text=ItemRackText.OPT_NOTIFY_TEXT, tooltip=ItemRackText.OPT_NOTIFY_TOOLTIP, type="Check", info="Notify" },
126 ["ItemRack_Opt_RotateMenu"] = { text=ItemRackText.OPT_ROTATEMENU_TEXT, tooltip=ItemRackText.OPT_ROTATEMENU_TOOLTIP, type="Check", info="RotateMenu" },
127 ["ItemRack_Sets_Close"] = { text=ItemRackText.SETS_CLOSE_TEXT, tooltip=ItemRackText.SETS_CLOSE_TOOLTIP },
128 ["ItemRack_Sets_NameLabel"] = { text=ItemRackText.SETS_NAMELABEL_TEXT, type="Label" },
129 ["ItemRack_Sets_HideSet"] = { text=ItemRackText.SETS_HIDESET_TEXT, tooltip=ItemRackText.SETS_HIDESET_TOOLTIP, type="Check" },
130 ["ItemRack_Sets_Tab1"] = { text=ItemRackText.SETS_TAB1_TEXT, tooltip=ItemRackText.SETS_TAB1_TOOLTIP, type="Tab" },
131 ["ItemRack_Sets_Tab2"] = { text=ItemRackText.SETS_TAB2_TEXT, tooltip=ItemRackText.SETS_TAB2_TOOLTIP, type="Tab" },
132 ["ItemRack_Sets_Tab3"] = { text=ItemRackText.SETS_TAB3_TEXT, tooltip=ItemRackText.SETS_TAB3_TOOLTIP, type="Tab" },
133 ["ItemRack_Opt_ShowIcon"] = { text=ItemRackText.OPT_SHOWICON_TEXT, tooltip=ItemRackText.OPT_SHOWICON_TOOLTIP, type="Check", info="ShowIcon" },
134 ["ItemRack_Opt_DisableToggle"] = { text=ItemRackText.OPT_DISABLETOGGLE_TEXT, tooltip=ItemRackText.OPT_DISABLETOGGLE_TOOLTIP, type="Check", info="DisableToggle" },
135 ["ItemRack_Sets_Lock"] = { text=ItemRackText.CONTROL_LOCK_TEXT, tooltip=ItemRackText.CONTROL_LOCK_TOOLTIP },
136 ["ItemRack_Sets_BindButton"] = { text=ItemRackText.SETS_BINDBUTTON_TEXT, tooltip=ItemRackText.SETS_BINDBUTTON_TOOLTIP, type="Label" },
137 ["ItemRack_Sets_SaveButton"] = { text=ItemRackText.SETS_SAVEBUTTON_TEXT, tooltip=ItemRackText.SETS_SAVEBUTTON_TOOLTIP, type="Label" },
138 ["ItemRack_Sets_RemoveButton"] = { text=ItemRackText.SETS_REMOVEBUTTON_TEXT, tooltip=ItemRackText.SETS_REMOVEBUTTON_TOOLTIP, type="Label" },
139 ["ItemRack_Opt_FlipBar"] = { text=ItemRackText.OPT_FLIPBAR_TEXT, tooltip=ItemRackText.OPT_FLIPBAR_TOOLTIP, type="Check", info="FlipBar" },
140 ["ItemRack_Opt_EnableEvents"] = { text=ItemRackText.OPT_ENABLEEVENTS_TEXT, tooltip=ItemRackText.OPT_ENABLEEVENTS_TOOLTIP, type="Check", info="EnableEvents" },
141 ["ItemRack_Opt_CompactList"] = { text=ItemRackText.OPT_COMPACTLIST_TEXT, tooltip=ItemRackText.OPT_COMPACTLIST_TOOLTIP, type="Check", info="CompactList" },
142 ["ItemRack_SavedSets_Close"] = { text=ItemRackText.OPT_SAVEDSETSCLOSE_TEXT, tooltip=ItemRackText.OPT_SAVEDSETSCLOSE_TOOLTIP },
143 ["ItemRack_Events_DeleteButton"] = { text=ItemRackText.EVENTSDELETE_TEXT, tooltip=ItemRackText.EVENTSDELETE_TOOLTIP },
144 ["ItemRack_Events_EditButton"] = { text=ItemRackText.EVENTSEDIT_TEXT, tooltip=ItemRackText.EVENTSEDIT_TOOLTIP },
145 ["ItemRack_Events_NewButton"] = { text=ItemRackText.EVENTSNEW_TEXT, tooltip=ItemRackText.EVENTSNEW_TOOLTIP },
146 ["ItemRack_EditEvent_Save"] = { text=ItemRackText.EVENTSSAVE_TEXT, tooltip=ItemRackText.EVENTSSAVE_TOOLTIP },
147 ["ItemRack_EditEvent_Test"] = { text=ItemRackText.EVENTSTEST_TEXT, tooltip=ItemRackText.EVENTSTEST_TOOLTIP },
148 ["ItemRack_EditEvent_Cancel"] = { text=ItemRackText.EVENTSCANCEL_TEXT, tooltip=ItemRackText.EVENTSCANCEL_TOOLTIP },
149 ["ItemRack_EventName"] = { text=ItemRackText.EVENTNAME_TEXT, tooltip=ItemRackText.EVENTNAME_TOOLTIP },
150 ["ItemRack_EventTrigger"] = { text=ItemRackText.EVENTTRIGGER_TEXT, tooltip=ItemRackText.EVENTTRIGGER_TOOLTIP },
151 ["ItemRack_EventDelay"] = { text=ItemRackText.EVENTDELAY_TEXT, tooltip=ItemRackText.EVENTDELAY_TOOLTIP },
152 ["ItemRack_Opt_NotifyThirty"] = { text=ItemRackText.OPT_NOTIFYTHIRTY_TEXT, tooltip=ItemRackText.OPT_NOTIFYTHIRTY_TOOLTIP, type="Check", info="NotifyThirty" },
153 ["ItemRack_Opt_ShowAllEvents"] = { text=ItemRackText.OPT_SHOWALLEVENTS_TEXT, tooltip=ItemRackText.OPT_SHOWALLEVENTS_TOOLTIP, type="Check", info="ShowAllEvents" },
154 ["ItemRack_ResetButton"] = { text=ItemRackText.RESETBUTTON_TEXT, tooltip=ItemRackText.RESETBUTTON_TOOLTIP },
155 ["ItemRack_Opt_AllowHidden"] = { text=ItemRackText.OPT_ALLOWHIDDEN_TEXT, tooltip=ItemRackText.OPT_ALLOWHIDDEN_TOOLTIP, type="Check", info="AllowHidden" },
156 ["ItemRack_Opt_LargeFont"] = { text=ItemRackText.OPT_LARGEFONT_TEXT, tooltip=ItemRackText.OPT_LARGEFONT_TOOLTIP, type="Check", info="LargeFont" },
157 ["ItemRack_ResetEventsButton"] = { text=ItemRackText.RESETEVENTSBUTTON_TEXT, tooltip=ItemRackText.RESETEVENTSBUTTON_TOOLTIP },
158 ["ItemRack_Opt_SquareMinimap"] = { text=ItemRackText.OPT_SQUAREMINIMAP_TEXT, tooltip=ItemRackText.OPT_SQUAREMINIMAP_TOOLTIP, info="SquareMinimap" },
159 ["ItemRack_Opt_BigCooldown"] = { text=ItemRackText.OPT_BIGCOOLDOWN_TEXT, tooltip=ItemRackText.OPT_BIGCOOLDOWN_TOOLTIP, info="BigCooldown" },
160 ["ItemRack_ShowHelmText"] = { text="Helm", type="Label" },
161 ["ItemRack_ShowCloakText" ] = { text="Cloak", type="Label" },
162 ["ItemRack_Opt_SetLabels"] = { text=ItemRackText.OPT_SETLABELS_TEXT, tooltip=ItemRackText.OPT_SETLABELS_TOOLTIP, info="SetLabels" },
163 ["ItemRack_Opt_AutoToggle"] = { text=ItemRackText.OPT_AUTOTOGGLE_TEXT, tooltip=ItemRackText.OPT_AUTOTOGGLE_TOOLTIP, info="AutoToggle" },
164 }
165  
166 -- numerically indexed list of options for scrollable options window
167 ItemRack.OptScroll = {
168 { idx="ItemRack_Opt_ShowTooltips" },
169 { idx="ItemRack_Opt_TooltipFollow", dependency="ItemRack_Opt_ShowTooltips" },
170 { idx="ItemRack_Opt_TinyTooltip", dependency="ItemRack_Opt_ShowTooltips" },
171 { idx="ItemRack_Opt_ShowIcon" },
172 { idx="ItemRack_Opt_DisableToggle", dependency="ItemRack_Opt_ShowIcon" },
173 { idx="ItemRack_Opt_SquareMinimap", dependency="ItemRack_Opt_ShowIcon" },
174 { idx="ItemRack_Opt_Bindings" },
175 { idx="ItemRack_Opt_SetLabels" },
176 { idx="ItemRack_Opt_CooldownNumbers" },
177 { idx="ItemRack_Opt_BigCooldown", dependency="ItemRack_Opt_CooldownNumbers" },
178 { idx="ItemRack_Opt_Notify" },
179 { idx="ItemRack_Opt_NotifyThirty", dependency="ItemRack_Opt_Notify" },
180 { idx="ItemRack_Opt_MenuShift" },
181 { idx="ItemRack_Opt_AutoToggle" },
182 { idx="ItemRack_Opt_ShowEmpty" },
183 { idx="ItemRack_Opt_AllowHidden" },
184 { idx="ItemRack_Opt_Soulbound" },
185 { idx="ItemRack_Opt_RightClick" },
186 { idx="ItemRack_Opt_FlipMenu" },
187 { idx="ItemRack_Opt_RotateMenu" },
188 { idx="ItemRack_Opt_FlipBar" }
189 }
190  
191 -- paperdoll_slot=frame name of the slot on the paperdoll frame (for alt+click purposes)
192 -- ["SlotName"]=1 for each allowable slot
193 -- swappable=1 for slots that can be swapped in combat
194 -- ignore_soulbound = ignore soulbound flag for this slot
195 ItemRack.Indexes = {
196 [0] = { name=AMMOSLOT, paperdoll_slot="CharacterAmmoSlot", keybind="Use Ammo Item", ignore_soulbound=1, swappable=1, INVTYPE_AMMO=1 },
197 [1] = { name=INVTYPE_HEAD, paperdoll_slot="CharacterHeadSlot", keybind="Use Head Item", INVTYPE_HEAD=1 },
198 [2] = { name=INVTYPE_NECK, paperdoll_slot="CharacterNeckSlot", keybind="Use Neck Item", INVTYPE_NECK=1 },
199 [3] = { name=INVTYPE_SHOULDER, paperdoll_slot="CharacterShoulderSlot", keybind="Use Shoulder Item", INVTYPE_SHOULDER=1 },
200 [4] = { name=INVTYPE_BODY, paperdoll_slot="CharacterShirtSlot", keybind="Use Shirt Item", ignore_soulbound=1, INVTYPE_BODY=1 },
201 [5] = { name=INVTYPE_CHEST, paperdoll_slot="CharacterChestSlot", keybind="Use Chest Item", INVTYPE_CHEST=1, INVTYPE_ROBE=1 },
202 [6] = { name=INVTYPE_WAIST, paperdoll_slot="CharacterWaistSlot", keybind="Use Waist Item", INVTYPE_WAIST=1 },
203 [7] = { name=INVTYPE_LEGS, paperdoll_slot="CharacterLegsSlot", keybind="Use Legs Item", INVTYPE_LEGS=1 },
204 [8] = { name=INVTYPE_FEET, paperdoll_slot="CharacterFeetSlot", keybind="Use Feet Item", INVTYPE_FEET=1 },
205 [9] = { name=INVTYPE_WRIST, paperdoll_slot="CharacterWristSlot", keybind="Use Wrist Item", INVTYPE_WRIST=1 },
206 [10]= { name=INVTYPE_HAND, paperdoll_slot="CharacterHandsSlot", keybind="Use Hands Item", INVTYPE_HAND=1 },
207 [11]= { name=INVTYPE_FINGER, paperdoll_slot="CharacterFinger0Slot", keybind="Use Top Finger Item", INVTYPE_FINGER=1 },
208 [12]= { name=INVTYPE_FINGER, paperdoll_slot="CharacterFinger1Slot", keybind="Use Bottom Finger Item", INVTYPE_FINGER=1 },
209 [13]= { name=INVTYPE_TRINKET, paperdoll_slot="CharacterTrinket0Slot", ignore_soulbound=1, keybind="Use Top Trinket Item", INVTYPE_TRINKET=1 },
210 [14]= { name=INVTYPE_TRINKET, paperdoll_slot="CharacterTrinket1Slot", ignore_soulbound=1, keybind="Use Bottom Trinket Item", INVTYPE_TRINKET=1 },
211 [15]= { name=INVTYPE_CLOAK, paperdoll_slot="CharacterBackSlot", keybind="Use Back Item", INVTYPE_CLOAK=1 },
212 [16]= { name=INVTYPE_WEAPONMAINHAND, paperdoll_slot="CharacterMainHandSlot", keybind="Use Main-Hand Item", swappable=1, INVTYPE_WEAPONMAINHAND=1, INVTYPE_2HWEAPON=1, INVTYPE_WEAPON=1 },
213 [17]= { name=INVTYPE_WEAPONOFFHAND, paperdoll_slot="CharacterSecondaryHandSlot", keybind="Use Off-Hand Item", swappable=1, INVTYPE_WEAPONOFFHAND=1, INVTYPE_SHIELD=1, INVTYPE_HOLDABLE=1, INVTYPE_WEAPON=1 },
214 [18]= { name=INVTYPE_RANGED, paperdoll_slot="CharacterRangedSlot", keybind="Use Range Item", swappable=1, INVTYPE_RANGED=1, INVTYPE_RANGEDRIGHT=1, INVTYPE_THROWN=1, INVTYPE_RELIC=1 },
215 [19]= { name=INVTYPE_TABARD, paperdoll_slot="CharacterTabardSlot", keybind="Use Tabard Item", ignore_soulbound=1, INVTYPE_TABARD=1 }
216 }
217  
218 -- "add" or "remove" a frame from UISpecialFrames
219 local function make_escable(frame,add)
220 local found
221 for i in UISpecialFrames do
222 if UISpecialFrames[i]==frame then
223 found = i
224 end
225 end
226 if not found and add=="add" then
227 table.insert(UISpecialFrames,frame)
228 elseif found and add=="remove" then
229 table.remove(UISpecialFrames,found)
230 end
231 end
232  
233 local _,_,durability_pattern = string.find(DURABILITY_TEMPLATE,"(.+) .+/.+")
234 durability_pattern = durability_pattern or ""
235  
236 -- dock-dependant offset and directions: MainDock..MenuDock
237 -- x/yoff = offset MenuFrame is positioned to InvFrame
238 -- x/ydir = direction items are added to menu
239 -- x/ystart = starting offset when building a menu, relativePoint MenuDock
240 -- mx/y = offset MenuFrame is positioned to contents of InvFrame
241 local dock_stats = { ["TOPRIGHTTOPLEFT"] = { xoff=-4, yoff=0, xdir=1, ydir=-1, xstart=8, ystart=-8, mx=3, my=8 },
242 ["BOTTOMRIGHTBOTTOMLEFT"] = { xoff=-4, yoff=0, xdir=1, ydir=1, xstart=8, ystart=44, mx=3, my=-8 },
243 ["TOPLEFTTOPRIGHT"] = { xoff=4, yoff=0, xdir=-1, ydir=-1, xstart=-44, ystart=-8, mx=-2, my=8 },
244 ["BOTTOMLEFTBOTTOMRIGHT"] = { xoff=4, yoff=0, xdir=-1, ydir=1, xstart=-44, ystart=44, mx=-2, my=-8 },
245 ["TOPRIGHTBOTTOMRIGHT"] = { xoff=0, yoff=-4, xdir=-1, ydir=1, xstart=-44, ystart=44, mx=8, my=3 },
246 ["BOTTOMRIGHTTOPRIGHT"] = { xoff=0, yoff=4, xdir=-1, ydir=-1, xstart=-44, ystart=-8, mx=8, my=-3 },
247 ["TOPLEFTBOTTOMLEFT"] = { xoff=0, yoff=-4, xdir=1, ydir=1, xstart=8, ystart=44, mx=-8, my=2 },
248 ["BOTTOMLEFTTOPLEFT"] = { xoff=0, yoff=4, xdir=1, ydir=-1, xstart=8, ystart=-8, mx=-8, my=-2 } }
249  
250 -- returns info depending on current docking. ie: dock_info("xoff")
251 local function dock_info(which)
252  
253 local anchor = ItemRack.MainDock..ItemRack.MenuDock
254  
255 if dock_stats[anchor] and which and dock_stats[anchor][which] then
256 return dock_stats[anchor][which]
257 else
258 return 0
259 end
260 end
261  
262 -- returns info depending on where the window is currently
263 -- since frame scales and positions can be nil at the most inconvenient times, it approximates
264 -- its corner based on settings and makes no assumptions that even UIParent exists
265 -- no argument : return corner window is in
266 -- "LEFTRIGHT" : return "LEFT" or "RIGHT"
267 -- "TOPBOTTOM" : return "TOP" or "BOTTOM"
268 local function corner_info(which)
269  
270 local length,cx,cy,xpoint,ypoint
271 local vertside,horzside = "TOP","LEFT"
272 local info
273  
274 if table.getn(ItemRack_Users[user].Bar)>0 then
275 length = table.getn(ItemRack_Users[user].Bar)*40 + 32
276 if ItemRack_Users[user].MainOrient=="HORIZONTAL" then
277 cx = length
278 cy = 55
279 else
280 cx = 55
281 cy = length
282 end
283 xpoint = cx/2+(ItemRack_Users[user].XPos*ItemRack_Users[user].MainScale)
284 ypoint = (ItemRack_Users[user].YPos*ItemRack_Users[user].MainScale)-cy/2
285  
286 if xpoint<(UIParent and UIParent:GetWidth()/2 or 512) then
287 horzside = "LEFT"
288 else
289 horzside = "RIGHT"
290 end
291  
292 if ypoint<(UIParent and UIParent:GetHeight()/2 or 386) then
293 vertside = "BOTTOM"
294 else
295 vertside = "TOP"
296 end
297 end
298  
299 if which=="LEFTRIGHT" then
300 info = horzside
301 elseif which=="TOPBOTTOM" then
302 info = vertside
303 else
304 info = vertside..horzside
305 end
306  
307 return info
308  
309 end
310  
311 local inv_dock = {
312 [0] = { orient="HORIZONTAL", maindock="BOTTOMLEFT", menudock="TOPLEFT" },
313 [1] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" },
314 [2] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" },
315 [3] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" },
316 [4] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" },
317 [5] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" },
318 [6] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
319 [7] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
320 [8] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
321 [9] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" },
322 [10] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
323 [11] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
324 [12] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
325 [13] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
326 [14] = { orient="VERTICAL", maindock="TOPRIGHT", menudock="TOPLEFT" },
327 [15] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" },
328 [16] = { orient="HORIZONTAL", maindock="BOTTOMLEFT", menudock="TOPLEFT" },
329 [17] = { orient="HORIZONTAL", maindock="BOTTOMLEFT", menudock="TOPLEFT" },
330 [18] = { orient="HORIZONTAL", maindock="BOTTOMLEFT", menudock="TOPLEFT" },
331 [19] = { orient="VERTICAL", maindock="TOPLEFT", menudock="TOPRIGHT" }
332 }
333  
334 -- places the menu against the invslot
335 -- setframe = true when docking to set frame
336 function ItemRack_DockMenu(invslot,relativeTo)
337  
338 local attachTo = ((not relativeTo) and "ItemRackInv"..invslot) or (relativeTo=="TITAN" and "TitanPanelItemRackButton") or (relativeTo=="SET" and "ItemRack_Sets_Inv"..invslot) or (relativeTo=="MINIMAP" and "ItemRack_IconFrame") or ItemRack.Indexes[invslot].paperdoll_slot
339 local item = getglobal(attachTo)
340 local noflip = ItemRack_Settings.FlipMenu=="OFF"
341 local corner=corner_info()
342 local mainorient = ItemRack_Users[user].MainOrient
343 local ynudge -- amount if any to nudge the y offset
344  
345 if relativeTo then
346 ItemRack.MenuDockedTo = relativeTo
347 end
348  
349 if relativeTo=="MINIMAP" then
350 if (ItemRack_IconFrame:GetTop() or 0)<(UIParent:GetHeight() or 0)/2 then
351 ItemRack.MainDock = "TOPLEFT"
352 ItemRack.MenuDock = "BOTTOMLEFT"
353 ynudge = -12
354 else
355 ItemRack.MainDock = "BOTTOMRIGHT"
356 ItemRack.MenuDock = "TOPRIGHT"
357 ynudge = 12
358 end
359 elseif relativeTo=="TITAN" then
360 local xpos, ypos = GetCursorPosition()
361 if ypos<400 then
362 ItemRack.MainDock = "TOPLEFT"
363 ItemRack.MenuDock = "BOTTOMLEFT"
364 ynudge = 0
365 else
366 ItemRack.MainDock = "BOTTOMLEFT"
367 ItemRack.MenuDock = "TOPLEFT"
368 ynudge = 0
369 end
370 elseif mainorient=="HORIZONTAL" then
371 if corner=="BOTTOMLEFT" then
372 ItemRack.MainDock = noflip and "TOPLEFT" or "BOTTOMLEFT"
373 ItemRack.MenuDock = noflip and "BOTTOMLEFT" or "TOPLEFT"
374 elseif corner=="BOTTOMRIGHT" then
375 ItemRack.MainDock = noflip and "TOPRIGHT" or "BOTTOMRIGHT"
376 ItemRack.MenuDock = noflip and "BOTTOMRIGHT" or "TOPRIGHT"
377 elseif corner=="TOPLEFT" then
378 ItemRack.MainDock = noflip and "BOTTOMLEFT" or "TOPLEFT"
379 ItemRack.MenuDock = noflip and "TOPLEFT" or "BOTTOMLEFT"
380 else -- "TOPRIGHT"
381 ItemRack.MainDock = noflip and "BOTTOMRIGHT" or "TOPRIGHT"
382 ItemRack.MenuDock = noflip and "TOPRIGHT" or "BOTTOMRIGHT"
383 end
384 else
385 if corner=="BOTTOMLEFT" then
386 ItemRack.MainDock = noflip and "BOTTOMRIGHT" or "BOTTOMLEFT"
387 ItemRack.MenuDock = noflip and "BOTTOMLEFT" or "BOTTOMRIGHT"
388 elseif corner=="BOTTOMRIGHT" then
389 ItemRack.MainDock = noflip and "BOTTOMLEFT" or "BOTTOMRIGHT"
390 ItemRack.MenuDock = noflip and "BOTTOMRIGHT" or "BOTTOMLEFT"
391 elseif corner=="TOPLEFT" then
392 ItemRack.MainDock = noflip and "TOPRIGHT" or "TOPLEFT"
393 ItemRack.MenuDock = noflip and "TOPLEFT" or "TOPRIGHT"
394 else -- "TOPRIGHT"
395 ItemRack.MainDock = noflip and "TOPLEFT" or "TOPRIGHT"
396 ItemRack.MenuDock = noflip and "TOPRIGHT" or "TOPLEFT"
397 end
398 end
399  
400 if relativeTo=="SET" then
401 ItemRack_MenuFrame:SetScale(ItemRack_SetsFrame:GetScale())
402 ItemRack.MainDock = inv_dock[invslot].maindock
403 ItemRack.MenuDock = inv_dock[invslot].menudock
404 elseif relativeTo=="CHARACTERSHEET" then
405 ItemRack_MenuFrame:SetScale(getglobal(ItemRack.Indexes[1].paperdoll_slot):GetScale())
406 ItemRack.MainDock = inv_dock[invslot].maindock
407 ItemRack.MenuDock = inv_dock[invslot].menudock
408 if ItemRack.MainDock == "TOPLEFT" then
409 -- horizontal menus always go to right on character sheet
410 ItemRack.MainDock = "TOPRIGHT"
411 ItemRack.MenuDock = "TOPLEFT"
412 end
413 else
414 ItemRack_MenuFrame:SetScale(ItemRack_Users[user].MainScale)
415 end
416  
417 ItemRack_MenuFrame:ClearAllPoints()
418 ItemRack_MenuFrame:SetPoint(ItemRack.MenuDock,attachTo,ItemRack.MainDock,dock_info("mx"),dock_info("my")+ (ynudge and ynudge or 0))
419 end
420  
421 -- v1="Left1" or "Right1" up to "Left30" or "Right30"
422 local function is_red(v1)
423  
424 local its_red,r,g,b = false
425  
426 r,g,b = getglobal("Rack_TooltipScanText"..v1):GetTextColor()
427 if r>.9 and g<.2 and b<.2 then
428 its_red = true
429 end
430  
431 return its_red
432 end
433  
434 -- returns true if the player can wear this item (no red text on its tooltip)
435 -- separated from get_item_info because tooltip scanning should be done only at utmost need
436 local function player_can_wear(bag,slot,invslot)
437  
438 local found,i,txt = false
439  
440 for i=2,15 do
441 -- ClearLines doesn't remove colors, manually remove them
442 getglobal("Rack_TooltipScanTextLeft"..i):SetTextColor(0,0,0)
443 getglobal("Rack_TooltipScanTextRight"..i):SetTextColor(0,0,0)
444 end
445 Rack_TooltipScan:SetBagItem(bag,slot)
446  
447 for i=2,15 do
448 txt = getglobal("Rack_TooltipScanTextLeft"..i):GetText()
449 -- if either left or right text is red and this isn't a Durability x/x line, this item can't be worn
450 if (is_red("Left"..i) or is_red("Right"..i)) and not string.find(txt,durability_pattern) and not string.find(txt,"^Requires") then
451 found = true
452 end
453 end
454  
455 local _,_,_,itemType = Rack.GetItemInfo(bag,slot)
456 if itemType=="INVTYPE_WEAPON" and invslot==17 and not ItemRack.CanWearOneHandOffHand then
457 found = true
458 end
459  
460 return not found
461 end
462  
463 -- the old central info gatherer, now a wrapper to Rack.GetItemInfo
464 local function get_item_info(bag,slot)
465  
466 local texture,name,equipslot,soulbound,count
467  
468 if bag==20 then
469 -- if querying set slot, return current set texture and name
470 name = Rack.CurrentSet()
471 texture = "Interface\\AddOns\\ItemRack\\ItemRack-Icon"
472 if name and Rack_User[user].Sets[name] and not string.find(name,"^ItemRack") and not string.find(name,"^Rack-") then
473 texture = Rack_User[user].Sets[name].icon
474 else
475 name = nil
476 end
477 return texture,name
478 end
479  
480 texture,_,name,equipslot = Rack.GetItemInfo(bag,slot)
481 if slot then
482 _,count = GetContainerItemInfo(bag,slot)
483 end
484 if ItemRack_Settings.Soulbound=="ON" and name then
485 local text
486 if slot then
487 Rack_TooltipScan:SetBagItem(bag,slot)
488 else
489 Rack_TooltipScan:SetInventoryItem("player",bag)
490 end
491 for i=2,5 do
492 text = getglobal("Rack_TooltipScanTextLeft"..i):GetText() or ""
493 if text==ITEM_SOULBOUND or text==ITEM_BIND_QUEST or text==ITEM_CONJURED then
494 soulbound = true
495 end
496 end
497 end
498  
499 return texture,name,equipslot,soulbound,count
500 end
501  
502 local function cursor_empty()
503 return not (CursorHasItem() or CursorHasMoney() or CursorHasSpell())
504 end
505  
506 -- updates cooldown spinners in the menu
507 local function update_menu_cooldowns()
508  
509 local start, duration, enable
510  
511 if ItemRack.InvOpen then
512 for i=1,ItemRack.NumberOfItems do
513 if ItemRack.BaggedItems[i].bag then
514 start, duration, enable = GetContainerItemCooldown(ItemRack.BaggedItems[i].bag,ItemRack.BaggedItems[i].slot)
515 CooldownFrame_SetTimer(getglobal("ItemRackMenu"..i.."Cooldown"), start, duration, enable)
516 else
517 getglobal("ItemRackMenu"..i.."Time"):SetText("")
518 end
519 end
520 end
521 end
522  
523 -- updates cooldown spinners in the main bar
524 local function update_inv_cooldowns()
525  
526 local i, start, duration, enable
527  
528 if table.getn(ItemRack_Users[user].Bar)>0 then
529 for i=1,table.getn(ItemRack_Users[user].Bar) do
530 start, duration, enable = GetInventoryItemCooldown("player",ItemRack_Users[user].Bar[i])
531 CooldownFrame_SetTimer(getglobal("ItemRackInv"..ItemRack_Users[user].Bar[i].."Cooldown"), start, duration, enable)
532 end
533 end
534  
535 update_menu_cooldowns()
536 end
537  
538 -- call this when window has changed and cooldowns need redrawn
539 local function cooldowns_need_updating()
540 Rack.StartTimer("CooldownUpdate",.25)
541 ItemRack.CooldownsNeedUpdating = true
542 end
543  
544 local function populate_baggeditems(idx,bag,slot,name,texture)
545  
546 if not ItemRack.BaggedItems[idx] then
547 ItemRack.BaggedItems[idx] = {}
548 end
549 ItemRack.BaggedItems[idx].bag = bag
550 ItemRack.BaggedItems[idx].slot = slot
551 ItemRack.BaggedItems[idx].name = name
552 ItemRack.BaggedItems[idx].texture = texture
553 end
554  
555 -- to minimize garbage creation, tables are manipulated by copying values instead of tables
556 local function copy_baggeditems(source,dest)
557  
558 if not ItemRack.BaggedItems[dest] then
559 ItemRack.BaggedItems[dest] = {}
560 end
561 ItemRack.BaggedItems[dest].bag = ItemRack.BaggedItems[source].bag
562 ItemRack.BaggedItems[dest].slot = ItemRack.BaggedItems[source].slot
563 ItemRack.BaggedItems[dest].name = ItemRack.BaggedItems[source].name
564 ItemRack.BaggedItems[dest].texture = ItemRack.BaggedItems[source].texture
565 end
566  
567 -- sorts menu up to stop_point, which is idx+1 usually (sort uses stop_point as a temp spot for swapping)
568 local function sort_menu(stop_point)
569  
570 local done,i=false
571  
572 if stop_point>2 then
573 while not done do
574 done = true
575 for i=1,stop_point-2 do
576 if ItemRack.BaggedItems[i].name > ItemRack.BaggedItems[i+1].name then
577 copy_baggeditems(i,stop_point)
578 copy_baggeditems(i+1,i)
579 copy_baggeditems(stop_point,i+1)
580  
581 done = false
582 end
583 end
584 end
585 end
586  
587 end
588  
589 function ItemRack_CurrentSet()
590 return Rack.CurrentSet()
591 end
592  
593 -- builds a menu outward from invslot (0-19)
594 -- setframe = true if this is to dock to the set frame
595 function ItemRack_BuildMenu(invslot,relativeTo)
596  
597 local idx,i,j,k,item,texture,name,equipslot,soulbound,found = 1
598 local mainorient = ItemRack_Users[user].MainOrient
599 local bagStart,bagEnd = 0,4
600  
601 if relativeTo=="SET" or relativeTo=="CHARACTERSHEET" then
602 -- if displaying to a set, then
603 mainorient = "VERTICAL"
604 if invslot==0 or invslot==16 or invslot==17 or invslot==18 then
605 mainorient = "HORIZONTAL"
606 end
607 elseif relativeTo=="MINIMAP" then
608 mainorient = "HORIZONTAL"
609 elseif relativeTo=="TITAN" then
610 mainorient = "HORIZONTAL"
611 end
612 ItemRack_DockMenu(invslot,relativeTo)
613  
614 for i=1,table.getn(ItemRack_Users[user].Bar) do
615 if invslot~=ItemRack_Users[user].Bar[i] then
616 getglobal("ItemRackInv"..ItemRack_Users[user].Bar[i]):UnlockHighlight()
617 else
618 getglobal("ItemRackInv"..ItemRack_Users[user].Bar[i]):LockHighlight()
619 end
620 end
621  
622 if invslot==0 then
623 -- if this is an ammo slot, clear totals
624 for i in ItemRack.AmmoCounts do
625 ItemRack.AmmoCounts[i] = 0
626 end
627 end
628  
629 if invslot<20 then
630  
631 if ItemRack.BankIsOpen then
632 bagStart,bagEnd = -1,10
633 end
634  
635 -- go through bags and gather items into .BaggedItems
636 for i=bagStart,bagEnd do
637 for j=1,GetContainerNumSlots(i) do
638 texture,name,equipslot,soulbound,count = get_item_info(i,j)
639 soulbound = soulbound or ItemRack.Indexes[invslot].ignore_soulbound -- pretend item soulbound if flagged to ignore_soulbound
640 if (equipslot and ItemRack.Indexes[invslot][equipslot]) and (soulbound or ItemRack_Settings.Soulbound=="OFF") then
641 if ItemRack_Settings.AllowHidden=="ON" and ItemRack_Users[user].Ignore[name] and not IsAltKeyDown() then
642 -- skip items that are on ignore list
643 elseif player_can_wear(i,j,invslot) then
644 if invslot==0 and count then
645 -- if this is an ammo slot menu
646 ItemRack.AmmoCounts[name] = (ItemRack.AmmoCounts[name] or 0) + count
647 found = false
648 for k=1,(idx-1) do
649 if ItemRack.BaggedItems[k].name==name then
650 found=true
651 end
652 end
653 if not found then
654 populate_baggeditems(idx,i,j,name,texture)
655 idx = idx + 1
656 end
657 else
658 populate_baggeditems(idx,i,j,name,texture)
659 idx = idx + 1
660 end
661 end
662 end
663 end
664 end
665 sort_menu(idx)
666  
667 if ItemRack_Settings.ShowEmpty=="ON" and GetInventoryItemLink("player",invslot) and not (ItemRack_Settings.RightClick=="ON" and (invslot==13 or invslot==14)) then
668 -- add an empty slot to the menu
669 _,_,i = string.find(ItemRack.Indexes[invslot].keybind,"Use (.+) Item")
670 _,j = GetInventorySlotInfo(string.gsub(ItemRack.Indexes[invslot].paperdoll_slot,"Character",""))
671 populate_baggeditems(idx,nil,nil,"(empty)",j)
672 idx = idx + 1
673 end
674  
675 else
676 -- this is a menu for sets
677 -- go through sets and gather them into .BaggedItems
678 for i in Rack_User[user].Sets do
679 if not string.find(i,"^ItemRack") and not string.find(i,"^Rack-") and (not Rack_User[user].Sets[i].hide or IsAltKeyDown()) then
680 populate_baggeditems(idx,nil,nil,i,Rack_User[user].Sets[i].icon)
681 idx = idx + 1
682 end
683 end
684 sort_menu(idx)
685 end
686  
687 ItemRack.NumberOfItems = math.min(idx-1,ItemRack.MaxItems)
688  
689 if ItemRack.NumberOfItems<1 then
690 -- user has no bagged items for this type
691 ItemRack_MenuFrame:Hide()
692 else
693 -- display items outward from docking point
694 local col,row,xpos,ypos = 0,0,dock_info("xstart"),dock_info("ystart")
695 local max_cols = 1
696  
697 if ItemRack.NumberOfItems>24 then
698 max_cols = 5
699 elseif ItemRack.NumberOfItems>18 then
700 max_cols = 4
701 elseif ItemRack.NumberOfItems>12 then
702 max_cols = 3
703 elseif ItemRack.NumberOfItems>4 then
704 max_cols = 2
705 end
706  
707 for i=1,ItemRack.NumberOfItems do
708  
709 local item = getglobal("ItemRackMenu"..i.."Icon")
710 item:SetTexture(ItemRack.BaggedItems[i].texture)
711 -- grey menu item if it's on the ignore list (ALT key is down if it made it to BaggedItems)
712 if ItemRack_Settings.AllowHidden=="ON" and (ItemRack_Users[user].Ignore[ItemRack.BaggedItems[i].name] or (Rack_User[user].Sets[ItemRack.BaggedItems[i].name] and Rack_User[user].Sets[ItemRack.BaggedItems[i].name].hide)) then
713 SetDesaturation(item,1)
714 else
715 SetDesaturation(item,nil)
716 end
717  
718 local item = getglobal("ItemRackMenu"..i)
719 item:SetPoint("TOPLEFT","ItemRack_MenuFrame",ItemRack.MenuDock,xpos,ypos)
720  
721 if (mainorient=="HORIZONTAL" and ItemRack_Settings.RotateMenu=="OFF") or (mainorient=="VERTICAL" and ItemRack_Settings.RotateMenu=="ON") then
722 xpos = xpos + dock_info("xdir")*40
723 col = col + 1
724 if col==max_cols then
725 xpos = dock_info("xstart")
726 col = 0
727 ypos = ypos + dock_info("ydir")*40
728 row = row + 1
729 end
730 item:Show()
731 else
732 ypos = ypos + dock_info("ydir")*40
733 col = col + 1
734 if col==max_cols then
735 ypos = dock_info("ystart")
736 col = 0
737 xpos = xpos + dock_info("xdir")*40
738 row = row + 1
739 end
740 item:Show()
741 end
742 end
743 for i=(ItemRack.NumberOfItems+1),ItemRack.MaxItems do
744 getglobal("ItemRackMenu"..i):Hide()
745 end
746 if col==0 then
747 row = row-1
748 end
749  
750 if (mainorient=="HORIZONTAL" and ItemRack_Settings.RotateMenu=="OFF") or (mainorient=="VERTICAL" and ItemRack_Settings.RotateMenu=="ON") then
751 ItemRack_MenuFrame:SetWidth(12+(max_cols*40))
752 ItemRack_MenuFrame:SetHeight(12+((row+1)*40))
753 else
754 ItemRack_MenuFrame:SetWidth(12+((row+1)*40))
755 ItemRack_MenuFrame:SetHeight(12+(max_cols*40))
756 end
757  
758 -- apply slot-dependant overlays, ammo count, set name and key bindings
759 if invslot==0 then -- if this is an ammo slot, show counts
760 for i=1,ItemRack.NumberOfItems do
761 if ItemRack.AmmoCounts[ItemRack.BaggedItems[i].name] then
762 getglobal("ItemRackMenu"..i.."Count"):SetText(ItemRack.AmmoCounts[ItemRack.BaggedItems[i].name])
763 end
764 end
765 elseif invslot==20 then -- if this is a set slot, show names and bindings
766 for i=1,ItemRack.NumberOfItems do
767 name = ItemRack.BaggedItems[i].name
768 if ItemRack.BankIsOpen and Rack.SetHasBanked(name) then
769 getglobal("ItemRackMenu"..i.."Border"):Show()
770 getglobal("ItemRackMenu"..i.."Icon"):SetVertexColor(.5,.5,.5)
771 else
772 getglobal("ItemRackMenu"..i.."Border"):Hide()
773 getglobal("ItemRackMenu"..i.."Icon"):SetVertexColor(1,1,1)
774 end
775  
776 item = getglobal("ItemRackMenu"..i.."Name")
777 if ItemRack_Settings.SetLabels=="ON" then
778 item:SetText(name)
779 item:Show()
780 else
781 item:Hide()
782 end
783 item = getglobal("ItemRackMenu"..i.."HotKey")
784 if Rack_User[user].Sets[name].key and ItemRack_Settings.Bindings=="ON" then
785 _,_,j,k = string.find(Rack_User[user].Sets[name].key or "","(.).+(-.)")
786 item:SetText((j or "")..(k or ""))
787 item:Show()
788 else
789 item:Hide()
790 end
791 end
792 else -- normal slot (1-19) has no overlays
793 for i=1,ItemRack.NumberOfItems do
794 getglobal("ItemRackMenu"..i.."Name"):SetText("")
795 getglobal("ItemRackMenu"..i.."Count"):SetText("")
796 getglobal("ItemRackMenu"..i.."HotKey"):SetText("")
797  
798 if ItemRack.BankedItems[ItemRack.BaggedItems[i].name] then
799 getglobal("ItemRackMenu"..i.."Border"):Show()
800 getglobal("ItemRackMenu"..i.."Icon"):SetVertexColor(.5,.5,.5)
801 else
802 getglobal("ItemRackMenu"..i.."Border"):Hide()
803 getglobal("ItemRackMenu"..i.."Icon"):SetVertexColor(1,1,1)
804 end
805 end
806 end
807  
808 ItemRack.InvOpen = invslot
809 ItemRack_MenuFrame:Show()
810 update_menu_cooldowns()
811 Rack.StartTimer("CooldownUpdate",0) -- immediate cooldown update
812 Rack.StartTimer("MenuFrame")
813  
814 end
815 end
816  
817 -- for use with main/menu frames with UIParent parent when relocated by the mod, to register for layout-cache.txt
818 local function really_setpoint(frame,point,relativeTo,relativePoint,xoff,yoff)
819 frame:SetPoint(point,relativeTo,relativePoint,xoff,yoff)
820 ItemRack_Users[user].XPos = xoff
821 ItemRack_Users[user].YPos = yoff
822 end
823  
824 -- updates the image inside the minimap button depending on current set and disable toggle option ("Minimap set menu")
825 local function draw_minimap_icon()
826 local setname = Rack.CurrentSet()
827  
828 if setname and Rack_User[user].Sets[setname] and ItemRack_Settings.DisableToggle=="ON" then
829 ItemRack_IconFrame_Icon:SetTexture(Rack_User[user].Sets[setname].icon)
830 else
831 ItemRack_IconFrame_Icon:SetTexture("Interface\\AddOns\\ItemRack\\ItemRack-Icon")
832 end
833 end
834  
835 -- draws the inventory bar
836 local function draw_inv()
837  
838 local oldx = ItemRack_InvFrame:GetLeft() or ItemRack_Users[user].XPos
839 local oldy = ItemRack_InvFrame:GetTop() or ItemRack_Users[user].YPos
840 local oldcx = ItemRack_InvFrame:GetWidth() or 0
841 local oldcy = ItemRack_InvFrame:GetHeight() or 0
842 local bar = ItemRack_Users[user].Bar
843  
844 if not oldx or not oldy then
845 return -- frame isn't fully defined yet, leave now
846 end
847  
848 -- for a left-to-right horizontal configuration
849 local cx,cy,i,item,texture,xspacer,yspacer = 56,56
850  
851 if ItemRack_Users[user].MainOrient=="HORIZONTAL" then
852 -- horizontal from left to right
853 xdir,ydir,corner,cornerTo,cornerStart,xdirStart,ydirStart,xadd,yadd = 4,0,"TOPRIGHT","TOPLEFT","TOPLEFT",10,-10,40,0
854  
855 if ItemRack_Settings.FlipBar=="ON" then
856 -- horizontal from right to left
857 xdir,ydir,corner,cornerTo,cornerStart,xdirStart,ydirStart,xadd,yadd = -4,0,"TOPLEFT","TOPRIGHT","TOPRIGHT",-10,-10,-40,0
858 end
859  
860 else
861 -- vertical from top to bottom
862 xdir,ydir,corner,cornerTo,cornerStart,xdirStart,ydirStart,xadd,yadd = 0,-4,"BOTTOMLEFT","TOPLEFT","TOPLEFT",10,-10,0,40
863  
864 if ItemRack_Settings.FlipBar=="ON" then
865 -- vertical from bottom to top
866 xdir,ydir,corner,cornerTo,cornerStart,xdirStart,ydirStart,xadd,yadd = 0,4,"TOPLEFT","BOTTOMLEFT","BOTTOMLEFT",10,10,0,-40
867 end
868 end
869  
870 for i=0,20 do
871 getglobal("ItemRackInv"..i):Hide()
872 end
873 ItemRack.TrinketsPaired = false -- changes to true if two trinkets are beside each other
874  
875 if table.getn(bar)>0 then
876 item = getglobal("ItemRackInv"..bar[1])
877 item:ClearAllPoints()
878 item:SetPoint(cornerStart,"ItemRack_InvFrame",cornerStart,xdirStart,ydirStart)
879 getglobal("ItemRackInv"..bar[1].."Icon"):SetTexture(get_item_info(bar[1]))
880 item:Show()
881 if ItemRack_Settings.RightClick=="ON" and (bar[1]==13 and bar[2]==14) then
882 ItemRack.TrinketsPaired = true
883 end
884 for i=2,table.getn(bar) do
885 xspacer,yspacer = 0,0
886 if ItemRack_Settings.RightClick=="ON" and ((bar[i]==13 and bar[i+1] and bar[i+1]==14) or
887 (bar[i-1]==14 and bar[i-2] and bar[i-2]==13)) then
888 ItemRack.TrinketsPaired = true
889 end
890 if ItemRack_Users[user].Spaces[bar[i-1]] then
891 xspacer = xdir*2
892 yspacer = ydir*2
893 end
894 item = getglobal("ItemRackInv"..bar[i])
895 item:ClearAllPoints()
896 item:SetPoint(cornerTo,"ItemRackInv"..bar[i-1],corner,xdir+xspacer,ydir+yspacer)
897 getglobal("ItemRackInv"..bar[i].."Icon"):SetTexture(get_item_info(bar[i]))
898 item:Show()
899 cx = cx + math.abs(xadd) + math.abs(xspacer)
900 cy = cy + math.abs(yadd) + math.abs(yspacer) -- was minus yspacer
901 end
902 ItemRack_InvFrame:SetWidth(cx)
903 ItemRack_InvFrame:SetHeight(cy)
904  
905 if ItemRack_Settings.FlipBar=="ON" and oldcx>32 and oldcy>32 then
906 -- if bar size changed (after being drawn before), and we're flipped, we need to shift it over
907 ItemRack_InvFrame:ClearAllPoints()
908 if ItemRack_Users[user].MainOrient=="HORIZONTAL" then
909 oldx = oldx + (oldcx-cx)
910 else
911 oldy = oldy + (cy-oldcy)
912 end
913 really_setpoint(ItemRack_InvFrame,"TOPLEFT","UIParent","BOTTOMLEFT",oldx,oldy)
914 end
915  
916 if ItemRack_Users[user].Visible~="OFF" then
917 ItemRack_InvFrame:Show()
918 end
919 cooldowns_need_updating()
920 Rack.StartTimer("CooldownUpdate",0)
921  
922 if ItemRack_Settings.SetLabels=="ON" then
923 local currentset = Rack.CurrentSet()
924 if currentset and Rack_User[user].Sets[currentset] then
925 ItemRackInv20Name:SetText(currentset)
926 else
927 ItemRackInv20Name:SetText(ItemRackText.EMPTYSET)
928 end
929 else
930 ItemRackInv20Name:SetText("")
931 end
932 else
933 ItemRack_Users[user].Visible="OFF"
934 ItemRack_InvFrame:Hide()
935 end
936 draw_minimap_icon()
937  
938 if ItemRack_UpdatePlugins then
939 -- update plugin if it exists
940 local setname = Rack.CurrentSet()
941 if setname and ItemRack_Users[user].Sets[setname] then
942 ItemRack_UpdatePlugins(setname,ItemRack_Users[user].Sets[setname].icon)
943 else
944 ItemRack_UpdatePlugins(nil,"Interface\\AddOns\\ItemRack\\ItemRack-Icon")
945 end
946 end
947  
948 end
949  
950 local function unlocked()
951 return ItemRack_Users[user].Locked~="ON"
952 end
953  
954 -- sets window lock "ON" or "OFF"
955 local function set_lock(arg1)
956  
957 ItemRack_Users[user].Locked = arg1
958  
959 if arg1=="ON" then
960 ItemRack_InvFrame:SetBackdropColor(0,0,0,0)
961 ItemRack_InvFrame:SetBackdropBorderColor(0,0,0,0)
962 ItemRack_InvFrame_Resize:Hide()
963 ItemRack_Control_Rotate:SetAlpha(.4)
964 ItemRack_Control_Rotate:Disable()
965 else
966 ItemRack_InvFrame:SetBackdropColor(1,1,1,1)
967 ItemRack_InvFrame:SetBackdropBorderColor(1,1,1,1)
968 ItemRack_InvFrame_Resize:Show()
969 ItemRack_Control_Rotate:SetAlpha(1)
970 ItemRack_Control_Rotate:Enable()
971 ItemRack_ControlFrame:Show()
972 Rack.StartTimer("ControlFrame")
973 end
974 end
975  
976 -- called at startup, UPDATE_BINDINGS and option change to show/hide key bindings
977 local function update_keybindings()
978  
979 local i,modifier,key,text
980  
981 -- update bindings for inventory slots
982 for i=0,19 do
983 if ItemRack.Indexes[i].keybind and ItemRack_Settings.Bindings=="ON" then
984 text = GetBindingKey(ItemRack.Indexes[i].keybind)
985 _,_,modifier,key = string.find(text or "","(.).+(-.)")
986 if modifier and key then
987 text = modifier..key
988 end
989 getglobal("ItemRackInv"..i.."HotKey"):SetText(text)
990 else
991 getglobal("ItemRackInv"..i.."HotKey"):SetText("")
992 end
993 end
994  
995 -- update bindings for items on the rack
996 ItemRack_AgreeOnKeyBindings()
997  
998 end
999  
1000 local function move_control()
1001  
1002 local item = ItemRack_InvFrame
1003  
1004 ItemRack_Control_Rotate:ClearAllPoints()
1005 ItemRack_Control_Lock:ClearAllPoints()
1006 ItemRack_Control_Options:ClearAllPoints()
1007  
1008 if ItemRack_Users[user].MainOrient=="HORIZONTAL" then
1009  
1010 if ItemRack_Settings.FlipBar=="OFF" then
1011 ItemRack_Control_Rotate:SetPoint("TOPLEFT","ItemRack_InvFrame","TOPRIGHT",-2,-3)
1012 else
1013 ItemRack_Control_Rotate:SetPoint("TOPRIGHT","ItemRack_InvFrame","TOPLEFT",2,-3)
1014 end
1015 ItemRack_Control_Lock:SetPoint("TOPLEFT","ItemRack_Control_Rotate","BOTTOMLEFT")
1016 ItemRack_Control_Options:SetPoint("TOPLEFT","ItemRack_Control_Lock","BOTTOMLEFT")
1017 else
1018 if ItemRack_Settings.FlipBar=="OFF" then
1019 ItemRack_Control_Rotate:SetPoint("BOTTOMLEFT","ItemRack_InvFrame","TOPLEFT",3,-2)
1020 else
1021 ItemRack_Control_Rotate:SetPoint("TOPLEFT","ItemRack_InvFrame","BOTTOMLEFT",3,2)
1022 end
1023 ItemRack_Control_Lock:SetPoint("TOPLEFT","ItemRack_Control_Rotate","TOPRIGHT")
1024 ItemRack_Control_Options:SetPoint("TOPLEFT","ItemRack_Control_Lock","TOPRIGHT")
1025 end
1026 end
1027  
1028 -- moves the minimap icon to last position in settings or default angle of 45
1029 local function move_icon()
1030  
1031 local xpos,ypos
1032 local angle = ItemRack_Settings.IconPos or 0
1033  
1034 if ItemRack_Settings.SquareMinimap=="ON" then
1035 -- brute force method until trig solution figured out - min/max a point on a circle beyond square
1036 xpos = 110 * cos(angle)
1037 ypos = 110 * sin(angle)
1038 xpos = math.max(-82,math.min(xpos,84))
1039 ypos = math.max(-86,math.min(ypos,82))
1040 else
1041 xpos = 80*cos(angle)
1042 ypos = 80*sin(angle)
1043 end
1044  
1045 ItemRack_IconFrame:SetPoint("TOPLEFT","Minimap","TOPLEFT",52-xpos,ypos-52)
1046  
1047 end
1048  
1049 local function initialize_data()
1050  
1051 -- if EquipSet isn't defined by some other mod, use it as an alias for ItemRack_EquipSet, since macros are limited by space
1052 EquipSet = EquipSet or ItemRack_EquipSet -- ItemRack_EquipSet
1053 SaveSet = SaveSet or ItemRack_SaveSet
1054 LoadSet = LoadSet or ItemRack_LoadSet
1055 ToggleSet = ToggleSet or ItemRack_ToggleSet
1056 IsSetEquipped = IsSetEquipped or ItemRack_IsSetEquipped
1057  
1058 -- create new user if one doesn't exist
1059 if not ItemRack_Users[user] then
1060 ItemRack_Users[user] = {} -- create new per-user setting
1061 ItemRack_Users[user].Inv = {} -- nil or 1, whether inv slot visible
1062 ItemRack_Users[user].Bar = {} -- 1-number of inventory bars on screen at once
1063 ItemRack_Users[user].Spaces = {} -- nil or true, whether a space should appear after this slot
1064 ItemRack_Users[user].Ignore = {} -- list of items to ignore on bar
1065 for i in ItemRackOpt_Defaults do
1066 ItemRack_Users[user][i] = ItemRackOpt_Defaults[i]
1067 end
1068 end
1069 -- upgrade old version data
1070 ItemRack_Settings.ShowEmpty = ItemRack_Settings.ShowEmpty or "OFF" -- 1.1
1071 ItemRack_Settings.FlipMenu = ItemRack_Settings.FlipMenu or "OFF" -- 1.1
1072 ItemRack_Settings.RightClick = ItemRack_Settings.RightClick or "OFF" -- 1.1
1073 ItemRack_Settings.TinyTooltip = ItemRack_Settings.TinyTooltip or "OFF" -- 1.2
1074 ItemRack_Settings.ShowTooltips = ItemRack_Settings.ShowTooltips or "ON" -- 1.2
1075 ItemRack_Settings.RotateMenu = ItemRack_Settings.RotateMenu or "OFF" -- 1.3
1076 ItemRack_Users[user].Spaces = ItemRack_Users[user].Spaces or {} -- 1.3
1077 ItemRack_Users[user].Sets = ItemRack_Users[user].Sets or {} -- 1.4
1078 ItemRack_Settings.ShowIcon = ItemRack_Settings.ShowIcon or "ON" -- 1.4
1079 ItemRack_Settings.DisableToggle = ItemRack_Settings.DisableToggle or "ON" -- 1.4
1080 ItemRack_Settings.FlipBar = ItemRack_Settings.FlipBar or "OFF" -- 1.5
1081 ItemRack_Users[user].Ignore = ItemRack_Users[user].Ignore or {} -- 1.5
1082 ItemRack_Settings.EnableEvents = ItemRack_Settings.EnableEvents or "OFF" -- 1.5
1083 ItemRack_Users[user].Events = ItemRack_Users[user].Events or {} -- 1.7
1084 ItemRack_Settings.CompactList = ItemRack_Settings.CompactList or "OFF" -- 1.7
1085 ItemRack_Settings.NotifyThirty = ItemRack_Settings.NotifyThirty or "OFF" -- 1.7
1086 ItemRack_Settings.ShowAllEvents = ItemRack_Settings.ShowAllEvents or "OFF" -- 1.7
1087 ItemRack_Settings.AllowHidden = ItemRack_Settings.AllowHidden or "OFF" -- 1.82
1088 ItemRack_Settings.LargeFont = ItemRack_Settings.LargeFont or "OFF" -- 1.9
1089 ItemRack_Settings.SquareMinimap = ItemRack_Settings.SquareMinimap or "OFF" -- 1.9
1090 ItemRack_Settings.BigCooldown = ItemRack_Settings.BigCooldown or "OFF" -- 1.9
1091 ItemRack_Settings.SetLabels = ItemRack_Settings.SetLabels or "ON" -- 1.91
1092 ItemRack_Settings.AutoToggle = ItemRack_Settings.AutoToggle or "OFF" -- 1.91
1093  
1094 local _,class = UnitClass("player")
1095 if class=="WARRIOR" or class=="ROGUE" or class=="HUNTER" then
1096 ItemRack.CanWearOneHandOffHand = 1
1097 end
1098 end
1099  
1100 local function initialize_display()
1101  
1102 -- set scale and position to last saved setting
1103 ItemRack_InvFrame:SetScale(ItemRack_Users[user].MainScale or 1)
1104 ItemRack_MenuFrame:SetScale(ItemRack_Users[user].MainScale or 1)
1105 ItemRack_InvFrame:ClearAllPoints()
1106 really_setpoint(ItemRack_InvFrame,"TOPLEFT","UIParent","BOTTOMLEFT",ItemRack_Users[user].XPos,ItemRack_Users[user].YPos)
1107 set_lock(ItemRack_Users[user].Locked)
1108 update_keybindings()
1109  
1110 ItemRackInv0Count:SetText(CharacterAmmoSlotCount:IsShown() and CharacterAmmoSlotCount:GetText() or "")
1111  
1112 for i in ItemRack.OptInfo do
1113 if ItemRack.OptInfo[i].type=="Check" and getglobal(i) then
1114 item = getglobal(i.."Text")
1115 item:SetText(ItemRack.OptInfo[i].text)
1116 item:SetTextColor(1,1,1)
1117 if ItemRack.OptInfo[i].info and ItemRack_Settings[ItemRack.OptInfo[i].info]=="ON" then
1118 getglobal(i):SetChecked(1)
1119 else
1120 getglobal(i):SetChecked(0)
1121 end
1122 end
1123 if ItemRack.OptInfo[i].type=="Label" then
1124 getglobal(i):SetText(ItemRack.OptInfo[i].text)
1125 end
1126 end
1127  
1128 if ItemRack_Users[user].Visible=="OFF" then
1129 ItemRack_InvFrame:Hide()
1130 end
1131  
1132 move_control() -- docks control buttons on edge of bar
1133 move_icon() -- moves minimap button
1134  
1135 if ItemRack_Settings.ShowIcon=="OFF" then
1136 ItemRack_IconFrame:Hide()
1137 else
1138 ItemRack_IconFrame:Show()
1139 end
1140  
1141 make_escable("ItemRack_SetsFrame","add")
1142  
1143 ItemRack_ChangeEventFont()
1144  
1145 for i=1,30 do
1146 getglobal("ItemRackMenu"..i.."Border"):SetVertexColor(.15,.25,1,1)
1147 getglobal("ItemRackMenu"..i.."Border"):Hide()
1148 end
1149  
1150 draw_inv() -- construct the bar
1151 ItemRack_SetAllCooldownFonts()
1152 Rack.StartTimer("CooldownUpdate")
1153 end
1154  
1155 -- returns true if no swaps are pending
1156 local function all_items_unlocked()
1157  
1158 local i,j,bagged_item_locked,locked
1159 local bagStart,bagEnd = 0,4
1160  
1161 for i=0,19 do
1162 locked = locked or IsInventoryItemLocked(i)
1163 end
1164  
1165 for i=bagStart,bagEnd do
1166 for j=1,GetContainerNumSlots(i) do
1167 _,_,bagged_item_locked = GetContainerItemInfo(i,j)
1168 locked = locked or bagged_item_locked
1169 end
1170 end
1171  
1172 return not locked
1173 end
1174  
1175 -- displays a quick tooltip note under the sets window
1176 local function sets_message(msg)
1177  
1178 local tooltip = ItemRack_Sets_Message
1179  
1180 tooltip:SetOwner(ItemRack_SetsFrame, "ANCHOR_NONE")
1181 tooltip:SetPoint("TOP", "ItemRack_SetsFrame", "BOTTOM", 0, 0)
1182 tooltip:AddLine(msg)
1183 tooltip:Show()
1184 tooltip:FadeOut()
1185 end
1186  
1187  
1188 --[[ Frame functions ]]--
1189  
1190 function ItemRack_OnLoad()
1191  
1192 -- hook for ALT+click of inventory slots (to add inventory slots to rack)
1193 oldItemRack_PaperDollItemSlotButton_OnClick = PaperDollItemSlotButton_OnClick
1194 PaperDollItemSlotButton_OnClick = newItemRack_PaperDollItemSlotButton_OnClick
1195  
1196 -- hook for ALT+click of character model (to add set slot to rack)
1197 oldItemRack_CharacterModelFrame_OnMouseUp = CharacterModelFrame_OnMouseUp
1198 CharacterModelFrame_OnMouseUp = newItemRack_CharacterModelFrame_OnMouseUp
1199  
1200 -- hook for mouseover of character sheet item slots (to display menu)
1201 oldItemRack_PaperDollItemSlotButton_OnEnter = PaperDollItemSlotButton_OnEnter
1202 PaperDollItemSlotButton_OnEnter = newItemRack_PaperDollItemSlotButton_OnEnter
1203  
1204 -- hook for character sheet hiding (to hide menu)
1205 oldItemRack_PaperDollFrame_OnHide = PaperDollFrame_OnHide
1206 PaperDollFrame_OnHide = newItemRack_PaperDollFrame_OnHide
1207  
1208 oldItemRack_UseInventoryItem = UseInventoryItem
1209 UseInventoryItem = newItemRack_UseInventoryItem
1210  
1211 oldItemRack_UseAction = UseAction
1212 UseAction = newItemRack_UseAction
1213  
1214 this:RegisterEvent("PLAYER_LOGIN")
1215 end
1216  
1217 local function initialize_events(v1)
1218  
1219 local i,j
1220  
1221 ItemRack_DisableAllEvents()
1222  
1223 if v1 then
1224 ItemRack_Events = {}
1225 -- go through and remove all old items from sets
1226 for i in Rack_User do
1227 if Rack_User[i].Sets then
1228 for j in Rack_User[i].Sets do
1229 for k=0,19 do
1230 if Rack_User[i].Sets[j][k] then
1231 Rack_User[i].Sets[j][k].old = nil
1232 end
1233 end
1234 end
1235 end
1236 end
1237 end
1238  
1239 -- if the events doesn't have a version or it's an old events version, load defaults
1240 if not tonumber(ItemRack_Events.events_version) or ItemRack_Events.events_version<current_events_version then
1241 ItemRack_Events.events_version = current_events_version
1242 for i in ItemRack_DefaultEvents do
1243 ItemRack_Events[i] = {}
1244 ItemRack_Events[i].trigger = ItemRack_DefaultEvents[i].trigger
1245 ItemRack_Events[i].delay = ItemRack_DefaultEvents[i].delay
1246 ItemRack_Events[i].script = ItemRack_DefaultEvents[i].script
1247 end
1248 end
1249  
1250 -- if an event is removed, remove the associated sets from the user
1251 for i in ItemRack_Users do
1252 if ItemRack_Users[i].Events then
1253 for j in ItemRack_Users[i].Events do
1254 if not ItemRack_Events[j] then
1255 ItemRack_Users[i].Events[j] = nil
1256 end
1257 end
1258 end
1259 end
1260  
1261 if TITAN_RIDER_ID and (not TitanRider_EquipToggle or (TitanGetVar and TitanGetVar(TITAN_RIDER_ID,"EquipItems"))) then
1262 if ItemRack_Users[user].Events["Mount"] and ItemRack_Users[user].Events["Mount"].enabled then
1263 ItemRack_Users[user].Events["Mount"] = nil
1264 end
1265 end
1266  
1267 ItemRack_Events_ScrollFrameScrollBar:SetValue(0)
1268 ItemRack_Build_eventList()
1269 ItemRack_EnableAllEvents()
1270 end
1271  
1272 function ItemRack_OnEvent(event)
1273  
1274 if event=="UNIT_INVENTORY_CHANGED" then
1275 if arg1=="player" then
1276 Rack.StartTimer("InvUpdate")
1277 end
1278 elseif event=="PLAYER_AURAS_CHANGED" then
1279 ItemRack_BuffsChanged()
1280  
1281 elseif event=="UPDATE_BINDINGS" then
1282 update_keybindings()
1283  
1284 elseif event=="BANKFRAME_OPENED" then
1285 Rack.BankOpened()
1286  
1287 elseif event=="BANKFRAME_CLOSED" then
1288 Rack.BankClosed()
1289  
1290 elseif event=="BAG_UPDATE" then -- only enabled when bank is open
1291 Rack.PopulateBank()
1292 if ItemRack_MenuFrame:IsVisible() and ItemRack.InvOpen then
1293 ItemRack_BuildMenu(ItemRack.InvOpen,ItemRack.MenuDockedTo)
1294 if ItemRack.InvOpen==20 then
1295 local id = GetMouseFocus() and GetMouseFocus():GetID() or ""
1296 local menuItem = ItemRack.BaggedItems[id or ""]
1297 if menuItem and menuItem.name and Rack_User[user].Sets[menuItem.name] then
1298 ItemRack_Sets_Tooltip(menuItem.name,1)
1299 end
1300 end
1301 end
1302  
1303 elseif event=="PLAYER_LOGIN" then
1304  
1305 user = UnitName("player").." of "..GetRealmName()
1306  
1307 SlashCmdList["ItemRackCOMMAND"] = ItemRack_SlashHandler
1308 SLASH_ItemRackCOMMAND1 = "/itemrack"
1309  
1310 Rack.Initialize()
1311  
1312 initialize_data()
1313 initialize_events()
1314  
1315 ItemRack_InitializeKeyBindings() -- create key bindings for this user
1316 initialize_display()
1317  
1318 this:RegisterEvent("UNIT_INVENTORY_CHANGED")
1319 this:RegisterEvent("UPDATE_BINDINGS")
1320  
1321 RackFrame:RegisterEvent("PLAYER_REGEN_ENABLED") -- leaving combat
1322 RackFrame:RegisterEvent("PLAYER_UNGHOST") -- leaving ghost
1323 RackFrame:RegisterEvent("PLAYER_ALIVE") -- leaving death
1324  
1325 this:RegisterEvent("BANKFRAME_OPENED")
1326 this:RegisterEvent("BANKFRAME_CLOSED")
1327  
1328 if not ItemRack_Settings.Minimap then
1329 ItemRack_Settings.Minimap = {}
1330 end
1331 end
1332 end
1333  
1334 function ItemRack_SlashHandler(arg1)
1335  
1336 if arg1 and string.find(arg1,"equip") then
1337 local _,_,set = string.find(arg1,"equip (.+)")
1338 if not set then
1339 DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00Usage: /itemrack equip (set name)")
1340 DEFAULT_CHAT_FRAME:AddMessage("ie, /itemrack equip pvp gear")
1341 else
1342 ItemRack_EquipSet(set)
1343 end
1344 return
1345 elseif arg1 and string.find(arg1,"toggle") then
1346 local _,_,set = string.find(arg1,"toggle (.+)")
1347 if not set then
1348 DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00Usage: /itemrack toggle (set name)")
1349 DEFAULT_CHAT_FRAME:AddMessage("ie, /itemrack toggle pvp gear")
1350 else
1351 ItemRack_ToggleSet(set)
1352 end
1353 return
1354 end
1355  
1356 arg1 = string.lower(arg1)
1357  
1358 if not string.find(arg1,".+") then
1359 ItemRack_Toggle()
1360 elseif arg1=="reset everything" then
1361 StaticPopupDialogs["ITEMRACKRESET"] = {
1362 text = "Are you sure you want to wipe all ItemRack sets, events and settings?\nThis will restore to default and then Reload the UI.",
1363 button1 = "Yes", button2 = "No", showAlert=1, timeout = 0, whileDead = 1,
1364 OnAccept = function() ItemRack_Users=nil ItemRack_Events=nil ItemRack_Settings=nil Rack_User=nil ReloadUI() end,
1365 }
1366 StaticPopup_Show("ITEMRACKRESET")
1367 elseif string.find(arg1,"^reset") then
1368 if string.find(arg1,"reset event") then
1369 -- /itemrack reset event will initialize events
1370 initialize_events("reset")
1371 ItemRack_Events_ScrollFrameScrollBar:SetValue(0)
1372 if ItemRack_SetsFrame:IsVisible() then
1373 sets_message("Default events reset.")
1374 end
1375 else
1376 -- /itemrack reset will reset the bar
1377 ItemRack_Reset()
1378 end
1379 elseif arg1=="lock" then
1380 set_lock("ON")
1381 elseif arg1=="unlock" then
1382 set_lock("OFF")
1383 elseif string.find(arg1,"scale") then
1384 local _,_,newscale = string.find(arg1,"scale (.+)")
1385 if not tonumber(newscale) then
1386 DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00Usage: /itemrack scale (number)")
1387 DEFAULT_CHAT_FRAME:AddMessage("ie, /itemrack scale 0.85")
1388 else
1389 ItemRack.FrameToScale = ItemRack_InvFrame
1390 ItemRack_ScaleFrame(newscale)
1391 ItemRack_Users[user].MainScale = ItemRack_InvFrame:GetScale()
1392 cooldowns_need_updating()
1393 end
1394 elseif string.find(arg1,"^opt") then
1395 ItemRack_Sets_Toggle()
1396 elseif arg1=="debug" then
1397 ItemRack_ListEvents()
1398 DEFAULT_CHAT_FRAME:AddMessage("Events version: "..tostring(ItemRack_Events.events_version))
1399 DEFAULT_CHAT_FRAME:AddMessage("ItemRack version: "..tostring(ItemRack_Version))
1400 else
1401 DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00ItemRack usage:")
1402 DEFAULT_CHAT_FRAME:AddMessage("/itemrack : toggle the window")
1403 DEFAULT_CHAT_FRAME:AddMessage("/itemrack reset : reset window")
1404 DEFAULT_CHAT_FRAME:AddMessage("/itemrack lock or unlock : toggles window lock")
1405 DEFAULT_CHAT_FRAME:AddMessage("/itemrack scale (number) : sets an exact scale")
1406 DEFAULT_CHAT_FRAME:AddMessage("/itemrack equip (set name) : equips a set")
1407 DEFAULT_CHAT_FRAME:AddMessage("/itemrack toggle (set name) : equips/unequips set")
1408 DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00Alt+click item slots in the character window to add/remove items.\nWhile locked, hold ALT over the window to access control buttons.")
1409 end
1410 end
1411  
1412 --[[ Main bar add/remove functions ]]--
1413  
1414 function ItemRack_Reset()
1415  
1416 local i
1417 -- restore user settings to default (but keep bar contents)
1418 for i in ItemRackOpt_Defaults do
1419 ItemRack_Users[user][i] = ItemRackOpt_Defaults[i]
1420 end
1421 initialize_data()
1422 initialize_display()
1423 initialize_events()
1424 ItemRack_SetsFrame:ClearAllPoints()
1425 ItemRack_SetsFrame:SetPoint("CENTER","UIParent","CENTER")
1426 end
1427  
1428 local function remove_inv(id)
1429  
1430 local i
1431  
1432 getglobal("ItemRackInv"..id):Hide()
1433 ItemRack_Users[user].Inv[id] = nil
1434 for i=1,table.getn(ItemRack_Users[user].Bar) do
1435 if ItemRack_Users[user].Bar[i]==id then
1436 table.remove(ItemRack_Users[user].Bar,i)
1437 end
1438 end
1439 draw_inv()
1440 end
1441  
1442 --[[ Hooked functions ]]--
1443  
1444 -- if action is currently equipped, then reflect its use to the mod
1445 function newItemRack_UseAction(slot,checkCursor,onSelf)
1446  
1447 if IsEquippedAction(slot) and cursor_empty() then
1448 Rack_TooltipScan:SetAction(slot)
1449 local usedName = Rack_TooltipScanTextLeft1:GetText()
1450  
1451 local i,name,foundSlot
1452  
1453 -- look for a worn item with same name as clicked item
1454 for i=1,20 do
1455 Rack_TooltipScan:SetInventoryItem("player",i)
1456 name = Rack_TooltipScanTextLeft1:GetText()
1457 if name==usedName then
1458 foundSlot = i -- found this item in slot i
1459 i = 21 -- whimpy break
1460 end
1461 end
1462  
1463 if foundSlot and GetActionCooldown(slot)==0 then
1464 ItemRack_ReactUseInventoryItem(foundSlot)
1465 end
1466  
1467 end
1468  
1469 oldItemRack_UseAction(slot,checkCursor,onSelf)
1470  
1471 end
1472  
1473 -- Inv slots are added by ALT+clicking the paper doll
1474 function newItemRack_PaperDollItemSlotButton_OnClick(button, ignoreShift)
1475  
1476 if IsAltKeyDown() then
1477 local item = string.gsub(tostring(this:GetName()),"Character","")
1478 local id,texture = GetInventorySlotInfo(item)
1479  
1480 if ItemRack_Users[user].Inv[id] then
1481 remove_inv(id)
1482 else
1483 ItemRack_Users[user].Visible="ON"
1484 ItemRack_Users[user].Inv[id] = 1
1485 getglobal("ItemRackInv"..id.."Icon"):SetTexture(texture)
1486 table.insert(ItemRack_Users[user].Bar,id)
1487 draw_inv()
1488 end
1489 else
1490 oldItemRack_PaperDollItemSlotButton_OnClick(button, ignoreShift)
1491 end
1492  
1493 end
1494  
1495 -- set slot is added by ALT+clicking the paper doll character model
1496 function newItemRack_CharacterModelFrame_OnMouseUp(button)
1497  
1498 if IsAltKeyDown() then
1499 if ItemRack_Users[user].Inv[20] then
1500 remove_inv(20)
1501 else
1502 ItemRack_Users[user].Visible="ON"
1503 ItemRack_Users[user].Inv[20]=1
1504 table.insert(ItemRack_Users[user].Bar,20)
1505 draw_inv()
1506 end
1507 else
1508 oldItemRack_CharacterModelFrame_OnMouseUp(button)
1509 end
1510  
1511 end
1512  
1513 function newItemRack_PaperDollItemSlotButton_OnEnter()
1514  
1515 local id,i = this:GetName()
1516  
1517 oldItemRack_PaperDollItemSlotButton_OnEnter()
1518  
1519 if IsAltKeyDown() then
1520 for i=0,19 do
1521 if ItemRack.Indexes[i].paperdoll_slot==id then
1522 id = i
1523 i = 20 -- whimpy break
1524 end
1525 end
1526  
1527 if tonumber(id) and not InRepairMode() then
1528 ItemRack_BuildMenu(id,"CHARACTERSHEET")
1529 end
1530 end
1531 end
1532  
1533 function newItemRack_PaperDollFrame_OnHide()
1534  
1535 if ItemRack.MenuDockedTo=="CHARACTERSHEET" then
1536 ItemRack_MenuFrame:Hide()
1537 end
1538  
1539 oldItemRack_PaperDollFrame_OnHide()
1540 end
1541  
1542 --[[ Inv Movement ]]--
1543  
1544 function ItemRack_InvFrame_OnMouseDown(arg1)
1545  
1546 if arg1=="LeftButton" and ItemRack_Users[user].Locked=="OFF" then
1547 this:StartMoving()
1548 end
1549 end
1550  
1551 function ItemRack_InvFrame_OnMouseUp(arg1)
1552  
1553 if arg1=="LeftButton" then
1554 this:StopMovingOrSizing()
1555 ItemRack_Users[user].XPos = ItemRack_InvFrame:GetLeft()
1556 ItemRack_Users[user].YPos = ItemRack_InvFrame:GetTop()
1557 cooldowns_need_updating()
1558 end
1559 end
1560  
1561 function ItemRack_Toggle()
1562 if ItemRack_InvFrame:IsVisible() then
1563 ItemRack_Users[user].Visible="OFF"
1564 ItemRack_InvFrame:Hide()
1565 else
1566 ItemRack_Users[user].Visible="ON"
1567 ItemRack_InvFrame:Show()
1568 end
1569 end
1570  
1571 --[[ Inventory changes ]]--
1572  
1573 -- any inventory changes will cause this OnUpdate to start counting. After InvUpdateLimit, it processes the inventory change
1574 function ItemRack_InvUpdate_OnUpdate()
1575  
1576 if SpellIsTargeting() then
1577 -- if we're in disenchant/enchant/applying poison/sharpening stone/etc, check back later. do nothing for now
1578 Rack.StartTimer("InvUpdate",1)
1579 else
1580  
1581 local i,j
1582 local bagStart,bagEnd = 0,4
1583  
1584 Rack.StopTimer("InvUpdate")
1585 ItemRack.Swapping = false
1586  
1587 draw_inv()
1588  
1589 if ItemRack_Users[user].Inv[0] then
1590 -- update ammo count
1591 ItemRackInv0Count:SetText(CharacterAmmoSlotCount:IsShown() and CharacterAmmoSlotCount:GetText() or "")
1592 end
1593  
1594 if table.getn(ItemRack_Users[user].Bar)>0 then
1595 for i=1,table.getn(ItemRack_Users[user].Bar) do
1596 getglobal("ItemRackInv"..ItemRack_Users[user].Bar[i]):SetChecked(0)
1597 SetDesaturation(getglobal("ItemRackInv"..ItemRack_Users[user].Bar[i].."Icon"),nil)
1598 end
1599 end
1600  
1601 -- if set builder is up, change the inventory to reflect the change
1602 if ItemRack_SetsFrame:IsVisible() then
1603 for i=0,19 do
1604 SetDesaturation(getglobal("ItemRack_Sets_Inv"..i.."Icon"),nil)
1605 end
1606 ItemRack_Sets_UpdateInventory()
1607 end
1608  
1609 if ItemRack.InvOpen then
1610 if ItemRack_Settings.RightClick=="ON" and ItemRack.InvOpen==13 then
1611 ItemRack.InvOpen = 14
1612 end
1613 ItemRack_BuildMenu(ItemRack.InvOpen)
1614 end
1615  
1616 end
1617 end
1618  
1619 --[[ Scaling ]]--
1620  
1621 function ItemRack_StartScaling(arg1)
1622 if arg1=="LeftButton" and unlocked() then
1623 this:LockHighlight()
1624 ItemRack.FrameToScale = this:GetParent()
1625 ItemRack.ScalingWidth = this:GetParent():GetWidth()
1626 ItemRack_MenuFrame:Hide()
1627 Rack.StartTimer("ScaleUpdate")
1628 end
1629 end
1630  
1631 function ItemRack_StopScaling(arg1)
1632 if arg1=="LeftButton" then
1633 Rack.StopTimer("ScaleUpdate")
1634 ItemRack.FrameToScale = nil
1635 cooldowns_need_updating()
1636 this:UnlockHighlight()
1637 if this:GetParent():GetName() == "ItemRack_InvFrame" then
1638 ItemRack_Users[user].MainScale = ItemRack_InvFrame:GetScale()
1639 end
1640 end
1641 end
1642  
1643 function ItemRack_ScaleFrame(scale)
1644 local frame = ItemRack.FrameToScale
1645 local oldscale = frame:GetScale() or 1
1646 local framex = (frame:GetLeft() or ItemRack_Users[user].XPos)* oldscale
1647 local framey = (frame:GetTop() or ItemRack_Users[user].YPos)* oldscale
1648  
1649 frame:SetScale(scale)
1650 really_setpoint(ItemRack_InvFrame,"TOPLEFT","UIParent","BOTTOMLEFT",framex/scale,framey/scale)
1651 end
1652  
1653 --[[ Clicks ]]--
1654  
1655 -- uses inventory slot v1 (0-19) can be called from key binding and slot doesn't need to be on the bar
1656 function ItemRack_UseItem(v1)
1657  
1658 if SpellIsTargeting() and v1~=0 then -- if poison or sharpening stone being applied (and not an ammo slot)
1659 PickupInventoryItem(v1)
1660 elseif v1 and not MerchantFrame:IsVisible() then
1661 UseInventoryItem(v1)
1662 end
1663 end
1664  
1665 function ItemRack_ReactUseInventoryItem(slot)
1666  
1667 if ItemRack_Users[user].Inv[slot] then
1668 getglobal("ItemRackInv"..slot):SetChecked(1)
1669 end
1670 Rack.StartTimer("InvUpdate",1.5) -- extra long wait
1671  
1672 _,_,item = string.find(GetInventoryItemLink("player",slot) or "","^.*%[(.*)%].*$")
1673 if ItemRack_Settings.Notify=="ON" and item then
1674 ItemRack.NotifyList[item] = { bag=nil, slot=nil, inv=slot }
1675 end
1676 if ItemRack_Settings.EnableEvents and item then
1677 local holdarg1,holdarg2 = arg1,arg2
1678 arg1 = item
1679 arg2 = slot
1680 ItemRack_RegisterFrame_OnEvent("ITEMRACK_ITEMUSED")
1681 arg1 = holdarg1
1682 arg2 = holdarg2
1683 end
1684 end
1685  
1686 -- hook for UseInventoryItem
1687 function newItemRack_UseInventoryItem(slot)
1688 local cooldown = GetInventoryItemCooldown("player",slot)
1689 oldItemRack_UseInventoryItem(slot) -- call original UseInventoryItem
1690 if cooldown==0 then
1691 ItemRack_ReactUseInventoryItem(slot) -- tell the mod an item was used
1692 end
1693 end
1694  
1695 function ItemRack_Inv_OnClick(arg1)
1696  
1697 local id = this:GetID()
1698  
1699 this:SetChecked(0)
1700  
1701 if id==20 and not IsAltKeyDown() then
1702 if arg1=="RightButton" then
1703 ItemRack_Sets_Toggle(2)
1704 else
1705 local setname = Rack.CurrentSet()
1706 if setname and Rack_User[user].Sets[setname] then
1707 if IsShiftKeyDown() then
1708 Rack.UnequipSet(setname)
1709 else
1710 ItemRack_EquipSet(setname)
1711 end
1712 end
1713 end
1714 elseif arg1=="RightButton" and IsAltKeyDown() and ItemRack_Users[user].Locked=="OFF" then
1715 -- toggle space after item
1716 ItemRack_Users[user].Spaces[id] = not ItemRack_Users[user].Spaces[id]
1717 draw_inv()
1718 elseif arg1=="LeftButton" and IsAltKeyDown() and ItemRack_Users[user].Locked=="OFF" then
1719 -- if Alt is down, remove the item from the bar
1720 if ItemRack_Users[user].Inv[id] then
1721 remove_inv(id)
1722 ItemRack_MenuFrame:Hide()
1723 end
1724 elseif arg1=="LeftButton" and IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then
1725 -- if Shift is down, link the item to chat
1726 ChatFrameEditBox:Insert(GetInventoryItemLink("player",id))
1727 else
1728 -- otherwise use the item
1729 ItemRack_UseItem(id)
1730 end
1731 end
1732  
1733 --[[ Menu ]]--
1734  
1735 function ItemRack_Inv_OnEnter()
1736 local id = this:GetID()
1737  
1738 ItemRack_Inv_Tooltip()
1739  
1740 ItemRack.MenuDockedTo = nil
1741 if not Rack.TimerEnabled("ScaleUpdate") then
1742 if IsShiftKeyDown() or ItemRack_Settings.MenuShift=="OFF" then
1743 if ItemRack_Settings.RightClick=="ON" and (id==13 or id==14) and ItemRack.TrinketsPaired then
1744 if ItemRack_Users[user].MainOrient=="HORIZONTAL" and corner_info("LEFTRIGHT")=="LEFT" then
1745 id = 13
1746 elseif ItemRack_Users[user].MainOrient=="HORIZONTAL" and corner_info("LEFTRIGHT")=="RIGHT" then
1747 id = 14
1748 elseif ItemRack_Users[user].MainOrient=="VERTICAL" and corner_info("TOPBOTTOM")=="TOP" then
1749 id = 13
1750 else
1751 id = 14
1752 end
1753 end
1754 ItemRack_BuildMenu(id)
1755 end
1756 if IsAltKeyDown() then
1757 ItemRack_ControlFrame:Show()
1758 Rack.StartTimer("ControlFrame")
1759 end
1760 end
1761 end
1762  
1763 function ItemRack_Menu_OnClick(arg1)
1764  
1765 local id,i,unqueue = this:GetID()
1766 local name = ItemRack.BaggedItems[id].name
1767  
1768 this:SetChecked(0)
1769  
1770 if SpellIsTargeting() or CursorHasItem() then return end -- prohibit swaps while in spell target/disenchant mode
1771  
1772 if ItemRack.BankIsOpen then
1773 if ItemRack.InvOpen~=20 then
1774 Rack.ClearLockList()
1775 local bag,slot
1776 if ItemRack.BankedItems[name] then
1777 bag,slot = Rack.FindSpace()
1778 if bag then
1779 PickupContainerItem(ItemRack.BaggedItems[id].bag,ItemRack.BaggedItems[id].slot)
1780 PickupContainerItem(bag,slot)
1781 else
1782 Rack.NoMoreRoom()
1783 end
1784 else
1785 bag,slot = Rack.FindSpace(1)
1786 if bag then
1787 PickupContainerItem(ItemRack.BaggedItems[id].bag,ItemRack.BaggedItems[id].slot)
1788 PickupContainerItem(bag,slot)
1789 else
1790 Rack.NoMoreRoom()
1791 end
1792 -- *** swap from bag to bank
1793 end
1794 else
1795 if Rack.SetHasBanked(name) then
1796 Rack.PullSetFromBank(name)
1797 else
1798 Rack.PushSetToBank(name)
1799 end
1800 end
1801 return
1802 end
1803  
1804 if ItemRack_Settings.RightClick=="ON" and arg1=="LeftButton" and ItemRack.InvOpen==14 then
1805 ItemRack.InvOpen = 13
1806 elseif ItemRack_Settings.RightClick=="ON" and arg1=="RightButton" and ItemRack.InvOpen==13 then
1807 ItemRack.InvOpen = 14
1808 end
1809  
1810 if (ItemRack_Settings.AllowHidden=="ON" or ItemRack.InvOpen==20) and IsAltKeyDown() and ItemRack.MenuDockedTo~="CHARACTERSHEET" then
1811  
1812 if ItemRack.InvOpen==20 then
1813 -- sets ignore flag is with the set
1814 if Rack_User[user].Sets[name].hide then
1815 Rack_User[user].Sets[name].hide = nil
1816 else
1817 Rack_User[user].Sets[name].hide = 1
1818 end
1819 elseif not ItemRack.BaggedItems[id].bag then
1820 -- empty slot, do nothing
1821 elseif ItemRack_Users[user].Ignore[ItemRack.BaggedItems[id].name] then
1822 ItemRack_Users[user].Ignore[ItemRack.BaggedItems[id].name] = nil
1823 else
1824 ItemRack_Users[user].Ignore[ItemRack.BaggedItems[id].name] = 1
1825 end
1826 ItemRack_BuildMenu(ItemRack.InvOpen,ItemRack.MenuDockedTo)
1827  
1828 elseif arg1=="LeftButton" and IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then
1829 -- if linking a menu item with shift+left click
1830 ChatFrameEditBox:Insert(GetContainerItemLink(ItemRack.BaggedItems[id].bag,ItemRack.BaggedItems[id].slot))
1831  
1832 elseif ItemRack.InvOpen==20 then
1833 -- if selecting a set menu item
1834 if ItemRack.BaggedItems[id].name then
1835 if (not UnitAffectingCombat("player") and not Rack.IsPlayerReallyDead()) and (IsShiftKeyDown() or ItemRack_Settings.AutoToggle=="ON") then
1836 -- toggle set if shift key is down or AutoToggle on
1837 ItemRack_ToggleSet(ItemRack.BaggedItems[id].name)
1838 else -- otherwise equip it
1839 ItemRack_EquipSet(ItemRack.BaggedItems[id].name)
1840 end
1841 ItemRack_MenuFrame:Hide()
1842 Rack.StartTimer("InvUpdate",1) -- extra long wait, force an update, set may not swap
1843 end
1844  
1845 elseif (UnitAffectingCombat("player") and not ItemRack.Indexes[ItemRack.InvOpen].swappable) or Rack.IsPlayerReallyDead() then
1846 -- if selecting a menu item while dead
1847 if ItemRack.BaggedItems[id].name=="(empty)" then
1848 Rack.AddToCombatQueue(ItemRack.InvOpen,0)
1849 else
1850 local _,itemID = Rack.GetItemInfo(ItemRack.BaggedItems[id].bag,ItemRack.BaggedItems[id].slot)
1851 Rack.AddToCombatQueue(ItemRack.InvOpen,itemID)
1852 end
1853 ItemRack_MenuFrame:Hide()
1854  
1855 elseif ItemRack.InvOpen and cursor_empty() and not SpellIsTargeting() then
1856 -- if this is a normal swap of a single item out of combat and the cursor is free/not doing anything
1857  
1858 ItemRack.Swapping = true
1859 if ItemRack.BaggedItems[id].bag then
1860 -- find out if incoming item is two-hand or offhand that may leave a loose item in bags for later move
1861 local _,_,equipslot = get_item_info(ItemRack.BaggedItems[id].bag,ItemRack.BaggedItems[id].slot)
1862 if equipslot=="INVTYPE_2HWEAPON" then
1863 if GetInventoryItemLink("player",17) then
1864 -- something is in offhand slot, move it out
1865 Rack.ClearLockList()
1866 bag,slot = Rack.FindSpace()
1867 if not bag then
1868 UIErrorsFrame:AddMessage(ERR_INV_FULL,1,.1,.1,1,UIERRORS_HOLD_TIME)
1869 else
1870 PickupInventoryItem(17)
1871 PickupContainerItem(bag,slot)
1872 end
1873 end
1874 end
1875 PickupContainerItem(ItemRack.BaggedItems[id].bag,ItemRack.BaggedItems[id].slot)
1876 PickupInventoryItem(ItemRack.InvOpen)
1877 else
1878 local j,bag,slot
1879 -- swapping to an empty slot, create freespace
1880 Rack.ClearLockList()
1881 bag,slot = Rack.FindSpace()
1882 if not bag then
1883 UIErrorsFrame:AddMessage(ERR_INV_FULL,1,.1,.1,1,UIERRORS_HOLD_TIME)
1884 else
1885 PickupInventoryItem(ItemRack.InvOpen)
1886 PickupContainerItem(bag,slot)
1887 end
1888 end
1889 SetDesaturation(getglobal("ItemRackInv"..ItemRack.InvOpen.."Icon"),1)
1890  
1891 if ItemRack_SetsFrame:IsVisible() then
1892 SetDesaturation(getglobal("ItemRack_Sets_Inv"..ItemRack.InvOpen.."Icon"),1)
1893 end
1894  
1895 if not IsShiftKeyDown() or ItemRack_Settings.RightClick=="OFF" then
1896 ItemRack_MenuFrame:Hide()
1897 end
1898 Rack.StartTimer("InvUpdate",1.25)
1899  
1900 end
1901 end
1902  
1903 function ItemRack_MenuFrame_OnShow()
1904 Rack.StartTimer("MenuFrame")
1905 end
1906  
1907 function ItemRack_MenuFrame_OnHide()
1908  
1909 Rack.StopTimer("MenuFrame")
1910 local i
1911 for i=0,20 do
1912 getglobal("ItemRackInv"..i):UnlockHighlight()
1913 end
1914 ItemRack.InvOpen = nil
1915 end
1916  
1917 --[[ Tooltips ]]--
1918  
1919 function ItemRack_Inv_Tooltip()
1920  
1921 local id = this:GetID()
1922  
1923 if Rack.TimerEnabled("ScaleUpdate") or ItemRack_Settings.ShowTooltips=="OFF" then
1924 return
1925 end
1926  
1927 if id==20 then
1928 -- if mouseover set on bar, display current set tooltip
1929 ItemRack_Sets_Tooltip(Rack.CurrentSet())
1930 else
1931 -- otherwise set up tooltip for an inventory item
1932 ItemRack.TooltipOwner = this
1933 ItemRack.TooltipType = "INVENTORY"
1934 ItemRack.TooltipSlot = id
1935  
1936 ItemRack.TooltipBag = Rack.GetNameByID(Rack.CombatQueue[id])
1937  
1938 Rack.StartTimer("TooltipUpdate",0)
1939 end
1940 end
1941  
1942 function ItemRack_Menu_Tooltip()
1943  
1944 local id = this:GetID()
1945  
1946 if Rack.TimerEnabled("ScaleUpdate") or ItemRack_Settings.ShowTooltips=="OFF" then
1947 return
1948 end
1949  
1950 if ItemRack.InvOpen==20 then
1951 -- if a sets menu, display set tooltip
1952 ItemRack_Sets_Tooltip(ItemRack.BaggedItems[id].name)
1953  
1954 elseif ItemRack.BaggedItems[id].bag then
1955 -- otherwise set up tooltip for a bagged item
1956 ItemRack.TooltipOwner = this
1957 ItemRack.TooltipType = "BAG"
1958 ItemRack.TooltipBag = ItemRack.BaggedItems[id].bag
1959 ItemRack.TooltipSlot = ItemRack.BaggedItems[id].slot
1960  
1961 Rack.StartTimer("TooltipUpdate",0)
1962 end
1963 end
1964  
1965 function ItemRack_ClearTooltip()
1966  
1967 GameTooltip:Hide()
1968 ItemRack.TooltipType = nil
1969 Rack.StopTimer("TooltipUpdate")
1970 if not ItemRack.InvOpen then
1971 ItemRack_MenuFrame_OnHide()
1972 end
1973 end
1974  
1975 local function set_tooltip_anchor(owner)
1976  
1977 if ItemRack.MenuDockedTo=="CHARACTERSHEET" and ItemRack.InvOpen then
1978 -- if this is a tooltip of an item docked to character sheet, anchor it to the paperdoll_slot
1979 GameTooltip:SetOwner(owner,"ANCHOR_RIGHT")
1980 elseif ItemRack_Settings.TooltipFollow=="ON" then
1981 if (owner:GetLeft() or 0)<400 then
1982 GameTooltip:SetOwner(owner,"ANCHOR_RIGHT")
1983 else
1984 GameTooltip:SetOwner(owner,"ANCHOR_LEFT")
1985 end
1986 else
1987 GameTooltip_SetDefaultAnchor(GameTooltip,UIParent)
1988 end
1989 end
1990  
1991 -- takes the currently-built tooltip and rebuilds with just name, durability and cooldown
1992 local function shrink_tooltip()
1993  
1994 local nameline_line,durability_line,cooldown_line,tooltip_line,item_color
1995  
1996 name_line = GameTooltipTextLeft1:GetText()
1997  
1998 if name_line then
1999  
2000 for i=2,30 do
2001 tooltip_line = getglobal("GameTooltipTextLeft"..i):GetText() or ""
2002 if string.find(tooltip_line,durability_pattern) then
2003 durability_line = tooltip_line
2004 elseif string.find(tooltip_line,COOLDOWN_REMAINING) then
2005 cooldown_line = "|cFFFFFFFF"..tooltip_line
2006 end
2007 end
2008  
2009 if ItemRack.TooltipType=="BAG" then
2010 item_color = string.sub(GetContainerItemLink(ItemRack.TooltipBag,ItemRack.TooltipSlot) or "",1,10) or ""
2011 else
2012 item_color = string.sub(GetInventoryItemLink("player",ItemRack.TooltipSlot) or "",1,10) or ""
2013 end
2014  
2015 set_tooltip_anchor(ItemRack.TooltipOwner)
2016 GameTooltip:ClearLines()
2017 GameTooltip:AddLine(item_color..name_line)
2018 GameTooltip:AddLine(durability_line)
2019 GameTooltip:AddLine(cooldown_line)
2020 end
2021 end
2022  
2023 --[[ Cooldowns ]]--
2024  
2025 local function format_time(seconds)
2026  
2027 if seconds<60 then
2028 return math.floor(seconds+.5)..((ItemRack_Settings.BigCooldown=="ON") and "" or " s")
2029 else
2030 if seconds < 3600 then
2031 return math.ceil((seconds/60)).." m"
2032 else
2033 return math.ceil((seconds/3600)).." h"
2034 end
2035 end
2036  
2037 end
2038  
2039 local function write_cooldown(where,start,duration)
2040  
2041 local cooldown = duration - (ItemRack.CurrentTime - start)
2042  
2043 if start==0 then
2044 where:SetText("")
2045 elseif cooldown<3 and not where:GetText() then
2046 -- this is a global cooldown. don't display it. not accurate but at least not annoying
2047 else
2048 where:SetText(format_time(cooldown))
2049 end
2050 end
2051  
2052 local function notify(v1)
2053  
2054 local text = string.format((ItemRack_Settings.NotifyThirty=="OFF") and ItemRackText.READY or ItemRackText.READYTHIRTY,v1 or "")
2055  
2056 if v1 then
2057 PlaySound("GnomeExploration")
2058 if SCT_Display then
2059 -- send via SCT if it exists
2060 SCT_Display(text,{r=.2,g=.7,b=.9})
2061 elseif SHOW_COMBAT_TEXT=="1" then
2062 CombatText_AddMessage(text, CombatText_StandardScroll, .2, .7, .9) -- or default UI's SCT
2063 else
2064 -- send vis UIErrorsFrame if SCT doesn't exit
2065 UIErrorsFrame:AddMessage(text,.2,.7,.9,1,UIERRORS_HOLD_TIME)
2066 end
2067 DEFAULT_CHAT_FRAME:AddMessage("|cff33b2e5"..text)
2068 if ItemRack_Settings.EnableEvents then
2069 local holdarg1= arg1
2070 arg1 = v1
2071 ItemRack_RegisterFrame_OnEvent("ITEMRACK_NOTIFY")
2072 arg1 = holdarg1
2073 end
2074 end
2075 end
2076  
2077 -- populate ItemRack.NotifyList[v1] with the location of item named 'v1'
2078 local function notify_find_item(v1)
2079  
2080 local found_inv,found_bag,found_slot = Rack.FindItem(nil,v1,"passive")
2081  
2082 if found_inv then
2083 ItemRack.NotifyList[v1].inv = found_inv
2084 ItemRack.NotifyList[v1].bag = nil
2085 ItemRack.NotifyList[v1].slot = nil
2086 elseif found_bag then
2087 ItemRack.NotifyList[v1].inv = nil
2088 ItemRack.NotifyList[v1].bag = found_bag
2089 ItemRack.NotifyList[v1].slot = found_slot
2090 else
2091 ItemRack.NotifyList[v1] = nil
2092 end
2093 end
2094  
2095 function ItemRack_CooldownUpdate_OnUpdate()
2096  
2097 if ItemRack.CooldownsNeedUpdating then
2098 ItemRack.CooldownsNeedUpdating = false
2099 update_inv_cooldowns()
2100 end
2101  
2102 if ItemRack_Settings.CooldownNumbers=="ON" then
2103  
2104 local i,start,duration
2105 ItemRack.CurrentTime = GetTime()
2106  
2107 if ItemRack_InvFrame:IsVisible() then
2108 for i=1,table.getn(ItemRack_Users[user].Bar) do
2109 start, duration = GetInventoryItemCooldown("player",ItemRack_Users[user].Bar[i])
2110 write_cooldown(getglobal("ItemRackInv"..ItemRack_Users[user].Bar[i].."Time"),start,duration)
2111 end
2112 end
2113  
2114 if ItemRack_MenuFrame:IsVisible() and ItemRack.InvOpen then
2115 for i=1,ItemRack.NumberOfItems do
2116 if ItemRack.BaggedItems[i].bag then
2117 start, duration = GetContainerItemCooldown(ItemRack.BaggedItems[i].bag,ItemRack.BaggedItems[i].slot)
2118 write_cooldown(getglobal("ItemRackMenu"..i.."Time"),start,duration)
2119 end
2120 end
2121 end
2122  
2123 end
2124  
2125 if ItemRack_Settings.Notify=="ON" then
2126  
2127 local i,name,start,duration,cooldown
2128 ItemRack.CurrentTime = GetTime()
2129  
2130 -- go down notify list and check up on each item used
2131 for i in ItemRack.NotifyList do
2132 if ItemRack.NotifyList[i].inv then
2133 _,_,name=string.find(GetInventoryItemLink("player",ItemRack.NotifyList[i].inv) or "","^.*%[(.*)%].*$")
2134 else
2135 _,_,name=string.find(GetContainerItemLink(ItemRack.NotifyList[i].bag,ItemRack.NotifyList[i].slot) or "","^.*%[(.*)%].*$")
2136 end
2137 if i ~= name then
2138 notify_find_item(i) -- item has moved, go find it!
2139 end
2140  
2141 if ItemRack.NotifyList[i] then -- if item still on person (wasn't banked)
2142 if ItemRack.NotifyList[i].inv then -- if it has an inventory spot
2143 start, duration = GetInventoryItemCooldown("player",ItemRack.NotifyList[i].inv)
2144 else -- otherwise it's in a bag
2145 start, duration = GetContainerItemCooldown(ItemRack.NotifyList[i].bag,ItemRack.NotifyList[i].slot)
2146 end
2147 cooldown = (start==0) and 0 or (duration - (ItemRack.CurrentTime - start))
2148 if cooldown>3 then
2149 ItemRack.NotifyList[i].hadcooldown = true
2150 end
2151 if ItemRack.NotifyList[i].hadcooldown and (cooldown==0 or (ItemRack_Settings.NotifyThirty=="ON" and cooldown<30)) then
2152 notify(i)
2153 ItemRack.NotifyList[i] = nil
2154 end
2155 end
2156 end
2157 end
2158  
2159 end
2160  
2161 function ItemRack_CooldownUpdate_OnHide()
2162  
2163 local i
2164  
2165 for i=0,19 do
2166 getglobal("ItemRackInv"..i.."Time"):SetText("")
2167 end
2168 for i=1,30 do
2169 getglobal("ItemRackMenu"..i.."Time"):SetText("")
2170 end
2171 end
2172  
2173 -- changes the cooldown number on button named "button" to big/small depending on .BigCooldown
2174 function ItemRack_SetCooldownFont(button)
2175  
2176 local item = getglobal(button.."Time")
2177  
2178 if ItemRack_Settings.BigCooldown=="ON" then
2179 item:SetFont("Fonts\\FRIZQT__.TTF",16,"OUTLINE")
2180 item:SetTextColor(1,.82,0,1)
2181 item:ClearAllPoints()
2182 item:SetPoint("CENTER",button,"CENTER")
2183 else
2184 item:SetFont("Fonts\\ARIALN.TTF",14,"OUTLINE")
2185 item:SetTextColor(1,1,1,1)
2186 item:ClearAllPoints()
2187 item:SetPoint("BOTTOM",button,"BOTTOM")
2188 end
2189 end
2190  
2191 -- changes all cooldown fonts
2192 function ItemRack_SetAllCooldownFonts()
2193  
2194 for i=0,20 do
2195 ItemRack_SetCooldownFont("ItemRackInv"..i)
2196 end
2197 for i=1,30 do
2198 ItemRack_SetCooldownFont("ItemRackMenu"..i)
2199 end
2200 end
2201  
2202 --[[ Options ]]--
2203  
2204 function ItemRack_OnTooltip(v1,v2)
2205  
2206 if ItemRack_Settings.ShowTooltips=="ON" then
2207 set_tooltip_anchor(this)
2208 GameTooltip:AddLine(v1)
2209 GameTooltip:AddLine(v2,.8,.8,.8,1)
2210 GameTooltip:Show()
2211 end
2212 end
2213  
2214 function ItemRack_Opt_OnEnter()
2215  
2216 local id = this:GetName()
2217  
2218 if ItemRack.OptInfo[id] and not Rack.TimerEnabled("ScaleUpdate") then
2219 ItemRack_OnTooltip(ItemRack.OptInfo[id].text,ItemRack.OptInfo[id].tooltip)
2220 end
2221 end
2222  
2223 function ItemRack_Control_OnClick()
2224  
2225 local id = this:GetName()
2226  
2227 if id=="ItemRack_Control_Rotate" and ItemRack_Users[user].Locked=="OFF" then
2228 -- rotate the window
2229 ItemRack_Users[user].MainOrient = ItemRack_Users[user].MainOrient=="HORIZONTAL" and "VERTICAL" or "HORIZONTAL"
2230 ItemRack_MenuFrame:Hide()
2231 draw_inv()
2232 move_control()
2233 elseif id=="ItemRack_Control_Lock" or id=="ItemRack_Sets_Lock" then
2234 -- lock/unlock the window
2235 ItemRack_Users[user].Locked = ItemRack_Users[user].Locked=="ON" and "OFF" or "ON"
2236 set_lock(ItemRack_Users[user].Locked)
2237 elseif id=="ItemRack_Control_Options" then
2238 ItemRack_Sets_Toggle()
2239 end
2240 end
2241  
2242 function ItemRack_Opt_OnClick(overrideID)
2243  
2244 local id = overrideID or this:GetName()
2245  
2246 if ItemRack.OptInfo[id] and ItemRack.OptInfo[id].info then
2247 local info = ItemRack.OptInfo[id].info
2248  
2249 if this:GetChecked() then
2250 ItemRack_Settings[info] = "ON"
2251 PlaySound("igMainMenuOptionCheckBoxOn")
2252 else
2253 ItemRack_Settings[info] = "OFF"
2254 PlaySound("igMainMenuOptionCheckBoxOff")
2255 end
2256  
2257 if id=="ItemRack_Opt_Bindings" then
2258 update_keybindings()
2259 elseif id=="ItemRack_Opt_CooldownNumbers" then
2260 ItemRack_CooldownUpdate_OnHide()
2261 Rack.StartTimer("CooldownUpdate",0)
2262 elseif id=="ItemRack_Opt_RightClick" then
2263 draw_inv()
2264 elseif id=="ItemRack_Opt_ShowIcon" then
2265 if ItemRack_Settings[info]=="ON" then
2266 ItemRack_IconFrame:Show()
2267 else
2268 ItemRack_IconFrame:Hide()
2269 end
2270 elseif id=="ItemRack_Opt_FlipBar" then
2271 move_control()
2272 draw_inv()
2273 elseif id=="ItemRack_Opt_CompactList" then
2274 ItemRack_Sets_SavedScrollFrameScrollBar:SetValue(0)
2275 ItemRack_Sets_SavedScrollFrame_Update()
2276 elseif id=="ItemRack_Opt_EnableEvents" then
2277 sets_message((ItemRack_Settings.EnableEvents=="ON") and "Events enabled" or "Events disabled")
2278 elseif id=="ItemRack_Opt_DisableToggle" then
2279 draw_minimap_icon()
2280 elseif id=="ItemRack_Opt_ShowAllEvents" then
2281 ItemRack_Events_ScrollFrameScrollBar:SetValue(0)
2282 ItemRack_Build_eventList()
2283 elseif id=="ItemRack_Opt_LargeFont" then
2284 ItemRack_ChangeEventFont()
2285 elseif id=="ItemRack_Opt_SquareMinimap" then
2286 move_icon()
2287 elseif id=="ItemRack_Opt_BigCooldown" then
2288 ItemRack_SetAllCooldownFonts()
2289 elseif id=="ItemRack_Opt_SetLabels" then
2290 draw_inv()
2291 end
2292 end
2293 end
2294  
2295 function ItemRack_OptList_ScrollFrame_Update()
2296  
2297 local i, idx, item, optinfo, optscroll, opttext, optbutton
2298 local offset = FauxScrollFrame_GetOffset(ItemRack_OptList_ScrollFrame)
2299  
2300 FauxScrollFrame_Update(ItemRack_OptList_ScrollFrame, table.getn(ItemRack.OptScroll), 11, 19 )
2301  
2302 for i=1,11 do
2303 item = getglobal("ItemRackOptList"..i)
2304 idx = offset+i
2305 if idx<=table.getn(ItemRack.OptScroll) then
2306 optscroll = ItemRack.OptScroll[idx]
2307 optinfo = ItemRack.OptInfo[optscroll.idx]
2308 opttext = getglobal("ItemRackOptList"..i.."CheckButtonText")
2309 optbutton = getglobal("ItemRackOptList"..i.."CheckButton")
2310 opttext:SetText(optinfo.text)
2311 opttext:SetTextColor(1,1,1,1)
2312 optbutton:Enable()
2313 if optscroll.dependency then
2314 item:SetWidth(128)
2315 if ItemRack_Settings[ItemRack.OptInfo[optscroll.dependency].info]=="OFF" then
2316 opttext:SetTextColor(.5,.5,.5,1)
2317 optbutton:Disable()
2318 end
2319 else
2320 item:SetWidth(142)
2321 end
2322 if ItemRack_Settings[optinfo.info]=="ON" then
2323 optbutton:SetChecked(1)
2324 else
2325 optbutton:SetChecked(0)
2326 end
2327 item:Show()
2328 else
2329 item:Hide()
2330 end
2331 end
2332  
2333 end
2334  
2335 -- tooltip for scrolling options
2336 function ItemRack_OptList_OnEnter()
2337 local idx = FauxScrollFrame_GetOffset(ItemRack_OptList_ScrollFrame) + this:GetParent():GetID()
2338 local optinfo = ItemRack.OptInfo[ItemRack.OptScroll[idx].idx]
2339 ItemRack_OnTooltip(optinfo.text,optinfo.tooltip)
2340 end
2341  
2342 -- onclick for scrolling options, gets name of option and sends to _Opt_OnClick to process
2343 function ItemRack_OptList_OnClick()
2344 local idx = FauxScrollFrame_GetOffset(ItemRack_OptList_ScrollFrame) + this:GetParent():GetID()
2345 local optinfo = ItemRack.OptInfo[ItemRack.OptScroll[idx].idx]
2346 ItemRack_Opt_OnClick(ItemRack.OptScroll[idx].idx)
2347 ItemRack_OptList_ScrollFrame_Update()
2348 end
2349  
2350 -- sets the state of a checkbutton to nil, 0 or 1
2351 function ItemRack_TriStateCheck_SetState(button,value)
2352 local label = getglobal(button:GetName().."Text")
2353 button.tristate = value
2354 if not value then
2355 button:SetCheckedTexture("Interface\\Buttons\\UI-ScrollBar-Knob")
2356 button:SetChecked(1)
2357 label:SetTextColor(.5,.5,.5)
2358 elseif value==0 then
2359 button:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check")
2360 button:SetChecked(0)
2361 label:SetTextColor(1,1,1)
2362 elseif value==1 then
2363 button:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check")
2364 button:SetChecked(1)
2365 label:SetTextColor(1,1,1)
2366 end
2367 end
2368  
2369 -- rotates a checkbutton from indeterminate->unchecked->checked (for show helm/cloak)
2370 function ItemRack_TriStateCheck_OnClick()
2371 if not this.tristate then
2372 ItemRack_TriStateCheck_SetState(this,0)
2373 elseif this.tristate==0 then
2374 ItemRack_TriStateCheck_SetState(this,1)
2375 elseif this.tristate==1 then
2376 ItemRack_TriStateCheck_SetState(this,nil)
2377 end
2378 ItemRack_TriStateCheck_Tooltip()
2379 end
2380  
2381 -- initializes tristate buttons to be indeterminate
2382 function ItemRack_TriStateCheck_OnLoad()
2383 ItemRack_TriStateCheck_SetState(this,nil)
2384 end
2385  
2386 function ItemRack_TriStateCheck_Tooltip()
2387 local tristate_names = { ["nil"] = "Ignore", ["0"] = "Hide", ["1"] = "Show" }
2388 local which = (this==ItemRack_ShowHelm) and "Helm" or "Cloak"
2389 ItemRack_OnTooltip(which..": "..tristate_names[tostring(this.tristate)],"This determines if the "..string.lower(which).." is shown or hidden when equipped.")
2390 end
2391  
2392 --[[ Sets ]]--
2393  
2394 ItemRack.SetBuild = {}
2395  
2396 local function build_icon()
2397  
2398 local setname = ItemRack_Sets_Name:GetText()
2399  
2400 ItemRack_Sets_ChosenIcon:SetNormalTexture(ItemRack.SelectedIcon)
2401 ItemRack_Sets_ChosenIconName:SetText(setname)
2402 if Rack_User[user].Sets[setname] then
2403 local _,_,modifier,basekey = string.find(Rack_User[user].Sets[setname].key or "","(.).+(-.)")
2404 ItemRack_Sets_ChosenIconHotKey:SetText((modifier or "")..(basekey or ""))
2405 else
2406 ItemRack_Sets_ChosenIconHotKey:SetText("")
2407 end
2408 end
2409  
2410 -- initializes .SetIcons mostly, the list of icons for the set
2411 function ItemRack_Sets_Initialize()
2412  
2413 local i
2414  
2415 ItemRack.SetIcons = {}
2416  
2417 for i=0,19 do
2418 -- add 20 spaces at start of list - this list is constructed once and only first 20 slots change
2419 table.insert(ItemRack.SetIcons,"")
2420 end
2421  
2422 for i=1,table.getn(ItemRackExtraIcons) do
2423 -- add ExtraIcons defined in ItemRackExtraIcons.lua
2424 table.insert(ItemRack.SetIcons,ItemRackExtraIcons[i])
2425 end
2426 for i=1,GetNumMacroIcons() do
2427 -- add the macro icons to choose from
2428 table.insert(ItemRack.SetIcons,GetMacroIconInfo(i))
2429 end
2430  
2431 end
2432  
2433 local function highlight_set_item(v1)
2434  
2435 if ItemRack.SetBuild[v1]==1 then
2436 getglobal("ItemRack_Sets_Inv"..v1.."Icon"):SetVertexColor(1,1,1,1)
2437 getglobal("ItemRack_Sets_Inv"..v1):SetAlpha(1)
2438 getglobal("ItemRack_Sets_Inv"..v1):LockHighlight()
2439 else
2440 getglobal("ItemRack_Sets_Inv"..v1.."Icon"):SetVertexColor(.4,.4,.4,1)
2441 getglobal("ItemRack_Sets_Inv"..v1):SetAlpha(.5)
2442 getglobal("ItemRack_Sets_Inv"..v1):UnlockHighlight()
2443 end
2444 end
2445  
2446 function ItemRack_Sets_UpdateInventory()
2447  
2448 local i,texture
2449  
2450 if not ItemRack.SetIcons then
2451 ItemRack_Sets_Initialize()
2452 end
2453  
2454 for i=0,19 do
2455 texture = GetInventoryItemTexture("player",i)
2456 if not texture then
2457 _,texture = GetInventorySlotInfo(string.gsub(ItemRack.Indexes[i].paperdoll_slot,"Character",""))
2458 end
2459 getglobal("ItemRack_Sets_Inv"..i.."Icon"):SetTexture(texture)
2460 ItemRack.SetIcons[i+1] = texture
2461 highlight_set_item(i)
2462 end
2463 ItemRack_Sets_ScrollFrame_Update()
2464 end
2465  
2466 function ItemRack_Sets_NewSet()
2467  
2468 local i,texture
2469  
2470 ItemRack_Sets_UpdateInventory()
2471  
2472 for i=0,19 do
2473 texture = GetInventoryItemTexture("player",i)
2474 if not texture then
2475 _,texture = GetInventorySlotInfo(string.gsub(ItemRack.Indexes[i].paperdoll_slot,"Character",""))
2476 end
2477 ItemRack.SetBuild[i] = ItemRack_Users[user].Inv[i] or 0
2478 highlight_set_item(i)
2479 ItemRack.SetIcons[i+1] = texture
2480 end
2481  
2482 ItemRack_Sets_Saved:Hide()
2483 ItemRack_Sets_Icons:Show() -- start out showing icons
2484  
2485 ItemRack.SelectedName = ""
2486 ItemRack_Sets_Name:SetText("")
2487 ItemRack.SelectedIcon = ItemRack.SetIcons[math.random(1,table.getn(ItemRack.SetIcons))]
2488 build_icon()
2489  
2490 ItemRack_TriStateCheck_SetState(ItemRack_ShowHelm,nil)
2491 ItemRack_TriStateCheck_SetState(ItemRack_ShowCloak,nil)
2492  
2493 for i=1,25 do
2494 getglobal("ItemRack_Sets_Icon"..i):UnlockHighlight()
2495 end
2496  
2497 ItemRack_Sets_NameLabel:SetText(ItemRackText.SETS_NAMELABEL_TEXT)
2498 ItemRack_Tab() -- flip to tab 2 (sets)
2499  
2500 ItemRack_SetsFrame:Show()
2501 ItemRack_Sets_Name:ClearFocus()
2502  
2503 end
2504  
2505 local function validate_set_buttons()
2506  
2507 local setname,found = ItemRack_Sets_Name:GetText()
2508  
2509 if Rack_User[user].Sets[setname] then
2510 ItemRack_Sets_RemoveButton:Enable()
2511 ItemRack_Sets_BindButton:Enable()
2512 ItemRack_Sets_HideSet:Enable()
2513 ItemRack_Sets_HideSetText:SetTextColor(1,1,1)
2514 else
2515 ItemRack_Sets_RemoveButton:Disable()
2516 ItemRack_Sets_BindButton:Disable()
2517 ItemRack_Sets_HideSet:Disable()
2518 ItemRack_Sets_HideSetText:SetTextColor(.4,.4,.4)
2519 end
2520  
2521 for i=0,19 do
2522 found = found or (ItemRack.SetBuild[i]==1)
2523 end
2524 setname = (found and setname) or nil
2525  
2526 if setname and string.len(setname)>0 then
2527 ItemRack_Sets_SaveButton:Enable()
2528 else
2529 ItemRack_Sets_SaveButton:Disable()
2530 end
2531  
2532 build_icon()
2533 end
2534  
2535 function ItemRack_Sets_InvToggle()
2536  
2537 local id = this:GetID()
2538  
2539 this:SetChecked(0)
2540 ItemRack.SetBuild[id] = 1-(ItemRack.SetBuild[id] or 0)
2541  
2542 highlight_set_item(id)
2543  
2544 if IsAltKeyDown() then
2545  
2546 if ItemRack_Users[user].Inv[id] and ItemRack.SetBuild[id]==0 then
2547 remove_inv(id)
2548 elseif not ItemRack_Users[user].Inv[id] and ItemRack.SetBuild[id]==1 then
2549 ItemRack_Users[user].Visible="ON"
2550 ItemRack_Users[user].Inv[id] = 1
2551 getglobal("ItemRackInv"..id.."Icon"):SetTexture(get_item_info(id))
2552 table.insert(ItemRack_Users[user].Bar,id)
2553 draw_inv()
2554 end
2555 end
2556  
2557 if ItemRack.SetBuild[id]==1 then
2558 ItemRack_BuildMenu(id,"SET")
2559 end
2560  
2561 validate_set_buttons()
2562 end
2563  
2564 function ItemRack_Sets_ScrollFrame_Update()
2565  
2566 local i, item, texture, idx
2567 local offset = FauxScrollFrame_GetOffset(ItemRack_Sets_ScrollFrame)
2568  
2569 FauxScrollFrame_Update(ItemRack_Sets_ScrollFrame, ceil(table.getn(ItemRack.SetIcons) / 5) , 5, 24 )
2570  
2571 for i=1,25 do
2572 item = getglobal("ItemRack_Sets_Icon"..i)
2573 idx = (offset*5) + i
2574 if idx<=table.getn(ItemRack.SetIcons) then
2575 texture = ItemRack.SetIcons[idx]
2576 item:SetNormalTexture(texture)
2577 item:SetPushedTexture(texture)
2578 item:Show()
2579 else
2580 item:Hide()
2581 end
2582 if ItemRack.SetIcons[idx]==ItemRack.SelectedIcon then
2583 item:LockHighlight()
2584 else
2585 item:UnlockHighlight()
2586 end
2587 end
2588 end
2589  
2590 function ItemRack_Sets_Icon_OnClick()
2591  
2592 local id = this:GetID()
2593 local offset = FauxScrollFrame_GetOffset(ItemRack_Sets_ScrollFrame)
2594 local idx = offset*5+id
2595  
2596 if idx<=table.getn(ItemRack.SetIcons) then
2597 ItemRack.SelectedIcon = ItemRack.SetIcons[idx]
2598 ItemRack_Sets_ScrollFrame_Update()
2599 end
2600 build_icon()
2601 end
2602  
2603 function ItemRack_Sets_Name_OnTextChanged()
2604  
2605 ItemRack.SelectedName = ItemRack_Sets_Name:GetText()
2606 ItemRack_Sets_ChosenIconName:SetText(ItemRack.SelectedName)
2607 validate_set_buttons()
2608 end
2609  
2610 -- save button clicked
2611 function ItemRack_Sets_Save_OnClick()
2612  
2613 local setname = ItemRack_Sets_Name:GetText()
2614 local itemcount, itemname, itemslot = 0
2615 local oldkey, oldkeyindex
2616  
2617 ItemRack_Sets_Name:ClearFocus()
2618  
2619 -- if a 2H weapon in mainhand, ignore offhand (don't try to equip empty slot to offhand)
2620 _,_,itemslot = get_item_info(16)
2621 if itemslot=="INVTYPE_2HWEAPON" then
2622 ItemRack.SetBuild[17] = nil
2623 end
2624  
2625 if string.len(setname)>0 then
2626 if Rack_User[user].Sets[setname] then
2627 -- grab old key binding before recreating set
2628 oldkey = Rack_User[user].Sets[setname].key
2629 oldkeyindex = Rack_User[user].Sets[setname].keyindex
2630 end
2631 Rack_User[user].Sets[setname] = { icon=ItemRack.SelectedIcon, key=oldkey, keyindex=oldkeyindex }
2632 for i=0,19 do
2633 if ItemRack.SetBuild[i]==1 then
2634 Rack_User[user].Sets[setname][i] = {}
2635 itemcount = itemcount + 1
2636 _,itemname = get_item_info(i)
2637 Rack_User[user].Sets[setname][i].name = itemname or "(empty)"
2638 _,Rack_User[user].Sets[setname][i].id = Rack.GetItemInfo(i)
2639 end
2640 end
2641 sets_message(string.format(ItemRackText.SAVED,setname,itemcount))
2642 Rack_User[user].Sets[setname].hide = ItemRack_Sets_HideSet:GetChecked()
2643 Rack_User[user].Sets[setname].showhelm = ItemRack_ShowHelm.tristate
2644 Rack_User[user].Sets[setname].showcloak = ItemRack_ShowCloak.tristate
2645 end
2646 draw_minimap_icon()
2647 validate_set_buttons()
2648 end
2649  
2650 -- equips a set by name, usable in macros. now a wrapper to Rack.EquipSet()
2651 function ItemRack_EquipSet(setname)
2652  
2653 if not setname and ItemRack.EventSetName then
2654 setname = ItemRack.EventSetName
2655 end
2656  
2657 Rack.EquipSet(setname)
2658 end
2659  
2660 -- hitting the dropdown button toggles between icons and savedsets
2661 function ItemRack_Sets_DropDownButton_OnClick()
2662  
2663 ItemRack_Sets_Name:ClearFocus()
2664 ItemRack_Sets_Build_Dropdown()
2665 ItemRack_Sets_SubFrame2:Hide()
2666 ItemRack_Sets_SubFrame4:Hide()
2667 ItemRack_Sets_SetSelect:Show()
2668 ItemRack_Sets_Saved:Show()
2669 end
2670  
2671 -- clicking one of the drop-down saved sets
2672 function ItemRack_Sets_Saved_OnClick(arg1)
2673  
2674 local id = this:GetID()
2675 local idx = FauxScrollFrame_GetOffset(ItemRack_Sets_SavedScrollFrame) + id
2676 local i,setname
2677  
2678 ItemRack_Sets_SetSelect:Hide()
2679  
2680 if ItemRack.SetsListSize<2 then
2681 -- do nothing
2682 elseif ItemRack.SelectedTab==2 then
2683 -- chose a set from the Sets tab (SubFrame2)
2684 setname = ItemRack.SetsList[idx].Name
2685 ItemRack_Sets_Name:SetText(setname)
2686 ItemRack.SelectedIcon = ItemRack.SetsList[idx].Icon
2687 ItemRack.SelectedName = setname
2688 build_icon()
2689 for i=0,19 do
2690 ItemRack.SetBuild[i] = Rack_User[user].Sets[setname][i] and 1 or 0
2691 highlight_set_item(i)
2692 end
2693 ItemRack_Sets_HideSet:SetChecked(Rack_User[user].Sets[setname].hide)
2694 ItemRack_TriStateCheck_SetState(ItemRack_ShowHelm,Rack_User[user].Sets[setname].showhelm)
2695 ItemRack_TriStateCheck_SetState(ItemRack_ShowCloak,Rack_User[user].Sets[setname].showcloak)
2696 ItemRack_EquipSet(setname)
2697 elseif ItemRack.SelectedTab==4 then
2698 -- chose a set from the Events tab (SubFrame4)
2699 setname = ItemRack.SetsList[idx].Name
2700 if not ItemRack_Users[user].Events[eventList[ItemRack.SelectedEvent].name] then
2701 ItemRack_Users[user].Events[eventList[ItemRack.SelectedEvent].name] = { setname=setname, enabled=1 }
2702 else
2703 ItemRack_Users[user].Events[eventList[ItemRack.SelectedEvent].name].setname = setname
2704 end
2705 ItemRack_Build_eventList()
2706 end
2707  
2708 end
2709  
2710 -- gather all sets into a numerically-indexes list to be used by SavedScrollFrame
2711 function ItemRack_Sets_Build_Dropdown()
2712  
2713 local idx,i,j,count=1
2714  
2715 ItemRack.SetsList = ItemRack.SetsList or {}
2716  
2717 for i in Rack_User[user].Sets do
2718 if not string.find(i,"^ItemRack-") and not string.find(i,"^Rack-") then -- skip special sets (ItemRack-Queue and ItemRack-Normal)
2719 count = 0
2720 ItemRack.SetsList[idx] = ItemRack.SetsList[idx] or {}
2721 ItemRack.SetsList[idx].Icon = Rack_User[user].Sets[i].icon
2722 ItemRack.SetsList[idx].Name = i
2723 for j=0,19 do
2724 if Rack_User[user].Sets[i][j] then
2725 count = count + 1
2726 end
2727 end
2728 ItemRack.SetsList[idx].Count = string.format(ItemRackText.COUNTFORMAT,count)
2729 ItemRack.SetsList[idx].Key = Rack_User[user].Sets[i].key
2730 ItemRack.SetsList[idx].Hide = Rack_User[user].Sets[i].hide
2731  
2732 idx = idx +1
2733 end
2734 end
2735 ItemRack.SetsListSize = idx
2736  
2737 -- sort drop-down list alphabetically
2738 table.sort(ItemRack.SetsList,function(e1,e2) if e1 and e2 and (e1.Name<e2.Name) then return true else return false end end)
2739 ItemRack_Sets_SavedScrollFrameScrollBar:SetValue(0)
2740 ItemRack_Sets_SavedScrollFrame_Update()
2741  
2742 end
2743  
2744 -- scrollbar update for saved sets (dropdown)
2745 function ItemRack_Sets_SavedScrollFrame_Update()
2746  
2747 local i, idx, item
2748 local offset = FauxScrollFrame_GetOffset(ItemRack_Sets_SavedScrollFrame)
2749  
2750 local listsize,listheight,liststub,compact = 8,28,"ItemRack_Sets_Saved",nil
2751  
2752 for i=1,8 do
2753 getglobal("ItemRack_Sets_Saved"..i):Hide()
2754 end
2755 for i=1,11 do
2756 getglobal("ItemRack_Sets_Compact"..i):Hide()
2757 end
2758  
2759 if ItemRack_Settings.CompactList=="ON" then
2760 listsize,listheight,liststub,compact = 11,21,"ItemRack_Sets_Compact",1
2761 end
2762  
2763 FauxScrollFrame_Update(ItemRack_Sets_SavedScrollFrame, ItemRack.SetsListSize-1, listsize, listheight )
2764  
2765 for i=1,listsize do
2766 idx = offset + i
2767 if idx<ItemRack.SetsListSize then
2768 getglobal(liststub..i.."Name"):SetText(ItemRack.SetsList[idx].Name)
2769 getglobal(liststub..i.."Icon"):SetTexture(ItemRack.SetsList[idx].Icon)
2770 if not compact then
2771 getglobal(liststub..i.."Count"):SetText(ItemRack.SetsList[idx].Count)
2772 getglobal(liststub..i.."Key"):SetText(ItemRack.SetsList[idx].Key)
2773 end
2774 if Rack_User[user].Sets[ItemRack.SetsList[idx].Name].hide then
2775 getglobal(liststub..i.."Name"):SetTextColor(.5,.5,.5)
2776 if not compact then
2777 getglobal(liststub..i.."Count"):SetTextColor(.5,.5,.5)
2778 getglobal(liststub..i.."Key"):SetTextColor(.5,.5,.5)
2779 end
2780 else
2781 getglobal(liststub..i.."Name"):SetTextColor(1,1,1)
2782 if not compact then
2783 getglobal(liststub..i.."Count"):SetTextColor(1,1,1)
2784 getglobal(liststub..i.."Key"):SetTextColor(1,1,1)
2785 end
2786 end
2787 getglobal(liststub..i):Show()
2788 else
2789 getglobal(liststub..i):Hide()
2790 end
2791 end
2792  
2793 if ItemRack.SetsListSize<=1 then
2794 ItemRack_Sets_Saved1Icon:SetTexture("")
2795 ItemRack_Sets_Saved1Name:SetText(ItemRackText.NOSAVEDSETS)
2796 ItemRack_Sets_Saved1Count:SetText("")
2797 ItemRack_Sets_Saved1Key:SetText("")
2798 ItemRack_Sets_Saved1:Show()
2799 for i=2,8 do
2800 getglobal("ItemRack_Sets_Saved"..i):Hide()
2801 end
2802 for i=1,11 do
2803 getglobal("ItemRack_Sets_Compact"..i):Hide()
2804 end
2805 end
2806  
2807 end
2808  
2809 -- displays a tooltip of a set's contents, set show_contents to override TinyTooltip option
2810 function ItemRack_Sets_Tooltip(setname,show_contents)
2811  
2812 local count,missing,r,g,b,i,name,inv,bag = 0,0
2813  
2814 setname = setname or ItemRack.SelectedName
2815  
2816 if setname and Rack_User[user].Sets[setname] then
2817  
2818 set_tooltip_anchor(this)
2819 GameTooltip:AddLine(string.format(ItemRackText.SETTOOLTIPFORMAT,setname))
2820 for i=0,19 do
2821 if Rack_User[user].Sets[setname][i] then
2822 name = Rack_User[user].Sets[setname][i].name or "(empty)"
2823 count = count + 1
2824 inv,bag = Rack.FindItem(nil,name,"passive")
2825 if ItemRack.BankedItems[name] then
2826 r,g,b = .3,.5,1
2827 elseif inv or bag or name=="(empty)" then
2828 r,g,b = .85,.85,.85
2829 else
2830 r,g,b = 1,.1,.1 -- mark missing items red
2831 missing = missing + 1
2832 end
2833 if ItemRack_Settings.TinyTooltip=="OFF" or show_contents then
2834 GameTooltip:AddLine(ItemRack.Indexes[i].name..": "..tostring(name),r,g,b)
2835 end
2836 end
2837 end
2838 if ItemRack_Settings.TinyTooltip=="ON" and not show_contents then
2839 -- if Tiny Tooltip enabled, just show a count
2840 GameTooltip:AddLine(string.format(ItemRackText.SETTOOLTIPCOUNT,count),.85,.85,.85)
2841 if missing>0 then
2842 GameTooltip:AddLine(string.format(ItemRackText.SETTOOLTIPMISSING,missing),1,.1,.1)
2843 end
2844 end
2845 GameTooltip:Show()
2846 end
2847 end
2848  
2849 -- tooltips displaying contents of a set in the dropdown list
2850 function ItemRack_Sets_Saved_OnEnter()
2851  
2852 if ItemRack.SetsListSize>1 then
2853 local id = this:GetID()
2854 local idx = FauxScrollFrame_GetOffset(ItemRack_Sets_SavedScrollFrame) + id
2855 local setname = ItemRack.SetsList[idx].Name
2856 ItemRack_Sets_Tooltip(setname,1) -- ,1 forces contents to show in tooltip
2857 end
2858 end
2859  
2860 --[[ Key bindings ]]--
2861  
2862 -- returns a number 1-10 (or MaxKeyBindings) of an available key binding, nil if none are free
2863 local function get_free_binding()
2864  
2865 local i,j,found,freeindex
2866  
2867 for i=1,ItemRackText.MaxKeyBindings do
2868 found = false
2869 for j in Rack_User[user].Sets do
2870 if Rack_User[user].Sets[j].keyindex == i then
2871 found = true
2872 end
2873 end
2874 if not found then
2875 freeindex = i
2876 i = ItemRackText.MaxKeyBindings+1 -- whimpy break
2877 end
2878 end
2879  
2880 return freeindex
2881 end
2882  
2883 -- removes a key by index, 1-10 (or .MaxKeyBindings)
2884 local function unbind_key_index(idx)
2885  
2886 if idx then
2887 local oldkey = GetBindingKey("EQUIPSET"..idx)
2888 if oldkey then
2889 SetBinding(oldkey)
2890 setglobal("BINDING_NAME_EQUIPSET"..idx,string.format(ItemRackText.BINDINGFORMAT,idx))
2891 end
2892 end
2893 end
2894  
2895 -- removes old key bindings, for initialization and preparation to bind a key
2896 -- if no setname given, then all keys are unbound
2897 local function unbind_keys(setname)
2898  
2899 local idx
2900  
2901 if not setname then
2902 for idx=1,ItemRackText.MaxKeyBindings do
2903 unbind_key_index(idx)
2904 end
2905 else
2906 idx = Rack_User[user].Sets[setname].keyindex
2907 unbind_key_index(idx)
2908 end
2909 SaveBindings(GetCurrentBindingSet())
2910  
2911 end
2912  
2913 -- this takes the current key bindings and updates the .Sets info -- usually as a result of user changing bindings outside the mod
2914 function ItemRack_AgreeOnKeyBindings()
2915 local i,oldkey,setname
2916  
2917 if ItemRack.KeyBindingsSettled then
2918 -- don't do this at startup, wait until we had a chance to initialize this player's keys
2919  
2920 for i in Rack_User[user].Sets do
2921 Rack_User[user].Sets[i].key = nil
2922 Rack_User[user].Sets[i].keyindex = nil
2923 end
2924  
2925 for i=1,10 do
2926 oldkey = GetBindingKey("EQUIPSET"..i)
2927 if oldkey then
2928 _,_,setname = string.find(getglobal("BINDING_NAME_EQUIPSET"..i) or "",ItemRackText.BINDINGSEARCH)
2929 if setname and Rack_User[user].Sets[setname] then
2930 Rack_User[user].Sets[setname].key = oldkey
2931 Rack_User[user].Sets[setname].keyindex = i
2932 end
2933 end
2934 end
2935 end
2936 end
2937  
2938 -- removes the currently selected set
2939 function ItemRack_Sets_Remove_OnClick()
2940  
2941 if Rack_User[user].Sets[ItemRack.SelectedName] then
2942 unbind_keys(ItemRack.SelectedName)
2943 Rack_User[user].Sets[ItemRack.SelectedName] = nil
2944 sets_message(string.format(ItemRackText.SETREMOVE,ItemRack.SelectedName))
2945 local i
2946 -- if an event has this set, remove the association
2947 for i in ItemRack_Users[user].Events do
2948 if ItemRack_Users[user].Events[i].setname==ItemRack.SelectedName then
2949 ItemRack_Users[user].Events[i] = nil
2950 end
2951 end
2952 ItemRack.SelectedName=nil
2953 ItemRack_Sets_Name:SetText("")
2954 build_icon()
2955 validate_set_buttons()
2956 ItemRack_Build_eventList()
2957 end
2958 end
2959  
2960 function ItemRack_UseSetBinding(v1)
2961  
2962 local i,setname
2963  
2964 -- look for sets with this key index
2965 for i in Rack_User[user].Sets do
2966 if Rack_User[user].Sets[i].keyindex == v1 then
2967 setname = i
2968 end
2969 end
2970  
2971 -- if we found a set with this key index, equip it
2972 if setname then
2973 ItemRack_EquipSet(setname)
2974 end
2975 end
2976  
2977 local function bind_key_to_set(key,setname)
2978  
2979 -- check if another set has this key binding
2980 local i
2981 for i in Rack_User[user].Sets do
2982 if Rack_User[user].Sets[i].key == key then
2983 unbind_keys(i) -- remove the other set's key binding if so
2984 Rack_User[user].Sets[i].keyindex = nil -- remove them from our table
2985 Rack_User[user].Sets[i].key = nil
2986 end
2987 end
2988  
2989 -- get the next free key binding
2990 local idx = Rack_User[user].Sets[setname].keyindex or get_free_binding()
2991  
2992 if idx then -- if we previously had a key binding or there's space for a new one, continue
2993 -- associate the key and index (1-10) with the set
2994 Rack_User[user].Sets[setname].key = key
2995 Rack_User[user].Sets[setname].keyindex = idx
2996 -- release any binding this key previously had
2997 unbind_keys(setname)
2998  
2999 -- finally bind the key
3000 ItemRack.KeyBindingsSettled = nil -- don't do an agree on key bindings until set
3001 local success = SetBinding(key,"EQUIPSET"..idx)
3002 if success then
3003 setglobal("BINDING_NAME_EQUIPSET"..idx,string.format(ItemRackText.BINDINGFORMAT,setname))
3004 sets_message(string.format(ItemRackText.BINDSET,key))
3005 else
3006 -- couldn't bind the key, forget we tried (should never get to this part)
3007 Rack_User[user].Sets[setname].key = nil
3008 Rack_User[user].Sets[setname].keyindex = nil
3009 end
3010 ItemRack.KeyBindingsSettled = 1
3011 SaveBindings(GetCurrentBindingSet())
3012 ItemRack_KeyBindFrame:Hide()
3013 end
3014 end
3015  
3016 -- hitting a key when "Press a key" mode up
3017 function ItemRack_Sets_KeyBind_OnKeyDown(arg1)
3018  
3019 local setname = ItemRack.SelectedName
3020  
3021 if arg1=="ESCAPE" then
3022 sets_message(ItemRackText.BINDCLEAR)
3023 ItemRack_KeyBindFrame:Hide()
3024  
3025 -- if this set has a key, restore its name and release the key binding
3026 unbind_keys(setname)
3027 Rack_User[user].Sets[setname].key = nil
3028 Rack_User[user].Sets[setname].keyindex = nil
3029  
3030 elseif arg1~="SHIFT" and arg1~="ALT" and arg1~="CTRL" then
3031 local key = arg1
3032 if IsShiftKeyDown() then
3033 key = "SHIFT-"..key
3034 elseif IsAltKeyDown() then
3035 key = "ALT-"..key
3036 elseif IsControlKeyDown() then
3037 key = "CTRL-"..key
3038 end
3039  
3040 local action
3041 action = GetBindingAction(key)
3042 if action and action~="" then
3043 StaticPopupDialogs["ITEMRACKKEYCONFIRM"] = {
3044 text = key.." is already used for "..tostring(getglobal("BINDING_NAME_"..action)).."\n\nOverwrite?",
3045 button1 = "Yes",
3046 button2 = "No",
3047 OnAccept = function() bind_key_to_set(key,setname) end,
3048 OnCancel = function() sets_message("No key binding set.") end,
3049 timeout = 0,
3050 whileDead = 1
3051 }
3052 StaticPopup_Show("ITEMRACKKEYCONFIRM")
3053 else
3054 bind_key_to_set(key,setname)
3055 end
3056 end
3057  
3058 build_icon()
3059  
3060 end
3061  
3062 function ItemRack_InitializeKeyBindings()
3063  
3064 local i, keyidx
3065  
3066 unbind_keys() -- remove all 10 keybindings (from previous users perhaps)
3067  
3068 for i in Rack_User[user].Sets do
3069 keyidx = Rack_User[user].Sets[i].keyindex
3070 if keyidx then
3071 setglobal("BINDING_NAME_EQUIPSET"..keyidx,string.format(ItemRackText.BINDINGFORMAT,i))
3072 SetBinding(Rack_User[user].Sets[i].key,"EQUIPSET"..keyidx)
3073 end
3074 end
3075 SaveBindings(GetCurrentBindingSet())
3076 ItemRack.KeyBindingsSettled = true
3077 end
3078  
3079 function ItemRack_Sets_Inv_OnEnter()
3080  
3081 local id = this:GetID()
3082  
3083 if ItemRack.SetBuild and ItemRack.SetBuild[id]==1 then
3084 ItemRack_BuildMenu(id,"SET")
3085 end
3086  
3087 end
3088  
3089 function ItemRack_Sets_ChosenIcon_OnClick()
3090  
3091 if IsAltKeyDown() then
3092  
3093 if ItemRack_Users[user].Inv[20] then
3094 remove_inv(20)
3095 elseif not ItemRack_Users[user].Inv[20] then
3096 ItemRack_Users[user].Visible="ON"
3097 ItemRack_Users[user].Inv[20] = 1
3098 ItemRackInv20Icon:SetTexture(get_item_info(20))
3099 table.insert(ItemRack_Users[user].Bar,20)
3100 draw_inv()
3101 end
3102 end
3103  
3104 validate_set_buttons()
3105 end
3106  
3107 --[[ Minimap button ]]--
3108  
3109 function ItemRack_Sets_Toggle(v1)
3110  
3111 if v1 then
3112 if not getglobal("ItemRack_Sets_SubFrame"..v1):IsVisible() then
3113 ItemRack_SetsFrame:Hide()
3114 end
3115 end
3116  
3117 if ItemRack_SetsFrame:IsVisible() then
3118 ItemRack_SetsFrame:Hide()
3119 else
3120 ItemRack_Sets_NewSet()
3121 end
3122 if v1 then
3123 ItemRack_Tab(v1)
3124 end
3125 end
3126  
3127 -- when user clicks the minimap button
3128 function ItemRack_IconFrame_OnClick(arg1)
3129  
3130 if arg1=="LeftButton" and ItemRack_Settings.DisableToggle=="OFF" then
3131 -- toggle bar
3132 ItemRack_Toggle()
3133 elseif arg1=="LeftButton" then
3134 -- toggle menu
3135 if ItemRack_MenuFrame:IsVisible() then
3136 ItemRack_MenuFrame:Hide()
3137 else
3138 ItemRack_BuildMenu(20,"MINIMAP")
3139 end
3140 else
3141 ItemRack_Sets_Toggle()
3142 end
3143  
3144 end
3145  
3146 --[[ Tabs ]]--
3147  
3148 function ItemRack_Tab(v1)
3149  
3150 ItemRack_Sets_SetSelect:Hide()
3151 ItemRack_EditEvent:Hide()
3152 ItemRack.SelectedTab = (v1 or ItemRack.SelectedTab) or 1
3153 for i=1,4 do
3154 getglobal("ItemRack_Sets_Tab"..i):UnlockHighlight()
3155 getglobal("ItemRack_Sets_SubFrame"..i):Hide()
3156 end
3157 getglobal("ItemRack_Sets_Tab"..ItemRack.SelectedTab):LockHighlight()
3158 getglobal("ItemRack_Sets_SubFrame"..ItemRack.SelectedTab):Show()
3159 end
3160  
3161 -- when set chooser dropdown shown
3162 function ItemRack_SetSelect_OnShow()
3163 -- remove ItemRack_SetsFrame from UISpecialFrames and add ItemRack_Sets_SetSelect
3164 make_escable("ItemRack_SetsFrame","remove")
3165 make_escable("ItemRack_Sets_SetSelect","add")
3166 end
3167  
3168 -- when set chooser dropdown hidden
3169 function ItemRack_SetSelect_OnHide()
3170 -- remove ItemRack_Sets_SetSelect from UISpecialFrames and add ItemRack_SetsFrame
3171 make_escable("ItemRack_Sets_SetSelect","remove")
3172 make_escable("ItemRack_SetsFrame","add")
3173 getglobal("ItemRack_Sets_SubFrame"..ItemRack.SelectedTab):Show()
3174 end
3175  
3176 -- when event editor shown
3177 function ItemRack_EditEvent_OnShow()
3178 ItemRack_Sets_SubFrame4:Hide()
3179 make_escable("ItemRack_SetsFrame","remove")
3180 make_escable("ItemRack_EditEvent","add")
3181 end
3182  
3183 -- when event editor hidden
3184 function ItemRack_EditEvent_OnHide()
3185 make_escable("ItemRack_EditEvent","remove")
3186 make_escable("ItemRack_SetsFrame","add")
3187 ItemRack_Sets_SubFrame4:Show()
3188 end
3189  
3190 -- when the sets frame is hidden, enable all events if events are on
3191 function ItemRack_SetsFrame_OnHide()
3192 ItemRack_MenuFrame:Hide()
3193 ItemRack_EnableAllEvents()
3194 end
3195  
3196 -- when the sets frame is shown, disable all events
3197 function ItemRack_SetsFrame_OnShow()
3198 if not ItemRack.SetIcons then
3199 ItemRack_Sets_Initialize()
3200 end
3201 ItemRack_DisableAllEvents()
3202 if ItemRack_Settings.EnableEvents=="ON" then
3203 sets_message(ItemRackText.EVENTSSUSPENDED)
3204 end
3205 end
3206  
3207 --[[ Events ]]--
3208  
3209 local function check_for_titanrider()
3210  
3211 if TITAN_RIDER_ID and (not TitanRider_EquipToggle or (TitanGetVar and TitanGetVar(TITAN_RIDER_ID,"EquipItems"))) then
3212 StaticPopupDialogs["ITEMRACK_TITANRIDER"] = {
3213 text = "It appears you have Titan Rider (a module of Titan Panel) enabled. Do not use this Mount event with Titan Rider enabled or it will create a mess (items on cursor, gear swapping back).",
3214 button1 = "Ok", timeout = 0, whileDead = 1, showAlert = 1, hideOnEscape = 1 }
3215 StaticPopup_Show("ITEMRACK_TITANRIDER")
3216 return 1
3217 else
3218 return nil
3219 end
3220 end
3221  
3222 -- changes the font size in the event edit window
3223 function ItemRack_ChangeEventFont()
3224 if ItemRack_Settings.LargeFont=="ON" then
3225 ItemRack_EventScript:SetFont("Fonts\\ARIALN.TTF",15)
3226 else
3227 ItemRack_EventScript:SetFont("Fonts\\FRIZQT__.TTF",11)
3228 end
3229 end
3230  
3231 -- builds eventList, a numerically-indexed list of events and their associated sets
3232 function ItemRack_Build_eventList()
3233  
3234 local i
3235 eventListSize = 1
3236 local oldeventname,eventname
3237  
3238 if ItemRack.SelectedEvent>0 then
3239 -- remember old selected event, it may move in the list
3240 oldeventname = eventList[ItemRack.SelectedEvent].name
3241 end
3242  
3243 scratchTableSize[1] = 1 -- size of each table for secondary sort
3244 scratchTableSize[2] = 1
3245  
3246 -- problems with secondary sort, so doing one manually - first split events into two tables: ones with a setname, ones without
3247 for i in ItemRack_Events do
3248 -- first split
3249 if i~="events_version" then
3250 _,_,class = string.find(i,"^(.+)%:")
3251 if ItemRack_Settings.ShowAllEvents=="ON" or (not class or class==UnitClass("player")) then
3252 if ItemRack_Users[user].Events[i] and Rack_User[user].Sets[ItemRack_Users[user].Events[i].setname] then
3253 scratchTable[1][scratchTableSize[1]] = i
3254 scratchTableSize[1] = scratchTableSize[1] + 1
3255 else
3256 scratchTable[2][scratchTableSize[2]] = i
3257 scratchTableSize[2] = scratchTableSize[2] + 1
3258 end
3259 end
3260 end
3261 end
3262 scratchTable[1][scratchTableSize[1]] = nil
3263 scratchTable[2][scratchTableSize[2]] = nil
3264  
3265 -- sort each half
3266 table.sort(scratchTable[1],function(e1,e2) return e1 and e2 and e1<e2 end)
3267 table.sort(scratchTable[2],function(e1,e2) return e1 and e2 and e1<e2 end)
3268  
3269 -- merge the halves
3270 eventListSize = 1
3271 for i=1,scratchTableSize[1]-1 do
3272 eventname = scratchTable[1][i]
3273 eventList[eventListSize] = eventList[eventListSize] or {}
3274 eventList[eventListSize].name = eventname
3275 eventList[eventListSize].trigger = ItemRack_Events[eventname].trigger
3276 eventList[eventListSize].delay = ItemRack_Events[eventname].delay
3277 eventList[eventListSize].script = ItemRack_Events[eventname].script
3278 eventList[eventListSize].setname = ItemRack_Users[user].Events[eventname].setname
3279 eventList[eventListSize].texture = Rack_User[user].Sets[eventList[eventListSize].setname].icon
3280 eventList[eventListSize].enabled = ItemRack_Users[user].Events[eventname].enabled
3281 eventListSize = eventListSize + 1
3282 end
3283 for i=1,scratchTableSize[2]-1 do
3284 eventname = scratchTable[2][i]
3285 eventList[eventListSize] = eventList[eventListSize] or {}
3286 eventList[eventListSize].name = eventname
3287 eventList[eventListSize].trigger = ItemRack_Events[eventname].trigger
3288 eventList[eventListSize].delay = ItemRack_Events[eventname].delay
3289 eventList[eventListSize].script = ItemRack_Events[eventname].script
3290 eventList[eventListSize].setname = nil
3291 eventList[eventListSize].texture = nil
3292 eventList[eventListSize].enabled = nil
3293 eventListSize = eventListSize + 1
3294 end
3295  
3296 ItemRack.SelectedEvent=0
3297 for i=1,eventListSize-1 do
3298 if eventList[i].name==oldeventname then
3299 ItemRack.SelectedEvent = i
3300 end
3301 end
3302  
3303 ItemRack_Validate_EventList_Buttons()
3304 ItemRack_Events_ScrollFrame_Update()
3305 end
3306  
3307 -- update for the list of events scrollframe
3308 function ItemRack_Events_ScrollFrame_Update()
3309  
3310 local i, texture, idx, button, icon, enable, name
3311 local offset = FauxScrollFrame_GetOffset(ItemRack_Events_ScrollFrame)
3312  
3313 FauxScrollFrame_Update(ItemRack_Events_ScrollFrame, eventListSize-1, 7, 26 )
3314  
3315 for i=1,7 do
3316 idx = offset + i
3317 button = getglobal("ItemRack_Event"..i)
3318 if idx<eventListSize then
3319 if ItemRack_Settings.ShowAllEvents=="ON" then
3320 getglobal("ItemRack_Event"..i.."Name"):SetText(eventList[idx].name)
3321 else
3322 _,_,name = string.find(eventList[idx].name,"^.+%:(.+)")
3323 name = name or eventList[idx].name
3324 getglobal("ItemRack_Event"..i.."Name"):SetText(name)
3325 end
3326 icon = getglobal("ItemRack_Event"..i.."Icon")
3327 enable = getglobal("ItemRack_Event"..i.."Enable")
3328 if eventList[idx].setname then
3329 icon:SetNormalTexture(eventList[idx].texture)
3330 icon:SetPushedTexture(eventList[idx].texture)
3331 enable:SetChecked(eventList[idx].enabled)
3332 enable:Show()
3333 else
3334 icon:SetNormalTexture("Interface\\Icons\\INV_Misc_QuestionMark")
3335 icon:SetPushedTexture("Interface\\Icons\\INV_Misc_QuestionMark")
3336 enable:Hide()
3337 end
3338 button:Show()
3339 else
3340 button:Hide()
3341 end
3342 if idx==ItemRack.SelectedEvent then
3343 button:LockHighlight()
3344 else
3345 button:UnlockHighlight()
3346 end
3347 end
3348 end
3349  
3350 -- onclick for events in the list, to lock highlight
3351 function ItemRack_EventsList_OnClick(arg1)
3352  
3353 local idx = this:GetID() + FauxScrollFrame_GetOffset(ItemRack_Events_ScrollFrame)
3354  
3355 if idx<eventListSize then
3356 ItemRack.SelectedEvent = (ItemRack.SelectedEvent==idx) and 0 or idx
3357 end
3358 ItemRack_Validate_EventList_Buttons()
3359 ItemRack_Events_ScrollFrame_Update()
3360 end
3361  
3362 -- returns the eventList index for the *parent* of "this"
3363 local function event_idx()
3364 return this:GetParent():GetID() + FauxScrollFrame_GetOffset(ItemRack_Events_ScrollFrame)
3365 end
3366  
3367 function ItemRack_EventsList_EnableOnClick()
3368  
3369 local idx = event_idx()
3370  
3371 if idx<eventListSize then
3372 local eventname = eventList[idx].name
3373 if not ItemRack_Users[user].Events[eventname] then
3374 ItemRack_Users[user].Events[eventname] = {}
3375 end
3376 ItemRack_Users[user].Events[eventname].enabled = this:GetChecked()
3377 if eventname=="Mount" and check_for_titanrider() then
3378 ItemRack_Users[user].Events[eventname].enabled = nil
3379 end
3380 end
3381 ItemRack_Build_eventList()
3382 end
3383  
3384 -- onenter of an event icon either a tooltip for an undefined event or the contents of the set
3385 function ItemRack_EventsListIcon_OnEnter()
3386  
3387 local idx = event_idx()
3388  
3389 if idx<eventListSize then
3390 local setname = eventList[idx].setname
3391 if not setname then
3392 ItemRack_OnTooltip(ItemRackText.UNDEFINEDEVENT_TEXT,ItemRackText.UNDEFINEDEVENT_TOOLTIP)
3393 else
3394 ItemRack_Sets_Tooltip(setname)
3395 end
3396 end
3397 end
3398  
3399 function ItemRack_Validate_EventList_Buttons()
3400  
3401 if ItemRack.SelectedEvent==0 then
3402 ItemRack_Events_DeleteButton:Disable()
3403 ItemRack_Events_EditButton:Disable()
3404 else
3405 ItemRack_Events_DeleteButton:Enable()
3406 ItemRack_Events_EditButton:Enable()
3407 end
3408 end
3409  
3410 -- clicking event icon summons set selection window
3411 function ItemRack_EventsListIcon_OnClick()
3412  
3413 local idx = event_idx()
3414  
3415 if idx<eventListSize then
3416 if eventList[idx].name=="Mount" and check_for_titanrider() then
3417 return
3418 else
3419 ItemRack.SelectedEvent = idx
3420 ItemRack_Events_ScrollFrame_Update()
3421 ItemRack_Validate_EventList_Buttons()
3422 ItemRack_Sets_DropDownButton_OnClick()
3423 end
3424 end
3425 end
3426  
3427 -- handler for buttons clicked in the events pane
3428 function ItemRack_EventButtons(v1)
3429  
3430 local i
3431 local others = ""
3432  
3433 if v1=="Delete" then
3434 local eventname = eventList[ItemRack.SelectedEvent].name
3435  
3436 ItemRack_DisableEvent(eventname)
3437  
3438 i = ItemRack_Users[user].Events[eventname]
3439 ItemRack_Users[user].Events[eventname] = nil
3440  
3441 if not i then
3442  
3443 for i in ItemRack_Users do
3444 if ItemRack_Users[i].Events and ItemRack_Users[i].Events[eventname] then
3445 others = others..i..", "
3446 end
3447 end
3448  
3449 if others=="" then
3450 sets_message("\""..eventname.."\" removed completely.")
3451 ItemRack_Events[eventname] = nil
3452 else
3453 others = string.gsub(others,", $","")
3454 sets_message("\""..eventname.."\" is used by: "..others)
3455 end
3456 else
3457 sets_message("\""..eventname.."\" disassociated.")
3458 end
3459 ItemRack_Events_ScrollFrameScrollBar:SetValue(0)
3460 elseif v1=="New" then
3461 ItemRack_EventScript:SetText("")
3462 ItemRack_EventName:SetText("")
3463 ItemRack_EventTrigger:SetText("")
3464 ItemRack_EventDelay:SetText("")
3465 ItemRack_EditEvent:Show()
3466 ItemRack_EventName:SetFocus()
3467 elseif v1=="Edit" then
3468 if ItemRack.SelectedEvent==0 then
3469 -- if we're here and SelectedEvent==0, it was a double-click to a selected event. Reselect it
3470 ItemRack_EventsList_OnClick("LeftButton")
3471 end
3472 -- if SelectedEvent defined, then either edit button or double-click to previously unselected event
3473 local eventname = eventList[ItemRack.SelectedEvent].name
3474  
3475 ItemRack_EventScript:SetText(ItemRack_Events[eventname].script or "")
3476 ItemRack_EventName:SetText(eventname)
3477 ItemRack_EventTrigger:SetText(ItemRack_Events[eventname].trigger or "")
3478 ItemRack_EventDelay:SetText(ItemRack_Events[eventname].delay or "")
3479 ItemRack_EditEvent:Show()
3480 ItemRack_EventScript:SetFocus()
3481  
3482 elseif v1=="Save" then
3483 local eventname = eventList[ItemRack.SelectedEvent] and eventList[ItemRack.SelectedEvent].name
3484 local name = ItemRack_EventName:GetText()
3485 if name and string.len(name)>0 then
3486 if not ItemRack_Events[name] then
3487 ItemRack_Events[name] = {}
3488 end
3489 ItemRack_Events[name].script = ItemRack_EventScript:GetText()
3490 ItemRack_Events[name].trigger = ItemRack_EventTrigger:GetText()
3491 ItemRack_Events[name].delay = tonumber(ItemRack_EventDelay:GetText()) or 0
3492 sets_message("Event \""..name.."\" saved.")
3493 ItemRack_EditEvent:Hide()
3494 else
3495 sets_message("Event not saved. Need a name at least.")
3496 end
3497 elseif v1=="Test" then
3498 RunScript(ItemRack_EventScript:GetText() or "")
3499 end
3500 ItemRack_Build_eventList()
3501 end
3502  
3503 --[[ Event Registration ]]--
3504  
3505 ItemRack.Register = {} -- game events (UNIT_AURA, etc) are stored here
3506  
3507 -- debug function, to list registered game events and the mod events they are for
3508 function ItemRack_ListEvents()
3509 local i,j,foundtrigger,foundevent
3510  
3511 for i in ItemRack.Register do
3512 foundtrigger=1
3513 foundevent=nil
3514 DEFAULT_CHAT_FRAME:AddMessage("__"..i.."__")
3515 for j in ItemRack.Register[i] do
3516 foundevent=1
3517 DEFAULT_CHAT_FRAME:AddMessage(j)
3518 end
3519 if not foundevent then
3520 DEFAULT_CHAT_FRAME:AddMessage("none")
3521 end
3522 end
3523 if not foundtrigger then
3524 DEFAULT_CHAT_FRAME:AddMessage("No events registered.")
3525 end
3526 end
3527  
3528 -- enables a specific eventname ("Riding","Warrior:Berserk",etc)
3529 function ItemRack_EnableEvent(eventname)
3530  
3531 if not ItemRack_Events[eventname] then return end
3532  
3533 local trigger = ItemRack_Events[eventname].trigger
3534  
3535 if not ItemRack.Register[trigger] then
3536 ItemRack_RegisterFrame:RegisterEvent(trigger)
3537 ItemRack.Register[trigger] = {}
3538 end
3539 ItemRack.Register[trigger][eventname] = 1
3540 end
3541  
3542 -- disables a sepcific eventname ("Riding","Warrior:Berserk",etc)
3543 function ItemRack_DisableEvent(eventname)
3544  
3545 if not ItemRack_Events[eventname] then return end
3546  
3547 local trigger = ItemRack_Events[eventname].trigger
3548 local has_event,i
3549  
3550 if ItemRack.Register[trigger] then
3551 ItemRack.Register[trigger][eventname] = nil
3552 for i in ItemRack.Register[trigger] do has_event=1 end
3553 if not has_event then
3554 ItemRack_RegisterFrame:UnregisterEvent(trigger)
3555 ItemRack.Register[trigger] = nil
3556 end
3557 else
3558 ItemRack_RegisterFrame:UnregisterEvent(trigger)
3559 end
3560 end
3561  
3562 -- use this to initialize, enable or refresh Register
3563 function ItemRack_EnableAllEvents()
3564 local i
3565  
3566 if ItemRack_Settings.Notify=="OFF" then
3567 -- if notify is off, see if any ITEMRACK_NOTIFY events are registered and turn on notify
3568 for i in ItemRack_Users[user].Events do
3569 if ItemRack_Users[user].Events[i].enabled and ItemRack_Events[i].trigger=="ITEMRACK_NOTIFY" then
3570 ItemRack_Settings.Notify="ON"
3571 -- ItemRack_Opt_Notify:SetChecked(1)
3572 DEFAULT_CHAT_FRAME:AddMessage("ItemRack: Notify has been turned on for Event: |cFFFFFF00"..i)
3573 end
3574 end
3575 end
3576  
3577 if ItemRack_Settings.EnableEvents=="ON" then
3578 for i in ItemRack_Users[user].Events do
3579 if ItemRack_Users[user].Events[i].enabled then
3580 ItemRack_EnableEvent(i)
3581 else
3582 ItemRack_DisableEvent(i)
3583 end
3584 end
3585 ItemRackFrame:RegisterEvent("PLAYER_AURAS_CHANGED")
3586 else
3587 ItemRack_DisableAllEvents()
3588 end
3589 end
3590  
3591 -- use this to trun off all event watching.
3592 function ItemRack_DisableAllEvents()
3593 local i
3594  
3595 for i in ItemRack_Users[user].Events do
3596 ItemRack_DisableEvent(i)
3597 end
3598 ItemRackFrame:UnregisterEvent("PLAYER_AURAS_CHANGED")
3599 end
3600  
3601 --[[ Event Processing ]]--
3602  
3603 ItemRack.EventQueue = {} -- indexed by events ("Riding", "Warrior:Battle") of GetTime()+delay to run
3604  
3605 -- these two holders of arg1 and arg2 are separate for minimal processing to happen
3606 -- a loop holding arg1-9 in a table takes 3.1 seconds for 100k iterations
3607 -- storing just the first two values and moving on drops processing to 0.28 seconds
3608 ItemRack.EventQueueArg1 = {} -- indexed by events also, values of arg1
3609 ItemRack.EventQueueArg2 = {} -- indexed by events also, values of arg2
3610  
3611 -- runs the eventname script, event = "Riding", "Warrior:Battle", etc
3612 local function run_event_script(eventname)
3613  
3614 if eventname and ItemRack_Users[user].Events[eventname] then
3615 ItemRack.EventSetName = ItemRack_Users[user].Events[eventname].setname
3616 ItemRack.EventEventName = eventname
3617 if ItemRack.EventSetName then
3618 RunScript(ItemRack_Events[eventname].script)
3619 ItemRack.EventSetName = nil
3620 ItemRack.EventEventName = nil
3621 end
3622 end
3623 end
3624  
3625 -- events("triggers") defined in game go through here
3626 function ItemRack_RegisterFrame_OnEvent(event)
3627 local i,j
3628  
3629 if ItemRack.Register[event] then
3630 for i in ItemRack.Register[event] do
3631 if ItemRack_Events[i].delay==0 then
3632 -- EventSetName is the name of the set to use for EquipSet(), it's the set associated with the event
3633 run_event_script(i)
3634 else
3635 ItemRack.EventQueue[i] = GetTime()+ItemRack_Events[i].delay
3636 ItemRack.EventQueueArg1[i] = arg1
3637 ItemRack.EventQueueArg2[i] = arg2
3638 ItemRack_RegisterFrame:Show() -- turn on OnUpdate
3639 end
3640 end
3641 end
3642 end
3643  
3644 local register_timer = 0
3645 function ItemRack_RegisterFrame_OnUpdate()
3646  
3647 local i
3648  
3649 -- check every .25 seconds if time has elapsed for events with a delay
3650 register_timer = register_timer + arg1
3651 if register_timer > .25 then
3652 register_timer = 0
3653 local current_time = GetTime()
3654 local queue_exists = nil
3655 for i in ItemRack.EventQueue do
3656 queue_exists = 1 -- something is in the queue
3657 if ItemRack.EventQueue[i]<current_time then
3658 local holdarg1,holdarg2 = arg1,arg2
3659 arg1 = ItemRack.EventQueueArg1[i]
3660 arg2 = ItemRack.EventQueueArg2[i]
3661 run_event_script(i)
3662 arg1 = holdarg1
3663 arg2 = holdarg2
3664 ItemRack.EventQueue[i] = nil
3665 end
3666 end
3667 if not queue_exists then
3668 ItemRack_RegisterFrame:Hide() -- shut down OnUpdates when nothing left to process
3669 end
3670 end
3671 end
3672  
3673 -- saves the items currently worn that EventSetName defines into a set named "ItemRack-Saved"
3674 -- a parameter passed will use the passed setname, for use in macros, ie SaveSet("pvp")
3675 function ItemRack_SaveSet(v1)
3676 -- NO LONGER NEEDED: EquipSet saves what was worn previously
3677 end
3678  
3679 -- equips the items previous saved in SaveSet(). it only equips items in the slots associated with the set
3680 -- a parameter passed will use the passed setname, for use in macros, ie LoadSet("pvp")
3681 function ItemRack_LoadSet(setname)
3682  
3683 if not setname and ItemRack.EventSetName then
3684 setname = ItemRack.EventSetName
3685 end
3686  
3687 Rack.UnequipSet(setname)
3688 end
3689  
3690 function ItemRack_ToggleEvents()
3691  
3692 if ItemRack_Settings.EnableEvents=="OFF" then
3693 ItemRack_Settings.EnableEvents="ON"
3694 ItemRack_Opt_EnableEvents:SetChecked(1)
3695 ItemRack_EnableAllEvents()
3696 DEFAULT_CHAT_FRAME:AddMessage("|cFF66AAFFItemRack Automatic Events are now |cFF55FF55ON")
3697 else
3698 ItemRack_Settings.EnableEvents="OFF"
3699 ItemRack_Opt_EnableEvents:SetChecked(nil)
3700 ItemRack_DisableAllEvents()
3701 DEFAULT_CHAT_FRAME:AddMessage("|cFF66AAFFItemRack Automatic Events are now |cFFFF5555OFF")
3702 end
3703 end
3704  
3705 function ItemRack_EventsList_OnEnter()
3706 local idx,notes = this:GetID() + FauxScrollFrame_GetOffset(ItemRack_Events_ScrollFrame)
3707  
3708 if eventList[idx].name and ItemRack_Settings.ShowTooltips=="ON" then
3709 set_tooltip_anchor(this)
3710 GameTooltip:AddLine(eventList[idx].name)
3711 if eventList[idx].setname then
3712 GameTooltip:AddLine("Set: "..eventList[idx].setname)
3713 end
3714 _,_,notes = string.find(eventList[idx].script or "","--%[%[(.+)%]%]")
3715 if notes then
3716 GameTooltip:AddLine(notes,.8,.8,.8,1)
3717 end
3718 GameTooltip:Show()
3719 end
3720 end
3721  
3722 -- toggles a set, remembering what it wore previous to equipping the set
3723 function ItemRack_ToggleSet(setname)
3724 Rack.ToggleSet(setname)
3725 end
3726  
3727 --[[ Event script helper functions
3728  
3729 These are not necessary. They can be completely encapsulated in the scripts themselves. They're here for convenience. ]]
3730  
3731 -- this is a special function to use for mount events. returns true if player is mounted, nil otherwise
3732 -- pass a non-nil value for v1 to do a slow/thorough scan
3733 function ItemRack_PlayerMounted(v1)
3734  
3735 local i,buff,mounted
3736  
3737 for i=1,24 do
3738 buff = UnitBuff("player",i)
3739 if buff then
3740 if problem_mounts[buff] or v1 or string.find(buff,"QirajiCrystal_") then
3741 -- hunter could be in group, could be warlock epic mount etc, check if this is truly a mount
3742 -- or if v1 is set to true, always check every buff. sigh this is slow but really no way around it without more data from users
3743 Rack_TooltipScan:SetUnitBuff("player",i)
3744 if string.find(Rack_TooltipScanTextLeft2:GetText() or "",ItemRackText.MOUNTCHECK) then
3745 mounted = true
3746 i = 25
3747 end
3748 elseif string.find(buff,"Mount_") then
3749 mounted = true
3750 i = 25
3751 end
3752 else
3753 i = 25
3754 end
3755 end
3756  
3757 return mounted
3758 end
3759  
3760 -- returns the name of the form the player is in
3761 function ItemRack_GetForm()
3762  
3763 local i,name,form
3764  
3765 for i=1,GetNumShapeshiftForms() do
3766 _,name,is_active = GetShapeshiftFormInfo(i)
3767 if is_active then
3768 form = name
3769 end
3770 end
3771 return form
3772 end
3773  
3774 -- gathers active buffs into ItemRack.Buffs and sends it via RegisterFrame for events
3775 function ItemRack_BuffsChanged()
3776 local buffName,buffTexture
3777 for i in ItemRack.Buffs do
3778 ItemRack.Buffs[i] = nil
3779 end
3780 for i=1,24 do
3781 Rack_TooltipScan:SetUnitBuff("player",i)
3782 buffName = Rack_TooltipScanTextLeft1:GetText()
3783 buffTexture = UnitBuff("player",i)
3784 if buffTexture then
3785 ItemRack.Buffs[buffName] = 1
3786 ItemRack.Buffs[buffTexture] = 1
3787 else
3788 break
3789 end
3790 end
3791 local oldarg1 = arg1
3792 arg1=ItemRack.Buffs
3793 ItemRack_RegisterFrame_OnEvent("ITEMRACK_BUFFS_CHANGED")
3794 arg1 = oldarg1
3795 end
3796  
3797 -- returns 1 if all pieces of setname are equipped, nil otherwise
3798 function ItemRack_IsSetEquipped(setname)
3799 return Rack.IsSetEquipped(setname)
3800 end
3801  
3802 -- returns three parameters: table of sets, current set name, current set texture
3803 function ItemRack_GetUserSets()
3804 local texture = "Interface\\AddOns\\ItemRack\\ItemRack-Icon"
3805 local setname = ItemRack_CurrentSet()
3806 if setname and Rack_User[user].Sets[setname] and not string.find(setname,"^Rack") and not string.find(setname,"^ItemRack") then
3807 texture = Rack_User[user].Sets[setname].icon
3808 end
3809 return Rack_User[user].Sets, setname, texture
3810 end
3811  
3812 --[[ Rack 2.0 code begins here ]]--
3813  
3814 --[[ There is some redundancy because everything that follows is a part of ItemRack 2.0.
3815 Everything above this section will be scrapped for 2.0 ]]
3816  
3817 Rack = {
3818 version = 1.9,
3819 debug = nil,
3820  
3821 TimerPool = {}, -- timer tables added here ["InvUpdate"]={timer,limit,func,rep}
3822  
3823 SetSwapping = nil, -- name of a set currently being swapped
3824 SwapList = {}, -- individual item swap details go here
3825 LockList = {}, -- tables of bags where slots are locked (to be skipped in FindItem and FindSpace)
3826 CombatQueue = {} -- table of items to swap in when dropping out of combat/death
3827 }
3828  
3829 Rack.SlotInfo = {
3830 [0] = { name="AmmoSlot", swappable=1, INVTYPE_AMMO=1 },
3831 [1] = { name="HeadSlot", INVTYPE_HEAD=1 },
3832 [2] = { name="NeckSlot", INVTYPE_NECK=1 },
3833 [3] = { name="ShoulderSlot", INVTYPE_SHOULDER=1 },
3834 [4] = { name="ShirtSlot", INVTYPE_BODY=1 },
3835 [5] = { name="ChestSlot", INVTYPE_CHEST=1, INVTYPE_ROBE=1 },
3836 [6] = { name="WaistSlot", INVTYPE_WAIST=1 },
3837 [7] = { name="LegsSlot", INVTYPE_LEGS=1 },
3838 [8] = { name="FeetSlot", INVTYPE_FEET=1 },
3839 [9] = { name="WristSlot", INVTYPE_WRIST=1 },
3840 [10] = { name="HandsSlot", INVTYPE_HAND=1 },
3841 [11] = { name="Finger0Slot", INVTYPE_FINGER=1 },
3842 [12] = { name="Finger1Slot", INVTYPE_FINGER=1 },
3843 [13] = { name="Trinket0Slot", INVTYPE_TRINKET=1 },
3844 [14] = { name="Trinket1Slot", INVTYPE_TRINKET=1 },
3845 [15] = { name="BackSlot", INVTYPE_CLOAK=1 },
3846 [16] = { name="MainHandSlot", swappable=1, INVTYPE_WEAPONMAINHAND=1, INVTYPE_2HWEAPON=1, INVTYPE_WEAPON=1 },
3847 [17] = { name="SecondaryHandSlot", swappable=1, INVTYPE_WEAPON=1, INVTYPE_WEAPONOFFHAND=1, INVTYPE_SHIELD=1, INVTYPE_HOLDABLE=1 },
3848 [18] = { name="RangedSlot", swappable=1, INVTYPE_RANGED=1, INVTYPE_THROWN=1, INVTYPE_RANGEDRIGHT=1 },
3849 [19] = { name="TabardSlot", INVTYPE_TABARD=1 },
3850 }
3851  
3852 --[[ Initialization ]]
3853  
3854 function Rack.Initialize()
3855  
3856 for i=0,19 do Rack.SwapList[i]={} end -- create blank SwapList table
3857 for i=-2,10 do Rack.LockList[i]={} end -- create a blank Locked table
3858  
3859 if not Rack_User then
3860 Rack_User = {}
3861 Rack.ConvertOldSets()
3862 end
3863  
3864 if not Rack_User[user] then
3865 Rack_User[user] = { Sets={}, CurrentSet=nil }
3866 end
3867  
3868 Rack_User[user].Sets["Rack-CombatQueue"] = {}
3869 for i=0,19 do
3870 Rack_User[user].Sets["Rack-CombatQueue"][i] = { id=nil, name=nil }
3871 end
3872  
3873 -- note: these timers are added to a pool, they don't affect processing time unless started
3874 Rack.CreateTimer("WaitToIterate",Rack.IterateWait,1) -- itemlock iterate pause
3875 Rack.CreateTimer("IconDragging",Rack.IconDragging,0,1) -- minimap button dragging
3876 Rack.CreateTimer("ScaleUpdate",Rack.ScaleUpdate,.1,1) -- scaling
3877 Rack.CreateTimer("TooltipUpdate",Rack.TooltipUpdate,1,1) -- tooltip update
3878 Rack.CreateTimer("ControlFrame",Rack.ControlFrame,.75,1) -- controls on bar
3879 Rack.CreateTimer("CooldownUpdate",ItemRack_CooldownUpdate_OnUpdate,1,1) -- cooldown/notify
3880 Rack.CreateTimer("MenuFrame",Rack.MenuFrame,.25,1) -- menu mouseover check
3881 Rack.CreateTimer("InvUpdate",ItemRack_InvUpdate_OnUpdate,.25,1) -- inventory throttle
3882 end
3883  
3884 function Rack.OnEvent()
3885  
3886 if event=="ITEM_LOCK_CHANGED" then
3887 Rack.OnItemLockChanged()
3888 elseif (event=="PLAYER_REGEN_ENABLED" or event=="PLAYER_UNGHOST" or event=="PLAYER_ALIVE") and (not Rack.IsPlayerReallyDead() and not UnitAffectingCombat("player")) then
3889 -- player is coming out of combat or being res'ed. EquipSet the CombatQueue
3890 local somethingQueued
3891 for i=0,19 do
3892 if Rack.CombatQueue[i] then
3893 Rack_User[user].Sets["Rack-CombatQueue"][i].id=Rack.CombatQueue[i]
3894 Rack.CombatQueue[i] = nil
3895 somethingQueued = 1
3896 else
3897 Rack_User[user].Sets["Rack-CombatQueue"][i].id = nil
3898 Rack_User[user].Sets["Rack-CombatQueue"][i].name = nil
3899 end
3900 getglobal("ItemRackInv"..i.."Queue"):Hide()
3901 getglobal(ItemRack.Indexes[i].paperdoll_slot.."Queue"):Hide()
3902 end
3903 if somethingQueued then
3904 Rack.EquipSet("Rack-CombatQueue")
3905 end
3906 end
3907 end
3908  
3909 --[[ Item information ]]
3910  
3911 -- returns itemTexture, itemID, itemName, itemSlot of an item in container(bag,slot) or inventory("player",bag)
3912 function Rack.GetItemInfo(bag,slot)
3913 local i,j,id,itemLink,itemID,itemSlot,itemTexture,itemName
3914  
3915 if slot then -- this is a container item
3916 itemLink = GetContainerItemLink(bag,slot)
3917 else
3918 itemLink = GetInventoryItemLink("player",bag)
3919 end
3920  
3921 if itemLink then
3922 _,_,id = string.find(itemLink,"(item:%d+:%d+:%d+:%d+)")
3923 _,_,itemID = string.find(id or "","item:(%d+:%d+:%d+):%d+")
3924 itemName,_,_,_,_,_,_,itemSlot,itemTexture = GetItemInfo(id)
3925 elseif not slot then -- if no link and this is an inventory slot, missing or ammo
3926 _,itemTexture = GetInventorySlotInfo(Rack.SlotInfo[bag].name) -- get paperdoll texture
3927 itemID = 0 -- assume empty slot
3928 if bag==0 then -- this is an ammo slot
3929 local ammoTexture = GetInventoryItemTexture("player",0)
3930 if ammoTexture then
3931 Rack_TooltipScan:SetInventoryItem("player",0)
3932 itemName = Rack_TooltipScanTextLeft1:GetText()
3933 for i=0,4 do -- look through containers to get itemID of this ammo
3934 for j=1,GetContainerNumSlots(i) do
3935 if itemName==Rack.GetContainerItemName(i,j) then
3936 _,itemID,_,itemSlot = Rack.GetItemInfo(i,j)
3937 i,j = 99,99
3938 end
3939 end
3940 end
3941 itemTexture = ammoTexture
3942 end
3943 end
3944 end
3945  
3946 return itemTexture, itemID, itemName, itemSlot
3947 end
3948  
3949 -- returns the name of an item in bag,slot
3950 function Rack.GetContainerItemName(bag,slot)
3951 local _,_,name = string.find(GetContainerItemLink(bag,slot) or "","%[(.+)%]")
3952 return name
3953 end
3954  
3955 -- converts a name to an itemID by searching through inventory, bags and bank for the item
3956 function Rack.GetItemID(itemName)
3957  
3958 local inv,bag,slot = Rack.FindItem(nil,itemName)
3959 local itemID
3960  
3961 if inv then
3962 _,itemID = Rack.GetItemInfo(inv)
3963 elseif bag and slot then
3964 _,itemID = Rack.GetItemInfo(bag,slot)
3965 end
3966  
3967 return itemID
3968 end
3969  
3970 -- returns the name and texture of an item by its itemID
3971 function Rack.GetNameByID(itemID)
3972 local name,texture
3973 local _,_,id = string.find(itemID or "","(%d+):%d+:%d+")
3974 name,_,_,_,_,_,_,_,texture = GetItemInfo(id or "")
3975 if itemID==0 then
3976 name = "(empty)"
3977 texture = "Interface\\PaperDoll\\UI-Backpack-EmptySlot"
3978 end
3979 return name,texture
3980 end
3981  
3982 -- returns true if the bagid (0-4) is a normal "Container", as opposed to quivers and ammo pouches
3983 function Rack.ValidBag(bagid)
3984  
3985 local linkid,bagtype,legal
3986  
3987 if bagid==0 or bagid==-1 then
3988 legal = true
3989 else
3990 local invID = ContainerIDToInventoryID(bagid)
3991 _,_,linkid = string.find(GetInventoryItemLink("player",invID) or "","item:(%d+)")
3992 if linkid then
3993 _,_,_,_,_,bagtype = GetItemInfo(linkid)
3994 if bagtype==ItemRackText.INVTYPE_CONTAINER then -- "Bag" for enUS clients, "Container" for other clients
3995 legal = true -- this is a true container
3996 end
3997 end
3998 end
3999  
4000 return legal
4001 end
4002  
4003 function Rack.FindSpaceInBag(bag)
4004 if Rack.ValidBag(bag) then
4005 for j=1,GetContainerNumSlots(bag) do
4006 if not Rack.LockList[bag][j] and not GetContainerItemLink(bag,j) then
4007 return j
4008 end
4009 end
4010 end
4011 end
4012  
4013 -- pass a value for bank to find a free bank slot
4014 function Rack.FindSpace(bank)
4015 local slot
4016 if bank and ItemRack.BankIsOpen then -- search bank
4017 for _,i in ItemRack.BankSlots do
4018 slot = Rack.FindSpaceInBag(i)
4019 if slot then
4020 Rack.LockList[i][slot] = 1
4021 return i,slot
4022 end
4023 end
4024 else
4025 for i=4,0,-1 do
4026 slot = Rack.FindSpaceInBag(i)
4027 if slot then
4028 Rack.LockList[i][slot] = 1
4029 return i,slot
4030 end
4031 end
4032 end
4033 end
4034  
4035 -- clears locks on all bag slots
4036 function Rack.ClearLockList(bag,slot)
4037 if not bag then
4038 for i=-2,10 do
4039 for j in Rack.LockList[i] do
4040 Rack.LockList[i][j] = nil
4041 end
4042 end
4043 else
4044 Rack.LockList[bag][slot] = nil
4045 end
4046 end
4047  
4048 -- returns inv,bag,slot of an itemID, or itemName if itemID doesn't exist
4049 function Rack.FindItem(itemID,itemName,passive)
4050  
4051 local bagStart,bagEnd,i,j,id,name = 0,4
4052 local inv,bag,slot
4053  
4054 if itemID then
4055  
4056 for i=bagStart,bagEnd do -- check bags for itemID
4057 for j=1,GetContainerNumSlots(i) do
4058 if not Rack.LockList[i][j] or passive then
4059 _,id = Rack.GetItemInfo(i,j)
4060 if id==itemID then
4061 bag,slot = i,j
4062 i,j = 99,99
4063 end
4064 end
4065 end
4066 end
4067  
4068 if not bag then
4069 for i=0,19 do -- if not in bags, check on person for itemID
4070 if not Rack.LockList[-2][i] or passive then
4071 _,id = Rack.GetItemInfo(i)
4072 if id==itemID then
4073 inv = i
4074 i = 99
4075 end
4076 end
4077 end
4078 end
4079 end
4080  
4081 -- if an exact itemID match wasn't found, search by name
4082 if not inv and not bag and not slot then
4083 itemName = itemName or Rack.GetNameByID(itemID)
4084 if itemName then
4085  
4086 for i=bagStart,bagEnd do -- check bags for itemID
4087 for j=1,GetContainerNumSlots(i) do
4088 if not Rack.LockList[i][j] or passive then
4089 _,_,name = Rack.GetItemInfo(i,j)
4090 if name==itemName then
4091 bag,slot = i,j
4092 i,j = 99,99
4093 end
4094 end
4095 end
4096 end
4097  
4098 if not bag then
4099 for i=0,19 do -- check on person for itemName
4100 if not Rack.LockList[-2][i] or passive then
4101 _,_,name = Rack.GetItemInfo(i)
4102 if name==itemName then
4103 inv = i
4104 i = 99
4105 end
4106 end
4107 end
4108 end
4109 end
4110 end
4111  
4112 return inv,bag,slot
4113 end
4114  
4115 -- performs a FindItem on a set entry ([0]-[19]) and updates itemid if one wasn't there
4116 function Rack.FindSetItem(setslot)
4117 local inv,bag,slot = Rack.FindItem(setslot.id,setslot.name)
4118 if not setslot.id and setslot.name and (inv or bag or slot) then
4119 setslot.id = Rack.GetItemID(setslot.name)
4120 end
4121 return inv,bag,slot
4122 end
4123  
4124 function Rack.InvToInvSwap(inv1,inv2)
4125  
4126 local swap = Rack.SwapList
4127  
4128 if swap[inv1].sourceInv==inv2 and swap[inv2].sourceInv==inv1 then
4129 PickupInventoryItem(inv1)
4130 PickupInventoryItem(inv2)
4131 Rack.ClearSwapListEntry(inv1)
4132 Rack.ClearSwapListEntry(inv2)
4133 end
4134 end
4135  
4136  
4137 --[[ Queue maintenance
4138  
4139 To prevent garbage creation in creating/destroying multi-level tables, queue entries are reused and only
4140 the index to that queue entry is created/destroyed.
4141  
4142 .SwapQueue is where the tables sit, indexed arbitrarily by the first available one returned by GetFreeQueueEntry
4143 .SwapQueueOrder is a numerically-indexed table of indexes into .SwapQueue in the order the swap should happen
4144 ]]
4145  
4146 Rack.SwapQueue = { [1]={ direction = "END" } } -- numerically-indexed queue of swaps to perform
4147 Rack.SwapQueueOrder = {} -- numerically-indexed queue of numbers in the order they're to be performed
4148  
4149 -- wipes out an entry without creating garbage
4150 function Rack.ClearQueueEntry(idx)
4151  
4152 Rack.SwapQueue[idx].direction = nil
4153 Rack.SwapQueue[idx].setname = nil
4154 for i=0,19 do
4155 Rack.SwapQueue[idx][i].id = nil
4156 Rack.SwapQueue[idx][i].fromBag = nil
4157 Rack.SwapQueue[idx][i].fromSlot = nil
4158 end
4159 end
4160  
4161 -- creates a new table and sub-table queue entries, only call when entry didn't exist before
4162 function Rack.ConstructQueueEntry(idx)
4163 Rack.SwapQueue[idx] = {}
4164 for i=0,19 do
4165 Rack.SwapQueue[idx][i] = {}
4166 end
4167 end
4168  
4169 -- get first available index into SwapQueue for use for a set. to avoid garbage creation these are reused
4170 function Rack.GetFreeQueueEntry()
4171 local i,found = 1,1
4172  
4173 while found do
4174 found = Rack.SwapQueue[i]
4175 if found and not found.direction then
4176 return i
4177 end
4178 i = i + 1
4179 end
4180 i = i - 1 -- backtrack to last available entry
4181  
4182 Rack.ConstructQueueEntry(i)
4183 return i
4184 end
4185  
4186 -- adds the .SwapQueue idx to the end of the QueueOrder
4187 function Rack.AddQueueEntry(idx)
4188 table.insert(Rack.SwapQueueOrder,idx)
4189 end
4190  
4191 -- removes the .SwapQueue idx from QueueOrder and clears the .SwapQueue entry
4192 function Rack.RemoveQueueEntry(idx)
4193 for i=1,table.getn(Rack.SwapQueueOrder) do
4194 if Rack.SwapQueueOrder[i]==idx then
4195 table.remove(Rack.SwapQueueOrder,i)
4196 Rack.ClearQueueEntry(idx)
4197 return
4198 end
4199 end
4200 end
4201  
4202 -- this sorts the queue, moving all NEWSETs to the end
4203 function Rack.SortQueue()
4204  
4205 if table.getn(Rack.SwapQueueOrder)>1 then
4206 local found,temp = 1
4207 local queue = Rack.SwapQueueOrder
4208 -- simple shell sort (this is a small, 5-6 max typically) table of indexes that must remain in order otherwise
4209 while found do
4210 found = nil
4211 for i=1,(table.getn(queue)-1) do
4212 if Rack.SwapQueue[queue[i]].direction=="NEWSET" and Rack.SwapQueue[queue[i+1]].direction~="NEWSET" then
4213 temp = queue[i]
4214 queue[i] = queue[i+1]
4215 queue[i+1] = temp
4216 found = 1
4217 end
4218 end
4219 end
4220 end
4221 end
4222  
4223 --[[ Set Maintenance
4224  
4225 Sets are tables in Rack_User[user].Sets[setname] structured as:
4226 { hide=1/nil, icon="Interface\\Icons\\etc", ["0"]={id=string,name=string,old=string} }
4227 id = itemID for the item to wear in the set, 0 for (empty) (can be nil)
4228 name = name of the item to wear in the set, (empty) for empty
4229 old = itemID for the item worn in this slot prior to this set equipped, usually nil
4230 ]]
4231  
4232 -- this converts all sets from ItemRack_Users
4233 function Rack.ConvertOldSets()
4234  
4235 local old,new
4236  
4237 DEFAULT_CHAT_FRAME:AddMessage("Performing one-time conversion of ItemRack sets.")
4238 for u in ItemRack_Users do
4239 if not Rack_User[u] then
4240 Rack_User[u] = { Sets={} }
4241 end
4242 Rack_User[u].CurrentSet = ItemRack_Users[u].CurrentSet
4243 for sets in ItemRack_Users[u].Sets do
4244 if not Rack_User[u].Sets[sets] then
4245 Rack_User[u].Sets[sets] = {}
4246 end
4247 old = ItemRack_Users[u].Sets[sets]
4248 new = Rack_User[u].Sets[sets]
4249  
4250 for i=0,19 do
4251 if old[i] then
4252 new[i] = { name=old[i] }
4253 if old[i]=="(empty)" then
4254 new[i].id = 0
4255 else
4256 new[i].id = Rack.GetItemID(old[i]) or nil
4257 end
4258 end
4259 end
4260 new.icon = old.icon
4261 new.hide = old.hide
4262 new.key = old.key
4263 new.keyindex = old.keyindex
4264 end
4265 end
4266 end
4267  
4268 function Rack.ClearSwapListEntry(idx)
4269 Rack.SwapList[idx].needsSwap = nil
4270 Rack.SwapList[idx].sourceInv = nil
4271 Rack.SwapList[idx].sourceBag = nil
4272 Rack.SwapList[idx].sourceSlot = nil
4273 Rack.SwapList[idx].direction = nil
4274 Rack.SwapList[idx].desiredName = nil
4275 Rack.SwapList[idx].needsEmptied = nil
4276 end
4277  
4278 -- returns the currently worn set, or last set worn
4279 function Rack.CurrentSet()
4280 return Rack_User[user].CurrentSet
4281 end
4282  
4283 --[[ EquipSet
4284 This function takes a setname and then equips the set, saving what it's replacing within the set.
4285 ]]
4286  
4287 function Rack.EquipSet(setname)
4288  
4289 local i,bag,slot,id,swap,idx
4290 local mustWait -- flag that this swap must happen in stages (INV needs to move out of the way)
4291 local set = Rack_User[user].Sets[setname]
4292 local hasTWOHAND, hasINVTOBAG, hasINVTOINV, hasBAGTOINV, hasAMMO
4293 local missing = "ItemRack could not find: "
4294 local invStart,invEnd = 1,19 -- changes to 16,18 if in combat
4295  
4296 if not set then
4297 DEFAULT_CHAT_FRAME:AddMessage("Rack: Set \""..setname.."\" doesn't exist.")
4298 return
4299 end
4300  
4301 if Rack.IsSetEquipped(setname) then
4302 if not string.find(setname,"^Rack-") and not string.find(setname,"^ItemRack") then
4303 Rack_User[user].CurrentSet = setname
4304 end
4305 Rack.StartTimer("InvUpdate")
4306 return
4307 end
4308  
4309 Rack.ClearLockList()
4310  
4311 if Rack.SetSwapping==setname then
4312 Rack.OnItemLockChanged() -- if trying to swap this already, move along in queue
4313 return
4314 end
4315  
4316 if (Rack.SetSwapping or Rack.IsPlayerReallyDead()) and not UnitAffectingCombat("player") then
4317 -- come back later, an EquipSet is in progress
4318 idx = Rack.GetFreeQueueEntry()
4319 Rack.AddQueueEntry(idx)
4320 Rack.SwapQueue[idx].direction = "NEWSET"
4321 Rack.SwapQueue[idx].setname = setname
4322 return
4323 end
4324  
4325 -- pre-scan for items that don't need to move
4326 for i=0,19 do
4327 _,id = Rack.GetItemInfo(i)
4328  
4329 if set[i] and (set[i].id or set[i].name) then
4330 Rack.FindSetItem(set[i])
4331 if set[i].id==id then
4332 Rack.LockList[-2][i] = 1
4333 else
4334 set[i].old = id -- store what was there previously
4335 end
4336 end
4337 Rack.ClearSwapListEntry(i)
4338 end
4339  
4340 if UnitAffectingCombat("player") then -- player is in combat
4341 for i=1,19 do
4342 if set[i] and set[i].id and not Rack.SlotInfo[i].swappable then
4343 Rack.AddToCombatQueue(i,set[i].id)
4344 end
4345 end
4346 invStart,invEnd = 16,18 -- restrict swap to weapons only
4347 elseif Rack.IsPlayerReallyDead() then -- player is dead
4348 for i=0,19 do
4349 if set[i] and set[i].id then
4350 Rack.AddToCombatQueue(i,set[i].id)
4351 end
4352 end
4353 return
4354 end
4355  
4356 -- determine what needs swapped and populate Rack.SwapList
4357 -- skipping ammo slot the black sheep of inventory slots
4358 for i=invStart,invEnd do
4359 if set[i] and set[i].id then
4360 _,id = Rack.GetItemInfo(i)
4361 if id~=set[i].id then
4362 swap = Rack.SwapList[i]
4363 swap.needsSwap = 1
4364 swap.desiredName = Rack.GetNameByID(set[i].id)
4365 if set[i].id==0 then -- empty slot
4366 swap.direction="INVTOBAG"
4367 swap.needsEmptied = 1
4368 hasINVTOBAG = 1
4369 else
4370 inv,bag,slot = Rack.FindSetItem(set[i])
4371 if inv then -- found it in another inventory slot
4372 Rack.LockList[-2][inv] = 1
4373 _,_,_,sourceEquipSlot = Rack.GetItemInfo(inv)
4374 _,_,_,destEquipSlot = Rack.GetItemInfo(i)
4375 swap.direction="INVTOINV" -- if the item can exist in both slots
4376 swap.sourceInv = inv
4377 hasINVTOINV = 1
4378 if destEquipSlot and sourceEquipSlot and not (Rack.SlotInfo[i][sourceEquipSlot] and Rack.SlotInfo[inv][destEquipSlot]) then
4379 swap.needsEmptied = 1
4380 hasINVTOBAG = 1
4381 end
4382 elseif bag and slot then -- found it in a bag slot
4383 Rack.LockList[bag][slot] = 1
4384 swap.direction="BAGTOINV"
4385 swap.sourceBag = bag
4386 swap.sourceSlot = slot
4387 if i==16 then -- this is a mainhand weapon
4388 _,_,_,slot = Rack.GetItemInfo(swap.sourceBag,swap.sourceSlot)
4389 if slot=="INVTYPE_2HWEAPON" and GetInventoryItemLink("player",17) then
4390 hasTWOHAND = 1 -- flag a 2h swap in this set if there's an offhand equipped
4391 if not set[17] then
4392 set[17] = {}
4393 end
4394 set[17].id = 0
4395 Rack.SwapList[17].needsEmptied = 1
4396 end
4397 end
4398 hasBAGTOINV = 1
4399 else -- couldn't find it
4400 missing = missing..tostring(swap.desiredName)..", "
4401 swap.needsSwap = nil
4402 end
4403 end
4404 end
4405 elseif set[i] and set[i].name then -- item has no id yet, not seen since conversion
4406 missing = missing..tostring(set[i].name)..", "
4407 end
4408 end
4409  
4410 if missing~="ItemRack could not find: " then
4411 DEFAULT_CHAT_FRAME:AddMessage(string.gsub(missing,", $",""))
4412 end
4413  
4414 -- at this stage, Rack.SwapList[0]-[19] is populated
4415  
4416 -- INVTOBAG and .needsEmptied swaps first
4417 if hasINVTOBAG then
4418 idx = Rack.GetFreeQueueEntry()
4419 Rack.AddQueueEntry(idx)
4420 Rack.SwapQueue[idx].direction = "INVTOBAG"
4421 Rack.SwapQueue[idx].setname = setname
4422 for i=0,19 do
4423 if Rack.SwapList[i].needsEmptied then
4424 Rack.SwapQueue[idx][i].id = 0
4425 end
4426 end
4427 end
4428  
4429 -- INVTOINV swaps next
4430 if hasINVTOINV then
4431 idx = Rack.GetFreeQueueEntry()
4432 Rack.AddQueueEntry(idx)
4433 Rack.SwapQueue[idx].direction = "INVTOINV"
4434 Rack.SwapQueue[idx].setname = setname
4435 for i=0,19 do
4436 if Rack.SwapList[i].direction=="INVTOINV" then
4437 Rack.SwapQueue[idx][i].id = set[i].id
4438 Rack.SwapQueue[idx][i].fromSlot = Rack.SwapList[i].sourceInv
4439 end
4440 end
4441 end
4442  
4443 -- BAGTOINV swaps last (90% of swaps this is only bit that queues)
4444 if hasBAGTOINV then
4445 idx = Rack.GetFreeQueueEntry()
4446 Rack.AddQueueEntry(idx)
4447 Rack.SwapQueue[idx].direction = "BAGTOINV"
4448 Rack.SwapQueue[idx].setname = setname
4449 for i=0,19 do
4450 if Rack.SwapList[i].direction=="BAGTOINV" then
4451 Rack.SwapQueue[idx][i].id = set[i].id
4452 Rack.SwapQueue[idx][i].fromBag = Rack.SwapList[i].sourceBag
4453 Rack.SwapQueue[idx][i].fromSlot = Rack.SwapList[i].sourceSlot
4454 end
4455 end
4456 end
4457  
4458 -- now deal with ammo why oh why can't this slot be normal
4459 -- perform ammo swaps directly. since it never takes up a new bag slot it's ok to do pickups and move on
4460 if set[0] and set[0].id then
4461 _,id = Rack.GetItemInfo(0)
4462 if id~=set[0].id then
4463 if set[0].id==0 then -- unequip ammo
4464 bag,slot = Rack.FindSpace()
4465 if bag then
4466 PickupInventoryItem(0)
4467 PickupContainerItem(bag,slot)
4468 end
4469 else
4470 _,bag,slot = Rack.FindSetItem(set[0])
4471 if bag then
4472 PickupContainerItem(bag,slot)
4473 PickupInventoryItem(0)
4474 end
4475 end
4476 end
4477 end
4478  
4479 Rack_User[user].Sets[setname].oldsetname = Rack_User[user].CurrentSet
4480  
4481 if Rack_User[user].Sets[setname].showhelm then
4482 ShowHelm(Rack_User[user].Sets[setname].showhelm)
4483 end
4484 if Rack_User[user].Sets[setname].showcloak then
4485 ShowCloak(Rack_User[user].Sets[setname].showcloak)
4486 end
4487  
4488 -- at last, perform the swaps by iterating over the queue
4489 Rack.IterateSwapQueue()
4490  
4491 end
4492  
4493 -- waits for some timed reason to do another iteration. for now just to check SpellIsTargeting once a second
4494 function Rack.IterateWait()
4495  
4496 if SpellIsTargeting() or CursorHasItem() then
4497 Rack.StartTimer("WaitToIterate",1)
4498 else
4499 Rack.StopTimer("WaitToIterate")
4500 Rack.IterateSwapQueue()
4501 end
4502 end
4503  
4504 function Rack.ShutdownQueue()
4505 RackFrame:UnregisterEvent("ITEM_LOCK_CHANGED")
4506 Rack.SetSwapping = nil
4507 for i=1,table.getn(Rack.SwapQueueOrder) do
4508 Rack.RemoveQueueEntry(Rack.SwapQueueOrder[i])
4509 end
4510 end
4511  
4512 -- this function grabs the next swap QueueEntry and performs the swap
4513 -- swaps only happen one direction at a time: INVTOBAG->INVTOINV->BAGTOINV
4514 -- complex swaps can require running this a few times
4515 function Rack.IterateSwapQueue()
4516  
4517 if table.getn(Rack.SwapQueueOrder)<1 then
4518 -- if queue is empty, unregister and leave
4519 Rack.SetSwapping = nil
4520 RackFrame:UnregisterEvent("ITEM_LOCK_CHANGED")
4521 return
4522  
4523 elseif SpellIsTargeting() or CursorHasItem() then
4524 -- check if in Spell Targeting mode to prevent disenchants/enchants
4525 Rack.StartTimer("WaitToIterate")
4526 return
4527  
4528 elseif Rack.IsPlayerReallyDead() then
4529 -- if player is dead, they can't swap anything, leave for now
4530 return
4531  
4532 else
4533  
4534 Rack.SortQueue() -- move NEWSETs to end of queue
4535  
4536 local bag,slot,id
4537 local idx = Rack.SwapQueueOrder[1]
4538 local queue = Rack.SwapQueue[idx]
4539  
4540 if queue.direction == "NEWSET" then
4541 Rack.SetSwapping = nil
4542 local setname = queue.setname
4543 Rack.RemoveQueueEntry(idx)
4544 RackFrame:UnregisterEvent("ITEM_LOCK_CHANGED")
4545 Rack.EquipSet(setname)
4546  
4547 else
4548  
4549 -- something to process
4550 RackFrame:RegisterEvent("ITEM_LOCK_CHANGED")
4551  
4552 Rack.SetSwapping = Rack.SwapQueue[idx].setname
4553  
4554 Rack.ClearLockList()
4555  
4556 if queue.direction == "INVTOBAG" then
4557 for i=0,19 do
4558 if queue[i].id==0 then
4559 bag,slot = Rack.FindSpace()
4560 if bag then
4561 queue[i].fromBag = bag
4562 queue[i].fromSlot = slot
4563 PickupInventoryItem(i)
4564 PickupContainerItem(bag,slot)
4565 else
4566 Rack.NoMoreRoom()
4567 queue[i].id = nil
4568 queue[i].fromBag = nil
4569 queue[i].fromSlot = nil
4570 Rack.ShutdownQueue() -- we ran out of room, stop all swaps now
4571 end
4572 end
4573 end
4574 elseif queue.direction == "INVTOINV" then
4575 for i=0,19 do
4576 if queue[i].fromSlot then
4577 PickupInventoryItem(queue[i].fromSlot)
4578 PickupInventoryItem(i)
4579 end
4580 end
4581 elseif queue.direction == "BAGTOINV" then
4582 for i=0,19 do
4583 if queue[i].fromBag then
4584 _,id = Rack.GetItemInfo(queue[i].fromBag,queue[i].fromSlot)
4585 if id == queue[i].id then
4586 PickupContainerItem(queue[i].fromBag,queue[i].fromSlot)
4587 PickupInventoryItem(i)
4588 else
4589 -- didn't find it at same bag spot when queued
4590 _,bag,slot = Rack.FindSetItem(queue[i])
4591 if bag then
4592 queue[i].fromBag = bag
4593 queue[i].fromSlot = slot
4594 PickupContainerItem(bag,slot)
4595 PickupInventoryItem(i)
4596 else
4597 queue[i].id = nil -- forget we tried
4598 queue[i].fromBag = nil
4599 queue[i].fromSlot = nil
4600 end
4601 end
4602 end
4603 end
4604 end
4605 end
4606 end
4607  
4608 end
4609  
4610 --[[ Debug ]]--
4611  
4612 function Rack.PrintSet(setname)
4613  
4614 local i,j
4615 for i=0,19 do
4616 j=Rack_User[user].Sets[setname]
4617 if j and j[i] then DEFAULT_CHAT_FRAME:AddMessage(Rack.SlotInfo[i].name..": "..tostring(j[i].id).." : "..tostring(Rack.GetNameByID(j[i].id)).." old:"..tostring(j[i].old)) end
4618 end
4619 end
4620  
4621 function Rack.PrintQueue()
4622  
4623 local txt = ""
4624  
4625 local function debug(msg)
4626 DEFAULT_CHAT_FRAME:AddMessage(msg)
4627 end
4628  
4629 debug("__ Rack.PrintQueue() __")
4630 debug("table.getn(Rack.SwapQueueOrder)="..tostring(table.getn(Rack.SwapQueueOrder)))
4631 txt = "Rack.SwapQueueOrder = { "
4632 for i=1,table.getn(Rack.SwapQueueOrder) do
4633 txt = txt.."["..i.."]="..tostring(Rack.SwapQueueOrder[i])..", "
4634 end
4635 txt = txt.."}"
4636 debug(txt)
4637 txt = "Rack.SwapQueue = { "
4638 for i in Rack.SwapQueue do
4639 txt = txt.."["..i.."]= { direction="..tostring(Rack.SwapQueue[i].direction)..", "
4640 txt = txt.."setname="..tostring(Rack.SwapQueue[i].setname)..", "
4641 for j=0,19 do
4642 if Rack.SwapQueue[i][j] and Rack.SwapQueue[i][j].id then
4643 txt = txt.."["..j.."]="..Rack.SwapQueue[i][j].id..", "
4644 txt = txt.."fromBag="..tostring(Rack.SwapQueue[i][j].fromBag)..", "
4645 txt = txt.."fromSlot="..tostring(Rack.SwapQueue[i][j].fromSlot)..", "
4646 end
4647 end
4648 txt = txt .."} "
4649 end
4650 txt = txt.."}"
4651 debug(txt)
4652  
4653 end
4654  
4655 --[[ Locks ]]--
4656  
4657 -- returns true if anything is locked
4658 function Rack.AnyLocked()
4659  
4660 local locked,isLocked
4661  
4662 for i=0,19 do
4663 if IsInventoryItemLocked(i) then
4664 locked = 1
4665 i = 99
4666 end
4667 end
4668  
4669 if not locked then
4670 for i=0,4 do
4671 for j=1,GetContainerNumSlots(i) do
4672 _,_,isLocked = GetContainerItemInfo(i,j)
4673 if isLocked then
4674 locked = 1
4675 i,j=99,99
4676 end
4677 end
4678 end
4679 end
4680  
4681 return locked
4682 end
4683  
4684 -- when an iteration begins, on each ITEM_LOCK_CHANGED check if the swaps are done
4685 -- if so, remove the current queue entry and go to the next one
4686 -- this is where swaps end
4687 function Rack.OnItemLockChanged()
4688  
4689 if not Rack.SwapQueueOrder[1] then
4690 Rack.SetSwapping = nil
4691 RackFrame:UnregisterEvent("ITEM_LOCK_CHANGED")
4692 return
4693 end
4694  
4695 if not Rack.AnyLocked() then
4696 local setname = Rack.SwapQueue[Rack.SwapQueueOrder[1]].setname
4697 if not string.find(setname,"^Rack-") and not string.find(setname,"^ItemRack") then
4698 Rack_User[user].CurrentSet = setname
4699 end
4700 Rack.RemoveQueueEntry(Rack.SwapQueueOrder[1])
4701 Rack.IterateSwapQueue()
4702 end
4703 end
4704  
4705 --[[ Combat/Death Queue Processing ]]
4706  
4707 function Rack.IsPlayerReallyDead()
4708 local dead = UnitIsDeadOrGhost("player")
4709 local _,class = UnitClass("player")
4710 if class~="HUNTER" then
4711 return dead
4712 end
4713 for i=1,24 do
4714 if UnitBuff("player",i)=="Interface\\Icons\\Ability_Rogue_FeignDeath" then
4715 dead = nil -- player is just FD, not really dead
4716 end
4717 end
4718 return dead
4719 end
4720  
4721 -- adds an item 'id' to 'slot' queue for post-combat/death swap
4722 function Rack.AddToCombatQueue(slot,id)
4723 local button = getglobal("ItemRackInv"..slot.."Queue")
4724 local paperdoll = getglobal(ItemRack.Indexes[slot].paperdoll_slot.."Queue")
4725 local _,wornId = Rack.GetItemInfo(slot)
4726 if Rack.CombatQueue[slot]==id or id==wornId then
4727 Rack.CombatQueue[slot] = nil
4728 button:Hide()
4729 paperdoll:Hide()
4730 elseif id and id~=wornId then
4731 Rack.CombatQueue[slot] = id
4732 local _,texture = Rack.GetNameByID(id)
4733 button:SetTexture(texture)
4734 button:Show()
4735 paperdoll:SetTexture(texture)
4736 paperdoll:Show()
4737 end
4738 end
4739  
4740 --[[ EquipSet wrappers ]]--
4741  
4742 -- returns true if the entire set is currently worn
4743 function Rack.IsSetEquipped(setname)
4744  
4745 local set,missing,id = Rack_User[user].Sets[setname or ""]
4746  
4747 if set then
4748 for i=0,19 do
4749 if set[i] then
4750 -- if item queued, check queued item instead of GetItemInfo
4751 if Rack.CombatQueue[i] then
4752 id = Rack.CombatQueue[i]
4753 else
4754 _,id = Rack.GetItemInfo(i)
4755 end
4756 if set[i].id ~= id then
4757 missing = 1
4758 end
4759 end
4760 end
4761 end
4762 return set and not missing
4763 end
4764  
4765 -- equips a set if it's worn, unequips it if not
4766 function Rack.ToggleSet(setname)
4767  
4768 if Rack_User[user].Sets[setname or ""] then
4769 if Rack.IsSetEquipped(setname) then
4770 Rack.UnequipSet(setname)
4771 else
4772 Rack.EquipSet(setname)
4773 end
4774 end
4775 end
4776  
4777 -- This function creates a set of all the items replaced by setname, and then does does an .EquipSet()
4778 function Rack.UnequipSet(setname)
4779  
4780 -- if not Rack.IsSetEquipped(setname) then
4781 -- return
4782 -- end
4783  
4784 local unequip_setname = "Rack-Unequip-"..setname
4785 Rack_User[user].Sets[unequip_setname] = {}
4786 local old = Rack_User[user].Sets[setname]
4787 local new = Rack_User[user].Sets[unequip_setname]
4788  
4789 for i=0,19 do
4790 if old[i] and old[i].old then
4791 new[i] = { id=old[i].old }
4792 old[i].old = nil -- comment this line when a mechanism to catch enchant changes
4793 end
4794 end
4795  
4796 Rack.EquipSet(unequip_setname)
4797 if old.oldsetname and not string.find(old.oldsetname,"^Rack") and not string.find(old.oldsetname,"^ItemRack") then
4798 Rack_User[user].CurrentSet = Rack_User[user].Sets[setname].oldsetname
4799 end
4800 Rack_User[user].Sets[setname].oldsetname = nil
4801 end
4802  
4803 --[[ Timer maintenance
4804  
4805 The goal is to reduce the processing of timer checks to as little as possible. One OnUpdate is a central
4806 repository of this mod's timers.
4807 ]]
4808  
4809  
4810 function Rack.CreateTimer(name,func,limit,rep)
4811 Rack.TimerPool[name] = { func=func, limit=limit, timer=limit, rep=rep, enabled=nil }
4812 end
4813  
4814 function Rack.StartTimer(name,timer)
4815 Rack.TimerPool[name].timer = timer or Rack.TimerPool[name].limit
4816 Rack.TimerPool[name].enabled = 1
4817 RackFrame:Show()
4818 end
4819  
4820 function Rack.StopTimer(name)
4821 Rack.TimerPool[name].enabled = nil
4822 Rack.ClearStoppedTimers()
4823 end
4824  
4825 function Rack.ClearStoppedTimers()
4826 local stuffLeft
4827 for i in Rack.TimerPool do
4828 stuffLeft = stuffLeft or Rack.TimerPool[i].enabled
4829 end
4830 if not stuffLeft then
4831 RackFrame:Hide()
4832 end
4833 end
4834  
4835 function Rack.TimerEnabled(name)
4836 if Rack.TimerPool[name] and Rack.TimerPool[name].enabled then
4837 return 1
4838 else
4839 return nil
4840 end
4841 end
4842  
4843 function Rack.OnUpdate()
4844  
4845 local clock,stopped
4846 local elapse = tonumber(arg1) or 0.1
4847  
4848 for i in Rack.TimerPool do
4849 clock = Rack.TimerPool[i]
4850 if clock.enabled then
4851 clock.timer = clock.timer - elapse
4852 if clock.timer<0 then
4853 if clock.rep then
4854 clock.timer = clock.limit -- rewind to start if 'rep' set
4855 else
4856 clock.enabled = nil
4857 stopped = 1
4858 end
4859 clock.func()
4860 end
4861 end
4862 end
4863 if stopped then
4864 Rack.ClearStoppedTimers()
4865 end
4866  
4867 end
4868  
4869 --[[ old OnUpdates : now gathered under Rack.Timers ]]
4870  
4871 -- formerly ItemRack_IconDraggingFrame_OnUpdate
4872 function Rack.IconDragging()
4873 local xpos,ypos = GetCursorPosition()
4874 local xmin,ymin = Minimap:GetLeft(), Minimap:GetBottom()
4875  
4876 xpos = xmin-xpos/Minimap:GetEffectiveScale()+70
4877 ypos = ypos/Minimap:GetEffectiveScale()-ymin-70
4878  
4879 ItemRack_Settings.IconPos = math.deg(math.atan2(ypos,xpos))
4880 move_icon()
4881 end
4882 ItemRack_IconDraggingFrame_OnUpdate = Rack.IconDragging -- legacy
4883  
4884 -- formerly ItemRack_ScaleUpdate_OnUpdate
4885 function Rack.ScaleUpdate()
4886 local frame = ItemRack.FrameToScale
4887 local oldscale = frame:GetEffectiveScale()
4888 local framex, framey, cursorx, cursory = frame:GetLeft()*oldscale, frame:GetTop()*oldscale, GetCursorPosition()
4889  
4890 if (cursorx-framex)>32 then
4891 local newscale = (cursorx-framex)/ItemRack.ScalingWidth
4892 ItemRack_ScaleFrame(newscale)
4893 end
4894 end
4895  
4896 -- formerly ItemRack_TooltipUpdate_OnUpdate
4897 function Rack.TooltipUpdate()
4898 if ItemRack.TooltipType then
4899  
4900 local cooldown
4901  
4902 set_tooltip_anchor(ItemRack.TooltipOwner)
4903  
4904 if ItemRack.TooltipType=="BAG" then
4905 if ItemRack.TooltipBag==-1 then
4906 local _,_,id = string.find(GetContainerItemLink(ItemRack.TooltipBag,ItemRack.TooltipSlot) or "","item:(%d+)")
4907 if id then
4908 _,id = GetItemInfo(id)
4909 if id then
4910 -- :SetBagItem doesn't appear to work for -1 bank slot
4911 GameTooltip:SetHyperlink(id)
4912 end
4913 end
4914 else
4915 GameTooltip:SetBagItem(ItemRack.TooltipBag,ItemRack.TooltipSlot)
4916 end
4917 cooldown = GetContainerItemCooldown(ItemRack.TooltipBag,ItemRack.TooltipSlot)
4918 if ItemRack_Settings.TinyTooltip=="ON" and not IsAltKeyDown() then
4919 shrink_tooltip()
4920 end
4921 elseif ItemRack.TooltipType=="INVENTORY" then
4922 GameTooltip:SetInventoryItem("player",ItemRack.TooltipSlot)
4923 cooldown = GetInventoryItemCooldown("player",ItemRack.TooltipSlot)
4924 if ItemRack_Settings.TinyTooltip=="ON" and not IsAltKeyDown() then
4925 shrink_tooltip()
4926 end
4927 if ItemRack.TooltipBag then
4928 GameTooltip:AddLine(string.format(ItemRackText.QUEUED,ItemRack.TooltipBag))
4929 end
4930 end
4931 GameTooltip:Show()
4932 if cooldown==0 then
4933 -- stop updates if this item has no cooldown
4934 Rack.StopTimer("TooltipUpdate")
4935 end
4936 end
4937 end
4938  
4939 -- formerly ItemRack_ControlFrame_OnUpdate
4940 function Rack.ControlFrame()
4941 if ItemRack_Users[user].Locked=="ON" and not IsAltKeyDown() then
4942 Rack.StopTimer("ControlFrame")
4943 ItemRack_ControlFrame:Hide()
4944 end
4945 end
4946  
4947 Rack.MenuFrameSources = { "ItemRack_InvFrame", "ItemRack_MenuFrame", "ItemRack_IconFrame",
4948 "TitanPanelItemRackButton" }
4949  
4950 -- formerly ItemRack_MenuFrame_OnUpdate
4951 function Rack.MenuFrame()
4952  
4953 local over,frame
4954  
4955 for i=1,table.getn(Rack.MenuFrameSources) do
4956 frame = getglobal(Rack.MenuFrameSources[i])
4957 over = over or (frame and MouseIsOver(frame))
4958 end
4959  
4960 if MouseIsOver(ItemRack_SetsFrame) and
4961 (string.sub(GetMouseFocus():GetName() or "",1,17)=="ItemRack_Sets_Inv") and
4962 GetMouseFocus():GetAlpha()>.5 then
4963 over = 1
4964 end
4965  
4966 if not over then
4967 ItemRack_MenuFrame:Hide()
4968 end
4969 end
4970  
4971 --[[ Bank support ]]
4972  
4973 function Rack.PopulateBank()
4974 Rack.UnpopulateBank()
4975 local itemLink,itemID,itemName,equipLoc
4976 for i=1,table.getn(ItemRack.BankSlots) do
4977 for j=1,GetContainerNumSlots(ItemRack.BankSlots[i]) do
4978 itemLink = GetContainerItemLink(ItemRack.BankSlots[i],j) or ""
4979 _,_,itemID = string.find(itemLink,"item:(%d+)")
4980 if itemID then
4981 itemName,_,_,_,_,_,_,equipLoc = GetItemInfo(itemID)
4982 if equipLoc and equipLoc~="" then
4983 ItemRack.BankedItems[itemName] = 1
4984 end
4985 end
4986 end
4987 end
4988 end
4989  
4990 function Rack.UnpopulateBank()
4991 for i in ItemRack.BankedItems do
4992 ItemRack.BankedItems[i] = nil
4993 end
4994 end
4995  
4996 function Rack.BankOpened()
4997 Rack.PopulateBank()
4998 ItemRack.BankIsOpen = 1
4999 ItemRackFrame:RegisterEvent("BAG_UPDATE")
5000 end
5001  
5002 function Rack.BankClosed()
5003 ItemRack.BankIsOpen = nil
5004 ItemRack_MenuFrame:Hide()
5005 Rack.UnpopulateBank()
5006 ItemRackFrame:UnregisterEvent("BAG_UPDATE")
5007 end
5008  
5009 function Rack.SetHasBanked(setname)
5010 if not setname or not Rack_User[user].Sets[setname] then return end
5011 local name,item
5012 for i=0,19 do
5013 item = Rack_User[user].Sets[setname][i]
5014 if item and item.name and ItemRack.BankedItems[item.name] then
5015 return 1
5016 end
5017 end
5018 end
5019  
5020 function Rack.FindBankedItem(name)
5021 for _,i in ItemRack.BankSlots do
5022 for j=1,GetContainerNumSlots(i) do
5023 if strfind(GetContainerItemLink(i,j) or "",name,1,1) then
5024 return i,j
5025 end
5026 end
5027 end
5028 end
5029  
5030 function Rack.PullSetFromBank(setname)
5031 Rack.ClearLockList()
5032 local set = Rack_User[user].Sets[setname]
5033 if not set or SpellIsTargeting() or CursorHasItem() then return end
5034 local bag,slot,freeBag,freeSlot
5035 for i=0,19 do
5036 if set[i] then
5037 if ItemRack.BankedItems[set[i].name] then
5038 bag,slot = Rack.FindBankedItem(set[i].name)
5039 if bag then
5040 freeBag,freeSlot = Rack.FindSpace()
5041 if freeBag then
5042 PickupContainerItem(bag,slot)
5043 PickupContainerItem(freeBag,freeSlot)
5044 else
5045 Rack.NoMoreRoom()
5046 return
5047 end
5048 end
5049 end
5050 end
5051 end
5052 end
5053  
5054 function Rack.PushSetToBank(setname)
5055 Rack.ClearLockList()
5056 local set = Rack_User[user].Sets[setname]
5057 if not set or SpellIsTargeting() or CursorHasItem() then return end
5058 local bag,slot,freeBag,freeSlot
5059 for i=0,19 do
5060 if set[i] then
5061 freeBag,freeSlot = Rack.FindSpace(1)
5062 if freeBag then
5063 inv,bag,slot = Rack.FindItem(set[i].id,set[i].name)
5064 if inv then
5065 PickupInventoryItem(inv)
5066 PickupContainerItem(freeBag,freeSlot)
5067 elseif bag then
5068 PickupContainerItem(bag,slot)
5069 PickupContainerItem(freeBag,freeSlot)
5070 end
5071 else
5072 Rack.NoMoreRoom()
5073 return
5074 end
5075 end
5076 end
5077 end
5078  
5079 function Rack.NoMoreRoom()
5080 DEFAULT_CHAT_FRAME:AddMessage("ItemRack: Not enough room to complete the swap.")
5081 end