lotrointerface.com
Search Downloads

LoTROInterface SVN SequenceBars

[/] [trunk/] [Thurallor/] [SequenceBars/] [Manager.lua] - Blame information for rev 216

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 Thurallor-7095
Manager = class(Group);
2 Thurallor-7095
 
3 Thurallor-7095
function Manager:Constructor()
4 Thurallor-7095
    Turbine.UI.Window.Constructor(self);
5 Thurallor-7095
    self.constructing = true;
6 Thurallor-7095
 
7 Thurallor-7095
    self.manager = self;
8 20 Thurallor-7095
    self.watcher = Thurallor.Utils.Watcher;
9 215 Thurallor-7095
    self.objectID = "0";
10 2 Thurallor-7095
    self.groupID = "0";
11 Thurallor-7095
 
12 Thurallor-7095
    self.objects = {};
13 Thurallor-7095
    self.eventGenerators = {};
14 Thurallor-7095
    self.eventCallbacks = {};
15 177 Thurallor-7095
    self.eventListeners = {};
16 Thurallor-7095
    self.eventLuaScripts = {};
17 Thurallor-7095
    self.eventChatTriggers = {};
18 Thurallor-7095
    self.keyDownHotkeys = {};
19 Thurallor-7095
    self.keyUpHotkeys = {};
20 16 Thurallor-7095
    self.propagatingEvent = {};
21 198 Thurallor-7095
    self.orderOfPropagation = {};
22 12 Thurallor-7095
    self.player = Turbine.Gameplay.LocalPlayer:GetInstance();
23 Thurallor-7095
    self.username = self.player:GetName();
24 122 Thurallor-7095
    self:SetUnequipDestination("1..." .. tostring(self.player:GetBackpack():GetSize()));
25 13 Thurallor-7095
    self.hudVisible = true;
26 16 Thurallor-7095
    self.includees = {};
27 Thurallor-7095
    self.includers = {};
28 143 Thurallor-7095
    self.lastActionTime = 0;
29 Thurallor-7095
    self.mouseX, self.mouseY = Turbine.UI.Display.GetMousePosition();
30 Thurallor-7095
 
31 2 Thurallor-7095
    -- Default settings
32 Thurallor-7095
    self.settings = {};
33 Thurallor-7095
    self.settings.type = "group";
34 Thurallor-7095
    self.settings.caption = {};
35 Thurallor-7095
    self.settings.snapToGrid = false;
36 Thurallor-7095
    self.settings.gridSize = 35;
37 Thurallor-7095
    self.settings.language = Turbine.Engine:GetLanguage();
38 Thurallor-7095
    local name = L:GetText("/Default/GlobalName");
39 Thurallor-7095
    self.settings.caption.text = string.gsub(name, "<name>", self.username);
40 Thurallor-7095
    self.settings.caption.width = 80;
41 Thurallor-7095
    self.settings.caption.height = 20;
42 Thurallor-7095
    self.settings.caption.font = Turbine.UI.Lotro.Font.TrajanPro15;
43 Thurallor-7095
    self.settings.color = "FFFFFF";
44 Thurallor-7095
    self.settings.barIDs = {};
45 Thurallor-7095
    self.settings.groupIDs = { "new" };
46 Thurallor-7095
    self.settings.deletedBarIDs = {};
47 Thurallor-7095
    self.settings.deletedGroupIDs = {};
48 177 Thurallor-7095
    self.settings.userEvents = {};
49 2 Thurallor-7095
    self.settings.eventHandlers = {};
50 Thurallor-7095
    self.settings.eventsEnabled = false;
51 Thurallor-7095
    self.settings.groups = {};
52 Thurallor-7095
    self.settings.bars = {};
53 Thurallor-7095
    self.settings.nextObjectID = 1;
54 16 Thurallor-7095
    self.settings.pluginVersion = plugin:GetVersion();
55 Thurallor-7095
    self.settings.uiScale = 32; -- zoom level will be applied to make quickslots this size
56 Thurallor-7095
    self.settings.uiOpacity = 1.0;
57 30 Thurallor-7095
 
58 27 Thurallor-7095
    self.settings.displaySize = { Turbine.UI.Display.GetSize() };
59 2 Thurallor-7095
    self.globals = self.settings;
60 Thurallor-7095
    self:LoadSettings();
61 179 Thurallor-7095
    self.optionsPanel = OptionsPanel(self);
62 177 Thurallor-7095
 
63 Thurallor-7095
    -- Monitor chat messages for user event chat triggers
64 Thurallor-7095
    self.colorStripper = Turbine.UI.Label();
65 Thurallor-7095
    self.colorStripper:SetMarkupEnabled(true);
66 Thurallor-7095
    self.chatReceivedCallback = function(_, args)
67 Thurallor-7095
        self:ChatReceived(args);
68 Thurallor-7095
    end
69 Thurallor-7095
    AddCallback(Turbine.Chat, "Received", self.chatReceivedCallback);
70 2 Thurallor-7095
end
71 Thurallor-7095
 
72 Thurallor-7095
function Manager:LoadSettings()
73 Thurallor-7095
    Turbine.PluginData.Load(Turbine.DataScope.Character, "SequenceBars", function(loadStr)
74 Thurallor-7095
        if (loadStr) then
75 162 Thurallor-7095
            -- Save backup
76 Thurallor-7095
            loadStr.backupTime = "#" .. Turbine.Engine.GetGameTime();
77 Thurallor-7095
            local dateInfo = Turbine.Engine.GetDate();
78 Thurallor-7095
            Turbine.PluginData.Save(Turbine.DataScope.Character, "SequenceBars_backup" .. dateInfo.DayOfWeek, loadStr);
79 Thurallor-7095
 
80 2 Thurallor-7095
            -- Workaround for Turbine localization bug -- Thanks, Lynx3d!
81 Thurallor-7095
            local settings = ImportTable(loadStr);
82 Thurallor-7095
            if (not settings) then
83 Thurallor-7095
                Turbine.Shell.WriteLine("Failed to parse SequenceBars.plugindata file!");
84 Thurallor-7095
                return;
85 Thurallor-7095
            end
86 Thurallor-7095
 
87 Thurallor-7095
            -- Previously-saved settings override the defaults
88 Thurallor-7095
            DeepTableCopy(settings, self.settings);
89 Thurallor-7095
        end
90 Thurallor-7095
 
91 Thurallor-7095
        -- Make color object from hex string
92 Thurallor-7095
        self.color = Thurallor.Utils.Color(1, 0, 0, 0);
93 Thurallor-7095
        self.color:SetHex(self.settings.color);
94 13 Thurallor-7095
 
95 2 Thurallor-7095
        -- Select language
96 18 Thurallor-7095
        self:SetLanguage(self.settings.language);
97 44 Thurallor-7095
 
98 216 Thurallor-7095
        -- Eliminate invalid shortcuts before trying to create the bars.
99 Thurallor-7095
        self:CheckForDefunctSkills();
100 Thurallor-7095
 
101 44 Thurallor-7095
        _G.manualUpdatesNeeded = "";
102 2 Thurallor-7095
        self:LoadBars();
103 Thurallor-7095
        self:LoadGroups();
104 175 Thurallor-7095
        self:UpdateDirectory();
105 7 Thurallor-7095
        self.settings.pluginVersion = tonumber(Plugins.SequenceBars:GetVersion());
106 2 Thurallor-7095
        self.constructing = nil;
107 7 Thurallor-7095
 
108 44 Thurallor-7095
        if (#_G.manualUpdatesNeeded > 0) then
109 Thurallor-7095
            _G.manualUpdatesNeeded = "Since you have upgraded to a new version of SequenceBars, your settings file needs to be updated.  The following item(s) cannot be updated automatically, so you will have to do it yourself:\n\n" .. _G.manualUpdatesNeeded;
110 Thurallor-7095
            Alert("Settings File Updated", _G.manualUpdatesNeeded, nil, Turbine.UI.Lotro.Font.Verdana14);
111 Thurallor-7095
            _G.manualUpdatesNeeded = "";
112 Thurallor-7095
        end
113 Thurallor-7095
 
114 13 Thurallor-7095
        self:SetWantsKeyEvents(true);
115 16 Thurallor-7095
        self:SetWantsEvents(self.settings.eventsEnabled);
116 177 Thurallor-7095
 
117 Thurallor-7095
        -- Copy user event hotkeys and chat triggers into an efficient data structure
118 Thurallor-7095
        for eventName, settings in pairs(self.settings.userEvents or {}) do
119 Thurallor-7095
            for hotkey, text in pairs(settings.keyUpHotkeys or {}) do
120 Thurallor-7095
                self:SetEventHotkey(eventName, hotkey, "KeyUp", text);
121 Thurallor-7095
            end
122 Thurallor-7095
            for hotkey, text in pairs(settings.keyDownHotkeys or {}) do
123 Thurallor-7095
                self:SetEventHotkey(eventName, hotkey, "KeyDown", text);
124 Thurallor-7095
            end
125 Thurallor-7095
            for channel, patternInfo in pairs(settings.chatTriggers or {}) do
126 Thurallor-7095
                for pattern, text in pairs(patternInfo) do
127 Thurallor-7095
                    self:SetEventChatTrigger(eventName, channel, pattern, text);
128 Thurallor-7095
                end
129 Thurallor-7095
            end
130 Thurallor-7095
        end
131 Thurallor-7095
 
132 Thurallor-7095
        -- Compile user event lua scripts
133 Thurallor-7095
        for eventName, settings in pairs(self.settings.userEvents) do
134 Thurallor-7095
            self:SetEventScript(eventName, settings.luaScript);
135 Thurallor-7095
        end
136 Thurallor-7095
 
137 16 Thurallor-7095
        self.displaySizeCallback = function(display)
138 26 Thurallor-7095
            self.anchorCoords = nil;
139 127 Thurallor-7095
            self:SetGridDisplayEnable(self.showGrid);
140 16 Thurallor-7095
        end
141 Thurallor-7095
        AddCallback(Turbine.UI.Display, "SizeChanged", self.displaySizeCallback);
142 Thurallor-7095
 
143 28 Thurallor-7095
        -- In case the display size changed since the last time the plugin was used:
144 Thurallor-7095
        self.anchorCoords = nil;
145 Thurallor-7095
        self:SetWantsUpdates(true);
146 177 Thurallor-7095
 
147 7 Thurallor-7095
        self:PropagateEvent("Startup\n");
148 2 Thurallor-7095
    end);
149 Thurallor-7095
end
150 Thurallor-7095
 
151 215 Thurallor-7095
-- From time to time, SSG will remove skills from the game, invalidating existing shortcuts.
152 Thurallor-7095
function Manager:CheckForDefunctSkills()
153 Thurallor-7095
    local errors = {};
154 Thurallor-7095
    local quickslot = Turbine.UI.Lotro.Quickslot();
155 Thurallor-7095
    local invalidStr = L:GetText("/InvalidShortcut");
156 Thurallor-7095
    for barID, barSettings in pairs(self.settings.bars) do
157 Thurallor-7095
        local slotsInfo = barSettings.sequenceItemInfo;
158 Thurallor-7095
        for s, info in ipairs(slotsInfo) do
159 Thurallor-7095
            if ((not info.class) or (info.class == "Turbine.UI.Lotro.Quickslot")) then
160 Thurallor-7095
                local shortcut = Turbine.UI.Lotro.Shortcut(info.type, info.Data);
161 Thurallor-7095
                -- Make sure this skill/item still exists
162 Thurallor-7095
                success, errorMsg = pcall(quickslot.SetShortcut, quickslot, shortcut);
163 Thurallor-7095
                if (not success) then
164 Thurallor-7095
                    -- Don't bother reporting the error for deleted bars.
165 Thurallor-7095
                    if (not barSettings.deleted) then
166 Thurallor-7095
                        key = shortcut:GetSkillName() or info.type .. "," .. info.Data;
167 Thurallor-7095
                        key = invalidStr .. " (" .. key .. ")";
168 Thurallor-7095
                        local barName = barSettings.caption.text;
169 Thurallor-7095
                        errors[key] = errors[key] or {};
170 Thurallor-7095
                        errors[key][barName] = errors[key][barName] or {};
171 Thurallor-7095
                        table.insert(errors[key][barName], s );
172 Thurallor-7095
                    end
173 Thurallor-7095
                    slotsInfo[s] = {};
174 Thurallor-7095
                end
175 Thurallor-7095
            end
176 Thurallor-7095
        end
177 Thurallor-7095
    end
178 Thurallor-7095
    slotStr = L:GetText("/BarMenu/SettingsMenu/CursorMenu/HomePositionMenu/Slot");
179 Thurallor-7095
    for key, bars in pairs(errors) do
180 Thurallor-7095
        Puts("<rgb=#FF0000>" .. key .. "</rgb>");
181 Thurallor-7095
        for barName in sorted_keys(bars) do
182 Thurallor-7095
            local slots = bars[barName];
183 Thurallor-7095
            for n, slot in pairs(slots) do
184 Thurallor-7095
                slots[n] = slotStr:gsub("<num>", tostring(slot));
185 Thurallor-7095
            end
186 Thurallor-7095
            Puts("  " .. barName .. " : " .. table.concat(slots, ", "));
187 Thurallor-7095
        end
188 Thurallor-7095
    end
189 Thurallor-7095
end
190 Thurallor-7095
 
191 2 Thurallor-7095
function Manager:Unload()
192 143 Thurallor-7095
    self:DoSave();
193 2 Thurallor-7095
    self.constructing = false;
194 Thurallor-7095
    self.editingCaption = true;
195 35 Thurallor-7095
 
196 Thurallor-7095
    -- The following may appear pointless, but failure to clean up (e.g. not unregistering event handlers) can cause crashes.
197 2 Thurallor-7095
    for objectID, object in pairs(self.objects) do
198 Thurallor-7095
--Puts("Destroying object " .. tostring(objectID));
199 Thurallor-7095
        if (object) then
200 Thurallor-7095
            object:Destroy();
201 Thurallor-7095
        end
202 Thurallor-7095
    end
203 Thurallor-7095
end
204 Thurallor-7095
 
205 177 Thurallor-7095
function Manager:SaveSettings(updateBarDirectory, updateEventDirectory)
206 Thurallor-7095
    if (updateBarDirectory) then
207 Thurallor-7095
        self.barDirectoryUpdateNeeded = true;
208 2 Thurallor-7095
    end
209 177 Thurallor-7095
    if (updateEventDirectory) then
210 Thurallor-7095
        self.eventDirectoryUpdateNeeded = true;
211 Thurallor-7095
    end
212 2 Thurallor-7095
    if (not self.constructing) then
213 Thurallor-7095
        -- Start the idle timer.
214 143 Thurallor-7095
        self.lastActionTime = Turbine.Engine.GetGameTime();
215 144 Thurallor-7095
        self.waitingForIdle = true;
216 147 Thurallor-7095
        self:SetWantsUpdates(true);
217 2 Thurallor-7095
    end
218 Thurallor-7095
end
219 Thurallor-7095
 
220 Thurallor-7095
function Manager:Update()
221 108 Thurallor-7095
 
222 Thurallor-7095
    -- Propagate queued events
223 Thurallor-7095
    if (next(self.propagatingEvent)) then
224 198 Thurallor-7095
        local events = self.orderOfPropagation;
225 108 Thurallor-7095
        self.propagatingEvent = {};
226 198 Thurallor-7095
        self.orderOfPropagation = {};
227 Thurallor-7095
        for _, eventName in ipairs(events) do
228 177 Thurallor-7095
            self:PropagateEvent(eventName, true);
229 108 Thurallor-7095
        end
230 Thurallor-7095
    end
231 Thurallor-7095
 
232 Thurallor-7095
    -- Adjust bar layout based on new screen size
233 28 Thurallor-7095
    if (not self.anchorCoords) then
234 Thurallor-7095
        local speed = 0.2;
235 Thurallor-7095
        local prevWidth, prevHeight = unpack(self.settings.displaySize);
236 Thurallor-7095
        local width, height = Turbine.UI.Display:GetSize();
237 Thurallor-7095
        local newWidth = prevWidth + (width - prevWidth) * speed;
238 Thurallor-7095
        local newHeight = prevHeight + (height - prevHeight) * speed;
239 Thurallor-7095
        if (math.abs(newWidth - width) < 1) then
240 Thurallor-7095
            newWidth = width;
241 Thurallor-7095
        end
242 Thurallor-7095
        if (math.abs(newHeight - height) < 1) then
243 Thurallor-7095
            newHeight = height;
244 Thurallor-7095
        end
245 Thurallor-7095
        self.settings.displaySize = { newWidth, newHeight };
246 Thurallor-7095
        self:SaveSettings(false);
247 34 Thurallor-7095
        self:Redraw(true);
248 28 Thurallor-7095
        if ((newWidth ~= width) or (newHeight ~= height)) then
249 Thurallor-7095
            self.anchorCoords = nil;
250 Thurallor-7095
        end
251 Thurallor-7095
    end
252 108 Thurallor-7095
 
253 144 Thurallor-7095
    if (self.waitingForIdle) then
254 143 Thurallor-7095
        -- Mouse movements mean we're not idle
255 Thurallor-7095
        mouseX, mouseY = Turbine.UI.Display.GetMousePosition();
256 Thurallor-7095
        if ((mouseX ~= self.mouseX) or (mouseY ~= self.mouseY) or self.mouseLooking) then
257 Thurallor-7095
            self.lastActionTime = Turbine.Engine.GetGameTime();
258 Thurallor-7095
        end
259 Thurallor-7095
        self.mouseX, self.mouseY = mouseX, mouseY;
260 177 Thurallor-7095
 
261 144 Thurallor-7095
        local currentTime = Turbine.Engine.GetGameTime();
262 177 Thurallor-7095
        -- Bar/group directory updates will occur when idle time reaches 0.25 s
263 Thurallor-7095
        if (self.barDirectoryUpdateNeeded or self.eventDirectoryUpdateNeeded) then
264 144 Thurallor-7095
            if (currentTime >= self.lastActionTime + 0.25) then
265 177 Thurallor-7095
                if (self.editingCaption) then
266 Thurallor-7095
                    self:UpdateDirectory(false, self.eventDirectoryUpdateNeeded);
267 Thurallor-7095
                else
268 Thurallor-7095
                    self:UpdateDirectory(self.barDirectoryUpdateNeeded, self.eventDirectoryUpdateNeeded);
269 Thurallor-7095
                    self.barDirectoryUpdateNeeded = false;
270 144 Thurallor-7095
                end
271 177 Thurallor-7095
                self.eventDirectoryUpdateNeeded = false;
272 144 Thurallor-7095
            end
273 Thurallor-7095
 
274 143 Thurallor-7095
        -- Save will only occur when idle time reaches 10 s, and never in combat.
275 144 Thurallor-7095
        elseif ((currentTime >= self.lastActionTime + 10) and not self.player:IsInCombat()) then
276 162 Thurallor-7095
-- 14-Jul-2020: Data will now only be saved at logout.  Attempt to address occasional crashes that may be associated with frequent saving.
277 Thurallor-7095
--            self:DoSave();
278 144 Thurallor-7095
            self.waitingForIdle = false;
279 108 Thurallor-7095
        end
280 28 Thurallor-7095
    end
281 16 Thurallor-7095
end
282 2 Thurallor-7095
 
283 16 Thurallor-7095
function Manager:DoSave()
284 2 Thurallor-7095
--Puts("Saving...");
285 Thurallor-7095
    -- Workaround for Turbine localization bug -- Thanks, Lynx3d!
286 Thurallor-7095
    local saveData = ExportTable(self.settings);
287 143 Thurallor-7095
    Turbine.PluginData.Save(Turbine.DataScope.Character, "SequenceBars", saveData, function()
288 Thurallor-7095
--Puts("Save complete.");
289 Thurallor-7095
    end);
290 2 Thurallor-7095
end
291 Thurallor-7095
 
292 177 Thurallor-7095
function Manager:ShowDirectory(whichTab)
293 179 Thurallor-7095
    if (self.optionsPanel) then
294 Thurallor-7095
        self.optionsPanel:ShowDirectory(whichTab);
295 2 Thurallor-7095
    end
296 Thurallor-7095
end
297 Thurallor-7095
 
298 177 Thurallor-7095
function Manager:UpdateDirectory(updateBarDirectory, updateEventDirectory)
299 179 Thurallor-7095
    if (self.optionsPanel) then
300 Thurallor-7095
        self.optionsPanel:UpdateDirectory(updateBarDirectory, updateEventDirectory);
301 177 Thurallor-7095
    end
302 Thurallor-7095
end
303 Thurallor-7095
 
304 13 Thurallor-7095
function Manager:KeyDown(args)
305 143 Thurallor-7095
    -- Keypresses (and mouse-looks) mean we're not idle
306 Thurallor-7095
    self.lastActionTime = Turbine.Engine.GetGameTime();
307 Thurallor-7095
 
308 Thurallor-7095
    if (args.Action == Turbine.UI.Lotro.Action.RotateCharacter) then
309 Thurallor-7095
        self.mouseLooking = true;
310 Thurallor-7095
    end
311 Thurallor-7095
 
312 Thurallor-7095
    -- Respond to toggle UI command (usually F12)
313 13 Thurallor-7095
    if (args.Action == Turbine.UI.Lotro.Action.ToggleHUD) then
314 Thurallor-7095
        self.hudVisible = not self.hudVisible;
315 Thurallor-7095
        self:ApplyHiddenness();
316 Thurallor-7095
    end
317 177 Thurallor-7095
 
318 Thurallor-7095
    -- If this hotkey is assigned to a user event, generate it.
319 Thurallor-7095
    for eventName, _ in pairs (self.keyDownHotkeys[args.Action] or {}) do
320 Thurallor-7095
        self:PropagateEvent(eventName);
321 Thurallor-7095
    end
322 13 Thurallor-7095
end
323 Thurallor-7095
 
324 143 Thurallor-7095
function Manager:KeyUp(args)
325 Thurallor-7095
    -- Keypresses (and mouse-looks) mean we're not idle
326 Thurallor-7095
    self.lastActionTime = Turbine.Engine.GetGameTime();
327 Thurallor-7095
 
328 Thurallor-7095
    if (args.Action == Turbine.UI.Lotro.Action.RotateCharacter) then
329 Thurallor-7095
        self.mouseLooking = false;
330 Thurallor-7095
    end
331 177 Thurallor-7095
 
332 Thurallor-7095
    -- If this hotkey is assigned to a user event, generate it.
333 Thurallor-7095
    for eventName, _ in pairs (self.keyUpHotkeys[args.Action] or {}) do
334 Thurallor-7095
        self:PropagateEvent(eventName);
335 Thurallor-7095
    end
336 143 Thurallor-7095
end
337 Thurallor-7095
 
338 177 Thurallor-7095
function Manager:ChatReceived(args)
339 Thurallor-7095
    local channelInfo = self.eventChatTriggers[args.ChatType];
340 Thurallor-7095
    if (channelInfo) then
341 Thurallor-7095
        self.colorStripper:SetText(args.Message);
342 Thurallor-7095
        local message = self.colorStripper:GetText();
343 Thurallor-7095
        for pattern, eventInfo in pairs(channelInfo) do
344 Thurallor-7095
            if (string.match(message, pattern)) then
345 Thurallor-7095
                for eventName, _ in pairs(eventInfo) do
346 Thurallor-7095
                    self:PropagateEvent(eventName, true);
347 Thurallor-7095
                end
348 Thurallor-7095
            end
349 Thurallor-7095
        end
350 Thurallor-7095
    end
351 Thurallor-7095
end
352 Thurallor-7095
 
353 13 Thurallor-7095
function Manager:IsParentHidden()
354 Thurallor-7095
    return (not self.hudVisible);
355 Thurallor-7095
end
356 Thurallor-7095
 
357 7 Thurallor-7095
function Manager:ShellCommand(cmd, args)
358 Thurallor-7095
    if (string.match(args, "^" .. L:GetText("/ShellCommand/Options"))) then
359 13 Thurallor-7095
        self.optionsPanel:ShowGlobalSettings();
360 7 Thurallor-7095
    elseif (string.match(args, "^" .. L:GetText("/ShellCommand/Events"))) then
361 Thurallor-7095
        local eventNames = self:GetEventNames();
362 Thurallor-7095
        if (#eventNames > 0) then
363 Thurallor-7095
            Puts(table.concat(eventNames, "\n"));
364 Thurallor-7095
        end
365 Thurallor-7095
    elseif (string.match(args, "^" .. L:GetText("/ShellCommand/Event"))) then
366 Thurallor-7095
        local eventName = string.sub(args, string.find(args, " ") + 1);
367 Thurallor-7095
        Puts(" -> \"" .. eventName .. "\"");
368 Thurallor-7095
        self:PropagateEvent(eventName);
369 143 Thurallor-7095
    elseif (string.match(args, "^" .. L:GetText("/ShellCommand/Sort"))) then
370 122 Thurallor-7095
        local slots = string.sub(args, string.find(args, " ") + 1);
371 Thurallor-7095
        local series = Thurallor.Utils.Series(slots);
372 Thurallor-7095
        local backpack = Thurallor.Utils.Watcher.playerBackpackObject;
373 Thurallor-7095
        local safety = backpack:GetSize();
374 Thurallor-7095
        local items = {};
375 Thurallor-7095
        local function comp(a, b)
376 Thurallor-7095
            if (a:GetCategory() > b:GetCategory()) then
377 Thurallor-7095
                return true;
378 Thurallor-7095
            elseif (a:GetCategory() == b:GetCategory()) then
379 Thurallor-7095
                if (a:GetIconImageID() > b:GetIconImageID()) then
380 Thurallor-7095
                    return true;
381 Thurallor-7095
                elseif (a:GetIconImageID() == b:GetIconImageID()) then
382 Thurallor-7095
                    if (a:GetName() < b:GetName()) then
383 Thurallor-7095
                        return true;
384 Thurallor-7095
                    elseif (a:GetName() == b:GetName()) then
385 Thurallor-7095
                        if (a:GetQuantity() > b:GetQuantity()) then
386 Thurallor-7095
                            return true;
387 Thurallor-7095
                        end
388 Thurallor-7095
                    end
389 Thurallor-7095
                end
390 Thurallor-7095
            end
391 Thurallor-7095
            return false;
392 Thurallor-7095
        end
393 Thurallor-7095
        for bpSlot in series:numbers(safety) do
394 Thurallor-7095
            local item = backpack:GetItem(bpSlot);
395 Thurallor-7095
            if (item) then
396 Thurallor-7095
                item.slot = bpSlot;
397 Thurallor-7095
                table.insert(items, item);
398 Thurallor-7095
            end
399 Thurallor-7095
            table.sort(items, comp);
400 Thurallor-7095
        end
401 Thurallor-7095
        for bpSlot in series:numbers(safety) do
402 Thurallor-7095
            local item = table.remove(items, 1);
403 Thurallor-7095
            if (item and (item.slot ~= bpSlot)) then
404 Thurallor-7095
                backpack:PerformItemDrop(item, bpSlot, false);
405 Thurallor-7095
            end
406 Thurallor-7095
        end
407 Thurallor-7095
    else
408 Thurallor-7095
        -- Unknown command.
409 Thurallor-7095
        return false;
410 7 Thurallor-7095
    end
411 122 Thurallor-7095
    return true;
412 7 Thurallor-7095
end
413 Thurallor-7095
 
414 2 Thurallor-7095
function Manager:FindNewObjectID()
415 Thurallor-7095
    local id = self.settings.nextObjectID;
416 Thurallor-7095
    self.settings.nextObjectID = id + 1;
417 Thurallor-7095
    id = self.username .. "." .. tostring(id);
418 Thurallor-7095
    self.objects[id] = { "reserved" };
419 Thurallor-7095
    return id;
420 Thurallor-7095
end
421 Thurallor-7095
 
422 16 Thurallor-7095
function Manager:GetObject(objectID)
423 Thurallor-7095
    return self.objects[objectID];
424 Thurallor-7095
end
425 Thurallor-7095
 
426 2 Thurallor-7095
function Manager:TransferBar(barID, oldGroup, newGroup)
427 20 Thurallor-7095
    local foundPos = Search(oldGroup.settings.barIDs, barID);
428 2 Thurallor-7095
    if (foundPos) then
429 Thurallor-7095
        table.remove(oldGroup.settings.barIDs, foundPos);
430 Thurallor-7095
        table.insert(newGroup.settings.barIDs, barID);
431 Thurallor-7095
        local bar = self.objects[barID];
432 Thurallor-7095
        bar.parent = newGroup;
433 Thurallor-7095
        self:SaveSettings(true);
434 13 Thurallor-7095
        newGroup:ApplyHiddenness();
435 2 Thurallor-7095
    end
436 Thurallor-7095
end
437 Thurallor-7095
 
438 Thurallor-7095
function Manager:TransferGroup(groupID, oldGroup, newGroup)
439 20 Thurallor-7095
    local foundPos = Search(oldGroup.settings.groupIDs, groupID);
440 2 Thurallor-7095
    if (foundPos) then
441 Thurallor-7095
        table.remove(oldGroup.settings.groupIDs, foundPos);
442 Thurallor-7095
        table.insert(newGroup.settings.groupIDs, groupID);
443 Thurallor-7095
        local group = self.objects[groupID];
444 Thurallor-7095
        group.parent = newGroup;
445 Thurallor-7095
        self:SaveSettings(true);
446 13 Thurallor-7095
        newGroup:ApplyHiddenness();
447 2 Thurallor-7095
    end
448 Thurallor-7095
end
449 Thurallor-7095
 
450 Thurallor-7095
function Manager:EmptyTrash()
451 Thurallor-7095
    self.settings.deletedGroupIDs = {};
452 Thurallor-7095
    for groupID, settings in pairs(self.settings.groups) do
453 Thurallor-7095
        settings.deletedGroupIDs = {};
454 Thurallor-7095
        settings.deletedBarIDs = {};
455 Thurallor-7095
        if (not self.objects[groupID]) then
456 Thurallor-7095
            self.settings.groups[groupID] = nil;
457 Thurallor-7095
        end
458 Thurallor-7095
    end
459 7 Thurallor-7095
    self.settings.deletedBarIDs = {};
460 2 Thurallor-7095
    for barID, bar in pairs(self.settings.bars) do
461 Thurallor-7095
        if (not self.objects[barID]) then
462 Thurallor-7095
            self.settings.bars[barID] = nil;
463 Thurallor-7095
        end
464 Thurallor-7095
    end
465 Thurallor-7095
    self:SaveSettings(false);
466 Thurallor-7095
end
467 Thurallor-7095
 
468 201 Thurallor-7095
function Manager:AddSettingsMenuItem(parent, itemName, amSubMenuOf, fromOptionsPanel)
469 200 Thurallor-7095
    self.amSubMenuOf = amSubMenuOf or self.amSubMenuOf;
470 2 Thurallor-7095
    local item = Turbine.UI.MenuItem(L:GetText(itemName), true, false);
471 200 Thurallor-7095
    item._name = itemName;
472 Thurallor-7095
    item._parent = parent;
473 Thurallor-7095
    parent._itemsByName = parent._itemsByName or {};
474 Thurallor-7095
    parent._itemsByName[itemName] = item;
475 2 Thurallor-7095
    parent:GetItems():Add(item);
476 18 Thurallor-7095
 
477 2 Thurallor-7095
    if (itemName == "Root") then
478 Thurallor-7095
        local prevContext = L:SetContext("/GlobalMenu");
479 Thurallor-7095
        parent:GetItems():Clear();
480 Thurallor-7095
        self:AddSettingsMenuItem(parent, "HideAll");
481 201 Thurallor-7095
        if (fromOptionsPanel) then
482 2 Thurallor-7095
            L:SetContext("/GroupMenu");
483 Thurallor-7095
            Group.AddSettingsMenuItem(self, parent, "CreateNewBar");
484 Thurallor-7095
            Group.AddSettingsMenuItem(self, parent, "CreateNewSubgroup");
485 Thurallor-7095
            Group.AddSettingsMenuItem(self, parent, "Import");
486 Thurallor-7095
        end
487 Thurallor-7095
        if (#self.settings.deletedBarIDs + #self.settings.deletedGroupIDs > 0) then
488 Thurallor-7095
            self:AddSettingsMenuItem(parent, "Undelete");
489 Thurallor-7095
        end
490 Thurallor-7095
        L:SetContext("/GroupMenu");
491 Thurallor-7095
        Group.AddSettingsMenuItem(self, parent, "ArrangeBars");
492 12 Thurallor-7095
        Group.AddSettingsMenuItem(self, parent, "GlobalEventBehaviors");
493 2 Thurallor-7095
        L:SetContext("/GlobalMenu");
494 Thurallor-7095
        self:AddSettingsMenuItem(parent, "GlobalSettings");
495 Thurallor-7095
        if (self:HaveTrash()) then
496 Thurallor-7095
            self:AddSettingsMenuItem(parent, "EmptyTrash");
497 Thurallor-7095
        end
498 Thurallor-7095
        L:SetContext(prevContext);
499 Thurallor-7095
    elseif (itemName == "HideAll") then
500 Thurallor-7095
        item:SetChecked(self.settings.hidden);
501 121 Thurallor-7095
        item.Click = function()
502 2 Thurallor-7095
            self:SetHidden(not self.settings.hidden);
503 Thurallor-7095
        end
504 Thurallor-7095
    elseif (itemName == "GlobalSettings") then
505 121 Thurallor-7095
        item.Click = function()
506 12 Thurallor-7095
            self.optionsPanel:ShowGlobalSettings();
507 2 Thurallor-7095
        end
508 Thurallor-7095
    elseif (itemName == "EmptyTrash") then
509 121 Thurallor-7095
        item.Click = function()
510 2 Thurallor-7095
            self:EmptyTrash();
511 Thurallor-7095
        end
512 Thurallor-7095
    else
513 Thurallor-7095
        -- Item is not handled here; send it to Group:AddSettingsMenuItem().
514 Thurallor-7095
        parent:GetItems():RemoveAt(parent:GetItems():GetCount());
515 200 Thurallor-7095
        Group.AddSettingsMenuItem(self, parent, itemName);
516 2 Thurallor-7095
    end
517 139 Thurallor-7095
    return item;
518 2 Thurallor-7095
end
519 Thurallor-7095
 
520 158 Thurallor-7095
function Manager:GetNearestGridPos(left, top, uiScale)
521 Thurallor-7095
    local gridSize = math.floor(0.5 + self.settings.gridSize * (uiScale / 32));
522 2 Thurallor-7095
    local displayBottom = Turbine.UI.Display:GetHeight() - 1;
523 Thurallor-7095
    local centerLeft = math.floor(0.5 + Turbine.UI.Display:GetWidth() / 2 + gridSize / 2) + 3;
524 Thurallor-7095
    local offsetLeft, offsetTop = left - centerLeft, displayBottom - top;
525 Thurallor-7095
    local newLeft = centerLeft + math.floor(0.5 + offsetLeft / gridSize) * gridSize;
526 Thurallor-7095
    local newTop = displayBottom - math.floor(0.5 + offsetTop / gridSize) * gridSize;
527 16 Thurallor-7095
    return newLeft, newTop, gridSize;
528 2 Thurallor-7095
end
529 Thurallor-7095
 
530 26 Thurallor-7095
function Manager:GetAnchorPosition(anchor)
531 Thurallor-7095
    if (not self.anchorCoords) then
532 28 Thurallor-7095
        local width, height = unpack (self.settings.displaySize);
533 26 Thurallor-7095
        local center = math.floor(0.5 + width / 2);
534 Thurallor-7095
        local middle = math.floor(0.5 + height / 2);
535 Thurallor-7095
        self.anchorCoords = {
536 Thurallor-7095
            TopLeft    = { 0, 0      }; TopCenter    = { center, 0      }; TopRight    = { width, 0      };
537 Thurallor-7095
            MiddleLeft = { 0, middle }; MiddleCenter = { center, middle }; MiddleRight = { width, middle };
538 Thurallor-7095
            BottomLeft = { 0, height }; BottomCenter = { center, height }; BottomRight = { width, height };
539 Thurallor-7095
        }
540 Thurallor-7095
    end
541 Thurallor-7095
    return unpack(self.anchorCoords[anchor]);
542 Thurallor-7095
end
543 Thurallor-7095
 
544 122 Thurallor-7095
function Manager:SetUnequipDestination(bagSlots)
545 Thurallor-7095
    self.preferredBagSlots = bagSlots;
546 Thurallor-7095
    self.preferredBagSlotSeries = Thurallor.Utils.Series(bagSlots);
547 6 Thurallor-7095
end
548 Thurallor-7095
 
549 122 Thurallor-7095
function Manager:Unequip(eqSlot)
550 Thurallor-7095
    local _, item = Thurallor.Utils.Watcher.GetEquippedItem(nil, eqSlot);
551 Thurallor-7095
    if (item == nil) then
552 Thurallor-7095
        return;
553 Thurallor-7095
    end
554 Thurallor-7095
 
555 Thurallor-7095
    local preferred_slots = self.preferredBagSlotSeries;
556 Thurallor-7095
    local backpack = Thurallor.Utils.Watcher.playerBackpackObject;
557 155 Thurallor-7095
    if (not backpack.cache) then
558 Thurallor-7095
        self.watcher.GetItemQuantity(nil); -- force cache hit
559 Thurallor-7095
    end
560 122 Thurallor-7095
    if (backpack.cache.recentlyFilled == nil) then
561 Thurallor-7095
        backpack.cache.recentlyFilled = {}; -- cache for keeping track of changes between ticks
562 Thurallor-7095
    end
563 142 Thurallor-7095
    local function trySlot(bpSlot)
564 122 Thurallor-7095
        if (not (backpack.cache.recentlyFilled[bpSlot] or backpack:GetItem(bpSlot))) then
565 Thurallor-7095
            backpack:PerformItemDrop(item, bpSlot);
566 Thurallor-7095
            backpack.cache.recentlyFilled[bpSlot] = item;
567 142 Thurallor-7095
--Puts("Unequipping " .. tostring(item:GetName()) .. " to " .. tostring(bpSlot));
568 Thurallor-7095
            return true;
569 Thurallor-7095
        end
570 Thurallor-7095
    end
571 Thurallor-7095
 
572 Thurallor-7095
    -- Search the preferred slots looking for an empty one.
573 Thurallor-7095
    local safety = backpack:GetSize();
574 Thurallor-7095
    local maxFullSlot = 1;
575 Thurallor-7095
    for bpSlot in preferred_slots:numbers(safety) do
576 Thurallor-7095
        if (trySlot(bpSlot)) then
577 122 Thurallor-7095
            return;
578 142 Thurallor-7095
        else
579 Thurallor-7095
            maxFullSlot = math.max(maxFullSlot, bpSlot);
580 114 Thurallor-7095
        end
581 20 Thurallor-7095
    end
582 142 Thurallor-7095
    --Puts(string.gsub(L:GetText("/BagSlotsFull"), "<slots>", self.preferredBagSlots));
583 Thurallor-7095
 
584 Thurallor-7095
    -- None of the preferred slots are empty.  Try bag slots after the preferred range.
585 Thurallor-7095
    for bpSlot = maxFullSlot + 1, safety do
586 Thurallor-7095
        if (trySlot(bpSlot)) then
587 Thurallor-7095
            return;
588 Thurallor-7095
        end
589 Thurallor-7095
    end
590 Thurallor-7095
 
591 Thurallor-7095
    -- Try bag slots before the preferred range.
592 Thurallor-7095
    for bpSlot = 1, maxFullSlot - 1 do
593 Thurallor-7095
        if (trySlot(bpSlot)) then
594 Thurallor-7095
            return;
595 Thurallor-7095
        end
596 Thurallor-7095
    end
597 Thurallor-7095
 
598 Thurallor-7095
    -- All bag slots are full!
599 Thurallor-7095
    Puts(L:GetText("/BagsFull"));
600 20 Thurallor-7095
end
601 Thurallor-7095
 
602 Thurallor-7095
-- Some trivial wrappers retained for backward compatibility
603 Thurallor-7095
function Manager:GetSkills()
604 Thurallor-7095
    return self.watcher.GetSkillsInfo();
605 Thurallor-7095
end
606 Thurallor-7095
function Manager:SkillReady(iconID)
607 Thurallor-7095
    return self.watcher.SkillReady(iconID);
608 Thurallor-7095
end
609 Thurallor-7095
function Manager:PlayerHasEffectCategory(category)
610 Thurallor-7095
    return self.watcher.PlayerHasEffectCategory(category);
611 Thurallor-7095
end
612 Thurallor-7095
function Manager:TargetHasEffectCategory(category)
613 Thurallor-7095
    return self.watcher.TargetHasEffectCategory(category);
614 Thurallor-7095
end
615 Thurallor-7095
 
616 12 Thurallor-7095
function Manager:SetLanguage(language)
617 Thurallor-7095
    self.settings.language = language;
618 Thurallor-7095
    L:SetLanguage(language);
619 18 Thurallor-7095
    if (language == Turbine.Language.Russian) then
620 Thurallor-7095
        SetCyrillicEnabled(true);
621 Thurallor-7095
    else
622 Thurallor-7095
        SetCyrillicEnabled(false);
623 Thurallor-7095
    end
624 Thurallor-7095
    if (self.optionsPanel) then
625 Thurallor-7095
        self.optionsPanel:Localize();
626 Thurallor-7095
    end
627 12 Thurallor-7095
    self:SaveSettings(false);
628 Thurallor-7095
end
629 Thurallor-7095
 
630 167 Thurallor-7095
function Manager:SetUseOnRightClick(useOnRightClick)
631 Thurallor-7095
    self.settings.useOnRightClick = useOnRightClick;
632 Thurallor-7095
    Group.SetUseOnRightClick(self, useOnRightClick);
633 Thurallor-7095
    self:SaveSettings(false);
634 Thurallor-7095
end
635 Thurallor-7095
 
636 12 Thurallor-7095
function Manager:SetSnapToGrid(snapToGrid)
637 Thurallor-7095
    self.settings.snapToGrid = snapToGrid;
638 Thurallor-7095
    if (snapToGrid) then
639 Thurallor-7095
        self:SnapToGrid();
640 Thurallor-7095
    end
641 16 Thurallor-7095
    self:SaveSettings(false);
642 12 Thurallor-7095
end
643 Thurallor-7095
 
644 127 Thurallor-7095
function Manager:SetGridDisplayEnable(enable)
645 Thurallor-7095
    self.showGrid = enable;
646 Thurallor-7095
    if (self.gridDisplay) then
647 Thurallor-7095
        self.gridDisplay:Close();
648 Thurallor-7095
        self.gridDisplay = nil;
649 Thurallor-7095
    end
650 Thurallor-7095
    if (enable) then
651 158 Thurallor-7095
        local left, top, scaledGridSize = self:GetNearestGridPos(0, 0, self.settings.uiScale);
652 127 Thurallor-7095
        left, top = left - scaledGridSize, top - scaledGridSize;
653 158 Thurallor-7095
        local width, height = self:GetNearestGridPos(Turbine.UI.Display:GetWidth(), Turbine.UI.Display:GetHeight(), self.settings.uiScale);
654 127 Thurallor-7095
        width, height = width + (2 * scaledGridSize), height + (2 * scaledGridSize);
655 Thurallor-7095
        local cellsWide, cellsHigh = width / scaledGridSize, height / scaledGridSize;
656 16 Thurallor-7095
        self.gridDisplay = Turbine.UI.Window();
657 127 Thurallor-7095
        self.gridDisplay:SetPosition(left, top);
658 Thurallor-7095
        self.gridDisplay:SetZOrder(-2147483647);
659 16 Thurallor-7095
        self.gridDisplay:SetVisible(true);
660 127 Thurallor-7095
        self.gridDisplay:SetOpacity(0.5);
661 16 Thurallor-7095
        self.gridDisplay:SetMouseVisible(false);
662 127 Thurallor-7095
        self.gridDisplay:SetBackground("Thurallor/SequenceBars/Images/grid.tga");
663 Thurallor-7095
        self.gridDisplay:SetSize(cellsWide * 35, cellsHigh * 35);
664 Thurallor-7095
        if (scaledGridSize ~= 35) then
665 Thurallor-7095
            self.gridDisplay:SetStretchMode(1);
666 Thurallor-7095
            self.gridDisplay:SetSize(width, height);
667 Thurallor-7095
        end
668 16 Thurallor-7095
    end
669 127 Thurallor-7095
end
670 Thurallor-7095
 
671 158 Thurallor-7095
function Manager:HighlightGrid(left, top, slotsRight, slotsDown, uiScale)
672 127 Thurallor-7095
    if (not self.gridHighlight) then
673 Thurallor-7095
        self.gridHighlight = Turbine.UI.Window();
674 Thurallor-7095
        self.gridHighlight:SetZOrder(-2147483647);
675 Thurallor-7095
        self.gridHighlight:SetVisible(true);
676 Thurallor-7095
        self.gridHighlight:SetMouseVisible(false);
677 Thurallor-7095
        self.gridHighlight:SetBackground(resources.Icon.Grid);
678 Thurallor-7095
        self.gridHighlight:SetStretchMode(0);
679 Thurallor-7095
        self.gridHighlight:SetSize(slotsRight * self.settings.gridSize, slotsDown * self.settings.gridSize);
680 Thurallor-7095
        self.gridHighlight:SetStretchMode(1);
681 Thurallor-7095
        self.gridHighlight.slotsRight, self.gridHighlight.slotsDown = slotsRight, slotsDown;
682 Thurallor-7095
    end
683 158 Thurallor-7095
    left, top, scaledGridSize = self:GetNearestGridPos(left, top, uiScale);
684 127 Thurallor-7095
    self.gridHighlight.prevLeft, self.gridHighlight.prevTop = left, top;
685 Thurallor-7095
    self.gridHighlight:SetSize(slotsRight * scaledGridSize, slotsDown * scaledGridSize);
686 Thurallor-7095
    self.gridHighlight:SetPosition(left, top);
687 16 Thurallor-7095
end
688 Thurallor-7095
 
689 127 Thurallor-7095
function Manager:UnhighlightGrid()
690 Thurallor-7095
    if (self.gridHighlight) then
691 Thurallor-7095
        self.gridHighlight:Close();
692 Thurallor-7095
        self.gridHighlight = nil;
693 16 Thurallor-7095
    end
694 Thurallor-7095
end
695 Thurallor-7095
 
696 127 Thurallor-7095
function Manager:ShowAnchorIcon(anchorPos, x, y)
697 Thurallor-7095
    if (anchorPos) then
698 Thurallor-7095
        if (not self.anchorIcon) then
699 Thurallor-7095
            local width, height = Turbine.UI.Display:GetSize();
700 Thurallor-7095
            local center, middle = math.floor(0.5 + width / 2), math.floor(0.5 + height / 2);
701 Thurallor-7095
            local coords = {
702 Thurallor-7095
                TopLeft =      { 0,           0           };
703 Thurallor-7095
                TopCenter =    { center - 32, 0           };
704 Thurallor-7095
                TopRight =     { width - 64,  0           };
705 Thurallor-7095
                MiddleLeft =   { 0,           middle - 44 };
706 Thurallor-7095
                MiddleCenter = { center - 32, middle - 44 };
707 Thurallor-7095
                MiddleRight =  { width - 64,  middle - 44 };
708 Thurallor-7095
                BottomLeft =   { 0,           height - 88 };
709 Thurallor-7095
                BottomCenter = { center - 32, height - 88 };
710 Thurallor-7095
                BottomRight =  { width - 64,  height - 88 };
711 Thurallor-7095
            }
712 Thurallor-7095
            local anchorX, anchorY = unpack(coords[anchorPos]);
713 Thurallor-7095
 
714 Thurallor-7095
            self.anchorIcon = Turbine.UI.Window();
715 Thurallor-7095
            self.anchorIcon:SetMouseVisible(false);
716 Thurallor-7095
            self.anchorIcon:SetBackground("Thurallor/SequenceBars/Images/anchor.tga");
717 Thurallor-7095
            self.anchorIcon:SetSize(64, 88);
718 Thurallor-7095
            self.anchorIcon:SetPosition(anchorX, anchorY);
719 Thurallor-7095
            self.anchorIcon:SetVisible(true);
720 Thurallor-7095
 
721 Thurallor-7095
            local color = Thurallor.Utils.Color();
722 Thurallor-7095
            color:SetHex("d8cf74"); -- primary color of anchor icon
723 Thurallor-7095
            self.anchorLine = Thurallor.UI.Line(anchorX + 31, anchorY + 12, 0, 0, 3, color);
724 Thurallor-7095
            self.anchorLine:SetMouseVisible(false);
725 Thurallor-7095
        end
726 Thurallor-7095
        self.anchorLine:SetEndPoint(x, y);
727 Thurallor-7095
    elseif (self.anchorIcon) then
728 Thurallor-7095
        self.anchorIcon:Close();
729 Thurallor-7095
        self.anchorLine:Close();
730 Thurallor-7095
        self.anchorIcon, self.anchorLine = nil, nil;
731 Thurallor-7095
    end
732 Thurallor-7095
end
733 Thurallor-7095
 
734 16 Thurallor-7095
function Manager:SetUIOpacity(opacity)
735 Thurallor-7095
    self.settings.uiOpacity = opacity;
736 Thurallor-7095
    self:Redraw(true);
737 Thurallor-7095
    self:SaveSettings(false);
738 Thurallor-7095
end
739 Thurallor-7095
 
740 Thurallor-7095
function Manager:SetUIScale(scale)
741 Thurallor-7095
    self.settings.uiScale = scale;
742 127 Thurallor-7095
    self:SetGridDisplayEnable(self.showGrid);
743 16 Thurallor-7095
    self:Redraw(true);
744 Thurallor-7095
    self:SaveSettings(false);
745 Thurallor-7095
end
746 Thurallor-7095
 
747 Thurallor-7095
function Manager:SetIncludees(includer, includees)
748 Thurallor-7095
--Puts(tostring(includer) .. " is including " .. Serialize(includees));
749 Thurallor-7095
    if (not includees) then
750 Thurallor-7095
        -- Destroying bar.  Notify includers.
751 Thurallor-7095
        self:NotifyIncluders(includer);
752 Thurallor-7095
    end
753 Thurallor-7095
 
754 Thurallor-7095
    -- Delete the previous includes mapping (if any)
755 Thurallor-7095
    local prevIncludes = self.includees[includer];
756 Thurallor-7095
    if (prevIncludes) then
757 Thurallor-7095
        for i = 1, #prevIncludes do
758 Thurallor-7095
            local includee = prevIncludes[i];
759 Thurallor-7095
            self.includers[includee][includer] = nil;
760 Thurallor-7095
--Puts(tostring(includee) .. " will no longer notify " .. tostring(includer));
761 Thurallor-7095
        end
762 Thurallor-7095
    end
763 Thurallor-7095
 
764 Thurallor-7095
    -- Create the new includes mapping (if any)
765 Thurallor-7095
    self.includees[includer] = includees;
766 Thurallor-7095
    if (includees) then
767 Thurallor-7095
        for i = 1, #includees, 1 do
768 Thurallor-7095
            local includee = includees[i];
769 Thurallor-7095
            if (not self.includers[includee]) then
770 Thurallor-7095
                self.includers[includee] = {};
771 Thurallor-7095
            end
772 Thurallor-7095
            self.includers[includee][includer] = true;
773 Thurallor-7095
--Puts(tostring(includee) .. " will notify " .. tostring(includer));
774 Thurallor-7095
        end
775 Thurallor-7095
    end
776 Thurallor-7095
end
777 Thurallor-7095
 
778 Thurallor-7095
-- This function gets called when a sequence changes, so any bars that include that sequence can be notified to update.
779 Thurallor-7095
function Manager:NotifyIncluders(includee)
780 Thurallor-7095
--Puts(tostring(includee) .. " changed");
781 Thurallor-7095
    if (not self.includers[includee]) then
782 Thurallor-7095
        return;
783 Thurallor-7095
    end
784 Thurallor-7095
    self.notifyList = {};
785 Thurallor-7095
    self:FindDependencies(includee);
786 Thurallor-7095
    for barID in keys(self.notifyList) do
787 Thurallor-7095
        if (barID ~= includee) then
788 Thurallor-7095
            local bar = self.objects[barID];
789 Thurallor-7095
            if (bar) then
790 Thurallor-7095
--Puts("Notifying " .. tostring(barID));
791 Thurallor-7095
                bar:ShortcutChanged();
792 Thurallor-7095
            end
793 Thurallor-7095
        end
794 Thurallor-7095
    end
795 Thurallor-7095
    self.notifyList = nil;
796 Thurallor-7095
end
797 Thurallor-7095
 
798 Thurallor-7095
function Manager:FindDependencies(includee)
799 Thurallor-7095
    if (self.includers[includee]) then
800 Thurallor-7095
        for includer in keys(self.includers[includee]) do
801 Thurallor-7095
            if (not self.notifyList[includer]) then
802 Thurallor-7095
                self.notifyList[includer] = true;
803 Thurallor-7095
                self:FindDependencies(includer);
804 Thurallor-7095
            end
805 Thurallor-7095
        end
806 Thurallor-7095
    end
807 Thurallor-7095
end
808 Thurallor-7095
 
809 177 Thurallor-7095
--function Manager:AddEventGenerator(bar, eventName)
810 Thurallor-7095
--    if (not self.eventGenerators[eventName]) then
811 Thurallor-7095
--        self.eventGenerators[eventName] = {};
812 Thurallor-7095
--    end
813 Thurallor-7095
----Puts("Adding event generator for event " .. Serialize(eventName) .. " from bar " .. Serialize(bar.settings.caption.text));
814 Thurallor-7095
--    self.eventGenerators[eventName][bar] = true;
815 Thurallor-7095
--    return bar;
816 Thurallor-7095
--end
817 Thurallor-7095
--
818 Thurallor-7095
--function Manager:RemoveEventGenerator(bar, eventName)
819 Thurallor-7095
----Puts("Removing event generator for event " .. Serialize(eventName) .. " from bar " .. Serialize(bar.settings.caption.text));
820 Thurallor-7095
--    local eventGenerators = self.eventGenerators[eventName];
821 Thurallor-7095
--    if (eventGenerators) then
822 Thurallor-7095
--        eventGenerators[bar] = nil;
823 Thurallor-7095
--        if (not next(eventGenerators)) then
824 Thurallor-7095
--            self.eventGenerators[eventName] = nil;
825 Thurallor-7095
--        end
826 Thurallor-7095
--    else
827 Thurallor-7095
----Puts("Not registered!");
828 Thurallor-7095
--    end
829 Thurallor-7095
--end
830 Thurallor-7095
 
831 Thurallor-7095
function Manager:SetEventGenerators(enable, bar, userEventsGenerated)
832 Thurallor-7095
    local eventGenerators = self.eventGenerators;
833 Thurallor-7095
    for eventName, bars in pairs(eventGenerators) do
834 Thurallor-7095
        bars[bar] = nil;
835 Thurallor-7095
        if (not next(bars)) then
836 Thurallor-7095
            eventGenerators[eventName] = nil;
837 Thurallor-7095
        end
838 2 Thurallor-7095
    end
839 177 Thurallor-7095
    if (enable) then
840 179 Thurallor-7095
        local barID = bar:GetID();
841 Thurallor-7095
        for eventName, includeIDs in pairs(userEventsGenerated) do
842 177 Thurallor-7095
            eventGenerators[eventName] = eventGenerators[eventName] or {};
843 179 Thurallor-7095
            eventGenerators[eventName][bar] = {};
844 Thurallor-7095
            for otherBarID, _ in pairs(includeIDs) do
845 Thurallor-7095
                if (otherBarID ~= barID) then
846 Thurallor-7095
                    eventGenerators[eventName][bar][otherBarID] = true;
847 Thurallor-7095
                end
848 Thurallor-7095
            end
849 177 Thurallor-7095
        end
850 Thurallor-7095
    end
851 Thurallor-7095
    self.eventDirectoryUpdateNeeded = true;
852 2 Thurallor-7095
end
853 Thurallor-7095
 
854 177 Thurallor-7095
function Manager:SetEventListeners(enable, node, userEventsWanted)
855 Thurallor-7095
    local eventListeners = self.eventListeners;
856 Thurallor-7095
    for eventName, nodes in pairs(eventListeners) do
857 Thurallor-7095
        nodes[node] = nil;
858 Thurallor-7095
        if (not next(nodes)) then
859 Thurallor-7095
            eventListeners[eventName] = nil;
860 13 Thurallor-7095
        end
861 2 Thurallor-7095
    end
862 177 Thurallor-7095
    if (enable) then
863 Thurallor-7095
        for eventName, _ in pairs(userEventsWanted) do
864 Thurallor-7095
            eventListeners[eventName] = eventListeners[eventName] or {};
865 Thurallor-7095
            eventListeners[eventName][node] = true;
866 Thurallor-7095
        end
867 Thurallor-7095
    end
868 Thurallor-7095
    self.eventDirectoryUpdateNeeded = true;
869 2 Thurallor-7095
end
870 Thurallor-7095
 
871 Thurallor-7095
-- Returns a list of bars that generates the specified event
872 Thurallor-7095
function Manager:GetEventGenerators(eventName)
873 177 Thurallor-7095
    local bars = self.eventGenerators[eventName];
874 Thurallor-7095
    if (bars) then
875 179 Thurallor-7095
        local barList, includes = {}, {};
876 Thurallor-7095
        for bar, incl in pairs(bars) do
877 Thurallor-7095
            table.insert(barList, bar);
878 Thurallor-7095
            includes[bar] = {};
879 Thurallor-7095
            for barID, _ in pairs(incl) do
880 Thurallor-7095
                local otherBar = self.objects[barID];
881 Thurallor-7095
                includes[bar][otherBar] = true;
882 Thurallor-7095
            end
883 177 Thurallor-7095
        end
884 Thurallor-7095
--Puts("barList for " .. eventName .. " is " .. Serialize(barList, 1));
885 179 Thurallor-7095
        return barList, includes;
886 177 Thurallor-7095
    end
887 2 Thurallor-7095
end
888 Thurallor-7095
 
889 177 Thurallor-7095
function Manager:SetEventScript(eventName, luaScript)
890 Thurallor-7095
    local settings = self.settings.userEvents[eventName] or {};
891 Thurallor-7095
    self.settings.userEvents[eventName] = settings;
892 Thurallor-7095
    settings.luaScript = luaScript;
893 Thurallor-7095
    local func, errMsg;
894 Thurallor-7095
    if (luaScript) then
895 181 Thurallor-7095
        func, errMsg = loadstring("setfenv(1, _G);" .. luaScript);
896 177 Thurallor-7095
        if (not func) then
897 194 Thurallor-7095
            errMsg = errMsg:gsub("^%[[^%]]+%]:", "");
898 Thurallor-7095
            func = function()
899 Thurallor-7095
                Puts("(" .. eventName .. ") Lua script parse error in line " .. errMsg);
900 177 Thurallor-7095
            end
901 Thurallor-7095
        end
902 Thurallor-7095
    end
903 Thurallor-7095
    self.eventLuaScripts[eventName] = func;
904 Thurallor-7095
    return (not errMsg);
905 Thurallor-7095
end
906 Thurallor-7095
 
907 Thurallor-7095
-- Sets (or clears, if text == nil) a chat trigger for the specified user event.
908 Thurallor-7095
function Manager:SetEventChatTrigger(eventName, channel, pattern, text)
909 Thurallor-7095
    -- Update settings
910 Thurallor-7095
    local settings = self.settings.userEvents[eventName] or {};
911 Thurallor-7095
    self.settings.userEvents[eventName] = settings;
912 Thurallor-7095
    local triggerSettings = settings.chatTriggers or {};
913 Thurallor-7095
    settings.chatTriggers = triggerSettings;
914 Thurallor-7095
    local chanInfo = triggerSettings[channel] or {};
915 Thurallor-7095
    triggerSettings[channel] = chanInfo;
916 Thurallor-7095
    chanInfo[pattern] = text;
917 Thurallor-7095
 
918 Thurallor-7095
    -- Update look-up tables
919 Thurallor-7095
    local channelInfo = self.eventChatTriggers[channel] or {};
920 Thurallor-7095
    self.eventChatTriggers[channel] = channelInfo;
921 Thurallor-7095
    local patternInfo = channelInfo[pattern] or {};
922 Thurallor-7095
    channelInfo[pattern] = patternInfo;
923 Thurallor-7095
    patternInfo[eventName] = text;
924 Thurallor-7095
    if (not next(patternInfo)) then
925 Thurallor-7095
        channelInfo[pattern] = nil;
926 Thurallor-7095
    end
927 Thurallor-7095
    if (not next(channelInfo)) then
928 Thurallor-7095
        self.eventChatTriggers[channel] = nil;
929 Thurallor-7095
    end
930 Thurallor-7095
end
931 Thurallor-7095
 
932 Thurallor-7095
-- Sets (or clears, if text == nil) a hotkey for the specified user event.
933 Thurallor-7095
function Manager:SetEventHotkey(eventName, hotkey, event, text)
934 Thurallor-7095
    local settings = self.settings.userEvents[eventName] or {};
935 Thurallor-7095
    self.settings.userEvents[eventName] = settings;
936 Thurallor-7095
    if (event == "KeyUp") then
937 Thurallor-7095
        settings.keyUpHotkeys = settings.keyUpHotkeys or {};
938 Thurallor-7095
        settings.keyUpHotkeys[hotkey] = text;
939 Thurallor-7095
        local hotkeyInfo = self.keyUpHotkeys[hotkey] or {};
940 Thurallor-7095
        self.keyUpHotkeys[hotkey] = hotkeyInfo
941 Thurallor-7095
        hotkeyInfo[eventName] = text;
942 Thurallor-7095
        if (not next(hotkeyInfo)) then
943 Thurallor-7095
            self.keyUpHotkeys[hotkey] = nil;
944 Thurallor-7095
        end
945 Thurallor-7095
    else -- (event == "KeyDown")
946 Thurallor-7095
        settings.keyDownHotkeys = settings.keyDownHotkeys or {};
947 Thurallor-7095
        settings.keyDownHotkeys[hotkey] = text;
948 Thurallor-7095
        local hotkeyInfo = self.keyDownHotkeys[hotkey] or {};
949 Thurallor-7095
        self.keyDownHotkeys[hotkey] = hotkeyInfo
950 Thurallor-7095
        hotkeyInfo[eventName] = text;
951 Thurallor-7095
        if (not next(hotkeyInfo)) then
952 Thurallor-7095
            self.keyDownHotkeys[hotkey] = nil;
953 Thurallor-7095
        end
954 Thurallor-7095
    end
955 Thurallor-7095
end
956 Thurallor-7095
 
957 Thurallor-7095
-- Clears all chat triggers (if any) for the specified user event.
958 Thurallor-7095
function Manager:ClearEventChatTriggers(eventName)
959 Thurallor-7095
    local settings = self.settings.userEvents[eventName] or {};
960 Thurallor-7095
    for channel, patternInfo in pairs(settings.chatTriggers or {}) do
961 Thurallor-7095
        for pattern, description in pairs(patternInfo) do
962 Thurallor-7095
            self:SetEventChatTrigger(eventName, channel, pattern, nil);
963 Thurallor-7095
        end
964 Thurallor-7095
    end
965 Thurallor-7095
end
966 Thurallor-7095
 
967 Thurallor-7095
-- Clears all hotkeys (if any) for the specified user event.
968 Thurallor-7095
function Manager:ClearEventHotkeys(eventName)
969 Thurallor-7095
    local settings = self.settings.userEvents[eventName] or {};
970 Thurallor-7095
    for hotkey, text in pairs(settings.keyDownHotkeys or {}) do
971 Thurallor-7095
        self:SetEventHotkey(eventName, hotkey, "KeyDown", nil);
972 Thurallor-7095
    end
973 Thurallor-7095
    for hotkey, text in pairs(settings.keyUpHotkeys or {}) do
974 Thurallor-7095
        self:SetEventHotkey(eventName, hotkey, "KeyUp", nil);
975 Thurallor-7095
    end
976 Thurallor-7095
end
977 Thurallor-7095
 
978 Thurallor-7095
-- Gets a table of the specified user event's chat triggers (if any), sorted by description.
979 Thurallor-7095
function Manager:GetEventChatTriggers(eventName)
980 Thurallor-7095
    local settings = self.settings.userEvents[eventName] or {};
981 Thurallor-7095
    local infoList = {};
982 Thurallor-7095
    for channel, patternInfo in pairs(settings.chatTriggers or {}) do
983 Thurallor-7095
        for pattern, description in pairs(patternInfo) do
984 Thurallor-7095
            table.insert(infoList, { channel, pattern, description });
985 Thurallor-7095
        end
986 Thurallor-7095
    end
987 Thurallor-7095
    if (next(infoList)) then
988 Thurallor-7095
        table.sort(infoList, function(a, b)
989 Thurallor-7095
            return (a[3] < b[3]);
990 Thurallor-7095
        end);
991 Thurallor-7095
        return infoList;
992 Thurallor-7095
    end
993 Thurallor-7095
end
994 Thurallor-7095
 
995 Thurallor-7095
-- Gets a text description for the specified user event's hotkeys (if any).
996 Thurallor-7095
function Manager:GetEventHotkeys(eventName)
997 Thurallor-7095
    local settings = self.settings.userEvents[eventName] or {};
998 Thurallor-7095
    local textList = {};
999 Thurallor-7095
    for hotkey, text in pairs(settings.keyDownHotkeys or {}) do
1000 Thurallor-7095
        table.insert(textList, text);
1001 Thurallor-7095
    end
1002 Thurallor-7095
    for hotkey, text in pairs(settings.keyUpHotkeys or {}) do
1003 Thurallor-7095
        table.insert(textList, text);
1004 Thurallor-7095
    end
1005 Thurallor-7095
    if (next(textList)) then
1006 Thurallor-7095
        table.sort(textList);
1007 Thurallor-7095
        return textList;
1008 Thurallor-7095
    end
1009 Thurallor-7095
end
1010 Thurallor-7095
 
1011 Thurallor-7095
-- Returns a list of bars/groups that are listening for the specified event
1012 Thurallor-7095
function Manager:GetEventListeners(eventName)
1013 Thurallor-7095
    local nodes = self.eventListeners[eventName];
1014 Thurallor-7095
    if (nodes) then
1015 Thurallor-7095
        local nodeList = {};
1016 Thurallor-7095
        for node, _ in pairs(nodes) do
1017 Thurallor-7095
            table.insert(nodeList, node);
1018 Thurallor-7095
        end
1019 Thurallor-7095
        return nodeList;
1020 Thurallor-7095
    end
1021 Thurallor-7095
end
1022 Thurallor-7095
 
1023 Thurallor-7095
-- Returns the names of all of the events generated by all bars, hotkeys, or chat triggers
1024 2 Thurallor-7095
function Manager:GetEventNames()
1025 177 Thurallor-7095
    local foundEventNames = {};
1026 Thurallor-7095
    for eventName, _ in pairs(self.eventGenerators) do
1027 Thurallor-7095
        foundEventNames[eventName] = true;
1028 Thurallor-7095
    end
1029 Thurallor-7095
    for eventName, settings in pairs(self.settings.userEvents) do
1030 Thurallor-7095
        if (next(settings)) then
1031 Thurallor-7095
            foundEventNames[eventName] = true;
1032 Thurallor-7095
        end
1033 Thurallor-7095
    end
1034 Thurallor-7095
 
1035 2 Thurallor-7095
    local eventNames = {};
1036 177 Thurallor-7095
    for eventName, _ in pairs(foundEventNames) do
1037 Thurallor-7095
        table.insert(eventNames, eventName);
1038 2 Thurallor-7095
    end
1039 177 Thurallor-7095
    table.sort(eventNames);
1040 2 Thurallor-7095
    return eventNames;
1041 Thurallor-7095
end
1042 Thurallor-7095
 
1043 177 Thurallor-7095
function Manager:CreateUserEvent(eventName)
1044 Thurallor-7095
    self.settings.userEvents[eventName] = { explicitlyCreated = true; };
1045 Thurallor-7095
    self:SaveSettings(false);
1046 Thurallor-7095
end
1047 Thurallor-7095
 
1048 Thurallor-7095
-- Note: This only deletes the hotkey and lua script (if any) after the event has already been removed from bars/groups
1049 Thurallor-7095
function Manager:DeleteUserEvent(eventName)
1050 Thurallor-7095
    if (self.eventListeners[eventName] or self.eventGenerators[eventName]) then
1051 Thurallor-7095
        return Puts("Error: Can't delete");
1052 Thurallor-7095
    end
1053 Thurallor-7095
    self:ClearEventHotkeys(eventName);
1054 Thurallor-7095
    self:SetEventScript(eventName, nil);
1055 Thurallor-7095
    self.settings.userEvents[eventName] = nil;
1056 Thurallor-7095
    self:SaveSettings(false);
1057 Thurallor-7095
end
1058 Thurallor-7095
 
1059 Thurallor-7095
function Manager:RenameUserEvent(oldName, newName)
1060 Thurallor-7095
 
1061 Thurallor-7095
    -- Update settings
1062 Thurallor-7095
    self.settings.userEvents[newName] = self.settings.userEvents[oldName]
1063 Thurallor-7095
    self.settings.userEvents[oldName] = nil;
1064 Thurallor-7095
 
1065 Thurallor-7095
    -- Update global data structures
1066 Thurallor-7095
    if (self.eventLuaScripts[oldName]) then
1067 Thurallor-7095
        self.eventLuaScripts[newName] = self.eventLuaScripts[oldName];
1068 Thurallor-7095
        self.eventLuaScripts[oldName] = nil;
1069 Thurallor-7095
    end
1070 Thurallor-7095
    for hotkey, action in pairs(self.keyDownHotkeys) do
1071 Thurallor-7095
        if (action[oldName]) then
1072 Thurallor-7095
           action[newName] = action[oldName];
1073 Thurallor-7095
           action[oldName] = nil;
1074 Thurallor-7095
        end
1075 Thurallor-7095
    end
1076 Thurallor-7095
    for hotkey, action in pairs(self.keyUpHotkeys) do
1077 Thurallor-7095
        if (action[oldName]) then
1078 Thurallor-7095
           action[newName] = action[oldName];
1079 Thurallor-7095
           action[oldName] = nil;
1080 Thurallor-7095
        end
1081 Thurallor-7095
    end
1082 Thurallor-7095
 
1083 Thurallor-7095
    -- Make a list of affected bars/groups
1084 Thurallor-7095
    local nodes = {};
1085 Thurallor-7095
    for node, _ in pairs(self.eventListeners[oldName] or {}) do
1086 Thurallor-7095
        nodes[node] = true;
1087 Thurallor-7095
    end
1088 Thurallor-7095
    for node, _ in pairs(self.eventGenerators[oldName] or {}) do
1089 Thurallor-7095
        nodes[node] = true;
1090 Thurallor-7095
    end
1091 Thurallor-7095
 
1092 Thurallor-7095
    -- Update bars/groups
1093 Thurallor-7095
    for node, _ in pairs(nodes) do
1094 Thurallor-7095
        node:RenameUserEvent(oldName, newName);
1095 Thurallor-7095
    end
1096 Thurallor-7095
 
1097 179 Thurallor-7095
    -- Rebuild bars
1098 Thurallor-7095
    for node, _ in pairs(nodes) do
1099 Thurallor-7095
        if (not node:CanHaveChildren()) then
1100 Thurallor-7095
            node:Rebuild();
1101 Thurallor-7095
        end
1102 Thurallor-7095
    end
1103 Thurallor-7095
 
1104 Thurallor-7095
    -- Redraw event directory table
1105 Thurallor-7095
    self:SaveSettings(false, true);
1106 177 Thurallor-7095
end
1107 Thurallor-7095
 
1108 Thurallor-7095
-- Note: the returned table may have empty elements due to RemoveCallback()
1109 2 Thurallor-7095
function Manager:GetEventCallbacks()
1110 Thurallor-7095
    return self.eventCallbacks;
1111 Thurallor-7095
end
1112 Thurallor-7095
 
1113 177 Thurallor-7095
function Manager:PropagateEvent(eventName, now)
1114 Thurallor-7095
    if (now) then
1115 Thurallor-7095
        local func = self.eventLuaScripts[eventName];
1116 Thurallor-7095
        if (func) then
1117 Thurallor-7095
            local success, errMsg = pcall(func, eventName, self.player, self);
1118 Thurallor-7095
            if (not success) then
1119 194 Thurallor-7095
                errMsg = errMsg:gsub("^%[[^%]]+%]:", "");
1120 Thurallor-7095
                Puts("(" .. eventName .. ") Lua script run-time error in line " .. errMsg);
1121 177 Thurallor-7095
            end
1122 Thurallor-7095
        end
1123 Thurallor-7095
        DoCallbacks(self.eventCallbacks, eventName, eventName);
1124 Thurallor-7095
    else
1125 Thurallor-7095
        -- Enqueue event for processing at next Update cycle.  We use a hash table to avoid
1126 Thurallor-7095
        -- generating the same event multiple times in the same tick, which would be useless
1127 Thurallor-7095
        -- and could cause stack overflow.
1128 198 Thurallor-7095
        if (not self.propagatingEvent[eventName]) then
1129 Thurallor-7095
            self.propagatingEvent[eventName] = true;
1130 Thurallor-7095
            table.insert(self.orderOfPropagation, eventName);
1131 Thurallor-7095
        end
1132 177 Thurallor-7095
    end
1133 2 Thurallor-7095
end
1134 16 Thurallor-7095
 
1135 Thurallor-7095
function Manager:Destroy()
1136 177 Thurallor-7095
    RemoveCallback(Turbine.Chat, "Received", self.chatReceivedCallback);
1137 Thurallor-7095
    self:SetWantsKeyEvents(false);
1138 Thurallor-7095
    self:SetWantsEvents(false);
1139 16 Thurallor-7095
    Node.Destroy(self);
1140 Thurallor-7095
end

All times are GMT -5. The time now is 05:50 PM.


Our Network
EQInterface | EQ2Interface | Minion | WoWInterface | ESOUI | LoTROInterface | MMOUI | Swtorui