Here is a more functional version of "DatePicker - H" as described above. This one is more functional because it stays in RAM and you activate it with
. The selected date gets typed into... Whatever. Press
while gui is shown to close it.
Code: Select all
#NoEnv ; For security
#SingleInstance force
#Persistent
; 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 TidBit.
; 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 Holiday names when pressing "h"
; Double-click a date, or press {Enter}, to type it.
; Calendar appears placed over active window.
; Waits for window to be active again before typing date.
!^+d:: ; Hotkey is Alt+Ctrl+Shift+D
;======== User Options ==========
PreferredFormat := "M-d-yyyy"
BorderColor := "Gray"
;================================
WinGetActiveTitle, myWinTarget
WinGetPos, X, Y, W, H, A
X := X + (W * 0.30)
Y := Y + (H * 0.04)
tics:=0 ; for below double-click code
global Year, Month, MonthCount
OnMessage(0x4E, "WM_NOTIFY")
Gui, Destroy
Gui, Color, %BorderColor%
Gui, -MinimizeBox +LastFound +Toolwindow
Gui, Add, MonthCal, R3 +0x01 +AltSubmit vDatePicked gDblClick
Gui, Show, x%X% y%Y%, DatePicker - H
Return
GuiClose:
GuiEscape:
Gui, Hide
Return
#IfWinActive, DatePicker - H
Enter::
gosub, SendDate
Return
; If you're editing this ahk file, and it is named "DatePicker - H",
; this will prevent you from typing 'h' and will drive you crazy.
h::
gosub, Holidays
return
Esc::
Gui, Destroy
return
#IfWinActive
DblClick:
Gui, submit, noHide
; Below several lines adapted from TidBit's 2015 Get A Date.
If (A_GuiControlEvent=1 && (A_TickCount-tics)<=DllCall("GetDoubleClickTime") )
gosub, SendDate
Else If (A_GuiControlEvent=1)
tics:=A_TickCount
Return
SendDate:
Gui, Submit
Gui, Destroy
FormatTime, MyDate, %DatePicked%, %PreferredFormat%
WinWaitActive, %myWinTarget%
SendInput, %MyDate%
Return
Holidays:
theseHolis =
loopNumber := (MonthCount - 2) * 31
Month := SubStr("0" . Month, strlen(Month), 2)
thisLoopDate := Year . Month . "01000000"
thisLoopDate += 30, D
loop, %loopNumber%
{
HoliThisLoop := isHoliday(thisLoopDate)
If (HoliThisLoop = "") || (HoliThisLoop = "-1") || (HoliThisLoop = " ")
{
EnvAdd, thisLoopDate, +1, days
continue
}
FormatTime, thisLoopMD, %thisLoopDate%, MMM-dd
thisHoli := isHoliday(thisLoopDate)
theseHolis .= thisLoopMD . "`t" . thisHoli . "`n"
thisLoopDate += 1, D
}
MsgBox Holidays currently displayed...`n`n%theseHolis%
return
; ------------------------------------------------------------------------------------
; 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) {
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)
}
; ------------------------------------------------------------------------
; Name: isHoliday; Author: tidbit; Created: Thursday, June 11, 2020
isHoliday(yyyymmddhhmiss:="", businessOnly:=0, stopAtFirst:=0)
{
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]
, ["05 25-31 Monday", "Memorial Day", 1]
, ["07 04", "Fourth of July", 1]
, ["09 01-07 Monday", "Labor Day", 1]
, ["11 11", "Veterans Day", 1]
, ["06 19", "Juneteenth", 1]
, ["11 22-28 Thursday", "Thanksgiving Day", 1]
, ["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]
, ["03 14", "Pi 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")
}