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