Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741/* * Date and time picker control * * Copyright 1998, 1999 Eric Kohl * Copyright 1999, 2000 Alex Priem <alexp@sci.kun.nl> * Copyright 2000 Chris Morgan <cmorgan@wpi.edu> * Copyright 2012 Owen Rudge for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * TODO: * -- DTS_APPCANPARSE * -- DTS_SHORTDATECENTURYFORMAT * -- DTN_FORMAT * -- DTN_FORMATQUERY * -- DTN_USERSTRING * -- DTN_WMKEYDOWN * -- FORMATCALLBACK */
#include <math.h>#include <string.h>#include <stdarg.h>#include <stdio.h>#include <limits.h>
#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winuser.h"#include "winnls.h"#include "commctrl.h"#include "comctl32.h"#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(datetime);
typedef struct{ HWND hwndSelf; HWND hMonthCal; HWND hwndNotify; HWND hUpdown; DWORD dwStyle; SYSTEMTIME date; BOOL dateValid; HWND hwndCheckbut; RECT rcClient; /* rect around the edge of the window */ RECT rcDraw; /* rect inside of the border */ RECT checkbox; /* checkbox allowing the control to be enabled/disabled */ RECT calbutton; /* button that toggles the dropdown of the monthcal control */ BOOL bCalDepressed; /* TRUE = cal button is depressed */ BOOL bDropdownEnabled; int select; WCHAR charsEntered[4]; int nCharsEntered; HFONT hFont; int nrFieldsAllocated; int nrFields; int haveFocus; int *fieldspec; RECT *fieldRect; int *buflen; WCHAR textbuf[256]; POINT monthcal_pos; int pendingUpdown;} DATETIME_INFO, *LPDATETIME_INFO;
/* in monthcal.c */extern int MONTHCAL_MonthLength(int month, int year);extern int MONTHCAL_CalculateDayOfWeek(SYSTEMTIME *date, BOOL inplace);
/* this list of defines is closely related to `allowedformatchars' defined * in datetime.c; the high nibble indicates the `base type' of the format * specifier. * Do not change without first reading DATETIME_UseFormat. * */
#define DT_END_FORMAT 0#define ONEDIGITDAY 0x01#define TWODIGITDAY 0x02#define THREECHARDAY 0x03#define FULLDAY 0x04#define ONEDIGIT12HOUR 0x11#define TWODIGIT12HOUR 0x12#define ONEDIGIT24HOUR 0x21#define TWODIGIT24HOUR 0x22#define ONEDIGITMINUTE 0x31#define TWODIGITMINUTE 0x32#define ONEDIGITMONTH 0x41#define TWODIGITMONTH 0x42#define THREECHARMONTH 0x43#define FULLMONTH 0x44#define ONEDIGITSECOND 0x51#define TWODIGITSECOND 0x52#define ONELETTERAMPM 0x61#define TWOLETTERAMPM 0x62#define ONEDIGITYEAR 0x71#define TWODIGITYEAR 0x72#define INVALIDFULLYEAR 0x73 /* FIXME - yyy is not valid - we'll treat it as yyyy */#define FULLYEAR 0x74#define FORMATCALLBACK 0x81 /* -> maximum of 0x80 callbacks possible */#define FORMATCALLMASK 0x80#define DT_STRING 0x0100
#define DTHT_DATEFIELD 0xff /* for hit-testing */
#define DTHT_NONE 0x1000#define DTHT_CHECKBOX 0x2000 /* these should end at '00' , to make */#define DTHT_MCPOPUP 0x3000 /* & DTHT_DATEFIELD 0 when DATETIME_KeyDown */#define DTHT_GOTFOCUS 0x4000 /* tests for date-fields */#define DTHT_NODATEMASK 0xf000 /* to mask check and drop down from others */
static BOOL DATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code);static BOOL DATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr);static const WCHAR allowedformatchars[] = L"dhHmMstyX";static const int maxrepetition [] = {4,2,2,2,4,2,2,4,-1};
/* valid date limits */#ifndef __REACTOS__static const SYSTEMTIME max_allowed_date = { .wYear = 9999, .wMonth = 12, .wDayOfWeek = 0, .wDay = 31 };static const SYSTEMTIME min_allowed_date = { .wYear = 1752, .wMonth = 9, .wDayOfWeek = 0, .wDay = 14 };#elsestatic const SYSTEMTIME max_allowed_date = { /*.wYear =*/ 9999, /*.wMonth =*/ 12, /*.wDayOfWeek =*/ 0, /*.wDay =*/ 31 };static const SYSTEMTIME min_allowed_date = { /*.wYear =*/ 1752, /*.wMonth =*/ 9, /*.wDayOfWeek =*/ 0, /*.wDay =*/ 14 };#endif
static DWORDDATETIME_GetSystemTime (const DATETIME_INFO *infoPtr, SYSTEMTIME *systime){ if (!systime) return GDT_NONE;
if ((infoPtr->dwStyle & DTS_SHOWNONE) && (SendMessageW (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0) == BST_UNCHECKED)) return GDT_NONE;
*systime = infoPtr->date;
return GDT_VALID;}
/* Checks value is within configured date range * * PARAMETERS * * [I] infoPtr : valid pointer to control data * [I] date : pointer to valid date data to check * * RETURN VALUE * * TRUE - date within configured range * FALSE - date is outside configured range */static BOOL DATETIME_IsDateInValidRange(const DATETIME_INFO *infoPtr, const SYSTEMTIME *date){ SYSTEMTIME range[2]; DWORD limits;
if ((MONTHCAL_CompareSystemTime(date, &max_allowed_date) == 1) || (MONTHCAL_CompareSystemTime(date, &min_allowed_date) == -1)) return FALSE;
limits = SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, 0, (LPARAM)range);
if (limits & GDTR_MAX) { if (MONTHCAL_CompareSystemTime(date, &range[1]) == 1) return FALSE; }
if (limits & GDTR_MIN) { if (MONTHCAL_CompareSystemTime(date, &range[0]) == -1) return FALSE; }
return TRUE;}
static BOOLDATETIME_SetSystemTime (DATETIME_INFO *infoPtr, DWORD flag, const SYSTEMTIME *systime){ if (!systime) return FALSE;
TRACE("%04d/%02d/%02d %02d:%02d:%02d\n", systime->wYear, systime->wMonth, systime->wDay, systime->wHour, systime->wMinute, systime->wSecond);
if (flag == GDT_VALID) { if (systime->wYear == 0 || systime->wMonth < 1 || systime->wMonth > 12 || systime->wDay < 1 || systime->wDay > MONTHCAL_MonthLength(systime->wMonth, systime->wYear) || systime->wHour > 23 || systime->wMinute > 59 || systime->wSecond > 59 || systime->wMilliseconds > 999 ) return FALSE;
/* Windows returns true if the date is valid but outside the limits set */ if (!DATETIME_IsDateInValidRange(infoPtr, systime)) return TRUE;
infoPtr->dateValid = TRUE; infoPtr->date = *systime; /* always store a valid day of week */ MONTHCAL_CalculateDayOfWeek(&infoPtr->date, TRUE);
SendMessageW (infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date)); SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0); } else if ((infoPtr->dwStyle & DTS_SHOWNONE) && (flag == GDT_NONE)) { infoPtr->dateValid = FALSE; SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_UNCHECKED, 0); } else return FALSE;
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); return TRUE;}
/*** * Split up a formattxt in actions. * See ms documentation for the meaning of the letter codes/'specifiers'. * * Notes: * *'dddddd' is handled as 'dddd' plus 'dd'. * *unrecognized formats are strings (here given the type DT_STRING; * start of the string is encoded in lower bits of DT_STRING. * Therefore, 'string' ends up as '<show seconds>tring'. * */static voidDATETIME_UseFormat (DATETIME_INFO *infoPtr, LPCWSTR formattxt){ unsigned int i; int j, k, len; BOOL inside_literal = FALSE; /* inside '...' */ int *nrFields = &infoPtr->nrFields;
*nrFields = 0; infoPtr->fieldspec[*nrFields] = 0; len = lstrlenW(allowedformatchars); k = 0;
for (i = 0; formattxt[i]; i++) { TRACE ("\n%d %c:", i, formattxt[i]); if (!inside_literal) { for (j = 0; j < len; j++) { if (allowedformatchars[j]==formattxt[i]) { TRACE ("%c[%d,%x]", allowedformatchars[j], *nrFields, infoPtr->fieldspec[*nrFields]); if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) { infoPtr->fieldspec[*nrFields] = (j<<4) + 1; break; } if (infoPtr->fieldspec[*nrFields] >> 4 != j) { (*nrFields)++; infoPtr->fieldspec[*nrFields] = (j<<4) + 1; break; } if ((infoPtr->fieldspec[*nrFields] & 0x0f) == maxrepetition[j]) { (*nrFields)++; infoPtr->fieldspec[*nrFields] = (j<<4) + 1; break; } infoPtr->fieldspec[*nrFields]++; break; } /* if allowedformatchar */ } /* for j */ } else j = len;
if (formattxt[i] == '\'') { inside_literal = !inside_literal; continue; }
/* char is not a specifier: handle char like a string */ if (j == len) { if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) { infoPtr->fieldspec[*nrFields] = DT_STRING + k; infoPtr->buflen[*nrFields] = 0; } else if ((infoPtr->fieldspec[*nrFields] & DT_STRING) != DT_STRING) { (*nrFields)++; infoPtr->fieldspec[*nrFields] = DT_STRING + k; infoPtr->buflen[*nrFields] = 0; } infoPtr->textbuf[k] = formattxt[i]; k++; infoPtr->buflen[*nrFields]++; } /* if j=len */
if (*nrFields == infoPtr->nrFieldsAllocated) { FIXME ("out of memory; should reallocate. crash ahead.\n"); } } /* for i */
TRACE("\n");
if (infoPtr->fieldspec[*nrFields] != 0) (*nrFields)++;}
static BOOLDATETIME_SetFormatW (DATETIME_INFO *infoPtr, LPCWSTR format){ WCHAR format_buf[80];
if (!format) { DWORD format_item;
if ((infoPtr->dwStyle & DTS_SHORTDATECENTURYFORMAT) == DTS_SHORTDATECENTURYFORMAT) format_item = LOCALE_SSHORTDATE; else if ((infoPtr->dwStyle & DTS_LONGDATEFORMAT) == DTS_LONGDATEFORMAT) format_item = LOCALE_SLONGDATE; else if ((infoPtr->dwStyle & DTS_TIMEFORMAT) == DTS_TIMEFORMAT) format_item = LOCALE_STIMEFORMAT; else /* DTS_SHORTDATEFORMAT */ format_item = LOCALE_SSHORTDATE; GetLocaleInfoW(LOCALE_USER_DEFAULT, format_item, format_buf, ARRAY_SIZE(format_buf)); format = format_buf; }
DATETIME_UseFormat (infoPtr, format); InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
return TRUE;}
static BOOLDATETIME_SetFormatA (DATETIME_INFO *infoPtr, LPCSTR lpszFormat){ if (lpszFormat) { BOOL retval; INT len = MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, NULL, 0); LPWSTR wstr = Alloc(len * sizeof(WCHAR)); if (wstr) MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, wstr, len); retval = DATETIME_SetFormatW (infoPtr, wstr); Free (wstr); return retval; } else return DATETIME_SetFormatW (infoPtr, 0);
}
static voidDATETIME_ReturnTxt (const DATETIME_INFO *infoPtr, int count, LPWSTR result, int resultSize){ SYSTEMTIME date = infoPtr->date; int spec; WCHAR buffer[80];
*result=0; TRACE ("%d,%d\n", infoPtr->nrFields, count); if (count>infoPtr->nrFields || count < 0) { WARN ("buffer overrun, have %d want %d\n", infoPtr->nrFields, count); return; }
if (!infoPtr->fieldspec) return;
spec = infoPtr->fieldspec[count]; if (spec & DT_STRING) { int txtlen = infoPtr->buflen[count];
if (txtlen > resultSize) txtlen = resultSize - 1; memcpy (result, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR)); result[txtlen] = 0; TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result)); return; }
switch (spec) { case DT_END_FORMAT: *result = 0; break; case ONEDIGITDAY: wsprintfW (result, L"%d", date.wDay); break; case TWODIGITDAY: wsprintfW (result, L"%.2d", date.wDay); break; case THREECHARDAY: GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1+(date.wDayOfWeek+6)%7, result, 4); /*wsprintfW (result,"%.3s",days[date.wDayOfWeek]);*/ break; case FULLDAY: GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDAYNAME1+(date.wDayOfWeek+6)%7, result, resultSize); break; case ONEDIGIT12HOUR: if (date.wHour == 0) { result[0] = '1'; result[1] = '2'; result[2] = 0; } else wsprintfW (result, L"%d", date.wHour - (date.wHour > 12 ? 12 : 0)); break; case TWODIGIT12HOUR: if (date.wHour == 0) { result[0] = '1'; result[1] = '2'; result[2] = 0; } else wsprintfW (result, L"%.2d", date.wHour - (date.wHour > 12 ? 12 : 0)); break; case ONEDIGIT24HOUR: wsprintfW (result, L"%d", date.wHour); break; case TWODIGIT24HOUR: wsprintfW (result, L"%.2d", date.wHour); break; case ONEDIGITSECOND: wsprintfW (result, L"%d", date.wSecond); break; case TWODIGITSECOND: wsprintfW (result, L"%.2d", date.wSecond); break; case ONEDIGITMINUTE: wsprintfW (result, L"%d", date.wMinute); break; case TWODIGITMINUTE: wsprintfW (result, L"%.2d", date.wMinute); break; case ONEDIGITMONTH: wsprintfW (result, L"%d", date.wMonth); break; case TWODIGITMONTH: wsprintfW (result, L"%.2d", date.wMonth); break; case THREECHARMONTH: GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1, buffer, ARRAY_SIZE(buffer)); wsprintfW (result, L"%s.3s", buffer); break; case FULLMONTH: GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1, result, resultSize); break; case ONELETTERAMPM: result[0] = (date.wHour < 12 ? 'A' : 'P'); result[1] = 0; break; case TWOLETTERAMPM: result[0] = (date.wHour < 12 ? 'A' : 'P'); result[1] = 'M'; result[2] = 0; break; case FORMATCALLBACK: FIXME ("Not implemented\n"); result[0] = 'x'; result[1] = 0; break; case ONEDIGITYEAR: wsprintfW (result, L"%d", date.wYear % 10); break; case TWODIGITYEAR: wsprintfW (result, L"%.2d", date.wYear % 100); break; case INVALIDFULLYEAR: case FULLYEAR: wsprintfW (result, L"%d", date.wYear); break; }
TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result));}
static int wrap(int val, int delta, int minVal, int maxVal){ val += delta; if (delta == INT_MIN || val < minVal) return maxVal; if (delta == INT_MAX || val > maxVal) return minVal; return val;}
static voidDATETIME_IncreaseField (DATETIME_INFO *infoPtr, int number, int delta){ SYSTEMTIME *date = &infoPtr->date; SYSTEMTIME range[2]; DWORD limits; BOOL min;
TRACE ("%d\n", number); if ((number > infoPtr->nrFields) || (number < 0)) return;
if ((infoPtr->fieldspec[number] & DTHT_DATEFIELD) == 0) return;
switch (infoPtr->fieldspec[number]) { case ONEDIGITYEAR: case TWODIGITYEAR: case FULLYEAR: if (delta == INT_MIN) date->wYear = 1752; else if (delta == INT_MAX) date->wYear = 9999; else date->wYear = max(min(date->wYear + delta, 9999), 1752);
if (date->wDay > MONTHCAL_MonthLength(date->wMonth, date->wYear)) /* This can happen when moving away from a leap year. */ date->wDay = MONTHCAL_MonthLength(date->wMonth, date->wYear); MONTHCAL_CalculateDayOfWeek(date, TRUE); break; case ONEDIGITMONTH: case TWODIGITMONTH: case THREECHARMONTH: case FULLMONTH: date->wMonth = wrap(date->wMonth, delta, 1, 12); MONTHCAL_CalculateDayOfWeek(date, TRUE); delta = 0; /* fall through */ case ONEDIGITDAY: case TWODIGITDAY: case THREECHARDAY: case FULLDAY: date->wDay = wrap(date->wDay, delta, 1, MONTHCAL_MonthLength(date->wMonth, date->wYear)); MONTHCAL_CalculateDayOfWeek(date, TRUE); break; case ONELETTERAMPM: case TWOLETTERAMPM: delta *= 12; /* fall through */ case ONEDIGIT12HOUR: case TWODIGIT12HOUR: case ONEDIGIT24HOUR: case TWODIGIT24HOUR: date->wHour = wrap(date->wHour, delta, 0, 23); break; case ONEDIGITMINUTE: case TWODIGITMINUTE: date->wMinute = wrap(date->wMinute, delta, 0, 59); break; case ONEDIGITSECOND: case TWODIGITSECOND: date->wSecond = wrap(date->wSecond, delta, 0, 59); break; case FORMATCALLBACK: FIXME ("Not implemented\n"); break; }
/* FYI: On 1752/9/14 the calendar changed and England and the * American colonies changed to the Gregorian calendar. This change * involved having September 14th follow September 2nd. So no date * algorithm works before that date. */ if (10000 * date->wYear + 100 * date->wMonth + date->wDay < 17520914) { date->wYear = 1752; date->wMonth = 9; date->wDay = 14; date->wSecond = 0; date->wMinute = 0; date->wHour = 0; }
/* Ensure time is within bounds */ limits = SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, 0, (LPARAM)range); min = delta < 0;
if (limits & (min ? GDTR_MIN : GDTR_MAX)) { int i = (min ? 0 : 1);
if (MONTHCAL_CompareSystemTime(date, &range[i]) == (min ? -1 : 1)) { date->wYear = range[i].wYear; date->wMonth = range[i].wMonth; date->wDayOfWeek = range[i].wDayOfWeek; date->wDay = range[i].wDay; date->wHour = range[i].wHour; date->wMinute = range[i].wMinute; date->wSecond = range[i].wSecond; date->wMilliseconds = range[i].wMilliseconds; } }}
static int DATETIME_GetFieldWidth (const DATETIME_INFO *infoPtr, HDC hdc, int count){ /* fields are a fixed width, determined by the largest possible string */ /* presumably, these widths should be language dependent */ int spec; WCHAR buffer[80]; LPCWSTR bufptr; SIZE size;
if (!infoPtr->fieldspec) return 0;
spec = infoPtr->fieldspec[count]; if (spec & DT_STRING) { int txtlen = infoPtr->buflen[count];
if (txtlen > 79) txtlen = 79; memcpy (buffer, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR)); buffer[txtlen] = 0; bufptr = buffer; } else { switch (spec) { case ONEDIGITDAY: case ONEDIGIT12HOUR: case ONEDIGIT24HOUR: case ONEDIGITSECOND: case ONEDIGITMINUTE: case ONEDIGITMONTH: case ONEDIGITYEAR: /* these seem to use a two byte field */ case TWODIGITDAY: case TWODIGIT12HOUR: case TWODIGIT24HOUR: case TWODIGITSECOND: case TWODIGITMINUTE: case TWODIGITMONTH: case TWODIGITYEAR: bufptr = L"22"; break; case INVALIDFULLYEAR: case FULLYEAR: bufptr = L"2222"; break; case THREECHARMONTH: case FULLMONTH: case THREECHARDAY: case FULLDAY: { const WCHAR *fall; LCTYPE lctype; INT i, max_count; LONG cx;
/* choose locale data type and fallback string */ switch (spec) { case THREECHARDAY: fall = L"Wed"; lctype = LOCALE_SABBREVDAYNAME1; max_count = 7; break; case FULLDAY: fall = L"Wednesday"; lctype = LOCALE_SDAYNAME1; max_count = 7; break; case THREECHARMONTH: fall = L"Dec"; lctype = LOCALE_SABBREVMONTHNAME1; max_count = 12; break; default: /* FULLMONTH */ fall = L"September"; lctype = LOCALE_SMONTHNAME1; max_count = 12; break; }
cx = 0; for (i = 0; i < max_count; i++) { if(GetLocaleInfoW(LOCALE_USER_DEFAULT, lctype + i, buffer, ARRAY_SIZE(buffer))) { GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &size); if (size.cx > cx) cx = size.cx; } else /* locale independent fallback on failure */ { GetTextExtentPoint32W(hdc, fall, lstrlenW(fall), &size); cx = size.cx; break; } } return cx; } case ONELETTERAMPM: bufptr = L"A"; break; case TWOLETTERAMPM: bufptr = L"AM"; break; default: bufptr = L"2"; break; } } GetTextExtentPoint32W (hdc, bufptr, lstrlenW(bufptr), &size); return size.cx;}
static void DATETIME_Refresh (DATETIME_INFO *infoPtr, HDC hdc){ TRACE("\n");
if (infoPtr->dateValid) { int i, prevright; RECT *field; RECT *rcDraw = &infoPtr->rcDraw; SIZE size; COLORREF oldTextColor; HFONT oldFont = SelectObject (hdc, infoPtr->hFont); INT oldBkMode = SetBkMode (hdc, TRANSPARENT); WCHAR txt[80];
DATETIME_ReturnTxt (infoPtr, 0, txt, ARRAY_SIZE(txt)); GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size); rcDraw->bottom = size.cy + 2;
prevright = infoPtr->checkbox.right = ((infoPtr->dwStyle & DTS_SHOWNONE) ? 18 : 2);
for (i = 0; i < infoPtr->nrFields; i++) { DATETIME_ReturnTxt (infoPtr, i, txt, ARRAY_SIZE(txt)); GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size); field = &infoPtr->fieldRect[i]; field->left = prevright; field->right = prevright + DATETIME_GetFieldWidth (infoPtr, hdc, i); field->top = rcDraw->top; field->bottom = rcDraw->bottom; prevright = field->right;
if (infoPtr->dwStyle & WS_DISABLED) oldTextColor = SetTextColor (hdc, comctl32_color.clrGrayText); else if ((infoPtr->haveFocus) && (i == infoPtr->select)) { RECT selection;
/* fill if focused */ HBRUSH hbr = CreateSolidBrush (comctl32_color.clrActiveCaption);
if (infoPtr->nCharsEntered) { memcpy(txt, infoPtr->charsEntered, infoPtr->nCharsEntered * sizeof(WCHAR)); txt[infoPtr->nCharsEntered] = 0; GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size); }
SetRect(&selection, 0, 0, size.cx, size.cy); /* center rectangle */ OffsetRect(&selection, (field->right + field->left - size.cx)/2, (field->bottom - size.cy)/2);
FillRect(hdc, &selection, hbr); DeleteObject (hbr); oldTextColor = SetTextColor (hdc, comctl32_color.clrWindow); } else oldTextColor = SetTextColor (hdc, comctl32_color.clrWindowText);
/* draw the date text using the colour set above */ DrawTextW (hdc, txt, lstrlenW(txt), field, DT_CENTER | DT_VCENTER | DT_SINGLELINE); SetTextColor (hdc, oldTextColor); } SetBkMode (hdc, oldBkMode); SelectObject (hdc, oldFont); }
if (!(infoPtr->dwStyle & DTS_UPDOWN)) { DrawFrameControl(hdc, &infoPtr->calbutton, DFC_SCROLL, DFCS_SCROLLDOWN | (infoPtr->bCalDepressed ? DFCS_PUSHED : 0) | (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) ); }}
static INTDATETIME_HitTest (const DATETIME_INFO *infoPtr, POINT pt){ int i;
TRACE ("%s\n", wine_dbgstr_point(&pt));
if (PtInRect (&infoPtr->calbutton, pt)) return DTHT_MCPOPUP; if (PtInRect (&infoPtr->checkbox, pt)) return DTHT_CHECKBOX;
for (i = 0; i < infoPtr->nrFields; i++) { if (PtInRect (&infoPtr->fieldRect[i], pt)) return i; }
return DTHT_NONE;}
/* Returns index of the nearest preceding date field from given, or -1 if none was found */static int DATETIME_GetPrevDateField(const DATETIME_INFO *infoPtr, int i){ for(--i; i >= 0; i--) { if (infoPtr->fieldspec[i] & DTHT_DATEFIELD) return i; } return -1;}
static voidDATETIME_ApplySelectedField (DATETIME_INFO *infoPtr){ int fieldNum = infoPtr->select & DTHT_DATEFIELD; int i, val = 0; BOOL clamp_day = FALSE; SYSTEMTIME date = infoPtr->date; int oldyear;
if (infoPtr->select == -1 || infoPtr->nCharsEntered == 0) return;
if ((infoPtr->fieldspec[fieldNum] == ONELETTERAMPM) || (infoPtr->fieldspec[fieldNum] == TWOLETTERAMPM)) val = infoPtr->charsEntered[0]; else { for (i=0; i<infoPtr->nCharsEntered; i++) val = val * 10 + infoPtr->charsEntered[i] - '0'; }
infoPtr->nCharsEntered = 0;
switch (infoPtr->fieldspec[fieldNum]) { case ONEDIGITYEAR: case TWODIGITYEAR: oldyear = date.wYear; date.wYear = date.wYear - (date.wYear%100) + val;
if (DATETIME_IsDateInValidRange(infoPtr, &date)) clamp_day = TRUE; else date.wYear = oldyear;
break; case INVALIDFULLYEAR: case FULLYEAR: oldyear = date.wYear; date.wYear = val;
if (DATETIME_IsDateInValidRange(infoPtr, &date)) clamp_day = TRUE; else date.wYear = oldyear;
break; case ONEDIGITMONTH: case TWODIGITMONTH: date.wMonth = val; clamp_day = TRUE; break; case ONEDIGITDAY: case TWODIGITDAY: date.wDay = val; break; case ONEDIGIT12HOUR: case TWODIGIT12HOUR: if (val >= 24) val -= 20;
if (val >= 13) date.wHour = val; else if (val != 0) { if (date.wHour >= 12) /* preserve current AM/PM state */ date.wHour = (val == 12 ? 12 : val + 12); else date.wHour = (val == 12 ? 0 : val); } break; case ONEDIGIT24HOUR: case TWODIGIT24HOUR: date.wHour = val; break; case ONEDIGITMINUTE: case TWODIGITMINUTE: date.wMinute = val; break; case ONEDIGITSECOND: case TWODIGITSECOND: date.wSecond = val; break; case ONELETTERAMPM: case TWOLETTERAMPM: if (val == 'a' || val == 'A') { if (date.wHour >= 12) date.wHour -= 12; } else if (val == 'p' || val == 'P') { if (date.wHour < 12) date.wHour += 12; } break; }
if (clamp_day && date.wDay > MONTHCAL_MonthLength(date.wMonth, date.wYear)) date.wDay = MONTHCAL_MonthLength(date.wMonth, date.wYear);
if (DATETIME_SetSystemTime(infoPtr, GDT_VALID, &date)) DATETIME_SendDateTimeChangeNotify (infoPtr);}
static voidDATETIME_SetSelectedField (DATETIME_INFO *infoPtr, int select){ DATETIME_ApplySelectedField(infoPtr);
infoPtr->select = select; infoPtr->nCharsEntered = 0;}
static LRESULTDATETIME_LButtonDown (DATETIME_INFO *infoPtr, INT x, INT y){ POINT pt; int new;
pt.x = x; pt.y = y; new = DATETIME_HitTest (infoPtr, pt);
SetFocus(infoPtr->hwndSelf);
if (!(new & DTHT_NODATEMASK) || (new == DTHT_NONE)) { if (new == DTHT_NONE) new = infoPtr->nrFields - 1; else { /* hitting string part moves selection to next date field to left */ if (infoPtr->fieldspec[new] & DT_STRING) { new = DATETIME_GetPrevDateField(infoPtr, new); if (new == -1) return 0; } /* never select full day of week */ if (infoPtr->fieldspec[new] == FULLDAY) return 0; } }
DATETIME_SetSelectedField(infoPtr, new);
if (infoPtr->select == DTHT_MCPOPUP) { RECT rcMonthCal; POINT pos; SendMessageW(infoPtr->hMonthCal, MCM_GETMINREQRECT, 0, (LPARAM)&rcMonthCal);
/* FIXME: button actually is only depressed during dropdown of the */ /* calendar control and when the mouse is over the button window */ infoPtr->bCalDepressed = TRUE;
/* recalculate the position of the monthcal popup */ if(infoPtr->dwStyle & DTS_RIGHTALIGN) pos.x = infoPtr->calbutton.left - (rcMonthCal.right - rcMonthCal.left); else /* FIXME: this should be after the area reserved for the checkbox */ pos.x = infoPtr->rcDraw.left;
pos.y = infoPtr->rcClient.bottom; OffsetRect( &rcMonthCal, pos.x, pos.y ); MapWindowPoints( infoPtr->hwndSelf, 0, (POINT *)&rcMonthCal, 2 ); SetWindowPos(infoPtr->hMonthCal, 0, rcMonthCal.left, rcMonthCal.top, rcMonthCal.right - rcMonthCal.left, rcMonthCal.bottom - rcMonthCal.top, 0);
if(IsWindowVisible(infoPtr->hMonthCal)) { ShowWindow(infoPtr->hMonthCal, SW_HIDE); infoPtr->bDropdownEnabled = FALSE; DATETIME_SendSimpleNotify (infoPtr, DTN_CLOSEUP); } else { const SYSTEMTIME *lprgSysTimeArray = &infoPtr->date; TRACE("update calendar %04d/%02d/%02d\n", lprgSysTimeArray->wYear, lprgSysTimeArray->wMonth, lprgSysTimeArray->wDay); SendMessageW(infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date));
if (infoPtr->bDropdownEnabled) { ShowWindow(infoPtr->hMonthCal, SW_SHOW); DATETIME_SendSimpleNotify (infoPtr, DTN_DROPDOWN); } infoPtr->bDropdownEnabled = TRUE; }
TRACE ("dt:%p mc:%p mc parent:%p, desktop:%p\n", infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hwndNotify, GetDesktopWindow ()); }
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;}
static LRESULTDATETIME_LButtonUp (DATETIME_INFO *infoPtr){ if(infoPtr->bCalDepressed) { infoPtr->bCalDepressed = FALSE; InvalidateRect(infoPtr->hwndSelf, &(infoPtr->calbutton), TRUE); }
return 0;}
static LRESULTDATETIME_Paint (DATETIME_INFO *infoPtr, HDC hdc){ if (!hdc) { PAINTSTRUCT ps; hdc = BeginPaint (infoPtr->hwndSelf, &ps); DATETIME_Refresh (infoPtr, hdc); EndPaint (infoPtr->hwndSelf, &ps); } else { DATETIME_Refresh (infoPtr, hdc); }
/* Not a click on the dropdown box, enabled it */ infoPtr->bDropdownEnabled = TRUE;
return 0;}
static LRESULTDATETIME_Button_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam){ if( HIWORD(wParam) == BN_CLICKED) { DWORD state = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0); infoPtr->dateValid = (state == BST_CHECKED); InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); } return 0;} static LRESULTDATETIME_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam){ TRACE("hwndbutton = %p\n", infoPtr->hwndCheckbut); if(infoPtr->hwndCheckbut == (HWND)lParam) return DATETIME_Button_Command(infoPtr, wParam, lParam); return 0;}
static LRESULTDATETIME_Enable (DATETIME_INFO *infoPtr, BOOL bEnable){ TRACE("%p %s\n", infoPtr, bEnable ? "TRUE" : "FALSE"); if (bEnable) infoPtr->dwStyle &= ~WS_DISABLED; else infoPtr->dwStyle |= WS_DISABLED;
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;}
static LRESULTDATETIME_EraseBackground (const DATETIME_INFO *infoPtr, HDC hdc){ HBRUSH hBrush, hSolidBrush = NULL; RECT rc;
if (infoPtr->dwStyle & WS_DISABLED) hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace); else { hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT, (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf); if (!hBrush) hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow); }
GetClientRect (infoPtr->hwndSelf, &rc);
FillRect (hdc, &rc, hBrush);
if (hSolidBrush) DeleteObject(hSolidBrush);
return -1;}
static LRESULTDATETIME_Notify (DATETIME_INFO *infoPtr, const NMHDR *lpnmh){ TRACE ("Got notification %x from %p\n", lpnmh->code, lpnmh->hwndFrom); TRACE ("info: %p %p %p\n", infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hUpdown);
if (lpnmh->code == MCN_SELECT) { ShowWindow(infoPtr->hMonthCal, SW_HIDE); infoPtr->dateValid = TRUE; SendMessageW (infoPtr->hMonthCal, MCM_GETCURSEL, 0, (LPARAM)&infoPtr->date); TRACE("got from calendar %04d/%02d/%02d day of week %d\n", infoPtr->date.wYear, infoPtr->date.wMonth, infoPtr->date.wDay, infoPtr->date.wDayOfWeek); SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0); InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); DATETIME_SendDateTimeChangeNotify (infoPtr); DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP); } if ((lpnmh->hwndFrom == infoPtr->hUpdown) && (lpnmh->code == UDN_DELTAPOS)) { const NM_UPDOWN *lpnmud = (const NM_UPDOWN*)lpnmh; TRACE("Delta pos %d\n", lpnmud->iDelta); infoPtr->pendingUpdown = lpnmud->iDelta; } return 0;}
static LRESULTDATETIME_KeyDown (DATETIME_INFO *infoPtr, DWORD vkCode){ int fieldNum = infoPtr->select & DTHT_DATEFIELD; int wrap = 0; int new;
if (!(infoPtr->haveFocus)) return 0; if ((fieldNum==0) && (infoPtr->select)) return 0;
if (infoPtr->select & FORMATCALLMASK) { FIXME ("Callbacks not implemented yet\n"); }
switch (vkCode) { case VK_ADD: case VK_UP: infoPtr->nCharsEntered = 0; DATETIME_IncreaseField (infoPtr, fieldNum, 1); DATETIME_SendDateTimeChangeNotify (infoPtr); break; case VK_SUBTRACT: case VK_DOWN: infoPtr->nCharsEntered = 0; DATETIME_IncreaseField (infoPtr, fieldNum, -1); DATETIME_SendDateTimeChangeNotify (infoPtr); break; case VK_HOME: infoPtr->nCharsEntered = 0; DATETIME_IncreaseField (infoPtr, fieldNum, INT_MIN); DATETIME_SendDateTimeChangeNotify (infoPtr); break; case VK_END: infoPtr->nCharsEntered = 0; DATETIME_IncreaseField (infoPtr, fieldNum, INT_MAX); DATETIME_SendDateTimeChangeNotify (infoPtr); break; case VK_LEFT: new = infoPtr->select; do { if (new == 0) { new = new - 1; wrap++; } else { new--; } } while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2)); if (new != infoPtr->select) DATETIME_SetSelectedField(infoPtr, new); break; case VK_RIGHT: new = infoPtr->select; do { new++; if (new==infoPtr->nrFields) { new = 0; wrap++; } } while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2)); if (new != infoPtr->select) DATETIME_SetSelectedField(infoPtr, new); break; }
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;}
static LRESULTDATETIME_Char (DATETIME_INFO *infoPtr, WPARAM vkCode){ int fieldNum, fieldSpec;
fieldNum = infoPtr->select & DTHT_DATEFIELD; fieldSpec = infoPtr->fieldspec[fieldNum];
if (fieldSpec == ONELETTERAMPM || fieldSpec == TWOLETTERAMPM) { infoPtr->charsEntered[0] = vkCode; infoPtr->nCharsEntered = 1;
DATETIME_ApplySelectedField(infoPtr); } else if (vkCode >= '0' && vkCode <= '9') { int maxChars;
infoPtr->charsEntered[infoPtr->nCharsEntered++] = vkCode;
if (fieldSpec == INVALIDFULLYEAR || fieldSpec == FULLYEAR) maxChars = 4; else maxChars = 2;
if ((fieldSpec == ONEDIGIT12HOUR || fieldSpec == TWODIGIT12HOUR || fieldSpec == ONEDIGIT24HOUR || fieldSpec == TWODIGIT24HOUR) && (infoPtr->nCharsEntered == 1)) { if (vkCode >= '3') maxChars = 1; }
if (maxChars == infoPtr->nCharsEntered) DATETIME_ApplySelectedField(infoPtr); }
return 0;}
static LRESULTDATETIME_VScroll (DATETIME_INFO *infoPtr, WORD wScroll){ int fieldNum = infoPtr->select & DTHT_DATEFIELD;
if ((SHORT)LOWORD(wScroll) != SB_THUMBPOSITION) return 0; if (!(infoPtr->haveFocus)) return 0; if ((fieldNum==0) && (infoPtr->select)) return 0;
if (infoPtr->pendingUpdown >= 0) { DATETIME_IncreaseField (infoPtr, fieldNum, 1); DATETIME_SendDateTimeChangeNotify (infoPtr); } else { DATETIME_IncreaseField (infoPtr, fieldNum, -1); DATETIME_SendDateTimeChangeNotify (infoPtr); }
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;}
static LRESULTDATETIME_KillFocus (DATETIME_INFO *infoPtr, HWND lostFocus){ TRACE("lost focus to %p\n", lostFocus);
if (infoPtr->haveFocus) { DATETIME_SendSimpleNotify (infoPtr, NM_KILLFOCUS); infoPtr->haveFocus = 0; DATETIME_SetSelectedField (infoPtr, -1); }
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
return 0;}
static LRESULTDATETIME_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs){ DWORD dwExStyle = GetWindowLongW(hwnd, GWL_EXSTYLE); /* force control to have client edge */ dwExStyle |= WS_EX_CLIENTEDGE; SetWindowLongW(hwnd, GWL_EXSTYLE, dwExStyle);
return 1;}
static LRESULTDATETIME_SetFocus (DATETIME_INFO *infoPtr, HWND lostFocus){ TRACE("got focus from %p\n", lostFocus);
/* if monthcal is open and it loses focus, close monthcal */ if (infoPtr->hMonthCal && (lostFocus == infoPtr->hMonthCal) && IsWindowVisible(infoPtr->hMonthCal)) { ShowWindow(infoPtr->hMonthCal, SW_HIDE); DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP); /* note: this get triggered even if monthcal loses focus to a dropdown * box click, which occurs without an intermediate WM_PAINT call */ infoPtr->bDropdownEnabled = FALSE; return 0; }
if (infoPtr->haveFocus == 0) { DATETIME_SendSimpleNotify (infoPtr, NM_SETFOCUS); infoPtr->haveFocus = DTHT_GOTFOCUS; }
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
return 0;}
static BOOLDATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr){ NMDATETIMECHANGE dtdtc;
dtdtc.nmhdr.hwndFrom = infoPtr->hwndSelf; dtdtc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); dtdtc.nmhdr.code = DTN_DATETIMECHANGE;
dtdtc.dwFlags = infoPtr->dateValid ? GDT_VALID : GDT_NONE;
dtdtc.st = infoPtr->date; return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, dtdtc.nmhdr.idFrom, (LPARAM)&dtdtc);}
static BOOLDATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code){ NMHDR nmhdr;
TRACE("%x\n", code); nmhdr.hwndFrom = infoPtr->hwndSelf; nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); nmhdr.code = code;
return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);}
static LRESULTDATETIME_Size (DATETIME_INFO *infoPtr, INT width, INT height){ /* set size */ infoPtr->rcClient.bottom = height; infoPtr->rcClient.right = width;
TRACE("Height=%d, Width=%d\n", infoPtr->rcClient.bottom, infoPtr->rcClient.right);
infoPtr->rcDraw = infoPtr->rcClient; if (infoPtr->dwStyle & DTS_UPDOWN) { SetWindowPos(infoPtr->hUpdown, NULL, infoPtr->rcClient.right-14, 0, 15, infoPtr->rcClient.bottom - infoPtr->rcClient.top, SWP_NOACTIVATE | SWP_NOZORDER); } else { /* set the size of the button that drops the calendar down */ /* FIXME: account for style that allows button on left side */ infoPtr->calbutton.top = infoPtr->rcDraw.top; infoPtr->calbutton.bottom= infoPtr->rcDraw.bottom; infoPtr->calbutton.left = infoPtr->rcDraw.right-15; infoPtr->calbutton.right = infoPtr->rcDraw.right; }
/* set enable/disable button size for show none style being enabled */ /* FIXME: these dimensions are completely incorrect */ infoPtr->checkbox.top = infoPtr->rcDraw.top; infoPtr->checkbox.bottom = infoPtr->rcDraw.bottom; infoPtr->checkbox.left = infoPtr->rcDraw.left; infoPtr->checkbox.right = infoPtr->rcDraw.left + 10;
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
return 0;}
static LRESULTDATETIME_StyleChanging(DATETIME_INFO *infoPtr, WPARAM wStyleType, STYLESTRUCT *lpss){ TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", wStyleType, lpss->styleOld, lpss->styleNew);
/* block DTS_SHOWNONE change */ if ((lpss->styleNew ^ lpss->styleOld) & DTS_SHOWNONE) { if (lpss->styleOld & DTS_SHOWNONE) lpss->styleNew |= DTS_SHOWNONE; else lpss->styleNew &= ~DTS_SHOWNONE; }
return 0;}
static LRESULT DATETIME_StyleChanged(DATETIME_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss){ TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", wStyleType, lpss->styleOld, lpss->styleNew);
if (wStyleType != GWL_STYLE) return 0; infoPtr->dwStyle = lpss->styleNew;
if ( !(lpss->styleOld & DTS_SHOWNONE) && (lpss->styleNew & DTS_SHOWNONE) ) { infoPtr->hwndCheckbut = CreateWindowExW (0, WC_BUTTONW, 0, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 2, 2, 13, 13, infoPtr->hwndSelf, 0, (HINSTANCE)GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_HINSTANCE), 0); SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, infoPtr->dateValid ? 1 : 0, 0); } if ( (lpss->styleOld & DTS_SHOWNONE) && !(lpss->styleNew & DTS_SHOWNONE) ) { DestroyWindow(infoPtr->hwndCheckbut); infoPtr->hwndCheckbut = 0; } if ( !(lpss->styleOld & DTS_UPDOWN) && (lpss->styleNew & DTS_UPDOWN) ) { infoPtr->hUpdown = CreateUpDownControl (WS_CHILD | WS_BORDER | WS_VISIBLE, 120, 1, 20, 20, infoPtr->hwndSelf, 1, 0, 0, UD_MAXVAL, UD_MINVAL, 0); } if ( (lpss->styleOld & DTS_UPDOWN) && !(lpss->styleNew & DTS_UPDOWN) ) { DestroyWindow(infoPtr->hUpdown); infoPtr->hUpdown = 0; }
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); return 0;}
static BOOL DATETIME_GetIdealSize(DATETIME_INFO *infoPtr, SIZE *size){ SIZE field_size; RECT rect; WCHAR txt[80]; HDC hdc; HFONT oldFont; int i;
size->cx = size->cy = 0;
hdc = GetDC(infoPtr->hwndSelf); oldFont = SelectObject(hdc, infoPtr->hFont);
/* Get text font height */ DATETIME_ReturnTxt(infoPtr, 0, txt, ARRAY_SIZE(txt)); GetTextExtentPoint32W(hdc, txt, lstrlenW(txt), &field_size); size->cy = field_size.cy;
/* Get text font width */ for (i = 0; i < infoPtr->nrFields; i++) { size->cx += DATETIME_GetFieldWidth(infoPtr, hdc, i); }
SelectObject(hdc, oldFont); ReleaseDC(infoPtr->hwndSelf, hdc);
if (infoPtr->dwStyle & DTS_UPDOWN) { GetWindowRect(infoPtr->hUpdown, &rect); size->cx += rect.right - rect.left; } else { size->cx += infoPtr->calbutton.right - infoPtr->calbutton.left; }
if (infoPtr->dwStyle & DTS_SHOWNONE) { size->cx += infoPtr->checkbox.right - infoPtr->checkbox.left; }
/* Add space between controls for them not to get too close */ size->cx += 12; size->cy += 4;
TRACE("cx=%d cy=%d\n", size->cx, size->cy); return TRUE;}
static LRESULTDATETIME_SetFont (DATETIME_INFO *infoPtr, HFONT font, BOOL repaint){ infoPtr->hFont = font; if (repaint) InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); return 0;}
static LRESULTDATETIME_Create (HWND hwnd, const CREATESTRUCTW *lpcs){ DATETIME_INFO *infoPtr = Alloc (sizeof(DATETIME_INFO)); STYLESTRUCT ss = { 0, lpcs->style };
if (!infoPtr) return -1;
infoPtr->hwndSelf = hwnd; infoPtr->dwStyle = lpcs->style;
infoPtr->nrFieldsAllocated = 32; infoPtr->fieldspec = Alloc (infoPtr->nrFieldsAllocated * sizeof(int)); infoPtr->fieldRect = Alloc (infoPtr->nrFieldsAllocated * sizeof(RECT)); infoPtr->buflen = Alloc (infoPtr->nrFieldsAllocated * sizeof(int)); infoPtr->hwndNotify = lpcs->hwndParent; infoPtr->select = -1; /* initially, nothing is selected */ infoPtr->bDropdownEnabled = TRUE;
DATETIME_StyleChanged(infoPtr, GWL_STYLE, &ss); DATETIME_SetFormatW (infoPtr, 0);
/* create the monthcal control */ infoPtr->hMonthCal = CreateWindowExW (0, MONTHCAL_CLASSW, 0, WS_BORDER | WS_POPUP | WS_CLIPSIBLINGS, 0, 0, 0, 0, infoPtr->hwndSelf, 0, 0, 0);
/* initialize info structure */ GetLocalTime (&infoPtr->date); infoPtr->dateValid = TRUE; infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
return 0;}
static LRESULTDATETIME_Destroy (DATETIME_INFO *infoPtr){ if (infoPtr->hwndCheckbut) DestroyWindow(infoPtr->hwndCheckbut); if (infoPtr->hUpdown) DestroyWindow(infoPtr->hUpdown); if (infoPtr->hMonthCal) DestroyWindow(infoPtr->hMonthCal); SetWindowLongPtrW( infoPtr->hwndSelf, 0, 0 ); /* clear infoPtr */ Free (infoPtr->buflen); Free (infoPtr->fieldRect); Free (infoPtr->fieldspec); Free (infoPtr); return 0;}
static INTDATETIME_GetText (const DATETIME_INFO *infoPtr, INT count, LPWSTR dst){ WCHAR buf[80]; int i;
if (!dst || (count <= 0)) return 0;
dst[0] = 0; for (i = 0; i < infoPtr->nrFields; i++) { DATETIME_ReturnTxt(infoPtr, i, buf, ARRAY_SIZE(buf)); if ((lstrlenW(dst) + lstrlenW(buf)) < count) lstrcatW(dst, buf); else break; } return lstrlenW(dst);}
static int DATETIME_GetTextLength(const DATETIME_INFO *info){ int i, length = 0; WCHAR buffer[80];
TRACE("%p.\n", info);
for (i = 0; i < info->nrFields; i++) { DATETIME_ReturnTxt(info, i, buffer, ARRAY_SIZE(buffer)); length += lstrlenW(buffer); } return length;}
static LRESULT WINAPIDATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ DATETIME_INFO *infoPtr = ((DATETIME_INFO *)GetWindowLongPtrW (hwnd, 0));
TRACE ("%x, %lx, %lx\n", uMsg, wParam, lParam);
if (!infoPtr && (uMsg != WM_CREATE) && (uMsg != WM_NCCREATE)) return DefWindowProcW( hwnd, uMsg, wParam, lParam );
switch (uMsg) {
case DTM_GETSYSTEMTIME: return DATETIME_GetSystemTime (infoPtr, (SYSTEMTIME *) lParam);
case DTM_SETSYSTEMTIME: return DATETIME_SetSystemTime (infoPtr, wParam, (SYSTEMTIME *) lParam);
case DTM_GETRANGE: return SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, wParam, lParam);
case DTM_SETRANGE: return SendMessageW (infoPtr->hMonthCal, MCM_SETRANGE, wParam, lParam);
case DTM_SETFORMATA: return DATETIME_SetFormatA (infoPtr, (LPCSTR)lParam);
case DTM_SETFORMATW: return DATETIME_SetFormatW (infoPtr, (LPCWSTR)lParam);
case DTM_GETMONTHCAL: return (LRESULT)infoPtr->hMonthCal;
case DTM_SETMCCOLOR: return SendMessageW (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
case DTM_GETMCCOLOR: return SendMessageW (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
case DTM_SETMCFONT: return SendMessageW (infoPtr->hMonthCal, WM_SETFONT, wParam, lParam);
case DTM_GETMCFONT: return SendMessageW (infoPtr->hMonthCal, WM_GETFONT, wParam, lParam);
case DTM_GETIDEALSIZE: return DATETIME_GetIdealSize(infoPtr, (SIZE *)lParam);
case WM_NOTIFY: return DATETIME_Notify (infoPtr, (LPNMHDR)lParam);
case WM_ENABLE: return DATETIME_Enable (infoPtr, (BOOL)wParam);
case WM_ERASEBKGND: return DATETIME_EraseBackground (infoPtr, (HDC)wParam);
case WM_GETDLGCODE: return DLGC_WANTARROWS | DLGC_WANTCHARS;
case WM_PRINTCLIENT: case WM_PAINT: return DATETIME_Paint (infoPtr, (HDC)wParam);
case WM_KEYDOWN: return DATETIME_KeyDown (infoPtr, wParam);
case WM_CHAR: return DATETIME_Char (infoPtr, wParam);
case WM_KILLFOCUS: return DATETIME_KillFocus (infoPtr, (HWND)wParam);
case WM_NCCREATE: return DATETIME_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);
case WM_SETFOCUS: return DATETIME_SetFocus (infoPtr, (HWND)wParam);
case WM_SIZE: return DATETIME_Size (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
case WM_LBUTTONDOWN: return DATETIME_LButtonDown (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
case WM_LBUTTONUP: return DATETIME_LButtonUp (infoPtr);
case WM_VSCROLL: return DATETIME_VScroll (infoPtr, (WORD)wParam);
case WM_CREATE: return DATETIME_Create (hwnd, (LPCREATESTRUCTW)lParam);
case WM_DESTROY: return DATETIME_Destroy (infoPtr);
case WM_COMMAND: return DATETIME_Command (infoPtr, wParam, lParam);
case WM_STYLECHANGING: return DATETIME_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
case WM_STYLECHANGED: return DATETIME_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
case WM_SETFONT: return DATETIME_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
case WM_GETFONT: return (LRESULT) infoPtr->hFont;
case WM_GETTEXT: return (LRESULT) DATETIME_GetText(infoPtr, wParam, (LPWSTR)lParam);
case WM_GETTEXTLENGTH: return (LRESULT)DATETIME_GetTextLength(infoPtr);
case WM_SETTEXT: return CB_ERR;
default: if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam); return DefWindowProcW (hwnd, uMsg, wParam, lParam); }}
voidDATETIME_Register (void){ WNDCLASSW wndClass;
ZeroMemory (&wndClass, sizeof(WNDCLASSW)); wndClass.style = CS_GLOBALCLASS; wndClass.lpfnWndProc = DATETIME_WindowProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = sizeof(DATETIME_INFO *); wndClass.hCursor = LoadCursorW (0, (LPCWSTR)IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszClassName = DATETIMEPICK_CLASSW;
RegisterClassW (&wndClass);}
voidDATETIME_Unregister (void){ UnregisterClassW (DATETIMEPICK_CLASSW, NULL);}