Event = class(Turbine.Object);
-- static function (doesn't require instance)
function Event.GetCoords(locationStr)
local y, yd = locationStr:match "(%d+%.?%d*)([NSns])";
local x, xd = locationStr:match "(%d+%.?%d*)([EWOewo])";
if (x and y) then
local coords = string.upper(y .. yd .. ", " .. x .. xd);
x, y = tonumber(x), tonumber(y);
if (yd:upper() == "S") then
y = -y;
end
if (xd:upper() == "W") then
x = -x;
end
return x, y, coords;
end
end
function Event:Constructor(tab, settings)
Turbine.Object.Constructor(self);
local player = Turbine.Gameplay.LocalPlayer.GetInstance();
self.tab = tab;
self.window = tab.window;
self.loggedInCharID = Turbine.Engine.GetServerID() .. "." .. player:GetName();
self.settings = settings;
if (not settings.postpone) then
settings.postpone = {
method = self.window.settings.postpone.method;
delay = self.window.settings.postpone.delay;
};
end
self:SetWantsTicks(true);
end
function Event:GetCells()
return self.cells;
end
function Event:GetToolTip()
-- Temporarily enable "show negative times", because we always want to do so in the tooltip.
local showNegativeTimes = self.window.settings.showNegativeTimes;
self.window.settings.showNegativeTimes = true;
self:UpdateTimeRemaining();
-- Use the TableControl to construct the tooltip
local rowContainer = self.tab.table:GetRow(self);
local tooltip = self.tab.table:_GetToolTip(rowContainer);
-- Restore the user's "show negative times" setting.
self.window.settings.showNegativeTimes = showNegativeTimes;
self:UpdateTimeRemaining();
-- Apply the category color to all text in the tooltip
local color = self:GetColor();
local lines = tooltip:GetControls();
for l = 1, lines:GetCount() do
local line = lines:Get(l);
local cells = line:GetControls();
for c = 1, cells:GetCount() do
local cell = cells:Get(c);
cell:SetForeColor(color);
cell:SetFontStyle(Turbine.UI.FontStyle.Outline);
cell:SetOutlineColor(Turbine.UI.Color.Black);
cell:SetMouseVisible(false);
end
line:SetMouseVisible(false);
end
-- Make a nice border / background
local container = Turbine.UI.Control();
local border = Turbine.UI.Lotro.TextBox();
container:SetBackColor(self.tab:GetCellColor());
border:SetMouseVisible(false);
border:SetEnabled(false);
local w, h = tooltip:GetSize();
tooltip:SetBackColor(nil);
container:SetSize(w, h + 6);
border:SetSize(w, h + 6);
border:SetParent(container);
tooltip:SetParent(container);
tooltip:SetPosition(0, 3);
tooltip.MouseClick = function(_, args)
container:MouseClick(args);
end
return container;
end
function Event:SelectionGained()
if (self.popup) then
self.popup:SelectionGained();
end
end
function Event:SelectionLost()
if (self.popup) then
self.popup:SelectionLost();
end
end
function Event:CleanupDropDowns()
self.tab:CleanupDropDowns();
end
function Event:SetCells(cells)
cells = cells or {};
self.cells = cells;
-- Event handlers for "Checkbox" cell
local checkBox = Turbine.UI.Lotro.CheckBox();
checkBox:SetParent(cells.checkbox);
checkBox:SetZOrder(1);
checkBox:SetSize(16, 16);
checkBox.CheckedChanged = function(ctl)
self.settings.checked = ctl:IsChecked();
cells.checkbox.sortValue = (self.settings.checked and 1) or 0;
self:SaveSettings();
self.tab:ReapplySort("checkbox");
end
checkBox.FocusGained = function(ctl)
self:CleanupDropDowns();
end
cells.checkbox.box = checkBox;
cells.checkbox.FocusGained = checkBox.FocusGained;
function cells.checkbox.SetWidth(ctl, width)
Turbine.UI.Lotro.TextBox.SetWidth(ctl, width);
ctl.box:SetPosition(width - 19, 4);
end
cells.checkbox.FocusGained = checkBox.FocusGained;
cells.checkbox:SetWidth(cells.checkbox:GetWidth());
-- Event handlers for "Description" cell
cells.desc:SetReadOnly(false);
cells.desc.FocusGained = function(ctl)
self:CleanupDropDowns();
ctl:SetForeColor(Turbine.UI.Color.White);
ctl:SetWantsKeyEvents(true);
end
cells.desc.KeyDown = function(ctl, args)
if (args.Action == Turbine.UI.Lotro.Action.EnterKey) then
ctl:GetParent():Focus();
elseif (args.Action == Turbine.UI.Lotro.Action.Escape) then
ctl:SetText(self.settings.desc);
ctl:GetParent():Focus();
end
end
cells.desc.FocusLost = function(ctl)
ctl:SetWantsKeyEvents(false);
self:SetCategory(self.settings.category); -- restore color
self.settings.desc = ctl:GetText();
self:SaveSettings();
self.tab:ReapplySort("desc");
end
-- Event handlers for "Location" cell
cells.location:SetReadOnly(false);
cells.location.FocusGained = function(ctl)
self:CleanupDropDowns();
ctl:SetForeColor(Turbine.UI.Color.White);
Turbine.UI.Lotro.TextBox.SetText(ctl, ctl.fullText);
ctl:SetWantsKeyEvents(true);
end
cells.location.KeyDown = function(ctl, args)
if (args.Action == Turbine.UI.Lotro.Action.EnterKey) then
ctl:GetParent():Focus();
elseif (args.Action == Turbine.UI.Lotro.Action.Escape) then
ctl:SetText(self.settings.location);
ctl:GetParent():Focus();
end
end
cells.location.FocusLost = function(ctl)
ctl:SetWantsKeyEvents(false);
self:SetCategory(self.settings.category); -- restore color
local text = Turbine.UI.Lotro.TextBox.GetText(ctl);
if (text == "") then
text = nil;
end
ctl:SetText(text);
self.settings.location = text;
self:SaveSettings();
self.tab:ReapplySort("location");
end
function cells.location.GetText(ctl)
return ctl.fullText;
end
function cells.location.SetText(ctl, fullText)
ctl.fullText = fullText;
ctl.coords = nil;
ctl.shortText = nil;
if (fullText) then
ctl.x, ctl.y, ctl.coords = Event.GetCoords(fullText);
ctl.shortText = ctl.coords or fullText:match(": ([^:]+)$");
local label = Turbine.UI.Label();
label:SetText(fullText);
label:SetFont(ctl:GetFont());
label:AutoSize();
ctl.fullWidth = label:GetWidth();
ctl.sortValue = ctl.coords or ctl.fullText or "";
ctl:SetWidth(ctl:GetWidth()); -- update text with coords or fulltext, based on width
else
Turbine.UI.Lotro.TextBox.SetText(ctl, "â");
end
end
function cells.location.SetWidth(ctl, width)
Turbine.UI.Lotro.TextBox.SetWidth(ctl, width);
if (ctl.fullText) then
Turbine.UI.Lotro.TextBox.SetText(ctl, ctl.fullText);
if (ctl.fullWidth and (width < ctl.fullWidth) and ctl.shortText) then
Turbine.UI.Lotro.TextBox.SetText(ctl, ctl.shortText);
end
end
end
-- Event handlers for "Distance" cell
cells.distance.GetText = function(ctl)
-- If distance is unknown, don't show it in the tooltip.
if (ctl.sortValue ~= math.huge) then
return Turbine.UI.Lotro.TextBox.GetText(ctl);
end
end
-- Event handlers for "Character" cell
cells.charName.FocusGained = function(ctl)
self:CleanupDropDowns();
local charNamesWithServers, charIDs = self.window:GetCharNamesWithServers()
local dropDown = Thurallor.UI.DropDown(charNamesWithServers, self.window:GetCharNameWithServer(self:GetCharacterID()));
dropDown.charIDs = charIDs;
dropDown:SetParent(ctl);
dropDown:SetFont(Turbine.UI.Lotro.Font.Verdana14);
dropDown:SetZOrder(2);
dropDown:SetPosition(1, 1);
local width = ctl:GetWidth() - 2;
dropDown:SetSize(ctl:GetWidth() - 2, ctl:GetHeight() - 2);
dropDown:SetExpandedWidth(math.max(width, 200));
dropDown.ItemChanged = function(dd, args)
local nameWithServer = args.Text;
local charID = dd.charIDs[nameWithServer];
self:SetCharacterID(charID);
self:UpdateTimeRemaining(); -- "remaining" can have different value depending on char
self:SaveSettings();
self.tab:ReapplySort("charName");
self.tab:ReapplySort("serverID");
self.tab:ReapplySort("remaining");
end
dropDown:SetWantsKeyEvents(true);
dropDown.KeyDown = function(_, args)
if ((args.Action == Turbine.UI.Lotro.Action.EnterKey) or
(args.Action == Turbine.UI.Lotro.Action.Escape)) then
self:CleanupDropDowns();
local parent = ctl:GetParent();
if (parent) then
parent:Focus();
end
end
end
ctl.dropDown = dropDown;
self.tab.dropDownCtl = ctl;
end
function cells.charName.SetWidth(ctl, width)
Turbine.UI.Lotro.TextBox.SetWidth(ctl, width);
if (ctl.dropDown) then
ctl.dropDown:SetWidth(width - 2);
end
end
-- Event handlers for "Server" cell
-- none (read only)
-- Event handlers for "Remaining time" cell
cells.remaining.FocusGained = function(ctl)
self:CleanupDropDowns();
local dialog = ExpireTimeDialog(self);
dialog.Ok = function(_, args)
self:SetExpirationTime(args.expTime);
self:SaveSettings();
self.tab:ReapplySort("remaining");
end
ctl:GetParent():Focus(); -- avoid reopening the dialog when the dialog closes and we regain focus
end
cells.remaining.GetText = function(ctl)
-- if time is "never", don't show the time in the tooltip
if (self.settings.expTime ~= -1) then
return Turbine.UI.Lotro.TextBox.GetText(ctl);
end
end
-- Event handlers for "Next reset" cell
cells.resetTimes.FocusGained = function(ctl)
self:CleanupDropDowns();
local dialog = ResetTimesDialog(self);
dialog.Ok = function(_, args)
self:SetResetTimes(args.resetTimes);
self:SetRotation(args.rotation);
self:SaveSettings();
self.tab:ReapplySort("resetTimes");
end
ctl:GetParent():Focus(); -- avoid reopening the dialog when the dialog closes and we regain focus
end
cells.resetTimes.GetText = function(ctl)
-- if time is "never", don't show the time in the tooltip
if (self.settings.resetTimes) then
return Turbine.UI.Lotro.TextBox.GetText(ctl);
end
end
-- Event handlers for "Category" cell
cells.category.FocusGained = function(ctl)
self:CleanupDropDowns();
local catNames = self.window:GetUserCategories();
local dropDown = Thurallor.UI.DropDown(catNames, self.settings.category);
dropDown:SetParent(ctl);
dropDown:SetFont(Turbine.UI.Lotro.Font.Verdana14);
dropDown:SetZOrder(2);
dropDown:SetPosition(1, 1);
local width = ctl:GetWidth() - 2;
dropDown:SetSize(ctl:GetWidth() - 2, ctl:GetHeight() - 2);
dropDown:SetExpandedWidth(math.max(width, 200));
dropDown.ItemChanged = function(_, args)
self:SetCategory(args.Text);
self:SaveSettings();
self.tab:ReapplySort("category");
end
dropDown:SetWantsKeyEvents(true);
dropDown.KeyDown = function(_, args)
if ((args.Action == Turbine.UI.Lotro.Action.EnterKey) or
(args.Action == Turbine.UI.Lotro.Action.Escape)) then
self:CleanupDropDowns();
ctl:GetParent():Focus();
end
end
ctl.dropDown = dropDown;
self.tab.dropDownCtl = ctl;
end
function cells.category.SetWidth(ctl, width)
Turbine.UI.Lotro.TextBox.SetWidth(ctl, width);
if (ctl.dropDown) then
ctl.dropDown:SetWidth(width - 2);
end
end
-- Event handlers for "Notes" cell
cells.notes.FocusGained = function(ctl)
self:CleanupDropDowns();
local dialog = NotesDialog(self);
dialog.Ok = function(_, args)
if (args.text == "") then
args.text = nil;
end
self:SetNotes(args.text);
self:SaveSettings();
self.tab:ReapplySort("notes");
end
ctl:GetParent():Focus(); -- avoid reopening the dialog when the dialog closes and we regain focus
end
cells.notes.GetText = function(ctl)
return self.settings.notes;
end
-- Fill in the values
self:SetChecked(self.settings.checked);
self:SetDescription(self.settings.desc);
self:SetCharacterID(self.settings.charID);
self:SetExpirationTime(self.settings.expTime, true);
self:SetResetTimes(self.settings.resetTimes);
self:SetRotation(self.settings.rotation);
self:SetCategory(self.settings.category);
self:SetLocation(self.settings.location);
self:SetNotes(self.settings.notes);
end
-- if 'ifOpen' is specified, then the window won't be opened if it wasn't already so, but you'll see the event selected next time you open the window
function Event:ZoomToEvent(fromCtl, ifOpen)
local tab = self.tab;
local window = tab.window;
if (not ifOpen) then
window:SetVisible(true);
end
window:BringTabToFront(tab.settings.name);
tab:SetVisible(true);
tab:SelectEvent(nil);
tab:EnsureEventVisible(self);
if (window:IsVisible()) then
local tableRow = tab.table:GetRow(self);
self.zoomer = Thurallor.UI.Zoomer(fromCtl, tableRow);
self.zoomer.ZoomComplete = function()
window:Activate();
tab:SelectEvent(self);
self.zoomer = nil;
end
else
tab:SelectEvent(self);
end
end
function Event:SaveSettings(now)
self.tab:SaveSettings(now);
end
function Event:GetDescription()
return self.settings.desc;
end
function Event:SetDescription(desc)
self.settings.desc = desc;
local cell = self.cells["desc"];
if (cell) then
cell:SetText(desc);
end
end
function Event:GetLocation()
local cell;
if (self.cells) then
cell = self.cells["location"];
end
return self.settings.location, cell and cell.coords;
end
function Event:SetLocation(loc)
if (loc == "") then
loc = nil;
end
self.settings.location = loc;
local cell = self.cells["location"];
if (cell) then
cell:SetText(loc);
cell.sortValue = cell.coords or cell.fullText or "";
end
end
function Event:IsChecked()
return self.settings.checked;
end
function Event:SetChecked(checked)
self.settings.checked = checked;
local cell = self.cells["checkbox"];
if (cell) then
cell.box:SetChecked(checked);
cell.sortValue = (self.settings.checked and 1) or 0;
end
end
function Event:GetCharacterID()
return self.settings.charID;
end
function Event:SetCharacterID(charID)
self.settings.charID = charID;
local serverID, charName = charID:match("^(.*)%.(.*)$");
local charCell, serverCell = self.cells.charName, self.cells.serverID;
if (charCell) then
charCell:SetText(charName);
end
if (serverCell) then
local serverName = Turbine.Engine:GetServerName(serverID) or serverID;
serverCell:SetText(serverName);
end
end
function Event:GetExpirationTime()
return self.settings.expTime;
end
function Event:GetResetTimes()
return self.settings.resetTimes;
end
function Event:GetRotation()
return self.settings.rotation;
end
-- If 'startup' is true, the 'expired' flag will be left alone.
function Event:SetExpirationTime(expTime, startup)
self.settings.expTime = expTime;
if (not startup) then
self:SetExpired(false);
end
self:UpdateTimeRemaining();
self.nextResetTime = nil;
self:UpdateResetTimeRemaining();
end
function Event:SetResetTimes(resetTimes)
self.settings.resetTimes = resetTimes;
self:_CreateSchedule();
end
function Event:SetRotation(rotation)
self.settings.rotation = rotation;
self:_CreateSchedule();
end
function Event:_CreateSchedule()
local timeSpec = self.settings.rotation or self.settings.resetTimes;
if (timeSpec) then
self.resetSchedule = Schedule(timeSpec);
else
self.resetSchedule = nil;
end
self.nextResetTime = nil;
self:UpdateResetTimeRemaining();
end
function Event:GetCategory()
return self.settings.category;
end
function Event:SetCategory(category)
self.settings.category = category
local cell = self.cells["category"];
if (cell) then
cell:SetText(category);
end
-- Set foreground color of all cells based on category
local color = self:GetColor();
for _, cell in pairs(self.cells) do
cell:SetForeColor(color);
end
end
function Event:GetCategory()
return self.settings.category;
end
function Event:GetNotes()
return self.settings.notes;
end
function Event:SetNotes(notes)
self.settings.notes = notes;
local cell = self.cells["notes"];
if (cell) then
if (notes) then
-- Show an abbreviation of the cell contents
local firstLine = string.gsub(notes, "%.*%c.*$", "...");
cell:SetText(firstLine);
else
cell:SetText("â");
end
end
end
function Event:GetColor()
return self.window:GetCategoryColor(self.settings.category);
end
-- Returns true if the event belongs to the currently logged in character
function Event:IsMine()
return (self:GetCharacterID() == self.loggedInCharID);
end
-- Used for self.settings.postpone.method =="DelayFromExpiration" to find
-- the next time after the current time that is a multiple of the specified
-- delay interval from the previous expiration time.
function Event:GetNextDelayMultiple()
local interval = self.settings.postpone.delay;
local currentTime = Time();
local nextTime = Time(0):SetGameTime(self.settings.expTime + interval);
if (nextTime:IsBefore(currentTime)) then
nextTime:RestrictToWindow(currentTime:JustAfter(), interval);
end
return nextTime:GetGameTime();
end
function Event:Postpone()
local expTime;
if (self.settings.postpone.method =="DelayFromNow") then
expTime = Turbine.Engine.GetGameTime() + self.settings.postpone.delay;
elseif (self.settings.postpone.method =="DelayFromExpiration") then
expTime = self:GetNextDelayMultiple();
elseif (self.settings.postpone.method =="DailyResetTime") then
expTime = Time():GetNextServerResetTime(true):GetGameTime();
elseif (self.settings.postpone.method =="ScheduledResetTime") then
expTime = self.nextResetTime or -1;
elseif (self.settings.postpone.method =="Never") then
expTime = -1;
else -- (self.settings.postpone.method =="AtNextLogin")
expTime = 0;
end
self:SetExpirationTime(expTime);
end
function Event:GetPostponeStr()
local prevContext = L:SetContext("/PostponeTimeDialog/Tooltip");
local text = L:GetText(self.settings.postpone.method or "AtNextLogin");
local delay = self.settings.postpone.delay;
local intervalStr = Time.GetDelayStr(delay);
if (self.settings.postpone.method == "DelayFromExpiration") then
local newTime = self:GetNextDelayMultiple();
local multiple = math.floor(0.5 + (newTime - self.settings.expTime) / delay);
delay = newTime - Turbine.Engine.GetGameTime();
if (multiple > 1) then
intervalStr = multiple .. " Ã (" .. intervalStr .. ")";
end
elseif (self.settings.postpone.method == "ScheduledResetTime") then
if (self.nextResetTime) then
delay = self.nextResetTime - Turbine.Engine.GetGameTime();
else
text = L:GetText("Never");
end
elseif (self.settings.postpone.method == "DailyResetTime") then
delay = Time():GetNextServerResetTime(true):GetGameTime() - Turbine.Engine.GetGameTime();
end
L:SetContext(prevContext);
return text:gsub("<delay>", Time.GetDelayStr(delay)):gsub("<interval>", intervalStr);
end
function Event:UpdateTimeRemaining()
local cell = self.cells.remaining;
if (cell) then
if (self.settings.expTime == -1) then
-- Special handling for "never" events:
self:SetExpired(false); -- remove alert from icon tooltip
self:SetWantsTicks(false); -- no need for updates, either
cell.sortValue = 10000000000001; -- sort at the end of the table
cell:SetText("â"); -- "Never"
elseif (self.settings.expTime == 0) then
-- Special handling for "at next login" events:
self:SetExpired(false); -- remove alert from icon tooltip
self:SetWantsTicks(false); -- no need for updates, either
if (self:IsMine()) then
cell.sortValue = 10000000000000; -- sort at the end for the current character
else
cell.sortValue = -10000000000000; -- sort at the beginning for other characters
end
cell:SetText(L:GetText("/Time/TimeDisplay/NextLogin"));
else
local remaining = self.settings.expTime - Turbine.Engine.GetGameTime();
if (remaining <= 0) then
self.nextResetTime = nil;
self:SetExpired(true);
end
cell.sortValue = remaining;
cell:SetText(Time.GetDelayStr(remaining, false, self.window.settings.showNegativeTimes));
end
end
end
function Event:UpdateResetTimeRemaining()
local cell = self.cells.resetTimes;
if (cell) then
if (not self.resetSchedule) then
cell.sortValue = 10000000000001; -- sort at the end of the table
cell:SetText("â"); -- "Never"
self.nextResetTime = nil;
else
if (not self.nextResetTime) then
local expTime = Time(0):SetGameTime(self.settings.expTime);
local prevTime = Time.max(Time(), expTime):AddSeconds(59);
self.nextResetTime = self.resetSchedule:GetNextTime(prevTime, true):GetGameTime();
cell.sortValue = self.nextResetTime;
end
local currentTime = Turbine.Engine.GetGameTime();
cell:SetText(Time.GetDelayStr(self.nextResetTime - currentTime, false, self.window.settings.showNegativeTimes));
end
end
end
function Event:SetWantsTicks(wantsTicks)
if (self.Tick) then
if (not wantsTicks) then
self.window:RemoveTickFunc(self.Tick);
self.Tick = nil;
end
else
if (wantsTicks) then
self.Tick = function()
self:UpdateTimeRemaining();
self:UpdateResetTimeRemaining();
end
self.window:AddTickFunc(self.Tick);
end
end
end
function Event:IsExpired()
return self.settings.expired;
end
function Event:SetExpired(expired)
local wasExpired = self.settings.expired or false;
self.settings.expired = expired;
self:SaveSettings();
if (expired ~= wasExpired) then
DoCallbacks(self, "ExpiredChanged", expired);
end
self:SetWantsTicks((not expired) or self.window.settings.showNegativeTimes);
end
function Event:SetLastAutomaticUpdate(charID, itemType, item)
self.settings.automatic = { charID, itemType, item, Turbine.Engine.GetGameTime() };
end
function Event:GetLastAutomaticUpdate()
return self.settings.automatic;
end
function Event:ShowPopUp()
if (not self.popup) then
self.popup = NotificationDialog(self);
end
end
function Event:HidePopUp()
if (self.popup) then
self.popup:Close();
self.popup = nil;
end
end
function Event:Destroy()
self:HidePopUp();
self:SetWantsTicks(false);
DoCallbacks(self, "Destroyed");
--Puts("Destroying event: " .. self.settings.desc);
end