Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/comctl32/datetime.c
8602 views
1
/*
2
* Date and time picker control
3
*
4
* Copyright 1998, 1999 Eric Kohl
5
* Copyright 1999, 2000 Alex Priem <[email protected]>
6
* Copyright 2000 Chris Morgan <[email protected]>
7
* Copyright 2012 Owen Rudge for CodeWeavers
8
*
9
* This library is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU Lesser General Public
11
* License as published by the Free Software Foundation; either
12
* version 2.1 of the License, or (at your option) any later version.
13
*
14
* This library is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
* Lesser General Public License for more details.
18
*
19
* You should have received a copy of the GNU Lesser General Public
20
* License along with this library; if not, write to the Free Software
21
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22
*
23
* TODO:
24
* -- DTS_APPCANPARSE
25
* -- DTS_SHORTDATECENTURYFORMAT
26
* -- DTN_FORMAT
27
* -- DTN_FORMATQUERY
28
* -- DTN_USERSTRING
29
* -- DTN_WMKEYDOWN
30
* -- FORMATCALLBACK
31
*/
32
33
#include <math.h>
34
#include <string.h>
35
#include <stdarg.h>
36
#include <stdio.h>
37
#include <limits.h>
38
39
#include "windef.h"
40
#include "winbase.h"
41
#include "wingdi.h"
42
#include "winuser.h"
43
#include "winnls.h"
44
#include "commctrl.h"
45
#include "comctl32.h"
46
#include "wine/debug.h"
47
48
WINE_DEFAULT_DEBUG_CHANNEL(datetime);
49
50
typedef struct
51
{
52
HWND hwndSelf;
53
HWND hMonthCal;
54
HWND hwndNotify;
55
HWND hUpdown;
56
DWORD dwStyle;
57
SYSTEMTIME date;
58
BOOL dateValid;
59
HWND hwndCheckbut;
60
RECT rcClient; /* rect around the edge of the window */
61
RECT rcDraw; /* rect inside of the border */
62
RECT checkbox; /* checkbox allowing the control to be enabled/disabled */
63
RECT calbutton; /* button that toggles the dropdown of the monthcal control */
64
BOOL bCalDepressed; /* TRUE = cal button is depressed */
65
BOOL bCalHot; /* TRUE if calendar button is hovered */
66
BOOL bDropdownEnabled;
67
int select;
68
WCHAR charsEntered[4];
69
int nCharsEntered;
70
HFONT hFont;
71
int nrFieldsAllocated;
72
int nrFields;
73
int haveFocus;
74
int *fieldspec;
75
RECT *fieldRect;
76
int *buflen;
77
WCHAR textbuf[256];
78
POINT monthcal_pos;
79
int pendingUpdown;
80
} DATETIME_INFO, *LPDATETIME_INFO;
81
82
/* this list of defines is closely related to `allowedformatchars' defined
83
* in datetime.c; the high nibble indicates the `base type' of the format
84
* specifier.
85
* Do not change without first reading DATETIME_UseFormat.
86
*
87
*/
88
89
#define DT_END_FORMAT 0
90
#define ONEDIGITDAY 0x01
91
#define TWODIGITDAY 0x02
92
#define THREECHARDAY 0x03
93
#define FULLDAY 0x04
94
#define ONEDIGIT12HOUR 0x11
95
#define TWODIGIT12HOUR 0x12
96
#define ONEDIGIT24HOUR 0x21
97
#define TWODIGIT24HOUR 0x22
98
#define ONEDIGITMINUTE 0x31
99
#define TWODIGITMINUTE 0x32
100
#define ONEDIGITMONTH 0x41
101
#define TWODIGITMONTH 0x42
102
#define THREECHARMONTH 0x43
103
#define FULLMONTH 0x44
104
#define ONEDIGITSECOND 0x51
105
#define TWODIGITSECOND 0x52
106
#define ONELETTERAMPM 0x61
107
#define TWOLETTERAMPM 0x62
108
#define ONEDIGITYEAR 0x71
109
#define TWODIGITYEAR 0x72
110
#define INVALIDFULLYEAR 0x73 /* FIXME - yyy is not valid - we'll treat it as yyyy */
111
#define FULLYEAR 0x74
112
#define FORMATCALLBACK 0x81 /* -> maximum of 0x80 callbacks possible */
113
#define FORMATCALLMASK 0x80
114
#define DT_STRING 0x0100
115
116
#define DTHT_DATEFIELD 0xff /* for hit-testing */
117
118
#define DTHT_NONE 0x1000
119
#define DTHT_CHECKBOX 0x2000 /* these should end at '00' , to make */
120
#define DTHT_MCPOPUP 0x3000 /* & DTHT_DATEFIELD 0 when DATETIME_KeyDown */
121
#define DTHT_GOTFOCUS 0x4000 /* tests for date-fields */
122
#define DTHT_NODATEMASK 0xf000 /* to mask check and drop down from others */
123
124
static BOOL DATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code);
125
static BOOL DATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr);
126
static const WCHAR allowedformatchars[] = L"dhHmMstyX";
127
static const int maxrepetition [] = {4,2,2,2,4,2,2,4,-1};
128
129
/* valid date limits */
130
static const SYSTEMTIME max_allowed_date = { .wYear = 9999, .wMonth = 12, .wDayOfWeek = 0, .wDay = 31 };
131
static const SYSTEMTIME min_allowed_date = { .wYear = 1752, .wMonth = 9, .wDayOfWeek = 0, .wDay = 14 };
132
133
static DWORD
134
DATETIME_GetSystemTime (const DATETIME_INFO *infoPtr, SYSTEMTIME *systime)
135
{
136
if (!systime) return GDT_NONE;
137
138
if ((infoPtr->dwStyle & DTS_SHOWNONE) &&
139
(SendMessageW (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0) == BST_UNCHECKED))
140
return GDT_NONE;
141
142
*systime = infoPtr->date;
143
144
return GDT_VALID;
145
}
146
147
/* Checks value is within configured date range
148
*
149
* PARAMETERS
150
*
151
* [I] infoPtr : valid pointer to control data
152
* [I] date : pointer to valid date data to check
153
*
154
* RETURN VALUE
155
*
156
* TRUE - date within configured range
157
* FALSE - date is outside configured range
158
*/
159
static BOOL DATETIME_IsDateInValidRange(const DATETIME_INFO *infoPtr, const SYSTEMTIME *date)
160
{
161
SYSTEMTIME range[2];
162
DWORD limits;
163
164
if ((MONTHCAL_CompareSystemTime(date, &max_allowed_date) == 1) ||
165
(MONTHCAL_CompareSystemTime(date, &min_allowed_date) == -1))
166
return FALSE;
167
168
limits = SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, 0, (LPARAM)range);
169
170
if (limits & GDTR_MAX)
171
{
172
if (MONTHCAL_CompareSystemTime(date, &range[1]) == 1)
173
return FALSE;
174
}
175
176
if (limits & GDTR_MIN)
177
{
178
if (MONTHCAL_CompareSystemTime(date, &range[0]) == -1)
179
return FALSE;
180
}
181
182
return TRUE;
183
}
184
185
static BOOL
186
DATETIME_SetSystemTime (DATETIME_INFO *infoPtr, DWORD flag, const SYSTEMTIME *systime)
187
{
188
if (!systime) return FALSE;
189
190
TRACE("%04d/%02d/%02d %02d:%02d:%02d\n",
191
systime->wYear, systime->wMonth, systime->wDay,
192
systime->wHour, systime->wMinute, systime->wSecond);
193
194
if (flag == GDT_VALID) {
195
if (systime->wYear == 0 ||
196
systime->wMonth < 1 || systime->wMonth > 12 ||
197
systime->wDay < 1 ||
198
systime->wDay > MONTHCAL_MonthLength(systime->wMonth, systime->wYear) ||
199
systime->wHour > 23 ||
200
systime->wMinute > 59 ||
201
systime->wSecond > 59 ||
202
systime->wMilliseconds > 999
203
)
204
return FALSE;
205
206
/* Windows returns true if the date is valid but outside the limits set */
207
if (!DATETIME_IsDateInValidRange(infoPtr, systime))
208
return TRUE;
209
210
infoPtr->dateValid = TRUE;
211
infoPtr->date = *systime;
212
/* always store a valid day of week */
213
MONTHCAL_CalculateDayOfWeek(&infoPtr->date, TRUE);
214
215
SendMessageW (infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date));
216
SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0);
217
} else if ((infoPtr->dwStyle & DTS_SHOWNONE) && (flag == GDT_NONE)) {
218
infoPtr->dateValid = FALSE;
219
SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_UNCHECKED, 0);
220
}
221
else
222
return FALSE;
223
224
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
225
return TRUE;
226
}
227
228
229
/***
230
* Split up a formattxt in actions.
231
* See ms documentation for the meaning of the letter codes/'specifiers'.
232
*
233
* Notes:
234
* *'dddddd' is handled as 'dddd' plus 'dd'.
235
* *unrecognized formats are strings (here given the type DT_STRING;
236
* start of the string is encoded in lower bits of DT_STRING.
237
* Therefore, 'string' ends up as '<show seconds>tring'.
238
*
239
*/
240
static void
241
DATETIME_UseFormat (DATETIME_INFO *infoPtr, LPCWSTR formattxt)
242
{
243
unsigned int i;
244
int j, k, len;
245
BOOL inside_literal = FALSE; /* inside '...' */
246
int *nrFields = &infoPtr->nrFields;
247
248
*nrFields = 0;
249
infoPtr->fieldspec[*nrFields] = 0;
250
len = lstrlenW(allowedformatchars);
251
k = 0;
252
253
for (i = 0; formattxt[i]; i++) {
254
TRACE ("\n%d %c:", i, formattxt[i]);
255
if (!inside_literal) {
256
for (j = 0; j < len; j++) {
257
if (allowedformatchars[j]==formattxt[i]) {
258
TRACE ("%c[%d,%x]", allowedformatchars[j], *nrFields, infoPtr->fieldspec[*nrFields]);
259
if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
260
infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
261
break;
262
}
263
if (infoPtr->fieldspec[*nrFields] >> 4 != j) {
264
(*nrFields)++;
265
infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
266
break;
267
}
268
if ((infoPtr->fieldspec[*nrFields] & 0x0f) == maxrepetition[j]) {
269
(*nrFields)++;
270
infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
271
break;
272
}
273
infoPtr->fieldspec[*nrFields]++;
274
break;
275
} /* if allowedformatchar */
276
} /* for j */
277
}
278
else
279
j = len;
280
281
if (formattxt[i] == '\'')
282
{
283
inside_literal = !inside_literal;
284
continue;
285
}
286
287
/* char is not a specifier: handle char like a string */
288
if (j == len) {
289
if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
290
infoPtr->fieldspec[*nrFields] = DT_STRING + k;
291
infoPtr->buflen[*nrFields] = 0;
292
} else if ((infoPtr->fieldspec[*nrFields] & DT_STRING) != DT_STRING) {
293
(*nrFields)++;
294
infoPtr->fieldspec[*nrFields] = DT_STRING + k;
295
infoPtr->buflen[*nrFields] = 0;
296
}
297
infoPtr->textbuf[k] = formattxt[i];
298
k++;
299
infoPtr->buflen[*nrFields]++;
300
} /* if j=len */
301
302
if (*nrFields == infoPtr->nrFieldsAllocated) {
303
FIXME ("out of memory; should reallocate. crash ahead.\n");
304
}
305
} /* for i */
306
307
TRACE("\n");
308
309
if (infoPtr->fieldspec[*nrFields] != 0) (*nrFields)++;
310
}
311
312
313
static BOOL
314
DATETIME_SetFormatW (DATETIME_INFO *infoPtr, LPCWSTR format)
315
{
316
WCHAR format_buf[80];
317
318
if (!format) {
319
DWORD format_item;
320
321
if ((infoPtr->dwStyle & DTS_SHORTDATECENTURYFORMAT) == DTS_SHORTDATECENTURYFORMAT)
322
format_item = LOCALE_SSHORTDATE;
323
else if ((infoPtr->dwStyle & DTS_LONGDATEFORMAT) == DTS_LONGDATEFORMAT)
324
format_item = LOCALE_SLONGDATE;
325
else if ((infoPtr->dwStyle & DTS_TIMEFORMAT) == DTS_TIMEFORMAT)
326
format_item = LOCALE_STIMEFORMAT;
327
else /* DTS_SHORTDATEFORMAT */
328
format_item = LOCALE_SSHORTDATE;
329
GetLocaleInfoW(LOCALE_USER_DEFAULT, format_item, format_buf, ARRAY_SIZE(format_buf));
330
format = format_buf;
331
}
332
333
DATETIME_UseFormat (infoPtr, format);
334
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
335
336
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, infoPtr->hwndSelf, OBJID_CLIENT, CHILDID_SELF);
337
338
return TRUE;
339
}
340
341
342
static BOOL
343
DATETIME_SetFormatA (DATETIME_INFO *infoPtr, LPCSTR lpszFormat)
344
{
345
if (lpszFormat) {
346
BOOL retval;
347
INT len = MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, NULL, 0);
348
LPWSTR wstr = Alloc(len * sizeof(WCHAR));
349
if (wstr) MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, wstr, len);
350
retval = DATETIME_SetFormatW (infoPtr, wstr);
351
Free (wstr);
352
return retval;
353
}
354
else
355
return DATETIME_SetFormatW (infoPtr, 0);
356
357
}
358
359
360
static void
361
DATETIME_ReturnTxt (const DATETIME_INFO *infoPtr, int count, LPWSTR result, int resultSize)
362
{
363
SYSTEMTIME date = infoPtr->date;
364
int spec;
365
WCHAR buffer[80];
366
367
*result=0;
368
TRACE ("%d,%d\n", infoPtr->nrFields, count);
369
if (count>infoPtr->nrFields || count < 0) {
370
WARN ("buffer overrun, have %d want %d\n", infoPtr->nrFields, count);
371
return;
372
}
373
374
if (!infoPtr->fieldspec) return;
375
376
spec = infoPtr->fieldspec[count];
377
if (spec & DT_STRING) {
378
int txtlen = infoPtr->buflen[count];
379
380
if (txtlen > resultSize)
381
txtlen = resultSize - 1;
382
memcpy (result, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR));
383
result[txtlen] = 0;
384
TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result));
385
return;
386
}
387
388
389
switch (spec) {
390
case DT_END_FORMAT:
391
*result = 0;
392
break;
393
case ONEDIGITDAY:
394
wsprintfW (result, L"%d", date.wDay);
395
break;
396
case TWODIGITDAY:
397
wsprintfW (result, L"%.2d", date.wDay);
398
break;
399
case THREECHARDAY:
400
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1+(date.wDayOfWeek+6)%7, result, 4);
401
/*wsprintfW (result,"%.3s",days[date.wDayOfWeek]);*/
402
break;
403
case FULLDAY:
404
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDAYNAME1+(date.wDayOfWeek+6)%7, result, resultSize);
405
break;
406
case ONEDIGIT12HOUR:
407
if (date.wHour == 0) {
408
result[0] = '1';
409
result[1] = '2';
410
result[2] = 0;
411
}
412
else
413
wsprintfW (result, L"%d", date.wHour - (date.wHour > 12 ? 12 : 0));
414
break;
415
case TWODIGIT12HOUR:
416
if (date.wHour == 0) {
417
result[0] = '1';
418
result[1] = '2';
419
result[2] = 0;
420
}
421
else
422
wsprintfW (result, L"%.2d", date.wHour - (date.wHour > 12 ? 12 : 0));
423
break;
424
case ONEDIGIT24HOUR:
425
wsprintfW (result, L"%d", date.wHour);
426
break;
427
case TWODIGIT24HOUR:
428
wsprintfW (result, L"%.2d", date.wHour);
429
break;
430
case ONEDIGITSECOND:
431
wsprintfW (result, L"%d", date.wSecond);
432
break;
433
case TWODIGITSECOND:
434
wsprintfW (result, L"%.2d", date.wSecond);
435
break;
436
case ONEDIGITMINUTE:
437
wsprintfW (result, L"%d", date.wMinute);
438
break;
439
case TWODIGITMINUTE:
440
wsprintfW (result, L"%.2d", date.wMinute);
441
break;
442
case ONEDIGITMONTH:
443
wsprintfW (result, L"%d", date.wMonth);
444
break;
445
case TWODIGITMONTH:
446
wsprintfW (result, L"%.2d", date.wMonth);
447
break;
448
case THREECHARMONTH:
449
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1, buffer, ARRAY_SIZE(buffer));
450
wsprintfW (result, L"%s.3s", buffer);
451
break;
452
case FULLMONTH:
453
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1,
454
result, resultSize);
455
break;
456
case ONELETTERAMPM:
457
result[0] = (date.wHour < 12 ? 'A' : 'P');
458
result[1] = 0;
459
break;
460
case TWOLETTERAMPM:
461
result[0] = (date.wHour < 12 ? 'A' : 'P');
462
result[1] = 'M';
463
result[2] = 0;
464
break;
465
case FORMATCALLBACK:
466
FIXME ("Not implemented\n");
467
result[0] = 'x';
468
result[1] = 0;
469
break;
470
case ONEDIGITYEAR:
471
wsprintfW (result, L"%d", date.wYear % 10);
472
break;
473
case TWODIGITYEAR:
474
wsprintfW (result, L"%.2d", date.wYear % 100);
475
break;
476
case INVALIDFULLYEAR:
477
case FULLYEAR:
478
wsprintfW (result, L"%d", date.wYear);
479
break;
480
}
481
482
TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result));
483
}
484
485
static int wrap(int val, int delta, int minVal, int maxVal)
486
{
487
val += delta;
488
if (delta == INT_MIN || val < minVal) return maxVal;
489
if (delta == INT_MAX || val > maxVal) return minVal;
490
return val;
491
}
492
493
static void
494
DATETIME_IncreaseField (DATETIME_INFO *infoPtr, int number, int delta)
495
{
496
SYSTEMTIME *date = &infoPtr->date;
497
SYSTEMTIME range[2];
498
DWORD limits;
499
BOOL min;
500
501
TRACE ("%d\n", number);
502
if ((number > infoPtr->nrFields) || (number < 0)) return;
503
504
if ((infoPtr->fieldspec[number] & DTHT_DATEFIELD) == 0) return;
505
506
switch (infoPtr->fieldspec[number]) {
507
case ONEDIGITYEAR:
508
case TWODIGITYEAR:
509
case FULLYEAR:
510
if (delta == INT_MIN)
511
date->wYear = 1752;
512
else if (delta == INT_MAX)
513
date->wYear = 9999;
514
else
515
date->wYear = max(min(date->wYear + delta, 9999), 1752);
516
517
if (date->wDay > MONTHCAL_MonthLength(date->wMonth, date->wYear))
518
/* This can happen when moving away from a leap year. */
519
date->wDay = MONTHCAL_MonthLength(date->wMonth, date->wYear);
520
MONTHCAL_CalculateDayOfWeek(date, TRUE);
521
break;
522
case ONEDIGITMONTH:
523
case TWODIGITMONTH:
524
case THREECHARMONTH:
525
case FULLMONTH:
526
date->wMonth = wrap(date->wMonth, delta, 1, 12);
527
MONTHCAL_CalculateDayOfWeek(date, TRUE);
528
delta = 0;
529
/* fall through */
530
case ONEDIGITDAY:
531
case TWODIGITDAY:
532
case THREECHARDAY:
533
case FULLDAY:
534
date->wDay = wrap(date->wDay, delta, 1, MONTHCAL_MonthLength(date->wMonth, date->wYear));
535
MONTHCAL_CalculateDayOfWeek(date, TRUE);
536
break;
537
case ONELETTERAMPM:
538
case TWOLETTERAMPM:
539
delta *= 12;
540
/* fall through */
541
case ONEDIGIT12HOUR:
542
case TWODIGIT12HOUR:
543
case ONEDIGIT24HOUR:
544
case TWODIGIT24HOUR:
545
date->wHour = wrap(date->wHour, delta, 0, 23);
546
break;
547
case ONEDIGITMINUTE:
548
case TWODIGITMINUTE:
549
date->wMinute = wrap(date->wMinute, delta, 0, 59);
550
break;
551
case ONEDIGITSECOND:
552
case TWODIGITSECOND:
553
date->wSecond = wrap(date->wSecond, delta, 0, 59);
554
break;
555
case FORMATCALLBACK:
556
FIXME ("Not implemented\n");
557
break;
558
}
559
560
/* FYI: On 1752/9/14 the calendar changed and England and the
561
* American colonies changed to the Gregorian calendar. This change
562
* involved having September 14th follow September 2nd. So no date
563
* algorithm works before that date.
564
*/
565
if (10000 * date->wYear + 100 * date->wMonth + date->wDay < 17520914) {
566
date->wYear = 1752;
567
date->wMonth = 9;
568
date->wDay = 14;
569
date->wSecond = 0;
570
date->wMinute = 0;
571
date->wHour = 0;
572
}
573
574
/* Ensure time is within bounds */
575
limits = SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, 0, (LPARAM)range);
576
min = delta < 0;
577
578
if (limits & (min ? GDTR_MIN : GDTR_MAX))
579
{
580
int i = (min ? 0 : 1);
581
582
if (MONTHCAL_CompareSystemTime(date, &range[i]) == (min ? -1 : 1))
583
{
584
date->wYear = range[i].wYear;
585
date->wMonth = range[i].wMonth;
586
date->wDayOfWeek = range[i].wDayOfWeek;
587
date->wDay = range[i].wDay;
588
date->wHour = range[i].wHour;
589
date->wMinute = range[i].wMinute;
590
date->wSecond = range[i].wSecond;
591
date->wMilliseconds = range[i].wMilliseconds;
592
}
593
}
594
}
595
596
static int DATETIME_GetFieldWidth (const DATETIME_INFO *infoPtr, HDC hdc, int count)
597
{
598
/* fields are a fixed width, determined by the largest possible string */
599
/* presumably, these widths should be language dependent */
600
int spec;
601
WCHAR buffer[80];
602
LPCWSTR bufptr;
603
SIZE size;
604
605
if (!infoPtr->fieldspec) return 0;
606
607
spec = infoPtr->fieldspec[count];
608
if (spec & DT_STRING) {
609
int txtlen = infoPtr->buflen[count];
610
611
if (txtlen > 79)
612
txtlen = 79;
613
memcpy (buffer, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR));
614
buffer[txtlen] = 0;
615
bufptr = buffer;
616
}
617
else {
618
switch (spec) {
619
case ONEDIGITDAY:
620
case ONEDIGIT12HOUR:
621
case ONEDIGIT24HOUR:
622
case ONEDIGITSECOND:
623
case ONEDIGITMINUTE:
624
case ONEDIGITMONTH:
625
case ONEDIGITYEAR:
626
/* these seem to use a two byte field */
627
case TWODIGITDAY:
628
case TWODIGIT12HOUR:
629
case TWODIGIT24HOUR:
630
case TWODIGITSECOND:
631
case TWODIGITMINUTE:
632
case TWODIGITMONTH:
633
case TWODIGITYEAR:
634
bufptr = L"22";
635
break;
636
case INVALIDFULLYEAR:
637
case FULLYEAR:
638
bufptr = L"2222";
639
break;
640
case THREECHARMONTH:
641
case FULLMONTH:
642
case THREECHARDAY:
643
case FULLDAY:
644
{
645
const WCHAR *fall;
646
LCTYPE lctype;
647
INT i, max_count;
648
LONG cx;
649
650
/* choose locale data type and fallback string */
651
switch (spec) {
652
case THREECHARDAY:
653
fall = L"Wed";
654
lctype = LOCALE_SABBREVDAYNAME1;
655
max_count = 7;
656
break;
657
case FULLDAY:
658
fall = L"Wednesday";
659
lctype = LOCALE_SDAYNAME1;
660
max_count = 7;
661
break;
662
case THREECHARMONTH:
663
fall = L"Dec";
664
lctype = LOCALE_SABBREVMONTHNAME1;
665
max_count = 12;
666
break;
667
default: /* FULLMONTH */
668
fall = L"September";
669
lctype = LOCALE_SMONTHNAME1;
670
max_count = 12;
671
break;
672
}
673
674
cx = 0;
675
for (i = 0; i < max_count; i++)
676
{
677
if(GetLocaleInfoW(LOCALE_USER_DEFAULT, lctype + i,
678
buffer, ARRAY_SIZE(buffer)))
679
{
680
GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &size);
681
if (size.cx > cx) cx = size.cx;
682
}
683
else /* locale independent fallback on failure */
684
{
685
GetTextExtentPoint32W(hdc, fall, lstrlenW(fall), &size);
686
cx = size.cx;
687
break;
688
}
689
}
690
return cx;
691
}
692
case ONELETTERAMPM:
693
bufptr = L"A";
694
break;
695
case TWOLETTERAMPM:
696
bufptr = L"AM";
697
break;
698
default:
699
bufptr = L"2";
700
break;
701
}
702
}
703
GetTextExtentPoint32W (hdc, bufptr, lstrlenW(bufptr), &size);
704
return size.cx;
705
}
706
707
static void DATETIME_DrawBackground (DATETIME_INFO *infoPtr, HDC hdc)
708
{
709
#if __WINE_COMCTL32_VERSION == 6
710
HTHEME theme;
711
712
theme = GetWindowTheme(infoPtr->hwndSelf);
713
if (theme)
714
{
715
int state;
716
717
if (infoPtr->dwStyle & WS_DISABLED)
718
state = ABS_DOWNDISABLED;
719
else if (infoPtr->bCalDepressed)
720
state = ABS_DOWNPRESSED;
721
else if (infoPtr->bCalHot)
722
state = ABS_DOWNHOT;
723
else
724
state = ABS_DOWNNORMAL;
725
726
DrawThemeBackground(theme, hdc, SBP_ARROWBTN, state, &infoPtr->calbutton, NULL);
727
return;
728
}
729
#endif /* __WINE_COMCTL32_VERSION == 6 */
730
731
DrawFrameControl(hdc, &infoPtr->calbutton, DFC_SCROLL, DFCS_SCROLLDOWN |
732
(infoPtr->bCalDepressed ? DFCS_PUSHED : 0) |
733
(infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
734
}
735
736
static void
737
DATETIME_Refresh (DATETIME_INFO *infoPtr, HDC hdc)
738
{
739
TRACE("\n");
740
741
if (infoPtr->dateValid) {
742
int i, prevright;
743
RECT *field;
744
RECT *rcDraw = &infoPtr->rcDraw;
745
SIZE size;
746
COLORREF oldTextColor;
747
HFONT oldFont = SelectObject (hdc, infoPtr->hFont);
748
INT oldBkMode = SetBkMode (hdc, TRANSPARENT);
749
WCHAR txt[80];
750
751
DATETIME_ReturnTxt (infoPtr, 0, txt, ARRAY_SIZE(txt));
752
GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size);
753
rcDraw->bottom = size.cy + 2;
754
755
prevright = infoPtr->checkbox.right = ((infoPtr->dwStyle & DTS_SHOWNONE) ? 18 : 2);
756
757
for (i = 0; i < infoPtr->nrFields; i++) {
758
DATETIME_ReturnTxt (infoPtr, i, txt, ARRAY_SIZE(txt));
759
GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size);
760
field = &infoPtr->fieldRect[i];
761
field->left = prevright;
762
field->right = prevright + DATETIME_GetFieldWidth (infoPtr, hdc, i);
763
field->top = rcDraw->top;
764
field->bottom = rcDraw->bottom;
765
prevright = field->right;
766
767
if (infoPtr->dwStyle & WS_DISABLED)
768
oldTextColor = SetTextColor (hdc, comctl32_color.clrGrayText);
769
else if ((infoPtr->haveFocus) && (i == infoPtr->select)) {
770
RECT selection;
771
772
/* fill if focused */
773
HBRUSH hbr = CreateSolidBrush (comctl32_color.clrActiveCaption);
774
775
if (infoPtr->nCharsEntered)
776
{
777
memcpy(txt, infoPtr->charsEntered, infoPtr->nCharsEntered * sizeof(WCHAR));
778
txt[infoPtr->nCharsEntered] = 0;
779
GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size);
780
}
781
782
SetRect(&selection, 0, 0, size.cx, size.cy);
783
/* center rectangle */
784
OffsetRect(&selection, (field->right + field->left - size.cx)/2,
785
(field->bottom - size.cy)/2);
786
787
FillRect(hdc, &selection, hbr);
788
DeleteObject (hbr);
789
oldTextColor = SetTextColor (hdc, comctl32_color.clrWindow);
790
}
791
else
792
oldTextColor = SetTextColor (hdc, comctl32_color.clrWindowText);
793
794
/* draw the date text using the colour set above */
795
DrawTextW (hdc, txt, lstrlenW(txt), field, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
796
SetTextColor (hdc, oldTextColor);
797
}
798
SetBkMode (hdc, oldBkMode);
799
SelectObject (hdc, oldFont);
800
}
801
802
if (infoPtr->dwStyle & DTS_UPDOWN)
803
return;
804
805
DATETIME_DrawBackground(infoPtr, hdc);
806
}
807
808
809
static INT
810
DATETIME_HitTest (const DATETIME_INFO *infoPtr, POINT pt)
811
{
812
int i;
813
814
TRACE ("%s\n", wine_dbgstr_point(&pt));
815
816
if (PtInRect (&infoPtr->calbutton, pt)) return DTHT_MCPOPUP;
817
if (PtInRect (&infoPtr->checkbox, pt)) return DTHT_CHECKBOX;
818
819
for (i = 0; i < infoPtr->nrFields; i++) {
820
if (PtInRect (&infoPtr->fieldRect[i], pt)) return i;
821
}
822
823
return DTHT_NONE;
824
}
825
826
/* Returns index of the nearest preceding date field from given,
827
or -1 if none was found */
828
static int DATETIME_GetPrevDateField(const DATETIME_INFO *infoPtr, int i)
829
{
830
for(--i; i >= 0; i--)
831
{
832
if (infoPtr->fieldspec[i] & DTHT_DATEFIELD) return i;
833
}
834
return -1;
835
}
836
837
static void
838
DATETIME_ApplySelectedField (DATETIME_INFO *infoPtr)
839
{
840
int fieldNum = infoPtr->select & DTHT_DATEFIELD;
841
int i, val = 0;
842
BOOL clamp_day = FALSE;
843
SYSTEMTIME date = infoPtr->date;
844
int oldyear;
845
846
if (infoPtr->select == -1 || infoPtr->nCharsEntered == 0)
847
return;
848
849
if ((infoPtr->fieldspec[fieldNum] == ONELETTERAMPM) ||
850
(infoPtr->fieldspec[fieldNum] == TWOLETTERAMPM))
851
val = infoPtr->charsEntered[0];
852
else {
853
for (i=0; i<infoPtr->nCharsEntered; i++)
854
val = val * 10 + infoPtr->charsEntered[i] - '0';
855
}
856
857
infoPtr->nCharsEntered = 0;
858
859
switch (infoPtr->fieldspec[fieldNum]) {
860
case ONEDIGITYEAR:
861
case TWODIGITYEAR:
862
oldyear = date.wYear;
863
date.wYear = date.wYear - (date.wYear%100) + val;
864
865
if (DATETIME_IsDateInValidRange(infoPtr, &date))
866
clamp_day = TRUE;
867
else
868
date.wYear = oldyear;
869
870
break;
871
case INVALIDFULLYEAR:
872
case FULLYEAR:
873
oldyear = date.wYear;
874
date.wYear = val;
875
876
if (DATETIME_IsDateInValidRange(infoPtr, &date))
877
clamp_day = TRUE;
878
else
879
date.wYear = oldyear;
880
881
break;
882
case ONEDIGITMONTH:
883
case TWODIGITMONTH:
884
date.wMonth = val;
885
clamp_day = TRUE;
886
break;
887
case ONEDIGITDAY:
888
case TWODIGITDAY:
889
date.wDay = val;
890
break;
891
case ONEDIGIT12HOUR:
892
case TWODIGIT12HOUR:
893
if (val >= 24)
894
val -= 20;
895
896
if (val >= 13)
897
date.wHour = val;
898
else if (val != 0) {
899
if (date.wHour >= 12) /* preserve current AM/PM state */
900
date.wHour = (val == 12 ? 12 : val + 12);
901
else
902
date.wHour = (val == 12 ? 0 : val);
903
}
904
break;
905
case ONEDIGIT24HOUR:
906
case TWODIGIT24HOUR:
907
date.wHour = val;
908
break;
909
case ONEDIGITMINUTE:
910
case TWODIGITMINUTE:
911
date.wMinute = val;
912
break;
913
case ONEDIGITSECOND:
914
case TWODIGITSECOND:
915
date.wSecond = val;
916
break;
917
case ONELETTERAMPM:
918
case TWOLETTERAMPM:
919
if (val == 'a' || val == 'A') {
920
if (date.wHour >= 12)
921
date.wHour -= 12;
922
} else if (val == 'p' || val == 'P') {
923
if (date.wHour < 12)
924
date.wHour += 12;
925
}
926
break;
927
}
928
929
if (clamp_day && date.wDay > MONTHCAL_MonthLength(date.wMonth, date.wYear))
930
date.wDay = MONTHCAL_MonthLength(date.wMonth, date.wYear);
931
932
if (DATETIME_SetSystemTime(infoPtr, GDT_VALID, &date))
933
DATETIME_SendDateTimeChangeNotify (infoPtr);
934
}
935
936
static void
937
DATETIME_SetSelectedField (DATETIME_INFO *infoPtr, int select)
938
{
939
DATETIME_ApplySelectedField(infoPtr);
940
941
infoPtr->select = select;
942
infoPtr->nCharsEntered = 0;
943
}
944
945
static LRESULT
946
DATETIME_LButtonDown (DATETIME_INFO *infoPtr, INT x, INT y)
947
{
948
POINT pt;
949
int new;
950
951
pt.x = x;
952
pt.y = y;
953
new = DATETIME_HitTest (infoPtr, pt);
954
955
SetFocus(infoPtr->hwndSelf);
956
957
if (!(new & DTHT_NODATEMASK) || (new == DTHT_NONE))
958
{
959
if (new == DTHT_NONE)
960
new = infoPtr->nrFields - 1;
961
else
962
{
963
/* hitting string part moves selection to next date field to left */
964
if (infoPtr->fieldspec[new] & DT_STRING)
965
{
966
new = DATETIME_GetPrevDateField(infoPtr, new);
967
if (new == -1) return 0;
968
}
969
/* never select full day of week */
970
if (infoPtr->fieldspec[new] == FULLDAY) return 0;
971
}
972
}
973
974
DATETIME_SetSelectedField(infoPtr, new);
975
976
if (infoPtr->select == DTHT_MCPOPUP) {
977
RECT rcMonthCal;
978
POINT pos;
979
SendMessageW(infoPtr->hMonthCal, MCM_GETMINREQRECT, 0, (LPARAM)&rcMonthCal);
980
981
/* FIXME: button actually is only depressed during dropdown of the */
982
/* calendar control and when the mouse is over the button window */
983
infoPtr->bCalDepressed = TRUE;
984
985
/* recalculate the position of the monthcal popup */
986
if(infoPtr->dwStyle & DTS_RIGHTALIGN)
987
pos.x = infoPtr->calbutton.left - (rcMonthCal.right - rcMonthCal.left);
988
else
989
/* FIXME: this should be after the area reserved for the checkbox */
990
pos.x = infoPtr->rcDraw.left;
991
992
pos.y = infoPtr->rcClient.bottom;
993
OffsetRect( &rcMonthCal, pos.x, pos.y );
994
MapWindowPoints( infoPtr->hwndSelf, 0, (POINT *)&rcMonthCal, 2 );
995
SetWindowPos(infoPtr->hMonthCal, 0, rcMonthCal.left, rcMonthCal.top,
996
rcMonthCal.right - rcMonthCal.left, rcMonthCal.bottom - rcMonthCal.top, 0);
997
998
if(IsWindowVisible(infoPtr->hMonthCal)) {
999
ShowWindow(infoPtr->hMonthCal, SW_HIDE);
1000
infoPtr->bDropdownEnabled = FALSE;
1001
DATETIME_SendSimpleNotify (infoPtr, DTN_CLOSEUP);
1002
} else {
1003
const SYSTEMTIME *lprgSysTimeArray = &infoPtr->date;
1004
TRACE("update calendar %04d/%02d/%02d\n",
1005
lprgSysTimeArray->wYear, lprgSysTimeArray->wMonth, lprgSysTimeArray->wDay);
1006
SendMessageW(infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date));
1007
1008
if (infoPtr->bDropdownEnabled) {
1009
ShowWindow(infoPtr->hMonthCal, SW_SHOW);
1010
DATETIME_SendSimpleNotify (infoPtr, DTN_DROPDOWN);
1011
}
1012
infoPtr->bDropdownEnabled = TRUE;
1013
}
1014
1015
TRACE ("dt:%p mc:%p mc parent:%p, desktop:%p\n",
1016
infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hwndNotify, GetDesktopWindow ());
1017
}
1018
1019
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1020
1021
return 0;
1022
}
1023
1024
1025
static LRESULT
1026
DATETIME_LButtonUp (DATETIME_INFO *infoPtr)
1027
{
1028
if(infoPtr->bCalDepressed) {
1029
infoPtr->bCalDepressed = FALSE;
1030
InvalidateRect(infoPtr->hwndSelf, &(infoPtr->calbutton), TRUE);
1031
}
1032
1033
return 0;
1034
}
1035
1036
1037
static LRESULT
1038
DATETIME_Paint (DATETIME_INFO *infoPtr, HDC hdc)
1039
{
1040
if (!hdc) {
1041
PAINTSTRUCT ps;
1042
hdc = BeginPaint (infoPtr->hwndSelf, &ps);
1043
DATETIME_Refresh (infoPtr, hdc);
1044
EndPaint (infoPtr->hwndSelf, &ps);
1045
} else {
1046
DATETIME_Refresh (infoPtr, hdc);
1047
}
1048
1049
/* Not a click on the dropdown box, enabled it */
1050
infoPtr->bDropdownEnabled = TRUE;
1051
1052
return 0;
1053
}
1054
1055
1056
static LRESULT
1057
DATETIME_Button_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
1058
{
1059
if( HIWORD(wParam) == BN_CLICKED) {
1060
DWORD state = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0);
1061
infoPtr->dateValid = (state == BST_CHECKED);
1062
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1063
DATETIME_SendDateTimeChangeNotify(infoPtr);
1064
}
1065
return 0;
1066
}
1067
1068
1069
1070
static LRESULT
1071
DATETIME_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
1072
{
1073
TRACE("hwndbutton = %p\n", infoPtr->hwndCheckbut);
1074
if(infoPtr->hwndCheckbut == (HWND)lParam)
1075
return DATETIME_Button_Command(infoPtr, wParam, lParam);
1076
return 0;
1077
}
1078
1079
1080
static LRESULT
1081
DATETIME_Enable (DATETIME_INFO *infoPtr, BOOL bEnable)
1082
{
1083
TRACE("%p %s\n", infoPtr, bEnable ? "TRUE" : "FALSE");
1084
if (bEnable)
1085
infoPtr->dwStyle &= ~WS_DISABLED;
1086
else
1087
infoPtr->dwStyle |= WS_DISABLED;
1088
1089
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1090
1091
return 0;
1092
}
1093
1094
1095
static LRESULT
1096
DATETIME_EraseBackground (const DATETIME_INFO *infoPtr, HDC hdc)
1097
{
1098
HBRUSH hBrush, hSolidBrush = NULL;
1099
RECT rc;
1100
1101
if (infoPtr->dwStyle & WS_DISABLED)
1102
hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace);
1103
else
1104
{
1105
hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT,
1106
(WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
1107
if (!hBrush)
1108
hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow);
1109
}
1110
1111
GetClientRect (infoPtr->hwndSelf, &rc);
1112
1113
FillRect (hdc, &rc, hBrush);
1114
1115
if (hSolidBrush)
1116
DeleteObject(hSolidBrush);
1117
1118
return -1;
1119
}
1120
1121
1122
static LRESULT
1123
DATETIME_Notify (DATETIME_INFO *infoPtr, const NMHDR *lpnmh)
1124
{
1125
TRACE ("Got notification %x from %p\n", lpnmh->code, lpnmh->hwndFrom);
1126
TRACE ("info: %p %p %p\n", infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hUpdown);
1127
1128
if (lpnmh->code == MCN_SELECT) {
1129
ShowWindow(infoPtr->hMonthCal, SW_HIDE);
1130
infoPtr->dateValid = TRUE;
1131
SendMessageW (infoPtr->hMonthCal, MCM_GETCURSEL, 0, (LPARAM)&infoPtr->date);
1132
TRACE("got from calendar %04d/%02d/%02d day of week %d\n",
1133
infoPtr->date.wYear, infoPtr->date.wMonth, infoPtr->date.wDay, infoPtr->date.wDayOfWeek);
1134
SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0);
1135
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1136
DATETIME_SendDateTimeChangeNotify (infoPtr);
1137
DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP);
1138
}
1139
if ((lpnmh->hwndFrom == infoPtr->hUpdown) && (lpnmh->code == UDN_DELTAPOS)) {
1140
const NM_UPDOWN *lpnmud = (const NM_UPDOWN*)lpnmh;
1141
TRACE("Delta pos %d\n", lpnmud->iDelta);
1142
infoPtr->pendingUpdown = lpnmud->iDelta;
1143
}
1144
return 0;
1145
}
1146
1147
1148
static LRESULT
1149
DATETIME_KeyDown (DATETIME_INFO *infoPtr, DWORD vkCode)
1150
{
1151
int fieldNum = infoPtr->select & DTHT_DATEFIELD;
1152
int wrap = 0;
1153
int new;
1154
1155
if (!(infoPtr->haveFocus)) return 0;
1156
if ((fieldNum==0) && (infoPtr->select)) return 0;
1157
1158
if (infoPtr->select & FORMATCALLMASK) {
1159
FIXME ("Callbacks not implemented yet\n");
1160
}
1161
1162
switch (vkCode) {
1163
case VK_ADD:
1164
case VK_UP:
1165
infoPtr->nCharsEntered = 0;
1166
DATETIME_IncreaseField (infoPtr, fieldNum, 1);
1167
DATETIME_SendDateTimeChangeNotify (infoPtr);
1168
break;
1169
case VK_SUBTRACT:
1170
case VK_DOWN:
1171
infoPtr->nCharsEntered = 0;
1172
DATETIME_IncreaseField (infoPtr, fieldNum, -1);
1173
DATETIME_SendDateTimeChangeNotify (infoPtr);
1174
break;
1175
case VK_HOME:
1176
infoPtr->nCharsEntered = 0;
1177
DATETIME_IncreaseField (infoPtr, fieldNum, INT_MIN);
1178
DATETIME_SendDateTimeChangeNotify (infoPtr);
1179
break;
1180
case VK_END:
1181
infoPtr->nCharsEntered = 0;
1182
DATETIME_IncreaseField (infoPtr, fieldNum, INT_MAX);
1183
DATETIME_SendDateTimeChangeNotify (infoPtr);
1184
break;
1185
case VK_LEFT:
1186
new = infoPtr->select;
1187
do {
1188
if (new == 0) {
1189
new = new - 1;
1190
wrap++;
1191
} else {
1192
new--;
1193
}
1194
} while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2));
1195
if (new != infoPtr->select)
1196
DATETIME_SetSelectedField(infoPtr, new);
1197
break;
1198
case VK_RIGHT:
1199
new = infoPtr->select;
1200
do {
1201
new++;
1202
if (new==infoPtr->nrFields) {
1203
new = 0;
1204
wrap++;
1205
}
1206
} while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2));
1207
if (new != infoPtr->select)
1208
DATETIME_SetSelectedField(infoPtr, new);
1209
break;
1210
}
1211
1212
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1213
1214
return 0;
1215
}
1216
1217
1218
static LRESULT
1219
DATETIME_Char (DATETIME_INFO *infoPtr, WPARAM vkCode)
1220
{
1221
int fieldNum, fieldSpec;
1222
1223
fieldNum = infoPtr->select & DTHT_DATEFIELD;
1224
fieldSpec = infoPtr->fieldspec[fieldNum];
1225
1226
if (fieldSpec == ONELETTERAMPM || fieldSpec == TWOLETTERAMPM) {
1227
infoPtr->charsEntered[0] = vkCode;
1228
infoPtr->nCharsEntered = 1;
1229
1230
DATETIME_ApplySelectedField(infoPtr);
1231
} else if (vkCode >= '0' && vkCode <= '9') {
1232
int maxChars;
1233
1234
infoPtr->charsEntered[infoPtr->nCharsEntered++] = vkCode;
1235
1236
if (fieldSpec == INVALIDFULLYEAR || fieldSpec == FULLYEAR)
1237
maxChars = 4;
1238
else
1239
maxChars = 2;
1240
1241
if ((fieldSpec == ONEDIGIT12HOUR ||
1242
fieldSpec == TWODIGIT12HOUR ||
1243
fieldSpec == ONEDIGIT24HOUR ||
1244
fieldSpec == TWODIGIT24HOUR) &&
1245
(infoPtr->nCharsEntered == 1))
1246
{
1247
if (vkCode >= '3')
1248
maxChars = 1;
1249
}
1250
1251
if (maxChars == infoPtr->nCharsEntered)
1252
DATETIME_ApplySelectedField(infoPtr);
1253
}
1254
1255
return 0;
1256
}
1257
1258
1259
static LRESULT
1260
DATETIME_VScroll (DATETIME_INFO *infoPtr, WORD wScroll)
1261
{
1262
int fieldNum = infoPtr->select & DTHT_DATEFIELD;
1263
1264
if ((SHORT)LOWORD(wScroll) != SB_THUMBPOSITION) return 0;
1265
if (!(infoPtr->haveFocus)) return 0;
1266
if ((fieldNum==0) && (infoPtr->select)) return 0;
1267
1268
if (infoPtr->pendingUpdown >= 0) {
1269
DATETIME_IncreaseField (infoPtr, fieldNum, 1);
1270
DATETIME_SendDateTimeChangeNotify (infoPtr);
1271
}
1272
else {
1273
DATETIME_IncreaseField (infoPtr, fieldNum, -1);
1274
DATETIME_SendDateTimeChangeNotify (infoPtr);
1275
}
1276
1277
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1278
1279
return 0;
1280
}
1281
1282
1283
static LRESULT
1284
DATETIME_KillFocus (DATETIME_INFO *infoPtr, HWND lostFocus)
1285
{
1286
TRACE("lost focus to %p\n", lostFocus);
1287
1288
if (infoPtr->haveFocus) {
1289
DATETIME_SendSimpleNotify (infoPtr, NM_KILLFOCUS);
1290
infoPtr->haveFocus = 0;
1291
DATETIME_SetSelectedField (infoPtr, -1);
1292
}
1293
1294
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
1295
1296
return 0;
1297
}
1298
1299
1300
static LRESULT
1301
DATETIME_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs)
1302
{
1303
DWORD dwExStyle = GetWindowLongW(hwnd, GWL_EXSTYLE);
1304
/* force control to have client edge */
1305
dwExStyle |= WS_EX_CLIENTEDGE;
1306
SetWindowLongW(hwnd, GWL_EXSTYLE, dwExStyle);
1307
1308
return 1;
1309
}
1310
1311
static LRESULT DATETIME_MouseMove (DATETIME_INFO *infoPtr, LONG x, LONG y)
1312
{
1313
TRACKMOUSEEVENT event;
1314
POINT point;
1315
BOOL hot;
1316
1317
point.x = x;
1318
point.y = y;
1319
hot = PtInRect(&infoPtr->calbutton, point);
1320
if (hot != infoPtr->bCalHot)
1321
{
1322
infoPtr->bCalHot = hot;
1323
RedrawWindow(infoPtr->hwndSelf, &infoPtr->calbutton, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1324
}
1325
1326
if (!hot)
1327
return 0;
1328
1329
event.cbSize = sizeof(TRACKMOUSEEVENT);
1330
event.dwFlags = TME_QUERY;
1331
if (!TrackMouseEvent(&event) || event.hwndTrack != infoPtr->hwndSelf || !(event.dwFlags & TME_LEAVE))
1332
{
1333
event.hwndTrack = infoPtr->hwndSelf;
1334
event.dwFlags = TME_LEAVE;
1335
TrackMouseEvent(&event);
1336
}
1337
1338
return 0;
1339
}
1340
1341
static LRESULT DATETIME_MouseLeave (DATETIME_INFO *infoPtr)
1342
{
1343
infoPtr->bCalHot = FALSE;
1344
RedrawWindow(infoPtr->hwndSelf, &infoPtr->calbutton, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1345
return 0;
1346
}
1347
1348
static LRESULT
1349
DATETIME_SetFocus (DATETIME_INFO *infoPtr, HWND lostFocus)
1350
{
1351
TRACE("got focus from %p\n", lostFocus);
1352
1353
/* if monthcal is open and it loses focus, close monthcal */
1354
if (infoPtr->hMonthCal && (lostFocus == infoPtr->hMonthCal) &&
1355
IsWindowVisible(infoPtr->hMonthCal))
1356
{
1357
ShowWindow(infoPtr->hMonthCal, SW_HIDE);
1358
DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP);
1359
/* note: this get triggered even if monthcal loses focus to a dropdown
1360
* box click, which occurs without an intermediate WM_PAINT call
1361
*/
1362
infoPtr->bDropdownEnabled = FALSE;
1363
return 0;
1364
}
1365
1366
if (infoPtr->haveFocus == 0) {
1367
DATETIME_SendSimpleNotify (infoPtr, NM_SETFOCUS);
1368
infoPtr->haveFocus = DTHT_GOTFOCUS;
1369
}
1370
1371
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1372
1373
return 0;
1374
}
1375
1376
1377
static BOOL
1378
DATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr)
1379
{
1380
NMDATETIMECHANGE dtdtc;
1381
1382
dtdtc.nmhdr.hwndFrom = infoPtr->hwndSelf;
1383
dtdtc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1384
dtdtc.nmhdr.code = DTN_DATETIMECHANGE;
1385
1386
dtdtc.dwFlags = infoPtr->dateValid ? GDT_VALID : GDT_NONE;
1387
1388
dtdtc.st = infoPtr->date;
1389
return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
1390
dtdtc.nmhdr.idFrom, (LPARAM)&dtdtc);
1391
}
1392
1393
1394
static BOOL
1395
DATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code)
1396
{
1397
NMHDR nmhdr;
1398
1399
TRACE("%x\n", code);
1400
nmhdr.hwndFrom = infoPtr->hwndSelf;
1401
nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1402
nmhdr.code = code;
1403
1404
return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
1405
nmhdr.idFrom, (LPARAM)&nmhdr);
1406
}
1407
1408
static LRESULT
1409
DATETIME_Size (DATETIME_INFO *infoPtr, INT width, INT height)
1410
{
1411
/* set size */
1412
infoPtr->rcClient.bottom = height;
1413
infoPtr->rcClient.right = width;
1414
1415
TRACE("Height %ld, Width %ld\n", infoPtr->rcClient.bottom, infoPtr->rcClient.right);
1416
1417
infoPtr->rcDraw = infoPtr->rcClient;
1418
1419
if (infoPtr->dwStyle & DTS_UPDOWN) {
1420
SetWindowPos(infoPtr->hUpdown, NULL,
1421
infoPtr->rcClient.right-14, 0,
1422
15, infoPtr->rcClient.bottom - infoPtr->rcClient.top,
1423
SWP_NOACTIVATE | SWP_NOZORDER);
1424
}
1425
else {
1426
/* set the size of the button that drops the calendar down */
1427
/* FIXME: account for style that allows button on left side */
1428
infoPtr->calbutton.top = infoPtr->rcDraw.top;
1429
infoPtr->calbutton.bottom= infoPtr->rcDraw.bottom;
1430
infoPtr->calbutton.left = infoPtr->rcDraw.right-15;
1431
infoPtr->calbutton.right = infoPtr->rcDraw.right;
1432
}
1433
1434
/* set enable/disable button size for show none style being enabled */
1435
/* FIXME: these dimensions are completely incorrect */
1436
infoPtr->checkbox.top = infoPtr->rcDraw.top;
1437
infoPtr->checkbox.bottom = infoPtr->rcDraw.bottom;
1438
infoPtr->checkbox.left = infoPtr->rcDraw.left;
1439
infoPtr->checkbox.right = infoPtr->rcDraw.left + 10;
1440
1441
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1442
1443
return 0;
1444
}
1445
1446
static LRESULT
1447
DATETIME_StyleChanging(DATETIME_INFO *infoPtr, WPARAM wStyleType, STYLESTRUCT *lpss)
1448
{
1449
TRACE("styletype %Ix, styleOld %#lx, styleNew %#lx\n", wStyleType, lpss->styleOld, lpss->styleNew);
1450
1451
/* block DTS_SHOWNONE change */
1452
if ((lpss->styleNew ^ lpss->styleOld) & DTS_SHOWNONE)
1453
{
1454
if (lpss->styleOld & DTS_SHOWNONE)
1455
lpss->styleNew |= DTS_SHOWNONE;
1456
else
1457
lpss->styleNew &= ~DTS_SHOWNONE;
1458
}
1459
1460
return 0;
1461
}
1462
1463
static LRESULT
1464
DATETIME_StyleChanged(DATETIME_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss)
1465
{
1466
TRACE("styletype %Ix, styleOld %#lx, styleNew %#lx\n", wStyleType, lpss->styleOld, lpss->styleNew);
1467
1468
if (wStyleType != GWL_STYLE) return 0;
1469
1470
infoPtr->dwStyle = lpss->styleNew;
1471
1472
if ( !(lpss->styleOld & DTS_SHOWNONE) && (lpss->styleNew & DTS_SHOWNONE) ) {
1473
infoPtr->hwndCheckbut = CreateWindowExW (0, WC_BUTTONW, 0, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
1474
2, 2, 13, 13, infoPtr->hwndSelf, 0,
1475
(HINSTANCE)GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_HINSTANCE), 0);
1476
SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, infoPtr->dateValid ? 1 : 0, 0);
1477
}
1478
if ( (lpss->styleOld & DTS_SHOWNONE) && !(lpss->styleNew & DTS_SHOWNONE) ) {
1479
DestroyWindow(infoPtr->hwndCheckbut);
1480
infoPtr->hwndCheckbut = 0;
1481
}
1482
if ( !(lpss->styleOld & DTS_UPDOWN) && (lpss->styleNew & DTS_UPDOWN) ) {
1483
infoPtr->hUpdown = CreateUpDownControl (WS_CHILD | WS_BORDER | WS_VISIBLE, 120, 1, 20, 20,
1484
infoPtr->hwndSelf, 1, 0, 0, UD_MAXVAL, UD_MINVAL, 0);
1485
}
1486
if ( (lpss->styleOld & DTS_UPDOWN) && !(lpss->styleNew & DTS_UPDOWN) ) {
1487
DestroyWindow(infoPtr->hUpdown);
1488
infoPtr->hUpdown = 0;
1489
}
1490
return 0;
1491
}
1492
1493
static BOOL DATETIME_GetIdealSize(DATETIME_INFO *infoPtr, SIZE *size)
1494
{
1495
SIZE field_size;
1496
RECT rect;
1497
WCHAR txt[80];
1498
HDC hdc;
1499
HFONT oldFont;
1500
int i;
1501
1502
size->cx = size->cy = 0;
1503
1504
hdc = GetDC(infoPtr->hwndSelf);
1505
oldFont = SelectObject(hdc, infoPtr->hFont);
1506
1507
/* Get text font height */
1508
DATETIME_ReturnTxt(infoPtr, 0, txt, ARRAY_SIZE(txt));
1509
GetTextExtentPoint32W(hdc, txt, lstrlenW(txt), &field_size);
1510
size->cy = field_size.cy;
1511
1512
/* Get text font width */
1513
for (i = 0; i < infoPtr->nrFields; i++)
1514
{
1515
size->cx += DATETIME_GetFieldWidth(infoPtr, hdc, i);
1516
}
1517
1518
SelectObject(hdc, oldFont);
1519
ReleaseDC(infoPtr->hwndSelf, hdc);
1520
1521
if (infoPtr->dwStyle & DTS_UPDOWN)
1522
{
1523
GetWindowRect(infoPtr->hUpdown, &rect);
1524
size->cx += rect.right - rect.left;
1525
}
1526
else
1527
{
1528
size->cx += infoPtr->calbutton.right - infoPtr->calbutton.left;
1529
}
1530
1531
if (infoPtr->dwStyle & DTS_SHOWNONE)
1532
{
1533
size->cx += infoPtr->checkbox.right - infoPtr->checkbox.left;
1534
}
1535
1536
/* Add space between controls for them not to get too close */
1537
size->cx += 12;
1538
size->cy += 4;
1539
1540
TRACE("cx %ld, cy %ld\n", size->cx, size->cy);
1541
return TRUE;
1542
}
1543
1544
static LRESULT
1545
DATETIME_SetFont (DATETIME_INFO *infoPtr, HFONT font, BOOL repaint)
1546
{
1547
infoPtr->hFont = font;
1548
if (repaint) InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1549
return 0;
1550
}
1551
1552
1553
static LRESULT
1554
DATETIME_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
1555
{
1556
DATETIME_INFO *infoPtr = Alloc (sizeof(DATETIME_INFO));
1557
STYLESTRUCT ss = { 0, lpcs->style };
1558
1559
if (!infoPtr) return -1;
1560
1561
infoPtr->hwndSelf = hwnd;
1562
infoPtr->dwStyle = lpcs->style;
1563
1564
infoPtr->nrFieldsAllocated = 32;
1565
infoPtr->fieldspec = Alloc (infoPtr->nrFieldsAllocated * sizeof(int));
1566
infoPtr->fieldRect = Alloc (infoPtr->nrFieldsAllocated * sizeof(RECT));
1567
infoPtr->buflen = Alloc (infoPtr->nrFieldsAllocated * sizeof(int));
1568
infoPtr->hwndNotify = lpcs->hwndParent;
1569
infoPtr->select = -1; /* initially, nothing is selected */
1570
infoPtr->bDropdownEnabled = TRUE;
1571
infoPtr->bCalHot = FALSE;
1572
1573
DATETIME_StyleChanged(infoPtr, GWL_STYLE, &ss);
1574
DATETIME_SetFormatW (infoPtr, 0);
1575
1576
/* create the monthcal control */
1577
infoPtr->hMonthCal = CreateWindowExW (0, MONTHCAL_CLASSW, 0, WS_BORDER | WS_POPUP | WS_CLIPSIBLINGS,
1578
0, 0, 0, 0, infoPtr->hwndSelf, 0, 0, 0);
1579
1580
/* initialize info structure */
1581
GetLocalTime (&infoPtr->date);
1582
infoPtr->dateValid = TRUE;
1583
infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1584
1585
SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
1586
COMCTL32_OpenThemeForWindow(hwnd, WC_SCROLLBARW);
1587
1588
return 0;
1589
}
1590
1591
1592
1593
static LRESULT
1594
DATETIME_Destroy (DATETIME_INFO *infoPtr)
1595
{
1596
COMCTL32_CloseThemeForWindow(infoPtr->hwndSelf);
1597
1598
if (infoPtr->hwndCheckbut)
1599
DestroyWindow(infoPtr->hwndCheckbut);
1600
if (infoPtr->hUpdown)
1601
DestroyWindow(infoPtr->hUpdown);
1602
if (infoPtr->hMonthCal)
1603
DestroyWindow(infoPtr->hMonthCal);
1604
SetWindowLongPtrW( infoPtr->hwndSelf, 0, 0 ); /* clear infoPtr */
1605
Free (infoPtr->buflen);
1606
Free (infoPtr->fieldRect);
1607
Free (infoPtr->fieldspec);
1608
Free (infoPtr);
1609
return 0;
1610
}
1611
1612
1613
static INT
1614
DATETIME_GetText (const DATETIME_INFO *infoPtr, INT count, LPWSTR dst)
1615
{
1616
WCHAR buf[80];
1617
int i;
1618
1619
if (!dst || (count <= 0)) return 0;
1620
1621
dst[0] = 0;
1622
for (i = 0; i < infoPtr->nrFields; i++)
1623
{
1624
DATETIME_ReturnTxt(infoPtr, i, buf, ARRAY_SIZE(buf));
1625
if ((lstrlenW(dst) + lstrlenW(buf)) < count)
1626
lstrcatW(dst, buf);
1627
else break;
1628
}
1629
return lstrlenW(dst);
1630
}
1631
1632
static int DATETIME_GetTextLength(const DATETIME_INFO *info)
1633
{
1634
int i, length = 0;
1635
WCHAR buffer[80];
1636
1637
TRACE("%p.\n", info);
1638
1639
for (i = 0; i < info->nrFields; i++)
1640
{
1641
DATETIME_ReturnTxt(info, i, buffer, ARRAY_SIZE(buffer));
1642
length += lstrlenW(buffer);
1643
}
1644
return length;
1645
}
1646
1647
static LRESULT WINAPI
1648
DATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1649
{
1650
DATETIME_INFO *infoPtr = ((DATETIME_INFO *)GetWindowLongPtrW (hwnd, 0));
1651
1652
TRACE("%x, %Ix, %Ix\n", uMsg, wParam, lParam);
1653
1654
if (!infoPtr && (uMsg != WM_CREATE) && (uMsg != WM_NCCREATE))
1655
return DefWindowProcW( hwnd, uMsg, wParam, lParam );
1656
1657
switch (uMsg) {
1658
1659
case DTM_GETSYSTEMTIME:
1660
return DATETIME_GetSystemTime (infoPtr, (SYSTEMTIME *) lParam);
1661
1662
case DTM_SETSYSTEMTIME:
1663
return DATETIME_SetSystemTime (infoPtr, wParam, (SYSTEMTIME *) lParam);
1664
1665
case DTM_GETRANGE:
1666
return SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, wParam, lParam);
1667
1668
case DTM_SETRANGE:
1669
return SendMessageW (infoPtr->hMonthCal, MCM_SETRANGE, wParam, lParam);
1670
1671
case DTM_SETFORMATA:
1672
return DATETIME_SetFormatA (infoPtr, (LPCSTR)lParam);
1673
1674
case DTM_SETFORMATW:
1675
return DATETIME_SetFormatW (infoPtr, (LPCWSTR)lParam);
1676
1677
case DTM_GETMONTHCAL:
1678
return (LRESULT)infoPtr->hMonthCal;
1679
1680
case DTM_SETMCCOLOR:
1681
return SendMessageW (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
1682
1683
case DTM_GETMCCOLOR:
1684
return SendMessageW (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
1685
1686
case DTM_SETMCFONT:
1687
return SendMessageW (infoPtr->hMonthCal, WM_SETFONT, wParam, lParam);
1688
1689
case DTM_GETMCFONT:
1690
return SendMessageW (infoPtr->hMonthCal, WM_GETFONT, wParam, lParam);
1691
1692
case DTM_GETIDEALSIZE:
1693
return DATETIME_GetIdealSize(infoPtr, (SIZE *)lParam);
1694
1695
case WM_NOTIFY:
1696
return DATETIME_Notify (infoPtr, (LPNMHDR)lParam);
1697
1698
case WM_ENABLE:
1699
return DATETIME_Enable (infoPtr, (BOOL)wParam);
1700
1701
case WM_ERASEBKGND:
1702
return DATETIME_EraseBackground (infoPtr, (HDC)wParam);
1703
1704
case WM_GETDLGCODE:
1705
return DLGC_WANTARROWS | DLGC_WANTCHARS;
1706
1707
case WM_PRINTCLIENT:
1708
case WM_PAINT:
1709
return DATETIME_Paint (infoPtr, (HDC)wParam);
1710
1711
case WM_KEYDOWN:
1712
return DATETIME_KeyDown (infoPtr, wParam);
1713
1714
case WM_CHAR:
1715
return DATETIME_Char (infoPtr, wParam);
1716
1717
case WM_KILLFOCUS:
1718
return DATETIME_KillFocus (infoPtr, (HWND)wParam);
1719
1720
case WM_NCCREATE:
1721
return DATETIME_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);
1722
1723
case WM_NCPAINT:
1724
return COMCTL32_NCPaint(hwnd, wParam, lParam, WC_EDITW);
1725
1726
case WM_MOUSEMOVE:
1727
return DATETIME_MouseMove(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
1728
1729
case WM_MOUSELEAVE:
1730
return DATETIME_MouseLeave(infoPtr);
1731
1732
case WM_SETFOCUS:
1733
return DATETIME_SetFocus (infoPtr, (HWND)wParam);
1734
1735
case WM_SIZE:
1736
return DATETIME_Size (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
1737
1738
case WM_LBUTTONDOWN:
1739
return DATETIME_LButtonDown (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
1740
1741
case WM_LBUTTONUP:
1742
return DATETIME_LButtonUp (infoPtr);
1743
1744
case WM_VSCROLL:
1745
return DATETIME_VScroll (infoPtr, (WORD)wParam);
1746
1747
case WM_CREATE:
1748
return DATETIME_Create (hwnd, (LPCREATESTRUCTW)lParam);
1749
1750
case WM_DESTROY:
1751
return DATETIME_Destroy (infoPtr);
1752
1753
case WM_COMMAND:
1754
return DATETIME_Command (infoPtr, wParam, lParam);
1755
1756
case WM_STYLECHANGING:
1757
return DATETIME_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
1758
1759
case WM_STYLECHANGED:
1760
return DATETIME_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
1761
1762
case WM_THEMECHANGED:
1763
return COMCTL32_ThemeChanged(infoPtr->hwndSelf, WC_SCROLLBARW, TRUE, TRUE);
1764
1765
case WM_SETFONT:
1766
return DATETIME_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
1767
1768
case WM_GETFONT:
1769
return (LRESULT) infoPtr->hFont;
1770
1771
case WM_GETTEXT:
1772
return (LRESULT) DATETIME_GetText(infoPtr, wParam, (LPWSTR)lParam);
1773
1774
case WM_GETTEXTLENGTH:
1775
return (LRESULT)DATETIME_GetTextLength(infoPtr);
1776
1777
case WM_SETTEXT:
1778
return CB_ERR;
1779
1780
default:
1781
if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
1782
ERR("unknown msg %04x, wp %Ix, lp %Ix\n", uMsg, wParam, lParam);
1783
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1784
}
1785
}
1786
1787
1788
void
1789
DATETIME_Register (void)
1790
{
1791
WNDCLASSW wndClass;
1792
1793
ZeroMemory (&wndClass, sizeof(WNDCLASSW));
1794
wndClass.style = CS_GLOBALCLASS;
1795
wndClass.lpfnWndProc = DATETIME_WindowProc;
1796
wndClass.cbClsExtra = 0;
1797
wndClass.cbWndExtra = sizeof(DATETIME_INFO *);
1798
wndClass.hCursor = LoadCursorW (0, (LPCWSTR)IDC_ARROW);
1799
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1800
wndClass.lpszClassName = DATETIMEPICK_CLASSW;
1801
1802
RegisterClassW (&wndClass);
1803
}
1804
1805