First try at the month calendar common control. Main features
[wine] / dlls / comctl32 / monthcal.c
1 /*
2  * Month calendar control
3  *
4  * Copyright 1998, 1999 Eric Kohl (ekohl@abo.rhein-zeitung.de)
5  * Copyright 1999  Alex Priem (alexp@sci.kun.nl)
6  *
7  * TODO:
8  *   - Notifications.
9  *
10  *
11  *  FIXME: refresh should ask for rect of required length. (?)
12  *  FIXME: when pressing next/prev button, button should disappear
13  *         until mouse is released. Should also set timer.
14  *  FIXME: we refresh to often; especially in LButtonDown/MouseMove.
15  *  FIXME: handle resources better (doesn't work now); also take care
16            of internationalization. 
17  */
18
19 #include "winbase.h"
20 #include "winuser.h"
21 #include "wingdi.h"
22 #include "win.h"
23 #include "winnls.h"
24 #include "commctrl.h"
25 #include "comctl32.h"
26 #include "monthcal.h"
27 #include "debugtools.h"
28
29 DEFAULT_DEBUG_CHANNEL(monthcal)
30
31 /* take #days/month from ole/parsedt.c;
32  * we want full month-names, and abbreviated weekdays, so these are
33  * defined here */
34
35 extern int mdays[];    
36 char *monthtxt[] = {"January", "February", "March", "April", "May", 
37                       "June", "July", "August", "September", "October", 
38                       "November", "December"};
39
40 char *daytxt[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
41 int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
42
43
44
45 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongA (hwnd, 0))
46
47 /* helper functions   
48  * MONTHCAL_ValidateTime: is time a valid date/time combo?
49  */
50
51
52 /* CHECKME: all these validations OK? */
53    
54 static int MONTHCAL_ValidateTime (SYSTEMTIME time) 
55
56 {
57  if (time.wMonth > 12) return FALSE;
58  if (time.wDayOfWeek > 6) return FALSE;
59  if (time.wDay > mdays[time.wMonth]) return FALSE;
60  if (time.wMonth > 23) return FALSE;
61  if (time.wMinute > 60) return FALSE;
62  if (time.wSecond > 60) return FALSE;
63  if (time.wMilliseconds > 100) return FALSE;
64  return TRUE;
65 }
66
67 static void MONTHCAL_CopyTime (const SYSTEMTIME *from, SYSTEMTIME *to) 
68
69 {
70  to->wYear=from->wYear;
71  to->wMonth=from->wMonth;
72  to->wDayOfWeek=from->wDayOfWeek;
73  to->wDay=from->wDay;
74  to->wHour=from->wHour;
75  to->wMinute=from->wMinute;
76  to->wSecond=from->wSecond;
77  to->wMilliseconds=from->wMilliseconds;
78 }
79
80
81 /* Note:Depending on DST, this may be offset by a day. 
82    Need to find out if we're on a DST place & adjust the clock accordingly.
83    Above function assumes we have a valid data.
84    Valid for year>1752;  d <= 1 <= 31, 1 <= m <= 12.
85    0=Monday.
86 */
87
88
89 static int MONTHCAL_CalculateDayOfWeek (DWORD day, DWORD month, DWORD year)
90
91 {
92  year -= month < 3;
93  return (year + year/4 - year/100 + year/400 + 
94          DayOfWeekTable[month-1] + day - 1 ) % 7;
95 }
96
97
98 static int MONTHCAL_CalcDayFromPos (MONTHCAL_INFO *infoPtr, int x, int y) 
99
100 {
101     int daypos,weekpos,retval,firstDay;
102
103     daypos=(x - infoPtr->prevmonth.left) / infoPtr->textWidth ;
104     weekpos=(y - infoPtr->days.bottom - infoPtr->rcClient.top) / 
105                      (infoPtr->textHeight*1.25);
106         firstDay=MONTHCAL_CalculateDayOfWeek (1,infoPtr->currentMonth,infoPtr->currentYear);
107     retval=daypos + 7*weekpos - firstDay;
108         TRACE ("%d %d %d\n",daypos,weekpos,retval);
109         return retval;
110 }
111
112
113 static void MONTHCAL_CalcDayXY (MONTHCAL_INFO *infoPtr, int day, int month, 
114                                  int *x, int *y)
115
116 {
117   int firstDay,prevMonth;
118
119   firstDay=MONTHCAL_CalculateDayOfWeek (1,infoPtr->currentMonth,infoPtr->currentYear);
120         
121   if (month==infoPtr->currentMonth) {
122         *x=(day+firstDay) & 7;
123         *y=(day+firstDay-*x) / 7;
124                 return;
125         }
126   if (month < infoPtr->currentMonth) {
127                 prevMonth=month - 1;
128                 if (prevMonth==0) prevMonth=11;
129         *x=(mdays[prevMonth]-firstDay) & 7;
130                 *y=0;
131         }
132
133   *y=mdays[month] / 7;
134   *x=(day+firstDay+mdays[month]) & 7;
135 }
136
137
138 static void MONTHCAL_CalcDayRect (MONTHCAL_INFO *infoPtr, RECT *r, int x, int y) 
139 {
140   r->left   = infoPtr->prevmonth.left + x * infoPtr->textWidth;
141   r->right  = r->left + infoPtr->textWidth;
142   r->top    = infoPtr->rcClient.top + y * 1.25 * infoPtr->textHeight 
143                                    + infoPtr->days.bottom;
144   r->bottom = r->top + infoPtr->textHeight;
145
146 }
147
148 static inline void MONTHCAL_CalcPosFromDay (MONTHCAL_INFO *infoPtr, 
149                                             int day, int month, RECT *r)
150
151 {
152   int x,y;
153
154   MONTHCAL_CalcDayXY (infoPtr, day, month, &x, &y);
155   MONTHCAL_CalcDayRect (infoPtr, r, x, y);
156 }
157
158
159
160
161 static void MONTHCAL_CircleDay (HDC hdc, MONTHCAL_INFO *infoPtr, int i, int j)
162
163 {
164         HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB (255,0,0) );
165     HPEN hOldPen2 = SelectObject( hdc, hRedPen );
166         POINT points[7];
167         int x,y;
168
169  /* use prevmonth to calculate position because it contains the extra width 
170   * from MCS_WEEKNUMBERS
171   */
172
173         x=infoPtr->prevmonth.left + (i+0.5)*infoPtr->textWidth;
174         y=infoPtr->rcClient.top + 1.25*(j+0.5)*infoPtr->textHeight + infoPtr->days.bottom;
175         points[0].x = x;
176         points[0].y = y-0.25*infoPtr->textHeight;
177         points[1].x = x-1.0*infoPtr->textWidth;
178         points[1].y = y;
179         points[2].x = x;
180         points[2].y = y+0.6*infoPtr->textHeight;
181         points[3].x = x+0.5*infoPtr->textWidth;
182         points[3].y = y;
183         points[4].x = x+0.3*infoPtr->textWidth;
184         points[4].y = y-0.5*infoPtr->textHeight;
185         points[5].x = x-0.25*infoPtr->textWidth;
186         points[5].y = y-0.5*infoPtr->textHeight;
187         points[6].x = x-0.5*infoPtr->textWidth;
188         points[6].y = y-0.45*infoPtr->textHeight;
189
190         PolyBezier (hdc,points,4);
191         PolyBezier (hdc,points+3,4);
192     DeleteObject (hRedPen);
193         SelectObject (hdc, hOldPen2);
194 }
195
196
197
198
199
200 static void MONTHCAL_DrawDay (HDC hdc, MONTHCAL_INFO *infoPtr, 
201                                                         int day, int month, int x, int y, int bold)
202
203 {
204   char buf[10];
205   RECT r;
206   static int haveBoldFont,haveSelectedDay=FALSE;
207   HBRUSH hbr; 
208   COLORREF oldCol,oldBk;
209
210   sprintf (buf,"%d",day);
211
212
213
214 /* No need to check styles: when selection is not valid, it is set to zero. 
215  * 1<day<31, so evertyhing's OK.
216  */
217
218   MONTHCAL_CalcDayRect (infoPtr, &r, x, y);
219   
220   if ((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
221       && (month==infoPtr->currentMonth)) {
222                 HRGN hrgn;
223                 RECT r2;
224                 
225                 TRACE ("%d %d %d\n",day,infoPtr->minSel.wDay,infoPtr->maxSel.wDay);
226                 TRACE ("%d %d %d %d\n", r.left, r.top, r.right, r.bottom);
227                 oldCol=SetTextColor (hdc, infoPtr->monthbk);
228                 oldBk=SetBkColor (hdc,infoPtr->trailingtxt);
229                 hbr= GetSysColorBrush (COLOR_GRAYTEXT);
230                 hrgn=CreateEllipticRgn (r.left,r.top, r.right,r.bottom);
231                 FillRgn (hdc,hrgn,hbr);
232
233                 r2.left   = r.left-0.25*infoPtr->textWidth;
234                 r2.top    = r.top;
235                 r2.right  = r.left+0.5*infoPtr->textWidth;
236                 r2.bottom = r.bottom;
237                 if (haveSelectedDay) FillRect (hdc,&r2,hbr);
238                 haveSelectedDay=TRUE;
239         } else {
240                 haveSelectedDay=FALSE;
241         }
242         
243
244
245 /* need to add some code for multiple selections */
246
247   if ((bold) && (!haveBoldFont)) {
248         SelectObject (hdc, infoPtr->hBoldFont);
249                 haveBoldFont=TRUE;
250         }
251   if ((!bold) && (haveBoldFont)) {
252         SelectObject (hdc, infoPtr->hFont);
253                 haveBoldFont=FALSE;
254         }
255
256         
257         
258   DrawTextA ( hdc, buf, lstrlenA(buf), &r, 
259                          DT_CENTER | DT_VCENTER | DT_SINGLELINE );
260   if (haveSelectedDay) {
261                 SetTextColor(hdc, oldCol);
262                 SetBkColor (hdc, oldBk);
263         }
264
265   if ((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth)) {
266                 HPEN hNewPen, hOldPen;
267
268                 hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
269                 hbr= GetSysColorBrush (COLOR_WINDOWTEXT);
270                 hOldPen = SelectObject( hdc, hNewPen );
271                 r.left+=2;
272                 r.right-=2;
273                 r.top-=1;
274                 r.bottom+=1;
275                 FrameRect (hdc, &r, hbr);
276                 SelectObject( hdc, hOldPen );
277   }
278 }
279
280
281 /* CHECKME: For `todays date', do we need to check the locale?*/
282 /* CHECKME: For `todays date', how do is Y2K handled?*/
283 /* FIXME:  todays date circle */
284
285 static void MONTHCAL_Refresh (HWND hwnd, HDC hdc) 
286
287 {
288     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
289         RECT *rcClient=&infoPtr->rcClient;
290         RECT *title=&infoPtr->title;
291         RECT *prev=&infoPtr->titlebtnprev;
292         RECT *next=&infoPtr->titlebtnnext;
293         RECT *titlemonth=&infoPtr->titlemonth;
294         RECT *titleyear=&infoPtr->titleyear;
295         RECT *prevmonth=&infoPtr->prevmonth;
296         RECT *nextmonth=&infoPtr->nextmonth;
297         RECT *days=&infoPtr->days;
298         RECT *weeknums=&infoPtr->weeknums;
299         RECT *rtoday=&infoPtr->today;
300     int i,j,m,mask,day,firstDay, weeknum,prevMonth;
301     int textHeight,textWidth;
302         SIZE size;
303         HBRUSH hbr;
304     HFONT currentFont;
305         TEXTMETRICA tm;
306 //    LOGFONTA logFont;
307         char buf[20],*thisMonthtxt;
308     COLORREF oldTextColor,oldBkColor;
309     DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
310         BOOL prssed;
311         
312
313     oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
314
315     currentFont = SelectObject (hdc, infoPtr->hFont);
316
317         /* FIXME: need a way to determine current font, without setting it */
318 /*
319         if (infoPtr->hFont!=currentFont) {
320         SelectObject (hdc, currentFont);
321                 infoPtr->hFont=currentFont;
322                 GetObjectA (currentFont, sizeof (LOGFONTA), &logFont);
323         logFont.lfWeight=FW_BOLD;
324         infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
325         }
326 */
327
328         GetTextMetricsA (hdc, &tm);
329         infoPtr->textHeight=textHeight=tm.tmHeight + tm.tmExternalLeading;
330     GetTextExtentPoint32A (hdc, "Sun",3, &size);
331         infoPtr->textWidth=textWidth=size.cx+2;
332
333     GetClientRect (hwnd, rcClient);
334     hbr =  CreateSolidBrush (RGB(255,255,255));
335     DrawEdge (hdc, rcClient, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
336     FillRect (hdc, rcClient, hbr);
337     DeleteObject (hbr);
338
339 /* calculate whole client area & title area */
340
341         infoPtr->rcClient.right=7*infoPtr->textWidth;
342         if (dwStyle & MCS_WEEKNUMBERS)
343           infoPtr->rcClient.right+=infoPtr->textWidth;
344         
345         title->top    = rcClient->top + 1;
346         title->bottom = title->top + 2*textHeight + 4;
347         title->left   = rcClient->left + 1;
348         title->right  = rcClient->right - 1;
349         infoPtr->rcClient.bottom=title->bottom + 6*textHeight;
350
351
352 /* draw header */
353
354     hbr =  CreateSolidBrush (infoPtr->titlebk);
355     FillRect (hdc, title, hbr);
356         
357         prev->top                 = next->top        = title->top + 6;
358         prev->bottom      = next->bottom     = title->top + 2*textHeight - 3;
359         prev->right       = title->left  + 28;
360         prev->left        = title->left  + 4;
361         next->left        = title->right - 28;
362         next->right       = title->right - 4;
363         titlemonth->bottom= titleyear->bottom = prev->top + 2*textHeight - 3;
364         titlemonth->top   = titleyear->top    = title->top + 6;
365         titlemonth->left  = title->left;
366         titlemonth->right = title->right;
367         prssed=FALSE;
368
369     DrawFrameControl(hdc, prev, DFC_SCROLL,
370         DFCS_SCROLLLEFT | (prssed ? DFCS_PUSHED : 0) |
371         (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
372
373
374     DrawFrameControl(hdc, next, DFC_SCROLL,
375         DFCS_SCROLLRIGHT | (prssed ? DFCS_PUSHED : 0) |
376         (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
377
378     oldBkColor=SetBkColor (hdc,infoPtr->titlebk);
379     SetTextColor(hdc, infoPtr->titletxt);
380         SelectObject (hdc, infoPtr->hBoldFont);
381
382         thisMonthtxt=monthtxt[infoPtr->currentMonth - 1];
383         sprintf (buf,"%s %ld",thisMonthtxt,infoPtr->currentYear);
384     DrawTextA ( hdc, buf, strlen(buf), titlemonth, 
385                         DT_CENTER | DT_VCENTER | DT_SINGLELINE );
386         SelectObject (hdc, infoPtr->hFont);
387
388  /* titlemonth left/right contained rect for whole titletxt ('June  1999')
389   * MCM_HitTestInfo wants month & year rects, so prepare these now.
390   * (no, we can't draw them separately; the whole text is centered) 
391   */
392   
393     GetTextExtentPoint32A (hdc, buf,lstrlenA (buf), &size);
394         titlemonth->left = title->right/2 - size.cx/2;
395         titleyear->right = title->right/2 + size.cx/2;
396     GetTextExtentPoint32A (hdc, thisMonthtxt,lstrlenA (thisMonthtxt), &size);
397     titlemonth->right= titlemonth->left+size.cx;
398     titleyear->right = titlemonth->right;
399     
400 /* draw line under day abbreviatons */
401
402          MoveToEx (hdc, rcClient->left+3, title->bottom + textHeight + 2, NULL);
403      LineTo   (hdc, rcClient->right-3, title->bottom + textHeight + 2);
404
405 /* draw day abbreviations */
406
407     SetBkColor (hdc, infoPtr->monthbk);
408     SetTextColor(hdc, infoPtr->trailingtxt);
409
410         days->left   = rcClient->left;
411         if (dwStyle & MCS_WEEKNUMBERS) days->left+=textWidth;
412     days->right  = days->left + textWidth;
413     days->top    = title->bottom + 2;
414     days->bottom = title->bottom + textHeight + 2;
415         i=infoPtr->firstDay;
416
417         for (j=0; j<7; j++) {
418             DrawTextA ( hdc, daytxt[i], strlen(daytxt[i]), days,
419                          DT_CENTER | DT_VCENTER | DT_SINGLELINE );
420                         i++;
421                         if (i>7) i-=7;
422                         days->left+=textWidth;
423                         days->right+=textWidth;
424         }
425
426         days->left   = rcClient->left + j;
427         if (dwStyle & MCS_WEEKNUMBERS) days->left+=textWidth;
428     days->right  = rcClient->left + (j+1)*textWidth-2;
429
430 /* draw day numbers; first, the previous month */
431
432         prevmonth->left=0;
433         if (dwStyle & MCS_WEEKNUMBERS) prevmonth->left=textWidth;
434
435         firstDay=MONTHCAL_CalculateDayOfWeek (1,infoPtr->currentMonth,infoPtr->currentYear);
436         prevMonth=infoPtr->currentMonth-1;
437         if (prevMonth==0) prevMonth=11;
438         day=mdays[prevMonth]-firstDay;
439         mask=1<<(day-1);
440
441         i=0;
442         m=0;
443         while (day<=mdays[prevMonth]) {
444                         MONTHCAL_DrawDay (hdc, infoPtr, day, prevMonth, i, 0, 
445                                                 infoPtr->monthdayState[m] & mask);
446                         mask<<=1;
447                         day++;
448                         i++;
449         }
450
451         prevmonth->right = prevmonth->left+i*textWidth;
452         prevmonth->top   = days->bottom;
453         prevmonth->bottom= prevmonth->top + textHeight;
454
455 /* draw `current' month  */
456
457         day=1;
458         infoPtr->firstDayplace=i;
459     SetTextColor(hdc, infoPtr->txt);
460         m++;
461         mask=1;
462         while (i<7) {
463                         MONTHCAL_DrawDay (hdc, infoPtr, day, infoPtr->currentMonth, i, 0, 
464                                                 infoPtr->monthdayState[m] & mask);
465                         if ((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
466                 (day==infoPtr->todaysDate.wDay)) 
467                                         MONTHCAL_CircleDay (hdc, infoPtr, i,j);
468                         mask<<=1;
469                         day++;
470                         i++;
471                 }
472
473         j=1;
474         i=0;
475         while (day<=mdays[infoPtr->currentMonth]) {     
476                         MONTHCAL_DrawDay (hdc, infoPtr, day, infoPtr->currentMonth, i, j,
477                                                 infoPtr->monthdayState[m] & mask);
478                         if ((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
479                 (day==infoPtr->todaysDate.wDay)) 
480                                         MONTHCAL_CircleDay (hdc, infoPtr, i,j);
481                         mask<<=1;
482                         day++;
483                         i++;
484                         if (i>6) {
485                                 i=0;
486                                 j++;
487                         }
488         }
489
490 /*  draw `next' month */
491
492 /* note: the nextmonth rect only hints for the `half-week' that needs to be
493  * drawn to complete the current week. An eventual next week that needs to
494  * be drawn to complete the month calendar is not taken into account in
495  * this rect -- HitTest knows about this.*/
496   
497   
498
499         nextmonth->left   = prevmonth->left+i*textWidth;
500         nextmonth->right  = rcClient->right;
501         nextmonth->top    = days->bottom+(j+1)*textHeight;
502         nextmonth->bottom = nextmonth->top + textHeight;
503
504         day=1;
505         m++;
506         mask=1;
507     SetTextColor(hdc, infoPtr->trailingtxt);
508         while ((i<7) && (j<6)) {
509                         MONTHCAL_DrawDay (hdc, infoPtr, day, infoPtr->currentMonth+1, i, j,
510                                                 infoPtr->monthdayState[m] & mask);
511                         mask<<=1;
512                         day++;
513                         i++;    
514                         if (i==7) {
515                                 i=0;
516                                 j++;
517                         }
518         }
519     SetTextColor(hdc, infoPtr->txt);
520                          
521
522
523 /* draw `today' date if style allows it, and draw a circle before today's
524  * date if necessairy */
525
526         if (!( dwStyle & MCS_NOTODAY))  {
527         int offset=0;
528         if (!( dwStyle & MCS_NOTODAYCIRCLE))  { 
529                         MONTHCAL_CircleDay (hdc, infoPtr, 0, 6);
530                         offset+=textWidth;
531                 }
532
533                 MONTHCAL_CalcDayRect (infoPtr, rtoday, offset==textWidth, 6);
534                 sprintf (buf,"Today: %d/%d/%d",infoPtr->todaysDate.wMonth,
535                         infoPtr->todaysDate.wDay, infoPtr->todaysDate.wYear-1900);
536                 rtoday->right  = rcClient->right;
537         SelectObject (hdc, infoPtr->hBoldFont);
538             DrawTextA ( hdc, buf, lstrlenA(buf), rtoday, 
539                          DT_LEFT | DT_VCENTER | DT_SINGLELINE );
540         SelectObject (hdc, infoPtr->hFont);
541         }
542
543         if (dwStyle & MCS_WEEKNUMBERS)  {
544                         /* display weeknumbers*/
545
546                 weeknums->left   = 0;
547                 weeknums->right  = textWidth;
548                 weeknums->top    = days->bottom;
549                 weeknums->bottom = days->bottom + textHeight;
550                 
551                 weeknum=0;
552                 for (i=0; i<infoPtr->currentMonth; i++) 
553                         weeknum+=mdays[i];
554
555                 weeknum/=7;
556                 for (i=0; i<6; i++) {
557                         sprintf (buf,"%d",weeknum);
558                 DrawTextA ( hdc, buf, lstrlenA(buf), weeknums, 
559                          DT_CENTER | DT_BOTTOM | DT_SINGLELINE );
560                         weeknums->bottom+=textHeight;
561                 }
562                         
563             MoveToEx (hdc, weeknums->right+3, weeknums->top, NULL);
564         LineTo   (hdc, weeknums->right-3, weeknums->bottom);
565                 
566         }
567
568                                  /* currentFont was font at entering Refresh */
569
570     SetBkColor (hdc, oldBkColor);
571     SelectObject (hdc, currentFont);     
572     SetTextColor (hdc, oldTextColor);
573 }
574
575
576 static LRESULT 
577 MONTHCAL_GetMinReqRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
578
579 {
580     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
581         LPRECT lpRect=(LPRECT) lParam;
582         TRACE ("%x %lx\n",wParam,lParam);
583         
584   /* validate parameters */
585
586     if ( (infoPtr==NULL) || (lpRect == NULL) ) return FALSE;
587
588         lpRect->left=infoPtr->rcClient.left;
589         lpRect->right=infoPtr->rcClient.right;
590         lpRect->top=infoPtr->rcClient.top;
591         lpRect->bottom=infoPtr->rcClient.bottom;
592         return TRUE;
593 }
594
595 static LRESULT 
596 MONTHCAL_GetColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
597
598 {
599     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
600
601         TRACE ("%x %lx\n",wParam,lParam);
602
603         switch ((int)wParam) {
604                 case MCSC_BACKGROUND:
605                         return infoPtr->bk;
606                 case MCSC_TEXT:
607                         return infoPtr->txt;
608                 case MCSC_TITLEBK:
609                         return infoPtr->titlebk;
610                 case MCSC_TITLETEXT:
611                         return infoPtr->titletxt;
612                 case MCSC_MONTHBK:
613                         return infoPtr->monthbk;
614                 case MCSC_TRAILINGTEXT:
615                         return infoPtr->trailingtxt;
616         }
617
618  return -1;
619 }
620
621 static LRESULT 
622 MONTHCAL_SetColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
623
624 {
625     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
626         int prev=-1;
627
628         TRACE ("%x %lx\n",wParam,lParam);
629
630         switch ((int)wParam) {
631                 case MCSC_BACKGROUND:
632                         prev=infoPtr->bk;
633                         infoPtr->bk=(COLORREF) lParam;
634                         break;
635                 case MCSC_TEXT:
636                         prev=infoPtr->txt;
637                         infoPtr->txt=(COLORREF) lParam;
638                         break;
639                 case MCSC_TITLEBK:
640                         prev=infoPtr->titlebk;
641                         infoPtr->titlebk=(COLORREF) lParam;
642                         break;
643                 case MCSC_TITLETEXT:
644                         prev=infoPtr->titletxt;
645                         infoPtr->titletxt=(COLORREF) lParam;
646                         break;
647                 case MCSC_MONTHBK:
648                         prev=infoPtr->monthbk;
649                         infoPtr->monthbk=(COLORREF) lParam;
650                         break;
651                 case MCSC_TRAILINGTEXT:
652                         prev=infoPtr->trailingtxt;
653                         infoPtr->trailingtxt=(COLORREF) lParam;
654                         break;
655         }
656
657  return prev;
658 }
659
660 static LRESULT 
661 MONTHCAL_GetMonthDelta (HWND hwnd, WPARAM wParam, LPARAM lParam)
662
663 {
664     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
665
666         TRACE ("%x %lx\n",wParam,lParam);
667
668         if (infoPtr->delta) return infoPtr->delta;
669         else return infoPtr->visible;
670 }
671
672 static LRESULT 
673 MONTHCAL_SetMonthDelta (HWND hwnd, WPARAM wParam, LPARAM lParam)
674
675 {
676     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
677     int prev=infoPtr->delta;
678
679         TRACE ("%x %lx\n",wParam,lParam);
680         
681         infoPtr->delta=(int) wParam;
682         return prev;
683 }
684
685
686
687 static LRESULT 
688 MONTHCAL_GetFirstDayOfWeek (HWND hwnd, WPARAM wParam, LPARAM lParam)
689 {
690     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
691         
692         return infoPtr->firstDay;
693 }
694
695 /* FIXME: we need more error checking here */
696
697 static LRESULT 
698 MONTHCAL_SetFirstDayOfWeek (HWND hwnd, WPARAM wParam, LPARAM lParam)
699
700 {
701     MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
702     int prev=infoPtr->firstDay;
703         char buf[40];
704         int day;
705
706         TRACE ("%x %lx\n",wParam,lParam);
707
708         if ((lParam>=0) && (lParam<7)) {
709                         infoPtr->firstDay=(int) lParam;
710                         GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK,
711                 buf, sizeof(buf));
712                 TRACE ("%s %d\n",buf,strlen(buf));
713             if ((sscanf(buf,"%d",&day)==1) && (infoPtr->firstDay!=day)) 
714                                 infoPtr->firstDay|=HIWORD(TRUE);
715                                 
716         }
717     return prev;
718 }
719
720
721
722 /* FIXME: fill this in */
723
724 static LRESULT
725 MONTHCAL_GetMonthRange (HWND hwnd, WPARAM wParam, LPARAM lParam) 
726 {
727   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
728
729   TRACE ("%x %lx\n",wParam,lParam);
730  
731   return infoPtr->monthRange;
732 }
733
734 static LRESULT
735 MONTHCAL_GetMaxTodayWidth (HWND hwnd)
736
737 {
738  MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
739
740  return (infoPtr->today.right-infoPtr->today.left);
741 }
742
743 /* FIXME: are validated times taken from current date/time or simply
744  * copied? 
745  * FIXME:    check whether MCM_GETMONTHRANGE shows correct result after
746  *            adjusting range with MCM_SETRANGE
747  */
748
749 static LRESULT
750 MONTHCAL_SetRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
751 {
752   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
753   SYSTEMTIME lprgSysTimeArray[1];
754   int prev;
755
756   TRACE ("%x %lx\n",wParam,lParam);
757   
758   if (wParam & GDTR_MAX) {
759                 if (MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
760                         MONTHCAL_CopyTime (&lprgSysTimeArray[1],&infoPtr->maxDate);
761                         infoPtr->rangeValid|=GDTR_MAX;
762                 } else  {
763                         GetSystemTime (&infoPtr->todaysDate);
764                         MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->maxDate);
765                         }
766                 }
767   if (wParam & GDTR_MIN) {
768                 if (MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
769                         MONTHCAL_CopyTime (&lprgSysTimeArray[0],&infoPtr->maxDate);
770                         infoPtr->rangeValid|=GDTR_MIN;
771                 } else {
772                         GetSystemTime (&infoPtr->todaysDate);
773                         MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->maxDate);
774                         }
775             }
776
777   prev=infoPtr->monthRange;
778   infoPtr->monthRange=infoPtr->maxDate.wMonth-infoPtr->minDate.wMonth;
779   if (infoPtr->monthRange!=prev) 
780         COMCTL32_ReAlloc (infoPtr->monthdayState, 
781                 infoPtr->monthRange*sizeof(MONTHDAYSTATE));
782
783   return 1;
784 }
785
786
787 /* CHECKME: At the moment, we copy ranges anyway,regardless of
788  * infoPtr->rangeValid; a invalid range is simply filled with zeros in 
789  * SetRange.  Is this the right behavior?
790 */
791
792 static LRESULT
793 MONTHCAL_GetRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
794 {
795   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
796   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
797
798   /* validate parameters */
799
800   if ( (infoPtr==NULL) || (lprgSysTimeArray==NULL) ) return FALSE;
801
802   MONTHCAL_CopyTime (&infoPtr->maxDate,&lprgSysTimeArray[1]);
803   MONTHCAL_CopyTime (&infoPtr->minDate,&lprgSysTimeArray[0]);
804
805   return infoPtr->rangeValid;
806 }
807
808 static LRESULT
809 MONTHCAL_SetDayState (HWND hwnd, WPARAM wParam, LPARAM lParam)
810
811 {
812   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
813   int i,iMonths=(int) wParam;
814   MONTHDAYSTATE *dayStates=(LPMONTHDAYSTATE) lParam;
815
816   TRACE ("%x %lx\n",wParam,lParam);
817   if (iMonths!=infoPtr->monthRange) return 0;
818
819   for (i=0; i<iMonths; i++) 
820                 infoPtr->monthdayState[i]=dayStates[i];
821   return 1;
822 }
823
824 static LRESULT 
825 MONTHCAL_GetCurSel (HWND hwnd, WPARAM wParam, LPARAM lParam)
826 {
827   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
828   SYSTEMTIME *lpSel=(SYSTEMTIME *) lParam;
829
830   TRACE ("%x %lx\n",wParam,lParam);
831   if ( (infoPtr==NULL) || (lpSel==NULL) ) return FALSE;
832   if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
833
834   MONTHCAL_CopyTime (&infoPtr->minSel,lpSel);
835   return TRUE;
836 }
837
838
839 /* FIXME: if the specified date is not visible, make it visible */
840 /* FIXME: redraw? */
841
842 static LRESULT 
843 MONTHCAL_SetCurSel (HWND hwnd, WPARAM wParam, LPARAM lParam)
844 {
845   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
846   SYSTEMTIME *lpSel=(SYSTEMTIME *) lParam;
847
848   TRACE ("%x %lx\n",wParam,lParam);
849   if ( (infoPtr==NULL) || (lpSel==NULL) ) return FALSE;
850   if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
851
852   TRACE ("%d %d\n",lpSel->wMonth,lpSel->wDay);
853
854   MONTHCAL_CopyTime (lpSel,&infoPtr->minSel);
855   MONTHCAL_CopyTime (lpSel,&infoPtr->maxSel);
856
857   return TRUE;
858 }
859
860 static LRESULT 
861 MONTHCAL_GetMaxSelCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
862 {
863   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
864
865   TRACE ("%x %lx\n",wParam,lParam);
866   return infoPtr->maxSelCount;
867 }
868
869 static LRESULT 
870 MONTHCAL_SetMaxSelCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
871 {
872   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
873
874   TRACE ("%x %lx\n",wParam,lParam);
875   if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)  {
876                 infoPtr->maxSelCount=wParam;
877   }
878
879   return TRUE;
880 }
881
882
883 static LRESULT 
884 MONTHCAL_GetSelRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
885 {
886   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
887   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
888
889   TRACE ("%x %lx\n",wParam,lParam);
890
891   /* validate parameters */
892
893   if ( (infoPtr==NULL) || (lprgSysTimeArray==NULL) ) return FALSE;
894
895   if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)  {
896                 MONTHCAL_CopyTime (&infoPtr->maxSel,&lprgSysTimeArray[1]);
897         MONTHCAL_CopyTime (&infoPtr->minSel,&lprgSysTimeArray[0]);
898                 TRACE ("[min,max]=[%d %d]\n",infoPtr->minSel.wDay,infoPtr->maxSel.wDay);
899         return TRUE;
900   }
901  
902   return FALSE;
903 }
904
905 static LRESULT 
906 MONTHCAL_SetSelRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
907 {
908   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
909   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
910
911   TRACE ("%x %lx\n",wParam,lParam);
912
913   /* validate parameters */
914
915   if ( (infoPtr==NULL) || (lprgSysTimeArray==NULL) ) return FALSE;
916
917   if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)  {
918                 infoPtr->selValid=TRUE;
919                 MONTHCAL_CopyTime (&lprgSysTimeArray[1],&infoPtr->maxSel);
920         MONTHCAL_CopyTime (&lprgSysTimeArray[0],&infoPtr->minSel);
921                 TRACE ("[min,max]=[%d %d]\n",infoPtr->minSel.wDay,infoPtr->maxSel.wDay);
922         return TRUE;
923   }
924  
925   return FALSE;
926 }
927
928
929
930
931 static LRESULT 
932 MONTHCAL_GetToday (HWND hwnd, WPARAM wParam, LPARAM lParam)
933 {
934   MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
935   SYSTEMTIME *lpToday=(SYSTEMTIME *) lParam;
936
937   TRACE ("%x %lx\n",wParam,lParam);
938
939   /* validate parameters */
940
941   if ( (infoPtr==NULL) || (lpToday==NULL) ) return FALSE;
942   MONTHCAL_CopyTime (&infoPtr->todaysDate,lpToday);
943   return TRUE;
944 }
945
946
947
948 static int MONTHCAL_inbox (int x, int y, RECT r) 
949
950 {
951  // TRACE ("%d %d [%d %d %d %d]\n",x,y,r.top,r.bottom,r.left,r.right);
952  
953  if ((y>r.top) && (y<r.bottom) && (x>r.left) && (x<r.right)) return TRUE;
954
955  return FALSE;
956 }
957
958
959 static LRESULT
960 MONTHCAL_HitTest (HWND hwnd, LPARAM lParam)
961 {
962  MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
963  PMCHITTESTINFO lpht=(PMCHITTESTINFO) lParam;
964  UINT x,y;
965  DWORD retval;
966
967  x=lpht->pt.x;
968  y=lpht->pt.y;
969  retval=MCHT_NOWHERE;
970  
971
972         /* are we in the header? */
973
974  if (MONTHCAL_inbox (x,y,infoPtr->title)) {
975                 
976                 if (MONTHCAL_inbox (x,y,infoPtr->titlebtnprev)) {
977                         retval=MCHT_TITLEBTNPREV;
978                         goto done;
979                 }
980                 if (MONTHCAL_inbox (x,y,infoPtr->titlebtnnext)) {
981                         retval=MCHT_TITLEBTNNEXT;
982                         goto done;
983                 }
984                 if (MONTHCAL_inbox (x,y,infoPtr->titlemonth)) {
985                         retval=MCHT_TITLEMONTH;
986                         goto done;
987                 }
988                 if (MONTHCAL_inbox (x,y,infoPtr->titleyear)) {
989                         retval=MCHT_TITLEYEAR;
990                         goto done;
991                 }
992                 retval=MCHT_TITLE;
993                 goto done;
994         }
995
996  if (MONTHCAL_inbox (x,y,infoPtr->days)) {
997                 retval=MCHT_CALENDARDAY;  /* FIXME: find out which day we're on */
998                 goto done;
999         }
1000  if (MONTHCAL_inbox (x,y,infoPtr->weeknums)) {  
1001                 retval=MCHT_CALENDARWEEKNUM;/* FIXME: find out which day we're on */
1002                 goto done;                                  
1003         }
1004  if (MONTHCAL_inbox (x,y,infoPtr->prevmonth)) {  
1005                 retval=MCHT_CALENDARDATEPREV;
1006                 goto done;                                  
1007         }
1008  if (MONTHCAL_inbox (x,y,infoPtr->nextmonth) || 
1009     ((x>infoPtr->nextmonth.left) && (x<infoPtr->nextmonth.right) &&
1010      (y>infoPtr->nextmonth.bottom) && (y<infoPtr->today.top))) {
1011                 retval=MCHT_CALENDARDATENEXT;
1012                 goto done;                                 
1013         }
1014
1015
1016  if (MONTHCAL_inbox (x,y,infoPtr->today)) {
1017                 retval=MCHT_TODAYLINK; 
1018                 goto done;
1019         }
1020
1021 /* MCHT_CALENDARDATE determination: since the next & previous month have
1022  * been handled already (MCHT_CALENDARDATEPREV/NEXT), we only have to check
1023  * whether we're in the calendar area. infoPtr->prevMonth.left handles the 
1024  * MCS_WEEKNUMBERS style nicely.
1025  */
1026         
1027
1028  TRACE ("%d %d [%d %d %d %d] [%d %d %d %d]\n",x,y, 
1029         infoPtr->prevmonth.left, infoPtr->prevmonth.right,
1030         infoPtr->prevmonth.top, infoPtr->prevmonth.bottom,
1031         infoPtr->nextmonth.left, infoPtr->nextmonth.right,
1032         infoPtr->nextmonth.top, infoPtr->nextmonth.bottom);
1033
1034  if ((x>infoPtr->prevmonth.left) && (x<infoPtr->nextmonth.right) &&
1035          (y>infoPtr->prevmonth.top) && (y<infoPtr->nextmonth.bottom))  {
1036                 lpht->st.wYear=infoPtr->currentYear;
1037                 lpht->st.wMonth=infoPtr->currentMonth;
1038                 
1039                 lpht->st.wDay=MONTHCAL_CalcDayFromPos (infoPtr,x,y);
1040
1041                 TRACE ("day hit: %d\n",lpht->st.wDay);
1042                 retval=MCHT_CALENDARDATE;
1043                 goto done;
1044
1045         }
1046
1047         /* Hit nothing special? What's left must be background :-) */
1048                 
1049   retval=MCHT_CALENDARBK;       
1050  done: 
1051   lpht->uHit=retval;
1052   return retval;
1053 }
1054
1055
1056
1057 static LRESULT
1058 MONTHCAL_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1059
1060 {
1061     MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1062         MCHITTESTINFO ht;
1063         HDC hdc;
1064         DWORD hit;
1065         HMENU hMenu;
1066         HWND retval;
1067
1068
1069         TRACE ("%x %lx\n",wParam,lParam);
1070         
1071     ht.pt.x = (INT)LOWORD(lParam);
1072     ht.pt.y = (INT)HIWORD(lParam);
1073     hit=MONTHCAL_HitTest (hwnd, (LPARAM) &ht);
1074
1075     hdc=GetDC (hwnd);
1076
1077         if (hit & MCHT_NEXT){
1078                 infoPtr->currentMonth++;
1079                 if (infoPtr->currentMonth>12) {
1080                         infoPtr->currentYear++;
1081                         infoPtr->currentMonth=1;
1082                 }
1083         }
1084         if (hit & MCHT_PREV) { 
1085                 infoPtr->currentMonth--;
1086                 if (infoPtr->currentMonth<1) {
1087                         infoPtr->currentYear--;
1088                         infoPtr->currentMonth=12;
1089                 }
1090         }
1091
1092         if (hit == MCHT_TITLEMONTH) {
1093 /*
1094                 HRSRC hrsrc = FindResourceA( COMCTL32_hModule, MAKEINTRESOURCEA(IDD_MCMONTHMENU), RT_MENUA );
1095         if (!hrsrc) { 
1096                         TRACE ("returning zero\n");
1097                         return 0;
1098                 }
1099                 TRACE ("resource is:%x\n",hrsrc);
1100         hMenu=LoadMenuIndirectA( (LPCVOID)LoadResource( COMCTL32_hModule, hrsrc ));
1101                         
1102                 TRACE ("menu is:%x\n",hMenu);
1103 */
1104
1105                 hMenu=CreateMenu ();
1106         AppendMenuA (hMenu,MF_STRING,IDM_JAN,"January");
1107         AppendMenuA (hMenu,MF_STRING,IDM_FEB,"February");
1108         AppendMenuA (hMenu,MF_STRING,IDM_MAR,"March");
1109         
1110                 retval=CreateWindowA (POPUPMENU_CLASS_ATOM, NULL, 
1111                         WS_CHILD | WS_VISIBLE,
1112                         0,0,100,220, 
1113                         hwnd, hMenu, GetWindowLongA (hwnd, GWL_HINSTANCE), NULL);
1114                 TRACE ("hwnd returned:%x\n",retval);
1115
1116         }
1117         if (hit == MCHT_TITLEYEAR) {
1118                         FIXME ("create updown for yearselection\n");
1119         }
1120         if (hit == MCHT_TODAYLINK) {
1121                         FIXME ("set currentday\n");
1122         }
1123         if (hit == MCHT_CALENDARDATE) {
1124                         SYSTEMTIME selArray[2];
1125
1126                         TRACE ("\n");
1127                         MONTHCAL_CopyTime (&ht.st, &selArray[0]);
1128                         MONTHCAL_CopyTime (&ht.st, &selArray[1]);
1129                         MONTHCAL_SetSelRange (hwnd,0,(LPARAM) &selArray); 
1130
1131                         infoPtr->firstSelDay=ht.st.wDay;
1132                         infoPtr->curSelDay=ht.st.wDay;
1133                         infoPtr->selValid=MC_SEL_LBUTDOWN;
1134         }
1135         
1136         MONTHCAL_Refresh (hwnd,hdc);
1137         ReleaseDC (hwnd,hdc);
1138         return 0;
1139 }
1140
1141 static LRESULT
1142 MONTHCAL_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1143
1144 {
1145     MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1146
1147         infoPtr->selValid=MC_SEL_LBUTUP;
1148         infoPtr->curSelDay=0;
1149         return 0;
1150 }
1151
1152
1153 static LRESULT
1154 MONTHCAL_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
1155 {
1156     MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1157         MCHITTESTINFO ht;
1158         HDC hdc;
1159         int selday,hit;
1160         RECT r;
1161
1162         if (infoPtr->selValid!=MC_SEL_LBUTDOWN) return 0;
1163
1164         ht.pt.x=LOWORD(lParam);
1165         ht.pt.y=HIWORD(lParam);
1166         
1167         hit=MONTHCAL_HitTest (hwnd, (LPARAM) &ht);
1168
1169         /* not on the calendar date numbers? bail out */
1170         TRACE ("hit:%x\n",hit);
1171         if ((hit & MCHT_CALENDARDATE) !=MCHT_CALENDARDATE) return 0;
1172
1173         selday=ht.st.wDay;
1174         infoPtr->curSelDay=selday;
1175         MONTHCAL_CalcPosFromDay (infoPtr,selday,ht.st.wMonth,&r);
1176
1177         if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)  {
1178                 SYSTEMTIME selArray[2];
1179                 int i;
1180
1181         MONTHCAL_GetSelRange (hwnd,0,(LPARAM) &selArray);
1182                 i=0;
1183                 if (infoPtr->firstSelDay==selArray[0].wDay) i=1;
1184                 TRACE ("oldRange:%d %d %d %d\n",infoPtr->firstSelDay,selArray[0].wDay,selArray[1].wDay,i);
1185                 if (infoPtr->firstSelDay==selArray[1].wDay) {  
1186                                 /* 1st time we get here: selArray[0]=selArray[1])  */
1187                                 /* if we're still at the first selected date, return */
1188                         if (infoPtr->firstSelDay==selday) goto done;
1189                                 
1190                         if (selday<infoPtr->firstSelDay) i=0;
1191                 }
1192                         
1193                 if (abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1194                         if (selday>infoPtr->firstSelDay)
1195                                 selday=infoPtr->firstSelDay+infoPtr->maxSelCount;
1196                         else
1197                                 selday=infoPtr->firstSelDay-infoPtr->maxSelCount;
1198                 }
1199                 
1200                 if (selArray[i].wDay!=selday) {
1201
1202                 TRACE ("newRange:%d %d %d %d\n",infoPtr->firstSelDay,selArray[0].wDay,selArray[1].wDay,i);
1203                         
1204                         selArray[i].wDay=selday;
1205
1206
1207                         if (selArray[0].wDay>selArray[1].wDay) {
1208                                 DWORD tempday;
1209                                 tempday=selArray[1].wDay;
1210                                 selArray[1].wDay=selArray[0].wDay;
1211                                 selArray[0].wDay=tempday;
1212                         }
1213
1214                 MONTHCAL_SetSelRange (hwnd,0,(LPARAM) &selArray);
1215                 }
1216         }
1217
1218 done:
1219
1220         hdc=GetDC (hwnd);
1221         MONTHCAL_Refresh (hwnd, hdc);
1222         ReleaseDC (hwnd, hdc);
1223
1224         return 0;
1225 }
1226
1227 static LRESULT
1228 MONTHCAL_Paint (HWND hwnd, WPARAM wParam)
1229 {
1230     HDC hdc;
1231     PAINTSTRUCT ps;
1232
1233     hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1234     MONTHCAL_Refresh (hwnd, hdc);
1235     if(!wParam)
1236     EndPaint (hwnd, &ps);
1237     return 0;
1238 }
1239
1240 static LRESULT
1241 MONTHCAL_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
1242 {
1243     HDC hdc;
1244
1245     TRACE ("\n");
1246
1247     hdc = GetDC (hwnd);
1248     MONTHCAL_Refresh (hwnd, hdc);
1249     ReleaseDC (hwnd, hdc);
1250     InvalidateRect (hwnd, NULL, TRUE);
1251
1252     return 0;
1253 }
1254
1255
1256 static LRESULT
1257 MONTHCAL_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
1258 {
1259     HDC hdc;
1260
1261     TRACE ("\n");
1262
1263     hdc = GetDC (hwnd);
1264     MONTHCAL_Refresh (hwnd, hdc);
1265     ReleaseDC (hwnd, hdc);
1266
1267     return 0;
1268 }
1269
1270
1271 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1272
1273
1274 static LRESULT
1275 MONTHCAL_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1276 {
1277     MONTHCAL_INFO *infoPtr;
1278         LOGFONTA        logFont;
1279
1280     /* allocate memory for info structure */
1281     infoPtr = (MONTHCAL_INFO *)COMCTL32_Alloc (sizeof(MONTHCAL_INFO));
1282     SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
1283
1284         if (infoPtr == NULL) {
1285         ERR ( "could not allocate info memory!\n");
1286         return 0;
1287     }
1288     if ((MONTHCAL_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
1289         ERR ( "pointer assignment error!\n");
1290         return 0;
1291     }
1292
1293
1294     infoPtr->hFont=GetStockObject(DEFAULT_GUI_FONT);
1295                 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
1296         logFont.lfWeight=FW_BOLD;
1297         infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
1298
1299     /* initialize info structure */
1300    /* FIXME: calculate systemtime ->> localtime (substract timezoneinfo) */
1301
1302         GetSystemTime (&infoPtr->todaysDate);
1303         infoPtr->firstDay  = 0;
1304         infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1305         infoPtr->currentYear= infoPtr->todaysDate.wYear;
1306         MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->minDate);
1307         MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->maxDate);
1308         infoPtr->maxSelCount=6;
1309         infoPtr->monthRange=3;
1310         infoPtr->monthdayState=COMCTL32_Alloc 
1311                            (infoPtr->monthRange*sizeof(MONTHDAYSTATE));
1312         infoPtr->titlebk     = GetSysColor (COLOR_GRAYTEXT);
1313         infoPtr->titletxt    = GetSysColor (COLOR_WINDOW);
1314         infoPtr->monthbk     = GetSysColor (COLOR_WINDOW);
1315         infoPtr->trailingtxt = GetSysColor (COLOR_GRAYTEXT);
1316         infoPtr->bk                  = GetSysColor (COLOR_WINDOW);
1317         infoPtr->txt         = GetSysColor (COLOR_WINDOWTEXT);
1318
1319     return 0;
1320 }
1321
1322
1323 static LRESULT
1324 MONTHCAL_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1325 {
1326     MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1327
1328     /* free month calendar info data */
1329     COMCTL32_Free (infoPtr);
1330
1331     return 0;
1332 }
1333
1334
1335
1336
1337
1338 LRESULT WINAPI
1339 MONTHCAL_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1340 {
1341     switch (uMsg)
1342     {
1343
1344         case MCM_GETCURSEL:
1345         return MONTHCAL_GetCurSel (hwnd, wParam, lParam);
1346
1347         case MCM_SETCURSEL:
1348         return MONTHCAL_SetCurSel (hwnd, wParam, lParam);
1349
1350         case MCM_GETMAXSELCOUNT:
1351         return MONTHCAL_GetMaxSelCount (hwnd, wParam, lParam);
1352
1353         case MCM_SETMAXSELCOUNT:
1354         return MONTHCAL_SetMaxSelCount (hwnd, wParam, lParam);
1355
1356         case MCM_GETSELRANGE:
1357         return MONTHCAL_GetSelRange (hwnd, wParam, lParam);
1358
1359         case MCM_SETSELRANGE:
1360         return MONTHCAL_SetSelRange (hwnd, wParam, lParam);
1361
1362         case MCM_GETMONTHRANGE:
1363         return MONTHCAL_GetMonthRange (hwnd, wParam, lParam);
1364
1365         case MCM_SETDAYSTATE:
1366         return MONTHCAL_SetDayState (hwnd, wParam, lParam);
1367
1368         case MCM_GETMINREQRECT:
1369         return MONTHCAL_GetMinReqRect (hwnd, wParam, lParam);
1370
1371         case MCM_GETCOLOR:
1372              return MONTHCAL_GetColor (hwnd, wParam, lParam);
1373
1374         case MCM_SETCOLOR:
1375              return MONTHCAL_SetColor (hwnd, wParam, lParam);
1376
1377         case MCM_GETTODAY:
1378              return MONTHCAL_GetToday (hwnd, wParam, lParam);
1379
1380         case MCM_SETTODAY:
1381             FIXME ( "Unimplemented msg MCM_SETTODAY\n");
1382         return 0;
1383
1384         case MCM_HITTEST:
1385             return MONTHCAL_HitTest (hwnd,lParam);
1386
1387         case MCM_GETFIRSTDAYOFWEEK:
1388         return MONTHCAL_GetFirstDayOfWeek (hwnd, wParam, lParam);
1389
1390         case MCM_SETFIRSTDAYOFWEEK:
1391         return MONTHCAL_SetFirstDayOfWeek (hwnd, wParam, lParam);
1392
1393         case MCM_GETRANGE:
1394              return MONTHCAL_GetRange (hwnd, wParam, lParam);
1395
1396         case MCM_SETRANGE:
1397              return MONTHCAL_SetRange (hwnd, wParam, lParam);
1398
1399         case MCM_GETMONTHDELTA:
1400              return MONTHCAL_GetMonthDelta (hwnd, wParam, lParam);
1401
1402         case MCM_SETMONTHDELTA:
1403              return MONTHCAL_SetMonthDelta (hwnd, wParam, lParam);
1404
1405         case MCM_GETMAXTODAYWIDTH:
1406              return MONTHCAL_GetMaxTodayWidth (hwnd);
1407
1408         case WM_GETDLGCODE:
1409         return DLGC_WANTARROWS | DLGC_WANTCHARS;
1410
1411     case WM_KILLFOCUS:
1412         return MONTHCAL_KillFocus (hwnd, wParam, lParam);
1413
1414     case WM_LBUTTONDOWN:
1415         return MONTHCAL_LButtonDown (hwnd, wParam, lParam);
1416
1417     case WM_MOUSEMOVE:
1418         return MONTHCAL_MouseMove (hwnd, wParam, lParam);
1419
1420     case WM_LBUTTONUP:
1421         return MONTHCAL_LButtonUp (hwnd, wParam, lParam);
1422
1423     case WM_PAINT:
1424         return MONTHCAL_Paint (hwnd, wParam);
1425
1426     case WM_SETFOCUS:
1427         return MONTHCAL_SetFocus (hwnd, wParam, lParam);
1428
1429         case WM_CREATE:
1430             return MONTHCAL_Create (hwnd, wParam, lParam);
1431
1432         case WM_DESTROY:
1433             return MONTHCAL_Destroy (hwnd, wParam, lParam);
1434
1435         default:
1436             if (uMsg >= WM_USER)
1437                 ERR ( "unknown msg %04x wp=%08x lp=%08lx\n",
1438                      uMsg, wParam, lParam);
1439             return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1440     }
1441     return 0;
1442 }
1443
1444
1445 VOID
1446 MONTHCAL_Register (void)
1447 {
1448     WNDCLASSA wndClass;
1449
1450     if (GlobalFindAtomA (MONTHCAL_CLASSA)) return;
1451
1452     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1453     wndClass.style         = CS_GLOBALCLASS;
1454     wndClass.lpfnWndProc   = (WNDPROC)MONTHCAL_WindowProc;
1455     wndClass.cbClsExtra    = 0;
1456     wndClass.cbWndExtra    = sizeof(MONTHCAL_INFO *);
1457     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
1458     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1459     wndClass.lpszClassName = MONTHCAL_CLASSA;
1460  
1461     RegisterClassA (&wndClass);
1462 }
1463
1464
1465 VOID
1466 MONTHCAL_Unregister (void)
1467 {
1468     if (GlobalFindAtomA (MONTHCAL_CLASSA))
1469         UnregisterClassA (MONTHCAL_CLASSA, (HINSTANCE)NULL);
1470 }
1471