datetime.c 49.9 KB
Newer Older
1 2 3
/*
 * Date and time picker control
 *
4
 * Copyright 1998, 1999 Eric Kohl
5
 * Copyright 1999, 2000 Alex Priem <alexp@sci.kun.nl>
6
 * Copyright 2000 Chris Morgan <cmorgan@wpi.edu>
7
 * Copyright 2012 Owen Rudge for CodeWeavers
8
 *
9 10 11 12 13 14 15 16 17 18 19 20
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 23
 *
 * TODO:
24 25 26 27 28 29 30
 *    -- DTS_APPCANPARSE
 *    -- DTS_SHORTDATECENTURYFORMAT
 *    -- DTN_FORMAT
 *    -- DTN_FORMATQUERY
 *    -- DTN_USERSTRING
 *    -- DTN_WMKEYDOWN
 *    -- FORMATCALLBACK
31 32
 */

33 34
#include <math.h>
#include <string.h>
35
#include <stdarg.h>
36
#include <stdio.h>
37
#include <limits.h>
38

39
#include "windef.h"
40
#include "winbase.h"
41
#include "wingdi.h"
42 43
#include "winuser.h"
#include "winnls.h"
44
#include "commctrl.h"
45
#include "comctl32.h"
46
#include "wine/debug.h"
47

48
WINE_DEFAULT_DEBUG_CHANNEL(datetime);
49

50 51
typedef struct
{
52 53 54 55
    HWND hwndSelf;
    HWND hMonthCal;
    HWND hwndNotify;
    HWND hUpdown;
56
    DWORD dwStyle;
57 58 59 60 61 62 63 64
    SYSTEMTIME date;
    BOOL dateValid;
    HWND hwndCheckbut;
    RECT rcClient; /* rect around the edge of the window */
    RECT rcDraw; /* rect inside of the border */
    RECT checkbox;  /* checkbox allowing the control to be enabled/disabled */
    RECT calbutton; /* button that toggles the dropdown of the monthcal control */
    BOOL bCalDepressed; /* TRUE = cal button is depressed */
65
    BOOL bDropdownEnabled;
66
    int  select;
67 68
    WCHAR charsEntered[4];
    int nCharsEntered;
69 70 71 72 73 74 75 76 77
    HFONT hFont;
    int nrFieldsAllocated;
    int nrFields;
    int haveFocus;
    int *fieldspec;
    RECT *fieldRect;
    int  *buflen;
    WCHAR textbuf[256];
    POINT monthcal_pos;
Duane Clark's avatar
Duane Clark committed
78
    int pendingUpdown;
79 80 81
} DATETIME_INFO, *LPDATETIME_INFO;

/* in monthcal.c */
82
extern int MONTHCAL_MonthLength(int month, int year);
83
extern int MONTHCAL_CalculateDayOfWeek(SYSTEMTIME *date, BOOL inplace);
84 85 86

/* this list of defines is closely related to `allowedformatchars' defined
 * in datetime.c; the high nibble indicates the `base type' of the format
87
 * specifier.
88
 * Do not change without first reading DATETIME_UseFormat.
89
 *
90 91
 */

92
#define DT_END_FORMAT      0
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
#define ONEDIGITDAY   	0x01
#define TWODIGITDAY   	0x02
#define THREECHARDAY  	0x03
#define FULLDAY         0x04
#define ONEDIGIT12HOUR  0x11
#define TWODIGIT12HOUR  0x12
#define ONEDIGIT24HOUR  0x21
#define TWODIGIT24HOUR  0x22
#define ONEDIGITMINUTE  0x31
#define TWODIGITMINUTE  0x32
#define ONEDIGITMONTH   0x41
#define TWODIGITMONTH   0x42
#define THREECHARMONTH  0x43
#define FULLMONTH       0x44
#define ONEDIGITSECOND  0x51
#define TWODIGITSECOND  0x52
#define ONELETTERAMPM   0x61
#define TWOLETTERAMPM   0x62
#define ONEDIGITYEAR    0x71
#define TWODIGITYEAR    0x72
Huw Davies's avatar
Huw Davies committed
113 114
#define INVALIDFULLYEAR 0x73      /* FIXME - yyy is not valid - we'll treat it as yyyy */
#define FULLYEAR        0x74
115 116 117 118 119 120
#define FORMATCALLBACK  0x81      /* -> maximum of 0x80 callbacks possible */
#define FORMATCALLMASK  0x80
#define DT_STRING 	0x0100

#define DTHT_DATEFIELD  0xff      /* for hit-testing */

121 122 123 124 125
#define DTHT_NONE       0x1000
#define DTHT_CHECKBOX   0x2000  /* these should end at '00' , to make */
#define DTHT_MCPOPUP    0x3000  /* & DTHT_DATEFIELD 0 when DATETIME_KeyDown */
#define DTHT_GOTFOCUS   0x4000  /* tests for date-fields */
#define DTHT_NODATEMASK 0xf000  /* to mask check and drop down from others */
126

127 128
static BOOL DATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code);
static BOOL DATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr);
129 130
static const WCHAR allowedformatchars[] = {'d', 'h', 'H', 'm', 'M', 's', 't', 'y', 'X', 0};
static const int maxrepetition [] = {4,2,2,2,4,2,2,4,-1};
131

132 133 134
/* valid date limits */
static const SYSTEMTIME max_allowed_date = { /* wYear */ 9999, /* wMonth */ 12, /* wDayOfWeek */ 0, /* wDay */ 31 };
static const SYSTEMTIME min_allowed_date = { /* wYear */ 1752, /* wMonth */ 9,  /* wDayOfWeek */ 0, /* wDay */ 14 };
135

136
static DWORD
137
DATETIME_GetSystemTime (const DATETIME_INFO *infoPtr, SYSTEMTIME *systime)
138
{
139
    if (!systime) return GDT_NONE;
140

141 142
    if ((infoPtr->dwStyle & DTS_SHOWNONE) &&
        (SendMessageW (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0) == BST_UNCHECKED))
143 144
        return GDT_NONE;

145
    *systime = infoPtr->date;
146

147
    return GDT_VALID;
148 149
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
/* Checks value is within configured date range
 *
 * PARAMETERS
 *
 *  [I] infoPtr : valid pointer to control data
 *  [I] date    : pointer to valid date data to check
 *
 * RETURN VALUE
 *
 *  TRUE  - date within configured range
 *  FALSE - date is outside configured range
 */
static BOOL DATETIME_IsDateInValidRange(const DATETIME_INFO *infoPtr, const SYSTEMTIME *date)
{
    SYSTEMTIME range[2];
    DWORD limits;

    if ((MONTHCAL_CompareSystemTime(date, &max_allowed_date) == 1) ||
        (MONTHCAL_CompareSystemTime(date, &min_allowed_date) == -1))
        return FALSE;

171
    limits = SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, 0, (LPARAM)range);
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

    if (limits & GDTR_MAX)
    {
        if (MONTHCAL_CompareSystemTime(date, &range[1]) == 1)
           return FALSE;
    }

    if (limits & GDTR_MIN)
    {
        if (MONTHCAL_CompareSystemTime(date, &range[0]) == -1)
           return FALSE;
    }

    return TRUE;
}
187

188
static BOOL
189
DATETIME_SetSystemTime (DATETIME_INFO *infoPtr, DWORD flag, const SYSTEMTIME *systime)
190
{
191
    if (!systime) return FALSE;
192

193
    TRACE("%04d/%02d/%02d %02d:%02d:%02d\n",
194 195
          systime->wYear, systime->wMonth, systime->wDay,
          systime->wHour, systime->wMinute, systime->wSecond);
196 197

    if (flag == GDT_VALID) {
198 199 200 201 202 203 204 205 206 207
        if (systime->wYear == 0 ||
            systime->wMonth < 1 || systime->wMonth > 12 ||
            systime->wDay < 1 ||
            systime->wDay > MONTHCAL_MonthLength(systime->wMonth, systime->wYear) ||
            systime->wHour > 23 ||
            systime->wMinute > 59 ||
            systime->wSecond > 59 ||
            systime->wMilliseconds > 999
           )
            return FALSE;
208

209
        /* Windows returns true if the date is valid but outside the limits set */
210
        if (!DATETIME_IsDateInValidRange(infoPtr, systime))
211 212
            return TRUE;

213
        infoPtr->dateValid = TRUE;
214
        infoPtr->date = *systime;
215
        /* always store a valid day of week */
216
        MONTHCAL_CalculateDayOfWeek(&infoPtr->date, TRUE);
217

218 219
        SendMessageW (infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date));
        SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0);
220
    } else if ((infoPtr->dwStyle & DTS_SHOWNONE) && (flag == GDT_NONE)) {
221 222 223
        infoPtr->dateValid = FALSE;
        SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_UNCHECKED, 0);
    }
224
    else
225
        return FALSE;
226

227 228
    InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
    return TRUE;
229 230
}

231

232 233 234 235 236 237 238 239
/***
 * Split up a formattxt in actions.
 * See ms documentation for the meaning of the letter codes/'specifiers'.
 *
 * Notes:
 * *'dddddd' is handled as 'dddd' plus 'dd'.
 * *unrecognized formats are strings (here given the type DT_STRING;
 * start of the string is encoded in lower bits of DT_STRING.
240
 * Therefore, 'string' ends up as '<show seconds>tring'.
241
 *
242
 */
243
static void
244
DATETIME_UseFormat (DATETIME_INFO *infoPtr, LPCWSTR formattxt)
245
{
246 247
    unsigned int i;
    int j, k, len;
248
    BOOL inside_literal = FALSE; /* inside '...' */
249 250 251 252
    int *nrFields = &infoPtr->nrFields;

    *nrFields = 0;
    infoPtr->fieldspec[*nrFields] = 0;
253
    len = lstrlenW(allowedformatchars);
254 255 256 257
    k = 0;

    for (i = 0; formattxt[i]; i++)  {
	TRACE ("\n%d %c:", i, formattxt[i]);
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
	if (!inside_literal) {
	    for (j = 0; j < len; j++) {
	        if (allowedformatchars[j]==formattxt[i]) {
                    TRACE ("%c[%d,%x]", allowedformatchars[j], *nrFields, infoPtr->fieldspec[*nrFields]);
                    if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
                        infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
                        break;
                    }
                    if (infoPtr->fieldspec[*nrFields] >> 4 != j) {
                        (*nrFields)++;
                        infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
                        break;
                    }
                    if ((infoPtr->fieldspec[*nrFields] & 0x0f) == maxrepetition[j]) {
                        (*nrFields)++;
                        infoPtr->fieldspec[*nrFields] = (j<<4) + 1;
                        break;
		    }
                    infoPtr->fieldspec[*nrFields]++;
                    break;
                }   /* if allowedformatchar */
            } /* for j */
        }
        else
            j = len;

        if (formattxt[i] == '\'')
        {
            inside_literal = !inside_literal;
            continue;
        }
289 290 291 292 293 294 295 296 297 298 299 300 301 302

	/* char is not a specifier: handle char like a string */
	if (j == len) {
	    if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
		infoPtr->fieldspec[*nrFields] = DT_STRING + k;
		infoPtr->buflen[*nrFields] = 0;
            } else if ((infoPtr->fieldspec[*nrFields] & DT_STRING) != DT_STRING)  {
		(*nrFields)++;
		infoPtr->fieldspec[*nrFields] = DT_STRING + k;
		infoPtr->buflen[*nrFields] = 0;
	    }
	    infoPtr->textbuf[k] = formattxt[i];
	    k++;
	    infoPtr->buflen[*nrFields]++;
303 304
	}   /* if j=len */

305 306
	if (*nrFields == infoPtr->nrFieldsAllocated) {
	    FIXME ("out of memory; should reallocate. crash ahead.\n");
307
	}
308
    } /* for i */
309

310
    TRACE("\n");
311

312
    if (infoPtr->fieldspec[*nrFields] != 0) (*nrFields)++;
313 314
}

315

316
static BOOL
317
DATETIME_SetFormatW (DATETIME_INFO *infoPtr, LPCWSTR format)
318
{
319 320 321
    WCHAR format_buf[80];

    if (!format) {
322
	DWORD format_item;
323

324 325 326 327 328 329
        if ((infoPtr->dwStyle & DTS_SHORTDATECENTURYFORMAT) == DTS_SHORTDATECENTURYFORMAT)
            format_item = LOCALE_SSHORTDATE;
        else if ((infoPtr->dwStyle & DTS_LONGDATEFORMAT) == DTS_LONGDATEFORMAT)
            format_item = LOCALE_SLONGDATE;
        else if ((infoPtr->dwStyle & DTS_TIMEFORMAT) == DTS_TIMEFORMAT)
            format_item = LOCALE_STIMEFORMAT;
330
        else /* DTS_SHORTDATEFORMAT */
331
	    format_item = LOCALE_SSHORTDATE;
332
	GetLocaleInfoW(LOCALE_USER_DEFAULT, format_item, format_buf, ARRAY_SIZE(format_buf));
333
	format = format_buf;
334
    }
335

336
    DATETIME_UseFormat (infoPtr, format);
337
    InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
338

339
    return TRUE;
340
}
341

342

343 344
static BOOL
DATETIME_SetFormatA (DATETIME_INFO *infoPtr, LPCSTR lpszFormat)
345
{
346 347 348 349 350 351 352
    if (lpszFormat) {
	BOOL retval;
	INT len = MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, NULL, 0);
	LPWSTR wstr = Alloc(len * sizeof(WCHAR));
	if (wstr) MultiByteToWideChar(CP_ACP, 0, lpszFormat, -1, wstr, len);
	retval = DATETIME_SetFormatW (infoPtr, wstr);
	Free (wstr);
353
	return retval;
354 355 356
    }
    else
	return DATETIME_SetFormatW (infoPtr, 0);
357 358 359

}

360

361
static void
362
DATETIME_ReturnTxt (const DATETIME_INFO *infoPtr, int count, LPWSTR result, int resultSize)
363
{
364 365 366 367 368 369 370 371 372 373
    static const WCHAR fmt_dW[] = { '%', 'd', 0 };
    static const WCHAR fmt__2dW[] = { '%', '.', '2', 'd', 0 };
    static const WCHAR fmt__3sW[] = { '%', '.', '3', 's', 0 };
    SYSTEMTIME date = infoPtr->date;
    int spec;
    WCHAR buffer[80];

    *result=0;
    TRACE ("%d,%d\n", infoPtr->nrFields, count);
    if (count>infoPtr->nrFields || count < 0) {
374 375
	WARN ("buffer overrun, have %d want %d\n", infoPtr->nrFields, count);
	return;
376
    }
377

378
    if (!infoPtr->fieldspec) return;
379

380 381 382
    spec = infoPtr->fieldspec[count];
    if (spec & DT_STRING) {
	int txtlen = infoPtr->buflen[count];
383

384 385
        if (txtlen > resultSize)
            txtlen = resultSize - 1;
386 387 388
	memcpy (result, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR));
	result[txtlen] = 0;
	TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result));
389
	return;
390
    }
391

392

393
    switch (spec) {
394
	case DT_END_FORMAT:
395 396
	    *result = 0;
	    break;
397
	case ONEDIGITDAY:
398 399
	    wsprintfW (result, fmt_dW, date.wDay);
	    break;
400
	case TWODIGITDAY:
401 402
	    wsprintfW (result, fmt__2dW, date.wDay);
	    break;
403
	case THREECHARDAY:
404 405 406
	    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1+(date.wDayOfWeek+6)%7, result, 4);
	    /*wsprintfW (result,"%.3s",days[date.wDayOfWeek]);*/
	    break;
407
	case FULLDAY:
408 409
	    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDAYNAME1+(date.wDayOfWeek+6)%7, result, resultSize);
	    break;
410
	case ONEDIGIT12HOUR:
411 412 413 414 415 416 417
	    if (date.wHour == 0) {
	        result[0] = '1';
	        result[1] = '2';
	        result[2] = 0;
	    }
	    else
	        wsprintfW (result, fmt_dW, date.wHour - (date.wHour > 12 ? 12 : 0));
418
	    break;
419
	case TWODIGIT12HOUR:
420 421 422 423 424 425 426
	    if (date.wHour == 0) {
	        result[0] = '1';
	        result[1] = '2';
	        result[2] = 0;
	    }
	    else
	        wsprintfW (result, fmt__2dW, date.wHour - (date.wHour > 12 ? 12 : 0));
427
	    break;
428
	case ONEDIGIT24HOUR:
429 430
	    wsprintfW (result, fmt_dW, date.wHour);
	    break;
431
	case TWODIGIT24HOUR:
432 433
	    wsprintfW (result, fmt__2dW, date.wHour);
	    break;
434
	case ONEDIGITSECOND:
435 436
	    wsprintfW (result, fmt_dW, date.wSecond);
	    break;
437
	case TWODIGITSECOND:
438 439
	    wsprintfW (result, fmt__2dW, date.wSecond);
	    break;
440
	case ONEDIGITMINUTE:
441 442
	    wsprintfW (result, fmt_dW, date.wMinute);
	    break;
443
	case TWODIGITMINUTE:
444 445
	    wsprintfW (result, fmt__2dW, date.wMinute);
	    break;
446
	case ONEDIGITMONTH:
447 448
	    wsprintfW (result, fmt_dW, date.wMonth);
	    break;
449
	case TWODIGITMONTH:
450 451
	    wsprintfW (result, fmt__2dW, date.wMonth);
	    break;
452
	case THREECHARMONTH:
453
	    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1, buffer, ARRAY_SIZE(buffer));
454 455
	    wsprintfW (result, fmt__3sW, buffer);
	    break;
456
	case FULLMONTH:
457
	    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1,
458 459
                           result, resultSize);
	    break;
460
	case ONELETTERAMPM:
461 462 463
	    result[0] = (date.wHour < 12 ? 'A' : 'P');
	    result[1] = 0;
	    break;
464
	case TWOLETTERAMPM:
465 466 467 468
	    result[0] = (date.wHour < 12 ? 'A' : 'P');
	    result[1] = 'M';
	    result[2] = 0;
	    break;
469
	case FORMATCALLBACK:
470 471 472 473
	    FIXME ("Not implemented\n");
	    result[0] = 'x';
	    result[1] = 0;
	    break;
474
	case ONEDIGITYEAR:
475 476
	    wsprintfW (result, fmt_dW, date.wYear-10* (int) floor(date.wYear/10));
	    break;
477
	case TWODIGITYEAR:
478 479
	    wsprintfW (result, fmt__2dW, date.wYear-100* (int) floor(date.wYear/100));
	    break;
Huw Davies's avatar
Huw Davies committed
480
        case INVALIDFULLYEAR:
481
	case FULLYEAR:
482 483
	    wsprintfW (result, fmt_dW, date.wYear);
	    break;
484
    }
485

486
    TRACE ("arg%d=%x->[%s]\n", count, infoPtr->fieldspec[count], debugstr_w(result));
487 488
}

489
static int wrap(int val, int delta, int minVal, int maxVal)
490
{
491 492 493 494
    val += delta;
    if (delta == INT_MIN || val < minVal) return maxVal;
    if (delta == INT_MAX || val > maxVal) return minVal;
    return val;
495 496
}

497
static void
498
DATETIME_IncreaseField (DATETIME_INFO *infoPtr, int number, int delta)
499
{
500
    SYSTEMTIME *date = &infoPtr->date;
501 502 503
    SYSTEMTIME range[2];
    DWORD limits;
    BOOL min;
504

505 506
    TRACE ("%d\n", number);
    if ((number > infoPtr->nrFields) || (number < 0)) return;
507

508
    if ((infoPtr->fieldspec[number] & DTHT_DATEFIELD) == 0) return;
509

510
    switch (infoPtr->fieldspec[number]) {
511 512 513
	case ONEDIGITYEAR:
	case TWODIGITYEAR:
	case FULLYEAR:
514 515 516 517 518 519 520
            if (delta == INT_MIN)
                date->wYear = 1752;
            else if (delta == INT_MAX)
                date->wYear = 9999;
            else
                date->wYear = max(min(date->wYear + delta, 9999), 1752);

521 522 523
	    if (date->wDay > MONTHCAL_MonthLength(date->wMonth, date->wYear))
	        /* This can happen when moving away from a leap year. */
	        date->wDay = MONTHCAL_MonthLength(date->wMonth, date->wYear);
524
	    MONTHCAL_CalculateDayOfWeek(date, TRUE);
525
	    break;
526 527 528 529
	case ONEDIGITMONTH:
	case TWODIGITMONTH:
	case THREECHARMONTH:
	case FULLMONTH:
530
	    date->wMonth = wrap(date->wMonth, delta, 1, 12);
531
	    MONTHCAL_CalculateDayOfWeek(date, TRUE);
532 533
	    delta = 0;
	    /* fall through */
534 535 536
	case ONEDIGITDAY:
	case TWODIGITDAY:
	case THREECHARDAY:
537
	case FULLDAY:
538
	    date->wDay = wrap(date->wDay, delta, 1, MONTHCAL_MonthLength(date->wMonth, date->wYear));
539
	    MONTHCAL_CalculateDayOfWeek(date, TRUE);
540 541 542 543 544
	    break;
	case ONELETTERAMPM:
	case TWOLETTERAMPM:
	    delta *= 12;
	    /* fall through */
545 546 547 548
	case ONEDIGIT12HOUR:
	case TWODIGIT12HOUR:
	case ONEDIGIT24HOUR:
	case TWODIGIT24HOUR:
549 550
	    date->wHour = wrap(date->wHour, delta, 0, 23);
	    break;
551 552
	case ONEDIGITMINUTE:
	case TWODIGITMINUTE:
553 554 555 556 557 558
	    date->wMinute = wrap(date->wMinute, delta, 0, 59);
	    break;
	case ONEDIGITSECOND:
	case TWODIGITSECOND:
	    date->wSecond = wrap(date->wSecond, delta, 0, 59);
	    break;
559
	case FORMATCALLBACK:
560 561 562
	    FIXME ("Not implemented\n");
	    break;
    }
563

564 565 566 567 568 569 570 571 572 573 574 575 576
    /* FYI: On 1752/9/14 the calendar changed and England and the
     * American colonies changed to the Gregorian calendar. This change
     * involved having September 14th follow September 2nd. So no date
     * algorithm works before that date.
     */
    if (10000 * date->wYear + 100 * date->wMonth + date->wDay < 17520914) {
	date->wYear = 1752;
    	date->wMonth = 9;
	date->wDay = 14;
	date->wSecond = 0;
	date->wMinute = 0;
	date->wHour = 0;
    }
577

578
    /* Ensure time is within bounds */
579
    limits = SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, 0, (LPARAM)range);
580
    min = delta < 0;
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598

    if (limits & (min ? GDTR_MIN : GDTR_MAX))
    {
        int i = (min ? 0 : 1);

        if (MONTHCAL_CompareSystemTime(date, &range[i]) == (min ? -1 : 1))
        {
            date->wYear = range[i].wYear;
            date->wMonth = range[i].wMonth;
            date->wDayOfWeek = range[i].wDayOfWeek;
            date->wDay = range[i].wDay;
            date->wHour = range[i].wHour;
            date->wMinute = range[i].wMinute;
            date->wSecond = range[i].wSecond;
            date->wMilliseconds = range[i].wMilliseconds;
        }
    }
}
599

600
static int DATETIME_GetFieldWidth (const DATETIME_INFO *infoPtr, HDC hdc, int count)
Duane Clark's avatar
Duane Clark committed
601 602 603 604 605 606 607 608 609
{
    /* fields are a fixed width, determined by the largest possible string */
    /* presumably, these widths should be language dependent */
    static const WCHAR fld_d1W[] = { '2', 0 };
    static const WCHAR fld_d2W[] = { '2', '2', 0 };
    static const WCHAR fld_d4W[] = { '2', '2', '2', '2', 0 };
    static const WCHAR fld_am1[] = { 'A', 0 };
    static const WCHAR fld_am2[] = { 'A', 'M', 0 };
    int spec;
610 611
    WCHAR buffer[80];
    LPCWSTR bufptr;
Duane Clark's avatar
Duane Clark committed
612 613
    SIZE size;

614
    if (!infoPtr->fieldspec) return 0;
Duane Clark's avatar
Duane Clark committed
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642

    spec = infoPtr->fieldspec[count];
    if (spec & DT_STRING) {
	int txtlen = infoPtr->buflen[count];

        if (txtlen > 79)
            txtlen = 79;
	memcpy (buffer, infoPtr->textbuf + (spec &~ DT_STRING), txtlen * sizeof(WCHAR));
	buffer[txtlen] = 0;
	bufptr = buffer;
    }
    else {
        switch (spec) {
	    case ONEDIGITDAY:
	    case ONEDIGIT12HOUR:
	    case ONEDIGIT24HOUR:
	    case ONEDIGITSECOND:
	    case ONEDIGITMINUTE:
	    case ONEDIGITMONTH:
	    case ONEDIGITYEAR:
	        /* these seem to use a two byte field */
	    case TWODIGITDAY:
	    case TWODIGIT12HOUR:
	    case TWODIGIT24HOUR:
	    case TWODIGITSECOND:
	    case TWODIGITMINUTE:
	    case TWODIGITMONTH:
	    case TWODIGITYEAR:
643
	        bufptr = fld_d2W;
Duane Clark's avatar
Duane Clark committed
644 645 646
	        break;
            case INVALIDFULLYEAR:
	    case FULLYEAR:
647
	        bufptr = fld_d4W;
Duane Clark's avatar
Duane Clark committed
648 649 650
	        break;
	    case THREECHARMONTH:
	    case FULLMONTH:
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
	    case THREECHARDAY:
	    case FULLDAY:
	    {
		static const WCHAR fld_day[] = {'W','e','d','n','e','s','d','a','y',0};
		static const WCHAR fld_abbrday[] = {'W','e','d',0};
		static const WCHAR fld_mon[] = {'S','e','p','t','e','m','b','e','r',0};
		static const WCHAR fld_abbrmon[] = {'D','e','c',0};

		const WCHAR *fall;
		LCTYPE lctype;
		INT i, max_count;
		LONG cx;

		/* choose locale data type and fallback string */
		switch (spec) {
		case THREECHARDAY:
		    fall   = fld_abbrday;
		    lctype = LOCALE_SABBREVDAYNAME1;
		    max_count = 7;
		    break;
		case FULLDAY:
		    fall   = fld_day;
		    lctype = LOCALE_SDAYNAME1;
		    max_count = 7;
		    break;
		case THREECHARMONTH:
		    fall   = fld_abbrmon;
		    lctype = LOCALE_SABBREVMONTHNAME1;
		    max_count = 12;
		    break;
681
		default: /* FULLMONTH */
682 683 684 685 686 687 688 689 690 691
		    fall   = fld_mon;
		    lctype = LOCALE_SMONTHNAME1;
		    max_count = 12;
		    break;
		}

		cx = 0;
		for (i = 0; i < max_count; i++)
		{
		    if(GetLocaleInfoW(LOCALE_USER_DEFAULT, lctype + i,
692
			buffer, ARRAY_SIZE(buffer)))
693 694 695 696 697 698 699 700 701 702 703
		    {
			GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &size);
			if (size.cx > cx) cx = size.cx;
		    }
		    else /* locale independent fallback on failure */
		    {
		        GetTextExtentPoint32W(hdc, fall, lstrlenW(fall), &size);
			cx = size.cx;
		        break;
		    }
		}
704
		return cx;
705
	    }
Duane Clark's avatar
Duane Clark committed
706
	    case ONELETTERAMPM:
707
	        bufptr = fld_am1;
Duane Clark's avatar
Duane Clark committed
708 709
	        break;
	    case TWOLETTERAMPM:
710
	        bufptr = fld_am2;
Duane Clark's avatar
Duane Clark committed
711 712
	        break;
	    default:
713
	        bufptr = fld_d1W;
Duane Clark's avatar
Duane Clark committed
714 715 716
	        break;
        }
    }
717
    GetTextExtentPoint32W (hdc, bufptr, lstrlenW(bufptr), &size);
718
    return size.cx;
Duane Clark's avatar
Duane Clark committed
719 720
}

721 722
static void 
DATETIME_Refresh (DATETIME_INFO *infoPtr, HDC hdc)
723
{
724 725 726
    TRACE("\n");

    if (infoPtr->dateValid) {
727 728 729 730 731
        int i, prevright;
        RECT *field;
        RECT *rcDraw = &infoPtr->rcDraw;
        SIZE size;
        COLORREF oldTextColor;
732
        HFONT oldFont = SelectObject (hdc, infoPtr->hFont);
733
        INT oldBkMode = SetBkMode (hdc, TRANSPARENT);
734 735
        WCHAR txt[80];

736
        DATETIME_ReturnTxt (infoPtr, 0, txt, ARRAY_SIZE(txt));
737
        GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size);
738 739
        rcDraw->bottom = size.cy + 2;

740
        prevright = infoPtr->checkbox.right = ((infoPtr->dwStyle & DTS_SHOWNONE) ? 18 : 2);
741 742

        for (i = 0; i < infoPtr->nrFields; i++) {
743
            DATETIME_ReturnTxt (infoPtr, i, txt, ARRAY_SIZE(txt));
744
            GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size);
745
            field = &infoPtr->fieldRect[i];
746
            field->left   = prevright;
747
            field->right  = prevright + DATETIME_GetFieldWidth (infoPtr, hdc, i);
748
            field->top    = rcDraw->top;
749 750 751
            field->bottom = rcDraw->bottom;
            prevright = field->right;

752 753 754
            if (infoPtr->dwStyle & WS_DISABLED)
                oldTextColor = SetTextColor (hdc, comctl32_color.clrGrayText);
            else if ((infoPtr->haveFocus) && (i == infoPtr->select)) {
755 756
                RECT selection;

757
                /* fill if focused */
758
                HBRUSH hbr = CreateSolidBrush (comctl32_color.clrActiveCaption);
759

760 761 762 763
                if (infoPtr->nCharsEntered)
                {
                    memcpy(txt, infoPtr->charsEntered, infoPtr->nCharsEntered * sizeof(WCHAR));
                    txt[infoPtr->nCharsEntered] = 0;
764
                    GetTextExtentPoint32W (hdc, txt, lstrlenW(txt), &size);
765 766
                }

767
                SetRect(&selection, 0, 0, size.cx, size.cy);
768
                /* center rectangle */
769 770
                OffsetRect(&selection, (field->right  + field->left - size.cx)/2,
                                       (field->bottom - size.cy)/2);
771 772

                FillRect(hdc, &selection, hbr);
773
                DeleteObject (hbr);
774 775 776 777 778 779
                oldTextColor = SetTextColor (hdc, comctl32_color.clrWindow);
            }
            else
                oldTextColor = SetTextColor (hdc, comctl32_color.clrWindowText);

            /* draw the date text using the colour set above */
780
            DrawTextW (hdc, txt, lstrlenW(txt), field, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
781
            SetTextColor (hdc, oldTextColor);
782
        }
783
        SetBkMode (hdc, oldBkMode);
784
        SelectObject (hdc, oldFont);
785
    }
786

787
    if (!(infoPtr->dwStyle & DTS_UPDOWN)) {
788
        DrawFrameControl(hdc, &infoPtr->calbutton, DFC_SCROLL,
789 790 791
                         DFCS_SCROLLDOWN | (infoPtr->bCalDepressed ? DFCS_PUSHED : 0) |
                         (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
    }
792 793
}

794

795
static INT
796
DATETIME_HitTest (const DATETIME_INFO *infoPtr, POINT pt)
797
{
798
    int i;
799

800
    TRACE ("%s\n", wine_dbgstr_point(&pt));
801

802 803
    if (PtInRect (&infoPtr->calbutton, pt)) return DTHT_MCPOPUP;
    if (PtInRect (&infoPtr->checkbox, pt)) return DTHT_CHECKBOX;
804

805
    for (i = 0; i < infoPtr->nrFields; i++) {
806
        if (PtInRect (&infoPtr->fieldRect[i], pt)) return i;
807
    }
808

809
    return DTHT_NONE;
810 811
}

812 813
/* Returns index of the nearest preceding date field from given,
   or -1 if none was found */
814 815 816 817 818 819 820 821
static int DATETIME_GetPrevDateField(const DATETIME_INFO *infoPtr, int i)
{
    for(--i; i >= 0; i--)
    {
        if (infoPtr->fieldspec[i] & DTHT_DATEFIELD) return i;
    }
    return -1;
}
822

823 824 825 826
static void
DATETIME_ApplySelectedField (DATETIME_INFO *infoPtr)
{
    int fieldNum = infoPtr->select & DTHT_DATEFIELD;
827 828
    int i, val = 0;
    BOOL clamp_day = FALSE;
829
    SYSTEMTIME date = infoPtr->date;
830
    int oldyear;
831 832 833 834

    if (infoPtr->select == -1 || infoPtr->nCharsEntered == 0)
        return;

835 836 837 838 839 840 841
    if ((infoPtr->fieldspec[fieldNum] == ONELETTERAMPM) ||
        (infoPtr->fieldspec[fieldNum] == TWOLETTERAMPM))
        val = infoPtr->charsEntered[0];
    else {
        for (i=0; i<infoPtr->nCharsEntered; i++)
            val = val * 10 + infoPtr->charsEntered[i] - '0';
    }
842 843 844 845 846 847

    infoPtr->nCharsEntered = 0;

    switch (infoPtr->fieldspec[fieldNum]) {
        case ONEDIGITYEAR:
        case TWODIGITYEAR:
848
            oldyear = date.wYear;
849
            date.wYear = date.wYear - (date.wYear%100) + val;
850 851

            if (DATETIME_IsDateInValidRange(infoPtr, &date))
852
                clamp_day = TRUE;
853 854 855
            else
                date.wYear = oldyear;

856 857 858
            break;
        case INVALIDFULLYEAR:
        case FULLYEAR:
859
            oldyear = date.wYear;
860
            date.wYear = val;
861 862

            if (DATETIME_IsDateInValidRange(infoPtr, &date))
863
                clamp_day = TRUE;
864 865 866
            else
                date.wYear = oldyear;

867 868 869 870
            break;
        case ONEDIGITMONTH:
        case TWODIGITMONTH:
            date.wMonth = val;
871
            clamp_day = TRUE;
872 873 874 875 876 877 878
            break;
        case ONEDIGITDAY:
        case TWODIGITDAY:
            date.wDay = val;
            break;
        case ONEDIGIT12HOUR:
        case TWODIGIT12HOUR:
879 880 881 882 883 884 885 886 887 888 889 890
            if (val >= 24)
                val -= 20;

            if (val >= 13)
                date.wHour = val;
            else if (val != 0) {
                if (date.wHour >= 12) /* preserve current AM/PM state */
                    date.wHour = (val == 12 ? 12 : val + 12);
                else
                    date.wHour = (val == 12 ? 0 : val);
            }
            break;
891 892 893 894 895 896 897 898 899 900 901 902
        case ONEDIGIT24HOUR:
        case TWODIGIT24HOUR:
            date.wHour = val;
            break;
        case ONEDIGITMINUTE:
        case TWODIGITMINUTE:
            date.wMinute = val;
            break;
        case ONEDIGITSECOND:
        case TWODIGITSECOND:
            date.wSecond = val;
            break;
903 904 905 906 907 908 909 910 911 912
        case ONELETTERAMPM:
        case TWOLETTERAMPM:
            if (val == 'a' || val == 'A') {
                if (date.wHour >= 12)
                    date.wHour -= 12;
            } else if (val == 'p' || val == 'P') {
                if (date.wHour < 12)
                    date.wHour += 12;
            }
            break;
913 914
    }

915 916 917
    if (clamp_day && date.wDay > MONTHCAL_MonthLength(date.wMonth, date.wYear))
        date.wDay = MONTHCAL_MonthLength(date.wMonth, date.wYear);

918 919 920 921 922 923 924 925 926 927 928 929 930
    if (DATETIME_SetSystemTime(infoPtr, GDT_VALID, &date))
        DATETIME_SendDateTimeChangeNotify (infoPtr);
}

static void
DATETIME_SetSelectedField (DATETIME_INFO *infoPtr, int select)
{
    DATETIME_ApplySelectedField(infoPtr);

    infoPtr->select = select;
    infoPtr->nCharsEntered = 0;
}

931
static LRESULT
932
DATETIME_LButtonDown (DATETIME_INFO *infoPtr, INT x, INT y)
933
{
934
    POINT pt;
935
    int new;
936 937 938 939

    pt.x = x;
    pt.y = y;
    new = DATETIME_HitTest (infoPtr, pt);
940

941
    SetFocus(infoPtr->hwndSelf);
942

943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
    if (!(new & DTHT_NODATEMASK) || (new == DTHT_NONE))
    {
        if (new == DTHT_NONE)
            new = infoPtr->nrFields - 1;
        else
        {
            /* hitting string part moves selection to next date field to left */
            if (infoPtr->fieldspec[new] & DT_STRING)
            {
                new = DATETIME_GetPrevDateField(infoPtr, new);
                if (new == -1) return 0;
            }
            /* never select full day of week */
            if (infoPtr->fieldspec[new] == FULLDAY) return 0;
        }
    }
959 960

    DATETIME_SetSelectedField(infoPtr, new);
961

962
    if (infoPtr->select == DTHT_MCPOPUP) {
963
        RECT rcMonthCal;
964
        POINT pos;
965 966
        SendMessageW(infoPtr->hMonthCal, MCM_GETMINREQRECT, 0, (LPARAM)&rcMonthCal);

967 968 969 970 971 972
        /* FIXME: button actually is only depressed during dropdown of the */
        /* calendar control and when the mouse is over the button window */
        infoPtr->bCalDepressed = TRUE;

        /* recalculate the position of the monthcal popup */
        if(infoPtr->dwStyle & DTS_RIGHTALIGN)
973
            pos.x = infoPtr->calbutton.left - (rcMonthCal.right - rcMonthCal.left);
974
        else
975
            /* FIXME: this should be after the area reserved for the checkbox */
976
            pos.x = infoPtr->rcDraw.left;
977

978 979 980 981 982
        pos.y = infoPtr->rcClient.bottom;
        OffsetRect( &rcMonthCal, pos.x, pos.y );
        MapWindowPoints( infoPtr->hwndSelf, 0, (POINT *)&rcMonthCal, 2 );
        SetWindowPos(infoPtr->hMonthCal, 0, rcMonthCal.left, rcMonthCal.top,
                     rcMonthCal.right - rcMonthCal.left, rcMonthCal.bottom - rcMonthCal.top, 0);
983 984 985

        if(IsWindowVisible(infoPtr->hMonthCal)) {
            ShowWindow(infoPtr->hMonthCal, SW_HIDE);
986 987
            infoPtr->bDropdownEnabled = FALSE;
            DATETIME_SendSimpleNotify (infoPtr, DTN_CLOSEUP);
988
        } else {
989
            const SYSTEMTIME *lprgSysTimeArray = &infoPtr->date;
990 991 992
            TRACE("update calendar %04d/%02d/%02d\n", 
            lprgSysTimeArray->wYear, lprgSysTimeArray->wMonth, lprgSysTimeArray->wDay);
            SendMessageW(infoPtr->hMonthCal, MCM_SETCURSEL, 0, (LPARAM)(&infoPtr->date));
993

994
            if (infoPtr->bDropdownEnabled) {
995
                ShowWindow(infoPtr->hMonthCal, SW_SHOW);
996 997
                DATETIME_SendSimpleNotify (infoPtr, DTN_DROPDOWN);
            }
998
            infoPtr->bDropdownEnabled = TRUE;
999 1000 1001 1002
        }

        TRACE ("dt:%p mc:%p mc parent:%p, desktop:%p\n",
               infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hwndNotify, GetDesktopWindow ());
1003
    }
1004

1005
    InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1006

1007
    return 0;
1008 1009 1010 1011
}


static LRESULT
1012
DATETIME_LButtonUp (DATETIME_INFO *infoPtr)
1013
{
1014
    if(infoPtr->bCalDepressed) {
1015 1016 1017
        infoPtr->bCalDepressed = FALSE;
        InvalidateRect(infoPtr->hwndSelf, &(infoPtr->calbutton), TRUE);
    }
1018

1019
    return 0;
1020 1021 1022 1023
}


static LRESULT
1024
DATETIME_Paint (DATETIME_INFO *infoPtr, HDC hdc)
1025
{
1026 1027 1028 1029 1030 1031 1032 1033
    if (!hdc) {
	PAINTSTRUCT ps;
        hdc = BeginPaint (infoPtr->hwndSelf, &ps);
        DATETIME_Refresh (infoPtr, hdc);
        EndPaint (infoPtr->hwndSelf, &ps);
    } else {
        DATETIME_Refresh (infoPtr, hdc);
    }
1034 1035 1036 1037

    /* Not a click on the dropdown box, enabled it */
    infoPtr->bDropdownEnabled = TRUE;

1038 1039 1040
    return 0;
}

1041

Huw Davies's avatar
Huw Davies committed
1042
static LRESULT
1043
DATETIME_Button_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
Huw Davies's avatar
Huw Davies committed
1044
{
1045 1046 1047 1048
    if( HIWORD(wParam) == BN_CLICKED) {
        DWORD state = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0);
        infoPtr->dateValid = (state == BST_CHECKED);
        InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
Huw Davies's avatar
Huw Davies committed
1049
    }
1050
    return 0;
Huw Davies's avatar
Huw Davies committed
1051 1052 1053 1054 1055
}
          
        
        
static LRESULT
1056
DATETIME_Command (DATETIME_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
Huw Davies's avatar
Huw Davies committed
1057 1058 1059
{
    TRACE("hwndbutton = %p\n", infoPtr->hwndCheckbut);
    if(infoPtr->hwndCheckbut == (HWND)lParam)
1060
        return DATETIME_Button_Command(infoPtr, wParam, lParam);
Huw Davies's avatar
Huw Davies committed
1061 1062 1063
    return 0;
}

1064

1065 1066 1067 1068 1069 1070 1071 1072
static LRESULT
DATETIME_Enable (DATETIME_INFO *infoPtr, BOOL bEnable)
{
    TRACE("%p %s\n", infoPtr, bEnable ? "TRUE" : "FALSE");
    if (bEnable)
        infoPtr->dwStyle &= ~WS_DISABLED;
    else
        infoPtr->dwStyle |= WS_DISABLED;
1073 1074 1075

    InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);

1076 1077 1078 1079 1080
    return 0;
}


static LRESULT
1081
DATETIME_EraseBackground (const DATETIME_INFO *infoPtr, HDC hdc)
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
{
    HBRUSH hBrush, hSolidBrush = NULL;
    RECT   rc;

    if (infoPtr->dwStyle & WS_DISABLED)
        hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace);
    else
    {
        hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT,
                                      (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
        if (!hBrush)
            hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow);
    }

    GetClientRect (infoPtr->hwndSelf, &rc);

    FillRect (hdc, &rc, hBrush);

    if (hSolidBrush)
        DeleteObject(hSolidBrush);

    return -1;
}


1107
static LRESULT
1108
DATETIME_Notify (DATETIME_INFO *infoPtr, const NMHDR *lpnmh)
1109
{
1110 1111 1112 1113 1114 1115 1116
    TRACE ("Got notification %x from %p\n", lpnmh->code, lpnmh->hwndFrom);
    TRACE ("info: %p %p %p\n", infoPtr->hwndSelf, infoPtr->hMonthCal, infoPtr->hUpdown);

    if (lpnmh->code == MCN_SELECT) {
        ShowWindow(infoPtr->hMonthCal, SW_HIDE);
        infoPtr->dateValid = TRUE;
        SendMessageW (infoPtr->hMonthCal, MCM_GETCURSEL, 0, (LPARAM)&infoPtr->date);
Duane Clark's avatar
Duane Clark committed
1117 1118
        TRACE("got from calendar %04d/%02d/%02d day of week %d\n", 
        infoPtr->date.wYear, infoPtr->date.wMonth, infoPtr->date.wDay, infoPtr->date.wDayOfWeek);
1119 1120 1121
        SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0);
        InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
        DATETIME_SendDateTimeChangeNotify (infoPtr);
1122
        DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP);
1123
    }
Duane Clark's avatar
Duane Clark committed
1124
    if ((lpnmh->hwndFrom == infoPtr->hUpdown) && (lpnmh->code == UDN_DELTAPOS)) {
1125
        const NM_UPDOWN *lpnmud = (const NM_UPDOWN*)lpnmh;
Duane Clark's avatar
Duane Clark committed
1126 1127 1128
        TRACE("Delta pos %d\n", lpnmud->iDelta);
        infoPtr->pendingUpdown = lpnmud->iDelta;
    }
1129
    return 0;
1130 1131
}

1132

1133
static LRESULT
1134
DATETIME_KeyDown (DATETIME_INFO *infoPtr, DWORD vkCode)
1135
{
1136 1137
    int fieldNum = infoPtr->select & DTHT_DATEFIELD;
    int wrap = 0;
1138
    int new;
1139

1140 1141
    if (!(infoPtr->haveFocus)) return 0;
    if ((fieldNum==0) && (infoPtr->select)) return 0;
1142

1143
    if (infoPtr->select & FORMATCALLMASK) {
1144
	FIXME ("Callbacks not implemented yet\n");
1145
    }
1146

1147
    switch (vkCode) {
1148
	case VK_ADD:
1149
    	case VK_UP:
1150
	    infoPtr->nCharsEntered = 0;
1151 1152 1153
	    DATETIME_IncreaseField (infoPtr, fieldNum, 1);
	    DATETIME_SendDateTimeChangeNotify (infoPtr);
	    break;
1154 1155
	case VK_SUBTRACT:
	case VK_DOWN:
1156
	    infoPtr->nCharsEntered = 0;
1157 1158 1159
	    DATETIME_IncreaseField (infoPtr, fieldNum, -1);
	    DATETIME_SendDateTimeChangeNotify (infoPtr);
	    break;
1160
	case VK_HOME:
1161
	    infoPtr->nCharsEntered = 0;
1162 1163 1164
	    DATETIME_IncreaseField (infoPtr, fieldNum, INT_MIN);
	    DATETIME_SendDateTimeChangeNotify (infoPtr);
	    break;
1165
	case VK_END:
1166
	    infoPtr->nCharsEntered = 0;
1167 1168 1169
	    DATETIME_IncreaseField (infoPtr, fieldNum, INT_MAX);
	    DATETIME_SendDateTimeChangeNotify (infoPtr);
	    break;
1170
	case VK_LEFT:
1171
	    new = infoPtr->select;
1172
	    do {
1173 1174
		if (new == 0) {
		    new = new - 1;
1175 1176
		    wrap++;
		} else {
1177
		    new--;
1178
		}
1179 1180 1181
	    } while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2));
	    if (new != infoPtr->select)
	        DATETIME_SetSelectedField(infoPtr, new);
1182
	    break;
1183
	case VK_RIGHT:
1184
	    new = infoPtr->select;
1185
	    do {
1186 1187 1188
		new++;
		if (new==infoPtr->nrFields) {
		    new = 0;
1189 1190
		    wrap++;
		}
1191 1192 1193
	    } while ((infoPtr->fieldspec[new] & DT_STRING) && (wrap<2));
	    if (new != infoPtr->select)
	        DATETIME_SetSelectedField(infoPtr, new);
1194 1195
	    break;
    }
1196

1197
    InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1198

1199
    return 0;
1200 1201 1202
}


1203
static LRESULT
1204
DATETIME_Char (DATETIME_INFO *infoPtr, WPARAM vkCode)
1205
{
1206
    int fieldNum, fieldSpec;
1207

1208 1209 1210 1211 1212 1213 1214 1215 1216
    fieldNum = infoPtr->select & DTHT_DATEFIELD;
    fieldSpec = infoPtr->fieldspec[fieldNum];

    if (fieldSpec == ONELETTERAMPM || fieldSpec == TWOLETTERAMPM) {
        infoPtr->charsEntered[0] = vkCode;
        infoPtr->nCharsEntered = 1;

        DATETIME_ApplySelectedField(infoPtr);
    } else if (vkCode >= '0' && vkCode <= '9') {
1217 1218 1219 1220 1221 1222 1223 1224 1225
        int maxChars;

        infoPtr->charsEntered[infoPtr->nCharsEntered++] = vkCode;

        if (fieldSpec == INVALIDFULLYEAR || fieldSpec == FULLYEAR)
            maxChars = 4;
        else
            maxChars = 2;

1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
        if ((fieldSpec == ONEDIGIT12HOUR ||
             fieldSpec == TWODIGIT12HOUR ||
             fieldSpec == ONEDIGIT24HOUR ||
             fieldSpec == TWODIGIT24HOUR) &&
            (infoPtr->nCharsEntered == 1))
        {
            if (vkCode >= '3')
                 maxChars = 1;
        }

1236 1237
        if (maxChars == infoPtr->nCharsEntered)
            DATETIME_ApplySelectedField(infoPtr);
1238
    }
1239

1240 1241 1242 1243
    return 0;
}


Duane Clark's avatar
Duane Clark committed
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
static LRESULT
DATETIME_VScroll (DATETIME_INFO *infoPtr, WORD wScroll)
{
    int fieldNum = infoPtr->select & DTHT_DATEFIELD;

    if ((SHORT)LOWORD(wScroll) != SB_THUMBPOSITION) return 0;
    if (!(infoPtr->haveFocus)) return 0;
    if ((fieldNum==0) && (infoPtr->select)) return 0;

    if (infoPtr->pendingUpdown >= 0) {
	DATETIME_IncreaseField (infoPtr, fieldNum, 1);
	DATETIME_SendDateTimeChangeNotify (infoPtr);
    }
    else {
	DATETIME_IncreaseField (infoPtr, fieldNum, -1);
	DATETIME_SendDateTimeChangeNotify (infoPtr);
    }

1262
    InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
Duane Clark's avatar
Duane Clark committed
1263 1264 1265 1266 1267

    return 0;
}


1268
static LRESULT
1269
DATETIME_KillFocus (DATETIME_INFO *infoPtr, HWND lostFocus)
1270
{
1271 1272
    TRACE("lost focus to %p\n", lostFocus);

1273
    if (infoPtr->haveFocus) {
1274
	DATETIME_SendSimpleNotify (infoPtr, NM_KILLFOCUS);
1275
	infoPtr->haveFocus = 0;
1276
        DATETIME_SetSelectedField (infoPtr, -1);
1277
    }
1278

1279
    InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
1280 1281 1282 1283 1284

    return 0;
}


1285
static LRESULT
1286
DATETIME_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs)
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
{
    DWORD dwExStyle = GetWindowLongW(hwnd, GWL_EXSTYLE);
    /* force control to have client edge */
    dwExStyle |= WS_EX_CLIENTEDGE;
    SetWindowLongW(hwnd, GWL_EXSTYLE, dwExStyle);

    return DefWindowProcW(hwnd, WM_NCCREATE, 0, (LPARAM)lpcs);
}


1297
static LRESULT
1298
DATETIME_SetFocus (DATETIME_INFO *infoPtr, HWND lostFocus)
1299
{
1300 1301
    TRACE("got focus from %p\n", lostFocus);

1302
    /* if monthcal is open and it loses focus, close monthcal */
1303
    if (infoPtr->hMonthCal && (lostFocus == infoPtr->hMonthCal) &&
1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314
        IsWindowVisible(infoPtr->hMonthCal))
    {
        ShowWindow(infoPtr->hMonthCal, SW_HIDE);
        DATETIME_SendSimpleNotify(infoPtr, DTN_CLOSEUP);
        /* note: this get triggered even if monthcal loses focus to a dropdown
         * box click, which occurs without an intermediate WM_PAINT call
         */
        infoPtr->bDropdownEnabled = FALSE;
        return 0;
    }

1315 1316
    if (infoPtr->haveFocus == 0) {
	DATETIME_SendSimpleNotify (infoPtr, NM_SETFOCUS);
1317
	infoPtr->haveFocus = DTHT_GOTFOCUS;
1318
    }
1319

1320
    InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1321 1322 1323 1324 1325

    return 0;
}


1326
static BOOL
1327
DATETIME_SendDateTimeChangeNotify (const DATETIME_INFO *infoPtr)
1328
{
1329 1330 1331 1332 1333 1334
    NMDATETIMECHANGE dtdtc;

    dtdtc.nmhdr.hwndFrom = infoPtr->hwndSelf;
    dtdtc.nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
    dtdtc.nmhdr.code     = DTN_DATETIMECHANGE;

1335
    dtdtc.dwFlags = infoPtr->dateValid ? GDT_VALID : GDT_NONE;
1336

1337
    dtdtc.st = infoPtr->date;
1338
    return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
1339
                                dtdtc.nmhdr.idFrom, (LPARAM)&dtdtc);
1340 1341 1342
}


1343
static BOOL
1344
DATETIME_SendSimpleNotify (const DATETIME_INFO *infoPtr, UINT code)
1345 1346 1347
{
    NMHDR nmhdr;

1348 1349 1350
    TRACE("%x\n", code);
    nmhdr.hwndFrom = infoPtr->hwndSelf;
    nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1351 1352
    nmhdr.code     = code;

1353
    return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
1354
                                nmhdr.idFrom, (LPARAM)&nmhdr);
1355
}
1356

1357
static LRESULT
1358
DATETIME_Size (DATETIME_INFO *infoPtr, INT width, INT height)
1359
{
1360 1361 1362 1363
    /* set size */
    infoPtr->rcClient.bottom = height;
    infoPtr->rcClient.right = width;

1364
    TRACE("Height=%d, Width=%d\n", infoPtr->rcClient.bottom, infoPtr->rcClient.right);
1365

1366
    infoPtr->rcDraw = infoPtr->rcClient;
Duane Clark's avatar
Duane Clark committed
1367 1368
    
    if (infoPtr->dwStyle & DTS_UPDOWN) {
1369 1370 1371 1372
        SetWindowPos(infoPtr->hUpdown, NULL,
            infoPtr->rcClient.right-14, 0,
            15, infoPtr->rcClient.bottom - infoPtr->rcClient.top,
            SWP_NOACTIVATE | SWP_NOZORDER);
Duane Clark's avatar
Duane Clark committed
1373 1374 1375 1376 1377 1378 1379 1380 1381
    }
    else {
        /* set the size of the button that drops the calendar down */
        /* FIXME: account for style that allows button on left side */
        infoPtr->calbutton.top   = infoPtr->rcDraw.top;
        infoPtr->calbutton.bottom= infoPtr->rcDraw.bottom;
        infoPtr->calbutton.left  = infoPtr->rcDraw.right-15;
        infoPtr->calbutton.right = infoPtr->rcDraw.right;
    }
1382 1383 1384 1385 1386 1387 1388 1389 1390

    /* set enable/disable button size for show none style being enabled */
    /* FIXME: these dimensions are completely incorrect */
    infoPtr->checkbox.top = infoPtr->rcDraw.top;
    infoPtr->checkbox.bottom = infoPtr->rcDraw.bottom;
    infoPtr->checkbox.left = infoPtr->rcDraw.left;
    infoPtr->checkbox.right = infoPtr->rcDraw.left + 10;

    InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1391

1392 1393
    return 0;
}
1394

1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
static LRESULT
DATETIME_StyleChanging(DATETIME_INFO *infoPtr, WPARAM wStyleType, STYLESTRUCT *lpss)
{
    TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
          wStyleType, lpss->styleOld, lpss->styleNew);

    /* block DTS_SHOWNONE change */
    if ((lpss->styleNew ^ lpss->styleOld) & DTS_SHOWNONE)
    {
        if (lpss->styleOld & DTS_SHOWNONE)
            lpss->styleNew |= DTS_SHOWNONE;
        else
            lpss->styleNew &= ~DTS_SHOWNONE;
    }

    return 0;
}
1412

1413
static LRESULT 
1414
DATETIME_StyleChanged(DATETIME_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss)
1415
{
1416
    TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
1417
          wStyleType, lpss->styleOld, lpss->styleNew);
1418

1419 1420 1421
    if (wStyleType != GWL_STYLE) return 0;
  
    infoPtr->dwStyle = lpss->styleNew;
1422

1423
    if ( !(lpss->styleOld & DTS_SHOWNONE) && (lpss->styleNew & DTS_SHOWNONE) ) {
1424
        infoPtr->hwndCheckbut = CreateWindowExW (0, WC_BUTTONW, 0, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
1425 1426
         					 2, 2, 13, 13, infoPtr->hwndSelf, 0, 
						(HINSTANCE)GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_HINSTANCE), 0);
1427
        SendMessageW (infoPtr->hwndCheckbut, BM_SETCHECK, infoPtr->dateValid ? 1 : 0, 0);
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
    }
    if ( (lpss->styleOld & DTS_SHOWNONE) && !(lpss->styleNew & DTS_SHOWNONE) ) {
        DestroyWindow(infoPtr->hwndCheckbut);
        infoPtr->hwndCheckbut = 0;
    }
    if ( !(lpss->styleOld & DTS_UPDOWN) && (lpss->styleNew & DTS_UPDOWN) ) {
	infoPtr->hUpdown = CreateUpDownControl (WS_CHILD | WS_BORDER | WS_VISIBLE, 120, 1, 20, 20, 
						infoPtr->hwndSelf, 1, 0, 0, UD_MAXVAL, UD_MINVAL, 0);
    }
    if ( (lpss->styleOld & DTS_UPDOWN) && !(lpss->styleNew & DTS_UPDOWN) ) {
	DestroyWindow(infoPtr->hUpdown);
	infoPtr->hUpdown = 0;
    }
1441

1442 1443
    InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
    return 0;
1444
}
1445

1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
static BOOL DATETIME_GetIdealSize(DATETIME_INFO *infoPtr, SIZE *size)
{
    SIZE field_size;
    RECT rect;
    WCHAR txt[80];
    HDC hdc;
    HFONT oldFont;
    int i;

    size->cx = size->cy = 0;

    hdc = GetDC(infoPtr->hwndSelf);
    oldFont = SelectObject(hdc, infoPtr->hFont);

    /* Get text font height */
    DATETIME_ReturnTxt(infoPtr, 0, txt, ARRAY_SIZE(txt));
1462
    GetTextExtentPoint32W(hdc, txt, lstrlenW(txt), &field_size);
1463 1464 1465 1466 1467
    size->cy = field_size.cy;

    /* Get text font width */
    for (i = 0; i < infoPtr->nrFields; i++)
    {
1468
        size->cx += DATETIME_GetFieldWidth(infoPtr, hdc, i);
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495
    }

    SelectObject(hdc, oldFont);
    ReleaseDC(infoPtr->hwndSelf, hdc);

    if (infoPtr->dwStyle & DTS_UPDOWN)
    {
        GetWindowRect(infoPtr->hUpdown, &rect);
        size->cx += rect.right - rect.left;
    }
    else
    {
        size->cx += infoPtr->calbutton.right - infoPtr->calbutton.left;
    }

    if (infoPtr->dwStyle & DTS_SHOWNONE)
    {
        size->cx += infoPtr->checkbox.right - infoPtr->checkbox.left;
    }

    /* Add space between controls for them not to get too close */
    size->cx += 12;
    size->cy += 4;

    TRACE("cx=%d cy=%d\n", size->cx, size->cy);
    return TRUE;
}
1496

1497 1498 1499 1500 1501 1502 1503 1504 1505
static LRESULT
DATETIME_SetFont (DATETIME_INFO *infoPtr, HFONT font, BOOL repaint)
{
    infoPtr->hFont = font;
    if (repaint) InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
    return 0;
}


1506
static LRESULT
1507
DATETIME_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
1508
{
1509
    DATETIME_INFO *infoPtr = Alloc (sizeof(DATETIME_INFO));
1510 1511 1512 1513 1514 1515 1516 1517
    STYLESTRUCT ss = { 0, lpcs->style };

    if (!infoPtr) return -1;

    infoPtr->hwndSelf = hwnd;
    infoPtr->dwStyle = lpcs->style;

    infoPtr->nrFieldsAllocated = 32;
1518 1519 1520
    infoPtr->fieldspec = Alloc (infoPtr->nrFieldsAllocated * sizeof(int));
    infoPtr->fieldRect = Alloc (infoPtr->nrFieldsAllocated * sizeof(RECT));
    infoPtr->buflen = Alloc (infoPtr->nrFieldsAllocated * sizeof(int));
1521
    infoPtr->hwndNotify = lpcs->hwndParent;
1522
    infoPtr->select = -1; /* initially, nothing is selected */
1523
    infoPtr->bDropdownEnabled = TRUE;
1524 1525 1526 1527 1528

    DATETIME_StyleChanged(infoPtr, GWL_STYLE, &ss);
    DATETIME_SetFormatW (infoPtr, 0);

    /* create the monthcal control */
1529
    infoPtr->hMonthCal = CreateWindowExW (0, MONTHCAL_CLASSW, 0, WS_BORDER | WS_POPUP | WS_CLIPSIBLINGS,
1530 1531 1532
					  0, 0, 0, 0, infoPtr->hwndSelf, 0, 0, 0);

    /* initialize info structure */
1533
    GetLocalTime (&infoPtr->date);
1534 1535 1536 1537 1538 1539
    infoPtr->dateValid = TRUE;
    infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);

    SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);

    return 0;
1540 1541 1542
}


1543

1544
static LRESULT
1545
DATETIME_Destroy (DATETIME_INFO *infoPtr)
1546
{
1547 1548 1549 1550
    if (infoPtr->hwndCheckbut)
	DestroyWindow(infoPtr->hwndCheckbut);
    if (infoPtr->hUpdown)
	DestroyWindow(infoPtr->hUpdown);
1551
    if (infoPtr->hMonthCal) 
1552
        DestroyWindow(infoPtr->hMonthCal);
1553
    SetWindowLongPtrW( infoPtr->hwndSelf, 0, 0 ); /* clear infoPtr */
1554 1555 1556
    Free (infoPtr->buflen);
    Free (infoPtr->fieldRect);
    Free (infoPtr->fieldspec);
1557
    Free (infoPtr);
1558 1559 1560 1561
    return 0;
}


1562
static INT
1563
DATETIME_GetText (const DATETIME_INFO *infoPtr, INT count, LPWSTR dst)
1564 1565 1566 1567 1568 1569 1570 1571 1572
{
    WCHAR buf[80];
    int i;

    if (!dst || (count <= 0)) return 0;

    dst[0] = 0;
    for (i = 0; i < infoPtr->nrFields; i++)
    {
1573
        DATETIME_ReturnTxt(infoPtr, i, buf, ARRAY_SIZE(buf));
1574 1575
        if ((lstrlenW(dst) + lstrlenW(buf)) < count)
            lstrcatW(dst, buf);
1576 1577
        else break;
    }
1578
    return lstrlenW(dst);
1579 1580 1581
}


1582
static LRESULT WINAPI
1583
DATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1584
{
1585 1586
    DATETIME_INFO *infoPtr = ((DATETIME_INFO *)GetWindowLongPtrW (hwnd, 0));

1587
    TRACE ("%x, %lx, %lx\n", uMsg, wParam, lParam);
1588

1589
    if (!infoPtr && (uMsg != WM_CREATE) && (uMsg != WM_NCCREATE))
1590
	return DefWindowProcW( hwnd, uMsg, wParam, lParam );
1591

1592
    switch (uMsg) {
1593

1594
    case DTM_GETSYSTEMTIME:
1595
        return DATETIME_GetSystemTime (infoPtr, (SYSTEMTIME *) lParam);
1596 1597

    case DTM_SETSYSTEMTIME:
1598
	return DATETIME_SetSystemTime (infoPtr, wParam, (SYSTEMTIME *) lParam);
1599 1600

    case DTM_GETRANGE:
1601
	return SendMessageW (infoPtr->hMonthCal, MCM_GETRANGE, wParam, lParam);
1602 1603

    case DTM_SETRANGE:
1604
	return SendMessageW (infoPtr->hMonthCal, MCM_SETRANGE, wParam, lParam);
1605

1606
    case DTM_SETFORMATA:
1607
        return DATETIME_SetFormatA (infoPtr, (LPCSTR)lParam);
1608

1609
    case DTM_SETFORMATW:
1610
        return DATETIME_SetFormatW (infoPtr, (LPCWSTR)lParam);
1611

1612 1613 1614
    case DTM_GETMONTHCAL:
	return (LRESULT)infoPtr->hMonthCal;

1615
    case DTM_SETMCCOLOR:
1616
	return SendMessageW (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
1617 1618

    case DTM_GETMCCOLOR:
1619
        return SendMessageW (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
1620 1621

    case DTM_SETMCFONT:
1622
	return SendMessageW (infoPtr->hMonthCal, WM_SETFONT, wParam, lParam);
1623 1624

    case DTM_GETMCFONT:
1625
	return SendMessageW (infoPtr->hMonthCal, WM_GETFONT, wParam, lParam);
1626

1627 1628 1629
    case DTM_GETIDEALSIZE:
        return DATETIME_GetIdealSize(infoPtr, (SIZE *)lParam);

1630
    case WM_NOTIFY:
1631
	return DATETIME_Notify (infoPtr, (LPNMHDR)lParam);
1632

1633 1634 1635 1636 1637 1638
    case WM_ENABLE:
        return DATETIME_Enable (infoPtr, (BOOL)wParam);

    case WM_ERASEBKGND:
        return DATETIME_EraseBackground (infoPtr, (HDC)wParam);

1639 1640
    case WM_GETDLGCODE:
        return DLGC_WANTARROWS | DLGC_WANTCHARS;
1641

1642
    case WM_PRINTCLIENT:
1643
    case WM_PAINT:
1644
        return DATETIME_Paint (infoPtr, (HDC)wParam);
1645

1646
    case WM_KEYDOWN:
1647
        return DATETIME_KeyDown (infoPtr, wParam);
1648

1649
    case WM_CHAR:
1650
        return DATETIME_Char (infoPtr, wParam);
1651

1652
    case WM_KILLFOCUS:
1653
        return DATETIME_KillFocus (infoPtr, (HWND)wParam);
1654

1655 1656 1657
    case WM_NCCREATE:
        return DATETIME_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);

1658
    case WM_SETFOCUS:
1659
        return DATETIME_SetFocus (infoPtr, (HWND)wParam);
1660

1661
    case WM_SIZE:
1662
        return DATETIME_Size (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
1663

1664
    case WM_LBUTTONDOWN:
1665
        return DATETIME_LButtonDown (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
1666

1667
    case WM_LBUTTONUP:
1668
        return DATETIME_LButtonUp (infoPtr);
1669

Duane Clark's avatar
Duane Clark committed
1670 1671 1672
    case WM_VSCROLL:
        return DATETIME_VScroll (infoPtr, (WORD)wParam);

1673
    case WM_CREATE:
1674
	return DATETIME_Create (hwnd, (LPCREATESTRUCTW)lParam);
1675

1676
    case WM_DESTROY:
1677
	return DATETIME_Destroy (infoPtr);
1678

Huw Davies's avatar
Huw Davies committed
1679
    case WM_COMMAND:
1680
        return DATETIME_Command (infoPtr, wParam, lParam);
Huw Davies's avatar
Huw Davies committed
1681

1682 1683 1684
    case WM_STYLECHANGING:
        return DATETIME_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);

1685 1686 1687
    case WM_STYLECHANGED:
        return DATETIME_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);

1688 1689 1690 1691 1692 1693
    case WM_SETFONT:
        return DATETIME_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);

    case WM_GETFONT:
        return (LRESULT) infoPtr->hFont;

1694 1695 1696
    case WM_GETTEXT:
        return (LRESULT) DATETIME_GetText(infoPtr, wParam, (LPWSTR)lParam);

1697 1698 1699
    case WM_SETTEXT:
        return CB_ERR;

1700
    default:
1701
	if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
1702
		ERR("unknown msg %04x wp=%08lx lp=%08lx\n",
1703
		     uMsg, wParam, lParam);
1704
	return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1705 1706 1707 1708
    }
}


1709
void
1710
DATETIME_Register (void)
1711
{
1712
    WNDCLASSW wndClass;
1713

1714
    ZeroMemory (&wndClass, sizeof(WNDCLASSW));
1715
    wndClass.style         = CS_GLOBALCLASS;
1716
    wndClass.lpfnWndProc   = DATETIME_WindowProc;
1717 1718
    wndClass.cbClsExtra    = 0;
    wndClass.cbWndExtra    = sizeof(DATETIME_INFO *);
1719
    wndClass.hCursor       = LoadCursorW (0, (LPCWSTR)IDC_ARROW);
1720
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1721
    wndClass.lpszClassName = DATETIMEPICK_CLASSW;
1722

1723
    RegisterClassW (&wndClass);
1724 1725 1726
}


1727
void
1728
DATETIME_Unregister (void)
1729
{
1730
    UnregisterClassW (DATETIMEPICK_CLASSW, NULL);
1731
}