[INTL]
[reactos.git] / reactos / dll / cpl / intl / date.c
1 /*
2 * ReactOS
3 * Copyright (C) 2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * PROJECT: ReactOS International Control Panel
21 * FILE: dll/cpl/intl/date.c
22 * PURPOSE: Date property page
23 * PROGRAMMER: Eric Kohl
24 */
25
26 #include "intl.h"
27
28 /* GLOBALS ******************************************************************/
29
30 #define YEAR_STR_MAX_SIZE 5
31 #define MAX_SHRT_DATE_SEPARATORS 3
32 #define STD_DATE_SEP L"."
33 #define YEAR_DIFF (99)
34 #define MAX_YEAR (9999)
35
36 static HWND hwndEnum = NULL;
37
38 /* FUNCTIONS ****************************************************************/
39
40 /* If char is 'y' or 'M' or 'd' return TRUE, else FALSE */
41 BOOL
42 isDateCompAl(WCHAR alpha)
43 {
44 if ((alpha == L'y') || (alpha == L'M') || (alpha == L'd') || (alpha == L' '))
45 return TRUE;
46 else
47 return FALSE;
48 }
49
50 /* Find first date separator in string */
51 LPTSTR
52 FindDateSep(const WCHAR *szSourceStr)
53 {
54 PWSTR pszFoundSep;
55 UINT nDateCompCount=0;
56 UINT nDateSepCount=0;
57
58 pszFoundSep = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, MAX_SAMPLES_STR_SIZE * sizeof(WCHAR));
59 if (pszFoundSep == NULL)
60 return NULL;
61
62 wcscpy(pszFoundSep,STD_DATE_SEP);
63
64 while (nDateCompCount < wcslen(szSourceStr))
65 {
66 if (!isDateCompAl(szSourceStr[nDateCompCount]) && (szSourceStr[nDateCompCount] != L'\''))
67 {
68 while (!isDateCompAl(szSourceStr[nDateCompCount]) && (szSourceStr[nDateCompCount] != L'\''))
69 {
70 pszFoundSep[nDateSepCount++] = szSourceStr[nDateCompCount];
71 nDateCompCount++;
72 }
73
74 pszFoundSep[nDateSepCount] = L'\0';
75 return pszFoundSep;
76 }
77
78 nDateCompCount++;
79 }
80
81 return pszFoundSep;
82 }
83
84 /* Replace given template in source string with string to replace and return received string */
85
86
87 /* Setted up short date separator to registry */
88 static BOOL
89 SetShortDateSep(HWND hwndDlg, PGLOBALDATA pGlobalData)
90 {
91 WCHAR szShortDateSep[MAX_SAMPLES_STR_SIZE];
92 INT nSepStrSize;
93 INT nSepCount;
94
95 /* Get separator */
96 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
97 WM_GETTEXT,
98 (WPARAM)MAX_SAMPLES_STR_SIZE,
99 (LPARAM)szShortDateSep);
100
101 /* Get separator string size */
102 nSepStrSize = wcslen(szShortDateSep);
103
104 /* Check date components */
105 for (nSepCount = 0; nSepCount < nSepStrSize; nSepCount++)
106 {
107 if (iswalnum(szShortDateSep[nSepCount]) || (szShortDateSep[nSepCount] == L'\''))
108 {
109 PrintErrorMsgBox(IDS_ERROR_SYMBOL_SEPARATE);
110 return FALSE;
111 }
112 }
113
114 /* Save date separator */
115 wcscpy(pGlobalData->szDateSep, szShortDateSep);
116
117 return TRUE;
118 }
119
120 /* Setted up short date format to registry */
121 static BOOL
122 SetShortDateFormat(HWND hwndDlg, PGLOBALDATA pGlobalData)
123 {
124 WCHAR szShortDateFmt[MAX_SAMPLES_STR_SIZE];
125 WCHAR szShortDateSep[MAX_SAMPLES_STR_SIZE];
126 WCHAR szFoundDateSep[MAX_SAMPLES_STR_SIZE];
127 PWSTR pszResultStr;
128 PWSTR pszFoundSep;
129 BOOL OpenApostFlg = FALSE;
130 INT nFmtStrSize;
131 INT nDateCompCount;
132
133 /* Get format */
134 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATEFMT_COMBO,
135 WM_GETTEXT,
136 (WPARAM)MAX_SAMPLES_STR_SIZE,
137 (LPARAM)szShortDateFmt);
138
139 /* Get separator */
140 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
141 WM_GETTEXT,
142 (WPARAM)MAX_SAMPLES_STR_SIZE,
143 (LPARAM)szShortDateSep);
144
145 /* Get format-string size */
146 nFmtStrSize = wcslen(szShortDateFmt);
147
148 /* Check date components */
149 for (nDateCompCount = 0; nDateCompCount < nFmtStrSize; nDateCompCount++)
150 {
151 if (szShortDateFmt[nDateCompCount] == L'\'')
152 {
153 OpenApostFlg = !OpenApostFlg;
154 }
155
156 if (iswalnum(szShortDateFmt[nDateCompCount]) &&
157 !isDateCompAl(szShortDateFmt[nDateCompCount]) &&
158 !OpenApostFlg)
159 {
160 PrintErrorMsgBox(IDS_ERROR_SYMBOL_FORMAT_SHORT);
161 return FALSE;
162 }
163
164 }
165
166 if (OpenApostFlg)
167 {
168 PrintErrorMsgBox(IDS_ERROR_SYMBOL_FORMAT_SHORT);
169 return FALSE;
170 }
171
172 pszFoundSep = FindDateSep(szShortDateFmt);
173 if (pszFoundSep != NULL)
174 {
175 /* Substring replacement of separator */
176 wcscpy(szFoundDateSep, pszFoundSep);
177 pszResultStr = ReplaceSubStr(szShortDateFmt, szShortDateSep, szFoundDateSep);
178 if (pszResultStr != NULL)
179 {
180 wcscpy(szShortDateFmt, pszResultStr);
181 HeapFree(GetProcessHeap(), 0, pszResultStr);
182 }
183
184 HeapFree(GetProcessHeap(), 0, pszFoundSep);
185 }
186
187 /* Save short date format */
188 wcscpy(pGlobalData->szShortDateFormat, szShortDateFmt);
189
190 return TRUE;
191 }
192
193 /* Setted up long date format to registry */
194 static BOOL
195 SetLongDateFormat(HWND hwndDlg, PGLOBALDATA pGlobalData)
196 {
197 WCHAR szLongDateFmt[MAX_SAMPLES_STR_SIZE];
198 BOOL OpenApostFlg = FALSE;
199 INT nFmtStrSize;
200 INT nDateCompCount;
201
202 /* Get format */
203 SendDlgItemMessageW(hwndDlg, IDC_LONGDATEFMT_COMBO,
204 WM_GETTEXT,
205 (WPARAM)MAX_SAMPLES_STR_SIZE,
206 (LPARAM)szLongDateFmt);
207
208 /* Get format string size */
209 nFmtStrSize = wcslen(szLongDateFmt);
210
211 /* Check date components */
212 for (nDateCompCount = 0; nDateCompCount < nFmtStrSize; nDateCompCount++)
213 {
214 if (szLongDateFmt[nDateCompCount] == L'\'')
215 {
216 OpenApostFlg = !OpenApostFlg;
217 }
218
219 if (iswalnum(szLongDateFmt[nDateCompCount]) &&
220 !isDateCompAl(szLongDateFmt[nDateCompCount]) &&
221 !OpenApostFlg)
222 {
223 PrintErrorMsgBox(IDS_ERROR_SYMBOL_FORMAT_LONG);
224 return FALSE;
225 }
226
227 }
228
229 if (OpenApostFlg)
230 {
231 PrintErrorMsgBox(IDS_ERROR_SYMBOL_FORMAT_LONG);
232 return FALSE;
233 }
234
235 /* Save long date format */
236 wcscpy(pGlobalData->szLongDateFormat, szLongDateFmt);
237
238 return TRUE;
239 }
240
241 /* Init short date separator control box */
242 static VOID
243 InitShortDateSepSamples(HWND hwndDlg, PGLOBALDATA pGlobalData)
244 {
245 PWSTR ShortDateSepSamples[MAX_SHRT_DATE_SEPARATORS] =
246 {
247 L".",
248 L"/",
249 L"-"
250 };
251 INT nCBIndex;
252 INT nRetCode;
253
254 /* Clear all box content */
255 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
256 CB_RESETCONTENT,
257 (WPARAM)0,
258 (LPARAM)0);
259
260 /* Create standard list of separators */
261 for (nCBIndex = 0; nCBIndex < MAX_SHRT_DATE_SEPARATORS; nCBIndex++)
262 {
263 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
264 CB_ADDSTRING,
265 0,
266 (LPARAM)ShortDateSepSamples[nCBIndex]);
267 }
268
269 /* Set current item to value from registry */
270 nRetCode = SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
271 CB_SELECTSTRING,
272 -1,
273 (LPARAM)pGlobalData->szDateSep);
274
275 /* If it is not successful, add new value to list and select them */
276 if (nRetCode == CB_ERR)
277 {
278 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
279 CB_ADDSTRING,
280 0,
281 (LPARAM)pGlobalData->szDateSep);
282 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
283 CB_SELECTSTRING,
284 -1,
285 (LPARAM)pGlobalData->szDateSep);
286 }
287 }
288
289 static BOOL CALLBACK
290 ShortDateFormatEnumProc(PWSTR lpTimeFormatString)
291 {
292 SendMessageW(hwndEnum,
293 CB_ADDSTRING,
294 0,
295 (LPARAM)lpTimeFormatString);
296
297 return TRUE;
298 }
299
300 /* Init short date control box */
301 VOID
302 InitShortDateCB(HWND hwndDlg, PGLOBALDATA pGlobalData)
303 {
304 INT nRetCode;
305
306 /* Limit text lengths */
307 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATEFMT_COMBO,
308 CB_LIMITTEXT,
309 MAX_SHORTDATEFORMAT,
310 0);
311 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESEP_COMBO,
312 CB_LIMITTEXT,
313 MAX_DATESEPARATOR,
314 0);
315
316 /* Clear all box content */
317 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATEFMT_COMBO,
318 CB_RESETCONTENT,
319 (WPARAM)0,
320 (LPARAM)0);
321
322 /* Enumerate short date formats */
323 hwndEnum = GetDlgItem(hwndDlg, IDC_SHRTDATEFMT_COMBO);
324 EnumDateFormatsW(ShortDateFormatEnumProc, pGlobalData->UserLCID, DATE_SHORTDATE);
325
326 /* Set current item to value from registry */
327 nRetCode = SendDlgItemMessageW(hwndDlg, IDC_SHRTDATEFMT_COMBO,
328 CB_SELECTSTRING,
329 -1,
330 (LPARAM)pGlobalData->szShortDateFormat);
331
332 /* If it is not successful, add new value to list and select them */
333 if (nRetCode == CB_ERR)
334 {
335 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATEFMT_COMBO,
336 CB_ADDSTRING,
337 0,
338 (LPARAM)pGlobalData->szShortDateFormat);
339 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATEFMT_COMBO,
340 CB_SELECTSTRING,
341 -1,
342 (LPARAM)pGlobalData->szShortDateFormat);
343 }
344 }
345
346 /* Init long date control box */
347 static VOID
348 InitLongDateCB(HWND hwndDlg, PGLOBALDATA pGlobalData)
349 {
350 INT nRetCode;
351
352 /* Limit text length */
353 SendDlgItemMessageW(hwndDlg, IDC_LONGDATEFMT_COMBO,
354 CB_LIMITTEXT,
355 MAX_LONGDATEFORMAT,
356 0);
357
358 /* Clear all box content */
359 SendDlgItemMessageW(hwndDlg, IDC_LONGDATEFMT_COMBO,
360 CB_RESETCONTENT,
361 (WPARAM)0,
362 (LPARAM)0);
363
364 /* Enumerate short long formats */
365 hwndEnum = GetDlgItem(hwndDlg, IDC_LONGDATEFMT_COMBO);
366 EnumDateFormatsW(ShortDateFormatEnumProc, pGlobalData->UserLCID, DATE_LONGDATE);
367
368 /* Set current item to value from registry */
369 nRetCode = SendDlgItemMessageW(hwndDlg, IDC_LONGDATEFMT_COMBO,
370 CB_SELECTSTRING,
371 -1,
372 (LPARAM)pGlobalData->szLongDateFormat);
373
374 /* If it is not successful, add new value to list and select them */
375 if (nRetCode == CB_ERR)
376 {
377 SendDlgItemMessageW(hwndDlg, IDC_LONGDATEFMT_COMBO,
378 CB_ADDSTRING,
379 0,
380 (LPARAM)pGlobalData->szLongDateFormat);
381 SendDlgItemMessageW(hwndDlg, IDC_LONGDATEFMT_COMBO,
382 CB_SELECTSTRING,
383 -1,
384 (LPARAM)pGlobalData->szLongDateFormat);
385 }
386 }
387
388 /* Set up max date value to registry */
389 static VOID
390 SetMaxDate(HWND hwndDlg, LCID lcid)
391 {
392 WCHAR szMaxDateVal[YEAR_STR_MAX_SIZE];
393 HWND hWndYearSpin;
394 INT nSpinVal;
395
396 hWndYearSpin = GetDlgItem(hwndDlg, IDC_SCR_MAX_YEAR);
397
398 /* Get spin value */
399 nSpinVal = LOWORD(SendMessageW(hWndYearSpin,
400 UDM_GETPOS,
401 0,
402 0));
403
404 /* convert to wide char */
405 _itow(nSpinVal, szMaxDateVal, DECIMAL_RADIX);
406
407 /* Save max date value */
408 SetCalendarInfoW(lcid,
409 CAL_GREGORIAN,
410 48 , /* CAL_ITWODIGITYEARMAX */
411 (PCWSTR)szMaxDateVal);
412 }
413
414 /* Get max date value from registry set */
415 static INT
416 GetMaxDate(LCID lcid)
417 {
418 INT nMaxDateVal = 0;
419
420 GetCalendarInfoW(lcid,
421 CAL_GREGORIAN,
422 CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER,
423 NULL,
424 0, /* ret type - number */
425 (LPDWORD)&nMaxDateVal);
426
427 return nMaxDateVal;
428 }
429
430 /* Set's MIN data edit control value to MAX-99 */
431 static VOID
432 SetMinDate(HWND hwndDlg)
433 {
434 WCHAR OutBuffer[YEAR_STR_MAX_SIZE];
435 HWND hWndYearSpin;
436 INT nSpinVal;
437
438 hWndYearSpin = GetDlgItem(hwndDlg, IDC_SCR_MAX_YEAR);
439
440 /* Get spin value */
441 nSpinVal = LOWORD(SendMessageW(hWndYearSpin,
442 UDM_GETPOS,
443 0,
444 0));
445
446 /* Set min year value */
447 wsprintf(OutBuffer, L"%d", (DWORD)nSpinVal - YEAR_DIFF);
448 SendDlgItemMessageW(hwndDlg, IDC_FIRSTYEAR_EDIT,
449 WM_SETTEXT,
450 0,
451 (LPARAM)OutBuffer);
452 }
453
454 /* Init spin control */
455 static VOID
456 InitMinMaxDateSpin(HWND hwndDlg, PGLOBALDATA pGlobalData)
457 {
458 WCHAR OutBuffer[YEAR_STR_MAX_SIZE];
459 HWND hWndYearSpin;
460
461 /* Limit text lengths */
462 SendDlgItemMessageW(hwndDlg, IDC_FIRSTYEAR_EDIT,
463 EM_LIMITTEXT,
464 MAX_YEAR_EDIT,
465 0);
466 SendDlgItemMessageW(hwndDlg, IDC_SECONDYEAR_EDIT,
467 EM_LIMITTEXT,
468 MAX_YEAR_EDIT,
469 0);
470
471 hWndYearSpin = GetDlgItem(hwndDlg, IDC_SCR_MAX_YEAR);
472
473 /* Init max date value */
474 wsprintf(OutBuffer, L"%04d", (DWORD)GetMaxDate(pGlobalData->UserLCID));
475 SendDlgItemMessageW(hwndDlg, IDC_SECONDYEAR_EDIT,
476 WM_SETTEXT,
477 0,
478 (LPARAM)OutBuffer);
479
480 /* Init min date value */
481 wsprintf(OutBuffer, L"%04d", (DWORD)GetMaxDate(pGlobalData->UserLCID) - YEAR_DIFF);
482 SendDlgItemMessageW(hwndDlg, IDC_FIRSTYEAR_EDIT,
483 WM_SETTEXT,
484 0,
485 (LPARAM)OutBuffer);
486
487 /* Init updown control */
488 /* Set bounds */
489 SendMessageW(hWndYearSpin,
490 UDM_SETRANGE,
491 0,
492 MAKELONG(MAX_YEAR,YEAR_DIFF));
493
494 /* Set current value */
495 SendMessageW(hWndYearSpin,
496 UDM_SETPOS,
497 0,
498 MAKELONG(GetMaxDate(pGlobalData->UserLCID),0));
499 }
500
501 /* Update all date locale samples */
502 static VOID
503 UpdateDateLocaleSamples(HWND hwndDlg,
504 PGLOBALDATA pGlobalData)
505 {
506 WCHAR OutBuffer[MAX_SAMPLES_STR_SIZE];
507
508 /* Get short date format sample */
509 GetDateFormatW(pGlobalData->UserLCID, 0, NULL,
510 pGlobalData->szShortDateFormat, OutBuffer,
511 MAX_SAMPLES_STR_SIZE);
512 SendDlgItemMessageW(hwndDlg, IDC_SHRTDATESAMPLE_EDIT, WM_SETTEXT,
513 0, (LPARAM)OutBuffer);
514
515 /* Get long date sample */
516 GetDateFormatW(pGlobalData->UserLCID, 0, NULL,
517 pGlobalData->szLongDateFormat, OutBuffer,
518 MAX_SAMPLES_STR_SIZE);
519 SendDlgItemMessageW(hwndDlg, IDC_LONGDATESAMPLE_EDIT,
520 WM_SETTEXT, 0, (LPARAM)OutBuffer);
521 }
522
523 /* Property page dialog callback */
524 INT_PTR CALLBACK
525 DatePageProc(HWND hwndDlg,
526 UINT uMsg,
527 WPARAM wParam,
528 LPARAM lParam)
529 {
530 PGLOBALDATA pGlobalData;
531
532 pGlobalData = (PGLOBALDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
533
534 switch (uMsg)
535 {
536 case WM_INITDIALOG:
537 pGlobalData = (PGLOBALDATA)((LPPROPSHEETPAGE)lParam)->lParam;
538 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData);
539
540 InitMinMaxDateSpin(hwndDlg, pGlobalData);
541 UpdateDateLocaleSamples(hwndDlg, pGlobalData);
542 InitShortDateCB(hwndDlg, pGlobalData);
543 InitLongDateCB(hwndDlg, pGlobalData);
544 InitShortDateSepSamples(hwndDlg, pGlobalData);
545 /* TODO: Add other calendar types */
546 break;
547
548 case WM_COMMAND:
549 switch (LOWORD(wParam))
550 {
551 case IDC_SECONDYEAR_EDIT:
552 if (HIWORD(wParam) == EN_CHANGE)
553 {
554 SetMinDate(hwndDlg);
555 }
556 break;
557
558 case IDC_SCR_MAX_YEAR:
559 /* Set "Apply" button enabled */
560 /* FIXME */
561 //PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
562 break;
563
564 case IDC_CALTYPE_COMBO:
565 case IDC_HIJCHRON_COMBO:
566 case IDC_SHRTDATEFMT_COMBO:
567 case IDC_LONGDATEFMT_COMBO:
568 case IDC_SHRTDATESEP_COMBO:
569 if (HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_EDITCHANGE)
570 {
571 /* Set "Apply" button enabled */
572 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
573 }
574 break;
575 }
576 break;
577
578 case WM_NOTIFY:
579 if (((LPNMHDR)lParam)->code == (UINT)PSN_APPLY)
580 {
581 if (!SetLongDateFormat(hwndDlg, pGlobalData))
582 break;
583
584 if (!SetShortDateFormat(hwndDlg, pGlobalData))
585 break;
586
587 if (!SetShortDateSep(hwndDlg, pGlobalData))
588 break;
589
590 pGlobalData->bUserLocaleChanged = TRUE;
591
592 SetMaxDate(hwndDlg, pGlobalData->UserLCID);
593 InitShortDateCB(hwndDlg, pGlobalData);
594 UpdateDateLocaleSamples(hwndDlg, pGlobalData);
595 }
596 break;
597 }
598
599 return FALSE;
600 }
601
602 /* EOF */