comctl32/tests: Initialize subitem index.
[wine] / dlls / comctl32 / monthcal.c
index 87ce751..79133ba 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright 1999 Chris Morgan <cmorgan@wpi.edu> and
  *               James Abbatiello <abbeyj@wpi.edu>
  * Copyright 2000 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
- * Copyright 2009 Nikolay Sivov
+ * Copyright 2009-2011 Nikolay Sivov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -33,7 +33,6 @@
  * 
  * TODO:
  *    -- MCM_[GS]ETUNICODEFORMAT
- *    -- MONTHCAL_GetMonthRange
  *    -- handle resources better (doesn't work now); 
  *    -- take care of internationalization.
  *    -- keyboard handling.
@@ -65,7 +64,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(monthcal);
 #define MC_PREVPRESSED      4   /* Prev month button pressed */
 #define MC_NEXTPRESSED      8   /* Next month button pressed */
 #define MC_PREVNEXTMONTHDELAY   350    /* when continuously pressing `next/prev
-                                          month', wait 500 ms before going
+                                          month', wait 350 ms before going
                                           to the next/prev month */
 #define MC_TODAYUPDATEDELAY 120000 /* time between today check for update (2 min) */
 
@@ -77,23 +76,49 @@ WINE_DEFAULT_DEBUG_CHANNEL(monthcal);
 /* convert from days to 100 nanoseconds unit - used as FILETIME unit */
 #define DAYSTO100NSECS(days) (((ULONGLONG)(days))*24*60*60*10000000)
 
+enum CachedPen
+{
+    PenRed = 0,
+    PenText,
+    PenLast
+};
+
+enum CachedBrush
+{
+    BrushTitle = 0,
+    BrushMonth,
+    BrushBackground,
+    BrushLast
+};
+
+/* single calendar data */
+typedef struct _CALENDAR_INFO
+{
+    RECT title;      /* rect for the header above the calendar */
+    RECT titlemonth; /* the 'month name' text in the header */
+    RECT titleyear;  /* the 'year number' text in the header */
+    RECT wdays;      /* week days at top */
+    RECT days;       /* calendar area */
+    RECT weeknums;   /* week numbers at left side */
+
+    SYSTEMTIME month;/* contains calendar main month/year */
+} CALENDAR_INFO;
+
 typedef struct
 {
     HWND       hwndSelf;
     DWORD      dwStyle; /* cached GWL_STYLE */
-    COLORREF   bk;
-    COLORREF   txt;
-    COLORREF   titlebk;
-    COLORREF   titletxt;
-    COLORREF   monthbk;
-    COLORREF   trailingtxt;
+
+    COLORREF    colors[MCSC_TRAILINGTEXT+1];
+    HBRUSH      brushes[BrushLast];
+    HPEN        pens[PenLast];
+
     HFONT      hFont;
     HFONT      hBoldFont;
     int                textHeight;
     int                textWidth;
     int                height_increment;
     int                width_increment;
-    int                firstDayplace; /* place of the first day of the current month */
     INT                delta;  /* scroll rate; # of months that the */
                         /* control moves when user clicks a scroll button */
     int                visible;        /* # of months visible */
@@ -101,6 +126,8 @@ typedef struct
                                   stored in SYSTEMTIME format */
     BOOL       firstDaySet;    /* first week day differs from locale defined */
 
+    BOOL       isUnicode;      /* value set with MCM_SETUNICODE format */
+
     int                monthRange;
     MONTHDAYSTATE *monthdayState;
     SYSTEMTIME todaysDate;
@@ -108,27 +135,23 @@ typedef struct
     int                status;         /* See MC_SEL flags */
     SYSTEMTIME firstSel;       /* first selected day */
     INT                maxSelCount;
-    SYSTEMTIME minSel;
+    SYSTEMTIME minSel;         /* contains single selection when used without MCS_MULTISELECT */
     SYSTEMTIME maxSel;
-    SYSTEMTIME  curSel;         /* contains currently selected year, month and day */
     SYSTEMTIME  focusedSel;     /* date currently focused with mouse movement */
     DWORD      rangeValid;
     SYSTEMTIME minDate;
     SYSTEMTIME maxDate;
 
-    RECT title;                /* rect for the header above the calendar */
     RECT titlebtnnext; /* the `next month' button in the header */
     RECT titlebtnprev;  /* the `prev month' button in the header */
-    RECT titlemonth;   /* the `month name' txt in the header */
-    RECT titleyear;    /* the `year number' txt in the header */
-    RECT wdays;                /* week days at top */
-    RECT days;         /* calendar area */
-    RECT weeknums;     /* week numbers at left side */
     RECT todayrect;    /* `today: xx/xx/xx' text rect */
     HWND hwndNotify;    /* Window to receive the notifications */
     HWND hWndYearEdit;  /* Window Handle of edit box to handle years */
     HWND hWndYearUpDown;/* Window Handle of updown box to handle years */
     WNDPROC EditWndProc;  /* original Edit window procedure */
+
+    CALENDAR_INFO *calendars;
+    INT            cal_num;
 } MONTHCAL_INFO, *LPMONTHCAL_INFO;
 
 static const WCHAR themeClass[] = { 'S','c','r','o','l','l','b','a','r',0 };
@@ -139,8 +162,12 @@ static const SYSTEMTIME st_null;
 static const SYSTEMTIME max_allowed_date = { .wYear = 9999, .wMonth = 12, .wDay = 31 };
 static const SYSTEMTIME min_allowed_date = { .wYear = 1752, .wMonth = 9,  .wDay = 14 };
 
-
-#define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0))
+/* Prev/Next buttons */
+enum nav_direction
+{
+    DIRECTION_BACKWARD,
+    DIRECTION_FORWARD
+};
 
 /* helper functions  */
 
@@ -175,7 +202,7 @@ static inline void MONTHCAL_NotifySelect(const MONTHCAL_INFO *infoPtr)
 /* january is 1, december is 12 */
 int MONTHCAL_MonthLength(int month, int year)
 {
-  const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
+  const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
   /* Wrap around, this eases handling. Getting length only we shouldn't care
      about year change here cause January and December have
      the same day quantity */
@@ -217,6 +244,35 @@ static BOOL MONTHCAL_ValidateDate(const SYSTEMTIME *time)
   return TRUE;
 }
 
+/* Copies timestamp part only.
+ *
+ * PARAMETERS
+ *
+ *  [I] from : source date
+ *  [O] to   : dest date
+ */
+static void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
+{
+  to->wHour   = from->wHour;
+  to->wMinute = from->wMinute;
+  to->wSecond = from->wSecond;
+}
+
+/* Copies date part only.
+ *
+ * PARAMETERS
+ *
+ *  [I] from : source date
+ *  [O] to   : dest date
+ */
+static void MONTHCAL_CopyDate(const SYSTEMTIME *from, SYSTEMTIME *to)
+{
+  to->wYear  = from->wYear;
+  to->wMonth = from->wMonth;
+  to->wDay   = from->wDay;
+  to->wDayOfWeek = from->wDayOfWeek;
+}
+
 /* Compares two dates in SYSTEMTIME format
  *
  * PARAMETERS
@@ -242,6 +298,29 @@ static LONG MONTHCAL_CompareSystemTime(const SYSTEMTIME *first, const SYSTEMTIME
   return CompareFileTime(&ft_first, &ft_second);
 }
 
+static LONG MONTHCAL_CompareMonths(const SYSTEMTIME *first, const SYSTEMTIME *second)
+{
+  SYSTEMTIME st_first, st_second;
+
+  st_first = st_second = st_null;
+  MONTHCAL_CopyDate(first, &st_first);
+  MONTHCAL_CopyDate(second, &st_second);
+  st_first.wDay = st_second.wDay = 1;
+
+  return MONTHCAL_CompareSystemTime(&st_first, &st_second);
+}
+
+static LONG MONTHCAL_CompareDate(const SYSTEMTIME *first, const SYSTEMTIME *second)
+{
+  SYSTEMTIME st_first, st_second;
+
+  st_first = st_second = st_null;
+  MONTHCAL_CopyDate(first, &st_first);
+  MONTHCAL_CopyDate(second, &st_second);
+
+  return MONTHCAL_CompareSystemTime(&st_first, &st_second);
+}
+
 /* Checks largest possible date range and configured one
  *
  * PARAMETERS
@@ -315,10 +394,10 @@ static BOOL MONTHCAL_IsSelRangeValid(const MONTHCAL_INFO *infoPtr,
   SystemTimeToFileTime(range0, &ft_range0);
   SystemTimeToFileTime(range1, &ft_range1);
 
-  ul_range0.LowPart  = ft_range0.dwLowDateTime;
-  ul_range0.HighPart = ft_range0.dwHighDateTime;
-  ul_range1.LowPart  = ft_range1.dwLowDateTime;
-  ul_range1.HighPart = ft_range1.dwHighDateTime;
+  ul_range0.u.LowPart  = ft_range0.dwLowDateTime;
+  ul_range0.u.HighPart = ft_range0.dwHighDateTime;
+  ul_range1.u.LowPart  = ft_range1.dwLowDateTime;
+  ul_range1.u.HighPart = ft_range1.dwHighDateTime;
 
   cmp = CompareFileTime(&ft_range0, &ft_range1);
 
@@ -335,8 +414,8 @@ static BOOL MONTHCAL_IsSelRangeValid(const MONTHCAL_INFO *infoPtr,
        else
           ul_range0.QuadPart = ul_range1.QuadPart - DAYSTO100NSECS(infoPtr->maxSelCount - 1);
 
-       ft_range0.dwLowDateTime  = ul_range0.LowPart;
-       ft_range0.dwHighDateTime = ul_range0.HighPart;
+       ft_range0.dwLowDateTime  = ul_range0.u.LowPart;
+       ft_range0.dwHighDateTime = ul_range0.u.HighPart;
        FileTimeToSystemTime(&ft_range0, adjust);
      }
 
@@ -355,35 +434,6 @@ static BOOL MONTHCAL_ValidateTime(const SYSTEMTIME *time)
     return TRUE;
 }
 
-/* Copies timestamp part only.
- *
- * PARAMETERS
- *
- *  [I] from : source date
- *  [O] to   : dest date
- */
-static void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
-{
-  to->wHour   = from->wHour;
-  to->wMinute = from->wMinute;
-  to->wSecond = from->wSecond;
-}
-
-/* Copies date part only.
- *
- * PARAMETERS
- *
- *  [I] from : source date
- *  [O] to   : dest date
- */
-static void MONTHCAL_CopyDate(const SYSTEMTIME *from, SYSTEMTIME *to)
-{
-  to->wYear  = from->wYear;
-  to->wMonth = from->wMonth;
-  to->wDay   = from->wDay;
-  to->wDayOfWeek = from->wDayOfWeek;
-}
-
 /* Note:Depending on DST, this may be offset by a day.
    Need to find out if we're on a DST place & adjust the clock accordingly.
    Above function assumes we have a valid data.
@@ -394,61 +444,63 @@ static void MONTHCAL_CopyDate(const SYSTEMTIME *from, SYSTEMTIME *to)
 /* Returns the day in the week
  *
  * PARAMETERS
- *  [i] day : day of month [1, 31]
- *  [I] month : month number [1, 12]
- *  [I] year : year value
+ *  [i] date    : input date
+ *  [I] inplace : set calculated value back to date structure
  *
  * RETURN VALUE
  *   day of week in SYSTEMTIME format: (0 == sunday,..., 6 == saturday)
  */
-int MONTHCAL_CalculateDayOfWeek(WORD day, WORD month, WORD year)
+int MONTHCAL_CalculateDayOfWeek(SYSTEMTIME *date, BOOL inplace)
 {
+  SYSTEMTIME st = st_null;
   FILETIME ft;
-  SYSTEMTIME st;
 
-  st.wYear  = year;
-  st.wMonth = month;
-  st.wDay   = day;
-  st.wHour  = st.wMinute = st.wSecond = st.wMilliseconds = 0;
+  MONTHCAL_CopyDate(date, &st);
 
   SystemTimeToFileTime(&st, &ft);
   FileTimeToSystemTime(&ft, &st);
 
+  if (inplace) date->wDayOfWeek = st.wDayOfWeek;
+
   return st.wDayOfWeek;
 }
 
+/* add/subtract 'months' from date */
+static inline void MONTHCAL_GetMonth(SYSTEMTIME *date, INT months)
+{
+  INT length, m = date->wMonth + months;
+
+  date->wYear += m > 0 ? (m - 1) / 12 : m / 12 - 1;
+  date->wMonth = m > 0 ? (m - 1) % 12 + 1 : 12 + m % 12;
+  /* fix moving from last day in a month */
+  length = MONTHCAL_MonthLength(date->wMonth, date->wYear);
+  if(date->wDay > length) date->wDay = length;
+  MONTHCAL_CalculateDayOfWeek(date, TRUE);
+}
+
 /* properly updates date to point on next month */
 static inline void MONTHCAL_GetNextMonth(SYSTEMTIME *date)
 {
-  if(++date->wMonth > 12)
-  {
-    date->wMonth = 1;
-    date->wYear++;
-  }
-  date->wDayOfWeek = MONTHCAL_CalculateDayOfWeek(date->wDay, date->wMonth,
-                                                 date->wYear);
+  return MONTHCAL_GetMonth(date, 1);
 }
 
 /* properly updates date to point on prev month */
 static inline void MONTHCAL_GetPrevMonth(SYSTEMTIME *date)
 {
-  if(--date->wMonth < 1)
-  {
-    date->wMonth = 12;
-    date->wYear--;
-  }
-  date->wDayOfWeek = MONTHCAL_CalculateDayOfWeek(date->wDay, date->wMonth,
-                                                 date->wYear);
+  return MONTHCAL_GetMonth(date, -1);
 }
 
 /* Returns full date for a first currently visible day */
 static void MONTHCAL_GetMinDate(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *date)
 {
-  int firstDay;
+  /* zero indexed calendar has the earliest date */
+  SYSTEMTIME st_first = infoPtr->calendars[0].month;
+  INT firstDay;
 
-  firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->curSel.wMonth, infoPtr->curSel.wYear);
+  st_first.wDay = 1;
+  firstDay = MONTHCAL_CalculateDayOfWeek(&st_first, FALSE);
 
-  *date = infoPtr->curSel;
+  *date = infoPtr->calendars[0].month;
   MONTHCAL_GetPrevMonth(date);
 
   date->wDay = MONTHCAL_MonthLength(date->wMonth, date->wYear) +
@@ -458,36 +510,36 @@ static void MONTHCAL_GetMinDate(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *date)
     date->wDay -= 7;
 
   /* fix day of week */
-  date->wDayOfWeek = MONTHCAL_CalculateDayOfWeek(date->wDay, date->wMonth,
-                                                 date->wYear);
+  MONTHCAL_CalculateDayOfWeek(date, TRUE);
 }
 
 /* Returns full date for a last currently visible day */
 static void MONTHCAL_GetMaxDate(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *date)
 {
-  SYSTEMTIME st;
+  /* the latest date is in latest calendar */
+  SYSTEMTIME st, lt_month = infoPtr->calendars[infoPtr->cal_num-1].month;
 
-  *date = infoPtr->curSel;
+  *date = lt_month;
   MONTHCAL_GetNextMonth(date);
 
   MONTHCAL_GetMinDate(infoPtr, &st);
   /* Use month length to get max day. 42 means max day count in calendar area */
   date->wDay = 42 - (MONTHCAL_MonthLength(st.wMonth, st.wYear) - st.wDay + 1) -
-                     MONTHCAL_MonthLength(infoPtr->curSel.wMonth, infoPtr->curSel.wYear);
+                     MONTHCAL_MonthLength(lt_month.wMonth, lt_month.wYear);
 
   /* fix day of week */
-  date->wDayOfWeek = MONTHCAL_CalculateDayOfWeek(date->wDay, date->wMonth,
-                                                 date->wYear);
+  MONTHCAL_CalculateDayOfWeek(date, TRUE);
 }
 
 /* From a given point, calculate the row (weekpos), column(daypos)
    and day in the calendar. day== 0 mean the last day of tha last month
 */
 static int MONTHCAL_CalcDayFromPos(const MONTHCAL_INFO *infoPtr, int x, int y,
-                                  int *daypos,int *weekpos)
+                                  int *daypos, int *weekpos)
 {
   int retval, firstDay;
   RECT rcClient;
+  SYSTEMTIME st = infoPtr->minSel;
 
   GetClientRect(infoPtr->hwndSelf, &rcClient);
 
@@ -496,65 +548,71 @@ static int MONTHCAL_CalcDayFromPos(const MONTHCAL_INFO *infoPtr, int x, int y,
   if (x > rcClient.right)
     x = rcClient.right;
 
+  *daypos  = (x - infoPtr->calendars[0].days.left ) / infoPtr->width_increment;
+  *weekpos = (y - infoPtr->calendars[0].days.top ) / infoPtr->height_increment;
 
-  *daypos = (x - infoPtr->days.left ) / infoPtr->width_increment;
-  *weekpos = (y - infoPtr->days.top ) / infoPtr->height_increment;
-
-  firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->curSel.wMonth, infoPtr->curSel.wYear)+6 - infoPtr->firstDay)%7;
+  st.wDay = 1;
+  firstDay = (MONTHCAL_CalculateDayOfWeek(&st, FALSE) + 6 - infoPtr->firstDay) % 7;
   retval = *daypos + (7 * *weekpos) - firstDay;
   return retval;
 }
 
-/* day is the day of the month, 1 == 1st day of the month */
-/* sets x and y to be the position of the day */
-/* x == day, y == week where(0,0) == firstDay, 1st week */
-static void MONTHCAL_CalcDayXY(const MONTHCAL_INFO *infoPtr, int day, int month,
-                                 int *x, int *y)
+/* Sets the RECT struct r to the rectangle around the date
+ *
+ * PARAMETERS
+ *
+ *  [I] infoPtr : pointer to control data
+ *  [I] date : date value
+ *  [O] x : day column (zero based)
+ *  [O] y : week column (zero based)
+ */
+static void MONTHCAL_CalcDayXY(const MONTHCAL_INFO *infoPtr,
+                               const SYSTEMTIME *date, INT *x, INT *y)
 {
-  int firstDay, prevMonth;
+  SYSTEMTIME st = infoPtr->minSel;
+  LONG cmp;
+  int first;
 
-  firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->curSel.wMonth, infoPtr->curSel.wYear) +6 - infoPtr->firstDay)%7;
+  st.wDay = 1;
+  first = (MONTHCAL_CalculateDayOfWeek(&st, FALSE) + 6 - infoPtr->firstDay) % 7;
 
-  if(month==infoPtr->curSel.wMonth) {
-    *x = (day + firstDay) % 7;
-    *y = (day + firstDay - *x) / 7;
-    return;
-  }
-  if(month < infoPtr->curSel.wMonth) {
-    prevMonth = month - 1;
-    if(prevMonth==0)
-       prevMonth = 12;
+  cmp = MONTHCAL_CompareMonths(date, &infoPtr->minSel);
 
-    *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->curSel.wYear) - firstDay) % 7;
+  /* previous month */
+  if(cmp == -1) {
+    *x = (first - MONTHCAL_MonthLength(date->wMonth, infoPtr->minSel.wYear) + date->wDay) % 7;
     *y = 0;
     return;
   }
 
-  *y = MONTHCAL_MonthLength(month, infoPtr->curSel.wYear - 1) / 7;
-  *x = (day + firstDay + MONTHCAL_MonthLength(month,
-       infoPtr->curSel.wYear)) % 7;
+  /* next month calculation is same as for current,
+     just add current month length */
+  if(cmp == 1) {
+    first += MONTHCAL_MonthLength(infoPtr->minSel.wMonth, infoPtr->minSel.wYear);
+  }
+
+  *x = (date->wDay + first) % 7;
+  *y = (date->wDay + first - *x) / 7;
 }
 
 
 /* x: column(day), y: row(week) */
-static void MONTHCAL_CalcDayRect(const MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
+static inline void MONTHCAL_CalcDayRect(const MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
 {
-  r->left = infoPtr->days.left + x * infoPtr->width_increment;
+  r->left = infoPtr->calendars[0].days.left + x * infoPtr->width_increment;
   r->right = r->left + infoPtr->width_increment;
-  r->top  = infoPtr->days.top  + y * infoPtr->height_increment;
+  r->top  = infoPtr->calendars[0].days.top  + y * infoPtr->height_increment;
   r->bottom = r->top + infoPtr->textHeight;
 }
 
 
-/* sets the RECT struct r to the rectangle around the day and month */
-/* day is the day value of the month(1 == 1st), month is the month */
-/* value(january == 1, december == 12) */
+/* Sets the RECT struct r to the rectangle around the date */
 static inline void MONTHCAL_CalcPosFromDay(const MONTHCAL_INFO *infoPtr,
-                                            int day, int month, RECT *r)
+                                           const SYSTEMTIME *date, RECT *r)
 {
   int x, y;
 
-  MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
+  MONTHCAL_CalcDayXY(infoPtr, date, &x, &y);
   MONTHCAL_CalcDayRect(infoPtr, r, x, y);
 }
 
@@ -577,15 +635,13 @@ static BOOL MONTHCAL_SetDayFocus(MONTHCAL_INFO *infoPtr, const SYSTEMTIME *st)
     if(MONTHCAL_IsDateEqual(&infoPtr->focusedSel, st)) return FALSE;
 
     /* invalidate old focused day */
-    MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->focusedSel.wDay,
-                                     infoPtr->focusedSel.wMonth, &r);
+    MONTHCAL_CalcPosFromDay(infoPtr, &infoPtr->focusedSel, &r);
     InvalidateRect(infoPtr->hwndSelf, &r, FALSE);
 
     infoPtr->focusedSel = *st;
   }
 
-  MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->focusedSel.wDay,
-                                   infoPtr->focusedSel.wMonth, &r);
+  MONTHCAL_CalcPosFromDay(infoPtr, &infoPtr->focusedSel, &r);
 
   if(!st && MONTHCAL_ValidateDate(&infoPtr->focusedSel))
     infoPtr->focusedSel = st_null;
@@ -596,94 +652,84 @@ static BOOL MONTHCAL_SetDayFocus(MONTHCAL_INFO *infoPtr, const SYSTEMTIME *st)
   return TRUE;
 }
 
-/* day is the day in the month(1 == 1st of the month) */
-/* month is the month value(1 == january, 12 == december) */
-static void MONTHCAL_CircleDay(const MONTHCAL_INFO *infoPtr, HDC hdc, int day, int month)
+/* draw today boundary box for specified rectangle */
+static void MONTHCAL_Circle(const MONTHCAL_INFO *infoPtr, HDC hdc, const RECT *r)
 {
-  HPEN hRedPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
-  HPEN hOldPen2 = SelectObject(hdc, hRedPen);
-  HBRUSH hOldBrush;
-  RECT day_rect;
+  HPEN old_pen = SelectObject(hdc, infoPtr->pens[PenRed]);
+  HBRUSH old_brush;
 
-  MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
+  old_brush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
+  Rectangle(hdc, r->left, r->top, r->right, r->bottom);
 
-  hOldBrush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
-  Rectangle(hdc, day_rect.left, day_rect.top, day_rect.right, day_rect.bottom);
+  SelectObject(hdc, old_brush);
+  SelectObject(hdc, old_pen);
+}
 
-  SelectObject(hdc, hOldBrush);
-  DeleteObject(hRedPen);
-  SelectObject(hdc, hOldPen2);
+/* Draw today day mark rectangle
+ *
+ * [I] hdc  : context to draw in
+ * [I] date : day to mark with rectangle
+ *
+ */
+static void MONTHCAL_CircleDay(const MONTHCAL_INFO *infoPtr, HDC hdc,
+                               const SYSTEMTIME *date)
+{
+  RECT day_rect;
+  MONTHCAL_CalcPosFromDay(infoPtr, date, &day_rect);
+  MONTHCAL_Circle(infoPtr, hdc, &day_rect);
 }
 
-static void MONTHCAL_DrawDay(const MONTHCAL_INFO *infoPtr, HDC hdc, int day, int month,
-                             int x, int y, int bold)
+static void MONTHCAL_DrawDay(const MONTHCAL_INFO *infoPtr, HDC hdc, const SYSTEMTIME *st,
+                             int bold, const PAINTSTRUCT *ps)
 {
   static const WCHAR fmtW[] = { '%','d',0 };
   WCHAR buf[10];
-  RECT r;
-  static BOOL haveBoldFont, haveSelectedDay = FALSE;
-  HBRUSH hbr;
+  RECT r, r_temp;
   COLORREF oldCol = 0;
-  COLORREF oldBk = 0;
+  COLORREF oldBk  = 0;
+  INT old_bkmode, selection;
 
-  wsprintfW(buf, fmtW, day);
+  /* no need to check styles: when selection is not valid, it is set to zero.
+     1 < day < 31, so everything is OK */
+  MONTHCAL_CalcPosFromDay(infoPtr, st, &r);
+  if(!IntersectRect(&r_temp, &(ps->rcPaint), &r)) return;
 
-/* No need to check styles: when selection is not valid, it is set to zero.
- * 1<day<31, so everything is OK.
- */
-
-  MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
-
-  if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
-       && (month == infoPtr->curSel.wMonth)) {
-    RECT r2;
+  if ((MONTHCAL_CompareDate(st, &infoPtr->minSel) >= 0) &&
+      (MONTHCAL_CompareDate(st, &infoPtr->maxSel) <= 0))
+  {
 
-    TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
+    TRACE("%d %d %d\n", st->wDay, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
     TRACE("%s\n", wine_dbgstr_rect(&r));
-    oldCol = SetTextColor(hdc, infoPtr->monthbk);
-    oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
-    hbr = GetSysColorBrush(COLOR_HIGHLIGHT);
-    FillRect(hdc, &r, hbr);
+    oldCol = SetTextColor(hdc, infoPtr->colors[MCSC_MONTHBK]);
+    oldBk = SetBkColor(hdc, infoPtr->colors[MCSC_TRAILINGTEXT]);
+    FillRect(hdc, &r, infoPtr->brushes[BrushTitle]);
 
-    /* FIXME: this may need to be changed now b/c of the other
-       drawing changes 11/3/99 CMM */
-    r2.left   = r.left - 0.25 * infoPtr->textWidth;
-    r2.top    = r.top;
-    r2.right  = r.left + 0.5 * infoPtr->textWidth;
-    r2.bottom = r.bottom;
-    if(haveSelectedDay) FillRect(hdc, &r2, hbr);
-      haveSelectedDay = TRUE;
-  } else {
-    haveSelectedDay = FALSE;
+    selection = 1;
   }
+  else
+    selection = 0;
 
-  /* need to add some code for multiple selections */
-
-  if((bold) &&(!haveBoldFont)) {
-    SelectObject(hdc, infoPtr->hBoldFont);
-    haveBoldFont = TRUE;
-  }
-  if((!bold) &&(haveBoldFont)) {
-    SelectObject(hdc, infoPtr->hFont);
-    haveBoldFont = FALSE;
-  }
+  SelectObject(hdc, bold ? infoPtr->hBoldFont : infoPtr->hFont);
 
-  SetBkMode(hdc,TRANSPARENT);
+  old_bkmode = SetBkMode(hdc, TRANSPARENT);
+  wsprintfW(buf, fmtW, st->wDay);
   DrawTextW(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
+  SetBkMode(hdc, old_bkmode);
 
-  if(haveSelectedDay) {
+  if (selection)
+  {
     SetTextColor(hdc, oldCol);
     SetBkColor(hdc, oldBk);
   }
 }
 
 
-static void paint_button (MONTHCAL_INFO *infoPtr, HDC hdc, BOOL btnNext)
+static void MONTHCAL_PaintButton(MONTHCAL_INFO *infoPtr, HDC hdc, enum nav_direction button)
 {
     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
-    RECT *r = btnNext ? &infoPtr->titlebtnnext : &infoPtr->titlebtnprev;
-    BOOL pressed = btnNext ? (infoPtr->status & MC_NEXTPRESSED) :
-                             (infoPtr->status & MC_PREVPRESSED);
+    RECT *r = button == DIRECTION_FORWARD ? &infoPtr->titlebtnnext : &infoPtr->titlebtnprev;
+    BOOL pressed = button == DIRECTION_FORWARD ? infoPtr->status & MC_NEXTPRESSED :
+                                                 infoPtr->status & MC_PREVPRESSED;
     if (theme)
     {
         static const int states[] = {
@@ -692,7 +738,7 @@ static void paint_button (MONTHCAL_INFO *infoPtr, HDC hdc, BOOL btnNext)
             /* Next button */
             ABS_RIGHTNORMAL, ABS_RIGHTPRESSED, ABS_RIGHTDISABLED
         };
-        int stateNum = btnNext ? 3 : 0;
+        int stateNum = button == DIRECTION_FORWARD ? 3 : 0;
         if (pressed)
             stateNum += 1;
         else
@@ -703,7 +749,7 @@ static void paint_button (MONTHCAL_INFO *infoPtr, HDC hdc, BOOL btnNext)
     }
     else
     {
-        int style = btnNext ? DFCS_SCROLLRIGHT : DFCS_SCROLLLEFT;
+        int style = button == DIRECTION_FORWARD ? DFCS_SCROLLRIGHT : DFCS_SCROLLLEFT;
         if (pressed)
             style |= DFCS_PUSHED;
         else
@@ -714,427 +760,430 @@ static void paint_button (MONTHCAL_INFO *infoPtr, HDC hdc, BOOL btnNext)
         DrawFrameControl(hdc, r, DFC_SCROLL, style);
     }
 }
-
-
-static void MONTHCAL_Refresh(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps)
+/* paint a title with buttons and month/year string */
+static void MONTHCAL_PaintTitle(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps, INT calIdx)
 {
   static const WCHAR fmt_monthW[] = { '%','s',' ','%','l','d',0 };
-  RECT *title=&infoPtr->title;
-  RECT *titlemonth=&infoPtr->titlemonth;
-  RECT *titleyear=&infoPtr->titleyear;
-  RECT dayrect;
-  int i, j, m, mask, day, prevMonth;
-  int textHeight = infoPtr->textHeight;
-  SIZE size;
-  HBRUSH hbr;
-  HFONT currentFont;
-  WCHAR buf[20];
-  WCHAR buf1[20];
-  WCHAR buf2[32];
-  COLORREF oldTextColor, oldBkColor;
-  RECT rcTemp;
-  RECT rcDay; /* used in MONTHCAL_CalcDayRect() */
-  int startofprescal;
-  SYSTEMTIME st;
+  RECT *title = &infoPtr->calendars[calIdx].title;
+  const SYSTEMTIME *st = &infoPtr->calendars[calIdx].month;
+  WCHAR buf_month[80], buf_fmt[80];
+  SIZE sz;
 
-  oldTextColor = SetTextColor(hdc, comctl32_color.clrWindowText);
+  /* fill header box */
+  FillRect(hdc, title, infoPtr->brushes[BrushTitle]);
 
-  /* fill background */
-  hbr = CreateSolidBrush (infoPtr->bk);
-  FillRect(hdc, &ps->rcPaint, hbr);
-  DeleteObject(hbr);
+  /* month/year string */
+  SetBkColor(hdc, infoPtr->colors[MCSC_TITLEBK]);
+  SetTextColor(hdc, infoPtr->colors[MCSC_TITLETEXT]);
+  SelectObject(hdc, infoPtr->hBoldFont);
 
-  /* draw header */
-  if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
-  {
-    hbr =  CreateSolidBrush(infoPtr->titlebk);
-    FillRect(hdc, title, hbr);
-    DeleteObject(hbr);
-  }
+  GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1 + st->wMonth - 1,
+                 buf_month, countof(buf_month));
+
+  wsprintfW(buf_fmt, fmt_monthW, buf_month, st->wYear);
+  DrawTextW(hdc, buf_fmt, strlenW(buf_fmt), title,
+                      DT_CENTER | DT_VCENTER | DT_SINGLELINE);
 
-  /* if the previous button is pressed draw it depressed */
-  if(IntersectRect(&rcTemp, &(ps->rcPaint), &infoPtr->titlebtnprev))
-    paint_button(infoPtr, hdc, FALSE);
+  /* update title rectangles with current month - used while testing hits */
+  GetTextExtentPoint32W(hdc, buf_fmt, strlenW(buf_fmt), &sz);
+  infoPtr->calendars[calIdx].titlemonth.left = title->right / 2 + title->left / 2 - sz.cx / 2;
+  infoPtr->calendars[calIdx].titleyear.right = title->right / 2 + title->left / 2 + sz.cx / 2;
 
-  /* if next button is depressed draw it depressed */
-  if(IntersectRect(&rcTemp, &(ps->rcPaint), &infoPtr->titlebtnnext))
-    paint_button(infoPtr, hdc, TRUE);
+  GetTextExtentPoint32W(hdc, buf_month, strlenW(buf_month), &sz);
+  infoPtr->calendars[calIdx].titlemonth.right = infoPtr->calendars[calIdx].titlemonth.left + sz.cx;
+  infoPtr->calendars[calIdx].titleyear.left   = infoPtr->calendars[calIdx].titlemonth.right;
+}
 
-  oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
-  SetTextColor(hdc, infoPtr->titletxt);
-  currentFont = SelectObject(hdc, infoPtr->hBoldFont);
+static void MONTHCAL_PaintWeeknumbers(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps, INT calIdx)
+{
+  const SYSTEMTIME *date = &infoPtr->calendars[calIdx].month;
+  static const WCHAR fmt_weekW[] = { '%','d',0 };
+  INT mindays, weeknum, weeknum1, startofprescal;
+  INT i, prev_month;
+  SYSTEMTIME st;
+  WCHAR buf[80];
+  HPEN old_pen;
+  RECT r;
 
-  GetLocaleInfoW( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+infoPtr->curSel.wMonth -1,
-                 buf1,countof(buf1));
-  wsprintfW(buf, fmt_monthW, buf1, infoPtr->curSel.wYear);
+  if (!(infoPtr->dwStyle & MCS_WEEKNUMBERS)) return;
 
-  if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
+  MONTHCAL_GetMinDate(infoPtr, &st);
+  startofprescal = st.wDay;
+  st = *date;
+
+  prev_month = date->wMonth - 1;
+  if(prev_month == 0) prev_month = 12;
+
+  /*
+     Rules what week to call the first week of a new year:
+     LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
+     The week containing Jan 1 is the first week of year
+     LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
+     First week of year must contain 4 days of the new year
+     LOCALE_IFIRSTWEEKOFYEAR == 1  (what contries?)
+     The first week of the year must contain only days of the new year
+  */
+  GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, countof(buf));
+  weeknum = atoiW(buf);
+  switch (weeknum)
   {
-    DrawTextW(hdc, buf, strlenW(buf), title,
-                        DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+    case 1: mindays = 6;
+       break;
+    case 2: mindays = 3;
+       break;
+    case 0: mindays = 0;
+        break;
+    default:
+        WARN("Unknown LOCALE_IFIRSTWEEKOFYEAR value %d, defaulting to 0\n", weeknum);
+       mindays = 0;
   }
 
-/* titlemonth left/right contained rect for whole titletxt('June  1999')
-  * MCM_HitTestInfo wants month & year rects, so prepare these now.
-  *(no, we can't draw them separately; the whole text is centered)
-  */
-  GetTextExtentPoint32W(hdc, buf, strlenW(buf), &size);
-  titlemonth->left = title->right / 2 + title->left / 2 - size.cx / 2;
-  titleyear->right = title->right / 2 + title->left / 2 + size.cx / 2;
-  GetTextExtentPoint32W(hdc, buf1, strlenW(buf1), &size);
-  titlemonth->right = titlemonth->left + size.cx;
-  titleyear->left = titlemonth->right;
-
-  /* draw month area */
-  rcTemp.top=infoPtr->wdays.top;
-  rcTemp.left=infoPtr->wdays.left;
-  rcTemp.bottom=infoPtr->todayrect.bottom;
-  rcTemp.right =infoPtr->todayrect.right;
-  if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcTemp))
+  if (date->wMonth == 1)
+  {
+    /* calculate all those exceptions for january */
+    st.wDay = st.wMonth = 1;
+    weeknum1 = MONTHCAL_CalculateDayOfWeek(&st, FALSE);
+    if ((infoPtr->firstDay - weeknum1) % 7 > mindays)
+       weeknum = 1;
+    else
+    {
+       weeknum = 0;
+       for(i = 0; i < 11; i++)
+          weeknum += MONTHCAL_MonthLength(i+1, date->wYear - 1);
+
+       weeknum  += startofprescal + 7;
+       weeknum  /= 7;
+       st.wYear -= 1;
+       weeknum1  = MONTHCAL_CalculateDayOfWeek(&st, FALSE);
+       if ((infoPtr->firstDay - weeknum1) % 7 > mindays) weeknum++;
+    }
+  }
+  else
   {
-    hbr =  CreateSolidBrush(infoPtr->monthbk);
-    FillRect(hdc, &rcTemp, hbr);
-    DeleteObject(hbr);
+    weeknum = 0;
+    for(i = 0; i < prev_month - 1; i++)
+       weeknum += MONTHCAL_MonthLength(i+1, date->wYear);
+
+    weeknum += startofprescal + 7;
+    weeknum /= 7;
+    st.wDay = st.wMonth = 1;
+    weeknum1 = MONTHCAL_CalculateDayOfWeek(&st, FALSE);
+    if ((infoPtr->firstDay - weeknum1) % 7 > mindays) weeknum++;
   }
 
-/* draw line under day abbreviations */
+  r = infoPtr->calendars[calIdx].weeknums;
 
-  MoveToEx(hdc, infoPtr->days.left + 3, title->bottom + textHeight + 1, NULL);
-  LineTo(hdc, infoPtr->days.right - 3, title->bottom + textHeight + 1);
+  /* erase whole week numbers area */
+  FillRect(hdc, &r, infoPtr->brushes[BrushTitle]);
+  SetTextColor(hdc, infoPtr->colors[MCSC_TITLEBK]);
 
-  prevMonth = infoPtr->curSel.wMonth - 1;
-  if(prevMonth == 0) /* if curSel.wMonth is january(1) prevMonth is */
-    prevMonth = 12;    /* december(12) of the previous year */
+  /* reduce rectangle to one week number */
+  r.bottom = r.top + infoPtr->height_increment;
 
-  infoPtr->wdays.left   = infoPtr->days.left   = infoPtr->weeknums.right;
+  for(i = 0; i < 6; i++) {
+    if((i == 0) && (weeknum > 50))
+    {
+        wsprintfW(buf, fmt_weekW, weeknum);
+        weeknum = 0;
+    }
+    else if((i == 5) && (weeknum > 47))
+    {
+       wsprintfW(buf, fmt_weekW, 1);
+    }
+    else
+       wsprintfW(buf, fmt_weekW, weeknum + i);
 
-  /* draw day abbreviations */
-  SelectObject(hdc, infoPtr->hFont);
-  SetBkColor(hdc, infoPtr->monthbk);
-  SetTextColor(hdc, infoPtr->trailingtxt);
+    DrawTextW(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+    OffsetRect(&r, 0, infoPtr->height_increment);
+  }
 
-  /* rectangle to draw a single day abbreviation within */
-  dayrect = infoPtr->wdays;
-  dayrect.right = dayrect.left + infoPtr->width_increment;
+  /* line separator for week numbers column */
+  old_pen = SelectObject(hdc, infoPtr->pens[PenText]);
+  MoveToEx(hdc, infoPtr->calendars[calIdx].weeknums.right, infoPtr->calendars[calIdx].weeknums.top + 3 , NULL);
+  LineTo(hdc,   infoPtr->calendars[calIdx].weeknums.right, infoPtr->calendars[calIdx].weeknums.bottom);
+  SelectObject(hdc, old_pen);
+}
 
-  i = infoPtr->firstDay;
+/* bottom today date */
+static void MONTHCAL_PaintTodayTitle(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps)
+{
+  static const WCHAR fmt_todayW[] = { '%','s',' ','%','s',0 };
+  WCHAR buf_todayW[30], buf_dateW[20], buf[80];
+  RECT text_rect, box_rect;
+  HFONT old_font;
+  INT col;
 
-  for(j = 0; j < 7; j++) {
-    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1 + (i+j+6)%7, buf, countof(buf));
-    DrawTextW(hdc, buf, strlenW(buf), &dayrect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
-    dayrect.left  += infoPtr->width_increment;
-    dayrect.right += infoPtr->width_increment;
+  if(infoPtr->dwStyle & MCS_NOTODAY) return;
+
+  if (!LoadStringW(COMCTL32_hModule, IDM_TODAY, buf_todayW, countof(buf_todayW)))
+  {
+    static const WCHAR todayW[] = { 'T','o','d','a','y',':',0 };
+    WARN("Can't load resource\n");
+    strcpyW(buf_todayW, todayW);
   }
 
-  /* draw day numbers; first, the previous month */
-  MONTHCAL_GetMinDate(infoPtr, &st);
-  day = st.wDay;
-  startofprescal = day;
-  mask = 1<<(day-1);
-
-  i = 0;
-  m = 0;
-  while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->curSel.wYear)) {
-    MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
-    if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
-    {
-      MONTHCAL_DrawDay(infoPtr, hdc, day, prevMonth, i, 0,
-          infoPtr->monthdayState[m] & mask);
-    }
+  col = infoPtr->dwStyle & MCS_NOTODAYCIRCLE ? 0 : 1;
+  if (infoPtr->dwStyle & MCS_WEEKNUMBERS) col--;
+  MONTHCAL_CalcDayRect(infoPtr, &text_rect, col, 6);
+  box_rect = text_rect;
+
+  GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &infoPtr->todaysDate, NULL,
+                                                      buf_dateW, countof(buf_dateW));
+  old_font = SelectObject(hdc, infoPtr->hBoldFont);
+  SetTextColor(hdc, infoPtr->colors[MCSC_TEXT]);
 
-    mask<<=1;
-    day++;
-    i++;
+  wsprintfW(buf, fmt_todayW, buf_todayW, buf_dateW);
+  DrawTextW(hdc, buf, -1, &text_rect, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
+  DrawTextW(hdc, buf, -1, &text_rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
+
+  if(!(infoPtr->dwStyle & MCS_NOTODAYCIRCLE)) {
+    OffsetRect(&box_rect, -infoPtr->width_increment, 0);
+    MONTHCAL_Circle(infoPtr, hdc, &box_rect);
   }
 
-/* draw `current' month  */
+  SelectObject(hdc, old_font);
+}
 
-  day = 1; /* start at the beginning of the current month */
+/* today mark + focus */
+static void MONTHCAL_PaintFocusAndCircle(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps)
+{
+  if((infoPtr->minSel.wMonth == infoPtr->todaysDate.wMonth) &&
+     (infoPtr->minSel.wYear  == infoPtr->todaysDate.wYear) &&
+    !(infoPtr->dwStyle & MCS_NOTODAYCIRCLE))
+  {
+    MONTHCAL_CircleDay(infoPtr, hdc, &infoPtr->todaysDate);
+  }
 
-  infoPtr->firstDayplace = i;
-  SetTextColor(hdc, infoPtr->txt);
-  m++;
-  mask = 1;
+  if(!MONTHCAL_IsDateEqual(&infoPtr->focusedSel, &st_null))
+  {
+    RECT r;
+    MONTHCAL_CalcPosFromDay(infoPtr, &infoPtr->focusedSel, &r);
+    DrawFocusRect(hdc, &r);
+  }
+}
 
-  /* draw the first week of the current month */
-  while(i<7) {
-    MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
-    if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
-    {
-      MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->curSel.wMonth, i, 0,
-       infoPtr->monthdayState[m] & mask);
-    }
+/* months before first calendar month and after last calendar month */
+static void MONTHCAL_PaintLeadTrailMonths(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps)
+{
+  INT mask, length;
+  SYSTEMTIME st_max, st;
+
+  if (infoPtr->dwStyle & MCS_NOTRAILINGDATES) return;
 
-    mask<<=1;
-    day++;
-    i++;
+  SetTextColor(hdc, infoPtr->colors[MCSC_TRAILINGTEXT]);
+
+  /* draw prev month */
+  MONTHCAL_GetMinDate(infoPtr, &st);
+  mask = 1 << (st.wDay-1);
+  /* December and January both 31 days long, so no worries if wrapped */
+  length = MONTHCAL_MonthLength(infoPtr->calendars[0].month.wMonth - 1,
+                                infoPtr->calendars[0].month.wYear);
+  while(st.wDay <= length)
+  {
+      MONTHCAL_DrawDay(infoPtr, hdc, &st, infoPtr->monthdayState[0] & mask, ps);
+      mask <<= 1;
+      st.wDay++;
   }
 
-  j = 1; /* move to the 2nd week of the current month */
-  i = 0; /* move back to sunday */
-  while(day <= MONTHCAL_MonthLength(infoPtr->curSel.wMonth, infoPtr->curSel.wYear)) {
-    MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
-    if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
-    {
-      MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->curSel.wMonth, i, j,
-          infoPtr->monthdayState[m] & mask);
-    }
-    mask<<=1;
-    day++;
-    i++;
-    if(i>6) { /* past saturday, goto the next weeks sunday */
-      i = 0;
-      j++;
-    }
+  /* draw next month */
+  st = infoPtr->calendars[infoPtr->cal_num-1].month;
+  st.wDay = 1;
+  MONTHCAL_GetNextMonth(&st);
+  MONTHCAL_GetMaxDate(infoPtr, &st_max);
+  mask = 1;
+
+  while(st.wDay <= st_max.wDay)
+  {
+      MONTHCAL_DrawDay(infoPtr, hdc, &st, infoPtr->monthdayState[2] & mask, ps);
+      mask <<= 1;
+      st.wDay++;
   }
+}
+
+/* paint a calendar area */
+static void MONTHCAL_PaintCalendar(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps, INT calIdx)
+{
+  const SYSTEMTIME *date = &infoPtr->calendars[calIdx].month;
+  INT prev_month, i, j, length;
+  RECT r, fill_bk_rect;
+  SYSTEMTIME st;
+  WCHAR buf[80];
+  HPEN old_pen;
+  int mask;
 
-/*  draw `next' month */
+  /* fill whole days area - from week days area to today note rectangle */
+  fill_bk_rect = infoPtr->calendars[calIdx].wdays;
+  fill_bk_rect.bottom = infoPtr->calendars[calIdx].days.bottom +
+                          (infoPtr->todayrect.bottom - infoPtr->todayrect.top);
 
-  day = 1; /* start at the first day of the next month */
-  m++;
-  mask = 1;
+  FillRect(hdc, &fill_bk_rect, infoPtr->brushes[BrushMonth]);
 
-  SetTextColor(hdc, infoPtr->trailingtxt);
-  while((i<7) &&(j<6)) {
-    MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
-    if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
-    {
-      MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->curSel.wMonth + 1, i, j,
-               infoPtr->monthdayState[m] & mask);
-    }
+  /* draw line under day abbreviations */
+  old_pen = SelectObject(hdc, infoPtr->pens[PenText]);
+  MoveToEx(hdc, infoPtr->calendars[calIdx].days.left + 3,
+                infoPtr->calendars[calIdx].title.bottom + infoPtr->textHeight + 1, NULL);
+  LineTo(hdc, infoPtr->calendars[calIdx].days.right - 3,
+              infoPtr->calendars[calIdx].title.bottom + infoPtr->textHeight + 1);
+  SelectObject(hdc, old_pen);
 
-    mask<<=1;
-    day++;
-    i++;
-    if(i==7) { /* past saturday, go to next week's sunday */
-      i = 0;
-      j++;
-    }
+  prev_month = date->wMonth - 1;
+  if (prev_month == 0) prev_month = 12;
+
+  infoPtr->calendars[calIdx].wdays.left = infoPtr->calendars[calIdx].days.left =
+      infoPtr->calendars[calIdx].weeknums.right;
+
+  /* draw day abbreviations */
+  SelectObject(hdc, infoPtr->hFont);
+  SetBkColor(hdc, infoPtr->colors[MCSC_MONTHBK]);
+  SetTextColor(hdc, infoPtr->colors[MCSC_TITLEBK]);
+  /* rectangle to draw a single day abbreviation within */
+  r = infoPtr->calendars[calIdx].wdays;
+  r.right = r.left + infoPtr->width_increment;
+
+  i = infoPtr->firstDay;
+  for(j = 0; j < 7; j++) {
+    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1 + (i+j+6)%7, buf, countof(buf));
+    DrawTextW(hdc, buf, strlenW(buf), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+    OffsetRect(&r, infoPtr->width_increment, 0);
   }
-  SetTextColor(hdc, infoPtr->txt);
 
-  /* draw today mark rectangle */
-  if((infoPtr->curSel.wMonth == infoPtr->todaysDate.wMonth) &&
-     (infoPtr->curSel.wYear == infoPtr->todaysDate.wYear) &&
-    !(infoPtr->dwStyle & MCS_NOTODAYCIRCLE))
+  /* draw current month */
+  SetTextColor(hdc, infoPtr->colors[MCSC_TEXT]);
+  st = *date;
+  st.wDay = 1;
+  mask = 1;
+  length = MONTHCAL_MonthLength(date->wMonth, date->wYear);
+  while(st.wDay <= length)
   {
-    MONTHCAL_CircleDay(infoPtr, hdc, infoPtr->todaysDate.wDay, infoPtr->todaysDate.wMonth);
+    MONTHCAL_DrawDay(infoPtr, hdc, &st, infoPtr->monthdayState[1] & mask, ps);
+    mask <<= 1;
+    st.wDay++;
   }
+}
 
-  /* draw focused day */
-  if(!MONTHCAL_IsDateEqual(&infoPtr->focusedSel, &st_null))
+static void MONTHCAL_Refresh(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps)
+{
+  COLORREF old_text_clr, old_bk_clr;
+  HFONT old_font;
+  INT i;
+
+  old_text_clr = SetTextColor(hdc, comctl32_color.clrWindowText);
+  old_bk_clr   = GetBkColor(hdc);
+  old_font     = GetCurrentObject(hdc, OBJ_FONT);
+
+  for (i = 0; i < infoPtr->cal_num; i++)
   {
-    MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->focusedSel.wDay,
-                                     infoPtr->focusedSel.wMonth, &rcDay);
+    RECT *title = &infoPtr->calendars[i].title;
+    RECT r;
 
-    DrawFocusRect(hdc, &rcDay);
-  }
+    /* draw title, redraw all its elements */
+    if (IntersectRect(&r, &(ps->rcPaint), title))
+        MONTHCAL_PaintTitle(infoPtr, hdc, ps, i);
 
-  /* draw `today' date if style allows it, and draw a circle before today's
-   * date if necessary */
-  if(!(infoPtr->dwStyle & MCS_NOTODAY))  {
-    static const WCHAR todayW[] = { 'T','o','d','a','y',':',0 };
-    static const WCHAR fmt_todayW[] = { '%','s',' ','%','s',0 };
-    RECT rtoday;
-
-    if(!(infoPtr->dwStyle & MCS_NOTODAYCIRCLE))  {
-      /*day is the number of days from nextmonth we put on the calendar */
-      MONTHCAL_CircleDay(infoPtr, hdc,
-                        day+MONTHCAL_MonthLength(infoPtr->curSel.wMonth, infoPtr->curSel.wYear),
-                        infoPtr->curSel.wMonth);
-    }
-    if (!LoadStringW(COMCTL32_hModule, IDM_TODAY, buf1, countof(buf1)))
-    {
-       WARN("Can't load resource\n");
-       strcpyW(buf1, todayW);
-    }
-    MONTHCAL_CalcDayRect(infoPtr, &rtoday, 1, 6);
-    GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &infoPtr->todaysDate, NULL,
-                                                        buf2, countof(buf2));
-    wsprintfW(buf, fmt_todayW, buf1, buf2);
-    SelectObject(hdc, infoPtr->hBoldFont);
-
-    DrawTextW(hdc, buf, -1, &rtoday, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
-    if(IntersectRect(&rcTemp, &(ps->rcPaint), &rtoday))
-    {
-      DrawTextW(hdc, buf, -1, &rtoday, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
-    }
-    SelectObject(hdc, infoPtr->hFont);
-  }
-
-  /* eventually draw week numbers */
-  if(infoPtr->dwStyle & MCS_WEEKNUMBERS) {
-    static const WCHAR fmt_weekW[] = { '%','d',0 }; /* week numbers format */
-    int mindays, weeknum, weeknum1;
-
-    /* Rules what week to call the first week of a new year:
-       LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
-       The week containing Jan 1 is the first week of year
-       LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
-       First week of year must contain 4 days of the new year
-       LOCALE_IFIRSTWEEKOFYEAR == 1  (what contries?)
-       The first week of the year must contain only days of the new year
-    */
-    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, countof(buf));
-    weeknum = atoiW(buf);
-    switch (weeknum)
-    {
-      case 1: mindays = 6;
-       break;
-      case 2: mindays = 3;
-       break;
-      case 0:
-      default:
-       mindays = 0;
-    }
-    if (infoPtr->curSel.wMonth < 2)
-    {
-       /* calculate all those exceptions for january */
-       weeknum1 = MONTHCAL_CalculateDayOfWeek(1, 1, infoPtr->curSel.wYear);
-       if ((infoPtr->firstDay - weeknum1) % 7 > mindays)
-           weeknum = 1;
-       else
-       {
-           weeknum = 0;
-           for(i = 0; i < 11; i++)
-             weeknum += MONTHCAL_MonthLength(i+1, infoPtr->curSel.wYear - 1);
-
-           weeknum += startofprescal + 7;
-           weeknum /= 7;
-           weeknum1 = MONTHCAL_CalculateDayOfWeek(1, 1, infoPtr->curSel.wYear - 1);
-           if ((infoPtr->firstDay - weeknum1) % 7 > mindays) weeknum++;
-       }
-    }
-    else
-    {
-       weeknum = 0;
-       for(i = 0; i < prevMonth - 1; i++)
-         weeknum += MONTHCAL_MonthLength(i+1, infoPtr->curSel.wYear);
+    /* draw calendar area */
+    UnionRect(&r, &infoPtr->calendars[i].wdays, &infoPtr->todayrect);
+    if (IntersectRect(&r, &(ps->rcPaint), &r))
+        MONTHCAL_PaintCalendar(infoPtr, hdc, ps, i);
 
-       weeknum += startofprescal + 7;
-       weeknum /= 7;
-       weeknum1 = MONTHCAL_CalculateDayOfWeek(1, 1, infoPtr->curSel.wYear);
-       if ((infoPtr->firstDay - weeknum1) % 7 > mindays) weeknum++;
-    }
+    /* week numbers */
+    MONTHCAL_PaintWeeknumbers(infoPtr, hdc, ps, i);
+  }
 
-    dayrect = infoPtr->weeknums;
-    dayrect.bottom = dayrect.top + infoPtr->height_increment;
+  /* partially visible months */
+  MONTHCAL_PaintLeadTrailMonths(infoPtr, hdc, ps);
 
-    for(i = 0; i < 6; i++) {
-      if((i == 0) && (weeknum > 50))
-      {
-         wsprintfW(buf, fmt_weekW, weeknum);
-         weeknum = 0;
-      }
-      else if((i == 5) && (weeknum > 47))
-      {
-         wsprintfW(buf, fmt_weekW, 1);
-      }
-      else
-         wsprintfW(buf, fmt_weekW, weeknum + i);
+  /* focus and today rectangle */
+  MONTHCAL_PaintFocusAndCircle(infoPtr, hdc, ps);
 
-      DrawTextW(hdc, buf, -1, &dayrect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
-      dayrect.top    += infoPtr->height_increment;
-      dayrect.bottom += infoPtr->height_increment;
-    }
+  /* today at the bottom left */
+  MONTHCAL_PaintTodayTitle(infoPtr, hdc, ps);
 
-    MoveToEx(hdc, infoPtr->weeknums.right, infoPtr->weeknums.top + 3 , NULL);
-    LineTo(hdc,   infoPtr->weeknums.right, infoPtr->weeknums.bottom);
-  }
+  /* navigation buttons */
+  MONTHCAL_PaintButton(infoPtr, hdc, DIRECTION_BACKWARD);
+  MONTHCAL_PaintButton(infoPtr, hdc, DIRECTION_FORWARD);
 
-  /* currentFont was font at entering Refresh */
-  SetBkColor(hdc, oldBkColor);
-  SelectObject(hdc, currentFont);
-  SetTextColor(hdc, oldTextColor);
+  /* restore context */
+  SetBkColor(hdc, old_bk_clr);
+  SelectObject(hdc, old_font);
+  SetTextColor(hdc, old_text_clr);
 }
 
-
 static LRESULT
-MONTHCAL_GetMinReqRect(const MONTHCAL_INFO *infoPtr, LPRECT lpRect)
+MONTHCAL_GetMinReqRect(const MONTHCAL_INFO *infoPtr, RECT *rect)
 {
-  TRACE("rect %p\n", lpRect);
+  TRACE("rect %p\n", rect);
 
-  if(!lpRect) return FALSE;
+  if(!rect) return FALSE;
 
-  lpRect->left   = infoPtr->title.left;
-  lpRect->top    = infoPtr->title.top;
-  lpRect->right  = infoPtr->title.right;
-  lpRect->bottom = infoPtr->todayrect.bottom;
+  *rect = infoPtr->calendars[0].title;
+  rect->bottom = infoPtr->calendars[0].days.bottom + infoPtr->todayrect.bottom -
+                 infoPtr->todayrect.top;
 
-  AdjustWindowRect(lpRect, infoPtr->dwStyle, FALSE);
+  AdjustWindowRect(rect, infoPtr->dwStyle, FALSE);
 
   /* minimal rectangle is zero based */
-  OffsetRect(lpRect, -lpRect->left, -lpRect->top);
+  OffsetRect(rect, -rect->left, -rect->top);
 
-  TRACE("%s\n", wine_dbgstr_rect(lpRect));
+  TRACE("%s\n", wine_dbgstr_rect(rect));
 
   return TRUE;
 }
 
+static COLORREF
+MONTHCAL_GetColor(const MONTHCAL_INFO *infoPtr, UINT index)
+{
+  TRACE("%p, %d\n", infoPtr, index);
+
+  if (index > MCSC_TRAILINGTEXT) return -1;
+  return infoPtr->colors[index];
+}
 
 static LRESULT
-MONTHCAL_GetColor(const MONTHCAL_INFO *infoPtr, INT index)
+MONTHCAL_SetColor(MONTHCAL_INFO *infoPtr, UINT index, COLORREF color)
 {
-  TRACE("\n");
+  enum CachedBrush type;
+  COLORREF prev;
 
-  switch(index) {
-    case MCSC_BACKGROUND:
-      return infoPtr->bk;
-    case MCSC_TEXT:
-      return infoPtr->txt;
-    case MCSC_TITLEBK:
-      return infoPtr->titlebk;
-    case MCSC_TITLETEXT:
-      return infoPtr->titletxt;
-    case MCSC_MONTHBK:
-      return infoPtr->monthbk;
-    case MCSC_TRAILINGTEXT:
-      return infoPtr->trailingtxt;
-  }
+  TRACE("%p, %d: color %08x\n", infoPtr, index, color);
 
-  return -1;
-}
+  if (index > MCSC_TRAILINGTEXT) return -1;
 
+  prev = infoPtr->colors[index];
+  infoPtr->colors[index] = color;
 
-static LRESULT
-MONTHCAL_SetColor(MONTHCAL_INFO *infoPtr, INT index, COLORREF color)
-{
-  COLORREF prev = -1;
-
-  TRACE("%d: color %08x\n", index, color);
-
-  switch(index) {
-    case MCSC_BACKGROUND:
-      prev = infoPtr->bk;
-      infoPtr->bk = color;
-      break;
-    case MCSC_TEXT:
-      prev = infoPtr->txt;
-      infoPtr->txt = color;
-      break;
-    case MCSC_TITLEBK:
-      prev = infoPtr->titlebk;
-      infoPtr->titlebk = color;
-      break;
-    case MCSC_TITLETEXT:
-      prev=infoPtr->titletxt;
-      infoPtr->titletxt = color;
-      break;
-    case MCSC_MONTHBK:
-      prev = infoPtr->monthbk;
-      infoPtr->monthbk = color;
-      break;
-    case MCSC_TRAILINGTEXT:
-      prev = infoPtr->trailingtxt;
-      infoPtr->trailingtxt = color;
-      break;
+  /* update cached brush */
+  switch (index)
+  {
+  case MCSC_BACKGROUND:
+    type = BrushBackground;
+    break;
+  case MCSC_TITLEBK:
+    type = BrushTitle;
+    break;
+  case MCSC_MONTHBK:
+    type = BrushMonth;
+    break;
+  default:
+    type = BrushLast;
+  }
+
+  if (type != BrushLast)
+  {
+    DeleteObject(infoPtr->brushes[type]);
+    infoPtr->brushes[type] = CreateSolidBrush(color);
+  }
+
+  /* update cached pen */
+  if (index == MCSC_TEXT)
+  {
+    DeleteObject(infoPtr->pens[PenText]);
+    infoPtr->pens[PenText] = CreatePen(PS_SOLID, 1, infoPtr->colors[index]);
   }
 
   InvalidateRect(infoPtr->hwndSelf, NULL, index == MCSC_BACKGROUND ? TRUE : FALSE);
   return prev;
 }
 
-
 static LRESULT
 MONTHCAL_GetMonthDelta(const MONTHCAL_INFO *infoPtr)
 {
@@ -1215,7 +1264,8 @@ MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, INT day)
   {
     /* Native behaviour for that case is broken: invalid date number >31
        got displayed at (0,0) position, current month starts always from
-       (1,0) position. Should be implemnted here as well. */
+       (1,0) position. Should be implemented here as well only if there's
+       nothing else to do. */
     if (day < -1)
       FIXME("No bug compatibility for day=%d\n", day);
 
@@ -1235,36 +1285,32 @@ MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, INT day)
 static LRESULT
 MONTHCAL_GetMonthRange(const MONTHCAL_INFO *infoPtr, DWORD flag, SYSTEMTIME *st)
 {
-  TRACE("\n");
+  TRACE("flag=%d, st=%p\n", flag, st);
 
   if(st)
   {
     switch (flag) {
     case GMR_VISIBLE:
     {
-        /*FIXME: currently multicalendar feature isn't implemented, so entirely
-                 visible month is current */
-        st[0] = st[1] = infoPtr->curSel;
+        st[0] = infoPtr->calendars[0].month;
+        st[1] = infoPtr->calendars[infoPtr->cal_num-1].month;
 
-        if (infoPtr->curSel.wMonth == min_allowed_date.wMonth &&
-            infoPtr->curSel.wYear  == min_allowed_date.wYear)
+        if (st[0].wMonth == min_allowed_date.wMonth &&
+            st[0].wYear  == min_allowed_date.wYear)
         {
             st[0].wDay = min_allowed_date.wDay;
         }
         else
             st[0].wDay = 1;
-        st[0].wDayOfWeek = MONTHCAL_CalculateDayOfWeek(1, st[0].wMonth, st[0].wYear);
+        MONTHCAL_CalculateDayOfWeek(&st[0], TRUE);
 
         st[1].wDay = MONTHCAL_MonthLength(st[1].wMonth, st[1].wYear);
-        st[1].wDayOfWeek = MONTHCAL_CalculateDayOfWeek(st[1].wDay, st[1].wMonth,
-                                                       st[1].wYear);
-        /* a single current month used */
-        return 1;
+        MONTHCAL_CalculateDayOfWeek(&st[1], TRUE);
+
+        return infoPtr->cal_num;
     }
     case GMR_DAYSTATE:
     {
-        /*FIXME: currently multicalendar feature isn't implemented,
-                 min date from previous month and max date from next one returned */
         MONTHCAL_GetMinDate(infoPtr, &st[0]);
         MONTHCAL_GetMaxDate(infoPtr, &st[1]);
         break;
@@ -1359,13 +1405,10 @@ MONTHCAL_GetRange(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *range)
 static LRESULT
 MONTHCAL_SetDayState(const MONTHCAL_INFO *infoPtr, INT months, MONTHDAYSTATE *states)
 {
-  int i;
-
-  TRACE("%d %p\n", months, states);
+  TRACE("%p %d %p\n", infoPtr, months, states);
   if(months != infoPtr->monthRange) return 0;
 
-  for(i = 0; i < months; i++)
-    infoPtr->monthdayState[i] = states[i];
+  memcpy(infoPtr->monthdayState, states, months*sizeof(MONTHDAYSTATE));
 
   return 1;
 }
@@ -1377,32 +1420,47 @@ MONTHCAL_GetCurSel(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *curSel)
   if(!curSel) return FALSE;
   if(infoPtr->dwStyle & MCS_MULTISELECT) return FALSE;
 
-  *curSel = infoPtr->curSel;
+  *curSel = infoPtr->minSel;
   TRACE("%d/%d/%d\n", curSel->wYear, curSel->wMonth, curSel->wDay);
   return TRUE;
 }
 
-/* FIXME: if the specified date is not visible, make it visible */
 static LRESULT
 MONTHCAL_SetCurSel(MONTHCAL_INFO *infoPtr, SYSTEMTIME *curSel)
 {
+  SYSTEMTIME prev = infoPtr->minSel;
+  WORD day;
+
   TRACE("%p\n", curSel);
   if(!curSel) return FALSE;
   if(infoPtr->dwStyle & MCS_MULTISELECT) return FALSE;
 
   if(!MONTHCAL_ValidateDate(curSel)) return FALSE;
   /* exit earlier if selection equals current */
-  if (MONTHCAL_IsDateEqual(&infoPtr->curSel, curSel)) return TRUE;
+  if (MONTHCAL_IsDateEqual(&infoPtr->minSel, curSel)) return TRUE;
 
   if(!MONTHCAL_IsDateInValidRange(infoPtr, curSel, FALSE)) return FALSE;
 
+  infoPtr->calendars[0].month = *curSel;
   infoPtr->minSel = *curSel;
   infoPtr->maxSel = *curSel;
 
-  infoPtr->curSel = *curSel;
+  /* if selection is still in current month, reduce rectangle */
+  day = prev.wDay;
+  prev.wDay = curSel->wDay;
+  if (MONTHCAL_IsDateEqual(&prev, curSel))
+  {
+    RECT r_prev, r_new;
 
-  /* FIXME: it's possible to reduce rectangle here */
-  InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
+    prev.wDay = day;
+    MONTHCAL_CalcPosFromDay(infoPtr, &prev, &r_prev);
+    MONTHCAL_CalcPosFromDay(infoPtr, curSel, &r_new);
+
+    InvalidateRect(infoPtr->hwndSelf, &r_prev, FALSE);
+    InvalidateRect(infoPtr->hwndSelf, &r_new,  FALSE);
+  }
+  else
+    InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
 
   return TRUE;
 }
@@ -1482,14 +1540,11 @@ MONTHCAL_SetSelRange(MONTHCAL_INFO *infoPtr, SYSTEMTIME *range)
       infoPtr->minSel = range[1];
       infoPtr->maxSel = range[0];
     }
+    infoPtr->calendars[0].month = infoPtr->minSel;
 
     /* update day of week */
-    infoPtr->minSel.wDayOfWeek =
-            MONTHCAL_CalculateDayOfWeek(infoPtr->minSel.wDay, infoPtr->minSel.wMonth,
-                                                              infoPtr->minSel.wYear);
-    infoPtr->maxSel.wDayOfWeek =
-            MONTHCAL_CalculateDayOfWeek(infoPtr->maxSel.wDay, infoPtr->maxSel.wMonth,
-                                                              infoPtr->maxSel.wYear);
+    MONTHCAL_CalculateDayOfWeek(&infoPtr->minSel, TRUE);
+    MONTHCAL_CalculateDayOfWeek(&infoPtr->maxSel, TRUE);
 
     /* redraw if bounds changed */
     /* FIXME: no actual need to redraw everything */
@@ -1531,9 +1586,8 @@ MONTHCAL_UpdateToday(MONTHCAL_INFO *infoPtr, const SYSTEMTIME *today)
 
   if(MONTHCAL_IsDateEqual(today, &infoPtr->todaysDate)) return FALSE;
 
-  MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->todaysDate.wDay,
-                                   infoPtr->todaysDate.wMonth, &old_r);
-  MONTHCAL_CalcPosFromDay(infoPtr, today->wDay, today->wMonth, &new_r);
+  MONTHCAL_CalcPosFromDay(infoPtr, &infoPtr->todaysDate, &old_r);
+  MONTHCAL_CalcPosFromDay(infoPtr, today, &new_r);
 
   infoPtr->todaysDate = *today;
 
@@ -1557,19 +1611,55 @@ MONTHCAL_SetToday(MONTHCAL_INFO *infoPtr, const SYSTEMTIME *today)
   return TRUE;
 }
 
+/* returns calendar index containing specified point, or -1 if it's background */
+static INT MONTHCAL_GetCalendarFromPoint(const MONTHCAL_INFO *infoPtr, const POINT *pt)
+{
+  RECT r;
+  INT i;
+
+  for (i = 0; i < infoPtr->cal_num; i++)
+  {
+     /* whole bounding rectangle allows some optimization to compute */
+     r.left   = infoPtr->calendars[i].title.left;
+     r.top    = infoPtr->calendars[i].title.top;
+     r.bottom = infoPtr->calendars[i].days.bottom;
+     r.right  = infoPtr->calendars[i].days.right;
+
+     if (PtInRect(&r, *pt)) return i;
+  }
+
+  return -1;
+}
+
+static inline UINT fill_hittest_info(const MCHITTESTINFO *src, MCHITTESTINFO *dest)
+{
+  dest->uHit = src->uHit;
+  dest->st = src->st;
+
+  if (dest->cbSize == sizeof(MCHITTESTINFO))
+    memcpy(&dest->rc, &src->rc, sizeof(MCHITTESTINFO) - MCHITTESTINFO_V1_SIZE);
+
+  return src->uHit;
+}
+
 static LRESULT
 MONTHCAL_HitTest(const MONTHCAL_INFO *infoPtr, MCHITTESTINFO *lpht)
 {
-  UINT x,y;
-  DWORD retval;
-  int day,wday,wnum;
+  INT day, wday, wnum, calIdx;
+  MCHITTESTINFO htinfo;
+  SYSTEMTIME ht_month;
+  UINT x, y;
 
   if(!lpht || lpht->cbSize < MCHITTESTINFO_V1_SIZE) return -1;
 
   x = lpht->pt.x;
   y = lpht->pt.y;
 
-  ZeroMemory(&lpht->st, sizeof(lpht->st));
+  htinfo.st = st_null;
+
+  /* we should preserve passed fields if hit area doesn't need them */
+  if (lpht->cbSize == sizeof(MCHITTESTINFO))
+    memcpy(&htinfo.rc, &lpht->rc, sizeof(MCHITTESTINFO) - MCHITTESTINFO_V1_SIZE);
 
   /* Comment in for debugging...
   TRACE("%d %d wd[%d %d %d %d] d[%d %d %d %d] t[%d %d %d %d] wn[%d %d %d %d]\n", x, y,
@@ -1583,88 +1673,125 @@ MONTHCAL_HitTest(const MONTHCAL_INFO *infoPtr, MCHITTESTINFO *lpht)
        infoPtr->weeknums.top, infoPtr->weeknums.bottom);
   */
 
-  /* are we in the header? */
+  /* guess in what calendar we are */
+  calIdx = MONTHCAL_GetCalendarFromPoint(infoPtr, &lpht->pt);
+  if (calIdx == -1)
+  {
+    if (PtInRect(&infoPtr->todayrect, lpht->pt))
+    {
+      htinfo.uHit = MCHT_TODAYLINK;
+      htinfo.rc = infoPtr->todayrect;
+    }
+    else
+      /* outside of calendar area? What's left must be background :-) */
+      htinfo.uHit = MCHT_CALENDARBK;
+
+    return fill_hittest_info(&htinfo, lpht);
+  }
+
+  ht_month = infoPtr->calendars[calIdx].month;
 
-  if(PtInRect(&infoPtr->title, lpht->pt)) {
-    if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
-      retval = MCHT_TITLEBTNPREV;
-      goto done;
+  /* are we in the header? */
+  if (PtInRect(&infoPtr->calendars[calIdx].title, lpht->pt)) {
+    /* FIXME: buttons hittesting could be optimized cause maximum
+              two calendars have buttons */
+    if (calIdx == 0 && PtInRect(&infoPtr->titlebtnprev, lpht->pt))
+    {
+      htinfo.uHit = MCHT_TITLEBTNPREV;
+      htinfo.rc = infoPtr->titlebtnprev;
     }
-    if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
-      retval = MCHT_TITLEBTNNEXT;
-      goto done;
+    else if (PtInRect(&infoPtr->titlebtnnext, lpht->pt))
+    {
+      htinfo.uHit = MCHT_TITLEBTNNEXT;
+      htinfo.rc = infoPtr->titlebtnnext;
     }
-    if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
-      retval = MCHT_TITLEMONTH;
-      goto done;
+    else if (PtInRect(&infoPtr->calendars[calIdx].titlemonth, lpht->pt))
+    {
+      htinfo.uHit = MCHT_TITLEMONTH;
+      htinfo.rc = infoPtr->calendars[calIdx].titlemonth;
+      htinfo.iOffset = calIdx;
+    }
+    else if (PtInRect(&infoPtr->calendars[calIdx].titleyear, lpht->pt))
+    {
+      htinfo.uHit = MCHT_TITLEYEAR;
+      htinfo.rc = infoPtr->calendars[calIdx].titleyear;
+      htinfo.iOffset = calIdx;
     }
-    if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
-      retval = MCHT_TITLEYEAR;
-      goto done;
+    else
+    {
+      htinfo.uHit = MCHT_TITLE;
+      htinfo.rc = infoPtr->calendars[calIdx].title;
+      htinfo.iOffset = calIdx;
     }
 
-    retval = MCHT_TITLE;
-    goto done;
-  }
-
-  day = MONTHCAL_CalcDayFromPos(infoPtr,x,y,&wday,&wnum);
-  if(PtInRect(&infoPtr->wdays, lpht->pt)) {
-    retval = MCHT_CALENDARDAY;
-    lpht->st.wYear  = infoPtr->curSel.wYear;
-    lpht->st.wMonth = (day < 1)? infoPtr->curSel.wMonth -1 : infoPtr->curSel.wMonth;
-    lpht->st.wDay   = (day < 1)?
-      MONTHCAL_MonthLength(infoPtr->curSel.wMonth-1, infoPtr->curSel.wYear) -day : day;
-    goto done;
-  }
-  if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
-    retval = MCHT_CALENDARWEEKNUM;
-    lpht->st.wYear  = infoPtr->curSel.wYear;
-    lpht->st.wMonth = (day < 1) ? infoPtr->curSel.wMonth -1 :
-      (day > MONTHCAL_MonthLength(infoPtr->curSel.wMonth,infoPtr->curSel.wYear)) ?
-      infoPtr->curSel.wMonth +1 :infoPtr->curSel.wMonth;
-    lpht->st.wDay   = (day < 1 ) ?
-      MONTHCAL_MonthLength(infoPtr->curSel.wMonth-1,infoPtr->curSel.wYear) -day :
-      (day > MONTHCAL_MonthLength(infoPtr->curSel.wMonth,infoPtr->curSel.wYear)) ?
-      day - MONTHCAL_MonthLength(infoPtr->curSel.wMonth,infoPtr->curSel.wYear) : day;
-    goto done;
-  }
-  if(PtInRect(&infoPtr->days, lpht->pt))
+    return fill_hittest_info(&htinfo, lpht);
+  }
+
+  /* days area (including week days and week numbers */
+  day = MONTHCAL_CalcDayFromPos(infoPtr, x, y, &wday, &wnum);
+  if (PtInRect(&infoPtr->calendars[calIdx].wdays, lpht->pt))
+  {
+    htinfo.uHit = MCHT_CALENDARDAY;
+    htinfo.iOffset = calIdx;
+    htinfo.st.wYear  = ht_month.wYear;
+    htinfo.st.wMonth = (day < 1) ? ht_month.wMonth -1 : ht_month.wMonth;
+    htinfo.st.wDay   = (day < 1) ?
+      MONTHCAL_MonthLength(ht_month.wMonth-1, ht_month.wYear) - day : day;
+
+    MONTHCAL_CalcDayXY(infoPtr, &htinfo.st, &htinfo.iCol, &htinfo.iRow);
+  }
+  else if(PtInRect(&infoPtr->calendars[calIdx].weeknums, lpht->pt))
+  {
+    htinfo.uHit = MCHT_CALENDARWEEKNUM;
+    htinfo.st.wYear  = ht_month.wYear;
+    htinfo.iOffset = calIdx;
+
+    if (day < 1)
+    {
+      htinfo.st.wMonth = ht_month.wMonth - 1;
+      htinfo.st.wDay = MONTHCAL_MonthLength(ht_month.wMonth-1, ht_month.wYear) - day;
+    }
+    else if (day > MONTHCAL_MonthLength(ht_month.wMonth, ht_month.wYear))
+    {
+      htinfo.st.wMonth = ht_month.wMonth + 1;
+      htinfo.st.wDay = day - MONTHCAL_MonthLength(ht_month.wMonth, ht_month.wYear);
+    }
+    else
+    {
+      htinfo.st.wMonth = ht_month.wMonth;
+      htinfo.st.wDay = day;
+    }
+  }
+  else if(PtInRect(&infoPtr->calendars[calIdx].days, lpht->pt))
   {
-      lpht->st.wYear  = infoPtr->curSel.wYear;
-      lpht->st.wMonth = infoPtr->curSel.wMonth;
+      htinfo.iOffset = calIdx;
+      htinfo.st.wYear  = ht_month.wYear;
+      htinfo.st.wMonth = ht_month.wMonth;
       if (day < 1)
       {
-         retval = MCHT_CALENDARDATEPREV;
-         MONTHCAL_GetPrevMonth(&lpht->st);
-         lpht->st.wDay = MONTHCAL_MonthLength(lpht->st.wMonth, lpht->st.wYear) + day;
+         htinfo.uHit = MCHT_CALENDARDATEPREV;
+         MONTHCAL_GetPrevMonth(&htinfo.st);
+         htinfo.st.wDay = MONTHCAL_MonthLength(htinfo.st.wMonth, htinfo.st.wYear) + day;
       }
-      else if (day > MONTHCAL_MonthLength(infoPtr->curSel.wMonth, infoPtr->curSel.wYear))
+      else if (day > MONTHCAL_MonthLength(ht_month.wMonth, ht_month.wYear))
       {
-         retval = MCHT_CALENDARDATENEXT;
-         MONTHCAL_GetNextMonth(&lpht->st);
-         lpht->st.wDay = day - MONTHCAL_MonthLength(infoPtr->curSel.wMonth, infoPtr->curSel.wYear);
+         htinfo.uHit = MCHT_CALENDARDATENEXT;
+         MONTHCAL_GetNextMonth(&htinfo.st);
+         htinfo.st.wDay = day - MONTHCAL_MonthLength(ht_month.wMonth, ht_month.wYear);
       }
-      else {
-       retval = MCHT_CALENDARDATE;
-       lpht->st.wDay = day;
+      else
+      {
+       htinfo.uHit = MCHT_CALENDARDATE;
+       htinfo.st.wDay = day;
       }
+
+      MONTHCAL_CalcDayXY(infoPtr, &htinfo.st, &htinfo.iCol, &htinfo.iRow);
+      MONTHCAL_CalcDayRect(infoPtr, &htinfo.rc, htinfo.iCol, htinfo.iRow);
       /* always update day of week */
-      lpht->st.wDayOfWeek = MONTHCAL_CalculateDayOfWeek(lpht->st.wDay, lpht->st.wMonth,
-                                                             lpht->st.wYear);
-      goto done;
-  }
-  if(PtInRect(&infoPtr->todayrect, lpht->pt)) {
-    retval = MCHT_TODAYLINK;
-    goto done;
+      MONTHCAL_CalculateDayOfWeek(&htinfo.st, TRUE);
   }
 
-
-  /* Hit nothing special? What's left must be background :-) */
-
-  retval = MCHT_CALENDARBK;
- done:
-  lpht->uHit = retval;
-  return retval;
+  return fill_hittest_info(&htinfo, lpht);
 }
 
 /* MCN_GETDAYSTATE notification helper */
@@ -1672,7 +1799,6 @@ static void MONTHCAL_NotifyDayState(MONTHCAL_INFO *infoPtr)
 {
   if(infoPtr->dwStyle & MCS_DAYSTATE) {
     NMDAYSTATE nmds;
-    INT i;
 
     nmds.nmhdr.hwndFrom = infoPtr->hwndSelf;
     nmds.nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
@@ -1681,47 +1807,74 @@ static void MONTHCAL_NotifyDayState(MONTHCAL_INFO *infoPtr)
     nmds.prgDayState   = Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
 
     nmds.stStart = infoPtr->todaysDate;
-    nmds.stStart.wYear  = infoPtr->curSel.wYear;
-    nmds.stStart.wMonth = infoPtr->curSel.wMonth;
+    nmds.stStart.wYear  = infoPtr->minSel.wYear;
+    nmds.stStart.wMonth = infoPtr->minSel.wMonth;
     nmds.stStart.wDay = 1;
 
     SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmds.nmhdr.idFrom, (LPARAM)&nmds);
-    for(i = 0; i < infoPtr->monthRange; i++)
-      infoPtr->monthdayState[i] = nmds.prgDayState[i];
+    memcpy(infoPtr->monthdayState, nmds.prgDayState, infoPtr->monthRange*sizeof(MONTHDAYSTATE));
 
     Free(nmds.prgDayState);
   }
 }
 
-static void MONTHCAL_GoToNextMonth(MONTHCAL_INFO *infoPtr)
+/* no valid range check performed */
+static void MONTHCAL_Scroll(MONTHCAL_INFO *infoPtr, INT delta)
 {
-  SYSTEMTIME next = infoPtr->curSel;
+  INT i, selIdx = -1;
 
-  TRACE("\n");
+  for(i = 0; i < infoPtr->cal_num; i++)
+  {
+    /* save selection position to shift it later */
+    if (selIdx == -1 && MONTHCAL_CompareMonths(&infoPtr->minSel, &infoPtr->calendars[i].month) == 0)
+      selIdx = i;
 
-  MONTHCAL_GetNextMonth(&next);
+    MONTHCAL_GetMonth(&infoPtr->calendars[i].month, delta);
+  }
 
-  if(!MONTHCAL_IsDateInValidRange(infoPtr, &next, FALSE)) return;
+  /* selection is always shifted to first calendar */
+  if(infoPtr->dwStyle & MCS_MULTISELECT)
+  {
+    SYSTEMTIME range[2];
 
-  infoPtr->curSel = next;
+    MONTHCAL_GetSelRange(infoPtr, range);
+    MONTHCAL_GetMonth(&range[0], delta - selIdx);
+    MONTHCAL_GetMonth(&range[1], delta - selIdx);
+    MONTHCAL_SetSelRange(infoPtr, range);
+  }
+  else
+  {
+    SYSTEMTIME st = infoPtr->minSel;
 
-  MONTHCAL_NotifyDayState(infoPtr);
+    MONTHCAL_GetMonth(&st, delta - selIdx);
+    MONTHCAL_SetCurSel(infoPtr, &st);
+  }
 }
 
-
-static void MONTHCAL_GoToPrevMonth(MONTHCAL_INFO *infoPtr)
+static void MONTHCAL_GoToMonth(MONTHCAL_INFO *infoPtr, enum nav_direction direction)
 {
-  SYSTEMTIME prev = infoPtr->curSel;
-
-  TRACE("\n");
+  INT delta = infoPtr->delta ? infoPtr->delta : infoPtr->cal_num;
+  SYSTEMTIME st;
 
-  MONTHCAL_GetPrevMonth(&prev);
+  TRACE("%s\n", direction == DIRECTION_BACKWARD ? "back" : "fwd");
 
-  if(!MONTHCAL_IsDateInValidRange(infoPtr, &prev, FALSE)) return;
+  /* check if change allowed by range set */
+  if(direction == DIRECTION_BACKWARD)
+  {
+    st = infoPtr->calendars[0].month;
+    MONTHCAL_GetMonth(&st, -delta);
+  }
+  else
+  {
+    st = infoPtr->calendars[infoPtr->cal_num-1].month;
+    MONTHCAL_GetMonth(&st, delta);
+  }
 
-  infoPtr->curSel = prev;
+  if(!MONTHCAL_IsDateInValidRange(infoPtr, &st, FALSE)) return;
 
+  MONTHCAL_Scroll(infoPtr, direction == DIRECTION_BACKWARD ? -delta : delta);
   MONTHCAL_NotifyDayState(infoPtr);
+  MONTHCAL_NotifySelectionChange(infoPtr);
 }
 
 static LRESULT
@@ -1745,7 +1898,7 @@ MONTHCAL_RButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam)
   if( TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,
                     menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL))
   {
-      infoPtr->curSel = infoPtr->todaysDate;
+      infoPtr->calendars[0].month = infoPtr->todaysDate;
       infoPtr->minSel = infoPtr->todaysDate;
       infoPtr->maxSel = infoPtr->todaysDate;
       InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
@@ -1802,12 +1955,15 @@ static LRESULT CALLBACK EditWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
 }
 
 /* creates updown control and edit box */
-static void MONTHCAL_EditYear(MONTHCAL_INFO *infoPtr)
+static void MONTHCAL_EditYear(MONTHCAL_INFO *infoPtr, INT calIdx)
 {
+    RECT *rc = &infoPtr->calendars[calIdx].titleyear;
+    RECT *title = &infoPtr->calendars[calIdx].title;
+
     infoPtr->hWndYearEdit =
        CreateWindowExW(0, WC_EDITW, 0, WS_VISIBLE | WS_CHILD | ES_READONLY,
-                       infoPtr->titleyear.left + 3, infoPtr->titlebtnnext.top,
-                       infoPtr->titleyear.right - infoPtr->titleyear.left + 4,
+                       rc->left + 3, (title->bottom + title->top - infoPtr->textHeight) / 2,
+                       rc->right - rc->left + 4,
                        infoPtr->textHeight, infoPtr->hwndSelf,
                        NULL, NULL, NULL);
 
@@ -1816,7 +1972,7 @@ static void MONTHCAL_EditYear(MONTHCAL_INFO *infoPtr)
     infoPtr->hWndYearUpDown =
        CreateWindowExW(0, UPDOWN_CLASSW, 0,
                        WS_VISIBLE | WS_CHILD | UDS_SETBUDDYINT | UDS_NOTHOUSANDS | UDS_ARROWKEYS,
-                       infoPtr->titleyear.right + 7, infoPtr->titlebtnnext.top,
+                       rc->right + 7, (title->bottom + title->top - infoPtr->textHeight) / 2,
                        18, infoPtr->textHeight, infoPtr->hwndSelf,
                        NULL, NULL, NULL);
 
@@ -1824,7 +1980,7 @@ static void MONTHCAL_EditYear(MONTHCAL_INFO *infoPtr)
     SendMessageW(infoPtr->hWndYearUpDown, UDM_SETRANGE, 0,
                  MAKELONG(max_allowed_date.wYear, min_allowed_date.wYear));
     SendMessageW(infoPtr->hWndYearUpDown, UDM_SETBUDDY, (WPARAM)infoPtr->hWndYearEdit, 0);
-    SendMessageW(infoPtr->hWndYearUpDown, UDM_SETPOS, 0, infoPtr->curSel.wYear);
+    SendMessageW(infoPtr->hWndYearUpDown, UDM_SETPOS, 0, infoPtr->calendars[calIdx].month.wYear);
 
     /* subclass edit box */
     infoPtr->EditWndProc = (WNDPROC)SetWindowLongPtrW(infoPtr->hWndYearEdit,
@@ -1860,14 +2016,14 @@ MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
   switch(hit)
   {
   case MCHT_TITLEBTNNEXT:
-    MONTHCAL_GoToNextMonth(infoPtr);
+    MONTHCAL_GoToMonth(infoPtr, DIRECTION_FORWARD);
     infoPtr->status = MC_NEXTPRESSED;
     SetTimer(infoPtr->hwndSelf, MC_PREVNEXTMONTHTIMER, MC_PREVNEXTMONTHDELAY, 0);
     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
     return 0;
 
   case MCHT_TITLEBTNPREV:
-    MONTHCAL_GoToPrevMonth(infoPtr);
+    MONTHCAL_GoToMonth(infoPtr, DIRECTION_BACKWARD);
     infoPtr->status = MC_PREVPRESSED;
     SetTimer(infoPtr->hwndSelf, MC_PREVNEXTMONTHTIMER, MC_PREVNEXTMONTHDELAY, 0);
     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
@@ -1891,22 +2047,34 @@ MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
     i = TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD,
                       menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL);
 
-    if ((i > 0) && (i < 13) && infoPtr->curSel.wMonth != i)
+    if ((i > 0) && (i < 13) && infoPtr->calendars[ht.iOffset].month.wMonth != i)
     {
-       infoPtr->curSel.wMonth = i;
-       MONTHCAL_IsDateInValidRange(infoPtr, &infoPtr->curSel, TRUE);
-       InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
+        INT delta = i - infoPtr->calendars[ht.iOffset].month.wMonth;
+        SYSTEMTIME st;
+
+        /* check if change allowed by range set */
+        st = delta < 0 ? infoPtr->calendars[0].month :
+                         infoPtr->calendars[infoPtr->cal_num-1].month;
+        MONTHCAL_GetMonth(&st, delta);
+
+        if (MONTHCAL_IsDateInValidRange(infoPtr, &st, FALSE))
+        {
+            MONTHCAL_Scroll(infoPtr, delta);
+            MONTHCAL_NotifyDayState(infoPtr);
+            MONTHCAL_NotifySelectionChange(infoPtr);
+            InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
+        }
     }
     return 0;
   }
   case MCHT_TITLEYEAR:
   {
-    MONTHCAL_EditYear(infoPtr);
+    MONTHCAL_EditYear(infoPtr, ht.iOffset);
     return 0;
   }
   case MCHT_TODAYLINK:
   {
-    infoPtr->curSel = infoPtr->todaysDate;
+    infoPtr->calendars[0].month = infoPtr->todaysDate;
     infoPtr->minSel = infoPtr->todaysDate;
     infoPtr->maxSel = infoPtr->todaysDate;
     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
@@ -1919,17 +2087,13 @@ MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
   case MCHT_CALENDARDATEPREV:
   case MCHT_CALENDARDATE:
   {
-    MONTHCAL_CopyDate(&ht.st, &infoPtr->firstSel);
-
-    if(infoPtr->dwStyle & MCS_MULTISELECT)
-    {
-      SYSTEMTIME st[2];
+    SYSTEMTIME st[2];
 
-      st[0] = st[1] = ht.st;
+    MONTHCAL_CopyDate(&ht.st, &infoPtr->firstSel);
 
-      /* clear selection range */
-      MONTHCAL_SetSelRange(infoPtr, st);
-    }
+    st[0] = st[1] = ht.st;
+    /* clear selection range */
+    MONTHCAL_SetSelRange(infoPtr, st);
 
     infoPtr->status = MC_SEL_LBUTDOWN;
     MONTHCAL_SetDayFocus(infoPtr, &ht.st);
@@ -1982,17 +2146,10 @@ MONTHCAL_LButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam)
 
   if((hit & MCHT_CALENDARDATE) == MCHT_CALENDARDATE)
   {
-    SYSTEMTIME sel = infoPtr->curSel;
+    SYSTEMTIME sel = infoPtr->minSel;
 
-    if(!(infoPtr->dwStyle & MCS_MULTISELECT))
-    {
-        SYSTEMTIME st[2];
-
-        st[0] = st[1] = ht.st;
-        MONTHCAL_SetSelRange(infoPtr, st);
-        /* will be invalidated here */
-        MONTHCAL_SetCurSel(infoPtr, &st[0]);
-    }
+    /* will be invalidated here */
+    MONTHCAL_SetCurSel(infoPtr, &ht.st);
 
     /* send MCN_SELCHANGE only if new date selected */
     if (!MONTHCAL_IsDateEqual(&sel, &ht.st))
@@ -2012,8 +2169,8 @@ MONTHCAL_Timer(MONTHCAL_INFO *infoPtr, WPARAM id)
 
   switch(id) {
   case MC_PREVNEXTMONTHTIMER:
-    if(infoPtr->status & MC_NEXTPRESSED) MONTHCAL_GoToNextMonth(infoPtr);
-    if(infoPtr->status & MC_PREVPRESSED) MONTHCAL_GoToPrevMonth(infoPtr);
+    if(infoPtr->status & MC_NEXTPRESSED) MONTHCAL_GoToMonth(infoPtr, DIRECTION_FORWARD);
+    if(infoPtr->status & MC_PREVPRESSED) MONTHCAL_GoToMonth(infoPtr, DIRECTION_BACKWARD);
     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
     break;
   case MC_TODAYUPDATETIMER:
@@ -2043,7 +2200,7 @@ static LRESULT
 MONTHCAL_MouseMove(MONTHCAL_INFO *infoPtr, LPARAM lParam)
 {
   MCHITTESTINFO ht;
-  SYSTEMTIME old_focused, st_ht;
+  SYSTEMTIME st_ht;
   INT hit;
   RECT r;
 
@@ -2064,12 +2221,11 @@ MONTHCAL_MouseMove(MONTHCAL_INFO *infoPtr, LPARAM lParam)
   }
 
   st_ht = ht.st;
-  old_focused = infoPtr->focusedSel;
 
   /* if pointer is over focused day still there's nothing to do */
   if(!MONTHCAL_SetDayFocus(infoPtr, &ht.st)) return 0;
 
-  MONTHCAL_CalcPosFromDay(infoPtr, ht.st.wDay, ht.st.wMonth, &r);
+  MONTHCAL_CalcPosFromDay(infoPtr, &ht.st, &r);
 
   if(infoPtr->dwStyle & MCS_MULTISELECT) {
     SYSTEMTIME st[2];
@@ -2122,6 +2278,35 @@ MONTHCAL_Paint(MONTHCAL_INFO *infoPtr, HDC hdc_paint)
   return 0;
 }
 
+static LRESULT
+MONTHCAL_EraseBkgnd(const MONTHCAL_INFO *infoPtr, HDC hdc)
+{
+  RECT rc;
+
+  if (!GetClipBox(hdc, &rc)) return FALSE;
+
+  FillRect(hdc, &rc, infoPtr->brushes[BrushBackground]);
+
+  return TRUE;
+}
+
+static LRESULT
+MONTHCAL_PrintClient(MONTHCAL_INFO *infoPtr, HDC hdc, DWORD options)
+{
+  FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
+
+  if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
+      return 0;
+
+  if (options & PRF_ERASEBKGND)
+      MONTHCAL_EraseBkgnd(infoPtr, hdc);
+
+  if (options & PRF_CLIENT)
+      MONTHCAL_Paint(infoPtr, hdc);
+
+  return 0;
+}
+
 static LRESULT
 MONTHCAL_SetFocus(const MONTHCAL_INFO *infoPtr)
 {
@@ -2137,14 +2322,14 @@ static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr)
 {
   static const WCHAR O0W[] = { '0','0',0 };
   HDC hdc = GetDC(infoPtr->hwndSelf);
-  RECT *title=&infoPtr->title;
+  RECT *title=&infoPtr->calendars[0].title;
   RECT *prev=&infoPtr->titlebtnprev;
   RECT *next=&infoPtr->titlebtnnext;
-  RECT *titlemonth=&infoPtr->titlemonth;
-  RECT *titleyear=&infoPtr->titleyear;
-  RECT *wdays=&infoPtr->wdays;
-  RECT *weeknumrect=&infoPtr->weeknums;
-  RECT *days=&infoPtr->days;
+  RECT *titlemonth=&infoPtr->calendars[0].titlemonth;
+  RECT *titleyear=&infoPtr->calendars[0].titleyear;
+  RECT *wdays=&infoPtr->calendars[0].wdays;
+  RECT *weeknumrect=&infoPtr->calendars[0].weeknums;
+  RECT *days=&infoPtr->calendars[0].days;
   RECT *todayrect=&infoPtr->todayrect;
   SIZE size, sz;
   TEXTMETRICW tm;
@@ -2273,8 +2458,6 @@ static LRESULT MONTHCAL_Size(MONTHCAL_INFO *infoPtr, int Width, int Height)
   TRACE("(width=%d, height=%d)\n", Width, Height);
 
   MONTHCAL_UpdateSize(infoPtr);
-
-  /* invalidate client area and erase background */
   InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
 
   return 0;
@@ -2361,36 +2544,51 @@ MONTHCAL_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
   infoPtr = Alloc(sizeof(MONTHCAL_INFO));
   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
 
-  if(infoPtr == NULL) {
-    ERR( "could not allocate info memory!\n");
+  if (infoPtr == NULL) {
+    ERR("could not allocate info memory!\n");
     return 0;
   }
 
   infoPtr->hwndSelf = hwnd;
   infoPtr->hwndNotify = lpcs->hwndParent;
   infoPtr->dwStyle  = GetWindowLongW(hwnd, GWL_STYLE);
+  infoPtr->calendars = Alloc(sizeof(CALENDAR_INFO));
+  if (!infoPtr->calendars) goto fail;
+
+  infoPtr->cal_num = 1;
 
   MONTHCAL_SetFont(infoPtr, GetStockObject(DEFAULT_GUI_FONT), FALSE);
 
   /* initialize info structure */
-  /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
+  /* FIXME: calculate systemtime ->> localtime(subtract timezoneinfo) */
 
   GetLocalTime(&infoPtr->todaysDate);
   MONTHCAL_SetFirstDayOfWeek(infoPtr, -1);
 
   infoPtr->maxSelCount   = (infoPtr->dwStyle & MCS_MULTISELECT) ? 7 : 1;
   infoPtr->monthRange    = 3;
+
   infoPtr->monthdayState = Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
-  infoPtr->titlebk       = comctl32_color.clrActiveCaption;
-  infoPtr->titletxt      = comctl32_color.clrWindow;
-  infoPtr->monthbk       = comctl32_color.clrWindow;
-  infoPtr->trailingtxt   = comctl32_color.clrGrayText;
-  infoPtr->bk            = comctl32_color.clrWindow;
-  infoPtr->txt          = comctl32_color.clrWindowText;
+  if (!infoPtr->monthdayState) goto fail;
+
+  infoPtr->colors[MCSC_BACKGROUND]   = comctl32_color.clrWindow;
+  infoPtr->colors[MCSC_TEXT]         = comctl32_color.clrWindowText;
+  infoPtr->colors[MCSC_TITLEBK]      = comctl32_color.clrActiveCaption;
+  infoPtr->colors[MCSC_TITLETEXT]    = comctl32_color.clrWindow;
+  infoPtr->colors[MCSC_MONTHBK]      = comctl32_color.clrWindow;
+  infoPtr->colors[MCSC_TRAILINGTEXT] = comctl32_color.clrGrayText;
+
+  infoPtr->brushes[BrushBackground]  = CreateSolidBrush(infoPtr->colors[MCSC_BACKGROUND]);
+  infoPtr->brushes[BrushTitle]       = CreateSolidBrush(infoPtr->colors[MCSC_TITLEBK]);
+  infoPtr->brushes[BrushMonth]       = CreateSolidBrush(infoPtr->colors[MCSC_MONTHBK]);
+
+  infoPtr->pens[PenRed]  = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
+  infoPtr->pens[PenText] = CreatePen(PS_SOLID, 1, infoPtr->colors[MCSC_TEXT]);
 
   infoPtr->minSel = infoPtr->todaysDate;
   infoPtr->maxSel = infoPtr->todaysDate;
-  infoPtr->curSel = infoPtr->todaysDate;
+  infoPtr->calendars[0].month = infoPtr->todaysDate;
+  infoPtr->isUnicode = TRUE;
 
   /* call MONTHCAL_UpdateSize to set all of the dimensions */
   /* of the control */
@@ -2402,18 +2600,29 @@ MONTHCAL_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
   OpenThemeData (infoPtr->hwndSelf, themeClass);
 
   return 0;
-}
 
+fail:
+  Free(infoPtr->monthdayState);
+  Free(infoPtr->calendars);
+  Free(infoPtr);
+  return 0;
+}
 
 static LRESULT
 MONTHCAL_Destroy(MONTHCAL_INFO *infoPtr)
 {
+  INT i;
+
   /* free month calendar info data */
   Free(infoPtr->monthdayState);
+  Free(infoPtr->calendars);
   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
 
   CloseThemeData (GetWindowTheme (infoPtr->hwndSelf));
-  
+
+  for (i = 0; i < BrushLast; i++) DeleteObject(infoPtr->brushes[i]);
+  for (i = 0; i < PenLast; i++) DeleteObject(infoPtr->pens[i]);
+
   Free(infoPtr);
   return 0;
 }
@@ -2429,29 +2638,38 @@ MONTHCAL_Notify(MONTHCAL_INFO *infoPtr, NMHDR *hdr)
   {
     NMUPDOWN *nmud = (NMUPDOWN*)hdr;
 
-    if (hdr->hwndFrom == infoPtr->hWndYearUpDown)
+    if (hdr->hwndFrom == infoPtr->hWndYearUpDown && nmud->iDelta)
     {
       /* year value limits are set up explicitly after updown creation */
-      if ((nmud->iDelta + nmud->iPos) != infoPtr->curSel.wYear)
-      {
-        SYSTEMTIME new_date = infoPtr->curSel;
-
-        new_date.wYear = nmud->iDelta + nmud->iPos;
-        MONTHCAL_SetCurSel(infoPtr, &new_date);
-      }
+      MONTHCAL_Scroll(infoPtr, 12 * nmud->iDelta);
+      MONTHCAL_NotifyDayState(infoPtr);
+      MONTHCAL_NotifySelectionChange(infoPtr);
     }
   }
   return 0;
 }
 
+static inline BOOL
+MONTHCAL_SetUnicodeFormat(MONTHCAL_INFO *infoPtr, BOOL isUnicode)
+{
+  BOOL prev = infoPtr->isUnicode;
+  infoPtr->isUnicode = isUnicode;
+  return prev;
+}
+
+static inline BOOL
+MONTHCAL_GetUnicodeFormat(const MONTHCAL_INFO *infoPtr)
+{
+  return infoPtr->isUnicode;
+}
+
 static LRESULT WINAPI
 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
-  MONTHCAL_INFO *infoPtr;
+  MONTHCAL_INFO *infoPtr = (MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0);
 
   TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd, uMsg, wParam, lParam);
 
-  infoPtr = MONTHCAL_GetInfoPtr(hwnd);
   if (!infoPtr && (uMsg != WM_CREATE))
     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
   switch(uMsg)
@@ -2519,6 +2737,12 @@ MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
   case MCM_GETMAXTODAYWIDTH:
     return MONTHCAL_GetMaxTodayWidth(infoPtr);
 
+  case MCM_SETUNICODEFORMAT:
+    return MONTHCAL_SetUnicodeFormat(infoPtr, (BOOL)wParam);
+
+  case MCM_GETUNICODEFORMAT:
+    return MONTHCAL_GetUnicodeFormat(infoPtr);
+
   case WM_GETDLGCODE:
     return DLGC_WANTARROWS | DLGC_WANTCHARS;
 
@@ -2534,10 +2758,15 @@ MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
   case WM_LBUTTONUP:
     return MONTHCAL_LButtonUp(infoPtr, lParam);
 
-  case WM_PRINTCLIENT:
   case WM_PAINT:
     return MONTHCAL_Paint(infoPtr, (HDC)wParam);
 
+  case WM_PRINTCLIENT:
+    return MONTHCAL_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
+
+  case WM_ERASEBKGND:
+    return MONTHCAL_EraseBkgnd(infoPtr, (HDC)wParam);
+
   case WM_SETFOCUS:
     return MONTHCAL_SetFocus(infoPtr);