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