Code: Select all
;################################################################
;################################################################
;################ DATEPICKER - H ###############
;################################################################
;################################################################
; A simple popup calendar that has US Holidays in bold font.
; Original calendar-with-bolded-dates code by PhiLho
; https://www.autohotkey.com/board/topic/13441-monthcal-setdaystate/
; IsHoliday function by TibBit.
; https://www.autohotkey.com/boards/viewtopic.php?t=77312
; The two were integrated by Just Me.
; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=117399&p
; Then kunkel321 added his customizations.
; Pressing "h" lists shown holiday names.
; - Hotstrings: ;d0 (semicolon, letter d, zero) yeilds today
; - Typing ;dn enters date that is n-days in the future, where n is
; - a number from 1-9. ;ddn yields days in the past, 1-9.
;################################################################
DateToolHandler:
+!d:: ; Alt+Shift+D
;=========== Popup calendar ===============
WinGetPos, X, Y, W, H, A ; "A" to get the active window's pos.
X := X + (W * 0.30) ; Use these with GUI Show, below.
Y := Y + (H * 0.04) ; Position near top/left of current window.
IniRead, ShowMulti, DateFormat.txt, DateTool, Rows, 3
IniRead, calColor, DateFormat.txt, DateTool, Color, Gray
IniRead, FontColor, DateFormat.txt, DateTool, FontColor, Black
;~ ; Following lines are specific to Steve's setup.
;~ iniread, calColor, S:\AutoHotkey\MasterScript\WayText\wtFiles\Settings.ini, MainSettings, GUIColor
;~ if (strLen(calColor) > 8)
;~ calColor := subStr(calColor, strLen(calColor) - 5, 6)
;~ iniread, FontColor, S:\AutoHotkey\MasterScript\WayText\wtFiles\Settings.ini, MainSettings, FontColor
;~ if (strLen(FontColor) > 8)
;~ FontColor := subStr(FontColor, strLen(FontColor) - 5, 6)
if (ShowMulti = 1) ; Calendar will display one month.
{
MultiButton = Multi
FaveDate := A_Now
}
else ; Calendar will show three months, vertically
{
MultiButton = Single
FaveDate := A_Now
EnvAdd, FaveDate, -30, days ; Start 30 in past, so last month appears too.
}
tics:=0 ; for below double-click code
global Year, Month, MonthCount
OnMessage(0x4E, "WM_NOTIFY")
Gui, dp:Destroy ; Clear the DatePicked variable to prevent an error.
Gui, dp:color, %calColor%,
Gui, dp:-MinimizeBox +LastFound +Toolwindow
Gui, dp:Font, s10 c%FontColor%,
Gui, dp:Add, MonthCal, R%ShowMulti% +0x01 +AltSubmit vDatePicked gDblClick, %FaveDate%
if (ShowMulti = 1) { ; Single month view
HolidayList() ; Show default list of holidays.
caltxtloops := 2 ; Show this many holidays under calendar.
}
Else { ; Muli-month view
HolidayList(60) ; Only show this month+ holidays.
caltxtloops := 5 ; Show this many holidays.
}
HoliArr := StrSplit(theseHolis, "`n")
Loop, %caltxtloops% {
If (HoliArr[A_Index]) ; might not show if date+holidate is >= Len(33)
Gui, dp:Add, Text, y+-1 w208 vHoliTxt%A_Index%, % HoliArr[A_Index]
else
Gui, dp:Add, Text, y+-1 w208 vHoliTxt%A_Index%,
}
Gui, dp:Add, Button, y+-1 x13 w50 h20 Section , Submit
Gui, dp:Add, Button, ys X+2 w50 h20 , Format
Gui, dp:Add, Button, ys X+2 w50 h20 , %MultiButton%
Gui, dp:Add, Button, ys X+2 w50 h20 , Cancel
Gui, dp:Show, x%X% y%Y%, DatePicker-H (Alt+Shift+D)
Gui, dp:+LastFound
If (ShowMulti != 1) ; More than one month being shown. FaveDate was 30 days ago,
GuiControl, dp:, DatePicked, %A_Now% ; so move selection to today.
return
#IfWinActive, DatePicker-H ; If name is changed at Gui, Show; change here too.
; Causes Enter key to enter date; but only if DatePicker is open.
; SetTitleMatchMode might need to be changed for this to work.
$Enter::
ControlGetFocus, FocusedControl, A
If (FocusedControl = "SysMonthCal321")
gosub, dpButtonSubmit ; Only if the calendar (not buttons) focused.
Else
Send {Enter} ; Otherwise do normal Enter key behavior.
Return
h::
gosub, Holidays
return
Esc::
Gui, dp:Destroy
return
#IfWinActive
DblClick:
Gui, dp:submit, noHide
; Below several lines adapted from TidBit's 2015 Get A Date.
If (A_GuiControlEvent=1 && (A_TickCount-tics)<=DllCall("GetDoubleClickTime") )
gosub, dpButtonSubmit
Else If (A_GuiControlEvent=1)
tics:=A_TickCount
Else If (A_GuiControlEvent="Normal") {
HolidayList()
HoliArr := StrSplit(theseHolis, "`n")
Loop, 5 {
If (HoliArr[A_Index]) ; might not show if date+holidate is >= Len(33)
GuiControl,, HoliTxt%A_Index%, % HoliArr[A_Index]
else
GuiControl,, HoliTxt%A_Index%,
}
}
Return
Holidays:
HolidayList()
MsgBox Currently shown holidays...`n`n%theseHolis%
Return
dpButtonSubmit:
Gui, dp:Submit
gosub, DateFormat
Gui, dp:Destroy
return
dpButtonFormat:
Run, DateFormat.txt
Gui, dp:Destroy
return
dpButtonMulti:
IniWrite, 3, DateFormat.txt, DateTool, Rows
gosub, DateToolHandler
return
dpButtonSingle:
IniWrite, 1, DateFormat.txt, DateTool, Rows
gosub, DateToolHandler
return
dpButtonCancel:
dpGuiClose:
Gui, dp:Destroy
DatePicked =
Return
;=========== Hotstrings ===============
:?*:;dd9:: ; For entering dates.
:?*:;dd8:: ; all start with {semicolon}
:?*:;dd7:: ; ddn = in the past, n days.
:?*:;dd6:: ; dn = future by n days.
:?*:;dd5::
:?*:;dd4::
:?*:;dd3::
:?*:;dd2::
:?*:;dd1:: ; yesterday
:?*:;d0:: ; ;d0 = today
:?*:;d1:: ; tomorrow
:?*:;d2:: ; day after tomorrow
:?*:;d3:: ; etc.
:?*:;d4::
:?*:;d5::
:?*:;d6::
:?*:;d7::
:?*:;d8::
:?*:;d9::
StringReplace, nOffset, A_ThisHotkey, :?*:;d
StringReplace, nOffset, nOffset, d,- ; This first part condensed with help from forum members :)
DatePicked += %nOffset%, days ; Puts offset into date format.
DateFormat:
IniRead, dateFormat, DateFormat.txt, DateTool, Format, M-d-yyyy
FormatTime, MyDate, %DatePicked%, %dateFormat%
SendInput, %MyDate% ; This types out the date.
;================= Tooltip Section ====================
dateOffset := DatePicked
vNow := subStr(A_Now, 1, 8)
EnvSub, dateOffset, vNow, days
WdayArr := [6,5,4,3,2,1,0]
daysTillSat := WdayArr[A_Wday]
fromSat := -1 * (daysTillSat - dateOffset)
;MsgBox, datePicked is: %DatePicked% `n now is %vNow% `n`nfromSat is: %fromSat%`n dateOffset is: %dateOffset% `n daysTillSat is: %daysTillSat%
; Use Saturday this week as a constant in determining the following suffixes.
MySuffix =
if (fromSat > 0)
MySuffix := " next week"
if (fromSat > 8)
MySuffix := ", week after next"
if (fromSat > 15)
MySuffix := " in three weeks"
if (fromSat > 22)
MySuffix := " in four weeks"
if (fromSat > 29)
MySuffix := " in five weeks"
if (fromSat > 36)
MySuffix := " in six+ weeks"
if (fromSat < -7)
MySuffix := " last week"
if (fromSat < -14)
MySuffix := ", week before last"
if (fromSat < -21)
MySuffix := " three weeks ago"
if (fromSat < -28)
MySuffix := " four weeks ago"
if (fromSat < -35)
MySuffix := " five weeks ago"
if (fromSat < -42)
MySuffix := " six+ weeks ago"
myToolTipX := A_CaretX + 10 ; For position of tooltip.
myToolTipY := A_CaretY + 25
DatePicked += 0, days ; Puts it in proper date format.
HoliTip := (isHoliday(DatePicked) <> "")? isHoliday(DatePicked) . " ---> " : ""
FormatTime, DayOfWeek, %DatePicked%, dddd
WeekEndTip := (DayOfWeek = "Saturday") || (DayOfWeek = "Sunday")? "Weekend ---> " : ""
ToolTip, %HoliTip%%WeekEndTip%%DayOfWeek%%MySuffix%, %myToolTipX%, %myToolTipY%
SetTimer, RemoveToolTip, 2000 ; Number of milliseconds for the tooltip to show.
DatePicked = ; Reset to nothing.
nOffset =
return
RemoveToolTip:
SetTimer, RemoveToolTip, Off
ToolTip
return ;========== End of Tooltip section ================
; ---------------------------------------------------------------------------------
HolidayList(offset := 30)
{
global theseHolis =
loopNumber := (MonthCount - 2) * 31
Month := SubStr("0" . Month, strlen(Month), 2)
thisLoopDate := Year . Month . "01000000"
thisLoopDate += %offset%, D
loop, %loopNumber% {
HoliThisLoop := isHoliday(thisLoopDate)
;MsgBox, loop %A_index% `nhisLoopDate is: %thisLoopDate% `nHoliThisLoop is: %HoliThisLoop%
If (HoliThisLoop = "") || (HoliThisLoop = "-1") || (HoliThisLoop = " ") {
thisLoopDate += 1, D
continue
}
FormatTime, thisLoopMD, %thisLoopDate%, MM-dd
thisHoli := isHoliday(thisLoopDate)
theseHolis .= thisLoopMD . "`t" . thisHoli . "`n"
thisLoopDate += 1, D
;MsgBox, thisLoopMD is: %thisLoopMD%
}
theseHolis := SubStr(theseHolis, 1, StrLen(theseHolis)-1)
Return, theseHolis
}
; ---------------------------------------------------------------------------------
; Process the MCN_GETDAYSTATE notification
; https://learn.microsoft.com/en-us/windows/win32/controls/mcn-getdaystate
; The first notification is sent while the GuiControl is created.
; ---------------------------------------------------------------------------------
WM_NOTIFY(W, L, M, H) {
; This function, and LDOM(), written by user: Just Me.
Static OffHwnd := 0
, OffCode := OffHwnd + (A_PtrSize * 2)
, OffYear := OffCode + A_PtrSize
, OffMonth := OffYear + 2
, OffCount := OffMonth + 14
, OffArray := OffCount + A_PtrSize
If (NumGet(L + OffCode, "Int") = -747) { ; MCN_GETDAYSTATE
Year := NumGet(L + OffYear, "UShort")
Month := NumGet(L + OffMonth, "UShort")
MonthCount := NumGet(L + OffCount, "Int")
Addr := NumGet(L + OffArray, "UPtr")
CurrentDate := Format("{:}{:02}01000000", Year, Month)
Loop, %MonthCount% {
LastDay := LDOM(CurrentDate)
BoldDays := 0
Loop, %LastDay% {
I := A_Index - 1
If (isHoliday(CurrentDate) <> "") {
BoldDays |= 1 << I
}
CurrentDate += 1, D
}
NumPut(BoldDays, Addr + 0, "UInt")
Addr += 4
CurrentDate := SubStr(CurrentDate, 1, 6) . "01000000"
}
Return 1
}
}
; ---------------------------------------------------------------------------------
LDOM(Date) { ; get the number of last day of the month
Date += 31, D
Date := SubStr(Date, 1, 6)
Date += -1, D
Return (SubStr(Date, 7, 2) + 0)
}
; ---------------------------------------------------------------------------------
isHoliday(yyyymmddhhmiss:="", businessOnly:=0, stopAtFirst:=0)
; This function written by user: Tidbit.
{
tstamp:=(yyyymmddhhmiss="") ? A_Now : yyyymmddhhmiss
; not a valid timestamp
if (strLen(yyyymmddhhmiss)!=14)
return -1
if yyyymmddhhmiss is not number
return -2
date={}
out:="" ; return a string of all possible events today
; grab more data than needed. safety first.
formatTime, ttt, %tstamp%, yyyy|MM|dd|MMMM|dddd
date:=strSplit(ttt, "|")
formatTime, ttt, %tstamp%, YWeek
date.push(substr(ttt, 5))
formatTime, ttt, %tstamp%, YDay
date.push(ttt)
date:={year: date[1], mon: date[2], day: date[3], monN: date[4], dayN: date[5], dayY: date[7], weekY: date[6]}
; Leap-year
isLeap:=0
if ((mod(date.year, 4)=0 && mod(date.year, 100)!=0) || mod(date.year, 400)=0)
isLeap:=1
; Easter... Amazing. Thank you, "Nature" Journal - 1876
a:=mod(date.year, 19), b:=floor(date.year/100), c:=mod(date.year, 100)
d:=floor(b/4), e:=mod(b, 4), f:=floor((b+8)/25)
g:=floor((b-f+1)/3), h:=mod(((19*a)+b-d-g+15), 30), i:=floor(c/4),
k:=mod(c, 4), l:=mod((32+(2*e)+(2*i)-h-k), 7), m:=floor((a+(11*h)+(22*L))/451)
emonth:=format("{:02}", (floor((h+l+(7*m)+114)/31)))
eday:=format("{:02}", mod((h+l-(7*m)+114), 31)+1)
; single space delimited, strictly
; ["month day-day dayName", "Day Text", federal/business day]
; if "dayName" = "absolute", "month" becomes "isLeapYear", and "day-day" is a number between 1-366
dates:=[["01 01", "New Year's Day", 1]
, ["01 15-21 Monday", "Martin Luther King Jr. Day", 1]
, ["02 15-21 Monday", "Presidents Day", 1]
, [ emonth " " eday, "Easter Sunday", 0]
; , [ emonth " " eday+1, "Easter Monday", 1]
, ["05 25-31 Monday", "Memorial Day", 1]
, ["07 04", "Fourth of July", 1]
, ["08 25", "Wedding Anniversary", 0]
, ["09 01-07 Monday", "Labor Day", 1]
; , ["10 08-14 Monday", "Columbus Day", 1]
, ["11 11", "Veterans Day", 1]
, ["11 22-28 Thursday", "Thanksgiving Day", 1]
; , ["12 24", "Christmas Eve", 0]
, ["12 25", "Christmas Day", 1]
, ["02 14", "Valentines Day", 0]
, ["05 08-14 Sunday", "Mother's Day", 0]
, ["06 15-21 Sunday", "Father's Day", 0]
, ["03 17", "St. Patrick's Day", 0]
, ["10 31", "Halloween", 0]
; , ["01 23", "0123 Day", 0]
, ["03 14", "Pi Day", 0]
; , ["06 28", "Tau Day", 0]
, ["06 20 Monday", "Juneteenth", 1]
, ["06 19 Monday", "Juneteenth", 1]
, ["06 19 Tuesday", "Juneteenth", 1]
, ["06 19 Wednesday", "Juneteenth", 1]
, ["06 19 Thursday", "Juneteenth", 1]
, ["06 19 Friday", "Juneteenth", 1]
, ["06 18 Friday", "Juneteenth", 1]] ; Added extra ] since below is commented-out.
; , ["05 04", "May the fourth be with you", 0]
; , ["11 11", "1111 Day", 0]
; , ["0 182 absolute", "50% through the year", 0]
; , ["1 183 absolute", "50% through the year", 0]]
stop:=0
for k, day in dates
{
if (day[3]=0 && businessOnly=1)
continue
holiday:=day[2] ; give it a nicer name
stamp:=strSplit(day[1], " ")
range:=strSplit(stamp[2], "-")
range[2]:=(range[2]="") ? range[1] : range[2]
if (stamp[3]!="absolute")
{
; set a temp var to blank if a weekday wasn't specified.
; Otherwise check if the specified day is today
ttt:=(stamp[3]="") ? "" : date.dayN
isBetween:=(date.day>=range[1] && date.day<=range[2]) ? 1 : 0
if (date.mon=stamp[1] && isBetween=1 && ttt=stamp[3])
out.=holiday "`n", stop:=1
}
else
{
if (isLeap=stamp[1] && date.dayY=stamp[2])
out.=holiday "`n", stop:=1
}
if (stopAtFirst=1 && stop=1) ; sometimes you'll want to be a nanosecond faster.
return trim(out, "`r`n `t")
}
return trim(out, "`r`n `t")
}
;#########################################################################
;############# END OF DATEPICKER - H #####################################
;#########################################################################