Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenmonthcal.c
Go to the documentation of this file.
00001 /* 00002 * Month calendar control 00003 * 00004 * Copyright 1998, 1999 Eric Kohl (ekohl@abo.rhein-zeitung.de) 00005 * Copyright 1999 Alex Priem (alexp@sci.kun.nl) 00006 * Copyright 1999 Chris Morgan <cmorgan@wpi.edu> and 00007 * James Abbatiello <abbeyj@wpi.edu> 00008 * Copyright 2000 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de> 00009 * Copyright 2009-2011 Nikolay Sivov 00010 * 00011 * This library is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU Lesser General Public 00013 * License as published by the Free Software Foundation; either 00014 * version 2.1 of the License, or (at your option) any later version. 00015 * 00016 * This library is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 * Lesser General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Lesser General Public 00022 * License along with this library; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00024 * 00025 * NOTE 00026 * 00027 * This code was audited for completeness against the documented features 00028 * of Comctl32.dll version 6.0 on Oct. 20, 2004, by Dimitrie O. Paun. 00029 * 00030 * Unless otherwise noted, we believe this code to be complete, as per 00031 * the specification mentioned above. 00032 * If you discover missing features, or bugs, please note them below. 00033 * 00034 * TODO: 00035 * -- MCM_[GS]ETUNICODEFORMAT 00036 * -- handle resources better (doesn't work now); 00037 * -- take care of internationalization. 00038 * -- keyboard handling. 00039 * -- search for FIXME 00040 */ 00041 00042 #include <math.h> 00043 #include <stdarg.h> 00044 #include <stdio.h> 00045 #include <stdlib.h> 00046 #include <string.h> 00047 00048 #include "windef.h" 00049 #include "winbase.h" 00050 #include "wingdi.h" 00051 #include "winuser.h" 00052 #include "winnls.h" 00053 #include "commctrl.h" 00054 #include "comctl32.h" 00055 #include "uxtheme.h" 00056 #include "vssym32.h" 00057 #include "wine/unicode.h" 00058 #include "wine/debug.h" 00059 00060 WINE_DEFAULT_DEBUG_CHANNEL(monthcal); 00061 00062 #define MC_SEL_LBUTUP 1 /* Left button released */ 00063 #define MC_SEL_LBUTDOWN 2 /* Left button pressed in calendar */ 00064 #define MC_PREVPRESSED 4 /* Prev month button pressed */ 00065 #define MC_NEXTPRESSED 8 /* Next month button pressed */ 00066 #define MC_PREVNEXTMONTHDELAY 350 /* when continuously pressing `next/prev 00067 month', wait 350 ms before going 00068 to the next/prev month */ 00069 #define MC_TODAYUPDATEDELAY 120000 /* time between today check for update (2 min) */ 00070 00071 #define MC_PREVNEXTMONTHTIMER 1 /* Timer IDs */ 00072 #define MC_TODAYUPDATETIMER 2 00073 00074 #define MC_CALENDAR_PADDING 6 00075 00076 #define countof(arr) (sizeof(arr)/sizeof(arr[0])) 00077 00078 /* convert from days to 100 nanoseconds unit - used as FILETIME unit */ 00079 #define DAYSTO100NSECS(days) (((ULONGLONG)(days))*24*60*60*10000000) 00080 00081 enum CachedPen 00082 { 00083 PenRed = 0, 00084 PenText, 00085 PenLast 00086 }; 00087 00088 enum CachedBrush 00089 { 00090 BrushTitle = 0, 00091 BrushMonth, 00092 BrushBackground, 00093 BrushLast 00094 }; 00095 00096 /* single calendar data */ 00097 typedef struct _CALENDAR_INFO 00098 { 00099 RECT title; /* rect for the header above the calendar */ 00100 RECT titlemonth; /* the 'month name' text in the header */ 00101 RECT titleyear; /* the 'year number' text in the header */ 00102 RECT wdays; /* week days at top */ 00103 RECT days; /* calendar area */ 00104 RECT weeknums; /* week numbers at left side */ 00105 00106 SYSTEMTIME month;/* contains calendar main month/year */ 00107 } CALENDAR_INFO; 00108 00109 typedef struct 00110 { 00111 HWND hwndSelf; 00112 DWORD dwStyle; /* cached GWL_STYLE */ 00113 00114 COLORREF colors[MCSC_TRAILINGTEXT+1]; 00115 HBRUSH brushes[BrushLast]; 00116 HPEN pens[PenLast]; 00117 00118 HFONT hFont; 00119 HFONT hBoldFont; 00120 int textHeight; 00121 int textWidth; 00122 int height_increment; 00123 int width_increment; 00124 INT delta; /* scroll rate; # of months that the */ 00125 /* control moves when user clicks a scroll button */ 00126 int visible; /* # of months visible */ 00127 int firstDay; /* Start month calendar with firstDay's day, 00128 stored in SYSTEMTIME format */ 00129 BOOL firstDaySet; /* first week day differs from locale defined */ 00130 00131 BOOL isUnicode; /* value set with MCM_SETUNICODE format */ 00132 00133 MONTHDAYSTATE *monthdayState; 00134 SYSTEMTIME todaysDate; 00135 BOOL todaySet; /* Today was forced with MCM_SETTODAY */ 00136 int status; /* See MC_SEL flags */ 00137 SYSTEMTIME firstSel; /* first selected day */ 00138 INT maxSelCount; 00139 SYSTEMTIME minSel; /* contains single selection when used without MCS_MULTISELECT */ 00140 SYSTEMTIME maxSel; 00141 SYSTEMTIME focusedSel; /* date currently focused with mouse movement */ 00142 DWORD rangeValid; 00143 SYSTEMTIME minDate; 00144 SYSTEMTIME maxDate; 00145 00146 RECT titlebtnnext; /* the `next month' button in the header */ 00147 RECT titlebtnprev; /* the `prev month' button in the header */ 00148 RECT todayrect; /* `today: xx/xx/xx' text rect */ 00149 HWND hwndNotify; /* Window to receive the notifications */ 00150 HWND hWndYearEdit; /* Window Handle of edit box to handle years */ 00151 HWND hWndYearUpDown;/* Window Handle of updown box to handle years */ 00152 WNDPROC EditWndProc; /* original Edit window procedure */ 00153 00154 CALENDAR_INFO *calendars; 00155 SIZE dim; /* [cx,cy] - dimensions of calendars matrix, row/column count */ 00156 } MONTHCAL_INFO, *LPMONTHCAL_INFO; 00157 00158 static const WCHAR themeClass[] = { 'S','c','r','o','l','l','b','a','r',0 }; 00159 00160 /* empty SYSTEMTIME const */ 00161 static const SYSTEMTIME st_null; 00162 /* valid date limits */ 00163 static const SYSTEMTIME max_allowed_date = { /* wYear */ 9999, /* wMonth */ 12, /* wDayOfWeek */ 0, /* wDay */ 31 }; 00164 static const SYSTEMTIME min_allowed_date = { /* wYear */ 1752, /* wMonth */ 9, /* wDayOfWeek */ 0, /* wDay */ 14 }; 00165 00166 /* Prev/Next buttons */ 00167 enum nav_direction 00168 { 00169 DIRECTION_BACKWARD, 00170 DIRECTION_FORWARD 00171 }; 00172 00173 /* helper functions */ 00174 static inline INT MONTHCAL_GetCalCount(const MONTHCAL_INFO *infoPtr) 00175 { 00176 return infoPtr->dim.cx * infoPtr->dim.cy; 00177 } 00178 00179 /* send a single MCN_SELCHANGE notification */ 00180 static inline void MONTHCAL_NotifySelectionChange(const MONTHCAL_INFO *infoPtr) 00181 { 00182 NMSELCHANGE nmsc; 00183 00184 nmsc.nmhdr.hwndFrom = infoPtr->hwndSelf; 00185 nmsc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 00186 nmsc.nmhdr.code = MCN_SELCHANGE; 00187 nmsc.stSelStart = infoPtr->minSel; 00188 nmsc.stSelEnd = infoPtr->maxSel; 00189 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmsc.nmhdr.idFrom, (LPARAM)&nmsc); 00190 } 00191 00192 /* send a single MCN_SELECT notification */ 00193 static inline void MONTHCAL_NotifySelect(const MONTHCAL_INFO *infoPtr) 00194 { 00195 NMSELCHANGE nmsc; 00196 00197 nmsc.nmhdr.hwndFrom = infoPtr->hwndSelf; 00198 nmsc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 00199 nmsc.nmhdr.code = MCN_SELECT; 00200 nmsc.stSelStart = infoPtr->minSel; 00201 nmsc.stSelEnd = infoPtr->maxSel; 00202 00203 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmsc.nmhdr.idFrom, (LPARAM)&nmsc); 00204 } 00205 00206 static inline int MONTHCAL_MonthDiff(const SYSTEMTIME *left, const SYSTEMTIME *right) 00207 { 00208 return (right->wYear - left->wYear)*12 + right->wMonth - left->wMonth; 00209 } 00210 00211 /* returns the number of days in any given month, checking for leap days */ 00212 /* January is 1, December is 12 */ 00213 int MONTHCAL_MonthLength(int month, int year) 00214 { 00215 const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 00216 /* Wrap around, this eases handling. Getting length only we shouldn't care 00217 about year change here cause January and December have 00218 the same day quantity */ 00219 if(month == 0) 00220 month = 12; 00221 else if(month == 13) 00222 month = 1; 00223 00224 /* special case for calendar transition year */ 00225 if(month == min_allowed_date.wMonth && year == min_allowed_date.wYear) return 19; 00226 00227 /* if we have a leap year add 1 day to February */ 00228 /* a leap year is a year either divisible by 400 */ 00229 /* or divisible by 4 and not by 100 */ 00230 if(month == 2) { /* February */ 00231 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) && 00232 (year%4 == 0)) ? 1 : 0); 00233 } 00234 else { 00235 return mdays[month - 1]; 00236 } 00237 } 00238 00239 /* compares timestamps using date part only */ 00240 static inline BOOL MONTHCAL_IsDateEqual(const SYSTEMTIME *first, const SYSTEMTIME *second) 00241 { 00242 return (first->wYear == second->wYear) && (first->wMonth == second->wMonth) && 00243 (first->wDay == second->wDay); 00244 } 00245 00246 /* make sure that date fields are valid */ 00247 static BOOL MONTHCAL_ValidateDate(const SYSTEMTIME *time) 00248 { 00249 if(time->wMonth < 1 || time->wMonth > 12 ) return FALSE; 00250 if(time->wDay > MONTHCAL_MonthLength(time->wMonth, time->wYear)) return FALSE; 00251 00252 return TRUE; 00253 } 00254 00255 /* Copies timestamp part only. 00256 * 00257 * PARAMETERS 00258 * 00259 * [I] from : source date 00260 * [O] to : dest date 00261 */ 00262 static void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to) 00263 { 00264 to->wHour = from->wHour; 00265 to->wMinute = from->wMinute; 00266 to->wSecond = from->wSecond; 00267 } 00268 00269 /* Copies date part only. 00270 * 00271 * PARAMETERS 00272 * 00273 * [I] from : source date 00274 * [O] to : dest date 00275 */ 00276 static void MONTHCAL_CopyDate(const SYSTEMTIME *from, SYSTEMTIME *to) 00277 { 00278 to->wYear = from->wYear; 00279 to->wMonth = from->wMonth; 00280 to->wDay = from->wDay; 00281 to->wDayOfWeek = from->wDayOfWeek; 00282 } 00283 00284 /* Compares two dates in SYSTEMTIME format 00285 * 00286 * PARAMETERS 00287 * 00288 * [I] first : pointer to valid first date data to compare 00289 * [I] second : pointer to valid second date data to compare 00290 * 00291 * RETURN VALUE 00292 * 00293 * -1 : first < second 00294 * 0 : first == second 00295 * 1 : first > second 00296 * 00297 * Note that no date validation performed, already validated values expected. 00298 */ 00299 static LONG MONTHCAL_CompareSystemTime(const SYSTEMTIME *first, const SYSTEMTIME *second) 00300 { 00301 FILETIME ft_first, ft_second; 00302 00303 SystemTimeToFileTime(first, &ft_first); 00304 SystemTimeToFileTime(second, &ft_second); 00305 00306 return CompareFileTime(&ft_first, &ft_second); 00307 } 00308 00309 static LONG MONTHCAL_CompareMonths(const SYSTEMTIME *first, const SYSTEMTIME *second) 00310 { 00311 SYSTEMTIME st_first, st_second; 00312 00313 st_first = st_second = st_null; 00314 MONTHCAL_CopyDate(first, &st_first); 00315 MONTHCAL_CopyDate(second, &st_second); 00316 st_first.wDay = st_second.wDay = 1; 00317 00318 return MONTHCAL_CompareSystemTime(&st_first, &st_second); 00319 } 00320 00321 static LONG MONTHCAL_CompareDate(const SYSTEMTIME *first, const SYSTEMTIME *second) 00322 { 00323 SYSTEMTIME st_first, st_second; 00324 00325 st_first = st_second = st_null; 00326 MONTHCAL_CopyDate(first, &st_first); 00327 MONTHCAL_CopyDate(second, &st_second); 00328 00329 return MONTHCAL_CompareSystemTime(&st_first, &st_second); 00330 } 00331 00332 /* Checks largest possible date range and configured one 00333 * 00334 * PARAMETERS 00335 * 00336 * [I] infoPtr : valid pointer to control data 00337 * [I] date : pointer to valid date data to check 00338 * [I] fix : make date fit valid range 00339 * 00340 * RETURN VALUE 00341 * 00342 * TRUE - date within largest and configured range 00343 * FALSE - date is outside largest or configured range 00344 */ 00345 static BOOL MONTHCAL_IsDateInValidRange(const MONTHCAL_INFO *infoPtr, 00346 SYSTEMTIME *date, BOOL fix) 00347 { 00348 const SYSTEMTIME *fix_st = NULL; 00349 00350 if(MONTHCAL_CompareSystemTime(date, &max_allowed_date) == 1) { 00351 fix_st = &max_allowed_date; 00352 } 00353 else if(MONTHCAL_CompareSystemTime(date, &min_allowed_date) == -1) { 00354 fix_st = &min_allowed_date; 00355 } 00356 else if(infoPtr->rangeValid & GDTR_MAX) { 00357 if((MONTHCAL_CompareSystemTime(date, &infoPtr->maxDate) == 1)) { 00358 fix_st = &infoPtr->maxDate; 00359 } 00360 } 00361 else if(infoPtr->rangeValid & GDTR_MIN) { 00362 if((MONTHCAL_CompareSystemTime(date, &infoPtr->minDate) == -1)) { 00363 fix_st = &infoPtr->minDate; 00364 } 00365 } 00366 00367 if (fix && fix_st) { 00368 date->wYear = fix_st->wYear; 00369 date->wMonth = fix_st->wMonth; 00370 } 00371 00372 return fix_st ? FALSE : TRUE; 00373 } 00374 00375 /* Checks passed range width with configured maximum selection count 00376 * 00377 * PARAMETERS 00378 * 00379 * [I] infoPtr : valid pointer to control data 00380 * [I] range0 : pointer to valid date data (requested bound) 00381 * [I] range1 : pointer to valid date data (primary bound) 00382 * [O] adjust : returns adjusted range bound to fit maximum range (optional) 00383 * 00384 * Adjust value computed basing on primary bound and current maximum selection 00385 * count. For simple range check (without adjusted value required) (range0, range1) 00386 * relation means nothing. 00387 * 00388 * RETURN VALUE 00389 * 00390 * TRUE - range is shorter or equal to maximum 00391 * FALSE - range is larger than maximum 00392 */ 00393 static BOOL MONTHCAL_IsSelRangeValid(const MONTHCAL_INFO *infoPtr, 00394 const SYSTEMTIME *range0, 00395 const SYSTEMTIME *range1, 00396 SYSTEMTIME *adjust) 00397 { 00398 ULARGE_INTEGER ul_range0, ul_range1, ul_diff; 00399 FILETIME ft_range0, ft_range1; 00400 LONG cmp; 00401 00402 SystemTimeToFileTime(range0, &ft_range0); 00403 SystemTimeToFileTime(range1, &ft_range1); 00404 00405 ul_range0.u.LowPart = ft_range0.dwLowDateTime; 00406 ul_range0.u.HighPart = ft_range0.dwHighDateTime; 00407 ul_range1.u.LowPart = ft_range1.dwLowDateTime; 00408 ul_range1.u.HighPart = ft_range1.dwHighDateTime; 00409 00410 cmp = CompareFileTime(&ft_range0, &ft_range1); 00411 00412 if(cmp == 1) 00413 ul_diff.QuadPart = ul_range0.QuadPart - ul_range1.QuadPart; 00414 else 00415 ul_diff.QuadPart = -ul_range0.QuadPart + ul_range1.QuadPart; 00416 00417 if(ul_diff.QuadPart >= DAYSTO100NSECS(infoPtr->maxSelCount)) { 00418 00419 if(adjust) { 00420 if(cmp == 1) 00421 ul_range0.QuadPart = ul_range1.QuadPart + DAYSTO100NSECS(infoPtr->maxSelCount - 1); 00422 else 00423 ul_range0.QuadPart = ul_range1.QuadPart - DAYSTO100NSECS(infoPtr->maxSelCount - 1); 00424 00425 ft_range0.dwLowDateTime = ul_range0.u.LowPart; 00426 ft_range0.dwHighDateTime = ul_range0.u.HighPart; 00427 FileTimeToSystemTime(&ft_range0, adjust); 00428 } 00429 00430 return FALSE; 00431 } 00432 else return TRUE; 00433 } 00434 00435 /* Used in MCM_SETRANGE/MCM_SETSELRANGE to determine resulting time part. 00436 Milliseconds are intentionally not validated. */ 00437 static BOOL MONTHCAL_ValidateTime(const SYSTEMTIME *time) 00438 { 00439 if((time->wHour > 24) || (time->wMinute > 59) || (time->wSecond > 59)) 00440 return FALSE; 00441 else 00442 return TRUE; 00443 } 00444 00445 /* Note:Depending on DST, this may be offset by a day. 00446 Need to find out if we're on a DST place & adjust the clock accordingly. 00447 Above function assumes we have a valid data. 00448 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12. 00449 0 = Sunday. 00450 */ 00451 00452 /* Returns the day in the week 00453 * 00454 * PARAMETERS 00455 * [i] date : input date 00456 * [I] inplace : set calculated value back to date structure 00457 * 00458 * RETURN VALUE 00459 * day of week in SYSTEMTIME format: (0 == sunday,..., 6 == saturday) 00460 */ 00461 int MONTHCAL_CalculateDayOfWeek(SYSTEMTIME *date, BOOL inplace) 00462 { 00463 SYSTEMTIME st = st_null; 00464 FILETIME ft; 00465 00466 MONTHCAL_CopyDate(date, &st); 00467 00468 SystemTimeToFileTime(&st, &ft); 00469 FileTimeToSystemTime(&ft, &st); 00470 00471 if (inplace) date->wDayOfWeek = st.wDayOfWeek; 00472 00473 return st.wDayOfWeek; 00474 } 00475 00476 /* add/subtract 'months' from date */ 00477 static inline void MONTHCAL_GetMonth(SYSTEMTIME *date, INT months) 00478 { 00479 INT length, m = date->wMonth + months; 00480 00481 date->wYear += m > 0 ? (m - 1) / 12 : m / 12 - 1; 00482 date->wMonth = m > 0 ? (m - 1) % 12 + 1 : 12 + m % 12; 00483 /* fix moving from last day in a month */ 00484 length = MONTHCAL_MonthLength(date->wMonth, date->wYear); 00485 if(date->wDay > length) date->wDay = length; 00486 MONTHCAL_CalculateDayOfWeek(date, TRUE); 00487 } 00488 00489 /* properly updates date to point on next month */ 00490 static inline void MONTHCAL_GetNextMonth(SYSTEMTIME *date) 00491 { 00492 MONTHCAL_GetMonth(date, 1); 00493 } 00494 00495 /* properly updates date to point on prev month */ 00496 static inline void MONTHCAL_GetPrevMonth(SYSTEMTIME *date) 00497 { 00498 MONTHCAL_GetMonth(date, -1); 00499 } 00500 00501 /* Returns full date for a first currently visible day */ 00502 static void MONTHCAL_GetMinDate(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *date) 00503 { 00504 /* zero indexed calendar has the earliest date */ 00505 SYSTEMTIME st_first = infoPtr->calendars[0].month; 00506 INT firstDay; 00507 00508 st_first.wDay = 1; 00509 firstDay = MONTHCAL_CalculateDayOfWeek(&st_first, FALSE); 00510 00511 *date = infoPtr->calendars[0].month; 00512 MONTHCAL_GetPrevMonth(date); 00513 00514 date->wDay = MONTHCAL_MonthLength(date->wMonth, date->wYear) + 00515 (infoPtr->firstDay - firstDay) % 7 + 1; 00516 00517 if(date->wDay > MONTHCAL_MonthLength(date->wMonth, date->wYear)) 00518 date->wDay -= 7; 00519 00520 /* fix day of week */ 00521 MONTHCAL_CalculateDayOfWeek(date, TRUE); 00522 } 00523 00524 /* Returns full date for a last currently visible day */ 00525 static void MONTHCAL_GetMaxDate(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *date) 00526 { 00527 /* the latest date is in latest calendar */ 00528 SYSTEMTIME st, *lt_month = &infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month; 00529 INT first_day; 00530 00531 *date = *lt_month; 00532 st = *lt_month; 00533 00534 /* day of week of first day of current month */ 00535 st.wDay = 1; 00536 first_day = MONTHCAL_CalculateDayOfWeek(&st, FALSE); 00537 00538 MONTHCAL_GetNextMonth(date); 00539 MONTHCAL_GetPrevMonth(&st); 00540 00541 /* last calendar starts with some date from previous month that not displayed */ 00542 st.wDay = MONTHCAL_MonthLength(st.wMonth, st.wYear) + 00543 (infoPtr->firstDay - first_day) % 7 + 1; 00544 if (st.wDay > MONTHCAL_MonthLength(st.wMonth, st.wYear)) st.wDay -= 7; 00545 00546 /* Use month length to get max day. 42 means max day count in calendar area */ 00547 date->wDay = 42 - (MONTHCAL_MonthLength(st.wMonth, st.wYear) - st.wDay + 1) - 00548 MONTHCAL_MonthLength(lt_month->wMonth, lt_month->wYear); 00549 00550 /* fix day of week */ 00551 MONTHCAL_CalculateDayOfWeek(date, TRUE); 00552 } 00553 00554 /* From a given point calculate the row, column and day in the calendar, 00555 'day == 0' means the last day of the last month. */ 00556 static int MONTHCAL_GetDayFromPos(const MONTHCAL_INFO *infoPtr, POINT pt, INT calIdx) 00557 { 00558 SYSTEMTIME st = infoPtr->calendars[calIdx].month; 00559 int firstDay, col, row; 00560 RECT client; 00561 00562 GetClientRect(infoPtr->hwndSelf, &client); 00563 00564 /* if the point is outside the x bounds of the window put it at the boundary */ 00565 if (pt.x > client.right) pt.x = client.right; 00566 00567 col = (pt.x - infoPtr->calendars[calIdx].days.left ) / infoPtr->width_increment; 00568 row = (pt.y - infoPtr->calendars[calIdx].days.top ) / infoPtr->height_increment; 00569 00570 st.wDay = 1; 00571 firstDay = (MONTHCAL_CalculateDayOfWeek(&st, FALSE) + 6 - infoPtr->firstDay) % 7; 00572 return col + 7 * row - firstDay; 00573 } 00574 00575 /* Get day position for given date and calendar 00576 * 00577 * PARAMETERS 00578 * 00579 * [I] infoPtr : pointer to control data 00580 * [I] date : date value 00581 * [O] col : day column (zero based) 00582 * [O] row : week column (zero based) 00583 * [I] calIdx : calendar index 00584 */ 00585 static void MONTHCAL_GetDayPos(const MONTHCAL_INFO *infoPtr, const SYSTEMTIME *date, 00586 INT *col, INT *row, INT calIdx) 00587 { 00588 SYSTEMTIME st = infoPtr->calendars[calIdx].month; 00589 INT first; 00590 00591 st.wDay = 1; 00592 first = (MONTHCAL_CalculateDayOfWeek(&st, FALSE) + 6 - infoPtr->firstDay) % 7; 00593 00594 if (calIdx == 0 || calIdx == MONTHCAL_GetCalCount(infoPtr)-1) { 00595 const SYSTEMTIME *cal = &infoPtr->calendars[calIdx].month; 00596 LONG cmp = MONTHCAL_CompareMonths(date, &st); 00597 00598 /* previous month */ 00599 if (cmp == -1) { 00600 *col = (first - MONTHCAL_MonthLength(date->wMonth, cal->wYear) + date->wDay) % 7; 00601 *row = 0; 00602 return; 00603 } 00604 00605 /* next month calculation is same as for current, just add current month length */ 00606 if (cmp == 1) 00607 first += MONTHCAL_MonthLength(cal->wMonth, cal->wYear); 00608 } 00609 00610 *col = (date->wDay + first) % 7; 00611 *row = (date->wDay + first - *col) / 7; 00612 } 00613 00614 /* returns bounding box for day in given position in given calendar */ 00615 static inline void MONTHCAL_GetDayRectI(const MONTHCAL_INFO *infoPtr, RECT *r, 00616 INT col, INT row, INT calIdx) 00617 { 00618 r->left = infoPtr->calendars[calIdx].days.left + col * infoPtr->width_increment; 00619 r->right = r->left + infoPtr->width_increment; 00620 r->top = infoPtr->calendars[calIdx].days.top + row * infoPtr->height_increment; 00621 r->bottom = r->top + infoPtr->textHeight; 00622 } 00623 00624 /* Returns bounding box for given date 00625 * 00626 * NOTE: when calendar index is unknown pass -1 00627 */ 00628 static inline void MONTHCAL_GetDayRect(const MONTHCAL_INFO *infoPtr, const SYSTEMTIME *date, 00629 RECT *r, INT calIdx) 00630 { 00631 INT col, row; 00632 00633 if (calIdx == -1) 00634 { 00635 INT cmp = MONTHCAL_CompareMonths(date, &infoPtr->calendars[0].month); 00636 00637 if (cmp <= 0) 00638 calIdx = 0; 00639 else 00640 { 00641 cmp = MONTHCAL_CompareMonths(date, &infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month); 00642 if (cmp >= 0) 00643 calIdx = MONTHCAL_GetCalCount(infoPtr)-1; 00644 else 00645 { 00646 for (calIdx = 1; calIdx < MONTHCAL_GetCalCount(infoPtr)-1; calIdx++) 00647 if (MONTHCAL_CompareMonths(date, &infoPtr->calendars[calIdx].month) == 0) 00648 break; 00649 } 00650 } 00651 } 00652 00653 MONTHCAL_GetDayPos(infoPtr, date, &col, &row, calIdx); 00654 MONTHCAL_GetDayRectI(infoPtr, r, col, row, calIdx); 00655 } 00656 00657 static LRESULT 00658 MONTHCAL_GetMonthRange(const MONTHCAL_INFO *infoPtr, DWORD flag, SYSTEMTIME *st) 00659 { 00660 INT range; 00661 00662 TRACE("flag=%d, st=%p\n", flag, st); 00663 00664 switch (flag) { 00665 case GMR_VISIBLE: 00666 { 00667 if (st) 00668 { 00669 st[0] = infoPtr->calendars[0].month; 00670 st[1] = infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month; 00671 00672 if (st[0].wMonth == min_allowed_date.wMonth && 00673 st[0].wYear == min_allowed_date.wYear) 00674 { 00675 st[0].wDay = min_allowed_date.wDay; 00676 } 00677 else 00678 st[0].wDay = 1; 00679 MONTHCAL_CalculateDayOfWeek(&st[0], TRUE); 00680 00681 st[1].wDay = MONTHCAL_MonthLength(st[1].wMonth, st[1].wYear); 00682 MONTHCAL_CalculateDayOfWeek(&st[1], TRUE); 00683 } 00684 00685 range = MONTHCAL_GetCalCount(infoPtr); 00686 break; 00687 } 00688 case GMR_DAYSTATE: 00689 { 00690 if (st) 00691 { 00692 MONTHCAL_GetMinDate(infoPtr, &st[0]); 00693 MONTHCAL_GetMaxDate(infoPtr, &st[1]); 00694 } 00695 /* include two partially visible months */ 00696 range = MONTHCAL_GetCalCount(infoPtr) + 2; 00697 break; 00698 } 00699 default: 00700 WARN("Unknown flag value, got %d\n", flag); 00701 range = 0; 00702 } 00703 00704 return range; 00705 } 00706 00707 /* Focused day helper: 00708 00709 - set focused date to given value; 00710 - reset to zero value if NULL passed; 00711 - invalidate previous and new day rectangle only if needed. 00712 00713 Returns TRUE if focused day changed, FALSE otherwise. 00714 */ 00715 static BOOL MONTHCAL_SetDayFocus(MONTHCAL_INFO *infoPtr, const SYSTEMTIME *st) 00716 { 00717 RECT r; 00718 00719 if(st) 00720 { 00721 /* there's nothing to do if it's the same date, 00722 mouse move within same date rectangle case */ 00723 if(MONTHCAL_IsDateEqual(&infoPtr->focusedSel, st)) return FALSE; 00724 00725 /* invalidate old focused day */ 00726 MONTHCAL_GetDayRect(infoPtr, &infoPtr->focusedSel, &r, -1); 00727 InvalidateRect(infoPtr->hwndSelf, &r, FALSE); 00728 00729 infoPtr->focusedSel = *st; 00730 } 00731 00732 MONTHCAL_GetDayRect(infoPtr, &infoPtr->focusedSel, &r, -1); 00733 00734 if(!st && MONTHCAL_ValidateDate(&infoPtr->focusedSel)) 00735 infoPtr->focusedSel = st_null; 00736 00737 /* on set invalidates new day, on reset clears previous focused day */ 00738 InvalidateRect(infoPtr->hwndSelf, &r, FALSE); 00739 00740 return TRUE; 00741 } 00742 00743 /* draw today boundary box for specified rectangle */ 00744 static void MONTHCAL_Circle(const MONTHCAL_INFO *infoPtr, HDC hdc, const RECT *r) 00745 { 00746 HPEN old_pen = SelectObject(hdc, infoPtr->pens[PenRed]); 00747 HBRUSH old_brush; 00748 00749 old_brush = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 00750 Rectangle(hdc, r->left, r->top, r->right, r->bottom); 00751 00752 SelectObject(hdc, old_brush); 00753 SelectObject(hdc, old_pen); 00754 } 00755 00756 /* Draw today day mark rectangle 00757 * 00758 * [I] hdc : context to draw in 00759 * [I] date : day to mark with rectangle 00760 * 00761 */ 00762 static void MONTHCAL_CircleDay(const MONTHCAL_INFO *infoPtr, HDC hdc, 00763 const SYSTEMTIME *date) 00764 { 00765 RECT r; 00766 00767 MONTHCAL_GetDayRect(infoPtr, date, &r, -1); 00768 MONTHCAL_Circle(infoPtr, hdc, &r); 00769 } 00770 00771 static void MONTHCAL_DrawDay(const MONTHCAL_INFO *infoPtr, HDC hdc, const SYSTEMTIME *st, 00772 int bold, const PAINTSTRUCT *ps) 00773 { 00774 static const WCHAR fmtW[] = { '%','d',0 }; 00775 WCHAR buf[10]; 00776 RECT r, r_temp; 00777 COLORREF oldCol = 0; 00778 COLORREF oldBk = 0; 00779 INT old_bkmode, selection; 00780 00781 /* no need to check styles: when selection is not valid, it is set to zero. 00782 1 < day < 31, so everything is OK */ 00783 MONTHCAL_GetDayRect(infoPtr, st, &r, -1); 00784 if(!IntersectRect(&r_temp, &(ps->rcPaint), &r)) return; 00785 00786 if ((MONTHCAL_CompareDate(st, &infoPtr->minSel) >= 0) && 00787 (MONTHCAL_CompareDate(st, &infoPtr->maxSel) <= 0)) 00788 { 00789 TRACE("%d %d %d\n", st->wDay, infoPtr->minSel.wDay, infoPtr->maxSel.wDay); 00790 TRACE("%s\n", wine_dbgstr_rect(&r)); 00791 oldCol = SetTextColor(hdc, infoPtr->colors[MCSC_MONTHBK]); 00792 oldBk = SetBkColor(hdc, infoPtr->colors[MCSC_TRAILINGTEXT]); 00793 FillRect(hdc, &r, infoPtr->brushes[BrushTitle]); 00794 00795 selection = 1; 00796 } 00797 else 00798 selection = 0; 00799 00800 SelectObject(hdc, bold ? infoPtr->hBoldFont : infoPtr->hFont); 00801 00802 old_bkmode = SetBkMode(hdc, TRANSPARENT); 00803 wsprintfW(buf, fmtW, st->wDay); 00804 DrawTextW(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE ); 00805 SetBkMode(hdc, old_bkmode); 00806 00807 if (selection) 00808 { 00809 SetTextColor(hdc, oldCol); 00810 SetBkColor(hdc, oldBk); 00811 } 00812 } 00813 00814 static void MONTHCAL_PaintButton(MONTHCAL_INFO *infoPtr, HDC hdc, enum nav_direction button) 00815 { 00816 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); 00817 RECT *r = button == DIRECTION_FORWARD ? &infoPtr->titlebtnnext : &infoPtr->titlebtnprev; 00818 BOOL pressed = button == DIRECTION_FORWARD ? infoPtr->status & MC_NEXTPRESSED : 00819 infoPtr->status & MC_PREVPRESSED; 00820 if (theme) 00821 { 00822 static const int states[] = { 00823 /* Prev button */ 00824 ABS_LEFTNORMAL, ABS_LEFTPRESSED, ABS_LEFTDISABLED, 00825 /* Next button */ 00826 ABS_RIGHTNORMAL, ABS_RIGHTPRESSED, ABS_RIGHTDISABLED 00827 }; 00828 int stateNum = button == DIRECTION_FORWARD ? 3 : 0; 00829 if (pressed) 00830 stateNum += 1; 00831 else 00832 { 00833 if (infoPtr->dwStyle & WS_DISABLED) stateNum += 2; 00834 } 00835 DrawThemeBackground (theme, hdc, SBP_ARROWBTN, states[stateNum], r, NULL); 00836 } 00837 else 00838 { 00839 int style = button == DIRECTION_FORWARD ? DFCS_SCROLLRIGHT : DFCS_SCROLLLEFT; 00840 if (pressed) 00841 style |= DFCS_PUSHED; 00842 else 00843 { 00844 if (infoPtr->dwStyle & WS_DISABLED) style |= DFCS_INACTIVE; 00845 } 00846 00847 DrawFrameControl(hdc, r, DFC_SCROLL, style); 00848 } 00849 } 00850 00851 /* paint a title with buttons and month/year string */ 00852 static void MONTHCAL_PaintTitle(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps, INT calIdx) 00853 { 00854 static const WCHAR fmt_monthW[] = { '%','s',' ','%','l','d',0 }; 00855 RECT *title = &infoPtr->calendars[calIdx].title; 00856 const SYSTEMTIME *st = &infoPtr->calendars[calIdx].month; 00857 WCHAR buf_month[80], buf_fmt[80]; 00858 SIZE sz; 00859 00860 /* fill header box */ 00861 FillRect(hdc, title, infoPtr->brushes[BrushTitle]); 00862 00863 /* month/year string */ 00864 SetBkColor(hdc, infoPtr->colors[MCSC_TITLEBK]); 00865 SetTextColor(hdc, infoPtr->colors[MCSC_TITLETEXT]); 00866 SelectObject(hdc, infoPtr->hBoldFont); 00867 00868 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1 + st->wMonth - 1, 00869 buf_month, countof(buf_month)); 00870 00871 wsprintfW(buf_fmt, fmt_monthW, buf_month, st->wYear); 00872 DrawTextW(hdc, buf_fmt, strlenW(buf_fmt), title, 00873 DT_CENTER | DT_VCENTER | DT_SINGLELINE); 00874 00875 /* update title rectangles with current month - used while testing hits */ 00876 GetTextExtentPoint32W(hdc, buf_fmt, strlenW(buf_fmt), &sz); 00877 infoPtr->calendars[calIdx].titlemonth.left = title->right / 2 + title->left / 2 - sz.cx / 2; 00878 infoPtr->calendars[calIdx].titleyear.right = title->right / 2 + title->left / 2 + sz.cx / 2; 00879 00880 GetTextExtentPoint32W(hdc, buf_month, strlenW(buf_month), &sz); 00881 infoPtr->calendars[calIdx].titlemonth.right = infoPtr->calendars[calIdx].titlemonth.left + sz.cx; 00882 infoPtr->calendars[calIdx].titleyear.left = infoPtr->calendars[calIdx].titlemonth.right; 00883 } 00884 00885 static void MONTHCAL_PaintWeeknumbers(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps, INT calIdx) 00886 { 00887 const SYSTEMTIME *date = &infoPtr->calendars[calIdx].month; 00888 static const WCHAR fmt_weekW[] = { '%','d',0 }; 00889 INT mindays, weeknum, weeknum1, startofprescal; 00890 INT i, prev_month; 00891 SYSTEMTIME st; 00892 WCHAR buf[80]; 00893 HPEN old_pen; 00894 RECT r; 00895 00896 if (!(infoPtr->dwStyle & MCS_WEEKNUMBERS)) return; 00897 00898 MONTHCAL_GetMinDate(infoPtr, &st); 00899 startofprescal = st.wDay; 00900 st = *date; 00901 00902 prev_month = date->wMonth - 1; 00903 if(prev_month == 0) prev_month = 12; 00904 00905 /* 00906 Rules what week to call the first week of a new year: 00907 LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?): 00908 The week containing Jan 1 is the first week of year 00909 LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany): 00910 First week of year must contain 4 days of the new year 00911 LOCALE_IFIRSTWEEKOFYEAR == 1 (what countries?) 00912 The first week of the year must contain only days of the new year 00913 */ 00914 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, countof(buf)); 00915 weeknum = atoiW(buf); 00916 switch (weeknum) 00917 { 00918 case 1: mindays = 6; 00919 break; 00920 case 2: mindays = 3; 00921 break; 00922 case 0: mindays = 0; 00923 break; 00924 default: 00925 WARN("Unknown LOCALE_IFIRSTWEEKOFYEAR value %d, defaulting to 0\n", weeknum); 00926 mindays = 0; 00927 } 00928 00929 if (date->wMonth == 1) 00930 { 00931 /* calculate all those exceptions for January */ 00932 st.wDay = st.wMonth = 1; 00933 weeknum1 = MONTHCAL_CalculateDayOfWeek(&st, FALSE); 00934 if ((infoPtr->firstDay - weeknum1) % 7 > mindays) 00935 weeknum = 1; 00936 else 00937 { 00938 weeknum = 0; 00939 for(i = 0; i < 11; i++) 00940 weeknum += MONTHCAL_MonthLength(i+1, date->wYear - 1); 00941 00942 weeknum += startofprescal + 7; 00943 weeknum /= 7; 00944 st.wYear -= 1; 00945 weeknum1 = MONTHCAL_CalculateDayOfWeek(&st, FALSE); 00946 if ((infoPtr->firstDay - weeknum1) % 7 > mindays) weeknum++; 00947 } 00948 } 00949 else 00950 { 00951 weeknum = 0; 00952 for(i = 0; i < prev_month - 1; i++) 00953 weeknum += MONTHCAL_MonthLength(i+1, date->wYear); 00954 00955 weeknum += startofprescal + 7; 00956 weeknum /= 7; 00957 st.wDay = st.wMonth = 1; 00958 weeknum1 = MONTHCAL_CalculateDayOfWeek(&st, FALSE); 00959 if ((infoPtr->firstDay - weeknum1) % 7 > mindays) weeknum++; 00960 } 00961 00962 r = infoPtr->calendars[calIdx].weeknums; 00963 00964 /* erase whole week numbers area */ 00965 FillRect(hdc, &r, infoPtr->brushes[BrushMonth]); 00966 SetTextColor(hdc, infoPtr->colors[MCSC_TITLEBK]); 00967 00968 /* reduce rectangle to one week number */ 00969 r.bottom = r.top + infoPtr->height_increment; 00970 00971 for(i = 0; i < 6; i++) { 00972 if((i == 0) && (weeknum > 50)) 00973 { 00974 wsprintfW(buf, fmt_weekW, weeknum); 00975 weeknum = 0; 00976 } 00977 else if((i == 5) && (weeknum > 47)) 00978 { 00979 wsprintfW(buf, fmt_weekW, 1); 00980 } 00981 else 00982 wsprintfW(buf, fmt_weekW, weeknum + i); 00983 00984 DrawTextW(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); 00985 OffsetRect(&r, 0, infoPtr->height_increment); 00986 } 00987 00988 /* line separator for week numbers column */ 00989 old_pen = SelectObject(hdc, infoPtr->pens[PenText]); 00990 MoveToEx(hdc, infoPtr->calendars[calIdx].weeknums.right, infoPtr->calendars[calIdx].weeknums.top + 3 , NULL); 00991 LineTo(hdc, infoPtr->calendars[calIdx].weeknums.right, infoPtr->calendars[calIdx].weeknums.bottom); 00992 SelectObject(hdc, old_pen); 00993 } 00994 00995 /* bottom today date */ 00996 static void MONTHCAL_PaintTodayTitle(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps) 00997 { 00998 static const WCHAR fmt_todayW[] = { '%','s',' ','%','s',0 }; 00999 WCHAR buf_todayW[30], buf_dateW[20], buf[80]; 01000 RECT text_rect, box_rect; 01001 HFONT old_font; 01002 INT col; 01003 01004 if(infoPtr->dwStyle & MCS_NOTODAY) return; 01005 01006 if (!LoadStringW(COMCTL32_hModule, IDM_TODAY, buf_todayW, countof(buf_todayW))) 01007 { 01008 static const WCHAR todayW[] = { 'T','o','d','a','y',':',0 }; 01009 WARN("Can't load resource\n"); 01010 strcpyW(buf_todayW, todayW); 01011 } 01012 01013 col = infoPtr->dwStyle & MCS_NOTODAYCIRCLE ? 0 : 1; 01014 if (infoPtr->dwStyle & MCS_WEEKNUMBERS) col--; 01015 /* label is located below first calendar last row */ 01016 MONTHCAL_GetDayRectI(infoPtr, &text_rect, col, 6, infoPtr->dim.cx * infoPtr->dim.cy - infoPtr->dim.cx); 01017 box_rect = text_rect; 01018 01019 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &infoPtr->todaysDate, NULL, 01020 buf_dateW, countof(buf_dateW)); 01021 old_font = SelectObject(hdc, infoPtr->hBoldFont); 01022 SetTextColor(hdc, infoPtr->colors[MCSC_TEXT]); 01023 01024 wsprintfW(buf, fmt_todayW, buf_todayW, buf_dateW); 01025 DrawTextW(hdc, buf, -1, &text_rect, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE); 01026 DrawTextW(hdc, buf, -1, &text_rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); 01027 01028 if(!(infoPtr->dwStyle & MCS_NOTODAYCIRCLE)) { 01029 OffsetRect(&box_rect, -infoPtr->width_increment, 0); 01030 MONTHCAL_Circle(infoPtr, hdc, &box_rect); 01031 } 01032 01033 SelectObject(hdc, old_font); 01034 } 01035 01036 /* today mark + focus */ 01037 static void MONTHCAL_PaintFocusAndCircle(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps) 01038 { 01039 /* circle today date if only it's in fully visible month */ 01040 if (!(infoPtr->dwStyle & MCS_NOTODAYCIRCLE)) 01041 { 01042 INT i; 01043 01044 for (i = 0; i < MONTHCAL_GetCalCount(infoPtr); i++) 01045 if (!MONTHCAL_CompareMonths(&infoPtr->todaysDate, &infoPtr->calendars[i].month)) 01046 { 01047 MONTHCAL_CircleDay(infoPtr, hdc, &infoPtr->todaysDate); 01048 break; 01049 } 01050 } 01051 01052 if (!MONTHCAL_IsDateEqual(&infoPtr->focusedSel, &st_null)) 01053 { 01054 RECT r; 01055 MONTHCAL_GetDayRect(infoPtr, &infoPtr->focusedSel, &r, -1); 01056 DrawFocusRect(hdc, &r); 01057 } 01058 } 01059 01060 /* months before first calendar month and after last calendar month */ 01061 static void MONTHCAL_PaintLeadTrailMonths(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps) 01062 { 01063 INT mask, length, index; 01064 SYSTEMTIME st_max, st; 01065 01066 if (infoPtr->dwStyle & MCS_NOTRAILINGDATES) return; 01067 01068 SetTextColor(hdc, infoPtr->colors[MCSC_TRAILINGTEXT]); 01069 01070 /* draw prev month */ 01071 MONTHCAL_GetMinDate(infoPtr, &st); 01072 mask = 1 << (st.wDay-1); 01073 /* December and January both 31 days long, so no worries if wrapped */ 01074 length = MONTHCAL_MonthLength(infoPtr->calendars[0].month.wMonth - 1, 01075 infoPtr->calendars[0].month.wYear); 01076 index = 0; 01077 while(st.wDay <= length) 01078 { 01079 MONTHCAL_DrawDay(infoPtr, hdc, &st, infoPtr->monthdayState[index] & mask, ps); 01080 mask <<= 1; 01081 st.wDay++; 01082 } 01083 01084 /* draw next month */ 01085 st = infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month; 01086 st.wDay = 1; 01087 MONTHCAL_GetNextMonth(&st); 01088 MONTHCAL_GetMaxDate(infoPtr, &st_max); 01089 mask = 1; 01090 index = MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0)-1; 01091 while(st.wDay <= st_max.wDay) 01092 { 01093 MONTHCAL_DrawDay(infoPtr, hdc, &st, infoPtr->monthdayState[index] & mask, ps); 01094 mask <<= 1; 01095 st.wDay++; 01096 } 01097 } 01098 01099 /* paint a calendar area */ 01100 static void MONTHCAL_PaintCalendar(const MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps, INT calIdx) 01101 { 01102 const SYSTEMTIME *date = &infoPtr->calendars[calIdx].month; 01103 INT i, j, length; 01104 RECT r, fill_bk_rect; 01105 SYSTEMTIME st; 01106 WCHAR buf[80]; 01107 HPEN old_pen; 01108 int mask; 01109 01110 /* fill whole days area - from week days area to today note rectangle */ 01111 fill_bk_rect = infoPtr->calendars[calIdx].wdays; 01112 fill_bk_rect.bottom = infoPtr->calendars[calIdx].days.bottom + 01113 (infoPtr->todayrect.bottom - infoPtr->todayrect.top); 01114 01115 FillRect(hdc, &fill_bk_rect, infoPtr->brushes[BrushMonth]); 01116 01117 /* draw line under day abbreviations */ 01118 old_pen = SelectObject(hdc, infoPtr->pens[PenText]); 01119 MoveToEx(hdc, infoPtr->calendars[calIdx].days.left + 3, 01120 infoPtr->calendars[calIdx].title.bottom + infoPtr->textHeight + 1, NULL); 01121 LineTo(hdc, infoPtr->calendars[calIdx].days.right - 3, 01122 infoPtr->calendars[calIdx].title.bottom + infoPtr->textHeight + 1); 01123 SelectObject(hdc, old_pen); 01124 01125 infoPtr->calendars[calIdx].wdays.left = infoPtr->calendars[calIdx].days.left = 01126 infoPtr->calendars[calIdx].weeknums.right; 01127 01128 /* draw day abbreviations */ 01129 SelectObject(hdc, infoPtr->hFont); 01130 SetBkColor(hdc, infoPtr->colors[MCSC_MONTHBK]); 01131 SetTextColor(hdc, infoPtr->colors[MCSC_TITLEBK]); 01132 /* rectangle to draw a single day abbreviation within */ 01133 r = infoPtr->calendars[calIdx].wdays; 01134 r.right = r.left + infoPtr->width_increment; 01135 01136 i = infoPtr->firstDay; 01137 for(j = 0; j < 7; j++) { 01138 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1 + (i+j+6)%7, buf, countof(buf)); 01139 DrawTextW(hdc, buf, strlenW(buf), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); 01140 OffsetRect(&r, infoPtr->width_increment, 0); 01141 } 01142 01143 /* draw current month */ 01144 SetTextColor(hdc, infoPtr->colors[MCSC_TEXT]); 01145 st = *date; 01146 st.wDay = 1; 01147 mask = 1; 01148 length = MONTHCAL_MonthLength(date->wMonth, date->wYear); 01149 while(st.wDay <= length) 01150 { 01151 MONTHCAL_DrawDay(infoPtr, hdc, &st, infoPtr->monthdayState[calIdx+1] & mask, ps); 01152 mask <<= 1; 01153 st.wDay++; 01154 } 01155 } 01156 01157 static void MONTHCAL_Refresh(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps) 01158 { 01159 COLORREF old_text_clr, old_bk_clr; 01160 HFONT old_font; 01161 INT i; 01162 01163 old_text_clr = SetTextColor(hdc, comctl32_color.clrWindowText); 01164 old_bk_clr = GetBkColor(hdc); 01165 old_font = GetCurrentObject(hdc, OBJ_FONT); 01166 01167 for (i = 0; i < MONTHCAL_GetCalCount(infoPtr); i++) 01168 { 01169 RECT *title = &infoPtr->calendars[i].title; 01170 RECT r; 01171 01172 /* draw title, redraw all its elements */ 01173 if (IntersectRect(&r, &(ps->rcPaint), title)) 01174 MONTHCAL_PaintTitle(infoPtr, hdc, ps, i); 01175 01176 /* draw calendar area */ 01177 UnionRect(&r, &infoPtr->calendars[i].wdays, &infoPtr->todayrect); 01178 if (IntersectRect(&r, &(ps->rcPaint), &r)) 01179 MONTHCAL_PaintCalendar(infoPtr, hdc, ps, i); 01180 01181 /* week numbers */ 01182 MONTHCAL_PaintWeeknumbers(infoPtr, hdc, ps, i); 01183 } 01184 01185 /* partially visible months */ 01186 MONTHCAL_PaintLeadTrailMonths(infoPtr, hdc, ps); 01187 01188 /* focus and today rectangle */ 01189 MONTHCAL_PaintFocusAndCircle(infoPtr, hdc, ps); 01190 01191 /* today at the bottom left */ 01192 MONTHCAL_PaintTodayTitle(infoPtr, hdc, ps); 01193 01194 /* navigation buttons */ 01195 MONTHCAL_PaintButton(infoPtr, hdc, DIRECTION_BACKWARD); 01196 MONTHCAL_PaintButton(infoPtr, hdc, DIRECTION_FORWARD); 01197 01198 /* restore context */ 01199 SetBkColor(hdc, old_bk_clr); 01200 SelectObject(hdc, old_font); 01201 SetTextColor(hdc, old_text_clr); 01202 } 01203 01204 static LRESULT 01205 MONTHCAL_GetMinReqRect(const MONTHCAL_INFO *infoPtr, RECT *rect) 01206 { 01207 TRACE("rect %p\n", rect); 01208 01209 if(!rect) return FALSE; 01210 01211 *rect = infoPtr->calendars[0].title; 01212 rect->bottom = infoPtr->calendars[0].days.bottom + infoPtr->todayrect.bottom - 01213 infoPtr->todayrect.top; 01214 01215 AdjustWindowRect(rect, infoPtr->dwStyle, FALSE); 01216 01217 /* minimal rectangle is zero based */ 01218 OffsetRect(rect, -rect->left, -rect->top); 01219 01220 TRACE("%s\n", wine_dbgstr_rect(rect)); 01221 01222 return TRUE; 01223 } 01224 01225 static COLORREF 01226 MONTHCAL_GetColor(const MONTHCAL_INFO *infoPtr, UINT index) 01227 { 01228 TRACE("%p, %d\n", infoPtr, index); 01229 01230 if (index > MCSC_TRAILINGTEXT) return -1; 01231 return infoPtr->colors[index]; 01232 } 01233 01234 static LRESULT 01235 MONTHCAL_SetColor(MONTHCAL_INFO *infoPtr, UINT index, COLORREF color) 01236 { 01237 enum CachedBrush type; 01238 COLORREF prev; 01239 01240 TRACE("%p, %d: color %08x\n", infoPtr, index, color); 01241 01242 if (index > MCSC_TRAILINGTEXT) return -1; 01243 01244 prev = infoPtr->colors[index]; 01245 infoPtr->colors[index] = color; 01246 01247 /* update cached brush */ 01248 switch (index) 01249 { 01250 case MCSC_BACKGROUND: 01251 type = BrushBackground; 01252 break; 01253 case MCSC_TITLEBK: 01254 type = BrushTitle; 01255 break; 01256 case MCSC_MONTHBK: 01257 type = BrushMonth; 01258 break; 01259 default: 01260 type = BrushLast; 01261 } 01262 01263 if (type != BrushLast) 01264 { 01265 DeleteObject(infoPtr->brushes[type]); 01266 infoPtr->brushes[type] = CreateSolidBrush(color); 01267 } 01268 01269 /* update cached pen */ 01270 if (index == MCSC_TEXT) 01271 { 01272 DeleteObject(infoPtr->pens[PenText]); 01273 infoPtr->pens[PenText] = CreatePen(PS_SOLID, 1, infoPtr->colors[index]); 01274 } 01275 01276 InvalidateRect(infoPtr->hwndSelf, NULL, index == MCSC_BACKGROUND ? TRUE : FALSE); 01277 return prev; 01278 } 01279 01280 static LRESULT 01281 MONTHCAL_GetMonthDelta(const MONTHCAL_INFO *infoPtr) 01282 { 01283 TRACE("\n"); 01284 01285 if(infoPtr->delta) 01286 return infoPtr->delta; 01287 else 01288 return infoPtr->visible; 01289 } 01290 01291 01292 static LRESULT 01293 MONTHCAL_SetMonthDelta(MONTHCAL_INFO *infoPtr, INT delta) 01294 { 01295 INT prev = infoPtr->delta; 01296 01297 TRACE("delta %d\n", delta); 01298 01299 infoPtr->delta = delta; 01300 return prev; 01301 } 01302 01303 01304 static inline LRESULT 01305 MONTHCAL_GetFirstDayOfWeek(const MONTHCAL_INFO *infoPtr) 01306 { 01307 int day; 01308 01309 /* convert from SYSTEMTIME to locale format */ 01310 day = (infoPtr->firstDay >= 0) ? (infoPtr->firstDay+6)%7 : infoPtr->firstDay; 01311 01312 return MAKELONG(day, infoPtr->firstDaySet); 01313 } 01314 01315 01316 /* Sets the first day of the week that will appear in the control 01317 * 01318 * 01319 * PARAMETERS: 01320 * [I] infoPtr : valid pointer to control data 01321 * [I] day : day number to set as new first day (0 == Monday,...,6 == Sunday) 01322 * 01323 * 01324 * RETURN VALUE: 01325 * Low word contains previous first day, 01326 * high word indicates was first day forced with this message before or is 01327 * locale defined (TRUE - was forced, FALSE - wasn't). 01328 * 01329 * FIXME: this needs to be implemented properly in MONTHCAL_Refresh() 01330 * FIXME: we need more error checking here 01331 */ 01332 static LRESULT 01333 MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, INT day) 01334 { 01335 LRESULT prev = MONTHCAL_GetFirstDayOfWeek(infoPtr); 01336 int new_day; 01337 01338 TRACE("%d\n", day); 01339 01340 if(day == -1) 01341 { 01342 WCHAR buf[80]; 01343 01344 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, buf, countof(buf)); 01345 TRACE("%s %d\n", debugstr_w(buf), strlenW(buf)); 01346 01347 new_day = atoiW(buf); 01348 01349 infoPtr->firstDaySet = FALSE; 01350 } 01351 else if(day >= 7) 01352 { 01353 new_day = 6; /* max first day allowed */ 01354 infoPtr->firstDaySet = TRUE; 01355 } 01356 else 01357 { 01358 /* Native behaviour for that case is broken: invalid date number >31 01359 got displayed at (0,0) position, current month starts always from 01360 (1,0) position. Should be implemented here as well only if there's 01361 nothing else to do. */ 01362 if (day < -1) 01363 FIXME("No bug compatibility for day=%d\n", day); 01364 01365 new_day = day; 01366 infoPtr->firstDaySet = TRUE; 01367 } 01368 01369 /* convert from locale to SYSTEMTIME format */ 01370 infoPtr->firstDay = (new_day >= 0) ? (++new_day) % 7 : new_day; 01371 01372 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 01373 01374 return prev; 01375 } 01376 01377 static LRESULT 01378 MONTHCAL_GetMaxTodayWidth(const MONTHCAL_INFO *infoPtr) 01379 { 01380 return(infoPtr->todayrect.right - infoPtr->todayrect.left); 01381 } 01382 01383 static LRESULT 01384 MONTHCAL_SetRange(MONTHCAL_INFO *infoPtr, SHORT limits, SYSTEMTIME *range) 01385 { 01386 FILETIME ft_min, ft_max; 01387 01388 TRACE("%x %p\n", limits, range); 01389 01390 if ((limits & GDTR_MIN && !MONTHCAL_ValidateDate(&range[0])) || 01391 (limits & GDTR_MAX && !MONTHCAL_ValidateDate(&range[1]))) 01392 return FALSE; 01393 01394 if (limits & GDTR_MIN) 01395 { 01396 if (!MONTHCAL_ValidateTime(&range[0])) 01397 MONTHCAL_CopyTime(&infoPtr->todaysDate, &range[0]); 01398 01399 infoPtr->minDate = range[0]; 01400 infoPtr->rangeValid |= GDTR_MIN; 01401 } 01402 if (limits & GDTR_MAX) 01403 { 01404 if (!MONTHCAL_ValidateTime(&range[1])) 01405 MONTHCAL_CopyTime(&infoPtr->todaysDate, &range[1]); 01406 01407 infoPtr->maxDate = range[1]; 01408 infoPtr->rangeValid |= GDTR_MAX; 01409 } 01410 01411 /* Only one limit set - we are done */ 01412 if ((infoPtr->rangeValid & (GDTR_MIN | GDTR_MAX)) != (GDTR_MIN | GDTR_MAX)) 01413 return TRUE; 01414 01415 SystemTimeToFileTime(&infoPtr->maxDate, &ft_max); 01416 SystemTimeToFileTime(&infoPtr->minDate, &ft_min); 01417 01418 if (CompareFileTime(&ft_min, &ft_max) >= 0) 01419 { 01420 if ((limits & (GDTR_MIN | GDTR_MAX)) == (GDTR_MIN | GDTR_MAX)) 01421 { 01422 /* Native swaps limits only when both limits are being set. */ 01423 SYSTEMTIME st_tmp = infoPtr->minDate; 01424 infoPtr->minDate = infoPtr->maxDate; 01425 infoPtr->maxDate = st_tmp; 01426 } 01427 else 01428 { 01429 /* reset the other limit */ 01430 if (limits & GDTR_MIN) infoPtr->maxDate = st_null; 01431 if (limits & GDTR_MAX) infoPtr->minDate = st_null; 01432 infoPtr->rangeValid &= limits & GDTR_MIN ? ~GDTR_MAX : ~GDTR_MIN; 01433 } 01434 } 01435 01436 return TRUE; 01437 } 01438 01439 01440 static LRESULT 01441 MONTHCAL_GetRange(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *range) 01442 { 01443 TRACE("%p\n", range); 01444 01445 if(!range) return FALSE; 01446 01447 range[1] = infoPtr->maxDate; 01448 range[0] = infoPtr->minDate; 01449 01450 return infoPtr->rangeValid; 01451 } 01452 01453 01454 static LRESULT 01455 MONTHCAL_SetDayState(const MONTHCAL_INFO *infoPtr, INT months, MONTHDAYSTATE *states) 01456 { 01457 TRACE("%p %d %p\n", infoPtr, months, states); 01458 01459 if (!(infoPtr->dwStyle & MCS_DAYSTATE)) return 0; 01460 if (months != MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0)) return 0; 01461 01462 memcpy(infoPtr->monthdayState, states, months*sizeof(MONTHDAYSTATE)); 01463 01464 return 1; 01465 } 01466 01467 static LRESULT 01468 MONTHCAL_GetCurSel(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *curSel) 01469 { 01470 TRACE("%p\n", curSel); 01471 if(!curSel) return FALSE; 01472 if(infoPtr->dwStyle & MCS_MULTISELECT) return FALSE; 01473 01474 *curSel = infoPtr->minSel; 01475 TRACE("%d/%d/%d\n", curSel->wYear, curSel->wMonth, curSel->wDay); 01476 return TRUE; 01477 } 01478 01479 static LRESULT 01480 MONTHCAL_SetCurSel(MONTHCAL_INFO *infoPtr, SYSTEMTIME *curSel) 01481 { 01482 SYSTEMTIME prev = infoPtr->minSel, selection; 01483 INT diff; 01484 WORD day; 01485 01486 TRACE("%p\n", curSel); 01487 if(!curSel) return FALSE; 01488 if(infoPtr->dwStyle & MCS_MULTISELECT) return FALSE; 01489 01490 if(!MONTHCAL_ValidateDate(curSel)) return FALSE; 01491 /* exit earlier if selection equals current */ 01492 if (MONTHCAL_IsDateEqual(&infoPtr->minSel, curSel)) return TRUE; 01493 01494 selection = *curSel; 01495 selection.wHour = selection.wMinute = selection.wSecond = selection.wMilliseconds = 0; 01496 MONTHCAL_CalculateDayOfWeek(&selection, TRUE); 01497 01498 if(!MONTHCAL_IsDateInValidRange(infoPtr, &selection, FALSE)) return FALSE; 01499 01500 /* scroll calendars only if we have to */ 01501 diff = MONTHCAL_MonthDiff(&infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month, curSel); 01502 if (diff <= 0) 01503 { 01504 diff = MONTHCAL_MonthDiff(&infoPtr->calendars[0].month, curSel); 01505 if (diff > 0) diff = 0; 01506 } 01507 01508 if (diff != 0) 01509 { 01510 INT i; 01511 01512 for (i = 0; i < MONTHCAL_GetCalCount(infoPtr); i++) 01513 MONTHCAL_GetMonth(&infoPtr->calendars[i].month, diff); 01514 } 01515 01516 /* we need to store time part as it is */ 01517 selection = *curSel; 01518 MONTHCAL_CalculateDayOfWeek(&selection, TRUE); 01519 infoPtr->minSel = infoPtr->maxSel = selection; 01520 01521 /* if selection is still in current month, reduce rectangle */ 01522 day = prev.wDay; 01523 prev.wDay = curSel->wDay; 01524 if (MONTHCAL_IsDateEqual(&prev, curSel)) 01525 { 01526 RECT r_prev, r_new; 01527 01528 prev.wDay = day; 01529 MONTHCAL_GetDayRect(infoPtr, &prev, &r_prev, -1); 01530 MONTHCAL_GetDayRect(infoPtr, curSel, &r_new, -1); 01531 01532 InvalidateRect(infoPtr->hwndSelf, &r_prev, FALSE); 01533 InvalidateRect(infoPtr->hwndSelf, &r_new, FALSE); 01534 } 01535 else 01536 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 01537 01538 return TRUE; 01539 } 01540 01541 01542 static LRESULT 01543 MONTHCAL_GetMaxSelCount(const MONTHCAL_INFO *infoPtr) 01544 { 01545 return infoPtr->maxSelCount; 01546 } 01547 01548 01549 static LRESULT 01550 MONTHCAL_SetMaxSelCount(MONTHCAL_INFO *infoPtr, INT max) 01551 { 01552 TRACE("%d\n", max); 01553 01554 if(!(infoPtr->dwStyle & MCS_MULTISELECT)) return FALSE; 01555 if(max <= 0) return FALSE; 01556 01557 infoPtr->maxSelCount = max; 01558 01559 return TRUE; 01560 } 01561 01562 01563 static LRESULT 01564 MONTHCAL_GetSelRange(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *range) 01565 { 01566 TRACE("%p\n", range); 01567 01568 if(!range) return FALSE; 01569 01570 if(infoPtr->dwStyle & MCS_MULTISELECT) 01571 { 01572 range[1] = infoPtr->maxSel; 01573 range[0] = infoPtr->minSel; 01574 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay); 01575 return TRUE; 01576 } 01577 01578 return FALSE; 01579 } 01580 01581 01582 static LRESULT 01583 MONTHCAL_SetSelRange(MONTHCAL_INFO *infoPtr, SYSTEMTIME *range) 01584 { 01585 SYSTEMTIME old_range[2]; 01586 INT diff; 01587 01588 TRACE("%p\n", range); 01589 01590 if(!range || !(infoPtr->dwStyle & MCS_MULTISELECT)) return FALSE; 01591 01592 /* adjust timestamps */ 01593 if(!MONTHCAL_ValidateTime(&range[0])) MONTHCAL_CopyTime(&infoPtr->todaysDate, &range[0]); 01594 if(!MONTHCAL_ValidateTime(&range[1])) MONTHCAL_CopyTime(&infoPtr->todaysDate, &range[1]); 01595 01596 /* maximum range exceeded */ 01597 if(!MONTHCAL_IsSelRangeValid(infoPtr, &range[0], &range[1], NULL)) return FALSE; 01598 01599 old_range[0] = infoPtr->minSel; 01600 old_range[1] = infoPtr->maxSel; 01601 01602 /* swap if min > max */ 01603 if(MONTHCAL_CompareSystemTime(&range[0], &range[1]) <= 0) 01604 { 01605 infoPtr->minSel = range[0]; 01606 infoPtr->maxSel = range[1]; 01607 } 01608 else 01609 { 01610 infoPtr->minSel = range[1]; 01611 infoPtr->maxSel = range[0]; 01612 } 01613 01614 diff = MONTHCAL_MonthDiff(&infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month, &infoPtr->maxSel); 01615 if (diff < 0) 01616 { 01617 diff = MONTHCAL_MonthDiff(&infoPtr->calendars[0].month, &infoPtr->maxSel); 01618 if (diff > 0) diff = 0; 01619 } 01620 01621 if (diff != 0) 01622 { 01623 INT i; 01624 01625 for (i = 0; i < MONTHCAL_GetCalCount(infoPtr); i++) 01626 MONTHCAL_GetMonth(&infoPtr->calendars[i].month, diff); 01627 } 01628 01629 /* update day of week */ 01630 MONTHCAL_CalculateDayOfWeek(&infoPtr->minSel, TRUE); 01631 MONTHCAL_CalculateDayOfWeek(&infoPtr->maxSel, TRUE); 01632 01633 /* redraw if bounds changed */ 01634 /* FIXME: no actual need to redraw everything */ 01635 if(!MONTHCAL_IsDateEqual(&old_range[0], &range[0]) || 01636 !MONTHCAL_IsDateEqual(&old_range[1], &range[1])) 01637 { 01638 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 01639 } 01640 01641 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay); 01642 return TRUE; 01643 } 01644 01645 01646 static LRESULT 01647 MONTHCAL_GetToday(const MONTHCAL_INFO *infoPtr, SYSTEMTIME *today) 01648 { 01649 TRACE("%p\n", today); 01650 01651 if(!today) return FALSE; 01652 *today = infoPtr->todaysDate; 01653 return TRUE; 01654 } 01655 01656 /* Internal helper for MCM_SETTODAY handler and auto update timer handler 01657 * 01658 * RETURN VALUE 01659 * 01660 * TRUE - today date changed 01661 * FALSE - today date isn't changed 01662 */ 01663 static BOOL 01664 MONTHCAL_UpdateToday(MONTHCAL_INFO *infoPtr, const SYSTEMTIME *today) 01665 { 01666 RECT new_r, old_r; 01667 01668 if(MONTHCAL_IsDateEqual(today, &infoPtr->todaysDate)) return FALSE; 01669 01670 MONTHCAL_GetDayRect(infoPtr, &infoPtr->todaysDate, &old_r, -1); 01671 MONTHCAL_GetDayRect(infoPtr, today, &new_r, -1); 01672 01673 infoPtr->todaysDate = *today; 01674 01675 /* only two days need redrawing */ 01676 InvalidateRect(infoPtr->hwndSelf, &old_r, FALSE); 01677 InvalidateRect(infoPtr->hwndSelf, &new_r, FALSE); 01678 /* and today label */ 01679 InvalidateRect(infoPtr->hwndSelf, &infoPtr->todayrect, FALSE); 01680 return TRUE; 01681 } 01682 01683 /* MCM_SETTODAT handler */ 01684 static LRESULT 01685 MONTHCAL_SetToday(MONTHCAL_INFO *infoPtr, const SYSTEMTIME *today) 01686 { 01687 TRACE("%p\n", today); 01688 01689 if (today) 01690 { 01691 /* remember if date was set successfully */ 01692 if (MONTHCAL_UpdateToday(infoPtr, today)) infoPtr->todaySet = TRUE; 01693 } 01694 01695 return 0; 01696 } 01697 01698 /* returns calendar index containing specified point, or -1 if it's background */ 01699 static INT MONTHCAL_GetCalendarFromPoint(const MONTHCAL_INFO *infoPtr, const POINT *pt) 01700 { 01701 RECT r; 01702 INT i; 01703 01704 for (i = 0; i < MONTHCAL_GetCalCount(infoPtr); i++) 01705 { 01706 /* whole bounding rectangle allows some optimization to compute */ 01707 r.left = infoPtr->calendars[i].title.left; 01708 r.top = infoPtr->calendars[i].title.top; 01709 r.bottom = infoPtr->calendars[i].days.bottom; 01710 r.right = infoPtr->calendars[i].days.right; 01711 01712 if (PtInRect(&r, *pt)) return i; 01713 } 01714 01715 return -1; 01716 } 01717 01718 static inline UINT fill_hittest_info(const MCHITTESTINFO *src, MCHITTESTINFO *dest) 01719 { 01720 dest->uHit = src->uHit; 01721 dest->st = src->st; 01722 01723 if (dest->cbSize == sizeof(MCHITTESTINFO)) 01724 memcpy(&dest->rc, &src->rc, sizeof(MCHITTESTINFO) - MCHITTESTINFO_V1_SIZE); 01725 01726 return src->uHit; 01727 } 01728 01729 static LRESULT 01730 MONTHCAL_HitTest(const MONTHCAL_INFO *infoPtr, MCHITTESTINFO *lpht) 01731 { 01732 MCHITTESTINFO htinfo; 01733 SYSTEMTIME *ht_month; 01734 INT day, calIdx; 01735 01736 if(!lpht || lpht->cbSize < MCHITTESTINFO_V1_SIZE) return -1; 01737 01738 htinfo.st = st_null; 01739 01740 /* we should preserve passed fields if hit area doesn't need them */ 01741 if (lpht->cbSize == sizeof(MCHITTESTINFO)) 01742 memcpy(&htinfo.rc, &lpht->rc, sizeof(MCHITTESTINFO) - MCHITTESTINFO_V1_SIZE); 01743 01744 /* Comment in for debugging... 01745 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, 01746 infoPtr->wdays.left, infoPtr->wdays.right, 01747 infoPtr->wdays.top, infoPtr->wdays.bottom, 01748 infoPtr->days.left, infoPtr->days.right, 01749 infoPtr->days.top, infoPtr->days.bottom, 01750 infoPtr->todayrect.left, infoPtr->todayrect.right, 01751 infoPtr->todayrect.top, infoPtr->todayrect.bottom, 01752 infoPtr->weeknums.left, infoPtr->weeknums.right, 01753 infoPtr->weeknums.top, infoPtr->weeknums.bottom); 01754 */ 01755 01756 /* guess in what calendar we are */ 01757 calIdx = MONTHCAL_GetCalendarFromPoint(infoPtr, &lpht->pt); 01758 if (calIdx == -1) 01759 { 01760 if (PtInRect(&infoPtr->todayrect, lpht->pt)) 01761 { 01762 htinfo.uHit = MCHT_TODAYLINK; 01763 htinfo.rc = infoPtr->todayrect; 01764 } 01765 else 01766 /* outside of calendar area? What's left must be background :-) */ 01767 htinfo.uHit = MCHT_CALENDARBK; 01768 01769 return fill_hittest_info(&htinfo, lpht); 01770 } 01771 01772 /* are we in the header? */ 01773 if (PtInRect(&infoPtr->calendars[calIdx].title, lpht->pt)) { 01774 /* FIXME: buttons hittesting could be optimized cause maximum 01775 two calendars have buttons */ 01776 if (calIdx == 0 && PtInRect(&infoPtr->titlebtnprev, lpht->pt)) 01777 { 01778 htinfo.uHit = MCHT_TITLEBTNPREV; 01779 htinfo.rc = infoPtr->titlebtnprev; 01780 } 01781 else if (PtInRect(&infoPtr->titlebtnnext, lpht->pt)) 01782 { 01783 htinfo.uHit = MCHT_TITLEBTNNEXT; 01784 htinfo.rc = infoPtr->titlebtnnext; 01785 } 01786 else if (PtInRect(&infoPtr->calendars[calIdx].titlemonth, lpht->pt)) 01787 { 01788 htinfo.uHit = MCHT_TITLEMONTH; 01789 htinfo.rc = infoPtr->calendars[calIdx].titlemonth; 01790 htinfo.iOffset = calIdx; 01791 } 01792 else if (PtInRect(&infoPtr->calendars[calIdx].titleyear, lpht->pt)) 01793 { 01794 htinfo.uHit = MCHT_TITLEYEAR; 01795 htinfo.rc = infoPtr->calendars[calIdx].titleyear; 01796 htinfo.iOffset = calIdx; 01797 } 01798 else 01799 { 01800 htinfo.uHit = MCHT_TITLE; 01801 htinfo.rc = infoPtr->calendars[calIdx].title; 01802 htinfo.iOffset = calIdx; 01803 } 01804 01805 return fill_hittest_info(&htinfo, lpht); 01806 } 01807 01808 ht_month = &infoPtr->calendars[calIdx].month; 01809 /* days area (including week days and week numbers) */ 01810 day = MONTHCAL_GetDayFromPos(infoPtr, lpht->pt, calIdx); 01811 if (PtInRect(&infoPtr->calendars[calIdx].wdays, lpht->pt)) 01812 { 01813 htinfo.uHit = MCHT_CALENDARDAY; 01814 htinfo.iOffset = calIdx; 01815 htinfo.st.wYear = ht_month->wYear; 01816 htinfo.st.wMonth = (day < 1) ? ht_month->wMonth -1 : ht_month->wMonth; 01817 htinfo.st.wDay = (day < 1) ? 01818 MONTHCAL_MonthLength(ht_month->wMonth-1, ht_month->wYear) - day : day; 01819 01820 MONTHCAL_GetDayPos(infoPtr, &htinfo.st, &htinfo.iCol, &htinfo.iRow, calIdx); 01821 } 01822 else if(PtInRect(&infoPtr->calendars[calIdx].weeknums, lpht->pt)) 01823 { 01824 htinfo.uHit = MCHT_CALENDARWEEKNUM; 01825 htinfo.st.wYear = ht_month->wYear; 01826 htinfo.iOffset = calIdx; 01827 01828 if (day < 1) 01829 { 01830 htinfo.st.wMonth = ht_month->wMonth - 1; 01831 htinfo.st.wDay = MONTHCAL_MonthLength(ht_month->wMonth-1, ht_month->wYear) - day; 01832 } 01833 else if (day > MONTHCAL_MonthLength(ht_month->wMonth, ht_month->wYear)) 01834 { 01835 htinfo.st.wMonth = ht_month->wMonth + 1; 01836 htinfo.st.wDay = day - MONTHCAL_MonthLength(ht_month->wMonth, ht_month->wYear); 01837 } 01838 else 01839 { 01840 htinfo.st.wMonth = ht_month->wMonth; 01841 htinfo.st.wDay = day; 01842 } 01843 } 01844 else if(PtInRect(&infoPtr->calendars[calIdx].days, lpht->pt)) 01845 { 01846 htinfo.iOffset = calIdx; 01847 htinfo.st.wYear = ht_month->wYear; 01848 htinfo.st.wMonth = ht_month->wMonth; 01849 /* previous month only valid for first calendar */ 01850 if (day < 1 && calIdx == 0) 01851 { 01852 htinfo.uHit = MCHT_CALENDARDATEPREV; 01853 MONTHCAL_GetPrevMonth(&htinfo.st); 01854 htinfo.st.wDay = MONTHCAL_MonthLength(htinfo.st.wMonth, htinfo.st.wYear) + day; 01855 } 01856 /* next month only valid for last calendar */ 01857 else if (day > MONTHCAL_MonthLength(ht_month->wMonth, ht_month->wYear) && 01858 calIdx == MONTHCAL_GetCalCount(infoPtr)-1) 01859 { 01860 htinfo.uHit = MCHT_CALENDARDATENEXT; 01861 MONTHCAL_GetNextMonth(&htinfo.st); 01862 htinfo.st.wDay = day - MONTHCAL_MonthLength(ht_month->wMonth, ht_month->wYear); 01863 } 01864 /* multiple calendars case - blank areas for previous/next month */ 01865 else if (day < 1 || day > MONTHCAL_MonthLength(ht_month->wMonth, ht_month->wYear)) 01866 { 01867 htinfo.uHit = MCHT_CALENDARBK; 01868 } 01869 else 01870 { 01871 htinfo.uHit = MCHT_CALENDARDATE; 01872 htinfo.st.wDay = day; 01873 } 01874 01875 MONTHCAL_GetDayPos(infoPtr, &htinfo.st, &htinfo.iCol, &htinfo.iRow, calIdx); 01876 MONTHCAL_GetDayRectI(infoPtr, &htinfo.rc, htinfo.iCol, htinfo.iRow, calIdx); 01877 /* always update day of week */ 01878 MONTHCAL_CalculateDayOfWeek(&htinfo.st, TRUE); 01879 } 01880 01881 return fill_hittest_info(&htinfo, lpht); 01882 } 01883 01884 /* MCN_GETDAYSTATE notification helper */ 01885 static void MONTHCAL_NotifyDayState(MONTHCAL_INFO *infoPtr) 01886 { 01887 MONTHDAYSTATE *state; 01888 NMDAYSTATE nmds; 01889 01890 if (!(infoPtr->dwStyle & MCS_DAYSTATE)) return; 01891 01892 nmds.nmhdr.hwndFrom = infoPtr->hwndSelf; 01893 nmds.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 01894 nmds.nmhdr.code = MCN_GETDAYSTATE; 01895 nmds.cDayState = MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0); 01896 nmds.prgDayState = state = Alloc(nmds.cDayState * sizeof(MONTHDAYSTATE)); 01897 01898 MONTHCAL_GetMinDate(infoPtr, &nmds.stStart); 01899 nmds.stStart.wDay = 1; 01900 01901 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmds.nmhdr.idFrom, (LPARAM)&nmds); 01902 memcpy(infoPtr->monthdayState, nmds.prgDayState, 01903 MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0)*sizeof(MONTHDAYSTATE)); 01904 01905 Free(state); 01906 } 01907 01908 /* no valid range check performed */ 01909 static void MONTHCAL_Scroll(MONTHCAL_INFO *infoPtr, INT delta) 01910 { 01911 INT i, selIdx = -1; 01912 01913 for(i = 0; i < MONTHCAL_GetCalCount(infoPtr); i++) 01914 { 01915 /* save selection position to shift it later */ 01916 if (selIdx == -1 && MONTHCAL_CompareMonths(&infoPtr->minSel, &infoPtr->calendars[i].month) == 0) 01917 selIdx = i; 01918 01919 MONTHCAL_GetMonth(&infoPtr->calendars[i].month, delta); 01920 } 01921 01922 /* selection is always shifted to first calendar */ 01923 if(infoPtr->dwStyle & MCS_MULTISELECT) 01924 { 01925 SYSTEMTIME range[2]; 01926 01927 MONTHCAL_GetSelRange(infoPtr, range); 01928 MONTHCAL_GetMonth(&range[0], delta - selIdx); 01929 MONTHCAL_GetMonth(&range[1], delta - selIdx); 01930 MONTHCAL_SetSelRange(infoPtr, range); 01931 } 01932 else 01933 { 01934 SYSTEMTIME st = infoPtr->minSel; 01935 01936 MONTHCAL_GetMonth(&st, delta - selIdx); 01937 MONTHCAL_SetCurSel(infoPtr, &st); 01938 } 01939 } 01940 01941 static void MONTHCAL_GoToMonth(MONTHCAL_INFO *infoPtr, enum nav_direction direction) 01942 { 01943 INT delta = infoPtr->delta ? infoPtr->delta : MONTHCAL_GetCalCount(infoPtr); 01944 SYSTEMTIME st; 01945 01946 TRACE("%s\n", direction == DIRECTION_BACKWARD ? "back" : "fwd"); 01947 01948 /* check if change allowed by range set */ 01949 if(direction == DIRECTION_BACKWARD) 01950 { 01951 st = infoPtr->calendars[0].month; 01952 MONTHCAL_GetMonth(&st, -delta); 01953 } 01954 else 01955 { 01956 st = infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month; 01957 MONTHCAL_GetMonth(&st, delta); 01958 } 01959 01960 if(!MONTHCAL_IsDateInValidRange(infoPtr, &st, FALSE)) return; 01961 01962 MONTHCAL_Scroll(infoPtr, direction == DIRECTION_BACKWARD ? -delta : delta); 01963 MONTHCAL_NotifyDayState(infoPtr); 01964 MONTHCAL_NotifySelectionChange(infoPtr); 01965 } 01966 01967 static LRESULT 01968 MONTHCAL_RButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam) 01969 { 01970 static const WCHAR todayW[] = { 'G','o',' ','t','o',' ','T','o','d','a','y',':',0 }; 01971 HMENU hMenu; 01972 POINT menupoint; 01973 WCHAR buf[32]; 01974 01975 hMenu = CreatePopupMenu(); 01976 if (!LoadStringW(COMCTL32_hModule, IDM_GOTODAY, buf, countof(buf))) 01977 { 01978 WARN("Can't load resource\n"); 01979 strcpyW(buf, todayW); 01980 } 01981 AppendMenuW(hMenu, MF_STRING|MF_ENABLED, 1, buf); 01982 menupoint.x = (short)LOWORD(lParam); 01983 menupoint.y = (short)HIWORD(lParam); 01984 ClientToScreen(infoPtr->hwndSelf, &menupoint); 01985 if( TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, 01986 menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL)) 01987 { 01988 if (infoPtr->dwStyle & MCS_MULTISELECT) 01989 { 01990 SYSTEMTIME range[2]; 01991 01992 range[0] = range[1] = infoPtr->todaysDate; 01993 MONTHCAL_SetSelRange(infoPtr, range); 01994 } 01995 else 01996 MONTHCAL_SetCurSel(infoPtr, &infoPtr->todaysDate); 01997 01998 MONTHCAL_NotifySelectionChange(infoPtr); 01999 MONTHCAL_NotifySelect(infoPtr); 02000 } 02001 02002 return 0; 02003 } 02004 02005 /*** 02006 * DESCRIPTION: 02007 * Subclassed edit control windproc function 02008 * 02009 * PARAMETER(S): 02010 * [I] hwnd : the edit window handle 02011 * [I] uMsg : the message that is to be processed 02012 * [I] wParam : first message parameter 02013 * [I] lParam : second message parameter 02014 * 02015 */ 02016 static LRESULT CALLBACK EditWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 02017 { 02018 MONTHCAL_INFO *infoPtr = (MONTHCAL_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0); 02019 02020 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx)\n", 02021 hwnd, uMsg, wParam, lParam); 02022 02023 switch (uMsg) 02024 { 02025 case WM_GETDLGCODE: 02026 return DLGC_WANTARROWS | DLGC_WANTALLKEYS; 02027 02028 case WM_DESTROY: 02029 { 02030 WNDPROC editProc = infoPtr->EditWndProc; 02031 infoPtr->EditWndProc = NULL; 02032 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc); 02033 return CallWindowProcW(editProc, hwnd, uMsg, wParam, lParam); 02034 } 02035 02036 case WM_KILLFOCUS: 02037 break; 02038 02039 case WM_KEYDOWN: 02040 if ((VK_ESCAPE == (INT)wParam) || (VK_RETURN == (INT)wParam)) 02041 break; 02042 02043 default: 02044 return CallWindowProcW(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam); 02045 } 02046 02047 SendMessageW(infoPtr->hWndYearUpDown, WM_CLOSE, 0, 0); 02048 SendMessageW(hwnd, WM_CLOSE, 0, 0); 02049 return 0; 02050 } 02051 02052 /* creates updown control and edit box */ 02053 static void MONTHCAL_EditYear(MONTHCAL_INFO *infoPtr, INT calIdx) 02054 { 02055 RECT *rc = &infoPtr->calendars[calIdx].titleyear; 02056 RECT *title = &infoPtr->calendars[calIdx].title; 02057 02058 infoPtr->hWndYearEdit = 02059 CreateWindowExW(0, WC_EDITW, 0, WS_VISIBLE | WS_CHILD | ES_READONLY, 02060 rc->left + 3, (title->bottom + title->top - infoPtr->textHeight) / 2, 02061 rc->right - rc->left + 4, 02062 infoPtr->textHeight, infoPtr->hwndSelf, 02063 NULL, NULL, NULL); 02064 02065 SendMessageW(infoPtr->hWndYearEdit, WM_SETFONT, (WPARAM)infoPtr->hBoldFont, TRUE); 02066 02067 infoPtr->hWndYearUpDown = 02068 CreateWindowExW(0, UPDOWN_CLASSW, 0, 02069 WS_VISIBLE | WS_CHILD | UDS_SETBUDDYINT | UDS_NOTHOUSANDS | UDS_ARROWKEYS, 02070 rc->right + 7, (title->bottom + title->top - infoPtr->textHeight) / 2, 02071 18, infoPtr->textHeight, infoPtr->hwndSelf, 02072 NULL, NULL, NULL); 02073 02074 /* attach edit box */ 02075 SendMessageW(infoPtr->hWndYearUpDown, UDM_SETRANGE, 0, 02076 MAKELONG(max_allowed_date.wYear, min_allowed_date.wYear)); 02077 SendMessageW(infoPtr->hWndYearUpDown, UDM_SETBUDDY, (WPARAM)infoPtr->hWndYearEdit, 0); 02078 SendMessageW(infoPtr->hWndYearUpDown, UDM_SETPOS, 0, infoPtr->calendars[calIdx].month.wYear); 02079 02080 /* subclass edit box */ 02081 infoPtr->EditWndProc = (WNDPROC)SetWindowLongPtrW(infoPtr->hWndYearEdit, 02082 GWLP_WNDPROC, (DWORD_PTR)EditWndProc); 02083 02084 SetFocus(infoPtr->hWndYearEdit); 02085 } 02086 02087 static LRESULT 02088 MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam) 02089 { 02090 MCHITTESTINFO ht; 02091 DWORD hit; 02092 02093 /* Actually we don't need input focus for calendar, this is used to kill 02094 year updown and its buddy edit box */ 02095 if (IsWindow(infoPtr->hWndYearUpDown)) 02096 { 02097 SetFocus(infoPtr->hwndSelf); 02098 return 0; 02099 } 02100 02101 SetCapture(infoPtr->hwndSelf); 02102 02103 ht.cbSize = sizeof(MCHITTESTINFO); 02104 ht.pt.x = (short)LOWORD(lParam); 02105 ht.pt.y = (short)HIWORD(lParam); 02106 02107 hit = MONTHCAL_HitTest(infoPtr, &ht); 02108 02109 TRACE("%x at (%d, %d)\n", hit, ht.pt.x, ht.pt.y); 02110 02111 switch(hit) 02112 { 02113 case MCHT_TITLEBTNNEXT: 02114 MONTHCAL_GoToMonth(infoPtr, DIRECTION_FORWARD); 02115 infoPtr->status = MC_NEXTPRESSED; 02116 SetTimer(infoPtr->hwndSelf, MC_PREVNEXTMONTHTIMER, MC_PREVNEXTMONTHDELAY, 0); 02117 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 02118 return 0; 02119 02120 case MCHT_TITLEBTNPREV: 02121 MONTHCAL_GoToMonth(infoPtr, DIRECTION_BACKWARD); 02122 infoPtr->status = MC_PREVPRESSED; 02123 SetTimer(infoPtr->hwndSelf, MC_PREVNEXTMONTHTIMER, MC_PREVNEXTMONTHDELAY, 0); 02124 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 02125 return 0; 02126 02127 case MCHT_TITLEMONTH: 02128 { 02129 HMENU hMenu = CreatePopupMenu(); 02130 WCHAR buf[32]; 02131 POINT menupoint; 02132 INT i; 02133 02134 for (i = 0; i < 12; i++) 02135 { 02136 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+i, buf, countof(buf)); 02137 AppendMenuW(hMenu, MF_STRING|MF_ENABLED, i + 1, buf); 02138 } 02139 menupoint.x = ht.pt.x; 02140 menupoint.y = ht.pt.y; 02141 ClientToScreen(infoPtr->hwndSelf, &menupoint); 02142 i = TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD, 02143 menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL); 02144 02145 if ((i > 0) && (i < 13) && infoPtr->calendars[ht.iOffset].month.wMonth != i) 02146 { 02147 INT delta = i - infoPtr->calendars[ht.iOffset].month.wMonth; 02148 SYSTEMTIME st; 02149 02150 /* check if change allowed by range set */ 02151 st = delta < 0 ? infoPtr->calendars[0].month : 02152 infoPtr->calendars[MONTHCAL_GetCalCount(infoPtr)-1].month; 02153 MONTHCAL_GetMonth(&st, delta); 02154 02155 if (MONTHCAL_IsDateInValidRange(infoPtr, &st, FALSE)) 02156 { 02157 MONTHCAL_Scroll(infoPtr, delta); 02158 MONTHCAL_NotifyDayState(infoPtr); 02159 MONTHCAL_NotifySelectionChange(infoPtr); 02160 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 02161 } 02162 } 02163 return 0; 02164 } 02165 case MCHT_TITLEYEAR: 02166 { 02167 MONTHCAL_EditYear(infoPtr, ht.iOffset); 02168 return 0; 02169 } 02170 case MCHT_TODAYLINK: 02171 { 02172 if (infoPtr->dwStyle & MCS_MULTISELECT) 02173 { 02174 SYSTEMTIME range[2]; 02175 02176 range[0] = range[1] = infoPtr->todaysDate; 02177 MONTHCAL_SetSelRange(infoPtr, range); 02178 } 02179 else 02180 MONTHCAL_SetCurSel(infoPtr, &infoPtr->todaysDate); 02181 02182 MONTHCAL_NotifySelectionChange(infoPtr); 02183 MONTHCAL_NotifySelect(infoPtr); 02184 return 0; 02185 } 02186 case MCHT_CALENDARDATENEXT: 02187 case MCHT_CALENDARDATEPREV: 02188 case MCHT_CALENDARDATE: 02189 { 02190 SYSTEMTIME st[2]; 02191 02192 MONTHCAL_CopyDate(&ht.st, &infoPtr->firstSel); 02193 02194 st[0] = st[1] = ht.st; 02195 /* clear selection range */ 02196 MONTHCAL_SetSelRange(infoPtr, st); 02197 02198 infoPtr->status = MC_SEL_LBUTDOWN; 02199 MONTHCAL_SetDayFocus(infoPtr, &ht.st); 02200 return 0; 02201 } 02202 } 02203 02204 return 1; 02205 } 02206 02207 02208 static LRESULT 02209 MONTHCAL_LButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam) 02210 { 02211 NMHDR nmhdr; 02212 MCHITTESTINFO ht; 02213 DWORD hit; 02214 02215 TRACE("\n"); 02216 02217 if(infoPtr->status & (MC_PREVPRESSED | MC_NEXTPRESSED)) { 02218 RECT *r; 02219 02220 KillTimer(infoPtr->hwndSelf, MC_PREVNEXTMONTHTIMER); 02221 r = infoPtr->status & MC_PREVPRESSED ? &infoPtr->titlebtnprev : &infoPtr->titlebtnnext; 02222 infoPtr->status &= ~(MC_PREVPRESSED | MC_NEXTPRESSED); 02223 02224 InvalidateRect(infoPtr->hwndSelf, r, FALSE); 02225 } 02226 02227 ReleaseCapture(); 02228 02229 /* always send NM_RELEASEDCAPTURE notification */ 02230 nmhdr.hwndFrom = infoPtr->hwndSelf; 02231 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 02232 nmhdr.code = NM_RELEASEDCAPTURE; 02233 TRACE("Sent notification from %p to %p\n", infoPtr->hwndSelf, infoPtr->hwndNotify); 02234 02235 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr); 02236 02237 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0; 02238 02239 ht.cbSize = sizeof(MCHITTESTINFO); 02240 ht.pt.x = (short)LOWORD(lParam); 02241 ht.pt.y = (short)HIWORD(lParam); 02242 hit = MONTHCAL_HitTest(infoPtr, &ht); 02243 02244 infoPtr->status = MC_SEL_LBUTUP; 02245 MONTHCAL_SetDayFocus(infoPtr, NULL); 02246 02247 if((hit & MCHT_CALENDARDATE) == MCHT_CALENDARDATE) 02248 { 02249 SYSTEMTIME sel = infoPtr->minSel; 02250 02251 /* will be invalidated here */ 02252 MONTHCAL_SetCurSel(infoPtr, &ht.st); 02253 02254 /* send MCN_SELCHANGE only if new date selected */ 02255 if (!MONTHCAL_IsDateEqual(&sel, &ht.st)) 02256 MONTHCAL_NotifySelectionChange(infoPtr); 02257 02258 MONTHCAL_NotifySelect(infoPtr); 02259 } 02260 02261 return 0; 02262 } 02263 02264 02265 static LRESULT 02266 MONTHCAL_Timer(MONTHCAL_INFO *infoPtr, WPARAM id) 02267 { 02268 TRACE("%ld\n", id); 02269 02270 switch(id) { 02271 case MC_PREVNEXTMONTHTIMER: 02272 if(infoPtr->status & MC_NEXTPRESSED) MONTHCAL_GoToMonth(infoPtr, DIRECTION_FORWARD); 02273 if(infoPtr->status & MC_PREVPRESSED) MONTHCAL_GoToMonth(infoPtr, DIRECTION_BACKWARD); 02274 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 02275 break; 02276 case MC_TODAYUPDATETIMER: 02277 { 02278 SYSTEMTIME st; 02279 02280 if(infoPtr->todaySet) return 0; 02281 02282 GetLocalTime(&st); 02283 MONTHCAL_UpdateToday(infoPtr, &st); 02284 02285 /* notification sent anyway */ 02286 MONTHCAL_NotifySelectionChange(infoPtr); 02287 02288 return 0; 02289 } 02290 default: 02291 ERR("got unknown timer %ld\n", id); 02292 break; 02293 } 02294 02295 return 0; 02296 } 02297 02298 02299 static LRESULT 02300 MONTHCAL_MouseMove(MONTHCAL_INFO *infoPtr, LPARAM lParam) 02301 { 02302 MCHITTESTINFO ht; 02303 SYSTEMTIME st_ht; 02304 INT hit; 02305 RECT r; 02306 02307 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0; 02308 02309 ht.cbSize = sizeof(MCHITTESTINFO); 02310 ht.pt.x = (short)LOWORD(lParam); 02311 ht.pt.y = (short)HIWORD(lParam); 02312 ht.iOffset = -1; 02313 02314 hit = MONTHCAL_HitTest(infoPtr, &ht); 02315 02316 /* not on the calendar date numbers? bail out */ 02317 TRACE("hit:%x\n",hit); 02318 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) 02319 { 02320 MONTHCAL_SetDayFocus(infoPtr, NULL); 02321 return 0; 02322 } 02323 02324 st_ht = ht.st; 02325 02326 /* if pointer is over focused day still there's nothing to do */ 02327 if(!MONTHCAL_SetDayFocus(infoPtr, &ht.st)) return 0; 02328 02329 MONTHCAL_GetDayRect(infoPtr, &ht.st, &r, ht.iOffset); 02330 02331 if(infoPtr->dwStyle & MCS_MULTISELECT) { 02332 SYSTEMTIME st[2]; 02333 02334 MONTHCAL_GetSelRange(infoPtr, st); 02335 02336 /* If we're still at the first selected date and range is empty, return. 02337 If range isn't empty we should change range to a single firstSel */ 02338 if(MONTHCAL_IsDateEqual(&infoPtr->firstSel, &st_ht) && 02339 MONTHCAL_IsDateEqual(&st[0], &st[1])) goto done; 02340 02341 MONTHCAL_IsSelRangeValid(infoPtr, &st_ht, &infoPtr->firstSel, &st_ht); 02342 02343 st[0] = infoPtr->firstSel; 02344 /* we should overwrite timestamp here */ 02345 MONTHCAL_CopyDate(&st_ht, &st[1]); 02346 02347 /* bounds will be swapped here if needed */ 02348 MONTHCAL_SetSelRange(infoPtr, st); 02349 02350 return 0; 02351 } 02352 02353 done: 02354 02355 /* FIXME: this should specify a rectangle containing only the days that changed 02356 using InvalidateRect */ 02357 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 02358 02359 return 0; 02360 } 02361 02362 02363 static LRESULT 02364 MONTHCAL_Paint(MONTHCAL_INFO *infoPtr, HDC hdc_paint) 02365 { 02366 HDC hdc; 02367 PAINTSTRUCT ps; 02368 02369 if (hdc_paint) 02370 { 02371 GetClientRect(infoPtr->hwndSelf, &ps.rcPaint); 02372 hdc = hdc_paint; 02373 } 02374 else 02375 hdc = BeginPaint(infoPtr->hwndSelf, &ps); 02376 02377 MONTHCAL_Refresh(infoPtr, hdc, &ps); 02378 if (!hdc_paint) EndPaint(infoPtr->hwndSelf, &ps); 02379 return 0; 02380 } 02381 02382 static LRESULT 02383 MONTHCAL_EraseBkgnd(const MONTHCAL_INFO *infoPtr, HDC hdc) 02384 { 02385 RECT rc; 02386 02387 if (!GetClipBox(hdc, &rc)) return FALSE; 02388 02389 FillRect(hdc, &rc, infoPtr->brushes[BrushBackground]); 02390 02391 return TRUE; 02392 } 02393 02394 static LRESULT 02395 MONTHCAL_PrintClient(MONTHCAL_INFO *infoPtr, HDC hdc, DWORD options) 02396 { 02397 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options); 02398 02399 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf)) 02400 return 0; 02401 02402 if (options & PRF_ERASEBKGND) 02403 MONTHCAL_EraseBkgnd(infoPtr, hdc); 02404 02405 if (options & PRF_CLIENT) 02406 MONTHCAL_Paint(infoPtr, hdc); 02407 02408 return 0; 02409 } 02410 02411 static LRESULT 02412 MONTHCAL_SetFocus(const MONTHCAL_INFO *infoPtr) 02413 { 02414 TRACE("\n"); 02415 02416 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 02417 02418 return 0; 02419 } 02420 02421 /* sets the size information */ 02422 static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr) 02423 { 02424 static const WCHAR O0W[] = { '0','0',0 }; 02425 RECT *title=&infoPtr->calendars[0].title; 02426 RECT *prev=&infoPtr->titlebtnprev; 02427 RECT *next=&infoPtr->titlebtnnext; 02428 RECT *titlemonth=&infoPtr->calendars[0].titlemonth; 02429 RECT *titleyear=&infoPtr->calendars[0].titleyear; 02430 RECT *wdays=&infoPtr->calendars[0].wdays; 02431 RECT *weeknumrect=&infoPtr->calendars[0].weeknums; 02432 RECT *days=&infoPtr->calendars[0].days; 02433 RECT *todayrect=&infoPtr->todayrect; 02434 02435 INT xdiv, dx, dy, i, j, x, y, c_dx, c_dy; 02436 WCHAR buff[80]; 02437 TEXTMETRICW tm; 02438 SIZE size, sz; 02439 RECT client; 02440 HFONT font; 02441 HDC hdc; 02442 02443 GetClientRect(infoPtr->hwndSelf, &client); 02444 02445 hdc = GetDC(infoPtr->hwndSelf); 02446 font = SelectObject(hdc, infoPtr->hFont); 02447 02448 /* get the height and width of each day's text */ 02449 GetTextMetricsW(hdc, &tm); 02450 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading + tm.tmInternalLeading; 02451 02452 /* find largest abbreviated day name for current locale */ 02453 size.cx = sz.cx = 0; 02454 for (i = 0; i < 7; i++) 02455 { 02456 if(GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1 + i, 02457 buff, countof(buff))) 02458 { 02459 GetTextExtentPoint32W(hdc, buff, lstrlenW(buff), &sz); 02460 if (sz.cx > size.cx) size.cx = sz.cx; 02461 } 02462 else /* locale independent fallback on failure */ 02463 { 02464 static const WCHAR SunW[] = { 'S','u','n',0 }; 02465 02466 GetTextExtentPoint32W(hdc, SunW, lstrlenW(SunW), &size); 02467 break; 02468 } 02469 } 02470 02471 infoPtr->textWidth = size.cx + 2; 02472 02473 /* recalculate the height and width increments and offsets */ 02474 GetTextExtentPoint32W(hdc, O0W, 2, &size); 02475 02476 /* restore the originally selected font */ 02477 SelectObject(hdc, font); 02478 ReleaseDC(infoPtr->hwndSelf, hdc); 02479 02480 xdiv = (infoPtr->dwStyle & MCS_WEEKNUMBERS) ? 8 : 7; 02481 02482 infoPtr->width_increment = size.cx * 2 + 4; 02483 infoPtr->height_increment = infoPtr->textHeight; 02484 02485 /* calculate title area */ 02486 title->top = 0; 02487 title->bottom = 3 * infoPtr->height_increment / 2; 02488 title->left = 0; 02489 title->right = infoPtr->width_increment * xdiv; 02490 02491 /* set the dimensions of the next and previous buttons and center */ 02492 /* the month text vertically */ 02493 prev->top = next->top = title->top + 4; 02494 prev->bottom = next->bottom = title->bottom - 4; 02495 prev->left = title->left + 4; 02496 prev->right = prev->left + (title->bottom - title->top); 02497 next->right = title->right - 4; 02498 next->left = next->right - (title->bottom - title->top); 02499 02500 /* titlemonth->left and right change based upon the current month 02501 and are recalculated in refresh as the current month may change 02502 without the control being resized */ 02503 titlemonth->top = titleyear->top = title->top + (infoPtr->height_increment)/2; 02504 titlemonth->bottom = titleyear->bottom = title->bottom - (infoPtr->height_increment)/2; 02505 02506 /* week numbers */ 02507 weeknumrect->left = 0; 02508 weeknumrect->right = infoPtr->dwStyle & MCS_WEEKNUMBERS ? prev->right : 0; 02509 02510 /* days abbreviated names */ 02511 wdays->left = days->left = weeknumrect->right; 02512 wdays->right = days->right = wdays->left + 7 * infoPtr->width_increment; 02513 wdays->top = title->bottom; 02514 wdays->bottom = wdays->top + infoPtr->height_increment; 02515 02516 days->top = weeknumrect->top = wdays->bottom; 02517 days->bottom = weeknumrect->bottom = days->top + 6 * infoPtr->height_increment; 02518 02519 todayrect->left = 0; 02520 todayrect->right = title->right; 02521 todayrect->top = days->bottom; 02522 todayrect->bottom = days->bottom + infoPtr->height_increment; 02523 02524 /* compute calendar count, update all calendars */ 02525 x = (client.right + MC_CALENDAR_PADDING) / (title->right - title->left + MC_CALENDAR_PADDING); 02526 /* today label affects whole height */ 02527 if (infoPtr->dwStyle & MCS_NOTODAY) 02528 y = (client.bottom + MC_CALENDAR_PADDING) / (days->bottom - title->top + MC_CALENDAR_PADDING); 02529 else 02530 y = (client.bottom - todayrect->bottom + todayrect->top + MC_CALENDAR_PADDING) / 02531 (days->bottom - title->top + MC_CALENDAR_PADDING); 02532 02533 /* TODO: ensure that count is properly adjusted to fit 12 months constraint */ 02534 if (x == 0) x = 1; 02535 if (y == 0) y = 1; 02536 02537 if (x*y != MONTHCAL_GetCalCount(infoPtr)) 02538 { 02539 infoPtr->dim.cx = x; 02540 infoPtr->dim.cy = y; 02541 infoPtr->calendars = ReAlloc(infoPtr->calendars, MONTHCAL_GetCalCount(infoPtr)*sizeof(CALENDAR_INFO)); 02542 02543 infoPtr->monthdayState = ReAlloc(infoPtr->monthdayState, 02544 MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0)*sizeof(MONTHDAYSTATE)); 02545 MONTHCAL_NotifyDayState(infoPtr); 02546 02547 /* update pointers that we'll need */ 02548 title = &infoPtr->calendars[0].title; 02549 wdays = &infoPtr->calendars[0].wdays; 02550 days = &infoPtr->calendars[0].days; 02551 } 02552 02553 for (i = 1; i < MONTHCAL_GetCalCount(infoPtr); i++) 02554 { 02555 /* set months */ 02556 infoPtr->calendars[i] = infoPtr->calendars[0]; 02557 MONTHCAL_GetMonth(&infoPtr->calendars[i].month, i); 02558 } 02559 02560 /* offset all rectangles to center in client area */ 02561 c_dx = (client.right - x * title->right - MC_CALENDAR_PADDING * (x-1)) / 2; 02562 c_dy = (client.bottom - y * todayrect->bottom - MC_CALENDAR_PADDING * (y-1)) / 2; 02563 02564 /* if calendar doesn't fit client area show it at left/top bounds */ 02565 if (title->left + c_dx < 0) c_dx = 0; 02566 if (title->top + c_dy < 0) c_dy = 0; 02567 02568 for (i = 0; i < y; i++) 02569 { 02570 for (j = 0; j < x; j++) 02571 { 02572 dx = j*(title->right - title->left + MC_CALENDAR_PADDING) + c_dx; 02573 dy = i*(days->bottom - title->top + MC_CALENDAR_PADDING) + c_dy; 02574 02575 OffsetRect(&infoPtr->calendars[i*x+j].title, dx, dy); 02576 OffsetRect(&infoPtr->calendars[i*x+j].titlemonth, dx, dy); 02577 OffsetRect(&infoPtr->calendars[i*x+j].titleyear, dx, dy); 02578 OffsetRect(&infoPtr->calendars[i*x+j].wdays, dx, dy); 02579 OffsetRect(&infoPtr->calendars[i*x+j].weeknums, dx, dy); 02580 OffsetRect(&infoPtr->calendars[i*x+j].days, dx, dy); 02581 } 02582 } 02583 02584 OffsetRect(prev, c_dx, c_dy); 02585 OffsetRect(next, (x-1)*(title->right - title->left + MC_CALENDAR_PADDING) + c_dx, c_dy); 02586 02587 i = infoPtr->dim.cx * infoPtr->dim.cy - infoPtr->dim.cx; 02588 todayrect->left = infoPtr->calendars[i].title.left; 02589 todayrect->right = infoPtr->calendars[i].title.right; 02590 todayrect->top = infoPtr->calendars[i].days.bottom; 02591 todayrect->bottom = infoPtr->calendars[i].days.bottom + infoPtr->height_increment; 02592 02593 TRACE("dx=%d dy=%d client[%s] title[%s] wdays[%s] days[%s] today[%s]\n", 02594 infoPtr->width_increment,infoPtr->height_increment, 02595 wine_dbgstr_rect(&client), 02596 wine_dbgstr_rect(title), 02597 wine_dbgstr_rect(wdays), 02598 wine_dbgstr_rect(days), 02599 wine_dbgstr_rect(todayrect)); 02600 } 02601 02602 static LRESULT MONTHCAL_Size(MONTHCAL_INFO *infoPtr, int Width, int Height) 02603 { 02604 TRACE("(width=%d, height=%d)\n", Width, Height); 02605 02606 MONTHCAL_UpdateSize(infoPtr); 02607 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 02608 02609 return 0; 02610 } 02611 02612 static LRESULT MONTHCAL_GetFont(const MONTHCAL_INFO *infoPtr) 02613 { 02614 return (LRESULT)infoPtr->hFont; 02615 } 02616 02617 static LRESULT MONTHCAL_SetFont(MONTHCAL_INFO *infoPtr, HFONT hFont, BOOL redraw) 02618 { 02619 HFONT hOldFont; 02620 LOGFONTW lf; 02621 02622 if (!hFont) return 0; 02623 02624 hOldFont = infoPtr->hFont; 02625 infoPtr->hFont = hFont; 02626 02627 GetObjectW(infoPtr->hFont, sizeof(lf), &lf); 02628 lf.lfWeight = FW_BOLD; 02629 infoPtr->hBoldFont = CreateFontIndirectW(&lf); 02630 02631 MONTHCAL_UpdateSize(infoPtr); 02632 02633 if (redraw) 02634 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 02635 02636 return (LRESULT)hOldFont; 02637 } 02638 02639 /* update theme after a WM_THEMECHANGED message */ 02640 static LRESULT theme_changed (const MONTHCAL_INFO* infoPtr) 02641 { 02642 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); 02643 CloseThemeData (theme); 02644 OpenThemeData (infoPtr->hwndSelf, themeClass); 02645 return 0; 02646 } 02647 02648 static INT MONTHCAL_StyleChanged(MONTHCAL_INFO *infoPtr, WPARAM wStyleType, 02649 const STYLESTRUCT *lpss) 02650 { 02651 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 02652 wStyleType, lpss->styleOld, lpss->styleNew); 02653 02654 if (wStyleType != GWL_STYLE) return 0; 02655 02656 infoPtr->dwStyle = lpss->styleNew; 02657 02658 /* make room for week numbers */ 02659 if ((lpss->styleNew ^ lpss->styleOld) & MCS_WEEKNUMBERS) 02660 MONTHCAL_UpdateSize(infoPtr); 02661 02662 return 0; 02663 } 02664 02665 static INT MONTHCAL_StyleChanging(MONTHCAL_INFO *infoPtr, WPARAM wStyleType, 02666 STYLESTRUCT *lpss) 02667 { 02668 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 02669 wStyleType, lpss->styleOld, lpss->styleNew); 02670 02671 /* block MCS_MULTISELECT change */ 02672 if ((lpss->styleNew ^ lpss->styleOld) & MCS_MULTISELECT) 02673 { 02674 if (lpss->styleOld & MCS_MULTISELECT) 02675 lpss->styleNew |= MCS_MULTISELECT; 02676 else 02677 lpss->styleNew &= ~MCS_MULTISELECT; 02678 } 02679 02680 /* block MCS_DAYSTATE change */ 02681 if ((lpss->styleNew ^ lpss->styleOld) & MCS_DAYSTATE) 02682 { 02683 if (lpss->styleOld & MCS_DAYSTATE) 02684 lpss->styleNew |= MCS_DAYSTATE; 02685 else 02686 lpss->styleNew &= ~MCS_DAYSTATE; 02687 } 02688 02689 return 0; 02690 } 02691 02692 /* FIXME: check whether dateMin/dateMax need to be adjusted. */ 02693 static LRESULT 02694 MONTHCAL_Create(HWND hwnd, LPCREATESTRUCTW lpcs) 02695 { 02696 MONTHCAL_INFO *infoPtr; 02697 02698 /* allocate memory for info structure */ 02699 infoPtr = Alloc(sizeof(MONTHCAL_INFO)); 02700 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); 02701 02702 if (infoPtr == NULL) { 02703 ERR("could not allocate info memory!\n"); 02704 return 0; 02705 } 02706 02707 infoPtr->hwndSelf = hwnd; 02708 infoPtr->hwndNotify = lpcs->hwndParent; 02709 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE); 02710 infoPtr->dim.cx = infoPtr->dim.cy = 1; 02711 infoPtr->calendars = Alloc(sizeof(CALENDAR_INFO)); 02712 if (!infoPtr->calendars) goto fail; 02713 infoPtr->monthdayState = Alloc(3*sizeof(MONTHDAYSTATE)); 02714 if (!infoPtr->monthdayState) goto fail; 02715 02716 /* initialize info structure */ 02717 /* FIXME: calculate systemtime ->> localtime(subtract timezoneinfo) */ 02718 02719 GetLocalTime(&infoPtr->todaysDate); 02720 MONTHCAL_SetFirstDayOfWeek(infoPtr, -1); 02721 02722 infoPtr->maxSelCount = (infoPtr->dwStyle & MCS_MULTISELECT) ? 7 : 1; 02723 02724 infoPtr->colors[MCSC_BACKGROUND] = comctl32_color.clrWindow; 02725 infoPtr->colors[MCSC_TEXT] = comctl32_color.clrWindowText; 02726 infoPtr->colors[MCSC_TITLEBK] = comctl32_color.clrActiveCaption; 02727 infoPtr->colors[MCSC_TITLETEXT] = comctl32_color.clrWindow; 02728 infoPtr->colors[MCSC_MONTHBK] = comctl32_color.clrWindow; 02729 infoPtr->colors[MCSC_TRAILINGTEXT] = comctl32_color.clrGrayText; 02730 02731 infoPtr->brushes[BrushBackground] = CreateSolidBrush(infoPtr->colors[MCSC_BACKGROUND]); 02732 infoPtr->brushes[BrushTitle] = CreateSolidBrush(infoPtr->colors[MCSC_TITLEBK]); 02733 infoPtr->brushes[BrushMonth] = CreateSolidBrush(infoPtr->colors[MCSC_MONTHBK]); 02734 02735 infoPtr->pens[PenRed] = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); 02736 infoPtr->pens[PenText] = CreatePen(PS_SOLID, 1, infoPtr->colors[MCSC_TEXT]); 02737 02738 infoPtr->minSel = infoPtr->todaysDate; 02739 infoPtr->maxSel = infoPtr->todaysDate; 02740 infoPtr->calendars[0].month = infoPtr->todaysDate; 02741 infoPtr->isUnicode = TRUE; 02742 02743 /* setup control layout and day state data */ 02744 MONTHCAL_UpdateSize(infoPtr); 02745 02746 /* today auto update timer, to be freed only on control destruction */ 02747 SetTimer(infoPtr->hwndSelf, MC_TODAYUPDATETIMER, MC_TODAYUPDATEDELAY, 0); 02748 02749 OpenThemeData (infoPtr->hwndSelf, themeClass); 02750 02751 return 0; 02752 02753 fail: 02754 Free(infoPtr->monthdayState); 02755 Free(infoPtr->calendars); 02756 Free(infoPtr); 02757 return 0; 02758 } 02759 02760 static LRESULT 02761 MONTHCAL_Destroy(MONTHCAL_INFO *infoPtr) 02762 { 02763 INT i; 02764 02765 /* free month calendar info data */ 02766 Free(infoPtr->monthdayState); 02767 Free(infoPtr->calendars); 02768 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0); 02769 02770 CloseThemeData (GetWindowTheme (infoPtr->hwndSelf)); 02771 02772 for (i = 0; i < BrushLast; i++) DeleteObject(infoPtr->brushes[i]); 02773 for (i = 0; i < PenLast; i++) DeleteObject(infoPtr->pens[i]); 02774 02775 Free(infoPtr); 02776 return 0; 02777 } 02778 02779 /* 02780 * Handler for WM_NOTIFY messages 02781 */ 02782 static LRESULT 02783 MONTHCAL_Notify(MONTHCAL_INFO *infoPtr, NMHDR *hdr) 02784 { 02785 /* notification from year edit updown */ 02786 if (hdr->code == UDN_DELTAPOS) 02787 { 02788 NMUPDOWN *nmud = (NMUPDOWN*)hdr; 02789 02790 if (hdr->hwndFrom == infoPtr->hWndYearUpDown && nmud->iDelta) 02791 { 02792 /* year value limits are set up explicitly after updown creation */ 02793 MONTHCAL_Scroll(infoPtr, 12 * nmud->iDelta); 02794 MONTHCAL_NotifyDayState(infoPtr); 02795 MONTHCAL_NotifySelectionChange(infoPtr); 02796 } 02797 } 02798 return 0; 02799 } 02800 02801 static inline BOOL 02802 MONTHCAL_SetUnicodeFormat(MONTHCAL_INFO *infoPtr, BOOL isUnicode) 02803 { 02804 BOOL prev = infoPtr->isUnicode; 02805 infoPtr->isUnicode = isUnicode; 02806 return prev; 02807 } 02808 02809 static inline BOOL 02810 MONTHCAL_GetUnicodeFormat(const MONTHCAL_INFO *infoPtr) 02811 { 02812 return infoPtr->isUnicode; 02813 } 02814 02815 static LRESULT WINAPI 02816 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 02817 { 02818 MONTHCAL_INFO *infoPtr = (MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0); 02819 02820 TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd, uMsg, wParam, lParam); 02821 02822 if (!infoPtr && (uMsg != WM_CREATE)) 02823 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 02824 switch(uMsg) 02825 { 02826 case MCM_GETCURSEL: 02827 return MONTHCAL_GetCurSel(infoPtr, (LPSYSTEMTIME)lParam); 02828 02829 case MCM_SETCURSEL: 02830 return MONTHCAL_SetCurSel(infoPtr, (LPSYSTEMTIME)lParam); 02831 02832 case MCM_GETMAXSELCOUNT: 02833 return MONTHCAL_GetMaxSelCount(infoPtr); 02834 02835 case MCM_SETMAXSELCOUNT: 02836 return MONTHCAL_SetMaxSelCount(infoPtr, wParam); 02837 02838 case MCM_GETSELRANGE: 02839 return MONTHCAL_GetSelRange(infoPtr, (LPSYSTEMTIME)lParam); 02840 02841 case MCM_SETSELRANGE: 02842 return MONTHCAL_SetSelRange(infoPtr, (LPSYSTEMTIME)lParam); 02843 02844 case MCM_GETMONTHRANGE: 02845 return MONTHCAL_GetMonthRange(infoPtr, wParam, (SYSTEMTIME*)lParam); 02846 02847 case MCM_SETDAYSTATE: 02848 return MONTHCAL_SetDayState(infoPtr, (INT)wParam, (LPMONTHDAYSTATE)lParam); 02849 02850 case MCM_GETMINREQRECT: 02851 return MONTHCAL_GetMinReqRect(infoPtr, (LPRECT)lParam); 02852 02853 case MCM_GETCOLOR: 02854 return MONTHCAL_GetColor(infoPtr, wParam); 02855 02856 case MCM_SETCOLOR: 02857 return MONTHCAL_SetColor(infoPtr, wParam, (COLORREF)lParam); 02858 02859 case MCM_GETTODAY: 02860 return MONTHCAL_GetToday(infoPtr, (LPSYSTEMTIME)lParam); 02861 02862 case MCM_SETTODAY: 02863 return MONTHCAL_SetToday(infoPtr, (LPSYSTEMTIME)lParam); 02864 02865 case MCM_HITTEST: 02866 return MONTHCAL_HitTest(infoPtr, (PMCHITTESTINFO)lParam); 02867 02868 case MCM_GETFIRSTDAYOFWEEK: 02869 return MONTHCAL_GetFirstDayOfWeek(infoPtr); 02870 02871 case MCM_SETFIRSTDAYOFWEEK: 02872 return MONTHCAL_SetFirstDayOfWeek(infoPtr, (INT)lParam); 02873 02874 case MCM_GETRANGE: 02875 return MONTHCAL_GetRange(infoPtr, (LPSYSTEMTIME)lParam); 02876 02877 case MCM_SETRANGE: 02878 return MONTHCAL_SetRange(infoPtr, (SHORT)wParam, (LPSYSTEMTIME)lParam); 02879 02880 case MCM_GETMONTHDELTA: 02881 return MONTHCAL_GetMonthDelta(infoPtr); 02882 02883 case MCM_SETMONTHDELTA: 02884 return MONTHCAL_SetMonthDelta(infoPtr, wParam); 02885 02886 case MCM_GETMAXTODAYWIDTH: 02887 return MONTHCAL_GetMaxTodayWidth(infoPtr); 02888 02889 case MCM_SETUNICODEFORMAT: 02890 return MONTHCAL_SetUnicodeFormat(infoPtr, (BOOL)wParam); 02891 02892 case MCM_GETUNICODEFORMAT: 02893 return MONTHCAL_GetUnicodeFormat(infoPtr); 02894 02895 case MCM_GETCALENDARCOUNT: 02896 return MONTHCAL_GetCalCount(infoPtr); 02897 02898 case WM_GETDLGCODE: 02899 return DLGC_WANTARROWS | DLGC_WANTCHARS; 02900 02901 case WM_RBUTTONUP: 02902 return MONTHCAL_RButtonUp(infoPtr, lParam); 02903 02904 case WM_LBUTTONDOWN: 02905 return MONTHCAL_LButtonDown(infoPtr, lParam); 02906 02907 case WM_MOUSEMOVE: 02908 return MONTHCAL_MouseMove(infoPtr, lParam); 02909 02910 case WM_LBUTTONUP: 02911 return MONTHCAL_LButtonUp(infoPtr, lParam); 02912 02913 case WM_PAINT: 02914 return MONTHCAL_Paint(infoPtr, (HDC)wParam); 02915 02916 case WM_PRINTCLIENT: 02917 return MONTHCAL_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam); 02918 02919 case WM_ERASEBKGND: 02920 return MONTHCAL_EraseBkgnd(infoPtr, (HDC)wParam); 02921 02922 case WM_SETFOCUS: 02923 return MONTHCAL_SetFocus(infoPtr); 02924 02925 case WM_SIZE: 02926 return MONTHCAL_Size(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 02927 02928 case WM_NOTIFY: 02929 return MONTHCAL_Notify(infoPtr, (NMHDR*)lParam); 02930 02931 case WM_CREATE: 02932 return MONTHCAL_Create(hwnd, (LPCREATESTRUCTW)lParam); 02933 02934 case WM_SETFONT: 02935 return MONTHCAL_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam); 02936 02937 case WM_GETFONT: 02938 return MONTHCAL_GetFont(infoPtr); 02939 02940 case WM_TIMER: 02941 return MONTHCAL_Timer(infoPtr, wParam); 02942 02943 case WM_THEMECHANGED: 02944 return theme_changed (infoPtr); 02945 02946 case WM_DESTROY: 02947 return MONTHCAL_Destroy(infoPtr); 02948 02949 case WM_SYSCOLORCHANGE: 02950 COMCTL32_RefreshSysColors(); 02951 return 0; 02952 02953 case WM_STYLECHANGED: 02954 return MONTHCAL_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam); 02955 02956 case WM_STYLECHANGING: 02957 return MONTHCAL_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam); 02958 02959 default: 02960 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) 02961 ERR( "unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam); 02962 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 02963 } 02964 } 02965 02966 02967 void 02968 MONTHCAL_Register(void) 02969 { 02970 WNDCLASSW wndClass; 02971 02972 ZeroMemory(&wndClass, sizeof(WNDCLASSW)); 02973 wndClass.style = CS_GLOBALCLASS; 02974 wndClass.lpfnWndProc = MONTHCAL_WindowProc; 02975 wndClass.cbClsExtra = 0; 02976 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *); 02977 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); 02978 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 02979 wndClass.lpszClassName = MONTHCAL_CLASSW; 02980 02981 RegisterClassW(&wndClass); 02982 } 02983 02984 02985 void 02986 MONTHCAL_Unregister(void) 02987 { 02988 UnregisterClassW(MONTHCAL_CLASSW, NULL); 02989 } Generated on Sun May 27 2012 04:21:05 for ReactOS by
1.7.6.1
|