lotrointerface.com
Search Downloads

LoTROInterface SVN SequenceBars

[/] [trunk/] [Thurallor/] [SequenceBars/] [Group.lua] - Rev 201

Compare with Previous | Blame | View Log

Group = class(Node);

function Group:Constructor(parent, groupID, settings)
    Turbine.UI.Window.Constructor(self);
    self.constructing = true;

    self.parent = parent;
    self.manager = parent.manager;
    self.groupID = groupID;
    self.objectID = groupID;
    self.globals = self.manager.settings;

    -- Default settings
    local defaults = {};
    defaults.type = "group";
    defaults.hidden = false;
    defaults.barsMoveTogether = false;
    defaults.caption = {};
    defaults.caption.text = L:GetText("/Default/GroupName");
    defaults.caption.width = 80;
    defaults.caption.height = 20;
    defaults.caption.font = Turbine.UI.Lotro.Font.TrajanPro15;
    defaults.color = "C0C0C0";
    defaults.barIDs = { "new" };
    defaults.groupIDs = {};
    defaults.deletedBarIDs = {};
    defaults.deletedGroupIDs = {};
    defaults.eventHandlers = {};
    defaults.eventsEnabled = false;
    
    -- Previously-saved settings override the defaults
    DeepTableCopy(settings, defaults);
    DeepTableCopy(defaults, settings);
    self.settings = settings;

    self:ReapplySettings();
    self:LoadBars();
    self:LoadGroups();
    self.constructing = nil;
    self:SetWantsEvents(self.settings.eventsEnabled);
end

function Group:ReapplySettings()

    -- Make color object from hex string
    self.color = Thurallor.Utils.Color(1, 0, 0, 0);
    self.color:SetHex(self.settings.color);

    -- For backward compatibility
    for _, handler in pairs(self.settings.eventHandlers) do
        if (handler.action) then
            handler.action = string.gsub(handler.action, "self:SetHidden%(false%); self:Activate%(%); ", "self:SetHidden(false); ");
            handler.action = string.gsub(handler.action, "self:SetHidden%(false%); self:BringToFront%(%); ", "self:SetHidden(false); ");
            handler.action = string.gsub(handler.action, "self:Expand%(%); self:Activate%(%); ", "self:Expand(); ");
            handler.action = string.gsub(handler.action, "self:Expand%(%); self:BringToFront%(%); ", "self:Expand(); ");
        end
    end
        -- Convert fadetogray's settings
        if (self.settings.barsCaptionsMinimized) then
                self.settings.barsCaptionsMinimized = nil;
                for b = 1, #self.settings.barIDs, 1 do
                        local barID = self.settings.barIDs[b];
                        local barSettings = self.globals.bars[barID];
                        if (type(barSettings) == "table") then
                                barSettings.caption.hidden = nil;
                                barSettings.caption.visible = "WhenMouseIsPresent";
                                barSettings.caption.clickThru = true;
                        end
                end
        end
end

function Group:LoadBars()
    for b = 1, #self.settings.barIDs, 1 do
        local barID = self.settings.barIDs[b];
        if (barID == "new") then
            barID = self:FindNewObjectID();
            self.settings.barIDs[b] = barID;
        end
        if (self.globals.bars[barID] == nil) then
            self.globals.bars[barID] = {};
        end
        self.manager.objects[barID] = Bar(self, barID, self.globals.bars[barID]);
    end
end

function Group:LoadGroups()
    for g = 1, #self.settings.groupIDs, 1 do
        local groupID = self.settings.groupIDs[g];
        if (groupID == "new") then
            groupID = self:FindNewObjectID();
            self.settings.groupIDs[g] = groupID;
        end
        if (self.globals.groups[groupID] == nil) then
            self.globals.groups[groupID] = {};
        end
        self.manager.objects[groupID] = Group(self, groupID, self.globals.groups[groupID]);
    end
end

-- Iterator for bar objects
function Group:bar_objects()
    local state = { ["index"] = 1 };
    local function iterator(state)
        local barID = self.settings.barIDs[state.index];
        state.index = state.index + 1;
        if (barID ~= nil) then
            local bar = self.manager.objects[barID];
            if (bar == nil) then -- proceed to the next bar ID
                return iterator(state);
            end
            return bar;
        end
    end
    return iterator, state, nil;
end

-- Iterator for subgroup objects
function Group:group_objects()
    local state = { ["index"] = 1 };
    local function iterator(state)
        local groupID = self.settings.groupIDs[state.index];
        state.index = state.index + 1;
        if (groupID ~= nil) then
            local group = self.manager.objects[groupID];
            if (group == nil) then -- proceed to the next group ID
                return iterator(state);
            end
            return group;
        end
    end
    return iterator, state, nil;
end

function Group:ApplyHiddenness()
    for bar in self:bar_objects() do
        bar:ApplyHiddenness();
    end
    for group in self:group_objects() do
        group:ApplyHiddenness();
    end
end

function Group:Activate()
    for bar in self:bar_objects() do
        bar:Activate();
    end
    for group in self:group_objects() do
        group:Activate();
    end
end

function Group:BringToFront()
    for bar in self:bar_objects() do
        bar:BringToFront();
    end
    for group in self:group_objects() do
        group:BringToFront();
    end
end

function Group:Expand()
    if (self.Log) then
        DoCallbacks(self, "Log", {"expanding all bars", "GROUP"});
    end
    for bar in self:bar_objects() do
        bar:Expand();
    end
    for group in self:group_objects() do
        group:Expand();
    end
end

function Group:IsExpanded()
    for bar in self:bar_objects() do
        if (not bar:IsExpanded()) then
            return false;
        end
    end
    for group in self:group_objects() do
        if (not group:IsExpanded()) then
            return false;
        end
    end
    return true;
end

function Group:Collapse()
    if (self.Log) then
        DoCallbacks(self, "Log", {"collapsing all bars", "GROUP"});
    end
    for bar in self:bar_objects() do
        bar:Collapse();
    end
    for group in self:group_objects() do
        group:Collapse();
    end
end

function Group:IsCollapsed()
    for bar in self:bar_objects() do
        if (not bar:IsCollapsed()) then
            return false;
        end
    end
    for group in self:group_objects() do
        if (not group:IsCollapsed()) then
            return false;
        end
    end
    return true;
end

function Group:StartExecuting()
    if (self.Log) then
        DoCallbacks(self, "Log", {"starting continuous execution", "GROUP"});
    end
    for bar in self:bar_objects() do
        bar:StartExecuting();
    end
    for group in self:group_objects() do
        group:StartExecuting();
    end
end

function Group:StopExecuting()
    if (self.Log) then
        DoCallbacks(self, "Log", {"halting continuous execution", "GROUP"});
    end
    for bar in self:bar_objects() do
        bar:StopExecuting();
    end
    for group in self:group_objects() do
        group:StopExecuting();
    end
end

function Group:Redraw(noReset)
    for bar in self:bar_objects() do
        bar:Redraw(noReset);
    end
    for group in self:group_objects() do
        group:Redraw(noReset);
    end
end

function Group:SetTransparency(opacity)
    for bar in self:bar_objects() do
        bar:SetTransparency(opacity);
    end
    for group in self:group_objects() do
        group:SetTransparency(opacity);
    end
    self:SaveSettings(true);
end

function Group:SetUseOnRightClick(useOnRightClick)
    for bar in self:bar_objects() do
        bar:SetUseOnRightClick(useOnRightClick);
    end
    for group in self:group_objects() do
        group:SetUseOnRightClick(useOnRightClick);
    end
end

function Group:CanHaveChildren()
    return true;
end

function Group:Contains(object)
    while (object.parent) do
        if (object.parent == self) then
            return true;
        end
        object = object.parent;
    end
    return false;
end

function Group:GetChildIDs()
    local objectIDs = {};
    for b = 1, #self.settings.barIDs, 1 do
        local barID = self.settings.barIDs[b];
        table.insert(objectIDs, barID);
    end
    for g = 1, #self.settings.groupIDs, 1 do
        local groupID = self.settings.groupIDs[g];
        table.insert(objectIDs, groupID);
    end
    return objectIDs;
end

function Group:GetChild(childID)
    return self.manager.objects[childID];
end

function Group:GetBarsMoveTogether()
    if (self.settings.barsMoveTogether) then
        return true;
    elseif (self.parent and self.parent.GetBarsMoveTogether) then
        return self.parent:GetBarsMoveTogether();
    else
        return false;
    end
end

function Group:IsEmpty()
    return ((#self.settings.barIDs == 0) and (#self.settings.groupIDs == 0));
end

function Group:CloneBar(barID)
    local oldSettings = self.globals.bars[barID];
    local newBarID = self:FindNewObjectID();
    local newSettings = { };
    self.globals.bars[newBarID] = newSettings;
    DeepTableCopy(oldSettings, newSettings);
    local newBar = Bar(self, newBarID, newSettings);
    self.manager.objects[newBarID] = newBar;
    local gridSize = self.globals.gridSize;
    newBar:OffsetPosition(gridSize, gridSize);
    newBar:MoveOntoScreen();
    table.insert(self.settings.barIDs, newBarID);
    self:SaveSettings(true);
    return newBarID;
end

function Group:CloneGroup(groupID)
--Alert("Settings before cloning", PrettyPrint(self.globals, ""));
    local oldSettings = self.globals.groups[groupID];
    local newGroupID = self:FindNewObjectID();
    local newSettings = {};

    -- Create a clone of the existing group, but with nothing in it
    local oldGroup = self.manager.objects[groupID];
    self.globals.groups[newGroupID] = newSettings;
    DeepTableCopy(oldSettings, newSettings);
    newSettings.barIDs = {};
    newSettings.groupIDs = {};
    table.insert(self.settings.groupIDs, newGroupID);
    local newGroup = Group(self, newGroupID, newSettings);
    self.manager.objects[newGroupID] = newGroup;

    -- Clone the bars and subgroups, and transfer the clones from old group to new group
    for b = 1, #oldSettings.barIDs, 1 do
        local barID = oldSettings.barIDs[b];
        local newBarID = oldGroup:CloneBar(barID);
        self.manager:TransferBar(newBarID, oldGroup, newGroup);
    end
    for g = 1, #oldSettings.groupIDs, 1 do
        local oldGroupID = oldSettings.groupIDs[g];
        local _newGroupID = oldGroup:CloneGroup(oldGroupID);
        self.manager:TransferGroup(_newGroupID, oldGroup, newGroup);
    end

    self:SaveSettings(true);
--Alert("Settings after cloning", PrettyPrint(self.globals, ""));
    return newGroupID;
end

function Group:DeleteBar(barID)
    local foundPos;
    local bar = self.manager.objects[barID];
    if (bar) then
        self.globals.bars[barID].deleted = true;
        bar:Destroy();
    end
    for pos = 1, #self.settings.barIDs, 1 do
        if (self.settings.barIDs[pos] == barID) then
            foundPos = pos;
            break;
        end
    end
    table.remove(self.settings.barIDs, foundPos);
    table.insert(self.settings.deletedBarIDs, barID);
    self.manager.objects[barID] = nil;
    self:SaveSettings(true);
end

function Group:DeleteGroup(groupID)
    local group = self.manager.objects[groupID];
    if (group) then
        self.globals.groups[groupID].deleted = true;
        group:Destroy();
    end
    local foundPos = Search(self.settings.groupIDs, groupID);
    if (foundPos) then
        table.remove(self.settings.groupIDs, foundPos);
    end
    table.insert(self.settings.deletedGroupIDs, groupID);
    self.manager.objects[groupID] = nil;
    self:SaveSettings(true);
end

function Group:UndeleteBar(barID)
    local foundPos = Search(self.settings.deletedBarIDs, barID);
    if (foundPos) then
        table.remove(self.settings.deletedBarIDs, foundPos);
        self.globals.bars[barID].deleted = nil;
        self.manager.objects[barID] = Bar(self, barID, self.globals.bars[barID]);
        table.insert(self.settings.barIDs, barID);
        self:SaveSettings(true);
    end
end

function Group:UndeleteGroup(groupID)
    local foundPos = Search(self.settings.deletedGroupIDs, groupID);
    if (foundPos) then
        table.remove(self.settings.deletedGroupIDs, foundPos);
        self.globals.groups[groupID].deleted = nil;
        self.manager.objects[groupID] = Group(self, groupID, self.globals.groups[groupID]);
        table.insert(self.settings.groupIDs, groupID);
        self:SaveSettings(true);
    end
end

function Group:CreateBar()
    local barID = self:FindNewObjectID();
    self.globals.bars[barID] = {};
    table.insert(self.settings.barIDs, barID);
    local bar = Bar(self, barID, self.globals.bars[barID]);
    self.manager.objects[barID] = bar;
    bar:SnapToGrid();
    self:SaveSettings(true);
    return bar;
end

function Group:CreateGroup()
    local groupID = self:FindNewObjectID();
    self.globals.groups[groupID] = { };
    table.insert(self.settings.groupIDs, groupID);
    self.manager.objects[groupID] = Group(self, groupID, self.globals.groups[groupID]);
    self:SaveSettings(true);
    return group;
end

function Group:FindNewObjectID()
    return self.manager:FindNewObjectID();
end

function Group:Destroy()
    for bar in self:bar_objects() do
        bar:Destroy();
    end
    for group in self:group_objects() do
        group:Destroy();
    end
    if (self.importWindow) then
        self.importWindow:Close();
    end
    Node.Destroy(self);
end

function Group:HaveTrash()
    if (#self.settings.deletedBarIDs + #self.settings.deletedGroupIDs > 0) then
        return true;
    end
    for group in self:group_objects() do
        if (group:HaveTrash()) then
            return true;
        end
    end
    return false;
end

function Group:OpenImportWindow()
    -- Create the import window
    local title = L:GetText("/ImportWindow/Title");
    title = string.gsub(title, "<group>", self.settings.caption.text);
    self.importWindow = ImportWindow(title, self);
    self.importWindow.Closing = function()
        self.importWindow = nil;
    end
end

function Group:Import(importData)
    self.importData = importData;
        
    -- Uncompress the data in the next Update cycle.
    self:SetWantsUpdates(true);
    self.updateCount = 0;
    self.oldUpdateFunction = self.Update;
    self.Update = function(g)
        g.updateCount = g.updateCount + 1;
        if (g.updateCount == 1) then
            if (not string.find(g.importData, "{")) then
                Puts(L:GetText("/ImportWindow/Decoding"));
                g.encodedData = g.importData;
            else
                g.decodedData = g.importData;
            end
            g.importData = nil;
        elseif (g.updateCount == 3) then
            if (g.encodedData) then
                -- Remove whitespace and newlines
                g.encodedData = string.gsub(g.encodedData, "[%c%s]", "");
                local success, decodedData = pcall(Text2Bin, g.encodedData);
                if (not success) then
                    Puts(L:GetText("/ImportWindow/InvalidEncoding"));
                    g.encodedData = nil;
                    g.updateCount = 10;
                    return;
                end
                g.encodedData = nil;
                g.decodedData = decodedData;
            end
        elseif (g.updateCount == 4) then
            Puts(L:GetText("/ImportWindow/Decompressing"));
            g.compressor = Thurallor.Utils.LibCompress();
        elseif (g.updateCount == 6) then
            if (g.decodedData) then
                local success, uncompressedData = pcall(g.compressor.Decompress, g.compressor, g.decodedData);
                if ((not success) or (not uncompressedData)) then
                    -- Data was either not compressed or invalid.  Let's assume the former for now.
                    uncompressedData = g.decodedData;
                end
                g.decodedData = nil;
                g.uncompressedData = uncompressedData;
            end
        elseif (g.updateCount == 7) then
            local f, e = loadstring("return " .. g.uncompressedData);
            if (not f) then
                Puts(L:GetText("/ImportWindow/ParseError") .. tostring(e));
            else
                g.newObjects = f();
                if (type(g.newObjects) == "table") then
                    Puts(L:GetText("/ImportWindow/Importing"));
                    g:AddImportedObjects();
                else
                    Puts(L:GetText("/ImportWindow/NothingToDo"));
                end
            end
            g.compressor = nil;
            g.uncompressedData = nil;
            g.newObjects = nil;
        elseif (g.updateCount == 10) then
            g:SetWantsUpdates(false);
            g.Update = g.oldUpdateFunction; -- if this is a Manager object, it needs to have its Update function restored
            g:SaveSettings(true);
        end
    end
end

function Group:AddImportedObjects()
    
    -- Assign new object IDs to the new bars and groups.
    -- Find out which one(s) are at the top level.
    local newObjectIDs, hasParent = {}, {};
    for oldObjectID, settings in pairs(self.newObjects) do
        if (not newObjectIDs[oldObjectID]) then
            newObjectIDs[oldObjectID] = self:FindNewObjectID();
--Puts("     " .. oldObjectID .. " -> " .. newObjectIDs[oldObjectID]);
        end
        if (settings.type == "group") then
            local title = L:GetText("/GroupMenu/UndeleteMenu/GroupName");
            title = string.gsub(title, "<name>", settings.caption.text);
            Puts(" + " .. title);
            for b = 1, #settings.barIDs do
                local oldBarID = settings.barIDs[b];
                if (not newObjectIDs[oldBarID]) then
                    newObjectIDs[oldBarID] = self:FindNewObjectID();
--Puts("     " .. oldObjectID .. " -> " .. newObjectIDs[oldObjectID]);
                end
                local barID = newObjectIDs[oldBarID];
                settings.barIDs[b] = barID;
                hasParent[barID] = true;
            end
            for g = 1, #settings.groupIDs do
                local oldGroupID = settings.groupIDs[g];
                if (not newObjectIDs[oldGroupID]) then
                    newObjectIDs[oldGroupID] = self:FindNewObjectID();
--Puts("     " .. oldObjectID .. " -> " .. newObjectIDs[oldObjectID]);
                end
                local groupID = newObjectIDs[oldGroupID];
                settings.groupIDs[g] = groupID;
                hasParent[groupID] = true;
            end
        else
            local title = L:GetText("/GroupMenu/UndeleteMenu/BarName");
            title = string.gsub(title, "<name>", settings.caption.text);
            Puts(" + " .. title);
        end
    end

    -- Update all "include" slots with new object IDs
    for oldObjectID, settings in pairs(self.newObjects) do
        if (settings.sequenceItemInfo) then
            for i = 1, #settings.sequenceItemInfo, 1 do
                local item = settings.sequenceItemInfo[i];
                if (item.include and newObjectIDs[item.include]) then
                    item.include = newObjectIDs[item.include];
                end
            end
        end
    end
    
    -- Add new group/bar settings to the database.
    for oldObjectID, settings in pairs(self.newObjects) do
        if (settings.type == "group") then
            local groupID = newObjectIDs[oldObjectID];
            self.globals.groups[groupID] = settings;
        else
            local barID = newObjectIDs[oldObjectID];
            self.globals.bars[barID] = settings;
        end
    end

    -- For the top-level bars/groups, add them to the current group.
    for oldObjectID, settings in pairs(self.newObjects) do
        if (settings.type == "group") then
            local groupID = newObjectIDs[oldObjectID];
            if (not hasParent[groupID]) then
                table.insert(self.settings.groupIDs, groupID);
                self.manager.objects[groupID] = Group(self, groupID, settings);
            end
        else
            local barID = newObjectIDs[oldObjectID];
            if (not hasParent[barID]) then
                table.insert(self.settings.barIDs, barID);
                self.manager.objects[barID] = Bar(self, barID, settings);
            end
        end
    end
end

function Group:AddSettingsMenuItem(parent, itemName, amSubMenuOf, fromOptionsPanel)
    self.amSubMenuOf = amSubMenuOf or self.amSubMenuOf;
    local item = Turbine.UI.MenuItem(L:GetText(itemName), true, false);
    item._name = itemName;
    item._parent = parent;
    parent._itemsByName = parent._itemsByName or {};
    parent._itemsByName[itemName] = item;
    parent:GetItems():Add(item);
    
    if (itemName == "Root") then
        local prevContext = L:SetContext("/GroupMenu");
        parent:GetItems():Clear();
        self:AddSettingsMenuItem(parent, "Hide");
        if (not self:IsExpanded()) then
            self:AddSettingsMenuItem(parent, "Expand");
        end
        if (not self:IsCollapsed()) then
            self:AddSettingsMenuItem(parent, "Collapse");
        end
        self:AddSettingsMenuItem(parent, "CloneGroup");
        self:AddSettingsMenuItem(parent, "DeleteGroup");
        self:AddSettingsMenuItem(parent, "ExportGroup");
        if (not self.importWindow) then
            self:AddSettingsMenuItem(parent, "Import");
        end
        self:AddSettingsMenuItem(parent, "CreateNewBar");
        self:AddSettingsMenuItem(parent, "CreateNewSubgroup");
        if (#self.settings.deletedBarIDs + #self.settings.deletedGroupIDs > 0) then
            self:AddSettingsMenuItem(parent, "Undelete");
        end
        self:AddSettingsMenuItem(parent, "ArrangeBars");
        self:AddSettingsMenuItem(parent, "GroupEventBehaviors");
        self:AddSettingsMenuItem(parent, "GroupSettings");
        self:AddSettingsMenuItem(parent, "Debug");
        L:SetContext(prevContext);
    elseif (itemName == "Debug") then
        item:SetText(item:GetText() .. "...");
        item.Click = function()
            self:OpenDebugWindow();
        end
    elseif (itemName == "ExportGroup") then
        item.Click = function()
            self:Export();
        end
    elseif (itemName == "Import") then
        item.Click = function()
            self:OpenImportWindow();
        end
    elseif (itemName == "Hide") then
        item:SetChecked(self.settings.hidden);
        item.Click = function()
            self:SetHidden(not self.settings.hidden);
        end
    elseif (itemName == "GroupSettings") then
        local prevContext = L:SetContext("/GroupMenu/GroupSettingsMenu");
        self:AddSettingsMenuItem(item, "Rename");
        self:AddSettingsMenuItem(item, "BarsMoveTogether");
        L:SetContext(prevContext);
    elseif (itemName == "Undelete") then
        local prevContext = L:SetContext("/GroupMenu/UndeleteMenu");
        for b = 1, #self.settings.deletedBarIDs, 1 do
            local barID = self.settings.deletedBarIDs[b];
            local barItemName = L:GetText("BarName");
            if (self.globals.bars[barID]) then
                local barCaption = self.globals.bars[barID].caption.text;
                barItemName = string.gsub(barItemName, "<name>", barCaption);
                local barItem = Turbine.UI.MenuItem(barItemName, true, false);
                item:GetItems():Add(barItem);
                barItem.Click = function()
                    self:UndeleteBar(barID);
                end
            end
        end
        for g = 1, #self.settings.deletedGroupIDs, 1 do
            local groupID = self.settings.deletedGroupIDs[g];
            local groupItemName = L:GetText("GroupName");
            if (self.globals.groups[groupID]) then
                local groupName = self.globals.groups[groupID].caption.text;
                groupItemName = string.gsub(groupItemName, "<name>", groupName);
                local groupItem = Turbine.UI.MenuItem(groupItemName, true, false);
                item:GetItems():Add(groupItem);
                groupItem.Click = function()
                    self:UndeleteGroup(groupID);
                end
            end
        end
        L:SetContext(prevContext);
    elseif (itemName == "Rename") then
        item:SetEnabled(not self.captionEditor);
        item.Click = function()
            self:EditCaption();
        end
    elseif (itemName == "BarsMoveTogether") then
        local topGroup = self:GetTopTogetherGroup();
        if (topGroup and (topGroup ~= self)) then
            item:SetChecked(true);
            local text = L:GetText("/GroupMenu/GroupSettingsMenu/BarsMoveTogetherInherited");
            item:SetText(text:gsub("<name>", topGroup:GetName()));
            item:SetEnabled(false);
        else
            item:SetChecked(self.settings.barsMoveTogether);
        end
        item.Click = function()
            self.settings.barsMoveTogether = not self.settings.barsMoveTogether;
        end
    elseif (itemName == "ArrangeBars") then
        local prevContext = L:SetContext("/GroupMenu/ArrangeBarsMenu");
        local descendentBarIDs = self:FindAllDescendentIDs(nil, true, nil);
        if (#descendentBarIDs < 2) then
            item:SetEnabled(false);
        else
            self:AddSettingsMenuItem(item, "AlignVertically");
            self:AddSettingsMenuItem(item, "AlignHorizontally");
        end
        L:SetContext(prevContext);
    elseif ((itemName == "GroupEventBehaviors") or (itemName == "GlobalEventBehaviors")) then
        local prevContext = L:SetContext("/EventBehaviorsMenu");
        if (self.settings.eventsEnabled) then
            self:AddSettingsMenuItem(item, "AddBehavior");
            if (#self.settings.eventHandlers > 0) then
                self:AddSettingsMenuItem(item, "CurrentBehaviors");
            end
            self:AddSettingsMenuItem(item, "DisableEvents");
        else
            self:AddSettingsMenuItem(item, "EnableEvents");
        end
        L:SetContext(prevContext);
    elseif (itemName == "EnableEvents") then
        item.Click = function(ctl)
            self:SetEventHandlersEnabled(true);
            self:RedisplayMenu(ctl._parent);
        end
    elseif (itemName == "DisableEvents") then
        item.Click = function(ctl)
            self:SetEventHandlersEnabled(false);
            self:RedisplayMenu(ctl._parent);
        end
    elseif (itemName == "CurrentBehaviors") then
        if (self.settings.eventHandlers) then
            for h = 1, #self.settings.eventHandlers do
                local handler = self.settings.eventHandlers[h];
                local handlerItem = Turbine.UI.MenuItem(handler.description, true, false);
                handlerItem._parent = item;
                item:GetItems():Add(handlerItem);
                local removeItem = Turbine.UI.MenuItem(L:GetText("Remove"), true, false);
                removeItem._parent = handlerItem;
                handlerItem:GetItems():Add(removeItem);
                removeItem.Click = function(ctl)
                    self:RemoveEventHandler(h);
                    self:RedisplayMenu(ctl._parent._parent);
                end
            end
        end
    elseif (itemName == "AddBehavior") then
        local prevContext = L:SetContext("/EventBehaviorsMenu/Actions");
        self:AddSettingsMenuItem(item, "ShowGroup");
        self:AddSettingsMenuItem(item, "HideGroup");
        self:AddSettingsMenuItem(item, "ResetGroup");
        self:AddSettingsMenuItem(item, "ExpandGroup");
        self:AddSettingsMenuItem(item, "CollapseGroup");
        self:AddSettingsMenuItem(item, "SetGroupTransparency");
        self:AddSettingsMenuItem(item, "StartExecuting");
        self:AddSettingsMenuItem(item, "StopExecuting");
        L:SetContext(prevContext);
    elseif (itemName == "SetGroupTransparency") then
        local prevContext = L:SetContext("/BarMenu/SettingsMenu/TransparencyMenu");
        local subItem = self:AddSettingsMenuItem(item, "SetGroupTransparency:");
        subItem._name = "SetGroupTransparency";
        subItem:SetText(L:GetText("UseGlobalSetting"));
        subItem.argName = L:GetText("default");
        subItem.argValue = "nil";
        for i = 0, 10 do
            subItem = self:AddSettingsMenuItem(item, "SetGroupTransparency:");
            subItem._name = "SetGroupTransparency";
            subItem.argName = L:GetText("PartiallyTransparent"):gsub("<pct>", i * 10);
            if (i == 0) then
                subItem:SetText(L:GetText("FullyTransparent"));
                subItem.argValue = "0";
            elseif (i == 10) then
                subItem:SetText(L:GetText("FullyOpaque"));
                subItem.argValue = "1";
            else
                subItem:SetText(subItem.argName);
                subItem.argValue = "0." .. i;
            end
        end
        L:SetContext(prevContext);
        
    elseif (string.find("|SetGroupTransparency:|ShowGroup|HideGroup|ResetGroup|ExpandGroup|CollapseGroup|StartExecuting|StopExecuting|", "|" .. itemName .. "|")) then
        local prevContext = L:SetContext("/EventBehaviorsMenu/Triggers");
        if ((itemName ~= "ResetGroup") and (itemName ~= "StopExecuting")) then
            self:AddSettingsMenuItem(item, "AtStartup");
        end
        self:AddSettingsMenuItem(item, "WhenCombatBegins");
        self:AddSettingsMenuItem(item, "WhenCombatEnds");
        self:AddSettingsMenuItem(item, "WhenTargetChanges");
        if (string.find("|SetGroupTransparency:|ResetGroup|ExpandGroup|StartExecuting|", "|" .. itemName .. "|")) then
            self:AddSettingsMenuItem(item, "WhenMouseArrives");
        end
        if (string.find("|SetGroupTransparency:|HideGroup|ResetGroup|CollapseGroup|StopExecuting|", "|" .. itemName .. "|")) then
            self:AddSettingsMenuItem(item, "WhenMouseDeparts");
        end
        if (string.find("|SetGroupTransparency:|ShowGroup|ResetGroup|StartExecuting|StopExecuting|", "|" .. itemName .. "|")) then
            self:AddSettingsMenuItem(item, "WhenTraitTreeChanges");
            self:AddSettingsMenuItem(item, "WhenStanceChanges");
        end
        self:AddSettingsMenuItem(item, "WhenPetChanges");
        self:AddSettingsMenuItem(item, "WhenMountChanges");
        self:AddSettingsMenuItem(item, "WhenYouAreIncapacitated");
        self:AddSettingsMenuItem(item, "WhenYouAreRevived");
        self.availableEvents = self.manager:GetEventNames();
        self:AddSettingsMenuItem(item, "WhenEventOccurs");
        L:SetContext(prevContext);
    elseif (itemName == "WhenEventOccurs") then
        if (#self.availableEvents > 0) then
            for e = 1, #self.availableEvents do
                local eventName = self.availableEvents[e];
                local eventNameItem = Turbine.UI.MenuItem(eventName, true, false);
                eventNameItem._parent = item;
                item:GetItems():Add(eventNameItem);
                eventNameItem.Click = function(ctl)
                    self:AddEventHandler("WhenEventOccurs:" .. eventName, parent._name, parent.argValue, parent.argName);
                    self:RedisplayMenu(ctl._parent._parent._parent._parent, "CurrentBehaviors");
                end
            end
        else
            local noEventsItem = Turbine.UI.MenuItem(L:GetText("/EventBehaviorsMenu/NoEventsDefined"), false, false);
            item:GetItems():Add(noEventsItem);
        end
        local eventDirectoryItem = Turbine.UI.MenuItem("( . . . )", true, false);
        eventDirectoryItem.Click = function()
            self.manager:ShowDirectory("events");
        end
        item:GetItems():Add(eventDirectoryItem);
    elseif (string.find("|AtStartup|WhenTargetChanges|WhenCombatBegins|WhenCombatEnds|WhenMouseArrives|WhenMouseDeparts|WhenTraitTreeChanges|WhenStanceChanges|WhenPetChanges|WhenMountChanges|WhenYouAreIncapacitated|WhenYouAreRevived|", "|" .. itemName .. "|")) then
        item.Click = function(ctl)
            self:AddEventHandler(itemName, parent._name, parent.argValue, parent.argName);
            self:RedisplayMenu(ctl._parent._parent._parent, "CurrentBehaviors");
        end
    elseif (itemName == "AlignVertically") then
        local prevContext = L:SetContext("/GroupMenu/ArrangeBarsMenu/AlignVerticallyMenu");
        self:AddSettingsMenuItem(item, "Tops");
        self:AddSettingsMenuItem(item, "Middles");
        self:AddSettingsMenuItem(item, "Bottoms");
        L:SetContext(prevContext);
    elseif (itemName == "AlignHorizontally") then
        local prevContext = L:SetContext("/GroupMenu/ArrangeBarsMenu/AlignHorizontallyMenu");
        self:AddSettingsMenuItem(item, "LeftSides");
        self:AddSettingsMenuItem(item, "Centers");
        self:AddSettingsMenuItem(item, "RightSides");
        L:SetContext(prevContext);
    elseif (string.find("|Tops|Middles|Bottoms|LeftSides|Centers|RightSides|", "|" .. itemName .. "|")) then
        local checked = self:BarsAreAligned(itemName);
        item:SetChecked(checked);
        item:SetEnabled(not checked);
        item.Click = function()
            self:AlignBars(itemName);
        end
    elseif (itemName == "Expand") then
        item.Click = function()
            self:Expand();
        end
    elseif (itemName == "Collapse") then
        item.Click = function()
            self:Collapse();
        end
    elseif (itemName == "CloneGroup") then
        item.Click = function()
            self.parent:CloneGroup(self.groupID);
        end
    elseif (itemName == "DeleteGroup") then
        item.Click = function()
            self.parent:DeleteGroup(self.groupID);
        end
    elseif (itemName == "Global") then
        self.manager:AddSettingsMenuItem(item, "Root", self);
    elseif (itemName == "CreateNewBar") then
        item.Click = function()
            self:CreateBar();
        end
    elseif (itemName == "CreateNewSubgroup") then
        item.Click = function()
            self:CreateGroup();
        end
    end
    return item;
end

function Group:Reset()
    if (self.Log) then
        DoCallbacks(self, "Log", {"reset", "GROUP"})
    end
    for bar in self:bar_objects() do
        bar:Reset();
    end
    for group in self:group_objects() do
        group:Reset();
    end
end

function Group:FindAllDescendentIDs(descendents, includeBars, includeGroups)
    if (descendents == nil) then
        descendents = {};
    end
    for group in self:group_objects() do
        if (includeGroups) then
            table.insert(descendents, group.objectID);
        end
        group:FindAllDescendentIDs(descendents, includeBars, includeGroups);
    end
    if (includeBars) then
        for bar in self:bar_objects() do
            table.insert(descendents, bar.objectID);
        end
    end
    return descendents;
end

function Group:GetBarLocationData(barIDs)
    local lefts, tops, rights, bottoms, centers, middles = {}, {}, {}, {}, {}, {};
    if (barIDs == nil) then
        barIDs = self:FindAllDescendentIDs(nil, true, nil);
    end
    local numBars = 0;
    for b = 1, #barIDs, 1 do
        local barID = barIDs[b];
        local bar = self.manager.objects[barID];
        if (bar) then
            numBars = numBars + 1;
            local left, top, width, height = bar:GetVisibleLeft(), bar:GetVisibleTop(), bar:GetVisibleWidth(), bar:GetVisibleHeight();
            table.insert(lefts, left);
            table.insert(tops, top);
            local right, bottom = left + width, top + height;
            table.insert(rights, right);
            table.insert(bottoms, bottom);
            local center = (left + right) / 2;
            local middle = (top + bottom) / 2;
            table.insert(centers, center);
            table.insert(middles, middle);
        end
    end
    return numBars, lefts, tops, rights, bottoms, centers, middles;
end

function Group:GetBarLocationStats(numBars, lefts, tops, rights, bottoms, centers, middles)
    local minLeft, maxRight = math.min(unpack(lefts)), math.max(unpack(rights));
    local minTop, maxBottom = math.min(unpack(tops)), math.max(unpack(bottoms));
    local avgCenter, avgMiddle = 0, 0;
    for b = 1, numBars, 1 do
        avgCenter = avgCenter + centers[b];
        avgMiddle = avgMiddle + middles[b];
    end
    avgCenter = avgCenter / numBars;
    avgMiddle = avgMiddle / numBars;
    local minCenter, maxCenter = math.min(unpack(centers)), math.max(unpack(centers));
    local minMiddle, maxMiddle = math.min(unpack(middles)), math.max(unpack(middles));
    return numBars, minLeft, maxRight, minTop, maxBottom, minCenter, avgCenter, maxCenter, minMiddle, avgMiddle, maxMiddle;
end

function Group:BarsAreAligned(alignment)
    local barIDs = self:FindAllDescendentIDs(nil, true, nil);
    local numBars, lefts, tops, rights, bottoms, centers, middles = self:GetBarLocationData(barIDs);
    local _, minLeft, maxRight, minTop, maxBottom, _, avgCenter, _, _, avgMiddle, _ = self:GetBarLocationStats(numBars, lefts, tops, rights, bottoms, centers, middles);
    local aligned = true;
    for b = 1, numBars, 1 do
        if (alignment == "LeftSides") then
            aligned = aligned and lefts[b] == minLeft;
        elseif (alignment == "RightSides") then
            aligned = aligned and rights[b] == maxRight;
        elseif (alignment == "Tops") then
            aligned = aligned and tops[b] == minTop;
        elseif (alignment == "Bottoms") then
            aligned = aligned and bottoms[b] == maxBottom;
        elseif (alignment == "Centers") then
            aligned = aligned and math.floor(centers[b]) == math.floor(avgCenter);
        elseif (alignment == "Middles") then
            aligned = aligned and math.floor(middles[b]) == math.floor(avgMiddle);
        end
    end
    return aligned;
end

function Group:AlignBars(alignment)
    local barIDs = self:FindAllDescendentIDs(nil, true, nil);
    local numBars, lefts, tops, rights, bottoms, centers, middles = self:GetBarLocationData(barIDs);
    local _, minLeft, maxRight, minTop, maxBottom, _, avgCenter, _, _, avgMiddle, _ = self:GetBarLocationStats(numBars, lefts, tops, rights, bottoms, centers, middles);
    for bar in self:bar_objects() do
        if (alignment == "LeftSides") then
            bar:SetVisibleLeft(minLeft);
        elseif (alignment == "RightSides") then
            bar:SetVisibleLeft(maxRight - bar:GetVisibleWidth());
        elseif (alignment == "Tops") then
            bar:SetVisibleTop(minTop);
        elseif (alignment == "Bottoms") then
            bar:SetVisibleTop(maxBottom - bar:GetVisibleHeight());
        elseif (alignment == "Centers") then
            bar:SetVisibleLeft(avgCenter - math.floor((bar:GetVisibleWidth() + 0.5) / 2));
        elseif (alignment == "Middles") then
            bar:SetVisibleTop(avgMiddle - math.floor((bar:GetVisibleHeight() + 0.5) / 2));
        end
        bar:Redraw();
    end
    self:SaveSettings(false);
end

-- Finds the topmost group containing (or being) this group which has the "bars move together" option enabled.
function Group:GetTopTogetherGroup()
    local group, topGroup = self;
    while (group) do
        if (group.settings.barsMoveTogether) then
            topGroup = group;
        end
        group = group.parent;
    end
    return topGroup;
end

function Group:SnapToGrid()
    -- Snap all children contained in this group and subgroups to grid
    for bar in self:bar_objects() do
        bar:SnapToGrid();
    end
    for group in self:group_objects() do
        group:SnapToGrid();
    end
end

function Group:OffsetPosition(deltaX, deltaY)
    -- Move all children contained in this group and subgroups, irrespective of locks
    for bar in self:bar_objects() do
        bar:OffsetPosition(deltaX, deltaY);
    end
    for group in self:group_objects() do
        group:OffsetPosition(deltaX, deltaY);
    end
end

function Group:MouseArrive(objectID)
    if (self.mouseInside) then
        -- Already inside; do nothing.
        return;
    end
    self.mouseInside = true;
    if (self.parent) then
        self.parent:MouseArrive(self.objectID);
    end
    DoCallbacks(self, "MouseArrived");
end

function Group:MouseInside(mouseLeft, mouseTop)
    if (self:IsHidden()) then
        return false;
    end
    for bar in self:bar_objects() do
        if (bar:MouseInside(mouseLeft, mouseTop)) then
            return true;
        end
    end
    for group in self:group_objects() do
        if (group:MouseInside(mouseLeft, mouseTop)) then
            return true;
        end
    end
    return false;
end

function Group:MouseDepart()
    if (self.parent) then
        self.parent:MouseDepart(self.objectID);
    end
    
    -- If a callback is registered, we need to make sure the mouse hasn't
    -- entered another bar in the group before generating the MouseDeparted event.
    if (self.MouseDeparted) then
        local mouseLeft, mouseTop = Turbine.UI.Display:GetMousePosition();
        if (not self:MouseInside(mouseLeft, mouseTop)) then
            self.mouseInside = false;
            DoCallbacks(self, "MouseDeparted");
        end
    else
        self.mouseInside = false;
    end
end

function Group:OpenDebugWindow()
    local title = L:GetText("/GroupMenu/Debug") .. ": " .. self.settings.caption.text;
    local debugWindow = Thurallor.UI.LogWindow(title, { self });
    debugWindow:SetBackColor(self.color);
    debugWindow:SetTrace("GROUP", "Group status changes", true);
    debugWindow:SetTrace("EVENT", "Event behaviours", true);
    debugWindow:SetVisible(true);
    debugWindow:AppendText("Debugging started");
end

function Group:EditCaption(optionsNode)
    if (self.captionEditor == nil) then
        if (optionsNode) then
            self.settings.caption.font = optionsNode:GetFont();
            self.settings.caption.width = optionsNode:GetWidth();
            self.settings.caption.height = optionsNode:GetHeight();
        end
        self.captionEditor = CaptionEditor("GroupName", self, self.settings.caption.width, self.settings.caption.height, self.settings.caption.text);
        self:FormatCaptionEditorTextBox();
        self.captionEditor.Closed = function()
            self.captionEditor = nil;
        end
    end
end

function Group:FormatCaptionEditorTextBox()
    if (self.captionEditor ~= nil) then
        local control = self.captionEditor:GetTextBox();
        control:SetFont(self.settings.caption.font);
        control:SetForeColor(self.color);
        control:SetFontStyle(Turbine.UI.FontStyle.Outline);
        control:SetOutlineColor(Turbine.UI.Color(0.75, 0, 0, 0));
        control:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
        control:SetWidth(self.settings.caption.width);
        control:SetHeight(self.settings.caption.height);
        self.captionEditor:Redraw();
    end
end

function Group:SetCaptionSize(width, height)
    Node.SetCaptionSize(self, width, height);
    self:FormatCaptionEditorTextBox();
end

function Group:SetColor(color)
    Node.SetColor(self, color);
    self:FormatCaptionEditorTextBox();
end

Compare with Previous | Blame


All times are GMT -5. The time now is 10:29 PM.


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