ExpireTimeDialog = class(Turbine.UI.Lotro.Window);
function ExpireTimeDialog:Constructor(event, hideSpecific)
Turbine.UI.Lotro.Window.Constructor(self);
local prevContext = L:SetContext("/ExpireTimeDialog");
self:SetText(L:GetText("Title"));
self:SetVisible(true);
self.event, self.hideSpecific, self.window = event, hideSpecific, event.tab.window;
-- If the event is deleted, we should close this window
self.eventDestroyedFunc = function()
self:Close();
end
-- If the window is closed, we should unregister the callback
self.Closing = function()
RemoveCallback(event, "Destroyed", self.eventDestroyedFunc);
self:SetWantsUpdates(false);
end
-- Register the callback now, but first make sure it isn't already registered
self.Closing();
AddCallback(event, "Destroyed", self.eventDestroyedFunc);
-- Make this window prevent access to the main window while it's open
self:SetZOrder(self.window:GetZOrder() + 1);
self.window:WaitForChild(self);
local dateInfo = Turbine.Engine.GetDate();
local dayOfWeek = dateInfo.DayOfWeek;
self.weekdays = {};
local dayNames = {};
for d, dayName in ipairs({ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }) do
table.insert(dayNames, L:GetText("/Time/DayOfWeek/" .. dayName));
end
for day = dayOfWeek, dayOfWeek + 7 do
local text = "<weekday>";
if (day == dayOfWeek) then
text = L:GetText("/Time/DayOfWeek/Today");
elseif (day == dayOfWeek + 1) then
text = L:GetText("/Time/DayOfWeek/Tomorrow");
elseif (day == dayOfWeek + 7) then
text = L:GetText("/Time/DayOfWeek/Next");
end
local dayNum = (day - 1) % 7 + 1;
text = string.gsub(text, "<weekday>", dayNames[dayNum]);
table.insert(self.weekdays, text);
end
self.monthNames = {};
for m, monthName in ipairs({ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }) do
table.insert(self.monthNames, L:GetText("/Time/Month/" .. monthName));
end
local font = Turbine.UI.Lotro.Font.Verdana14;
local xmargin = 28;
local parent = self;
local function AddRadioButton(left, top, width, text, selected)
local button = Thurallor.UI.RadioButton(parent, text, selected);
button:SetFont(font);
button:SetPosition(left, top);
button:SetSize(width, 14);
return button, left + width;
end
local function AddTextBox(left, top, width, value, minValue, maxValue, zeroPadDigits)
local textBox = Nudger(parent, font, value, minValue, maxValue, zeroPadDigits);
textBox:SetPosition(left, top);
textBox:SetWidth(width);
return textBox, left + width;
end
local function AddLabel(left, top, text)
local label = Turbine.UI.Label();
label:SetFont(font);
label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
label:SetText(text);
label:SetMultiline(false);
label:AutoSize();
label:SetSize(label:GetWidth() + 8, 20)
label:SetParent(parent);
label:SetPosition(left, top + 13);
return label, left + label:GetWidth();
end
local function AddPulldown(left, top, width, options, value)
local dropDown = Thurallor.UI.DropDown(options, value);
dropDown:SetParent(parent);
dropDown:SetFont(font);
dropDown:SetZOrder(1);
dropDown:SetPosition(left, top + 14);
dropDown:SetWidth(width);
return dropDown;
end
local function AddButton(left, top, width, text)
local button = Turbine.UI.Lotro.Button();
button:SetParent(parent);
button:SetText(text);
button:SetPosition(left, top);
button:SetWidth(width);
return button;
end
local left, top = xmargin, 44;
-------------------------------
-- * Delay from now:
-------------------------------
self.delayFromNow = AddRadioButton(left, top, 300, " " .. L:GetText("DelayFromNow") .. ":", false);
self.delayFromNow.Select = function() self.delayFromNow:MouseClick(); end
top = top + 20;
left = xmargin + 16;
self.delayFromNow.days, left = AddTextBox(left, top, 30, 0, 0, nil);
self.delayFromNow.days.FocusGained = self.delayFromNow.Select;
label, left = AddLabel(left, top, L:GetText("/Time/Days") .. " ");
self.delayFromNow.hours, left = AddTextBox(left, top, 30, 0, 0, 23);
self.delayFromNow.hours.FocusGained = self.delayFromNow.Select;
label, left = AddLabel(left, top, L:GetText("/Time/Hours") .. " ");
self.delayFromNow.minutes, left = AddTextBox(left, top, 30, 0, 0, 59);
self.delayFromNow.minutes.FocusGained = self.delayFromNow.Select;
label, left = AddLabel(left, top, L:GetText("/Time/Minutes") .. " ");
self.delayFromNow.seconds, left = AddTextBox(left, top, 30, 0, 0, 59);
self.delayFromNow.seconds.FocusGained = self.delayFromNow.Select;
label, left = AddLabel(left, top, L:GetText("/Time/Seconds"));
local width = left + 16 + xmargin;
top = top + 60;
-------------------------------
-- * Specific time this week:
-------------------------------
left = xmargin;
self.timeThisWeek = AddRadioButton(left, top, 300, " " .. L:GetText("TimeThisWeek") .. ":", false);
self.timeThisWeek.Select = function() self.timeThisWeek:MouseClick(); end
self.timeThisWeek.Clicked = function() self:ShowTimeThisWeek(); end
top = top + 20;
left = xmargin + 16;
-- Make a control for grouping the following controls to allow easy show/hide of them all
self.timeThisWeek.group = Turbine.UI.Control();
if (hideSpecific) then
self.timeThisWeek:SetParent(nil);
else
self.timeThisWeek.group:SetParent(self);
end
self.timeThisWeek.group:SetPosition(left, top);
self.timeThisWeek.group:SetSize(width - 2 * xmargin - 16, 46);
parent = self.timeThisWeek.group;
top = 0; left = 0;
self.selectedDayOfWeek = 1;
self.timeThisWeek.dayOfWeek = AddPulldown(left, top, 188, self.weekdays, self.weekdays[1]);
AddCallback(self.timeThisWeek.dayOfWeek, "MouseClick", function()
self.timeThisWeek.Select();
self:Focus();
end);
self.timeThisWeek.dayOfWeek.ItemChanged = function(_, args)
self.selectedDayOfWeek = args.Index;
end
left = left + 188;
label, left = AddLabel(left, top, " " .. L:GetText("AtTime") .. " ");
self.timeThisWeek.hour = AddTextBox(left, top, 30, 0, 0, 23);
self.timeThisWeek.hour.FocusGained = self.timeThisWeek.Select;
left = left + 30;
label, left = AddLabel(left, top, L:GetText("HourMinuteSeparator"));
label:SetWidth(label:GetWidth() - 4);
left = left - 4 + L:GetText("HourMinuteSeparatorAdjust");
self.timeThisWeek.minute = AddTextBox(left, top, 30, 0, 0, 59, 2);
self.timeThisWeek.minute.FocusGained = self.timeThisWeek.Select;
left = left + 30;
label, left = AddLabel(left, top, L:GetText("OClock"));
self.timeThisWeek.second = Turbine.UI.TextBox(); -- hidden
self.timeThisWeek.second:SetText(0);
parent = self;
self.timeThisWeek.group:SetWidth(left);
self.timeThisWeek.group:SetLeft(math.floor(0.5 + (width - left) / 2));
top = self.timeThisWeek.group:GetTop() + 60;
-------------------------------
-- * Specific date and time:
-------------------------------
left = xmargin;
self.dateAndTime = AddRadioButton(left, top, 300, " " .. L:GetText("DateAndTime") .. ": ", false);
self.dateAndTime.Select = function() self.dateAndTime:MouseClick(); end
top = top + 20;
left = xmargin + 16;
self.dateAndTime.group = Turbine.UI.Control();
if (hideSpecific) then
self.dateAndTime:SetParent(nil);
else
self.dateAndTime.group:SetParent(self);
end
self.dateAndTime.group:SetPosition(left, top);
self.dateAndTime.group:SetSize(width - 2 * xmargin - 16, 46);
parent = self.dateAndTime.group;
top = 0; left = 0;
self.dateAndTime.day = AddTextBox(left, top, 30, 1, 1, 31);
self.dateAndTime.day.FocusGained = self.dateAndTime.Select;
left = left + 34;
self.selectedMonth = 1;
self.dateAndTime.month = AddPulldown(left, top, 100, self.monthNames, self.monthNames[1]);
AddCallback(self.dateAndTime.month, "MouseClick", function()
self.dateAndTime.Select();
self:Focus();
end);
self.dateAndTime.month.ItemChanged = function(_, args)
self.selectedMonth = args.Index;
end
left = left + 104;
local currentYear = Turbine.Engine.GetDate().Year;
self.dateAndTime.year = AddTextBox(left, top, 50, currentYear, 2007, nil);
self.dateAndTime.year.FocusGained = self.dateAndTime.Select;
left = left + 50;
label, left = AddLabel(left, top, " " .. L:GetText("AtTime") .. " ");
self.dateAndTime.hour = AddTextBox(left, top, 30, 0, 0, 23);
self.dateAndTime.hour.FocusGained = self.dateAndTime.Select;
left = left + 30;
label, left = AddLabel(left, top, L:GetText("HourMinuteSeparator"));
label:SetWidth(label:GetWidth() - 4);
left = left - 4 + L:GetText("HourMinuteSeparatorAdjust");
self.dateAndTime.minute = AddTextBox(left, top, 30, 0, 0, 59, 2);
self.dateAndTime.minute.FocusGained = self.dateAndTime.Select;
left = left + 30;
label, left = AddLabel(left, top, L:GetText("OClock"));
self.dateAndTime.second = Turbine.UI.TextBox(); -- hidden
self.dateAndTime.second:SetText(0);
parent = self;
self.dateAndTime.group:SetWidth(left);
self.dateAndTime.group:SetLeft(math.floor(0.5 + (width - left) / 2));
top = self.dateAndTime.group:GetTop() + 64;
if (hideSpecific) then
top = top - 170;
end
left = xmargin;
self.now = AddRadioButton(left, top, 200, " " .. L:GetText("/Time/TimeDisplay/Now"), false);
self.never = AddRadioButton(left, top, 200, " " .. L:GetText("/Time/Never"), false);
top = top + 20;
self.dailyResetTime, left = AddRadioButton(left, top, 200, " " .. L:GetText("/Time/DailyResetTime"), false);
self.atNextLogin = AddRadioButton(left, top, 200, " " .. L:GetText("/Time/AtNextLogin"), false);
top = top + 30;
Thurallor.UI.RadioButton.LinkPeers({ self.delayFromNow, self.timeThisWeek, self.dateAndTime, self.now, self.never, self.dailyResetTime, self.atNextLogin });
local buttons = Turbine.UI.Control();
buttons:SetParent(self);
buttons:SetTop(top);
parent = buttons;
self.okButton = AddButton(0, 0, 50, L:GetText("Ok"));
self.okButton.SetEnabled = function(_, enable)
-- Lotro.Button can't get mouse events while disabled. Need to superimpose another control.
if (enable and not self.okButton:IsEnabled()) then
self.okMask:SetParent(nil);
self.okMask = nil;
elseif (not enable and self.okButton:IsEnabled()) then
self.okMask = Turbine.UI.Control();
self.okMask:SetParent(self.okButton);
self.okMask:SetSize(self.okButton:GetSize());
Thurallor.UI.Tooltip(L:GetText("/ExpireTimeDialog/NotOkTooltip")):Attach(self.okMask);
end
Turbine.UI.Lotro.Button.SetEnabled(self.okButton, enable);
end
self.okButton.Click = function()
DoCallbacks(self, "Update");
for _, checkBox in pairs({ "now", "dailyResetTime", "never", "atNextLogin", "delayFromNow", "timeThisWeek", "dateAndTime" }) do
if (self[checkBox]:IsChecked()) then
DoCallbacks(self, "Ok", { expTime = self.expTime; method = checkBox });
break;
end
end
self:Close();
end
left = self.okButton:GetWidth() + 4;
self.cancelButton = AddButton(left, 0, L:GetText("CancelWidth"), L:GetText("Cancel"));
self.cancelButton.Click = function()
self:Close();
end
buttons:SetSize(left + self.cancelButton:GetWidth(), self.cancelButton:GetHeight());
buttons:SetLeft(math.floor(0.5 + (width - buttons:GetWidth()) / 2));
top = top + buttons:GetHeight();
local height = top + 22;
self:SetSize(width, height);
local center = math.floor(0.5 + width / 2)
self.never:SetLeft(center);
self.atNextLogin:SetLeft(center);
-- Position this window so its center is at the mouse position
local x, y = Turbine.UI.Display.GetMousePosition();
local left = math.max(0, x - math.floor(width / 2));
local top = math.max(0, y - math.floor(height / 2));
self:SetPosition(left, top);
-- Handle ENTER and ESC keys
EnableEnterEscHandling(self);
-- Intelligently set the initial radio button choice:
local expTime = self.event:GetExpirationTime();
local nextResetTime = Time():GetNextServerResetTime(true):GetGameTime();
if (expTime == -1) then
self.never:SetChecked(true);
elseif (expTime == 0) then
self.atNextLogin:SetChecked(true);
elseif (math.floor(0.5 + nextResetTime) == math.floor(0.5 + expTime)) then
self.dailyResetTime:SetChecked(true);
elseif (hideSpecific) then
if (expTime == Turbine.Engine.GetGameTime()) then
self.now:SetChecked(true);
else
self:SetDelayFromNow(expTime);
self.delayFromNow:SetChecked(true);
end
elseif (self:SetTimeThisWeek(expTime)) then
self:SetTimeThisWeek(expTime);
self.timeThisWeek:SetChecked(true);
else
self:SetDateAndTime(expTime);
self.dateAndTime:SetChecked(true);
end
DoCallbacks(self, "Update");
L:SetContext(prevContext);
self:SetWantsUpdates(true);
end
function ExpireTimeDialog:EnterKeyPressed()
if (self.okButton:IsEnabled()) then
DoCallbacks(self.okButton, "Click");
end
end
function ExpireTimeDialog:EscapeKeyPressed()
self:Close();
end
function ExpireTimeDialog:Close()
DoCallbacks(self, "Closing");
Turbine.UI.Lotro.Window.Close(self);
end
function ExpireTimeDialog:Update()
local gameTime = Turbine.Engine.GetGameTime();
if (self.delayFromNow:IsChecked()) then
expTime = self:ReadDelayFromNow();
elseif (self.timeThisWeek:IsChecked()) then
expTime = self:ReadTimeThisWeek();
elseif (self.dateAndTime:IsChecked()) then
expTime = self:ReadDateAndTime();
elseif (self.now:IsChecked()) then
expTime = gameTime;
elseif (self.dailyResetTime:IsChecked()) then
expTime = Time():GetNextServerResetTime(true):GetGameTime();
elseif (self.never:IsChecked()) then
self.expTime = -1;
self.okButton:SetEnabled(true);
return; -- no need to update the times in the other controls
else -- self.atNextLogin:IsChecked()
self.expTime = 0;
self.okButton:SetEnabled(true);
return; -- no need to update the times in the other controls
end
if (expTime < gameTime) then
self.okButton:SetEnabled(false);
expTime = gameTime;
else
self.okButton:SetEnabled(true);
end
if (self.now:IsChecked() or self.dailyResetTime:IsChecked()) then
self:SetDelayFromNow(expTime);
self:SetTimeThisWeek(expTime);
self:SetDateAndTime(expTime);
elseif (self.delayFromNow:IsChecked()) then
self:SetTimeThisWeek(expTime);
self:SetDateAndTime(expTime);
elseif (self.timeThisWeek:IsChecked()) then
self:SetDelayFromNow(expTime);
self:SetDateAndTime(expTime);
else -- self.dateAndTime:IsChecked()
self:SetDelayFromNow(expTime);
self:SetTimeThisWeek(expTime);
end
end
function ExpireTimeDialog:ReadDelayFromNow()
local expTime = Turbine.Engine.GetGameTime();
expTime = expTime + tonumber(self.delayFromNow.days:GetText()) * 24 * 60 * 60;
expTime = expTime + tonumber(self.delayFromNow.hours:GetText()) * 60 * 60;
expTime = expTime + tonumber(self.delayFromNow.minutes:GetText()) * 60;
expTime = expTime + tonumber(self.delayFromNow.seconds:GetText());
return expTime;
end
function ExpireTimeDialog:SetDelayFromNow(expTime)
self.expTime = expTime;
remaining = math.floor(expTime - Turbine.Engine.GetGameTime() + 0.5);
local s = math.floor(remaining % 60);
local m = math.floor((remaining % 3600) / 60);
local h = math.floor((remaining % 86400) / 3600);
local d = math.floor(remaining / 86400);
self.delayFromNow.days:SetText(d);
self.delayFromNow.hours:SetText(h);
self.delayFromNow.minutes:SetText(m);
self.delayFromNow.seconds:SetText(s);
end
function ExpireTimeDialog:ReadTimeThisWeek()
local expTime = Turbine.Engine.GetGameTime();
local dateInfo = Turbine.Engine.GetDate();
local daysAdvanced = self.selectedDayOfWeek - 1;
local hoursAdvanced = tonumber(self.timeThisWeek.hour:GetText()) - dateInfo["Hour"];
local minutesAdvanced = tonumber(self.timeThisWeek.minute:GetText()) - dateInfo["Minute"];
local secondsAdvanced = tonumber(self.timeThisWeek.second:GetText()) - dateInfo["Second"];
expTime = expTime + daysAdvanced * 24* 60 * 60;
expTime = expTime + hoursAdvanced * 60 * 60;
expTime = expTime + minutesAdvanced * 60;
expTime = expTime + secondsAdvanced;
return expTime;
end
function ExpireTimeDialog:SetTimeThisWeek(expTime)
self.expTime = expTime;
remaining = math.floor(expTime - Turbine.Engine.GetGameTime() + 0.5);
local s = math.floor(remaining % 60);
local m = math.floor((remaining % 3600) / 60);
local h = math.floor((remaining % 86400) / 3600);
local d = math.floor(remaining / 86400);
local dateInfo = Turbine.Engine.GetDate();
local day = d;
local hour = h + dateInfo.Hour;
local minute = m + dateInfo.Minute;
local second = s + dateInfo.Second;
if (second >= 60) then
minute = minute + 1;
second = second - 60;
end
if (minute >= 60) then
hour = hour + 1;
minute = minute - 60;
end
if (hour >= 24) then
day = day + 1;
hour = hour - 24;
end
self.selectedDayOfWeek = day + 1;
local weekday = self.weekdays[day + 1];
if (weekday) then
self:ShowTimeThisWeek();
if (self.timeThisWeek.dayOfWeek:GetText() ~= weekday) then
self.timeThisWeek.dayOfWeek:SetText(self.weekdays[day + 1]);
end
self.timeThisWeek.hour:SetText(hour);
if (minute < 10) then
minute = "0" .. minute;
end
self.timeThisWeek.minute:SetText(minute);
self.timeThisWeek.second:SetText(second);
return true;
else
self:HideTimeThisWeek();
return false;
end
end
function ExpireTimeDialog:HideTimeThisWeek()
if (self.timeThisWeek.group:GetParent() ~= nil) then
self.timeThisWeek.group:SetParent(nil);
self.timeThisWeek:SetText(" " .. L:GetText("/ExpireTimeDialog/TimeThisWeek"));
end
end
function ExpireTimeDialog:ShowTimeThisWeek()
if ((self.timeThisWeek.group:GetParent() == nil) and (not self.hideSpecific)) then
self.timeThisWeek.group:SetParent(self);
self.timeThisWeek:SetText(" " .. L:GetText("/ExpireTimeDialog/TimeThisWeek") .. ":");
end
end
function ExpireTimeDialog:ReadDateAndTime()
local dateInfo = Turbine.Engine.GetDate();
local dayOfMonth = tonumber(self.dateAndTime.day:GetText());
local month = self.selectedMonth;
local year = tonumber(self.dateAndTime.year:GetText());
local d = self:GetDayOfYear(year, month, dayOfMonth);
d = d - self:GetDayOfYear(dateInfo.Year, dateInfo.Month, dateInfo.Day);
while true do
local yearLength = 365;
if (self:IsLeapYear(dateInfo.Year)) then
yearLength = 366;
end
if (dateInfo.Year < year) then
d = d + yearLength;
dateInfo.Year = dateInfo.Year + 1;
elseif (dateInfo.Year > year) then
d = d - yearLength;
dateInfo.Year = dateInfo.Year - 1;
else
break;
end
end
local h = tonumber(self.dateAndTime.hour:GetText()) - dateInfo.Hour;
local m = tonumber(self.dateAndTime.minute:GetText()) - dateInfo.Minute;
local s = tonumber(self.dateAndTime.second:GetText()) - dateInfo.Second;
local remaining = (d * 24 * 60 * 60) + (h * 60 * 60) + (m * 60) + s;
local expTime = Turbine.Engine.GetGameTime() + remaining;
return expTime;
end
function ExpireTimeDialog:SetDateAndTime(expTime)
self.expTime = expTime;
remaining = math.floor(expTime - Turbine.Engine.GetGameTime() + 0.5);
local s = math.floor(remaining % 60);
local m = math.floor((remaining % 3600) / 60);
local h = math.floor((remaining % 86400) / 3600);
local d = math.floor(remaining / 86400);
local dateInfo = Turbine.Engine.GetDate();
local year = dateInfo.Year;
local day = d + dateInfo.DayOfYear;
local hour = h + dateInfo.Hour;
local minute = m + dateInfo.Minute;
local second = s + dateInfo.Second;
if (second >= 60) then
minute = minute + 1;
second = second - 60;
end
if (minute >= 60) then
hour = hour + 1;
minute = minute - 60;
end
if (hour >= 24) then
day = day + 1;
hour = hour - 24;
end
while true do
local yearLength = 365;
if (self:IsLeapYear(year)) then
yearLength = 366;
end
if (day >= yearLength) then
year = year + 1;
day = day - yearLength;
else
break;
end
end
local month, dayOfMonth = self:GetMonthAndDay(year, day);
local monthName = self.monthNames[month];
self.selectedMonth = month;
self.dateAndTime.day:SetText(dayOfMonth);
if (self.dateAndTime.month:GetText() ~= monthName) then
self.dateAndTime.month:SetText(monthName);
end
self.dateAndTime.year:SetText(year);
self.dateAndTime.hour:SetText(hour);
if (minute < 10) then
minute = "0" .. minute;
end
self.dateAndTime.second:SetText(second);
self.dateAndTime.minute:SetText(minute);
end
function ExpireTimeDialog:IsLeapYear(year)
local div4 = ((year % 4) == 0);
local div100 = ((year % 100) == 0);
local div400 = ((year % 400) == 0);
if (div4) then
if (div100) then
return div400;
else
return true;
end
end
end
-- Static variable
ExpireTimeDialog.monthLengths = {
31, -- January
28, -- February
31, -- March
30, -- April
31, -- May
30, -- June
31, -- July
31, -- August
30, -- September
31, -- October
30, -- November
31 -- December
};
function ExpireTimeDialog:GetMonthAndDay(year, dayOfYear)
if (self:IsLeapYear(year)) then
ExpireTimeDialog.monthLengths[2] = 29;
else
ExpireTimeDialog.monthLengths[2] = 28;
end
local month, dayOfMonth = 1, dayOfYear;
while true do
local monthLength = ExpireTimeDialog.monthLengths[month];
if (dayOfMonth > monthLength) then
month = month + 1;
dayOfMonth = dayOfMonth - monthLength;
else
break;
end
end
return month, dayOfMonth;
end
function ExpireTimeDialog:GetDayOfYear(year, month, dayOfMonth)
if (self:IsLeapYear(year)) then
ExpireTimeDialog.monthLengths[2] = 29;
else
ExpireTimeDialog.monthLengths[2] = 28;
end
local dayOfYear = dayOfMonth;
while (month > 1) do
dayOfYear = dayOfYear + ExpireTimeDialog.monthLengths[month - 1];
month = month - 1;
end
return dayOfYear;
end