lotrointerface.com
Search Downloads

LoTROInterface SVN Reminders

[/] [trunk/] [Thurallor/] [Reminders/] [OptionsPanel.lua] - Rev 11

Compare with Previous | Blame | View Log

OptionsPanel = class(Turbine.UI.Lotro.Window);

local importPath = getfenv(1)._.Name;
local imagePath = string.gsub(string.gsub(importPath, "%.OptionsPanel$", ""), "%.", "/") .. "/Images/";

function OptionsPanel:Constructor(window, settings)
    Turbine.UI.Lotro.Window.Constructor(self);
    self.window = window;
    self.settings = settings;
    self.updateNeeded = true;

    local player = Turbine.Gameplay.LocalPlayer.GetInstance();
    self.charID = Turbine.Engine.GetServerID() .. "." .. player:GetName();
    self.selectedCharID = self.charID;
    self.selectedTab = 1;
    self.expanded = {}; -- keeps track of which sections are expanded / collapsed
    self.highlightAreas = {};

    AddCallback(L, "LanguageChanged", function()
        self:Redraw();
    end);
    self:Redraw();

    self:SetResizable(true);
    self:SetSize(unpack(settings.optionsPanel.size));
    self:SetPosition(unpack(settings.optionsPanel.position));
end

function OptionsPanel:Redraw()
    local prevContext = L:SetContext("/OptionsPanel");
    self:SetText(L:GetText("Title"));

    self:GetControls():Clear();

    self.tabStack = Thurallor.UI.TabCardStack();
    self.tabStack:SetParent(self);
    self.tabStack:SetPosition(14, 34);
    
    local globalTab = Thurallor.UI.TabCard(false, true, false, false);
    globalTab:SetTabText(L:GetText("GlobalTab/TabName"));
    globalTab:SetTabText(L:GetText("GlobalTab/TabName"));
    self.globalPanel = Turbine.UI.Control();
    globalTab:SetInteriorControl(self.globalPanel);
    globalTab:SetInteriorAlignment(Turbine.UI.ContentAlignment.TopLeft);
    self.tabStack:AddTabCard(globalTab);
    self.globalTab = globalTab;

    local tabTab = Thurallor.UI.TabCard(false, true, false, false);
    tabTab:SetTabText(L:GetText("TabTab/TabName"));
    self.tabPanel = Turbine.UI.Control();
    tabTab:SetInteriorControl(self.tabPanel);
    tabTab:SetInteriorAlignment(Turbine.UI.ContentAlignment.TopLeft);
    self.tabStack:AddTabCard(tabTab);
    self.tabTab = tabTab;

    local charTab = Thurallor.UI.TabCard(false, true, false, false);
    self.charPanel = Turbine.UI.Control();
    charTab:SetInteriorControl(self.charPanel);
    charTab:SetInteriorAlignment(Turbine.UI.ContentAlignment.TopLeft);
    charTab:SetTabLeft(1);
    charTab:SetTabText(L:GetText("CharacterTab/TabName"));
    self.tabStack:AddTabCard(charTab);
    self.charTab = charTab;

    local function tabClicked(_, args)
        if (args.Button == Turbine.UI.MouseButton.Left) then
            self:UpdateDisplay();
        end
    end
    AddCallback(globalTab, "Click", tabClicked);
    AddCallback(tabTab, "Click", tabClicked);
    AddCallback(charTab, "Click", tabClicked);

    globalTab:BringToFront();
    L:SetContext(prevContext);
    self:UpdateDisplay();
end

function OptionsPanel:Show()
    if (not self:IsVisible()) then
        EnsureOnScreen(self, true);
    end
    self:SetVisible(true);
    self:UpdateDisplay();
    self:Activate();
end

function OptionsPanel:Closing(args)
    self:SetVisible(false);
    args.Cancel = true;
    if (self.window:IsVisible()) then
        self.zoomer = Thurallor.UI.Zoomer(self, self.window.settingsButton);
    else
        self.zoomer = Thurallor.UI.Zoomer(self, self.window.icon);
    end
    self.zoomer.ZoomComplete = function()
        self.zoomer = nil;
    end
end

function OptionsPanel:MouseEnter()
    if (self.needsUpdate) then
        self:UpdateDisplay();
    end
end

function OptionsPanel:MouseLeave()
    -- Accept textbox values.
    self:Focus();
end

function OptionsPanel:SaveSettings()
    self.window:SaveSettings();
end

function OptionsPanel:SizeChanged()
    local width, height = self:GetSize();
    self.globalPanel:SetWidth(width - 44);
    self.tabPanel:SetWidth(width - 44);
    self.charPanel:SetWidth(width - 44);
    self.tabStack:SetSize(width - 28, height - 56);
    self.tabStack:RedistributeTabs();
    self.settings.optionsPanel.size = { width, height };
    self:SaveSettings();
end

function OptionsPanel:PositionChanged()
    local left, top = self:GetPosition();
    self.settings.optionsPanel.position = { left, top };
    self:SaveSettings();
end

function OptionsPanel:UpdateDisplay(force)
    if (self.colorPicker) then
        self.colorPicker:Close();
        self.colorPicker = nil;
    end
    if (self:IsVisible() or force) then
        if (self.globalTab:IsInFront()) then
            self:UpdateGlobalDisplay();
        elseif (self.tabTab:IsInFront()) then
            self:UpdateTabDisplay();
        else -- (self.charTab:IsInFront())
            self:UpdateCharDisplay();
        end
        self:SizeChanged();
        self.needsUpdate = false;
    else
        self.needsUpdate = true;
    end
end

function OptionsPanel:UpdateTabDisplay()
    local panel = self.tabPanel;
    local top = 16;
    local heading, checkbox, label, swatch, button, dropDown, xButton, expandButton;
    local tabSettings = self.settings.tabs[self.selectedTab];
    local tabNames, tabExists = self.window:GetUserTabs();
    local tabName, catName, sectionName;
    local prevContext = L:SetContext("/OptionsPanel/TabTab");
    
    -- Remove child controls and SizeChanged callbacks; we'll recreate them now
    panel:GetControls():Clear();
    panel.SizeChanged = nil;

    dropDown, label, top = self:AddDropDown(panel, top, 200, tabNames, tabSettings.name, L:GetText("ChooseTab"));
    dropDown.SelectionChanged = function(_, args)
        for n, tabInfo in pairs (self.settings.tabs) do
            if (args.Item == tabInfo.name) then
                self.selectedTab = n;
                break;
            end
        end
        self:UpdateDisplay();
    end

    sectionName = "Alerts";
    heading, top = self:AddHeading(panel, top, sectionName, true);
    if (self.expanded[sectionName]) then
    
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("UpdateIcon"), tabSettings.updateIcon);
        checkbox.CheckedChanged = function(ctl)
            tabSettings.updateIcon = ctl:IsChecked();
            self.window:RebuildTabs();
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("ShowPopupNotifications"), tabSettings.showPopupNotifications);
        checkbox.CheckedChanged = function(ctl)
            tabSettings.showPopupNotifications = ctl:IsChecked();
            self.window:RebuildTabs();
            self:SaveSettings();
        end
    end

    sectionName = "OtherOptions";
    heading, top = self:AddHeading(panel, top, sectionName, true);
    if (self.expanded[sectionName]) then
        label, top = self:AddLabel(panel, top, L:GetText("OtherOptionsHow"));
    end

    panel:SetHeight(top + 24);
    L:SetContext(prevContext);
    DoCallbacks(panel, "SizeChanged");
end

function OptionsPanel:DeleteSelectedChar()
    if (self.selectedCharID == self.charID) then
        self.settings.characters[self.charID] = self.window:UpdateCharSettings({});
    else
        self.settings.characters[self.selectedCharID] = nil;
        self.selectedCharID = self.charID;
    end
    self:SaveSettings();
    self:UpdateDisplay();
end

function OptionsPanel:CreateHighlightArea(name, control)
    self.highlightAreas[name] = { control };
end

function OptionsPanel:ExpandHighlightArea(name, control)
    table.insert(self.highlightAreas[name], control);
end

function OptionsPanel:ShowHighlightArea(charID, name)
    self.expanded.StandardAutoReminders = true;
    self.charTab:BringToFront();
    self.charTab:ScrollToTop();
    self.selectedCharID = charID;
    self:Show();
    local panel = self.charPanel;
    local left, top, right, bottom = math.huge, math.huge, 0, 0;
    for _, control in pairs(self.highlightAreas[name]) do
        l, t = control:GetPosition();
        w, h = control:GetSize();
        r, b = l + w, t + h;
        left = math.min(left, l);
        top = math.min(top, t);
        right = math.max(right, r);
        bottom = math.max(bottom, b);
    end
    left = 0;
    top = top - 5;
    bottom = bottom + 5;
    local width = panel:GetWidth() - 4;
    local height = bottom - top;
    panel.marquee = Thurallor.UI.Marquee();
    panel.marquee:SetColor(Turbine.UI.Color(0.75, 0.75, 0.75));
    panel.marquee:SetPosition(left, top);
    panel.marquee:SetSize(width, height);
    panel.marquee:SetParent(panel);
    AddCallback(panel, "SizeChanged", function()
        panel.marquee:SetWidth(panel:GetWidth() - 4);
    end);
    return panel.marquee;
end

function OptionsPanel:ShowTabSettings(tabName)
    self.tabTab:BringToFront();
    self.tabTab:ScrollToTop();
    for n, tabInfo in pairs (self.settings.tabs) do
        if (tabName == tabInfo.name) then
            self.selectedTab = n;
            break;
        end
    end
    self:Show();
end

function OptionsPanel:UpdateCharDisplay()
    local panel = self.charPanel;
    local top = 16;
    local heading, checkbox, label, swatch, button, dropDown, xButton, expandButton;
    local charSettings = self.settings.characters[self.selectedCharID];
    local tabNames, tabExists = self.window:GetUserTabs();
    local catNames, catExists = self.window:GetUserCategories();
    local tabName, catName, sectionName;
    local prevContext = L:SetContext("/OptionsPanel/CharacterTab");
    
    -- Remove child controls and SizeChanged callbacks; we'll recreate them now
    panel:GetControls():Clear();
    panel.SizeChanged = nil;

    local selectedChar = self.window:GetCharNameWithServer(self.selectedCharID, true);
    local charNamesWithServers, charIDs = self.window:GetCharNamesWithServers(true);
    dropDown, label, top = self:AddDropDown(panel, top, 200, charNamesWithServers, selectedChar, L:GetText("ChooseCharacter"));
    dropDown.charIDs = charIDs;
    dropDown.SelectionChanged = function(dd, args)
        self.selectedCharID = dd.charIDs[args.Item];
        self:UpdateDisplay();
    end

    self.forgetCharButton = self:AddXButton(panel, 19);
    Thurallor.UI.Tooltip(L:GetText("ForgetCharacter")):Attach(self.forgetCharButton);
    self.forgetCharButton.MouseClick = function(ctl, args)
        if (args.Button == Turbine.UI.MouseButton.Left) then
            if (self.window.settings.askBeforeDeleting) then
                if (not self.confirmDialog) then
                    self.confirmDialog = ConfirmDialog(self.window, L:GetText("/OptionsPanel/CharacterTab/ForgetCharacter"));
                    self.confirmDialog.Yes = function(_, arg)
                        self:DeleteSelectedChar();
                        self.confirmDialog = nil;
                    end
                    self.confirmDialog.No = function(_, arg)
                        self.window.settings.askBeforeDeleting = arg;
                        self.confirmDialog = nil;
                    end
                end
            else
                self:DeleteSelectedChar();
            end
        end
    end
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        self.forgetCharButton:SetLeft(center + 204);
    end);

    sectionName = "StandardAutoReminders";
    heading, top = self:AddHeading(panel, top, sectionName, true);
    if (self.expanded[sectionName]) then
        local warning = L:GetText("ChannelNeededWarning");

        local function CheckAutoReminderParams(name)
            if (not charSettings.autoReminders[name]) then
                charSettings.autoReminders[name] = {};
            end
            tabName = charSettings.autoReminders[name].tab;
            if ((not tabName) or (not tabExists[tabName])) then
                charSettings.autoReminders[name].tab = tabNames[1];
            end
            catName = charSettings.autoReminders[name].category;
            if ((not catName) or (not catExists[catName])) then
                charSettings.autoReminders[name].category = catNames[1];
            end
        end

        CheckAutoReminderParams("raidlocks");
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("RaidLockRemindersEnabled"), charSettings.autoReminders.raidlocks.enabled);
        self:CreateHighlightArea("raidlocks", checkbox);
        checkbox.CheckedChanged = function(ctl)
            charSettings.autoReminders.raidlocks.enabled = ctl:IsChecked();
            self:SaveSettings();
            self:UpdateDisplay();
        end

        if (charSettings.autoReminders.raidlocks.enabled) then
            dropDown, label, top = self:AddDropDown(panel, top, 200, tabNames, charSettings.autoReminders.raidlocks.tab, L:GetText("ChooseTab"));
            self:ExpandHighlightArea("raidlocks", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.raidlocks.tab = args.Item;
                self:SaveSettings();
            end

            dropDown, label, top = self:AddDropDown(panel, top, 200, catNames, charSettings.autoReminders.raidlocks.category, L:GetText("ChooseCategory"));
            self:ExpandHighlightArea("raidlocks", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.raidlocks.category = args.Item;
                self:SaveSettings();
            end
            
            checkbox, top = self:AddCheckBox(panel, top, L:GetText("RaidLockRemindersNotesEnabled"), charSettings.autoReminders.raidlocks.notesEnabled, true);
            self:ExpandHighlightArea("raidlocks", checkbox);
            checkbox.CheckedChanged = function(ctl)
                charSettings.autoReminders.raidlocks.notesEnabled = ctl:IsChecked();
                self:SaveSettings();
            end
        end

        top = top + 10; -- skip some space

        CheckAutoReminderParams("crafting");
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("CraftingRemindersEnabled"), charSettings.autoReminders.crafting.enabled);
        self:CreateHighlightArea("crafting", checkbox);
        checkbox.CheckedChanged = function(ctl)
            charSettings.autoReminders.crafting.enabled = ctl:IsChecked();
            self:SaveSettings();
            self:UpdateDisplay();
        end

        if (charSettings.autoReminders.crafting.enabled) then
            dropDown, label, top = self:AddDropDown(panel, top, 200, tabNames, charSettings.autoReminders.crafting.tab, L:GetText("ChooseTab"));
            self:ExpandHighlightArea("crafting", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.crafting.tab = args.Item;
                self:SaveSettings();
            end

            dropDown, label, top = self:AddDropDown(panel, top, 200, catNames, charSettings.autoReminders.crafting.category, L:GetText("ChooseCategory"));
            self:ExpandHighlightArea("crafting", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.crafting.category = args.Item;
                self:SaveSettings();
            end

            local channel = L:GetText("CraftingRemindersChannelNeeded");
            label, top = self:AddWarning(panel, top, warning:gsub("<channel>", channel));
            self:ExpandHighlightArea("crafting", label);
        end

        top = top + 10; -- skip some space

        CheckAutoReminderParams("tasks");
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("TasksRemindersEnabled"), charSettings.autoReminders.tasks.enabled);
        self:CreateHighlightArea("tasks", checkbox);
        checkbox.CheckedChanged = function(ctl)
            charSettings.autoReminders.tasks.enabled = ctl:IsChecked();
            self:SaveSettings();
            self:UpdateDisplay();
        end

        if (charSettings.autoReminders.tasks.enabled) then
            dropDown, label, top = self:AddDropDown(panel, top, 200, tabNames, charSettings.autoReminders.tasks.tab, L:GetText("ChooseTab"));
            self:ExpandHighlightArea("tasks", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.tasks.tab = args.Item;
                self:SaveSettings();
            end

            dropDown, label, top = self:AddDropDown(panel, top, 200, catNames, charSettings.autoReminders.tasks.category, L:GetText("ChooseCategory"));
            self:ExpandHighlightArea("tasks", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.tasks.category = args.Item;
                self:SaveSettings();
            end

            local channel = L:GetText("TasksRemindersChannelNeeded");
            label, top = self:AddWarning(panel, top, warning:gsub("<channel>", channel));
            self:ExpandHighlightArea("tasks", label);
        end

        top = top + 10; -- skip some space

        CheckAutoReminderParams("crates");
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("CratesRemindersEnabled"), charSettings.autoReminders.crates.enabled);
        self:CreateHighlightArea("crates", checkbox);
        checkbox.CheckedChanged = function(ctl)
            charSettings.autoReminders.crates.enabled = ctl:IsChecked();
            self:SaveSettings();
            self:UpdateDisplay();
        end

        if (charSettings.autoReminders.crates.enabled) then
            dropDown, label, top = self:AddDropDown(panel, top, 200, tabNames, charSettings.autoReminders.crates.tab, L:GetText("ChooseTab"));
            self:ExpandHighlightArea("crates", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.crates.tab = args.Item;
                self:SaveSettings();
            end

            dropDown, label, top = self:AddDropDown(panel, top, 200, catNames, charSettings.autoReminders.crates.category, L:GetText("ChooseCategory"));
            self:ExpandHighlightArea("crates", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.crates.category = args.Item;
                self:SaveSettings();
            end

            local channel = L:GetText("CratesRemindersChannelNeeded");
            label, top = self:AddWarning(panel, top, warning:gsub("<channel>", channel));
            self:ExpandHighlightArea("crates", label);
        end

        top = top + 10; -- skip some space

        CheckAutoReminderParams("quests");
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("QuestsRemindersEnabled"), charSettings.autoReminders.quests.enabled);
        self:CreateHighlightArea("quests", checkbox);
        checkbox.CheckedChanged = function(ctl)
            charSettings.autoReminders.quests.enabled = ctl:IsChecked();
            self:SaveSettings();
            self:UpdateDisplay();
        end

        if (charSettings.autoReminders.quests.enabled) then
            dropDown, label, top = self:AddDropDown(panel, top, 200, tabNames, charSettings.autoReminders.quests.tab, L:GetText("ChooseTab"));
            self:ExpandHighlightArea("quests", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.quests.tab = args.Item;
                self:SaveSettings();
            end

            dropDown, label, top = self:AddDropDown(panel, top, 200, catNames, charSettings.autoReminders.quests.category, L:GetText("ChooseCategory"));
            self:ExpandHighlightArea("quests", dropDown);
            dropDown.SelectionChanged = function(ctl, args)
                charSettings.autoReminders.quests.category = args.Item;
                self:SaveSettings();
            end

            local channel = L:GetText("QuestsRemindersChannelNeeded");
            label, top = self:AddWarning(panel, top, warning:gsub("<channel>", channel));
            self:ExpandHighlightArea("quests", label);
        end
    end

    if (charSettings.autoReminders.raidlocks.enabled) then
        
        local function AddLock(top, name, enabled)
        
            local xButton = self:AddXButton(panel, top);
            xButton.name = name;
            Thurallor.UI.Tooltip(L:GetText("ForgetLock")):Attach(xButton);
            
            local checkBox = Turbine.UI.Lotro.CheckBox();
            checkBox:SetParent(panel);
            checkBox:SetChecked(enabled);
            checkBox:SetChecked(enabled);
            checkBox:SetTop(top);
            checkBox:SetSize(16, 16);
            checkBox.name = name;
            
            local label = Turbine.UI.Label();
            label:SetParent(panel);
            label:SetMarkupEnabled(true);
            label:SetFont(Turbine.UI.Lotro.Font.Verdana14);
            label:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
            label.text = name;
            label:SetText(name);
            label:SetTop(top);
            label:SetHeight(16);
            label.name = name;
            label.MouseEnter = function(ctl)
                ctl:SetText("<u>" .. ctl.text .. "</u>");
            end
            label.MouseLeave = function(ctl)
                ctl:SetText(ctl.text);
            end
            Thurallor.UI.Tooltip(L:GetText("EditLock")):Attach(label);
            
            AddCallback(panel, "SizeChanged", function()
                local width = panel:GetWidth();
                local center = math.floor(0.5 + width / 2);
                local left = math.max(0, center - 200);
                xButton:SetLeft(left);
                checkBox:SetLeft(left + 20);
                label:SetLeft(left + 40);
                label:SetWidth(width); -- goes off the edge; don't care
            end);
            return xButton, checkBox, label, top + 18;
        end

        -- Create a sorted list of raid/chest locks
        local lockInfo = charSettings.autoReminders.raidlocks.lockInfo;
        local locks = {};
        for name, info in pairs(lockInfo) do
            table.insert(locks, name);
        end
        table.sort(locks);

        if (#locks > 0) then

            sectionName = "KnownLocks";
            heading, top = self:AddHeading(panel, top, sectionName, false);
            if (self.expanded[sectionName]) then

                for _, name in ipairs(locks) do
                    xButton, checkBox, label, top = AddLock(top, name, lockInfo[name].enabled);
                    xButton.MouseClick = function(ctl, args)
                        if (args.Button == Turbine.UI.MouseButton.Left) then
                            if (self.window.settings.askBeforeDeleting) then
                                if (not self.confirmDialog) then
                                    self.confirmDialog = ConfirmDialog(self.window, L:GetText("/OptionsPanel/CharacterTab/ForgetLock"));
                                    self.confirmDialog.Yes = function(_, arg)
                                        lockInfo[ctl.name] = nil;
                                        self:SaveSettings();
                                        self:UpdateDisplay();
                                        self.confirmDialog = nil;
                                    end
                                    self.confirmDialog.No = function(_, arg)
                                        self.window.settings.askBeforeDeleting = arg;
                                        self.confirmDialog = nil;
                                    end
                                end
                            else
                                lockInfo[ctl.name] = nil;
                                self:SaveSettings();
                                self:UpdateDisplay();
                            end
                        end
                    end

                    checkBox.CheckedChanged = function(ctl)
                        lockInfo[ctl.name].enabled = ctl:IsChecked();
                        self:SaveSettings();
                        self:UpdateDisplay();
                    end
                    label.name = name;
                    label.MouseClick = function(ctl, args)
                        if (args.Button == Turbine.UI.MouseButton.Left) then
                            local dialog = RaidLockDialog.GetInstance(self.window, ctl.name, lockInfo[ctl.name], charSettings, self.selectedCharID);
                            dialog:SetVisible(false);
                            self.zoomer = Thurallor.UI.Zoomer(ctl, dialog);
                            AddCallback(self.zoomer, "ZoomComplete", function()
                                dialog:SetVisible(true);
                                dialog:Activate();
                            end);
                        end
                    end
                end
                top = top + 5;
            end
        end
    end
    
    if (charSettings.autoReminders.quests.enabled) then
        
        local function AddQuest(top, name, enabled)
        
            local xButton = self:AddXButton(panel, top);
            xButton.name = name;
            Thurallor.UI.Tooltip(L:GetText("ForgetQuest")):Attach(xButton);
            
            local checkBox = Turbine.UI.Lotro.CheckBox();
            checkBox:SetParent(panel);
            checkBox:SetChecked(enabled);
            checkBox:SetChecked(enabled);
            checkBox:SetTop(top);
            checkBox:SetSize(16, 16);
            checkBox.name = name;
            
            local label = Turbine.UI.Label();
            label:SetParent(panel);
            label:SetMarkupEnabled(true);
            label:SetFont(Turbine.UI.Lotro.Font.Verdana14);
            label:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
            label.text = name;
            label:SetText(name);
            label:SetTop(top);
            label:SetHeight(16);
            label.name = name;
            label.MouseEnter = function(ctl)
                ctl:SetText("<u>" .. ctl.text .. "</u>");
            end
            label.MouseLeave = function(ctl)
                ctl:SetText(ctl.text);
            end
            Thurallor.UI.Tooltip(L:GetText("EditQuest")):Attach(label);
            
            AddCallback(panel, "SizeChanged", function()
                local width = panel:GetWidth();
                local center = math.floor(0.5 + width / 2);
                local left = math.max(0, center - 200);
                xButton:SetLeft(left);
                checkBox:SetLeft(left + 20);
                label:SetLeft(left + 40);
                label:SetWidth(width); -- goes off the edge; don't care
            end);
            return xButton, checkBox, label, top + 18;
        end

        -- Create a sorted list of repeated quests
        local questInfo = charSettings.autoReminders.quests.questInfo;
        local quests = {};
        for name, info in pairs(questInfo) do
            if (info.repeated) then
                table.insert(quests, name);
            end
        end
        table.sort(quests);

        if (#quests > 0) then

            sectionName = "KnownRepeatableQuests";
            heading, top = self:AddHeading(panel, top, sectionName, false);
            if (self.expanded[sectionName]) then

                for _, name in ipairs(quests) do
                    xButton, checkBox, label, top = AddQuest(top, name, questInfo[name].enabled);
                    xButton.MouseClick = function(ctl, args)
                        if (args.Button == Turbine.UI.MouseButton.Left) then
                            if (self.window.settings.askBeforeDeleting) then
                                if (not self.confirmDialog) then
                                    self.confirmDialog = ConfirmDialog(self.window, L:GetText("/OptionsPanel/CharacterTab/ForgetQuest"));
                                    self.confirmDialog.Yes = function(_, arg)
                                        questInfo[ctl.name] = nil;
                                        self:SaveSettings();
                                        self:UpdateDisplay();
                                        self.confirmDialog = nil;
                                    end
                                    self.confirmDialog.No = function(_, arg)
                                        self.window.settings.askBeforeDeleting = arg;
                                        self.confirmDialog = nil;
                                    end
                                end
                            else
                                questInfo[ctl.name] = nil;
                                self:SaveSettings();
                                self:UpdateDisplay();
                            end
                        end
                    end

                    checkBox.CheckedChanged = function(ctl)
                        questInfo[ctl.name].enabled = ctl:IsChecked();
                        self:SaveSettings();
                        self:UpdateDisplay();
                    end
                    label.name = name;
                    label.MouseClick = function(ctl, args)
                        if (args.Button == Turbine.UI.MouseButton.Left) then
                            local dialog = QuestRepeatedDialog.GetInstance(self.window, ctl.name, questInfo[ctl.name], charSettings, self.selectedCharID);
                            dialog:SetVisible(false);
                            self.zoomer = Thurallor.UI.Zoomer(ctl, dialog);
                            AddCallback(self.zoomer, "ZoomComplete", function()
                                dialog:SetVisible(true);
                                dialog:Activate();
                            end);
                        end
                    end
                end
                top = top + 5;
            end
        end
    end

    panel:SetHeight(top + 16);
    L:SetContext(prevContext);
    DoCallbacks(panel, "SizeChanged");
end

function OptionsPanel:DetectTimeZone(...)
    -- Get server time fields

    local serverMonth, serverDay, serverHour, serverMinute, serverAmPm = ...;
    if ((Turbine.Engine.GetLanguage() == Turbine.Language.German) or (Turbine.Engine.GetLanguage() == Turbine.Language.French)) then
        -- order is swapped
        serverDay, serverMonth = tonumber(serverMonth), tonumber(serverDay);
    else -- English
        serverMonth, serverDay = tonumber(serverMonth), tonumber(serverDay);
    end
    serverHour, serverMinute = tonumber(serverHour) % 12, tonumber(serverMinute);
    if (serverAmPm == "PM") then
        serverHour = serverHour + 12;
    end
    local serverMinuteOfDay = serverHour * 60 + serverMinute;

    -- Get local time fields
    local dateInfo = Turbine.Engine.GetDate();
    local localDay, localHour, localMinute = dateInfo.Day, dateInfo.Hour, dateInfo.Minute;
    local localMinuteOfDay = localHour * 60 + localMinute;

    -- Detect day offset between server and local time
    local offsetDays = localDay - serverDay;
    if (offsetDays > 1) then
        -- Server time is next month
        offsetDays = -1;
    elseif (offsetDays < -1) then
        -- Server time is last month
        offsetDays = 1;
    end

    -- Chat message is not exactly synched with GetDate() function.
    -- To cope with this, round the offset to the nearest 15-minute interval.
    -- All time zone offsets in the world are multiples of 15 minutes.
    local offsetMinutes = (24 * 60 * offsetDays) + localMinuteOfDay - serverMinuteOfDay;
    offsetMinutes = 15 * math.floor(0.5 + offsetMinutes / 15);

    return offsetMinutes / 60;
end

function OptionsPanel:UpdateGlobalDisplay()
    local panel = self.globalPanel;
    local top = 0;
    local heading, checkbox, label, swatch, button, dropDown, buttons;
    local catNames, catExists = self.window:GetUserCategories();
    local sectionName;
    local prevContext = L:SetContext("/OptionsPanel/GlobalTab");

    -- Remove child controls and SizeChanged callbacks; we'll recreate them now
    panel:GetControls():Clear();
    panel.SizeChanged = nil;

    sectionName = "General";
    heading, top = self:AddHeading(panel, top, sectionName, true);
    if (self.expanded[sectionName]) then

        self.languages = {
            L:GetText("English");
            L:GetText("French");
            L:GetText("German");
            L:GetText("Russian");
        };
        local currentLanguage = L:GetText(Search(Turbine.Language, self.settings.language));
        dropDown, label, top = self:AddDropDown(panel, top, 200, self.languages, currentLanguage, L:GetText("Language"));
        dropDown.SelectionChanged = function(_, args)
            local langs = {
                [L:GetText("/OptionsPanel/GlobalTab/English")] = Turbine.Language.English;
                [L:GetText("/OptionsPanel/GlobalTab/French")] = Turbine.Language.French;
                [L:GetText("/OptionsPanel/GlobalTab/German")] = Turbine.Language.German;
                [L:GetText("/OptionsPanel/GlobalTab/Russian")] = Turbine.Language.Russian;
            };
            self.window:ChangeLanguage(self.settings.language, langs[args.Item]);
            self:SaveSettings();
            self:UpdateDisplay();
        end

        top = top + 10; -- skip some space
        label, top = self:AddLabel(panel, top, L:GetText("WhatServer"));

        local serverName = Turbine.Engine:GetServerName() or L:GetText("ServerUnknown");
        local serverNames = {};
        for name, _ in pairs(Turbine.ServerName) do
            table.insert(serverNames, name);
        end
        table.sort(serverNames);
        panel.serverNameDropDown, label, top = self:AddDropDown(panel, top, 110, serverNames, serverName, L:GetText("ServerName"));
        panel.serverNameDropDown.SelectionChanged = function(_, args)
            Turbine.Engine:SetServerName(args.Item);
            self.window:RebuildTabs();
        end
        button = Thurallor.UI.TrickButton(L:GetClientLanguageText("/Chat/ServerNameCommand"), Turbine.ChatType.Standard, L:GetClientLanguageText("/Chat/ServerNameDetect"));
        button:SetParent(panel);
        button:SetFont(Turbine.UI.Lotro.Font.Verdana14);
        button:SetText(L:GetText("DetectServerName"));
        button:SetWidth(L:GetText("DetectServerNameWidth"));
        button.Click = function(ctl, ...)
            dropDown = ctl:GetParent().serverNameDropDown;
            dropDown:SetSelectedItem(...);
        end
                panel.serverNameButton = button;
                AddCallback(panel, "SizeChanged", function(ctl)
            local button, dropDown = ctl.serverNameButton, ctl.serverNameDropDown;
            button:SetPosition(dropDown:GetLeft() + dropDown:GetWidth() + 4, dropDown:GetTop());
                end);

        top = top + 10; -- skip some space
        label, top = self:AddLabel(panel, top, L:GetText("WhatIsLocalTimeOffset"));

        local value = self.settings.localTimeOffset;
        value = (((value > 0) and "+") or "") .. tostring(value);
        textBox, leftLabel, rightLabel, top = self:AddTextBox(panel, top, 60, L:GetText("OffsetHours"), value, nil);
        button = Thurallor.UI.TrickButton(L:GetClientLanguageText("/Chat/ServerTimeCommand"), Turbine.ChatType.Standard, L:GetClientLanguageText("/Chat/ServerTimeDetect"));
        button:SetParent(rightLabel);
        button:SetFont(Turbine.UI.Lotro.Font.Verdana14);
        button:SetText(L:GetText("DetectServerTime"));
        button:SetTop(1);
        button:SetWidth(L:GetText("DetectServerTimeWidth"));
        button.textBox = textBox;
        button.Click = function(ctl, ...)
           ctl.textBox:SetText(self:DetectTimeZone(...));
           ctl.textBox:FocusLost();
        end
        textBox.FocusLost = function(ctl)
            local offsetHours = tonumber(ctl:GetText()) or 0;
            local sign = ((offsetHours > 0) and "+") or "";
            ctl:SetText(sign .. tostring(offsetHours));
            self.window:SetLocalTimeOffset(offsetHours);
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("OpenWindowAtLogin"), self.settings.openWindowAtLogin);
        checkbox.CheckedChanged = function(ctl)
            self.settings.openWindowAtLogin = ctl:IsChecked();
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        label, top = self:AddLabel(panel, top, L:GetText("WhenCreatingNew"));
        
        local defaultCat = self.settings.defaultCategory;
        if ((not defaultCat) or (not catExists[defaultCat])) then
            self.settings.defaultCategory = catNames[1];
        end
        dropDown, label, top = self:AddDropDown(panel, top, 200, catNames, self.settings.defaultCategory, L:GetText("DefaultCategory"));
        dropDown.SelectionChanged = function(_, args)
            self.settings.defaultCategory = args.Item;
        end

        if (not self.settings.defaultDelay) then
            self.settings.defaultDelay = 0;
            self.settings.defaultDelayMethod = "atNextLogin";
        end
        local text;
        if (self.settings.defaultDelayMethod == "now") then
            text = L:GetText("/Time/TimeDisplay/Now");
        elseif (self.settings.defaultDelayMethod == "dailyResetTime") then
            text = L:GetText("/Time/DailyResetTime");
        elseif (self.settings.defaultDelayMethod == "atNextLogin") then
            text = L:GetText("/Time/TimeDisplay/NextLogin");
        elseif (self.settings.defaultDelayMethod == "never") then
            text = "―"; -- "Never"
        else -- (self.settings.defaultDelayMethod == "delayFromNow")
            text = Time.GetDelayStr(self.settings.defaultDelay, true);
        end
        textBox, label, label, top = self:AddTextBox(panel, top, 200, L:GetText("DefaultTime"), text, nil);
        textBox:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
        textBox.FocusGained = function()
            self:OpenDefaultTimeDialog();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("ReuseExpiredReminders"), self.settings.reuseExpiredReminders);
        checkbox.CheckedChanged = function(ctl)
            self.settings.reuseExpiredReminders = ctl:IsChecked();
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("PauseDuringCombat"), self.settings.pauseDuringCombat);
        checkbox.CheckedChanged = function(ctl)
            self.settings.pauseDuringCombat = ctl:IsChecked();
            self.window:InCombatChanged();
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("UseNowForPastTimes"), not self.settings.showNegativeTimes);
        checkbox.CheckedChanged = function(ctl)
            self.settings.showNegativeTimes = not ctl:IsChecked();
            if (self.settings.showNegativeTimes) then
                self.window:TickAllEvents();
            end
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("AskBeforeDeleting"), self.settings.askBeforeDeleting);
        checkbox.CheckedChanged = function(ctl)
            self.settings.askBeforeDeleting = ctl:IsChecked();
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("HideEmptyTabsWhenFiltering"), self.settings.hideEmptyTabsWhenFiltering);
        checkbox.CheckedChanged = function(ctl)
            self.settings.hideEmptyTabsWhenFiltering = ctl:IsChecked();
            self:SaveSettings();
        end
    end
    
    sectionName = "Appearance";
    heading, top = self:AddHeading(panel, top, sectionName, true);
    if (self.expanded[sectionName]) then

        checkbox, top = self:AddCheckBox(panel, top, L:GetText("HideWindowControls"), self.settings.mouseLeaveHideControls);
        checkbox.CheckedChanged = function(ctl)
            self.settings.mouseLeaveHideControls = ctl:IsChecked();
            self.window.mouseWasOutside = nil;
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        label, top = self:AddLabel(panel, top, L:GetText("MouseLeaveWindowOpacity"));
        
        self.settings.mouseLeaveWindowOpacity = self.settings.mouseLeaveWindowOpacity or 1;
        _, _, scrollBar, top = self:AddSlider(panel, top, L:GetText("Transparent"), L:GetText("Opaque"), 0, 100, self.settings.mouseLeaveWindowOpacity * 100);
        scrollBar.ValueChanged = function(ctl)
            self.settings.mouseLeaveWindowOpacity = ctl:GetValue() / 100;
            self.window.mouseWasOutside = nil;
            self:SaveSettings();
        end
        
        top = top + 10; -- skip some space
        label, top = self:AddLabel(panel, top, L:GetText("IconSize"));
        
        self.settings.icon.size = self.settings.icon.size or { 64, 64 };
        _, _, scrollBar, top = self:AddSlider(panel, top, L:GetText("Small"), L:GetText("Large"), 40, 127, self.settings.icon.size[1]);
        scrollBar.ValueChanged = function(ctl)
            self.window:SetIconSize(ctl:GetValue());
            self:SaveSettings();
        end
        
        top = top + 10; -- skip some space
        label, top = self:AddLabel(panel, top, L:GetText("MouseLeaveIconOpacity"));
        
        self.settings.icon.mouseLeaveOpacity = self.settings.icon.mouseLeaveOpacity or 0.5;
        _, _, scrollBar, top = self:AddSlider(panel, top, L:GetText("Transparent"), L:GetText("Opaque"), 0, 100, self.settings.icon.mouseLeaveOpacity * 100);
        scrollBar.ValueChanged = function(ctl)
            self.settings.icon.mouseLeaveOpacity = ctl:GetValue() / 100;
            self.window.icon:SetMouseLeaveOpacity(self.settings.icon.mouseLeaveOpacity);
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("IconAlwaysInFront"), (self.settings.icon.zOrder > 0));
        checkbox.CheckedChanged = function(ctl)
            if (ctl:IsChecked()) then
                self.settings.icon.zOrder = 1;
            else
                self.settings.icon.zOrder = 0;
            end
            self.window.icon:SetZOrder(self.settings.icon.zOrder);
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("ShowCascade"), self.settings.showCascade);
        checkbox.MouseEnter = function()
            local showCascade = self.settings.showCascade;
            self.settings.showCascade = true;
            self.window:CascadeNotify(L:GetText("/Cascade/Example"));
            self.settings.showCascade = showCascade;
        end
        checkbox.CheckedChanged = function(ctl)
            self.settings.showCascade = ctl:IsChecked();
            self:SaveSettings();
        end

        top = top + 10; -- skip some space
        label, buttons, top = self:AddRadioButtons(panel, top, L:GetText("MarqueeStyle"),
            { L:GetText("Ants"), (self.settings.marqueeAntsEnabled) },
            { L:GetText("SolidLine"), (not self.settings.marqueeAntsEnabled) }
        );
        local antsButton, solidLineButton = unpack(buttons);
        antsButton.MouseEnter = function(ctl)
            if (not ctl.marquee) then
                ctl.marquee = Thurallor.UI.Marquee();
                ctl.marquee:SetColor(Turbine.UI.Color(0.75, 0.75, 0.75));
                ctl.marquee:SetSize(ctl:GetSize());
                ctl.marquee:SetParent(ctl);
            end
        end
        antsButton.MouseLeave = function(ctl)
            if (ctl.marquee) then
                ctl.marquee:SetParent(nil);
                ctl.marquee = nil;
            end
        end
        antsButton.Clicked = function()
            self.window:SetMarqueeAntsEnabled(true);
        end
        solidLineButton.MouseEnter = function(ctl)
            antsButton.MouseEnter(ctl);
            ctl.marquee:SetAntsEnabled(false);
        end
        solidLineButton.MouseLeave = antsButton.MouseLeave;
        solidLineButton.Clicked = function()
            self.window:SetMarqueeAntsEnabled(false);
        end

        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("ZoomEffects"), self.settings.zoomEffects);
        checkbox.CheckedChanged = function(ctl)
            self.window:SetZoomEffectsEnabled(ctl:IsChecked());
            self:SaveSettings();
        end
    end

    if (L:GetLanguage() ~= Turbine.Language.English) then
        top = top + 10; -- skip some space
        checkbox, top = self:AddCheckBox(panel, top, L:GetText("IndicateUntranslated"), self.settings.showTranslationMarkers);
        checkbox.CheckedChanged = function(ctl)
            local showMarkers = ctl:IsChecked();
            self.settings.showTranslationMarkers = showMarkers;
            L:SetShowMarkers(showMarkers);
            L:SetLanguage(L:GetLanguage()); -- update currently-displayed windows
            self:SaveSettings();
        end
    end
    
    sectionName = "Categories";
    heading, top = self:AddHeading(panel, top, sectionName, true);
    if (self.expanded[sectionName]) then
    
        local function AddCategory(top, text, color)
            color = Turbine.UI.Color(unpack(color));
        
            local button = self:AddXButton(panel, top + 4);
            Thurallor.UI.Tooltip(L:GetText("DeleteCategory")):Attach(button);
            
            local swatch = Turbine.UI.Control();
            swatch:SetParent(panel);
            swatch:SetSize(16, 16);
            swatch:SetTop(top + 4);
            swatch:SetBackColor(Turbine.UI.Color.Gray);
            swatch.MouseEnter = function(ctl) ctl:SetBackColor(Turbine.UI.Color.White) end;
            swatch.MouseLeave = function(ctl) ctl:SetBackColor(Turbine.UI.Color.Gray) end;
            Thurallor.UI.Tooltip(L:GetText("ChangeCategoryColor")):Attach(swatch);
            
            local inside = Turbine.UI.Control();
            inside:SetParent(swatch);
            inside:SetMouseVisible(false);
            inside:SetBackColor(color);
            inside:SetPosition(1, 1);
            inside:SetSize(14, 14);
            swatch.inside = inside;
            
            local label = Turbine.UI.Lotro.TextBox();
            label:SetParent(panel);
            label:SetSelectable(true);
            label:SetMultiline(false);
            label:SetFont(Turbine.UI.Lotro.Font.Verdana14);
            label:SetForeColor(color);
            label:SetBackColor(Turbine.UI.Color(1, 0, 0.19, 0.33));
            label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
            label:SetText(text);
            label:SetPosition(0, top);
            label:SetSize(200, 24);
            label.MouseEnter = function(ctl) ctl:SetBackColor(Turbine.UI.Color(1, 0, 0.57, 1)) end;
            label.MouseLeave = function(ctl) ctl:SetBackColor(Turbine.UI.Color(1, 0, 0.19, 0.33)) end;
            
            AddCallback(panel, "SizeChanged", function()
                local width = panel:GetWidth();
                local center = math.floor(0.5 + width / 2);
                swatch:SetLeft(math.max(0, center - 150));
                label:SetLeft(swatch:GetLeft() + 20);
                label:SetWidth(math.min(264, width - label:GetLeft() - 20));
                button:SetLeft(label:GetLeft() + label:GetWidth() + 4);
            end);
            return label, swatch, button, top + 23;
        end
        
        for _, name in ipairs(catNames) do
            local n = catExists[name];
            local color = self.settings.categories[n].color;
            label, swatch, button, top = AddCategory(top, name, color);
            swatch.n, swatch.label = n, label;
            swatch.MouseClick = function(ctl, args)
                if (args.Button == Turbine.UI.MouseButton.Left) then
                    self:PickCategoryColor(ctl.n, ctl.inside, ctl.label);
                end
            end
            label.n = n;
            label.FocusLost = function(ctl)
                self:CategoryRenamed(ctl.n, ctl:GetText());
            end
            button.n, button.label = n, label;
            button.MouseClick = function(ctl, args)
                if (args.Button == Turbine.UI.MouseButton.Left) then
                    if (self.window.settings.askBeforeDeleting) then
                        if (not self.confirmDialog) then
                            self.confirmDialog = ConfirmDialog(self.window, L:GetText("/OptionsPanel/GlobalTab/DeleteCategory"));
                            self.confirmDialog.Yes = function(_, arg)
                                ctl.label.FocusLost = nil;
                                self:CategoryDeleted(ctl.n);
                                self.confirmDialog = nil;
                            end
                            self.confirmDialog.No = function(_, arg)
                                self.window.settings.askBeforeDeleting = arg;
                                self.confirmDialog = nil;
                            end
                        end
                    else
                        ctl.label.FocusLost = nil;
                        self:CategoryDeleted(ctl.n);
                    end
                end
            end
            -- Don't let the user delete the last category
            if (#catNames == 1) then
                button:SetParent(nil);
            end
        end
        top = top + 5;
        
        button, top = self:AddButton(panel, top, L:GetText("CreateNewCategoryWidth"), L:GetText("CreateNewCategory"));
        button.Click = function()
            self:NewCategory();
        end
    end
    
    panel:SetHeight(top);
    L:SetContext(prevContext);
    DoCallbacks(panel, "SizeChanged");
end

function OptionsPanel:AddTextBox(panel, top, width, leftDesc, value, rightDesc)
    local leftLabel = Turbine.UI.Label();
    leftLabel:SetParent(panel);
    leftLabel:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    leftLabel:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    leftLabel:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleRight);
    leftLabel:SetPosition(0, top);
    leftLabel:SetHeight(22);
    leftLabel:SetMultiline(false);
    leftLabel:SetText(leftDesc);
    local textBox = Turbine.UI.Lotro.TextBox();
    textBox:SetParent(panel);
    textBox:SetSelectable(true);
    textBox:SetMultiline(false);
    textBox:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    textBox:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    textBox:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
    textBox:SetText(value);
    textBox:SetPosition(0, top + 1);
    textBox:SetSize(width, 21);
    local rightLabel = Turbine.UI.Label();
    rightLabel:SetParent(panel);
    rightLabel:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    rightLabel:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    rightLabel:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
    rightLabel:SetTop(top);
    rightLabel:SetHeight(22);
    rightLabel:SetMultiline(false);
    rightLabel:SetText(rightDesc);
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        leftLabel:SetWidth(center - 2);
        textBox:SetLeft(center + 2);
        rightLabel:SetLeft(center + 2 + textBox:GetWidth() + 4);
        rightLabel:SetWidth(center); -- goes off the edge; don't care
    end);
    return textBox, leftLabel, rightLabel, top + 24;
end

function OptionsPanel:AddDropDown(panel, top, width, items, selectedItem, desc)
    local label = Turbine.UI.Label();
    label:SetParent(panel);
    label:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    label:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleRight);
    label:SetPosition(0, top);
    label:SetHeight(22);
    label:SetMultiline(false);
    label:SetText(desc);
    local dropDown = Thurallor.UI.DropDown(items, selectedItem);
    dropDown:SetParent(panel);
    dropDown:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    dropDown:SetTop(top);
    dropDown:SetSize(width, 22);
    dropDown:SetExpandedWidth(width);
    dropDown:SetAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        label:SetWidth(center - 2);
        dropDown:SetLeft(center + 2);
        dropDown:SetWidth(math.min(center - 6, width));
    end);
    return dropDown, label, top + 24;
end

function OptionsPanel:AddHeading(panel, top, name, expanded)

    -- Add expand/collapse button
    local button = Turbine.UI.Control();
    button:SetSize(16, 16);
    button:SetTop(top + 21);
    button:SetParent(panel);
    button:SetBlendMode(Turbine.UI.BlendMode.Overlay);
    function button.UpdateBackground(b)
        if (self.expanded[b.name]) then
            b:SetBackground(0x41007E26) -- collapse icon
        else
            b:SetBackground(0x41007E27) -- expand icon
        end
    end
    function button.MouseClick(b, args)
        self.expanded[b.name] = not self.expanded[b.name];
        b:UpdateBackground();
        self:UpdateDisplay();
    end
    
    button.name = name;
    if (self.expanded[name] == nil) then
        self.expanded[name] = expanded;
    end
    button:UpdateBackground();

    -- Thanks Pengoros for this pretty heading style :-)
    local label = Turbine.UI.Label();
    label:SetParent(panel);
    label:SetMouseVisible(false);
    label:SetSize(400, 30);
    label:SetFont(Turbine.UI.Lotro.Font.TrajanPro18);
    label:SetForeColor(Turbine.UI.Color(244/255, 255/255, 51/255));
    label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
    label:SetFontStyle(Turbine.UI.FontStyle.Outline);
    label:SetOutlineColor(Turbine.UI.Color.Black);
    label:SetText(L:GetText(name));
    label:SetBlendMode(Turbine.UI.BlendMode.Overlay);
    label:SetBackground(imagePath .. "options_panel_divider.tga");
    label:SetTop(top + 15);
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        button:SetLeft(center + 204);
        label:SetLeft(center - 200);
    end);
    return label, top + 56;
end

function OptionsPanel:AddCheckBox(panel, top, text, checked, alignRight)
    local checkbox = Turbine.UI.Lotro.CheckBox();
    checkbox:SetParent(panel);
    checkbox:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    checkbox:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    checkbox:SetMultiline(true);
    checkbox:SetChecked(checked);
    checkbox:SetHeight(28); -- two lines in case there is wrapping
    if (alignRight) then
        checkbox:SetPosition(0, top + 4);
        checkbox:SetText(text .. " ");
        checkbox:SetCheckAlignment(Turbine.UI.ContentAlignment.TopRight);
        checkbox:SetTextAlignment(Turbine.UI.ContentAlignment.TopRight);
        AddCallback(panel, "SizeChanged", function()
            local width = panel:GetWidth();
            local center = math.floor(0.5 + width / 2);
            checkbox:SetWidth(math.min(width, center + 202));
        end);
    else
        checkbox:SetPosition(0, top + 3);
        checkbox:SetText(" " .. text);
        checkbox:SetCheckAlignment(Turbine.UI.ContentAlignment.TopLeft);
        checkbox:SetTextAlignment(Turbine.UI.ContentAlignment.TopLeft);
        AddCallback(panel, "SizeChanged", function()
            local center = math.floor(0.5 + panel:GetWidth() / 2);
            checkbox:SetLeft(math.max(0, center - 200));
            checkbox:SetWidth(center + 200);
        end);
    end
    return checkbox, top + 24;
end

function OptionsPanel:AddXButton(panel, top)
    local button = Turbine.UI.Control();
    button:SetParent(panel);
    button:SetSize(16, 16);
    button:SetTop(top);
    button:SetBackground(0x41000196);
    button:SetBlendMode(Turbine.UI.BlendMode.Overlay);
    button.MouseEnter = function(ctl) ctl:SetBackground(0x41000198) end;
    button.MouseLeave = function(ctl) ctl:SetBackground(0x41000196) end;
    button.MouseDown = function(ctl) ctl:SetBackground(0x41000197) end;
    button.MouseUp = function(ctl) ctl:SetBackground(0x41000198) end;
    return button, top + 16;
end

function OptionsPanel:AddLabel(panel, top, text)
    local label = Turbine.UI.Label();
    label:SetParent(panel);
    label:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    label:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    label:SetTextAlignment(Turbine.UI.ContentAlignment.TopLeft);
    label:SetMultiline(true);
    label:SetText(text);
    label:SetTop(top);
    label:SetHeight(48);
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        label:SetLeft(math.max(0, center - 200));
        label:SetWidth(center + 200);
    end);
    return label, top + 24;
end

function OptionsPanel:AddWarning(panel, top, text)
    local label = Turbine.UI.Label();
    label:SetParent(panel);
    label:SetFont(Turbine.UI.Lotro.Font.Verdana12);
    label:SetForeColor(Turbine.UI.Color.DarkGoldenrod);
    label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleRight);
    label:SetMultiline(true);
    label:SetText(text);
    label:SetTop(top);
    label:SetHeight(36);
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        label:SetLeft(math.max(0, center - 184));
        label:SetWidth(384);
    end);
    return label, top + 36;
end

function OptionsPanel:AddRadioButtons(panel, top, desc, ...)
    local label = Turbine.UI.Label();
    label:SetParent(panel);
    label:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    label:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
    label:SetMultiline(false);
    label:SetText(desc .. ":  ");
    label:SetTop(top);
    label:AutoSize();
    label:SetHeight(24);
    local buttons = {};
    for _, info in ipairs({...}) do
        local text, checked = unpack(info);
        local button = Thurallor.UI.RadioButton(panel, text .. "  ", checked);
        button:SetFont(Turbine.UI.Lotro.Font.Verdana14);
        button:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
        button:SetMultiline(false);
        button:SetTop(top);
        button:SetHeight(24);
        button:AutoSize();
        table.insert(buttons, button);
    end
    Thurallor.UI.RadioButton.LinkPeers(buttons);
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        local left = math.max(0, center - 200);
        label:SetLeft(left);
        left = left + label:GetWidth();
        for _, button in ipairs(buttons) do
            button:SetLeft(left);
            left = left + button:GetWidth();
        end
    end);
    return label, buttons, top + 24;
end

function OptionsPanel:AddSlider(panel, top, leftText, rightText, minValue, maxValue, value)
    local leftLabel = Turbine.UI.Label();
    leftLabel:SetParent(panel);
    leftLabel:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    leftLabel:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    leftLabel:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleRight);
    leftLabel:SetMultiline(false);
    leftLabel:SetText(leftText);
    leftLabel:SetTop(top);
    leftLabel:SetHeight(24);
    
    local scrollBar = Turbine.UI.Lotro.ScrollBar();
    scrollBar:SetParent(panel);
    scrollBar:SetOrientation(Turbine.UI.Orientation.Horizontal);
    scrollBar:SetMinimum(minValue);
    scrollBar:SetMaximum(maxValue);
    scrollBar:SetValue(value);
    scrollBar:SetTop(top + 8);
    scrollBar:SetHeight(10);
    scrollBar:SetBackground(imagePath .. "scrollbar_background_horiz.tga");    
    scrollBar:SetBlendMode(Turbine.UI.BlendMode.Overlay);

    local rightLabel = Turbine.UI.Label();
    rightLabel:SetParent(panel);
    rightLabel:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    rightLabel:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
    rightLabel:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
    rightLabel:SetMultiline(false);
    rightLabel:SetText(rightText);
    rightLabel:SetTop(top);
    rightLabel:SetHeight(24);
    
    AddCallback(panel, "SizeChanged", function()
        local width = panel:GetWidth();
        scrollBar:SetWidth(math.min(width, 200));
        scrollBar:SetLeft(math.floor(0.5 + (width - scrollBar:GetWidth()) / 2));
        rightLabel:SetLeft(scrollBar:GetLeft() + scrollBar:GetWidth() + 4);
        rightLabel:SetWidth(width - rightLabel:GetLeft());
        leftLabel:SetWidth(rightLabel:GetWidth());
    end);
    return leftLabel, rightLabel, scrollBar, top + 24;
end

function OptionsPanel:AddButton(panel, top, width, text)
    local button = Turbine.UI.Lotro.Button();
    button:SetParent(panel);
    button:SetFont(Turbine.UI.Lotro.Font.Verdana14);
    button:SetText(text);
    button:SetTop(top);
    button:SetWidth(width);
    AddCallback(panel, "SizeChanged", function()
        local center = math.floor(0.5 + panel:GetWidth() / 2);
        button:SetLeft(center - math.floor(button:GetWidth() / 2));
    end);
    return button, top + button:GetHeight();
end

function OptionsPanel:NewCategory()
    local catName, catExists = self.window:GetUserCategories();
    local nameStem = L:GetText("/Defaults/CategoryName");
    local num = 1;
    local name = nameStem;
    while catExists[name] do
        num = num + 1;
        name = nameStem .. " (" .. num .. ")";
    end
    local new = { name = name; color = { 1, 0.75, 0.75, 0.75 } };
    table.insert(self.settings.categories, new);
    self:SaveSettings();
    self:UpdateDisplay();
end

function OptionsPanel:CategoryDeleted(n)
    local name = self.settings.categories[n].name;
    table.remove(self.settings.categories, n);
    self:SaveSettings();
    self:UpdateDisplay();

    -- Find all existing events with this category and change them to the default
    self.window:ForEachEventDo(function(event)
        local cat = event:GetCategory();
        if (cat == name) then
            event:SetCategory(self.settings.defaultCategory);
        end
    end);
end

function OptionsPanel:CategoryRenamed(n, newName)
    local oldName = self.settings.categories[n].name;
    self.window:RenameCategory(oldName, newName);
    self:UpdateDisplay();
end    

function OptionsPanel:CategoryColorChanged(n, color)
    local name = self.settings.categories[n].name;
    self.settings.categories[n].color = { 1, color.R; color.G; color.B };
    self:SaveSettings();
    self:UpdateDisplay();

    -- Apply new color to all existing events
    self.window:ForEachEventDo(function(event)
        local cat = event:GetCategory();
        if (cat == name) then
            event:SetCategory(name);
        end
    end);
end

function OptionsPanel:PickCategoryColor(n, swatch, label)
    if (self.colorPicker) then
        self.colorPicker:Close();
    end
    self.colorPicker = Thurallor.UI.ColorPicker(swatch:GetBackColor());
    self.colorPicker.Accepted = function(picker)
        local color = picker:GetColor();
        self:CategoryColorChanged(n, color);
        self.colorPicker = nil;
    end
    self.colorPicker.ColorChanged = function(picker)
        local color = picker:GetColor();
        swatch:SetBackColor(color);
        label:SetForeColor(color);
    end
    self.colorPicker.Canceled = self.colorPicker.ColorChanged;
end

function OptionsPanel:OpenDefaultTimeDialog()
    if (self.defaultTimeDialog) then
        return;
    end

    local event = {
        window = self.window;
        defaultDelay = self.settings.defaultDelay;
        defaultDelayMethod = self.settings.defaultDelayMethod;
        settings = self.settings;
        tab = { window = self.window };
        GetExpirationTime = function(e)
            if (e.defaultDelayMethod == "now") then
                return Turbine.Engine.GetGameTime();
            elseif (e.defaultDelayMethod == "dailyResetTime") then
                return Time():GetNextServerResetTime(true):GetGameTime();
            elseif (e.defaultDelayMethod == "atNextLogin") then
                return 0;
            elseif (e.defaultDelayMethod == "never") then
                return -1;
            else -- (e.defaultDelayMethod == "delayFromNow")
                return Turbine.Engine.GetGameTime() + self.settings.defaultDelay;
            end
        end
    };
    local dialog = ExpireTimeDialog(event, true);
    dialog:SetText(L:GetText("/OptionsPanel/GlobalTab/DefaultTime"));
    AddCallback(dialog, "Closing", function()
        self.defaultTimeDialog = nil;
    end);
    AddCallback(dialog, "Ok", function(dlg, results)
        self.settings.defaultDelayMethod = results.method;
        self.settings.defaultDelay = results.expTime - Turbine.Engine.GetGameTime();
        self:SaveSettings();
        self:UpdateDisplay();
    end);
    self.defaultTimeDialog = dialog;
end

function OptionsPanel:Unload()
    self:SetWantsUpdates(false);
    self:SetWantsKeyEvents(false);
    self.Closing = nil;
end

Compare with Previous | Blame


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


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