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