-------------------------------------------------------------------------------
-- Calendar by dimfish
-------------------------------------------------------------------------------
local Key = "CtrlShiftF11"
local Formats = {
"%d.%m.%Y",
"%d-%m-%Y",
"%d/%m/%Y",
"%m/%d/%Y",
"%Y_%m_%d",
"%Y-%m-%d",
"%Y.%m.%d",
"%d-%b-%y",
"%d-%b-%Y",
}
local Weeks = {
"[%U] [%j]",
"[%V] [%j]",
"[%W] [%j]",
}
local Colors = {
Normal = 0x0,
Weekend = 0x4,
Today = 0x9,
Selected = 0xE,
Disabled = 0x8,
}
-- See http://www.omniglot.com/language/time/days.htm, http://www.omniglot.com/language/time/months.htm
local function Localization()
if far.lang == "Russian" then
return {
Title = "Календарь";
DaysOfWeek = { "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс" };
Months = { "&Январь", "&Февраль", "&Март", "&Апрель", "Ма&й", "И&юнь", "Ию&ль", "Ав&густ", "&Сентябрь", "&Октябрь", "&Ноябрь", "&Декабрь" };
Year = '&Г:'; Month = '&М:'; DateFormat = '&Ф:'; DayWeekNumFmt = '&Н:'; FormattedDate = '&Д:';
Today = "&Сегодня"; Choose = '&Выберите:'; Jump = "&Прыг"; Insert = "Вставить"; Copy = "&Копировать";
}
else
return {
Title = "Calendar";
DaysOfWeek = { "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su" };
Months = { "&January", "&February", "&March", "&April", "Ma&y", "Ju&ne", "Ju&ly", "Au&gust", "&September", "&October", "&November", "&December" };
Year = '&Y:'; Month = '&M:'; DateFormat = '&F:'; DayWeekNumFmt = '&N:'; FormattedDate = '&D:';
Today = "&Today"; Choose = '&Select:'; Jump = "&Jump"; Insert = "Insert"; Copy = "&Copy";
}
end
end
local F = far.Flags
local date = require("date")
local fmt = string.format
local band = bit.band
local function ExecCalendar()
local Settings = mf.mload("dimfish", "Calendar") or { Format = 1, Weeks = 1 }
local Text
local today = date()
local dt = date()
local tableSelected
local isRendering = false
local ComboMonths = {} for i = 1, 12 do ComboMonths[i] = { Text = Localization().Months[i] } end
local ComboFormats = {} for i = 1, #Formats do ComboFormats[i] = { Text = Formats[i] } end
local ComboWeeks = {} for i = 1, #Weeks do ComboWeeks[i] = { Text = Weeks[i] } end
local I = {}
local ID = {}
local CF = {}
I[#I + 1] = { F.DI_DOUBLEBOX, 3, 1, 32, 19, 0, 0, 0, 0, Localization().Title }
I[#I + 1] = { F.DI_BUTTON , 6, 2, 0, 2, 0, 0, 0, F.DIF_BTNNOCLOSE + F.DIF_NOBRACKETS, "Ctrl→" }
ID.yearInc = #I
I[#I + 1] = { F.DI_TEXT , 5, 3, 0, 3, 0, 0, 0, 0, Localization().Year }
I[#I + 1] = { F.DI_FIXEDIT , 7, 3, 11, 3, 0, 0, "9999", F.DIF_MASKEDIT, "" }
ID.year = #I
I[#I + 1] = { F.DI_BUTTON , 6, 4, 0, 4, 0, 0, 0, F.DIF_BTNNOCLOSE + F.DIF_NOBRACKETS, "Ctrl←" }
ID.yearDec = #I
I[#I + 1] = { F.DI_BUTTON , 14, 2, 0, 2, 0, 0, 0, F.DIF_BTNNOCLOSE + F.DIF_NOBRACKETS, "Ctrl↑" }
ID.monthDec = #I
I[#I + 1] = { F.DI_TEXT , 13, 3, 0, 3, 0, 0, 0, 0, Localization().Month }
-- FAR has Polish localization; Polish has “październik” (11 chars). Note: e.g. Moroccan Arabic has a 15-char month name
I[#I + 1] = { F.DI_COMBOBOX, 15, 3, 29, 5, ComboMonths, 0, 0, F.DIF_DROPDOWNLIST + F.DIF_LISTNOAMPERSAND + F.DIF_LISTAUTOHIGHLIGHT, "" }
ID.month = #I
I[#I + 1] = { F.DI_BUTTON , 14, 4, 0, 4, 0, 0, 0, F.DIF_BTNNOCLOSE + F.DIF_NOBRACKETS, "Ctrl↓" }
ID.monthInc = #I
I[#I + 1] = { F.DI_TEXT , 20, 4, 30, 4, 0, 0, 0, F.DIF_RIGHTTEXT, Localization().Choose }
local row = 5
for d = 1, 7 do -- TODO make them buttons which jump to the date right below
I[#I + 1] = { F.DI_TEXT, d * 4 + 1, row, 0, row, 0, 0, 0, d > 5 and F.DIF_DISABLE or 0, Localization().DaysOfWeek[d] }
if d > 5 then
CF[#I] = Colors.Weekend
end
end
ID.table = #I
for w = 0, 5 do
for d = 1, 7 do
I[#I + 1] = { F.DI_TEXT, d * 4, row + 1 + w, 0, row + 1 + w, 0, 0, 0, 0, "" }
if d > 5 then
CF[#I] = Colors.Weekend
end
end
end
I[#I + 1] = { F.DI_USERCONTROL, 4, row + 1, 31, row + 6, 0, 0, 0, F.DIF_FOCUS }
ID.userControl = #I
I[#I + 1] = { F.DI_TEXT , 5, 13, 0, 13, 0, 0, 0, 0, Localization().DateFormat }
I[#I + 1] = { F.DI_COMBOBOX, 7, 13, 15, 13, ComboFormats, 0, 0, F.DIF_DROPDOWNLIST, "" }
ID.format = #I
I[#I + 1] = { F.DI_TEXT , 18, 13, 0, 13, 0, 0, 0, 0, Localization().DayWeekNumFmt }
I[#I + 1] = { F.DI_COMBOBOX, 20, 13, 29, 13, ComboWeeks, 0, 0, F.DIF_DROPDOWNLIST, "" }
ID.weeks = #I
I[#I + 1] = { F.DI_TEXT , 5, 15, 0, 15, 0, 0, 0, 0, Localization().FormattedDate }
I[#I + 1] = { F.DI_EDIT , 7, 15, 18, 15, 0, 0, 0, F.DIF_SELECTONENTRY, "" }
ID.textDate = #I
I[#I + 1] = { F.DI_TEXT , 20, 15, 29, 15, 0, 0, 0, 0, "" }
ID.textNums = #I
I[#I + 1] = { F.DI_BUTTON , 7, 16, 17, 16, 0, 0, 0, F.DIF_BTNNOCLOSE, Localization().Jump }
ID.jump = #I
I[#I + 1] = { F.DI_BUTTON , 20, 16, 29, 16, 0, 0, 0, F.DIF_BTNNOCLOSE, Localization().Today }
ID.today = #I
I[#I + 1] = { F.DI_TEXT , 0, 17, 0, 16, 0, 0, 0, F.DIF_SEPARATOR, "" }
I[#I + 1] = { F.DI_BUTTON , 0, 18, 0, 17, 0, 0, 0, F.DIF_CENTERGROUP + F.DIF_DEFAULTBUTTON, Localization().Insert }
ID.insert = #I
I[#I + 1] = { F.DI_BUTTON , 0, 18, 0, 17, 0, 0, 0, F.DIF_CENTERGROUP , Localization().Copy }
ID.copyDate = #I
CF[ID.yearInc] = Colors.Disabled
CF[ID.yearDec] = Colors.Disabled
CF[ID.monthInc] = Colors.Disabled
CF[ID.monthDec] = Colors.Disabled
CF[ID.textDate] = Colors.Selected
local function GetDateText(hDlg)
return far.SendDlgMessage(hDlg, "DM_GETTEXT", ID.textDate, nil)
end
-- Not all formats are supported, see https://github.com/LuaDist/luadate/blob ... te.doc.htm
local function GetDateObj(hDlg) -- nil on failure
local ok, dateObj = pcall(date, GetDateText(hDlg))
return ok and dateObj or nil
end
local function UpdateControlsState(hDlg)
far.SendDlgMessage(hDlg, "DM_ENABLE", ID.jump, GetDateObj(hDlg) and 1 or 0)
end
local function Redraw(hDlg)
isRendering = true
far.SendDlgMessage(hDlg, "DM_ENABLEREDRAW", 0)
far.SendDlgMessage(hDlg, "DM_SETTEXT", ID.year, dt:fmt("%Y"))
far.SendDlgMessage(hDlg, "DM_LISTSETCURPOS", ID.month, { SelectPos = dt:getmonth() })
local day = date(dt:getyear(), dt:getmonth(), 1)
day:adddays(-(day:getisoweekday() == 1 and 7 or day:getisoweekday() - 1))
for w = 0, 5 do
for d = 1, 7 do
local dayFormat = " %2s "
local currentId = w * 7 + d
local id = ID.table + currentId
if day:getmonth() == dt:getmonth() and day:getday() == dt:getday() then
CF[id] = Colors.Selected
dayFormat = "[%2s]"
tableSelected = currentId
elseif day:getyear() == today:getyear() and day:getmonth() == today:getmonth() and day:getday() == today:getday() then
CF[id] = Colors.Today
elseif day:getmonth() ~= dt:getmonth() then
CF[id] = Colors.Disabled
elseif d > 5 then
CF[id] = Colors.Weekend
else
CF[id] = Colors.Normal
end
far.SendDlgMessage(hDlg, "DM_ENABLE", id, day:getmonth() == dt:getmonth() and 1 or 0)
far.SendDlgMessage(hDlg, "DM_SETTEXT", id, fmt(dayFormat, day:getday()))
day:adddays(1)
end
end
far.SendDlgMessage(hDlg, "DM_LISTSETCURPOS", ID.format, { SelectPos = Settings.Format })
far.SendDlgMessage(hDlg, "DM_LISTSETCURPOS", ID.weeks, { SelectPos = Settings.Weeks })
far.SendDlgMessage(hDlg, "DM_SETTEXT", ID.textDate, dt:fmt(Formats[Settings.Format]))
far.SendDlgMessage(hDlg, "DM_SETTEXT", ID.textNums, dt:fmt(Weeks[Settings.Weeks]))
UpdateControlsState(hDlg)
far.SendDlgMessage(hDlg, "DM_ENABLEREDRAW", 1)
isRendering = false
end
local function GetItemColor(hDlg, Param1, Color)
local _ = hDlg -- suppress the "unused argument" warning
if Param1 == ID.month or Param1 == ID.format or Param1 == ID.weeks then
Color[3] = Color[1]
return Color
elseif CF[Param1] then
Color[1].ForegroundColor = CF[Param1]
return Color
end
end
-- Not all formats are supported, see https://github.com/LuaDist/luadate/blob ... te.doc.htm
local function JumpToDate(hDlg)
local dateObj = GetDateObj(hDlg)
dt = dateObj or dt
return dateObj and true or false
end
local function DlgProc(hDlg, Msg, Param1, Param2)
if Msg == F.DN_CTLCOLORDLGITEM then
return GetItemColor(hDlg, Param1, Param2)
elseif isRendering then
return
elseif Msg == F.DN_INITDIALOG then
Redraw(hDlg)
elseif Param1 == ID.insert then
Text = GetDateText(hDlg)
elseif Param1 == -1 then
Text = ""
elseif Msg == F.DN_EDITCHANGE then
if Param1 == ID.year then
local selY = tonumber(far.SendDlgMessage(hDlg, "DM_GETTEXT", Param1, nil))
if selY ~= dt:getyear() then
dt:setyear(selY)
Redraw(hDlg)
end
elseif Param1 == ID.month then
local selM = (far.SendDlgMessage(hDlg, "DM_LISTGETCURPOS", Param1, nil)).SelectPos
if selM ~= dt:getmonth() then
dt:setmonth(selM)
Redraw(hDlg)
end
elseif Param1 == ID.format then
Settings.Format = (far.SendDlgMessage(hDlg, "DM_LISTGETCURPOS", Param1, nil)).SelectPos
mf.msave("dimfish", "Calendar", Settings)
Redraw(hDlg)
elseif Param1 == ID.weeks then
Settings.Weeks = (far.SendDlgMessage(hDlg, "DM_LISTGETCURPOS", Param1, nil)).SelectPos
mf.msave("dimfish", "Calendar", Settings)
Redraw(hDlg)
elseif Param1 == ID.textDate then
UpdateControlsState(hDlg)
end
elseif Msg == F.DN_BTNCLICK then
if Param1 == ID.yearDec then
dt:addyears(-1)
elseif Param1 == ID.yearInc then
dt:addyears(1)
elseif Param1 == ID.monthDec then
dt:addmonths(-1)
elseif Param1 == ID.monthInc then
dt:addmonths(1)
elseif Param1 == ID.jump then
JumpToDate(hDlg)
elseif Param1 == ID.today then
dt = date()
elseif Param1 == ID.copyDate then
far.CopyToClipboard(GetDateText(hDlg))
Text = "" -- do not print
end
Redraw(hDlg)
elseif Msg == F.DN_CONTROLINPUT then
local VK = { Enter = 13; Left = 37; Up = 38; Right = 39; Down = 40; }
local leftOrRightCtrl = 0x0008 + 0x0004 -- TODO fix the check so that it doesn't match e.g. CtrlAlt
if Param1 ~= ID.month and Param1 ~= ID.format and Param1 ~= ID.weeks and
Param2.ControlKeyState and band(Param2.ControlKeyState, leftOrRightCtrl) ~= 0 then
if Param2.VirtualKeyCode == VK.Left then
dt:addyears(-1)
elseif Param2.VirtualKeyCode == VK.Up then
dt:addmonths(-1)
elseif Param2.VirtualKeyCode == VK.Right then
dt:addyears(1)
elseif Param2.VirtualKeyCode == VK.Down then
dt:addmonths(1)
end
Redraw(hDlg)
elseif Param1 == ID.userControl then
if Param2.VirtualKeyCode == VK.Left then
dt:adddays(-1)
elseif Param2.VirtualKeyCode == VK.Up then
dt:adddays(-7)
elseif Param2.VirtualKeyCode == VK.Right then
dt:adddays(1)
elseif Param2.VirtualKeyCode == VK.Down then
dt:adddays(7)
elseif Param2.ButtonState == 1 then
dt:adddays(math.floor(Param2.MousePositionX / 4) + Param2.MousePositionY * 7 + 1 - tableSelected)
end
Redraw(hDlg)
elseif Param1 == ID.textDate and Param2.VirtualKeyCode == VK.Enter then
if JumpToDate(hDlg) then
Redraw(hDlg)
end
return true -- don't pass the keypress to the standard handler
end
end
end
local guid = win.Uuid("06a13b89-3fec-46a2-be11-a50b68ceaa56")
far.Dialog(guid, -1, -1, 36, 21, nil, I, nil, DlgProc)
if Text then print(Text) end
end
Macro {
area = "Common"; key = Key; description = Localization().Title; flags = "";
action = ExecCalendar;
}