Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygendatetime.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
1.7.6.1
|