~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/comctl32/datetime.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.