#include "uipriv_windows.hpp"
struct uiDateTimePicker {
uiWindowsControl c;
HWND hwnd;
void (*onChanged)(uiDateTimePicker *, void *);
void *onChangedData;
};
#define GLI(what, buf, n) GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, what, buf, n)
static WCHAR *expandYear(WCHAR *dts, int n)
{
WCHAR *out;
WCHAR *p, *q;
int ny = 0;
out = (WCHAR *) uiprivAlloc((n * 3) * sizeof (WCHAR), "WCHAR[]");
q = out;
for (p = dts; *p != L'\0'; p++) {
if (*p != L'y') {
if (ny == 2) {
*q++ = L'y';
*q++ = L'y';
}
ny = 0;
} else
ny++;
if (*p == L'\'') {
*q++ = *p;
for (;;) {
p++;
if (*p == L'\'')
break;
if (*p == L'\0')
uiprivImplBug("unterminated quote in system-provided locale date string in expandYear()");
*q++ = *p;
}
}
*q++ = *p;
}
if (ny == 2) {
*q++ = L'y';
*q++ = L'y';
}
*q++ = L'\0';
return out;
}
static void setDateTimeFormat(HWND hwnd)
{
WCHAR *unexpandedDate, *date;
WCHAR *time;
WCHAR *datetime;
int ndate, ntime;
ndate = GLI(LOCALE_SSHORTDATE, NULL, 0);
if (ndate == 0)
logLastError(L"error getting date string length");
date = (WCHAR *) uiprivAlloc(ndate * sizeof (WCHAR), "WCHAR[]");
if (GLI(LOCALE_SSHORTDATE, date, ndate) == 0)
logLastError(L"error geting date string");
unexpandedDate = date; date = expandYear(unexpandedDate, ndate);
uiprivFree(unexpandedDate);
ntime = GLI(LOCALE_STIMEFORMAT, NULL, 0);
if (ndate == 0)
logLastError(L"error getting time string length");
time = (WCHAR *) uiprivAlloc(ntime * sizeof (WCHAR), "WCHAR[]");
if (GLI(LOCALE_STIMEFORMAT, time, ntime) == 0)
logLastError(L"error geting time string");
datetime = strf(L"%s %s", date, time);
if (SendMessageW(hwnd, DTM_SETFORMAT, 0, (LPARAM) datetime) == 0)
logLastError(L"error applying format string to date/time picker");
uiprivFree(datetime);
uiprivFree(time);
uiprivFree(date);
}
static void uiDateTimePickerDestroy(uiControl *c)
{
uiDateTimePicker *d = uiDateTimePicker(c);
uiWindowsUnregisterReceiveWM_WININICHANGE(d->hwnd);
uiWindowsUnregisterWM_NOTIFYHandler(d->hwnd);
uiWindowsEnsureDestroyWindow(d->hwnd);
uiFreeControl(uiControl(d));
}
uiWindowsControlAllDefaultsExceptDestroy(uiDateTimePicker)
#define entryHeight 14
static void uiDateTimePickerMinimumSize(uiWindowsControl *c, int *width, int *height)
{
uiDateTimePicker *d = uiDateTimePicker(c);
SIZE s;
uiWindowsSizing sizing;
int y;
s.cx = 0;
s.cy = 0;
SendMessageW(d->hwnd, DTM_GETIDEALSIZE, 0, (LPARAM) (&s));
*width = s.cx;
y = entryHeight;
uiWindowsGetSizing(d->hwnd, &sizing);
uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &y);
*height = y;
}
static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
{
uiDateTimePicker *d = uiDateTimePicker(c);
if (nmhdr->code != DTN_DATETIMECHANGE)
return FALSE;
(*(d->onChanged))(d, d->onChangedData);
*lResult = 0;
return TRUE;
}
static void fromSystemTime(SYSTEMTIME *systime, struct tm *time)
{
ZeroMemory(time, sizeof (struct tm));
time->tm_sec = systime->wSecond;
time->tm_min = systime->wMinute;
time->tm_hour = systime->wHour;
time->tm_mday = systime->wDay;
time->tm_mon = systime->wMonth - 1;
time->tm_year = systime->wYear - 1900;
time->tm_wday = systime->wDayOfWeek;
time->tm_isdst = -1;
}
static void toSystemTime(const struct tm *time, SYSTEMTIME *systime)
{
ZeroMemory(systime, sizeof (SYSTEMTIME));
systime->wYear = time->tm_year + 1900;
systime->wMonth = time->tm_mon + 1;
systime->wDayOfWeek = time->tm_wday;
systime->wDay = time->tm_mday;
systime->wHour = time->tm_hour;
systime->wMinute = time->tm_min;
systime->wSecond = time->tm_sec;
}
static void defaultOnChanged(uiDateTimePicker *d, void *data)
{
}
void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time)
{
SYSTEMTIME systime;
if (SendMessageW(d->hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM) (&systime)) != GDT_VALID)
logLastError(L"error getting date and time");
fromSystemTime(&systime, time);
}
void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time)
{
SYSTEMTIME systime;
toSystemTime(time, &systime);
if (SendMessageW(d->hwnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) (&systime)) == 0)
logLastError(L"error setting date and time");
}
void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data)
{
d->onChanged = f;
d->onChangedData = data;
}
static uiDateTimePicker *finishNewDateTimePicker(DWORD style)
{
uiDateTimePicker *d;
uiWindowsNewControl(uiDateTimePicker, d);
d->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE,
DATETIMEPICK_CLASSW, L"",
style | WS_TABSTOP,
hInstance, NULL,
TRUE);
uiWindowsRegisterReceiveWM_WININICHANGE(d->hwnd);
uiWindowsRegisterWM_NOTIFYHandler(d->hwnd, onWM_NOTIFY, uiControl(d));
uiDateTimePickerOnChanged(d, defaultOnChanged, NULL);
return d;
}
static LRESULT CALLBACK datetimepickerSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg) {
case WM_WININICHANGE:
setDateTimeFormat(hwnd);
return 0;
case WM_NCDESTROY:
if (RemoveWindowSubclass(hwnd, datetimepickerSubProc, uIdSubclass) == FALSE)
logLastError(L"error removing date-time picker locale change handling subclass");
break;
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
uiDateTimePicker *uiNewDateTimePicker(void)
{
uiDateTimePicker *d;
d = finishNewDateTimePicker(0);
setDateTimeFormat(d->hwnd);
if (SetWindowSubclass(d->hwnd, datetimepickerSubProc, 0, (DWORD_PTR) d) == FALSE)
logLastError(L"error subclassing date-time-picker to assist in locale change handling");
return d;
}
uiDateTimePicker *uiNewDatePicker(void)
{
return finishNewDateTimePicker(DTS_SHORTDATECENTURYFORMAT);
}
uiDateTimePicker *uiNewTimePicker(void)
{
return finishNewDateTimePicker(DTS_TIMEFORMAT);
}