RetroArch/deps/libui/win32/datetimepicker.cpp
2017-07-03 07:27:09 +02:00

199 lines
5.8 KiB
C++

/* 22 may 2015 */
#include "uipriv_windows.hpp"
struct uiDateTimePicker {
uiWindowsControl c;
HWND hwnd;
};
#ifndef DTM_GETIDEALSIZE
#define DTM_GETIDEALSIZE 0x100f
#endif
/* utility functions */
#define GLI(what, buf, n) GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, what, buf, n)
/* The real date/time picker does a manual replacement of "yy" with "yyyy" for DTS_SHORTDATECENTURYFORMAT.
* Because we're also duplicating its functionality (see below), we have to do it too. */
static WCHAR *expandYear(WCHAR *dts, int n)
{
WCHAR *p = NULL;
int ny = 0;
/* allocate more than we need to be safe */
WCHAR *out = (WCHAR *) uiAlloc((n * 3) * sizeof (WCHAR), "WCHAR[]");
WCHAR *q = out;
for (p = dts; *p != L'\0'; p++)
{
/* first, if the current character is a y, increment the number of consecutive ys
* otherwise, stop counting, and if there were only two, add two more to make four */
if (*p != L'y') {
if (ny == 2) {
*q++ = L'y';
*q++ = L'y';
}
ny = 0;
} else
ny++;
/* next, handle quoted blocks
* we do this AFTER the above so yy'abc' becomes yyyy'abc' and not yy'abc'yy
* this handles the case of 'a''b' elegantly as well */
if (*p == L'\'') {
/* copy the opening quote */
*q++ = *p;
/* copy the contents */
for (;;) {
p++;
if (*p == L'\'')
break;
if (*p == L'\0')
implbug("unterminated quote in system-provided locale date string in expandYear()");
*q++ = *p;
}
/* and fall through to copy the closing quote */
}
/* copy the current character */
*q++ = *p;
}
/* handle trailing yy */
if (ny == 2) {
*q++ = L'y';
*q++ = L'y';
}
*q++ = L'\0';
return out;
}
/* Windows has no combined date/time prebuilt constant;
* we have to build the format string ourselves
* TODO use a default format if one fails */
static void setDateTimeFormat(HWND hwnd)
{
WCHAR *unexpandedDate, *date;
WCHAR *time;
WCHAR *datetime;
int ntime;
int ndate = GLI(LOCALE_SSHORTDATE, NULL, 0);
if (ndate == 0)
logLastError(L"error getting date string length");
date = (WCHAR *) uiAlloc(ndate * sizeof (WCHAR), "WCHAR[]");
if (GLI(LOCALE_SSHORTDATE, date, ndate) == 0)
logLastError(L"error geting date string");
unexpandedDate = date; /* so we can free it */
date = expandYear(unexpandedDate, ndate);
uiFree(unexpandedDate);
ntime = GLI(LOCALE_STIMEFORMAT, NULL, 0);
if (ndate == 0)
logLastError(L"error getting time string length");
time = (WCHAR *) uiAlloc(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");
uiFree(datetime);
uiFree(time);
uiFree(date);
}
/* control implementation */
static void uiDateTimePickerDestroy(uiControl *c)
{
uiDateTimePicker *d = uiDateTimePicker(c);
uiWindowsUnregisterReceiveWM_WININICHANGE(d->hwnd);
uiWindowsEnsureDestroyWindow(d->hwnd);
uiFreeControl(uiControl(d));
}
uiWindowsControlAllDefaultsExceptDestroy(uiDateTimePicker)
/* the height returned from DTM_GETIDEALSIZE is unreliable; see http://stackoverflow.com/questions/30626549/what-is-the-proper-use-of-dtm-getidealsize-treating-the-returned-size-as-pixels
* from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing */
#define entryHeight 14
static void uiDateTimePickerMinimumSize(uiWindowsControl *c, int *width, int *height)
{
SIZE s;
uiWindowsSizing sizing;
uiDateTimePicker *d = uiDateTimePicker(c);
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 uiDateTimePicker *finishNewDateTimePicker(DWORD style)
{
uiDateTimePicker *d;
uiWindowsNewControl(uiDateTimePicker, d);
d->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE,
DATETIMEPICK_CLASSW, L"",
style | WS_TABSTOP,
hInstance, NULL,
TRUE);
/* automatically update date/time format when user changes locale settings
* for the standard styles, this is in the date-time picker itself
* for our date/time mode, we do it in a subclass assigned in uiNewDateTimePicker() */
uiWindowsRegisterReceiveWM_WININICHANGE(d->hwnd);
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:
// we can optimize this by only doing it when the real date/time picker does it
// unfortunately, I don't know when that is :/
// hopefully this won't hurt
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 = 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");
/* TODO set a suitable default in this case */
return d;
}
uiDateTimePicker *uiNewDatePicker(void)
{
return finishNewDateTimePicker(DTS_SHORTDATECENTURYFORMAT);
}
uiDateTimePicker *uiNewTimePicker(void)
{
return finishNewDateTimePicker(DTS_TIMEFORMAT);
}