ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

datetime.c
Go to the documentation of this file.
00001 /*
00002  * Date and time picker control
00003  *
00004  * Copyright 1998, 1999 Eric Kohl
00005  * Copyright 1999, 2000 Alex Priem <alexp@sci.kun.nl>
00006  * Copyright 2000 Chris Morgan <cmorgan@wpi.edu>
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00021  *
00022  * NOTE
00023  * 
00024  * This code was audited for completeness against the documented features
00025  * of Comctl32.dll version 6.0 on Oct. 20, 2004, by Dimitrie O. Paun.
00026  * 
00027  * Unless otherwise noted, we believe this code to be complete, as per
00028  * the specification mentioned above.
00029  * If you discover missing features, or bugs, please note them below.
00030  * 
00031  * TODO:
00032  *    -- DTS_APPCANPARSE
00033  *    -- DTS_SHORTDATECENTURYFORMAT
00034  *    -- DTN_FORMAT
00035  *    -- DTN_FORMATQUERY
00036  *    -- DTN_USERSTRING
00037  *    -- DTN_WMKEYDOWN
00038  *    -- FORMATCALLBACK
00039  */
00040 
00041 #include <math.h>
00042 #include <string.h>
00043 #include <stdarg.h>
00044 #include <stdio.h>
00045 #include <limits.h>
00046 
00047 #include "windef.h"
00048 #include "winbase.h"
00049 #include "wingdi.h"
00050 #include "winuser.h"
00051 #include "winnls.h"
00052 #include "commctrl.h"
00053 #include "comctl32.h"
00054 #include "wine/debug.h"
00055 #include "wine/unicode.h"
00056 
00057 WINE_DEFAULT_DEBUG_CHANNEL(datetime);
00058 
00059 typedef struct
00060 {
00061     HWND hwndSelf;
00062     HWND hMonthCal;
00063     HWND hwndNotify;
00064     HWND hUpdown;
00065     DWORD dwStyle;
00066     SYSTEMTIME date;
00067     BOOL dateValid;
00068     HWND hwndCheckbut;
00069     RECT rcClient; /* rect around the edge of the window */
00070     RECT rcDraw; /* rect inside of the border */
00071     RECT checkbox;  /* checkbox allowing the control to be enabled/disabled */
00072     RECT calbutton; /* button that toggles the dropdown of the monthcal control */
00073     BOOL bCalDepressed; /* TRUE = cal button is depressed */
00074     int  bDropdownEnabled;
00075     int  select;
00076     WCHAR charsEntered[4];
00077     int nCharsEntered;
00078     HFONT hFont;
00079     int nrFieldsAllocated;
00080     int nrFields;
00081     int haveFocus;
00082     int *fieldspec;
00083     RECT *fieldRect;
00084     int  *buflen;
00085     WCHAR textbuf[256];
00086     POINT monthcal_pos;
00087     int pendingUpdown;
00088 } DATETIME_INFO, *LPDATETIME_INFO;
00089 
00090 /* in monthcal.c */
00091 extern int MONTHCAL_MonthLength(int month, int year);
00092 extern int MONTHCAL_CalculateDayOfWeek(SYSTEMTIME *date, BOOL inplace);
00093 
00094 /* this list of defines is closely related to `allowedformatchars' defined
00095  * in datetime.c; the high nibble indicates the `base type' of the format
00096  * specifier.
00097  * Do not change without first reading DATETIME_UseFormat.
00098  *
00099  */
00100 
00101 #define DT_END_FORMAT      0
00102 #define ONEDIGITDAY     0x01
00103 #define TWODIGITDAY     0x02
00104 #define THREECHARDAY    0x03
00105 #define FULLDAY         0x04
00106 #define ONEDIGIT12HOUR  0x11
00107 #define TWODIGIT12HOUR  0x12
00108 #define ONEDIGIT24HOUR  0x21
00109 #define TWODIGIT24HOUR  0x22
00110 #define ONEDIGITMINUTE  0x31
00111 #define TWODIGITMINUTE  0x32
00112 #define ONEDIGITMONTH   0x41
00113 #define TWODIGITMONTH   0x42
00114 #define THREECHARMONTH  0x43
00115 #define FULLMONTH       0x44
00116 #define ONEDIGITSECOND  0x51
00117 #define TWODIGITSECOND  0x52
00118 #define ONELETTERAMPM   0x61
00119 #define TWOLETTERAMPM   0x62
00120 #define ONEDIGITYEAR    0x71
00121 #define TWODIGITYEAR    0x72
00122 #define INVALIDFULLYEAR 0x73      /* FIXME - yyy is not valid - we'll treat it as yyyy */
00123 #define FULLYEAR        0x74
00124 #define FORMATCALLBACK  0x81      /* -> maximum of 0x80 callbacks possible */
00125 #define FORMATCALLMASK  0x80
00126 #define DT_STRING   0x0100
00127 
00128 #define DTHT_DATEFIELD  0xff      /* for hit-testing */
00129 
00130 #define DTHT_NONE       0x1000
00131 #define DTHT_CHECKBOX   0x2000  /* these should end at '00' , to make */
00132 #define DTHT_MCPOPUP    0x3000  /* & DTHT_DATEFIELD 0 when DATETIME_KeyDown */
00133 #define DTHT_GOTFOCUS   0x4000  /* tests for date-fields */
00134 #define DTHT_NODATEMASK 0xf000  /* to mask check and drop down from others */
00135 
00136 static BOOL DATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code);
00137 static BOOL DATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr);
00138 static const WCHAR allowedformatchars[] = {'d', 'h', 'H', 'm', 'M', 's', 't', 'y', 'X', 0};
00139 static const int maxrepetition [] = {4,2,2,2,4,2,2,4,-1};
00140 
00141 
00142 static DWORD
00143 DATETIME_GetSystemTime (const DATETIME_INFO *infoPtr, SYSTEMTIME *systime)
00144 {
00145     if (!systime) return GDT_NONE;
00146 
00147     if ((infoPtr->dwStyle & DTS_SHOWNONE) &&
00148         (SendMessageW (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0) == BST_UNCHECKED))
00149         return GDT_NONE;
00150 
00151     *systime = infoPtr->date;
00152 
00153     return GDT_VALID;
00154 }
00155 
00156 
00157 static BOOL
00158 DATETIME_SetSystemTime (DATETIME_INFO *infoPtr, DWORD flag, const SYSTEMTIME *systime)
00159 {
00160     if (!systime) return 0;
00161 
00162     TRACE("%04d/%02d/%02d %02d:%02d:%02d\n",
00163           systime->wYear, systime->wMonth, systime->wDay,
00164           systime->wHour, systime->wMinute, systime->wSecond);
00165 
00166     if (flag == GDT_VALID) {
00167       if (systime->wYear < 1601 || systime->wYear > 30827 ||
00168           systime->wMonth < 1 || systime->wMonth > 12 ||
00169           systime->wDay < 1 ||
00170           systime->wDay > MONTHCAL_MonthLength(systime->wMonth, systime->wYear) ||
00171           systime->wHour > 23 ||
00172           systime->wMinute > 59 ||
00173           systime->wSecond > 59 ||
00174           systime->wMilliseconds > 999
00175           )
00176         return FALSE;
00177 
00178         infoPtr->dateValid = TRUE;
00179         infoPtr->date = *systime;
00180         /* always store a valid day of week */
00181         MONTHCAL_CalculateDayOfWeek(&infoPtr->date, TRUE);
00182 
00183         SendMessageW (infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date));
00184         SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0);
00185     } else if ((infoPtr->dwStyle & DTS_SHOWNONE) && (flag == GDT_NONE)) {
00186         infoPtr->dateValid = FALSE;
00187         SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_UNCHECKED, 0);
00188     }
00189     else
00190         return FALSE;
00191 
00192     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
00193     return TRUE;
00194 }
00195 
00196 
00197 /***
00198  * Split up a formattxt in actions.
00199  * See ms documentation for the meaning of the letter codes/'specifiers'.
00200  *
00201  * Notes:
00202  * *'dddddd' is handled as 'dddd' plus 'dd'.
00203  * *unrecognized formats are strings (here given the type DT_STRING;
00204  * start of the string is encoded in lower bits of DT_STRING.
00205  * Therefore, 'string' ends finally up as '<show seconds>tring'.
00206  *
00207  */
00208 static void
00209 DATETIME_UseFormat (DATETIME_INFO *infoPtr, LPCWSTR formattxt)
00210 {
00211     unsigned int i;
00212     int j, k, len;
00213     BOOL inside_literal = FALSE; /* inside '...' */
00214     int *nrFields = &infoPtr->nrFields;
00215 
00216     *nrFields = 0;
00217     infoPtr->fieldspec[*nrFields] = 0;
00218     len = strlenW(allowedformatchars);
00219     k = 0;
00220 
00221     for (i = 0; formattxt[i]; i++)  {
00222     TRACE ("\n%d %c:", i, formattxt[i]);
00223     if (!inside_literal) {
00224         for (j = 0; j < len; j++) {
00225             if (allowedformatchars[j]==formattxt[i]) {
00226                     TRACE ("%c[%d,%x]", allowedformatchars[j], *nrFields, infoPtr->fieldspec[*nrFields]);
00227                     if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
00228                         infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
00229                         break;
00230                     }
00231                     if (infoPtr->fieldspec[*nrFields] >> 4 != j) {
00232                         (*nrFields)++;
00233                         infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
00234                         break;
00235                     }
00236                     if ((infoPtr->fieldspec[*nrFields] & 0x0f) == maxrepetition[j]) {
00237                         (*nrFields)++;
00238                         infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
00239                         break;
00240             }
00241                     infoPtr->fieldspec[*nrFields]++;
00242                     break;
00243                 }   /* if allowedformatchar */
00244             } /* for j */
00245         }
00246         else
00247             j = len;
00248 
00249         if (formattxt[i] == '\'')
00250         {
00251             inside_literal = !inside_literal;
00252             continue;
00253         }
00254 
00255     /* char is not a specifier: handle char like a string */
00256     if (j == len) {
00257         if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
00258         infoPtr->fieldspec[*nrFields] = DT_STRING + k;
00259         infoPtr->buflen[*nrFields] = 0;
00260             } else if ((infoPtr->fieldspec[*nrFields] & DT_STRING) != DT_STRING)  {
00261         (*nrFields)++;
00262         infoPtr->fieldspec[*nrFields] = DT_STRING + k;
00263         infoPtr->buflen[*nrFields] = 0;
00264         }
00265         infoPtr->textbuf[k] = formattxt[i];
00266         k++;
00267         infoPtr->buflen[*nrFields]++;
00268     }   /* if j=len */
00269 
00270     if (*nrFields == infoPtr->nrFieldsAllocated) {
00271         FIXME ("out of memory; should reallocate. crash ahead.\n");
00272     }
00273     } /* for i */
00274 
00275     TRACE("\n");
00276 
00277     if (infoPtr->fieldspec[*nrFields] != 0) (*nrFields)++;
00278 }
00279 
00280 
00281 static BOOL
00282 DATETIME_SetFormatW (DATETIME_INFO *infoPtr, LPCWSTR format)
00283 {
00284     WCHAR format_buf[80];
00285 
00286     if (!format) {
00287     DWORD format_item;
00288 
00289     if (infoPtr->dwStyle & DTS_LONGDATEFORMAT)
00290         format_item = LOCALE_SLONGDATE;
00291     else if ((infoPtr->dwStyle & DTS_TIMEFORMAT) == DTS_TIMEFORMAT)
00292         format_item = LOCALE_STIMEFORMAT;
00293         else /* DTS_SHORTDATEFORMAT */
00294         format_item = LOCALE_SSHORTDATE;
00295     GetLocaleInfoW(LOCALE_USER_DEFAULT, format_item, format_buf, sizeof(format_buf)/sizeof(format_buf[0]));
00296     format = format_buf;
00297     }
00298 
00299     DATETIME_UseFormat (infoPtr, format);
00300     InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
00301 
00302     return TRUE;
00303 }
00304 
00305 
00306 static BOOL
00307 DATETIME_SetFormatA (DATETIME_INFO *infoPtr, LPCSTR lpszFormat)
00308 {
00309     if (lpszFormat) {
00310     BOOL retval;
00311     INT len = MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, NULL, 0);
00312     LPWSTR wstr = Alloc(len * sizeof(WCHAR));
00313     if (wstr) MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, wstr, len);
00314     retval = DATETIME_SetFormatW (infoPtr, wstr);
00315     Free (wstr);
00316     return retval;
00317     }
00318     else
00319     return DATETIME_SetFormatW (infoPtr, 0);
00320 
00321 }
00322 
00323 
00324 static void
00325 DATETIME_ReturnTxt (const DATETIME_INFO *infoPtr, int count, LPWSTR result, int resultSize)
00326 {
00327     static const WCHAR fmt_dW[] = { '%', 'd', 0 };
00328     static const WCHAR fmt__2dW[] = { '%', '.', '2', 'd', 0 };
00329     static const WCHAR fmt__3sW[] = { '%', '.', '3', 's', 0 };
00330     SYSTEMTIME date = infoPtr->date;
00331     int spec;
00332     WCHAR buffer[80];
00333 
00334     *result=0;
00335     TRACE ("%d,%d\n", infoPtr->nrFields, count);
00336     if (count>infoPtr->nrFields || count < 0) {
00337     WARN ("buffer overrun, have %d want %d\n", infoPtr->nrFields, count);
00338     return;
00339     }
00340 
00341     if (!infoPtr->fieldspec) return;
00342 
00343     spec = infoPtr->fieldspec[count];
00344     if (spec & DT_STRING) {
00345     int txtlen = infoPtr->buflen[count];
00346 
00347         if (txtlen > resultSize)
00348             txtlen = resultSize - 1;
00349     memcpy (result, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR));
00350     result[txtlen] = 0;
00351     TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result));
00352     return;
00353     }
00354 
00355 
00356     switch (spec) {
00357     case DT_END_FORMAT:
00358         *result = 0;
00359         break;
00360     case ONEDIGITDAY:
00361         wsprintfW (result, fmt_dW, date.wDay);
00362         break;
00363     case TWODIGITDAY:
00364         wsprintfW (result, fmt__2dW, date.wDay);
00365         break;
00366     case THREECHARDAY:
00367         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1+(date.wDayOfWeek+6)%7, result, 4);
00368         /*wsprintfW (result,"%.3s",days[date.wDayOfWeek]);*/
00369         break;
00370     case FULLDAY:
00371         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDAYNAME1+(date.wDayOfWeek+6)%7, result, resultSize);
00372         break;
00373     case ONEDIGIT12HOUR:
00374         if (date.wHour == 0) {
00375             result[0] = '1';
00376             result[1] = '2';
00377             result[2] = 0;
00378         }
00379         else
00380             wsprintfW (result, fmt_dW, date.wHour - (date.wHour > 12 ? 12 : 0));
00381         break;
00382     case TWODIGIT12HOUR:
00383         if (date.wHour == 0) {
00384             result[0] = '1';
00385             result[1] = '2';
00386             result[2] = 0;
00387         }
00388         else
00389             wsprintfW (result, fmt__2dW, date.wHour - (date.wHour > 12 ? 12 : 0));
00390         break;
00391     case ONEDIGIT24HOUR:
00392         wsprintfW (result, fmt_dW, date.wHour);
00393         break;
00394     case TWODIGIT24HOUR:
00395         wsprintfW (result, fmt__2dW, date.wHour);
00396         break;
00397     case ONEDIGITSECOND:
00398         wsprintfW (result, fmt_dW, date.wSecond);
00399         break;
00400     case TWODIGITSECOND:
00401         wsprintfW (result, fmt__2dW, date.wSecond);
00402         break;
00403     case ONEDIGITMINUTE:
00404         wsprintfW (result, fmt_dW, date.wMinute);
00405         break;
00406     case TWODIGITMINUTE:
00407         wsprintfW (result, fmt__2dW, date.wMinute);
00408         break;
00409     case ONEDIGITMONTH:
00410         wsprintfW (result, fmt_dW, date.wMonth);
00411         break;
00412     case TWODIGITMONTH:
00413         wsprintfW (result, fmt__2dW, date.wMonth);
00414         break;
00415     case THREECHARMONTH:
00416         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1,
00417                buffer, sizeof(buffer)/sizeof(buffer[0]));
00418         wsprintfW (result, fmt__3sW, buffer);
00419         break;
00420     case FULLMONTH:
00421         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1,
00422                            result, resultSize);
00423         break;
00424     case ONELETTERAMPM:
00425         result[0] = (date.wHour < 12 ? 'A' : 'P');
00426         result[1] = 0;
00427         break;
00428     case TWOLETTERAMPM:
00429         result[0] = (date.wHour < 12 ? 'A' : 'P');
00430         result[1] = 'M';
00431         result[2] = 0;
00432         break;
00433     case FORMATCALLBACK:
00434         FIXME ("Not implemented\n");
00435         result[0] = 'x';
00436         result[1] = 0;
00437         break;
00438     case ONEDIGITYEAR:
00439         wsprintfW (result, fmt_dW, date.wYear-10* (int) floor(date.wYear/10));
00440         break;
00441     case TWODIGITYEAR:
00442         wsprintfW (result, fmt__2dW, date.wYear-100* (int) floor(date.wYear/100));
00443         break;
00444         case INVALIDFULLYEAR:
00445     case FULLYEAR:
00446         wsprintfW (result, fmt_dW, date.wYear);
00447         break;
00448     }
00449 
00450     TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result));
00451 }
00452 
00453 static int wrap(int val, int delta, int minVal, int maxVal)
00454 {
00455     val += delta;
00456     if (delta == INT_MIN || val < minVal) return maxVal;
00457     if (delta == INT_MAX || val > maxVal) return minVal;
00458     return val;
00459 }
00460 
00461 static void
00462 DATETIME_IncreaseField (DATETIME_INFO *infoPtr, int number, int delta)
00463 {
00464     SYSTEMTIME *date = &infoPtr->date;
00465 
00466     TRACE ("%d\n", number);
00467     if ((number > infoPtr->nrFields) || (number < 0)) return;
00468 
00469     if ((infoPtr->fieldspec[number] & DTHT_DATEFIELD) == 0) return;
00470 
00471     switch (infoPtr->fieldspec[number]) {
00472     case ONEDIGITYEAR:
00473     case TWODIGITYEAR:
00474     case FULLYEAR:
00475         date->wYear = wrap(date->wYear, delta, 1752, 9999);
00476         if (date->wDay > MONTHCAL_MonthLength(date->wMonth, date->wYear))
00477             /* This can happen when moving away from a leap year. */
00478             date->wDay = MONTHCAL_MonthLength(date->wMonth, date->wYear);
00479         MONTHCAL_CalculateDayOfWeek(date, TRUE);
00480         break;
00481     case ONEDIGITMONTH:
00482     case TWODIGITMONTH:
00483     case THREECHARMONTH:
00484     case FULLMONTH:
00485         date->wMonth = wrap(date->wMonth, delta, 1, 12);
00486         MONTHCAL_CalculateDayOfWeek(date, TRUE);
00487         delta = 0;
00488         /* fall through */
00489     case ONEDIGITDAY:
00490     case TWODIGITDAY:
00491     case THREECHARDAY:
00492     case FULLDAY:
00493         date->wDay = wrap(date->wDay, delta, 1, MONTHCAL_MonthLength(date->wMonth, date->wYear));
00494         MONTHCAL_CalculateDayOfWeek(date, TRUE);
00495         break;
00496     case ONELETTERAMPM:
00497     case TWOLETTERAMPM:
00498         delta *= 12;
00499         /* fall through */
00500     case ONEDIGIT12HOUR:
00501     case TWODIGIT12HOUR:
00502     case ONEDIGIT24HOUR:
00503     case TWODIGIT24HOUR:
00504         date->wHour = wrap(date->wHour, delta, 0, 23);
00505         break;
00506     case ONEDIGITMINUTE:
00507     case TWODIGITMINUTE:
00508         date->wMinute = wrap(date->wMinute, delta, 0, 59);
00509         break;
00510     case ONEDIGITSECOND:
00511     case TWODIGITSECOND:
00512         date->wSecond = wrap(date->wSecond, delta, 0, 59);
00513         break;
00514     case FORMATCALLBACK:
00515         FIXME ("Not implemented\n");
00516         break;
00517     }
00518 
00519     /* FYI: On 1752/9/14 the calendar changed and England and the
00520      * American colonies changed to the Gregorian calendar. This change
00521      * involved having September 14th follow September 2nd. So no date
00522      * algorithm works before that date.
00523      */
00524     if (10000 * date->wYear + 100 * date->wMonth + date->wDay < 17520914) {
00525     date->wYear = 1752;
00526         date->wMonth = 9;
00527     date->wDay = 14;
00528     date->wSecond = 0;
00529     date->wMinute = 0;
00530     date->wHour = 0;
00531     }
00532 }
00533 
00534 
00535 static void
00536 DATETIME_ReturnFieldWidth (const DATETIME_INFO *infoPtr, HDC hdc, int count, SHORT *width)
00537 {
00538     /* fields are a fixed width, determined by the largest possible string */
00539     /* presumably, these widths should be language dependent */
00540     static const WCHAR fld_d1W[] = { '2', 0 };
00541     static const WCHAR fld_d2W[] = { '2', '2', 0 };
00542     static const WCHAR fld_d4W[] = { '2', '2', '2', '2', 0 };
00543     static const WCHAR fld_am1[] = { 'A', 0 };
00544     static const WCHAR fld_am2[] = { 'A', 'M', 0 };
00545     int spec;
00546     WCHAR buffer[80];
00547     LPCWSTR bufptr;
00548     SIZE size;
00549 
00550     TRACE ("%d,%d\n", infoPtr->nrFields, count);
00551     if (count>infoPtr->nrFields || count < 0) {
00552     WARN ("buffer overrun, have %d want %d\n", infoPtr->nrFields, count);
00553     return;
00554     }
00555 
00556     if (!infoPtr->fieldspec) return;
00557 
00558     spec = infoPtr->fieldspec[count];
00559     if (spec & DT_STRING) {
00560     int txtlen = infoPtr->buflen[count];
00561 
00562         if (txtlen > 79)
00563             txtlen = 79;
00564     memcpy (buffer, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR));
00565     buffer[txtlen] = 0;
00566     bufptr = buffer;
00567     }
00568     else {
00569         switch (spec) {
00570         case ONEDIGITDAY:
00571         case ONEDIGIT12HOUR:
00572         case ONEDIGIT24HOUR:
00573         case ONEDIGITSECOND:
00574         case ONEDIGITMINUTE:
00575         case ONEDIGITMONTH:
00576         case ONEDIGITYEAR:
00577             /* these seem to use a two byte field */
00578         case TWODIGITDAY:
00579         case TWODIGIT12HOUR:
00580         case TWODIGIT24HOUR:
00581         case TWODIGITSECOND:
00582         case TWODIGITMINUTE:
00583         case TWODIGITMONTH:
00584         case TWODIGITYEAR:
00585             bufptr = fld_d2W;
00586             break;
00587             case INVALIDFULLYEAR:
00588         case FULLYEAR:
00589             bufptr = fld_d4W;
00590             break;
00591         case THREECHARMONTH:
00592         case FULLMONTH:
00593         case THREECHARDAY:
00594         case FULLDAY:
00595         {
00596         static const WCHAR fld_day[] = {'W','e','d','n','e','s','d','a','y',0};
00597         static const WCHAR fld_abbrday[] = {'W','e','d',0};
00598         static const WCHAR fld_mon[] = {'S','e','p','t','e','m','b','e','r',0};
00599         static const WCHAR fld_abbrmon[] = {'D','e','c',0};
00600 
00601         const WCHAR *fall;
00602         LCTYPE lctype;
00603         INT i, max_count;
00604         LONG cx;
00605 
00606         /* choose locale data type and fallback string */
00607         switch (spec) {
00608         case THREECHARDAY:
00609             fall   = fld_abbrday;
00610             lctype = LOCALE_SABBREVDAYNAME1;
00611             max_count = 7;
00612             break;
00613         case FULLDAY:
00614             fall   = fld_day;
00615             lctype = LOCALE_SDAYNAME1;
00616             max_count = 7;
00617             break;
00618         case THREECHARMONTH:
00619             fall   = fld_abbrmon;
00620             lctype = LOCALE_SABBREVMONTHNAME1;
00621             max_count = 12;
00622             break;
00623         default: /* FULLMONTH */
00624             fall   = fld_mon;
00625             lctype = LOCALE_SMONTHNAME1;
00626             max_count = 12;
00627             break;
00628         }
00629 
00630         cx = 0;
00631         for (i = 0; i < max_count; i++)
00632         {
00633             if(GetLocaleInfoW(LOCALE_USER_DEFAULT, lctype + i,
00634             buffer, lstrlenW(buffer)))
00635             {
00636             GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &size);
00637             if (size.cx > cx) cx = size.cx;
00638             }
00639             else /* locale independent fallback on failure */
00640             {
00641                 GetTextExtentPoint32W(hdc, fall, lstrlenW(fall), &size);
00642             cx = size.cx;
00643                 break;
00644             }
00645         }
00646         *width = cx;
00647         return;
00648         }
00649         case ONELETTERAMPM:
00650             bufptr = fld_am1;
00651             break;
00652         case TWOLETTERAMPM:
00653             bufptr = fld_am2;
00654             break;
00655         default:
00656             bufptr = fld_d1W;
00657             break;
00658         }
00659     }
00660     GetTextExtentPoint32W (hdc, bufptr, strlenW(bufptr), &size);
00661     *width = size.cx;
00662 }
00663 
00664 static void 
00665 DATETIME_Refresh (DATETIME_INFO *infoPtr, HDC hdc)
00666 {
00667     TRACE("\n");
00668 
00669     if (infoPtr->dateValid) {
00670         int i, prevright;
00671         RECT *field;
00672         RECT *rcDraw = &infoPtr->rcDraw;
00673         SIZE size;
00674         COLORREF oldTextColor;
00675         SHORT fieldWidth = 0;
00676         HFONT oldFont = SelectObject (hdc, infoPtr->hFont);
00677         INT oldBkMode = SetBkMode (hdc, TRANSPARENT);
00678         WCHAR txt[80];
00679 
00680         DATETIME_ReturnTxt (infoPtr, 0, txt, sizeof(txt)/sizeof(txt[0]));
00681         GetTextExtentPoint32W (hdc, txt, strlenW(txt), &size);
00682         rcDraw->bottom = size.cy + 2;
00683 
00684         prevright = infoPtr->checkbox.right = ((infoPtr->dwStyle & DTS_SHOWNONE) ? 18 : 2);
00685 
00686         for (i = 0; i < infoPtr->nrFields; i++) {
00687             DATETIME_ReturnTxt (infoPtr, i, txt, sizeof(txt)/sizeof(txt[0]));
00688             GetTextExtentPoint32W (hdc, txt, strlenW(txt), &size);
00689             DATETIME_ReturnFieldWidth (infoPtr, hdc, i, &fieldWidth);
00690             field = &infoPtr->fieldRect[i];
00691             field->left   = prevright;
00692             field->right  = prevright + fieldWidth;
00693             field->top    = rcDraw->top;
00694             field->bottom = rcDraw->bottom;
00695             prevright = field->right;
00696 
00697             if (infoPtr->dwStyle & WS_DISABLED)
00698                 oldTextColor = SetTextColor (hdc, comctl32_color.clrGrayText);
00699             else if ((infoPtr->haveFocus) && (i == infoPtr->select)) {
00700                 RECT selection;
00701 
00702                 /* fill if focused */
00703                 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrActiveCaption);
00704 
00705                 if (infoPtr->nCharsEntered)
00706                 {
00707                     memcpy(txt, infoPtr->charsEntered, infoPtr->nCharsEntered * sizeof(WCHAR));
00708                     txt[infoPtr->nCharsEntered] = 0;
00709                     GetTextExtentPoint32W (hdc, txt, strlenW(txt), &size);
00710                 }
00711 
00712                 selection.left   = 0;
00713                 selection.top    = 0;
00714                 selection.right  = size.cx;
00715                 selection.bottom = size.cy;
00716                 /* center rectangle */
00717                 OffsetRect(&selection, (field->right  + field->left - size.cx)/2,
00718                                        (field->bottom - size.cy)/2);
00719 
00720                 FillRect(hdc, &selection, hbr);
00721                 DeleteObject (hbr);
00722                 oldTextColor = SetTextColor (hdc, comctl32_color.clrWindow);
00723             }
00724             else
00725                 oldTextColor = SetTextColor (hdc, comctl32_color.clrWindowText);
00726 
00727             /* draw the date text using the colour set above */
00728             DrawTextW (hdc, txt, strlenW(txt), field, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
00729             SetTextColor (hdc, oldTextColor);
00730         }
00731         SetBkMode (hdc, oldBkMode);
00732         SelectObject (hdc, oldFont);
00733     }
00734 
00735     if (!(infoPtr->dwStyle & DTS_UPDOWN)) {
00736         DrawFrameControl(hdc, &infoPtr->calbutton, DFC_SCROLL,
00737                          DFCS_SCROLLDOWN | (infoPtr->bCalDepressed ? DFCS_PUSHED : 0) |
00738                          (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
00739     }
00740 }
00741 
00742 
00743 static INT
00744 DATETIME_HitTest (const DATETIME_INFO *infoPtr, POINT pt)
00745 {
00746     int i;
00747 
00748     TRACE ("%d, %d\n", pt.x, pt.y);
00749 
00750     if (PtInRect (&infoPtr->calbutton, pt)) return DTHT_MCPOPUP;
00751     if (PtInRect (&infoPtr->checkbox, pt)) return DTHT_CHECKBOX;
00752 
00753     for (i = 0; i < infoPtr->nrFields; i++) {
00754         if (PtInRect (&infoPtr->fieldRect[i], pt)) return i;
00755     }
00756 
00757     return DTHT_NONE;
00758 }
00759 
00760 /* Returns index of a closest date field from given counting to left
00761    or -1 if there's no such fields at left */
00762 static int DATETIME_GetPrevDateField(const DATETIME_INFO *infoPtr, int i)
00763 {
00764     for(--i; i >= 0; i--)
00765     {
00766         if (infoPtr->fieldspec[i] & DTHT_DATEFIELD) return i;
00767     }
00768     return -1;
00769 }
00770 
00771 static void
00772 DATETIME_ApplySelectedField (DATETIME_INFO *infoPtr)
00773 {
00774     int fieldNum = infoPtr->select & DTHT_DATEFIELD;
00775     int i, val=0, clamp_day=0;
00776     SYSTEMTIME date = infoPtr->date;
00777 
00778     if (infoPtr->select == -1 || infoPtr->nCharsEntered == 0)
00779         return;
00780 
00781     for (i=0; i<infoPtr->nCharsEntered; i++)
00782         val = val * 10 + infoPtr->charsEntered[i] - '0';
00783 
00784     infoPtr->nCharsEntered = 0;
00785 
00786     switch (infoPtr->fieldspec[fieldNum]) {
00787         case ONEDIGITYEAR:
00788         case TWODIGITYEAR:
00789             date.wYear = date.wYear - (date.wYear%100) + val;
00790             clamp_day = 1;
00791             break;
00792         case INVALIDFULLYEAR:
00793         case FULLYEAR:
00794             date.wYear = val;
00795             clamp_day = 1;
00796             break;
00797         case ONEDIGITMONTH:
00798         case TWODIGITMONTH:
00799             date.wMonth = val;
00800             clamp_day = 1;
00801             break;
00802         case ONEDIGITDAY:
00803         case TWODIGITDAY:
00804             date.wDay = val;
00805             break;
00806         case ONEDIGIT12HOUR:
00807         case TWODIGIT12HOUR:
00808         case ONEDIGIT24HOUR:
00809         case TWODIGIT24HOUR:
00810             /* FIXME: Preserve AM/PM for 12HOUR? */
00811             date.wHour = val;
00812             break;
00813         case ONEDIGITMINUTE:
00814         case TWODIGITMINUTE:
00815             date.wMinute = val;
00816             break;
00817         case ONEDIGITSECOND:
00818         case TWODIGITSECOND:
00819             date.wSecond = val;
00820             break;
00821     }
00822 
00823     if (clamp_day && date.wDay > MONTHCAL_MonthLength(date.wMonth, date.wYear))
00824         date.wDay = MONTHCAL_MonthLength(date.wMonth, date.wYear);
00825 
00826     if (DATETIME_SetSystemTime(infoPtr, GDT_VALID, &date))
00827         DATETIME_SendDateTimeChangeNotify (infoPtr);
00828 }
00829 
00830 static void
00831 DATETIME_SetSelectedField (DATETIME_INFO *infoPtr, int select)
00832 {
00833     DATETIME_ApplySelectedField(infoPtr);
00834 
00835     infoPtr->select = select;
00836     infoPtr->nCharsEntered = 0;
00837 }
00838 
00839 static LRESULT
00840 DATETIME_LButtonDown (DATETIME_INFO *infoPtr, INT x, INT y)
00841 {
00842     POINT pt;
00843     int new;
00844 
00845     pt.x = x;
00846     pt.y = y;
00847     new = DATETIME_HitTest (infoPtr, pt);
00848 
00849     SetFocus(infoPtr->hwndSelf);
00850 
00851     if (!(new & DTHT_NODATEMASK) || (new == DTHT_NONE))
00852     {
00853         if (new == DTHT_NONE)
00854             new = infoPtr->nrFields - 1;
00855         else
00856         {
00857             /* hitting string part moves selection to next date field to left */
00858             if (infoPtr->fieldspec[new] & DT_STRING)
00859             {
00860                 new = DATETIME_GetPrevDateField(infoPtr, new);
00861                 if (new == -1) return 0;
00862             }
00863             /* never select full day of week */
00864             if (infoPtr->fieldspec[new] == FULLDAY) return 0;
00865         }
00866     }
00867 
00868     DATETIME_SetSelectedField(infoPtr, new);
00869 
00870     if (infoPtr->select == DTHT_MCPOPUP) {
00871         RECT rcMonthCal;
00872         POINT pos;
00873         SendMessageW(infoPtr->hMonthCal, MCM_GETMINREQRECT, 0, (LPARAM)&rcMonthCal);
00874 
00875         /* FIXME: button actually is only depressed during dropdown of the */
00876         /* calendar control and when the mouse is over the button window */
00877         infoPtr->bCalDepressed = TRUE;
00878 
00879         /* recalculate the position of the monthcal popup */
00880         if(infoPtr->dwStyle & DTS_RIGHTALIGN)
00881             pos.x = infoPtr->calbutton.left - (rcMonthCal.right - rcMonthCal.left);
00882         else
00883             /* FIXME: this should be after the area reserved for the checkbox */
00884             pos.x = infoPtr->rcDraw.left;
00885 
00886         pos.y = infoPtr->rcClient.bottom;
00887         OffsetRect( &rcMonthCal, pos.x, pos.y );
00888         MapWindowPoints( infoPtr->hwndSelf, 0, (POINT *)&rcMonthCal, 2 );
00889         SetWindowPos(infoPtr->hMonthCal, 0, rcMonthCal.left, rcMonthCal.top,
00890                      rcMonthCal.right - rcMonthCal.left, rcMonthCal.bottom - rcMonthCal.top, 0);
00891 
00892         if(IsWindowVisible(infoPtr->hMonthCal)) {
00893             ShowWindow(infoPtr->hMonthCal, SW_HIDE);
00894             infoPtr->bDropdownEnabled = FALSE;
00895             DATETIME_SendSimpleNotify (infoPtr, DTN_CLOSEUP);
00896         } else {
00897             const SYSTEMTIME *lprgSysTimeArray = &infoPtr->date;
00898             TRACE("update calendar %04d/%02d/%02d\n", 
00899             lprgSysTimeArray->wYear, lprgSysTimeArray->wMonth, lprgSysTimeArray->wDay);
00900             SendMessageW(infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date));
00901 
00902             if (infoPtr->bDropdownEnabled) {
00903                 ShowWindow(infoPtr->hMonthCal, SW_SHOW);
00904                 DATETIME_SendSimpleNotify (infoPtr, DTN_DROPDOWN);
00905             }
00906             infoPtr->bDropdownEnabled = TRUE;
00907         }
00908 
00909         TRACE ("dt:%p mc:%p mc parent:%p, desktop:%p\n",
00910                infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hwndNotify, GetDesktopWindow ());
00911     }
00912 
00913     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
00914 
00915     return 0;
00916 }
00917 
00918 
00919 static LRESULT
00920 DATETIME_LButtonUp (DATETIME_INFO *infoPtr)
00921 {
00922     if(infoPtr->bCalDepressed) {
00923         infoPtr->bCalDepressed = FALSE;
00924         InvalidateRect(infoPtr->hwndSelf, &(infoPtr->calbutton), TRUE);
00925     }
00926 
00927     return 0;
00928 }
00929 
00930 
00931 static LRESULT
00932 DATETIME_Paint (DATETIME_INFO *infoPtr, HDC hdc)
00933 {
00934     if (!hdc) {
00935     PAINTSTRUCT ps;
00936         hdc = BeginPaint (infoPtr->hwndSelf, &ps);
00937         DATETIME_Refresh (infoPtr, hdc);
00938         EndPaint (infoPtr->hwndSelf, &ps);
00939     } else {
00940         DATETIME_Refresh (infoPtr, hdc);
00941     }
00942 
00943     /* Not a click on the dropdown box, enabled it */
00944     infoPtr->bDropdownEnabled = TRUE;
00945 
00946     return 0;
00947 }
00948 
00949 
00950 static LRESULT
00951 DATETIME_Button_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
00952 {
00953     if( HIWORD(wParam) == BN_CLICKED) {
00954         DWORD state = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0);
00955         infoPtr->dateValid = (state == BST_CHECKED);
00956         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
00957     }
00958     return 0;
00959 }
00960           
00961         
00962         
00963 static LRESULT
00964 DATETIME_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
00965 {
00966     TRACE("hwndbutton = %p\n", infoPtr->hwndCheckbut);
00967     if(infoPtr->hwndCheckbut == (HWND)lParam)
00968         return DATETIME_Button_Command(infoPtr, wParam, lParam);
00969     return 0;
00970 }
00971 
00972 
00973 static LRESULT
00974 DATETIME_Enable (DATETIME_INFO *infoPtr, BOOL bEnable)
00975 {
00976     TRACE("%p %s\n", infoPtr, bEnable ? "TRUE" : "FALSE");
00977     if (bEnable)
00978         infoPtr->dwStyle &= ~WS_DISABLED;
00979     else
00980         infoPtr->dwStyle |= WS_DISABLED;
00981 
00982     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
00983 
00984     return 0;
00985 }
00986 
00987 
00988 static LRESULT
00989 DATETIME_EraseBackground (const DATETIME_INFO *infoPtr, HDC hdc)
00990 {
00991     HBRUSH hBrush, hSolidBrush = NULL;
00992     RECT   rc;
00993 
00994     if (infoPtr->dwStyle & WS_DISABLED)
00995         hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace);
00996     else
00997     {
00998         hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT,
00999                                       (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
01000         if (!hBrush)
01001             hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow);
01002     }
01003 
01004     GetClientRect (infoPtr->hwndSelf, &rc);
01005 
01006     FillRect (hdc, &rc, hBrush);
01007 
01008     if (hSolidBrush)
01009         DeleteObject(hSolidBrush);
01010 
01011     return -1;
01012 }
01013 
01014 
01015 static LRESULT
01016 DATETIME_Notify (DATETIME_INFO *infoPtr, const NMHDR *lpnmh)
01017 {
01018     TRACE ("Got notification %x from %p\n", lpnmh->code, lpnmh->hwndFrom);
01019     TRACE ("info: %p %p %p\n", infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hUpdown);
01020 
01021     if (lpnmh->code == MCN_SELECT) {
01022         ShowWindow(infoPtr->hMonthCal, SW_HIDE);
01023         infoPtr->dateValid = TRUE;
01024         SendMessageW (infoPtr->hMonthCal, MCM_GETCURSEL, 0, (LPARAM)&infoPtr->date);
01025         TRACE("got from calendar %04d/%02d/%02d day of week %d\n", 
01026         infoPtr->date.wYear, infoPtr->date.wMonth, infoPtr->date.wDay, infoPtr->date.wDayOfWeek);
01027         SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0);
01028         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
01029         DATETIME_SendDateTimeChangeNotify (infoPtr);
01030         DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP);
01031     }
01032     if ((lpnmh->hwndFrom == infoPtr->hUpdown) && (lpnmh->code == UDN_DELTAPOS)) {
01033         const NM_UPDOWN *lpnmud = (const NM_UPDOWN*)lpnmh;
01034         TRACE("Delta pos %d\n", lpnmud->iDelta);
01035         infoPtr->pendingUpdown = lpnmud->iDelta;
01036     }
01037     return 0;
01038 }
01039 
01040 
01041 static LRESULT
01042 DATETIME_KeyDown (DATETIME_INFO *infoPtr, DWORD vkCode)
01043 {
01044     int fieldNum = infoPtr->select & DTHT_DATEFIELD;
01045     int wrap = 0;
01046     int new;
01047 
01048     if (!(infoPtr->haveFocus)) return 0;
01049     if ((fieldNum==0) && (infoPtr->select)) return 0;
01050 
01051     if (infoPtr->select & FORMATCALLMASK) {
01052     FIXME ("Callbacks not implemented yet\n");
01053     }
01054 
01055     switch (vkCode) {
01056     case VK_ADD:
01057         case VK_UP:
01058         infoPtr->nCharsEntered = 0;
01059         DATETIME_IncreaseField (infoPtr, fieldNum, 1);
01060         DATETIME_SendDateTimeChangeNotify (infoPtr);
01061         break;
01062     case VK_SUBTRACT:
01063     case VK_DOWN:
01064         infoPtr->nCharsEntered = 0;
01065         DATETIME_IncreaseField (infoPtr, fieldNum, -1);
01066         DATETIME_SendDateTimeChangeNotify (infoPtr);
01067         break;
01068     case VK_HOME:
01069         infoPtr->nCharsEntered = 0;
01070         DATETIME_IncreaseField (infoPtr, fieldNum, INT_MIN);
01071         DATETIME_SendDateTimeChangeNotify (infoPtr);
01072         break;
01073     case VK_END:
01074         infoPtr->nCharsEntered = 0;
01075         DATETIME_IncreaseField (infoPtr, fieldNum, INT_MAX);
01076         DATETIME_SendDateTimeChangeNotify (infoPtr);
01077         break;
01078     case VK_LEFT:
01079         new = infoPtr->select;
01080         do {
01081         if (new == 0) {
01082             new = new - 1;
01083             wrap++;
01084         } else {
01085             new--;
01086         }
01087         } while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2));
01088         if (new != infoPtr->select)
01089             DATETIME_SetSelectedField(infoPtr, new);
01090         break;
01091     case VK_RIGHT:
01092         new = infoPtr->select;
01093         do {
01094         new++;
01095         if (new==infoPtr->nrFields) {
01096             new = 0;
01097             wrap++;
01098         }
01099         } while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2));
01100         if (new != infoPtr->select)
01101             DATETIME_SetSelectedField(infoPtr, new);
01102         break;
01103     }
01104 
01105     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
01106 
01107     return 0;
01108 }
01109 
01110 
01111 static LRESULT
01112 DATETIME_Char (DATETIME_INFO *infoPtr, WPARAM vkCode)
01113 {
01114     int fieldNum = infoPtr->select & DTHT_DATEFIELD;
01115 
01116     if (vkCode >= '0' && vkCode <= '9') {
01117         int maxChars;
01118         int fieldSpec;
01119 
01120         infoPtr->charsEntered[infoPtr->nCharsEntered++] = vkCode;
01121 
01122         fieldSpec = infoPtr->fieldspec[fieldNum];
01123 
01124         if (fieldSpec == INVALIDFULLYEAR || fieldSpec == FULLYEAR)
01125             maxChars = 4;
01126         else
01127             maxChars = 2;
01128 
01129         if (maxChars == infoPtr->nCharsEntered)
01130             DATETIME_ApplySelectedField(infoPtr);
01131     }
01132     return 0;
01133 }
01134 
01135 
01136 static LRESULT
01137 DATETIME_VScroll (DATETIME_INFO *infoPtr, WORD wScroll)
01138 {
01139     int fieldNum = infoPtr->select & DTHT_DATEFIELD;
01140 
01141     if ((SHORT)LOWORD(wScroll) != SB_THUMBPOSITION) return 0;
01142     if (!(infoPtr->haveFocus)) return 0;
01143     if ((fieldNum==0) && (infoPtr->select)) return 0;
01144 
01145     if (infoPtr->pendingUpdown >= 0) {
01146     DATETIME_IncreaseField (infoPtr, fieldNum, 1);
01147     DATETIME_SendDateTimeChangeNotify (infoPtr);
01148     }
01149     else {
01150     DATETIME_IncreaseField (infoPtr, fieldNum, -1);
01151     DATETIME_SendDateTimeChangeNotify (infoPtr);
01152     }
01153 
01154     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
01155 
01156     return 0;
01157 }
01158 
01159 
01160 static LRESULT
01161 DATETIME_KillFocus (DATETIME_INFO *infoPtr, HWND lostFocus)
01162 {
01163     TRACE("lost focus to %p\n", lostFocus);
01164 
01165     if (infoPtr->haveFocus) {
01166     DATETIME_SendSimpleNotify (infoPtr, NM_KILLFOCUS);
01167     infoPtr->haveFocus = 0;
01168         DATETIME_SetSelectedField (infoPtr, -1);
01169     }
01170 
01171     InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
01172 
01173     return 0;
01174 }
01175 
01176 
01177 static LRESULT
01178 DATETIME_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs)
01179 {
01180     DWORD dwExStyle = GetWindowLongW(hwnd, GWL_EXSTYLE);
01181     /* force control to have client edge */
01182     dwExStyle |= WS_EX_CLIENTEDGE;
01183     SetWindowLongW(hwnd, GWL_EXSTYLE, dwExStyle);
01184 
01185     return DefWindowProcW(hwnd, WM_NCCREATE, 0, (LPARAM)lpcs);
01186 }
01187 
01188 
01189 static LRESULT
01190 DATETIME_SetFocus (DATETIME_INFO *infoPtr, HWND lostFocus)
01191 {
01192     TRACE("got focus from %p\n", lostFocus);
01193 
01194     /* if monthcal is open and it loses focus, close monthcal */
01195     if (infoPtr->hMonthCal && (lostFocus == infoPtr->hMonthCal) &&
01196         IsWindowVisible(infoPtr->hMonthCal))
01197     {
01198         ShowWindow(infoPtr->hMonthCal, SW_HIDE);
01199         DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP);
01200         /* note: this get triggered even if monthcal loses focus to a dropdown
01201          * box click, which occurs without an intermediate WM_PAINT call
01202          */
01203         infoPtr->bDropdownEnabled = FALSE;
01204         return 0;
01205     }
01206 
01207     if (infoPtr->haveFocus == 0) {
01208     DATETIME_SendSimpleNotify (infoPtr, NM_SETFOCUS);
01209     infoPtr->haveFocus = DTHT_GOTFOCUS;
01210     }
01211 
01212     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
01213 
01214     return 0;
01215 }
01216 
01217 
01218 static BOOL
01219 DATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr)
01220 {
01221     NMDATETIMECHANGE dtdtc;
01222 
01223     dtdtc.nmhdr.hwndFrom = infoPtr->hwndSelf;
01224     dtdtc.nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
01225     dtdtc.nmhdr.code     = DTN_DATETIMECHANGE;
01226 
01227     dtdtc.dwFlags = infoPtr->dateValid ? GDT_VALID : GDT_NONE;
01228 
01229     dtdtc.st = infoPtr->date;
01230     return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
01231                                 dtdtc.nmhdr.idFrom, (LPARAM)&dtdtc);
01232 }
01233 
01234 
01235 static BOOL
01236 DATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code)
01237 {
01238     NMHDR nmhdr;
01239 
01240     TRACE("%x\n", code);
01241     nmhdr.hwndFrom = infoPtr->hwndSelf;
01242     nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
01243     nmhdr.code     = code;
01244 
01245     return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
01246                                 nmhdr.idFrom, (LPARAM)&nmhdr);
01247 }
01248 
01249 static LRESULT
01250 DATETIME_Size (DATETIME_INFO *infoPtr, INT width, INT height)
01251 {
01252     /* set size */
01253     infoPtr->rcClient.bottom = height;
01254     infoPtr->rcClient.right = width;
01255 
01256     TRACE("Height=%d, Width=%d\n", infoPtr->rcClient.bottom, infoPtr->rcClient.right);
01257 
01258     infoPtr->rcDraw = infoPtr->rcClient;
01259     
01260     if (infoPtr->dwStyle & DTS_UPDOWN) {
01261         SetWindowPos(infoPtr->hUpdown, NULL,
01262             infoPtr->rcClient.right-14, 0,
01263             15, infoPtr->rcClient.bottom - infoPtr->rcClient.top,
01264             SWP_NOACTIVATE | SWP_NOZORDER);
01265     }
01266     else {
01267         /* set the size of the button that drops the calendar down */
01268         /* FIXME: account for style that allows button on left side */
01269         infoPtr->calbutton.top   = infoPtr->rcDraw.top;
01270         infoPtr->calbutton.bottom= infoPtr->rcDraw.bottom;
01271         infoPtr->calbutton.left  = infoPtr->rcDraw.right-15;
01272         infoPtr->calbutton.right = infoPtr->rcDraw.right;
01273     }
01274 
01275     /* set enable/disable button size for show none style being enabled */
01276     /* FIXME: these dimensions are completely incorrect */
01277     infoPtr->checkbox.top = infoPtr->rcDraw.top;
01278     infoPtr->checkbox.bottom = infoPtr->rcDraw.bottom;
01279     infoPtr->checkbox.left = infoPtr->rcDraw.left;
01280     infoPtr->checkbox.right = infoPtr->rcDraw.left + 10;
01281 
01282     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
01283 
01284     return 0;
01285 }
01286 
01287 static LRESULT
01288 DATETIME_StyleChanging(DATETIME_INFO *infoPtr, WPARAM wStyleType, STYLESTRUCT *lpss)
01289 {
01290     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
01291           wStyleType, lpss->styleOld, lpss->styleNew);
01292 
01293     /* block DTS_SHOWNONE change */
01294     if ((lpss->styleNew ^ lpss->styleOld) & DTS_SHOWNONE)
01295     {
01296         if (lpss->styleOld & DTS_SHOWNONE)
01297             lpss->styleNew |= DTS_SHOWNONE;
01298         else
01299             lpss->styleNew &= ~DTS_SHOWNONE;
01300     }
01301 
01302     return 0;
01303 }
01304 
01305 static LRESULT 
01306 DATETIME_StyleChanged(DATETIME_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss)
01307 {
01308     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
01309           wStyleType, lpss->styleOld, lpss->styleNew);
01310 
01311     if (wStyleType != GWL_STYLE) return 0;
01312   
01313     infoPtr->dwStyle = lpss->styleNew;
01314 
01315     if ( !(lpss->styleOld & DTS_SHOWNONE) && (lpss->styleNew & DTS_SHOWNONE) ) {
01316         infoPtr->hwndCheckbut = CreateWindowExW (0, WC_BUTTONW, 0, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
01317                              2, 2, 13, 13, infoPtr->hwndSelf, 0, 
01318                         (HINSTANCE)GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_HINSTANCE), 0);
01319         SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, 1, 0);
01320     }
01321     if ( (lpss->styleOld & DTS_SHOWNONE) && !(lpss->styleNew & DTS_SHOWNONE) ) {
01322         DestroyWindow(infoPtr->hwndCheckbut);
01323         infoPtr->hwndCheckbut = 0;
01324     }
01325     if ( !(lpss->styleOld & DTS_UPDOWN) && (lpss->styleNew & DTS_UPDOWN) ) {
01326     infoPtr->hUpdown = CreateUpDownControl (WS_CHILD | WS_BORDER | WS_VISIBLE, 120, 1, 20, 20, 
01327                         infoPtr->hwndSelf, 1, 0, 0, UD_MAXVAL, UD_MINVAL, 0);
01328     }
01329     if ( (lpss->styleOld & DTS_UPDOWN) && !(lpss->styleNew & DTS_UPDOWN) ) {
01330     DestroyWindow(infoPtr->hUpdown);
01331     infoPtr->hUpdown = 0;
01332     }
01333 
01334     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
01335     return 0;
01336 }
01337 
01338 
01339 static LRESULT
01340 DATETIME_SetFont (DATETIME_INFO *infoPtr, HFONT font, BOOL repaint)
01341 {
01342     infoPtr->hFont = font;
01343     if (repaint) InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
01344     return 0;
01345 }
01346 
01347 
01348 static LRESULT
01349 DATETIME_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
01350 {
01351     DATETIME_INFO *infoPtr = Alloc (sizeof(DATETIME_INFO));
01352     STYLESTRUCT ss = { 0, lpcs->style };
01353 
01354     if (!infoPtr) return -1;
01355 
01356     infoPtr->hwndSelf = hwnd;
01357     infoPtr->dwStyle = lpcs->style;
01358 
01359     infoPtr->nrFieldsAllocated = 32;
01360     infoPtr->fieldspec = Alloc (infoPtr->nrFieldsAllocated * sizeof(int));
01361     infoPtr->fieldRect = Alloc (infoPtr->nrFieldsAllocated * sizeof(RECT));
01362     infoPtr->buflen = Alloc (infoPtr->nrFieldsAllocated * sizeof(int));
01363     infoPtr->hwndNotify = lpcs->hwndParent;
01364     infoPtr->select = -1; /* initially, nothing is selected */
01365     infoPtr->bDropdownEnabled = TRUE;
01366 
01367     DATETIME_StyleChanged(infoPtr, GWL_STYLE, &ss);
01368     DATETIME_SetFormatW (infoPtr, 0);
01369 
01370     /* create the monthcal control */
01371     infoPtr->hMonthCal = CreateWindowExW (0, MONTHCAL_CLASSW, 0, WS_BORDER | WS_POPUP | WS_CLIPSIBLINGS,
01372                       0, 0, 0, 0, infoPtr->hwndSelf, 0, 0, 0);
01373 
01374     /* initialize info structure */
01375     GetLocalTime (&infoPtr->date);
01376     infoPtr->dateValid = TRUE;
01377     infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
01378 
01379     SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
01380 
01381     return 0;
01382 }
01383 
01384 
01385 
01386 static LRESULT
01387 DATETIME_Destroy (DATETIME_INFO *infoPtr)
01388 {
01389     if (infoPtr->hwndCheckbut)
01390     DestroyWindow(infoPtr->hwndCheckbut);
01391     if (infoPtr->hUpdown)
01392     DestroyWindow(infoPtr->hUpdown);
01393     if (infoPtr->hMonthCal) 
01394         DestroyWindow(infoPtr->hMonthCal);
01395     SetWindowLongPtrW( infoPtr->hwndSelf, 0, 0 ); /* clear infoPtr */
01396     Free (infoPtr->buflen);
01397     Free (infoPtr->fieldRect);
01398     Free (infoPtr->fieldspec);
01399     Free (infoPtr);
01400     return 0;
01401 }
01402 
01403 
01404 static INT
01405 DATETIME_GetText (const DATETIME_INFO *infoPtr, INT count, LPWSTR dst)
01406 {
01407     WCHAR buf[80];
01408     int i;
01409 
01410     if (!dst || (count <= 0)) return 0;
01411 
01412     dst[0] = 0;
01413     for (i = 0; i < infoPtr->nrFields; i++)
01414     {
01415         DATETIME_ReturnTxt(infoPtr, i, buf, sizeof(buf)/sizeof(buf[0]));
01416         if ((strlenW(dst) + strlenW(buf)) < count)
01417             strcatW(dst, buf);
01418         else break;
01419     }
01420     return strlenW(dst);
01421 }
01422 
01423 
01424 static LRESULT WINAPI
01425 DATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
01426 {
01427     DATETIME_INFO *infoPtr = ((DATETIME_INFO *)GetWindowLongPtrW (hwnd, 0));
01428     LRESULT ret;
01429 
01430     TRACE ("%x, %lx, %lx\n", uMsg, wParam, lParam);
01431 
01432     if (!infoPtr && (uMsg != WM_CREATE) && (uMsg != WM_NCCREATE))
01433     return DefWindowProcW( hwnd, uMsg, wParam, lParam );
01434 
01435     switch (uMsg) {
01436 
01437     case DTM_GETSYSTEMTIME:
01438         return DATETIME_GetSystemTime (infoPtr, (SYSTEMTIME *) lParam);
01439 
01440     case DTM_SETSYSTEMTIME:
01441     return DATETIME_SetSystemTime (infoPtr, wParam, (SYSTEMTIME *) lParam);
01442 
01443     case DTM_GETRANGE:
01444     ret = SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, wParam, lParam);
01445     return ret ? ret : 1; /* bug emulation */
01446 
01447     case DTM_SETRANGE:
01448     return SendMessageW (infoPtr->hMonthCal, MCM_SETRANGE, wParam, lParam);
01449 
01450     case DTM_SETFORMATA:
01451         return DATETIME_SetFormatA (infoPtr, (LPCSTR)lParam);
01452 
01453     case DTM_SETFORMATW:
01454         return DATETIME_SetFormatW (infoPtr, (LPCWSTR)lParam);
01455 
01456     case DTM_GETMONTHCAL:
01457     return (LRESULT)infoPtr->hMonthCal;
01458 
01459     case DTM_SETMCCOLOR:
01460     return SendMessageW (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
01461 
01462     case DTM_GETMCCOLOR:
01463         return SendMessageW (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
01464 
01465     case DTM_SETMCFONT:
01466     return SendMessageW (infoPtr->hMonthCal, WM_SETFONT, wParam, lParam);
01467 
01468     case DTM_GETMCFONT:
01469     return SendMessageW (infoPtr->hMonthCal, WM_GETFONT, wParam, lParam);
01470 
01471     case WM_NOTIFY:
01472     return DATETIME_Notify (infoPtr, (LPNMHDR)lParam);
01473 
01474     case WM_ENABLE:
01475         return DATETIME_Enable (infoPtr, (BOOL)wParam);
01476 
01477     case WM_ERASEBKGND:
01478         return DATETIME_EraseBackground (infoPtr, (HDC)wParam);
01479 
01480     case WM_GETDLGCODE:
01481         return DLGC_WANTARROWS | DLGC_WANTCHARS;
01482 
01483     case WM_PRINTCLIENT:
01484     case WM_PAINT:
01485         return DATETIME_Paint (infoPtr, (HDC)wParam);
01486 
01487     case WM_KEYDOWN:
01488         return DATETIME_KeyDown (infoPtr, wParam);
01489 
01490     case WM_CHAR:
01491         return DATETIME_Char (infoPtr, wParam);
01492 
01493     case WM_KILLFOCUS:
01494         return DATETIME_KillFocus (infoPtr, (HWND)wParam);
01495 
01496     case WM_NCCREATE:
01497         return DATETIME_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);
01498 
01499     case WM_SETFOCUS:
01500         return DATETIME_SetFocus (infoPtr, (HWND)wParam);
01501 
01502     case WM_SIZE:
01503         return DATETIME_Size (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
01504 
01505     case WM_LBUTTONDOWN:
01506         return DATETIME_LButtonDown (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
01507 
01508     case WM_LBUTTONUP:
01509         return DATETIME_LButtonUp (infoPtr);
01510 
01511     case WM_VSCROLL:
01512         return DATETIME_VScroll (infoPtr, (WORD)wParam);
01513 
01514     case WM_CREATE:
01515     return DATETIME_Create (hwnd, (LPCREATESTRUCTW)lParam);
01516 
01517     case WM_DESTROY:
01518     return DATETIME_Destroy (infoPtr);
01519 
01520     case WM_COMMAND:
01521         return DATETIME_Command (infoPtr, wParam, lParam);
01522 
01523     case WM_STYLECHANGING:
01524         return DATETIME_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
01525 
01526     case WM_STYLECHANGED:
01527         return DATETIME_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
01528 
01529     case WM_SETFONT:
01530         return DATETIME_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
01531 
01532     case WM_GETFONT:
01533         return (LRESULT) infoPtr->hFont;
01534 
01535     case WM_GETTEXT:
01536         return (LRESULT) DATETIME_GetText(infoPtr, wParam, (LPWSTR)lParam);
01537 
01538     case WM_SETTEXT:
01539         return CB_ERR;
01540 
01541     default:
01542     if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
01543         ERR("unknown msg %04x wp=%08lx lp=%08lx\n",
01544              uMsg, wParam, lParam);
01545     return DefWindowProcW (hwnd, uMsg, wParam, lParam);
01546     }
01547 }
01548 
01549 
01550 void
01551 DATETIME_Register (void)
01552 {
01553     WNDCLASSW wndClass;
01554 
01555     ZeroMemory (&wndClass, sizeof(WNDCLASSW));
01556     wndClass.style         = CS_GLOBALCLASS;
01557     wndClass.lpfnWndProc   = DATETIME_WindowProc;
01558     wndClass.cbClsExtra    = 0;
01559     wndClass.cbWndExtra    = sizeof(DATETIME_INFO *);
01560     wndClass.hCursor       = LoadCursorW (0, (LPCWSTR)IDC_ARROW);
01561     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
01562     wndClass.lpszClassName = DATETIMEPICK_CLASSW;
01563 
01564     RegisterClassW (&wndClass);
01565 }
01566 
01567 
01568 void
01569 DATETIME_Unregister (void)
01570 {
01571     UnregisterClassW (DATETIMEPICK_CLASSW, NULL);
01572 }

Generated on Sun May 27 2012 04:22:59 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.