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