Home | History | Annotate | Download | only in fwl
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "xfa/fwl/cfwl_monthcalendar.h"
      8 
      9 #include <algorithm>
     10 #include <memory>
     11 #include <utility>
     12 
     13 #include "third_party/base/ptr_util.h"
     14 #include "third_party/base/stl_util.h"
     15 #include "xfa/fde/tto/fde_textout.h"
     16 #include "xfa/fwl/cfwl_datetimepicker.h"
     17 #include "xfa/fwl/cfwl_formproxy.h"
     18 #include "xfa/fwl/cfwl_messagemouse.h"
     19 #include "xfa/fwl/cfwl_notedriver.h"
     20 #include "xfa/fwl/cfwl_themebackground.h"
     21 #include "xfa/fwl/cfwl_themetext.h"
     22 #include "xfa/fwl/ifwl_themeprovider.h"
     23 
     24 #define MONTHCAL_HSEP_HEIGHT 1
     25 #define MONTHCAL_VSEP_WIDTH 1
     26 #define MONTHCAL_HMARGIN 3
     27 #define MONTHCAL_VMARGIN 2
     28 #define MONTHCAL_ROWS 9
     29 #define MONTHCAL_COLUMNS 7
     30 #define MONTHCAL_HEADER_BTN_VMARGIN 7
     31 #define MONTHCAL_HEADER_BTN_HMARGIN 5
     32 
     33 namespace {
     34 
     35 CFX_WideString GetCapacityForDay(IFWL_ThemeProvider* pTheme,
     36                                  CFWL_ThemePart& params,
     37                                  uint32_t day) {
     38   ASSERT(day < 7);
     39 
     40   if (day == 0)
     41     return L"Sun";
     42   if (day == 1)
     43     return L"Mon";
     44   if (day == 2)
     45     return L"Tue";
     46   if (day == 3)
     47     return L"Wed";
     48   if (day == 4)
     49     return L"Thu";
     50   if (day == 5)
     51     return L"Fri";
     52   return L"Sat";
     53 }
     54 
     55 CFX_WideString GetCapacityForMonth(IFWL_ThemeProvider* pTheme,
     56                                    CFWL_ThemePart& params,
     57                                    uint32_t month) {
     58   ASSERT(month < 12);
     59 
     60   if (month == 0)
     61     return L"January";
     62   if (month == 1)
     63     return L"February";
     64   if (month == 2)
     65     return L"March";
     66   if (month == 3)
     67     return L"April";
     68   if (month == 4)
     69     return L"May";
     70   if (month == 5)
     71     return L"June";
     72   if (month == 6)
     73     return L"July";
     74   if (month == 7)
     75     return L"August";
     76   if (month == 8)
     77     return L"September";
     78   if (month == 9)
     79     return L"October";
     80   if (month == 10)
     81     return L"November";
     82   return L"December";
     83 }
     84 
     85 }  // namespace
     86 
     87 CFWL_MonthCalendar::CFWL_MonthCalendar(
     88     const CFWL_App* app,
     89     std::unique_ptr<CFWL_WidgetProperties> properties,
     90     CFWL_Widget* pOuter)
     91     : CFWL_Widget(app, std::move(properties), pOuter),
     92       m_bInitialized(false),
     93       m_pDateTime(new CFX_DateTime),
     94       m_iCurYear(2011),
     95       m_iCurMonth(1),
     96       m_iYear(2011),
     97       m_iMonth(1),
     98       m_iDay(1),
     99       m_iHovered(-1),
    100       m_iLBtnPartStates(CFWL_PartState_Normal),
    101       m_iRBtnPartStates(CFWL_PartState_Normal),
    102       m_bFlag(false) {
    103   m_rtHead.Reset();
    104   m_rtWeek.Reset();
    105   m_rtLBtn.Reset();
    106   m_rtRBtn.Reset();
    107   m_rtDates.Reset();
    108   m_rtHSep.Reset();
    109   m_rtHeadText.Reset();
    110   m_rtToday.Reset();
    111   m_rtTodayFlag.Reset();
    112   m_rtClient.Reset();
    113   m_rtWeekNum.Reset();
    114   m_rtWeekNumSep.Reset();
    115 }
    116 
    117 CFWL_MonthCalendar::~CFWL_MonthCalendar() {
    118   ClearDateItem();
    119   m_arrSelDays.clear();
    120 }
    121 
    122 FWL_Type CFWL_MonthCalendar::GetClassID() const {
    123   return FWL_Type::MonthCalendar;
    124 }
    125 
    126 CFX_RectF CFWL_MonthCalendar::GetAutosizedWidgetRect() {
    127   CFX_SizeF fs = CalcSize();
    128   CFX_RectF rect(0, 0, fs.width, fs.height);
    129   InflateWidgetRect(rect);
    130   return rect;
    131 }
    132 
    133 void CFWL_MonthCalendar::Update() {
    134   if (IsLocked())
    135     return;
    136   if (!m_pProperties->m_pThemeProvider)
    137     m_pProperties->m_pThemeProvider = GetAvailableTheme();
    138 
    139   GetCapValue();
    140   if (!m_bInitialized) {
    141     InitDate();
    142     m_bInitialized = true;
    143   }
    144 
    145   ClearDateItem();
    146   ResetDateItem();
    147   Layout();
    148 }
    149 
    150 void CFWL_MonthCalendar::DrawWidget(CFX_Graphics* pGraphics,
    151                                     const CFX_Matrix* pMatrix) {
    152   if (!pGraphics)
    153     return;
    154   if (!m_pProperties->m_pThemeProvider)
    155     m_pProperties->m_pThemeProvider = GetAvailableTheme();
    156 
    157   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
    158   if (HasBorder())
    159     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, pMatrix);
    160 
    161   DrawBackground(pGraphics, pTheme, pMatrix);
    162   DrawHeadBK(pGraphics, pTheme, pMatrix);
    163   DrawLButton(pGraphics, pTheme, pMatrix);
    164   DrawRButton(pGraphics, pTheme, pMatrix);
    165   DrawSeperator(pGraphics, pTheme, pMatrix);
    166   DrawDatesInBK(pGraphics, pTheme, pMatrix);
    167   DrawDatesInCircle(pGraphics, pTheme, pMatrix);
    168   DrawCaption(pGraphics, pTheme, pMatrix);
    169   DrawWeek(pGraphics, pTheme, pMatrix);
    170   DrawDatesIn(pGraphics, pTheme, pMatrix);
    171   DrawDatesOut(pGraphics, pTheme, pMatrix);
    172   DrawToday(pGraphics, pTheme, pMatrix);
    173 }
    174 
    175 void CFWL_MonthCalendar::SetSelect(int32_t iYear,
    176                                    int32_t iMonth,
    177                                    int32_t iDay) {
    178   ChangeToMonth(iYear, iMonth);
    179   AddSelDay(iDay);
    180 }
    181 
    182 void CFWL_MonthCalendar::DrawBackground(CFX_Graphics* pGraphics,
    183                                         IFWL_ThemeProvider* pTheme,
    184                                         const CFX_Matrix* pMatrix) {
    185   CFWL_ThemeBackground params;
    186   params.m_pWidget = this;
    187   params.m_iPart = CFWL_Part::Background;
    188   params.m_pGraphics = pGraphics;
    189   params.m_dwStates = CFWL_PartState_Normal;
    190   params.m_rtPart = m_rtClient;
    191   if (pMatrix)
    192     params.m_matrix.Concat(*pMatrix);
    193   pTheme->DrawBackground(&params);
    194 }
    195 
    196 void CFWL_MonthCalendar::DrawHeadBK(CFX_Graphics* pGraphics,
    197                                     IFWL_ThemeProvider* pTheme,
    198                                     const CFX_Matrix* pMatrix) {
    199   CFWL_ThemeBackground params;
    200   params.m_pWidget = this;
    201   params.m_iPart = CFWL_Part::Header;
    202   params.m_pGraphics = pGraphics;
    203   params.m_dwStates = CFWL_PartState_Normal;
    204   params.m_rtPart = m_rtHead;
    205   if (pMatrix)
    206     params.m_matrix.Concat(*pMatrix);
    207   pTheme->DrawBackground(&params);
    208 }
    209 
    210 void CFWL_MonthCalendar::DrawLButton(CFX_Graphics* pGraphics,
    211                                      IFWL_ThemeProvider* pTheme,
    212                                      const CFX_Matrix* pMatrix) {
    213   CFWL_ThemeBackground params;
    214   params.m_pWidget = this;
    215   params.m_iPart = CFWL_Part::LBtn;
    216   params.m_pGraphics = pGraphics;
    217   params.m_dwStates = m_iLBtnPartStates;
    218   params.m_rtPart = m_rtLBtn;
    219   if (pMatrix)
    220     params.m_matrix.Concat(*pMatrix);
    221   pTheme->DrawBackground(&params);
    222 }
    223 
    224 void CFWL_MonthCalendar::DrawRButton(CFX_Graphics* pGraphics,
    225                                      IFWL_ThemeProvider* pTheme,
    226                                      const CFX_Matrix* pMatrix) {
    227   CFWL_ThemeBackground params;
    228   params.m_pWidget = this;
    229   params.m_iPart = CFWL_Part::RBtn;
    230   params.m_pGraphics = pGraphics;
    231   params.m_dwStates = m_iRBtnPartStates;
    232   params.m_rtPart = m_rtRBtn;
    233   if (pMatrix)
    234     params.m_matrix.Concat(*pMatrix);
    235   pTheme->DrawBackground(&params);
    236 }
    237 
    238 void CFWL_MonthCalendar::DrawCaption(CFX_Graphics* pGraphics,
    239                                      IFWL_ThemeProvider* pTheme,
    240                                      const CFX_Matrix* pMatrix) {
    241   CFWL_ThemeText textParam;
    242   textParam.m_pWidget = this;
    243   textParam.m_iPart = CFWL_Part::Caption;
    244   textParam.m_dwStates = CFWL_PartState_Normal;
    245   textParam.m_pGraphics = pGraphics;
    246   textParam.m_wsText = GetHeadText(m_iCurYear, m_iCurMonth);
    247   m_szHead =
    248       CalcTextSize(textParam.m_wsText, m_pProperties->m_pThemeProvider, false);
    249   CalcHeadSize();
    250   textParam.m_rtPart = m_rtHeadText;
    251   textParam.m_dwTTOStyles = FDE_TTOSTYLE_SingleLine;
    252   textParam.m_iTTOAlign = FDE_TTOALIGNMENT_Center;
    253   if (pMatrix)
    254     textParam.m_matrix.Concat(*pMatrix);
    255   pTheme->DrawText(&textParam);
    256 }
    257 
    258 void CFWL_MonthCalendar::DrawSeperator(CFX_Graphics* pGraphics,
    259                                        IFWL_ThemeProvider* pTheme,
    260                                        const CFX_Matrix* pMatrix) {
    261   CFWL_ThemeBackground params;
    262   params.m_pWidget = this;
    263   params.m_iPart = CFWL_Part::HSeparator;
    264   params.m_pGraphics = pGraphics;
    265   params.m_dwStates = CFWL_PartState_Normal;
    266   params.m_rtPart = m_rtHSep;
    267   if (pMatrix)
    268     params.m_matrix.Concat(*pMatrix);
    269   pTheme->DrawBackground(&params);
    270 }
    271 
    272 void CFWL_MonthCalendar::DrawDatesInBK(CFX_Graphics* pGraphics,
    273                                        IFWL_ThemeProvider* pTheme,
    274                                        const CFX_Matrix* pMatrix) {
    275   CFWL_ThemeBackground params;
    276   params.m_pWidget = this;
    277   params.m_iPart = CFWL_Part::DateInBK;
    278   params.m_pGraphics = pGraphics;
    279   if (pMatrix)
    280     params.m_matrix.Concat(*pMatrix);
    281 
    282   int32_t iCount = pdfium::CollectionSize<int32_t>(m_arrDates);
    283   for (int32_t j = 0; j < iCount; j++) {
    284     DATEINFO* pDataInfo = m_arrDates[j].get();
    285     if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Selected) {
    286       params.m_dwStates |= CFWL_PartState_Selected;
    287       if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Flag) {
    288         params.m_dwStates |= CFWL_PartState_Flagged;
    289       }
    290     } else if (j == m_iHovered - 1) {
    291       params.m_dwStates |= CFWL_PartState_Hovered;
    292     } else if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Flag) {
    293       params.m_dwStates = CFWL_PartState_Flagged;
    294       pTheme->DrawBackground(&params);
    295     }
    296     params.m_rtPart = pDataInfo->rect;
    297     pTheme->DrawBackground(&params);
    298     params.m_dwStates = 0;
    299   }
    300 }
    301 
    302 void CFWL_MonthCalendar::DrawWeek(CFX_Graphics* pGraphics,
    303                                   IFWL_ThemeProvider* pTheme,
    304                                   const CFX_Matrix* pMatrix) {
    305   CFWL_ThemeText params;
    306   params.m_pWidget = this;
    307   params.m_iPart = CFWL_Part::Week;
    308   params.m_pGraphics = pGraphics;
    309   params.m_dwStates = CFWL_PartState_Normal;
    310   params.m_iTTOAlign = FDE_TTOALIGNMENT_Center;
    311   CFX_RectF rtDayOfWeek;
    312   if (pMatrix)
    313     params.m_matrix.Concat(*pMatrix);
    314 
    315   for (int32_t i = 0; i < 7; i++) {
    316     rtDayOfWeek =
    317         CFX_RectF(m_rtWeek.left + i * (m_szCell.width + MONTHCAL_HMARGIN * 2),
    318                   m_rtWeek.top, m_szCell);
    319 
    320     params.m_rtPart = rtDayOfWeek;
    321     params.m_wsText = GetCapacityForDay(pTheme, params, i);
    322     params.m_dwTTOStyles = FDE_TTOSTYLE_SingleLine;
    323     pTheme->DrawText(&params);
    324   }
    325 }
    326 
    327 void CFWL_MonthCalendar::DrawToday(CFX_Graphics* pGraphics,
    328                                    IFWL_ThemeProvider* pTheme,
    329                                    const CFX_Matrix* pMatrix) {
    330   CFWL_ThemeText params;
    331   params.m_pWidget = this;
    332   params.m_iPart = CFWL_Part::Today;
    333   params.m_pGraphics = pGraphics;
    334   params.m_dwStates = CFWL_PartState_Normal;
    335   params.m_iTTOAlign = FDE_TTOALIGNMENT_CenterLeft;
    336   params.m_wsText = L"Today" + GetTodayText(m_iYear, m_iMonth, m_iDay);
    337 
    338   m_szToday =
    339       CalcTextSize(params.m_wsText, m_pProperties->m_pThemeProvider, false);
    340   CalcTodaySize();
    341   params.m_rtPart = m_rtToday;
    342   params.m_dwTTOStyles = FDE_TTOSTYLE_SingleLine;
    343   if (pMatrix)
    344     params.m_matrix.Concat(*pMatrix);
    345   pTheme->DrawText(&params);
    346 }
    347 
    348 void CFWL_MonthCalendar::DrawDatesIn(CFX_Graphics* pGraphics,
    349                                      IFWL_ThemeProvider* pTheme,
    350                                      const CFX_Matrix* pMatrix) {
    351   CFWL_ThemeText params;
    352   params.m_pWidget = this;
    353   params.m_iPart = CFWL_Part::DatesIn;
    354   params.m_pGraphics = pGraphics;
    355   params.m_dwStates = CFWL_PartState_Normal;
    356   params.m_iTTOAlign = FDE_TTOALIGNMENT_Center;
    357   if (pMatrix)
    358     params.m_matrix.Concat(*pMatrix);
    359 
    360   int32_t iCount = pdfium::CollectionSize<int32_t>(m_arrDates);
    361   for (int32_t j = 0; j < iCount; j++) {
    362     DATEINFO* pDataInfo = m_arrDates[j].get();
    363     params.m_wsText = pDataInfo->wsDay;
    364     params.m_rtPart = pDataInfo->rect;
    365     params.m_dwStates = pDataInfo->dwStates;
    366     if (j + 1 == m_iHovered)
    367       params.m_dwStates |= CFWL_PartState_Hovered;
    368     params.m_dwTTOStyles = FDE_TTOSTYLE_SingleLine;
    369     pTheme->DrawText(&params);
    370   }
    371 }
    372 
    373 void CFWL_MonthCalendar::DrawDatesOut(CFX_Graphics* pGraphics,
    374                                       IFWL_ThemeProvider* pTheme,
    375                                       const CFX_Matrix* pMatrix) {
    376   CFWL_ThemeText params;
    377   params.m_pWidget = this;
    378   params.m_iPart = CFWL_Part::DatesOut;
    379   params.m_pGraphics = pGraphics;
    380   params.m_dwStates = CFWL_PartState_Normal;
    381   params.m_iTTOAlign = FDE_TTOALIGNMENT_Center;
    382   if (pMatrix)
    383     params.m_matrix.Concat(*pMatrix);
    384   pTheme->DrawText(&params);
    385 }
    386 
    387 void CFWL_MonthCalendar::DrawDatesInCircle(CFX_Graphics* pGraphics,
    388                                            IFWL_ThemeProvider* pTheme,
    389                                            const CFX_Matrix* pMatrix) {
    390   if (m_iMonth != m_iCurMonth || m_iYear != m_iCurYear)
    391     return;
    392 
    393   if (m_iDay < 1 || m_iDay > pdfium::CollectionSize<int32_t>(m_arrDates))
    394     return;
    395 
    396   DATEINFO* pDate = m_arrDates[m_iDay - 1].get();
    397   if (!pDate)
    398     return;
    399 
    400   CFWL_ThemeBackground params;
    401   params.m_pWidget = this;
    402   params.m_iPart = CFWL_Part::DateInCircle;
    403   params.m_pGraphics = pGraphics;
    404   params.m_rtPart = pDate->rect;
    405   params.m_dwStates = CFWL_PartState_Normal;
    406   if (pMatrix)
    407     params.m_matrix.Concat(*pMatrix);
    408   pTheme->DrawBackground(&params);
    409 }
    410 
    411 CFX_SizeF CFWL_MonthCalendar::CalcSize() {
    412   if (!m_pProperties->m_pThemeProvider)
    413     return CFX_SizeF();
    414 
    415   CFWL_ThemePart params;
    416   params.m_pWidget = this;
    417   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
    418   FX_FLOAT fMaxWeekW = 0.0f;
    419   FX_FLOAT fMaxWeekH = 0.0f;
    420 
    421   for (uint32_t i = 0; i < 7; ++i) {
    422     CFX_SizeF sz = CalcTextSize(GetCapacityForDay(pTheme, params, i),
    423                                 m_pProperties->m_pThemeProvider, false);
    424     fMaxWeekW = (fMaxWeekW >= sz.width) ? fMaxWeekW : sz.width;
    425     fMaxWeekH = (fMaxWeekH >= sz.height) ? fMaxWeekH : sz.height;
    426   }
    427 
    428   FX_FLOAT fDayMaxW = 0.0f;
    429   FX_FLOAT fDayMaxH = 0.0f;
    430   for (int day = 10; day <= 31; day++) {
    431     CFX_WideString wsDay;
    432     wsDay.Format(L"%d", day);
    433     CFX_SizeF sz = CalcTextSize(wsDay, m_pProperties->m_pThemeProvider, false);
    434     fDayMaxW = (fDayMaxW >= sz.width) ? fDayMaxW : sz.width;
    435     fDayMaxH = (fDayMaxH >= sz.height) ? fDayMaxH : sz.height;
    436   }
    437   m_szCell.width = FX_FLOAT((fMaxWeekW >= fDayMaxW) ? (int)(fMaxWeekW + 0.5)
    438                                                     : (int)(fDayMaxW + 0.5));
    439   m_szCell.height = (fMaxWeekH >= fDayMaxH) ? fMaxWeekH : fDayMaxH;
    440 
    441   CFX_SizeF fs;
    442   fs.width = m_szCell.width * MONTHCAL_COLUMNS +
    443              MONTHCAL_HMARGIN * MONTHCAL_COLUMNS * 2 +
    444              MONTHCAL_HEADER_BTN_HMARGIN * 2;
    445   FX_FLOAT fMonthMaxW = 0.0f;
    446   FX_FLOAT fMonthMaxH = 0.0f;
    447 
    448   for (uint32_t i = 0; i < 12; ++i) {
    449     CFX_SizeF sz = CalcTextSize(GetCapacityForMonth(pTheme, params, i),
    450                                 m_pProperties->m_pThemeProvider, false);
    451     fMonthMaxW = (fMonthMaxW >= sz.width) ? fMonthMaxW : sz.width;
    452     fMonthMaxH = (fMonthMaxH >= sz.height) ? fMonthMaxH : sz.height;
    453   }
    454 
    455   CFX_SizeF szYear = CalcTextSize(GetHeadText(m_iYear, m_iMonth),
    456                                   m_pProperties->m_pThemeProvider, false);
    457   fMonthMaxH = std::max(fMonthMaxH, szYear.height);
    458   m_szHead = CFX_SizeF(fMonthMaxW + szYear.width, fMonthMaxH);
    459   fMonthMaxW =
    460       m_szHead.width + MONTHCAL_HEADER_BTN_HMARGIN * 2 + m_szCell.width * 2;
    461   fs.width = std::max(fs.width, fMonthMaxW);
    462 
    463   CFX_WideString wsToday = GetTodayText(m_iYear, m_iMonth, m_iDay);
    464   m_wsToday = L"Today" + wsToday;
    465   m_szToday = CalcTextSize(wsToday, m_pProperties->m_pThemeProvider, false);
    466   m_szToday.height = (m_szToday.height >= m_szCell.height) ? m_szToday.height
    467                                                            : m_szCell.height;
    468   fs.height = m_szCell.width + m_szCell.height * (MONTHCAL_ROWS - 2) +
    469               m_szToday.height + MONTHCAL_VMARGIN * MONTHCAL_ROWS * 2 +
    470               MONTHCAL_HEADER_BTN_VMARGIN * 4;
    471   return fs;
    472 }
    473 
    474 void CFWL_MonthCalendar::CalcHeadSize() {
    475   FX_FLOAT fHeadHMargin = (m_rtClient.width - m_szHead.width) / 2;
    476   FX_FLOAT fHeadVMargin = (m_szCell.width - m_szHead.height) / 2;
    477   m_rtHeadText = CFX_RectF(m_rtClient.left + fHeadHMargin,
    478                            m_rtClient.top + MONTHCAL_HEADER_BTN_VMARGIN +
    479                                MONTHCAL_VMARGIN + fHeadVMargin,
    480                            m_szHead);
    481 }
    482 
    483 void CFWL_MonthCalendar::CalcTodaySize() {
    484   m_rtTodayFlag = CFX_RectF(
    485       m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN,
    486       m_rtDates.bottom() + MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN,
    487       m_szCell.width, m_szToday.height);
    488   m_rtToday = CFX_RectF(
    489       m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN + m_szCell.width +
    490           MONTHCAL_HMARGIN * 2,
    491       m_rtDates.bottom() + MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN,
    492       m_szToday);
    493 }
    494 
    495 void CFWL_MonthCalendar::Layout() {
    496   m_rtClient = GetClientRect();
    497 
    498   m_rtHead = CFX_RectF(
    499       m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN, m_rtClient.top,
    500       m_rtClient.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
    501       m_szCell.width + (MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN) * 2);
    502   m_rtWeek = CFX_RectF(m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN,
    503                        m_rtHead.bottom(),
    504                        m_rtClient.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
    505                        m_szCell.height + MONTHCAL_VMARGIN * 2);
    506   m_rtLBtn = CFX_RectF(m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN,
    507                        m_rtClient.top + MONTHCAL_HEADER_BTN_VMARGIN,
    508                        m_szCell.width, m_szCell.width);
    509   m_rtRBtn = CFX_RectF(m_rtClient.left + m_rtClient.width -
    510                            MONTHCAL_HEADER_BTN_HMARGIN - m_szCell.width,
    511                        m_rtClient.top + MONTHCAL_HEADER_BTN_VMARGIN,
    512                        m_szCell.width, m_szCell.width);
    513   m_rtHSep = CFX_RectF(
    514       m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN,
    515       m_rtWeek.bottom() - MONTHCAL_VMARGIN,
    516       m_rtClient.width - (MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN) * 2,
    517       MONTHCAL_HSEP_HEIGHT);
    518   m_rtDates = CFX_RectF(m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN,
    519                         m_rtWeek.bottom(),
    520                         m_rtClient.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
    521                         m_szCell.height * (MONTHCAL_ROWS - 3) +
    522                             MONTHCAL_VMARGIN * (MONTHCAL_ROWS - 3) * 2);
    523 
    524   CalDateItem();
    525 }
    526 
    527 void CFWL_MonthCalendar::CalDateItem() {
    528   bool bNewWeek = false;
    529   int32_t iWeekOfMonth = 0;
    530   FX_FLOAT fLeft = m_rtDates.left;
    531   FX_FLOAT fTop = m_rtDates.top;
    532   for (const auto& pDateInfo : m_arrDates) {
    533     if (bNewWeek) {
    534       iWeekOfMonth++;
    535       bNewWeek = false;
    536     }
    537     pDateInfo->rect = CFX_RectF(
    538         fLeft +
    539             pDateInfo->iDayOfWeek * (m_szCell.width + (MONTHCAL_HMARGIN * 2)),
    540         fTop + iWeekOfMonth * (m_szCell.height + (MONTHCAL_VMARGIN * 2)),
    541         m_szCell.width + (MONTHCAL_HMARGIN * 2),
    542         m_szCell.height + (MONTHCAL_VMARGIN * 2));
    543     if (pDateInfo->iDayOfWeek >= 6)
    544       bNewWeek = true;
    545   }
    546 }
    547 
    548 void CFWL_MonthCalendar::GetCapValue() {
    549   if (!m_pProperties->m_pThemeProvider)
    550     m_pProperties->m_pThemeProvider = GetAvailableTheme();
    551 }
    552 
    553 void CFWL_MonthCalendar::InitDate() {
    554   // TODO(dsinclair): These should pull the real today values instead of
    555   // pretending it's 2011-01-01.
    556   m_iYear = 2011;
    557   m_iMonth = 1;
    558   m_iDay = 1;
    559   m_iCurYear = m_iYear;
    560   m_iCurMonth = m_iMonth;
    561 
    562   m_wsToday = GetTodayText(m_iYear, m_iMonth, m_iDay);
    563   m_wsHead = GetHeadText(m_iCurYear, m_iCurMonth);
    564   m_dtMin = DATE(1500, 12, 1);
    565   m_dtMax = DATE(2200, 1, 1);
    566 }
    567 
    568 void CFWL_MonthCalendar::ClearDateItem() {
    569   m_arrDates.clear();
    570 }
    571 
    572 void CFWL_MonthCalendar::ResetDateItem() {
    573   m_pDateTime->Set(m_iCurYear, m_iCurMonth, 1);
    574   int32_t iDays = FX_DaysInMonth(m_iCurYear, m_iCurMonth);
    575   int32_t iDayOfWeek = m_pDateTime->GetDayOfWeek();
    576   for (int32_t i = 0; i < iDays; i++) {
    577     if (iDayOfWeek >= 7)
    578       iDayOfWeek = 0;
    579 
    580     CFX_WideString wsDay;
    581     wsDay.Format(L"%d", i + 1);
    582     uint32_t dwStates = 0;
    583     if (m_iYear == m_iCurYear && m_iMonth == m_iCurMonth && m_iDay == (i + 1))
    584       dwStates |= FWL_ITEMSTATE_MCD_Flag;
    585     if (pdfium::ContainsValue(m_arrSelDays, i + 1))
    586       dwStates |= FWL_ITEMSTATE_MCD_Selected;
    587 
    588     CFX_RectF rtDate;
    589     m_arrDates.push_back(pdfium::MakeUnique<DATEINFO>(i + 1, iDayOfWeek,
    590                                                       dwStates, rtDate, wsDay));
    591     iDayOfWeek++;
    592   }
    593 }
    594 
    595 void CFWL_MonthCalendar::NextMonth() {
    596   int32_t iYear = m_iCurYear;
    597   int32_t iMonth = m_iCurMonth;
    598   if (iMonth >= 12) {
    599     iMonth = 1;
    600     iYear++;
    601   } else {
    602     iMonth++;
    603   }
    604   DATE dt(m_iCurYear, m_iCurMonth, 1);
    605   if (!(dt < m_dtMax))
    606     return;
    607 
    608   m_iCurYear = iYear, m_iCurMonth = iMonth;
    609   ChangeToMonth(m_iCurYear, m_iCurMonth);
    610 }
    611 
    612 void CFWL_MonthCalendar::PrevMonth() {
    613   int32_t iYear = m_iCurYear;
    614   int32_t iMonth = m_iCurMonth;
    615   if (iMonth <= 1) {
    616     iMonth = 12;
    617     iYear--;
    618   } else {
    619     iMonth--;
    620   }
    621 
    622   DATE dt(m_iCurYear, m_iCurMonth, 1);
    623   if (!(dt > m_dtMin))
    624     return;
    625 
    626   m_iCurYear = iYear, m_iCurMonth = iMonth;
    627   ChangeToMonth(m_iCurYear, m_iCurMonth);
    628 }
    629 
    630 void CFWL_MonthCalendar::ChangeToMonth(int32_t iYear, int32_t iMonth) {
    631   m_iCurYear = iYear;
    632   m_iCurMonth = iMonth;
    633   m_iHovered = -1;
    634 
    635   ClearDateItem();
    636   ResetDateItem();
    637   CalDateItem();
    638   m_wsHead = GetHeadText(m_iCurYear, m_iCurMonth);
    639 }
    640 
    641 void CFWL_MonthCalendar::RemoveSelDay() {
    642   int32_t iDatesCount = pdfium::CollectionSize<int32_t>(m_arrDates);
    643   for (int32_t iSelDay : m_arrSelDays) {
    644     if (iSelDay <= iDatesCount)
    645       m_arrDates[iSelDay - 1]->dwStates &= ~FWL_ITEMSTATE_MCD_Selected;
    646   }
    647   m_arrSelDays.clear();
    648 }
    649 
    650 void CFWL_MonthCalendar::AddSelDay(int32_t iDay) {
    651   ASSERT(iDay > 0);
    652   if (!pdfium::ContainsValue(m_arrSelDays, iDay))
    653     return;
    654 
    655   RemoveSelDay();
    656   if (iDay <= pdfium::CollectionSize<int32_t>(m_arrDates))
    657     m_arrDates[iDay - 1]->dwStates |= FWL_ITEMSTATE_MCD_Selected;
    658 
    659   m_arrSelDays.push_back(iDay);
    660 }
    661 
    662 void CFWL_MonthCalendar::JumpToToday() {
    663   if (m_iYear != m_iCurYear || m_iMonth != m_iCurMonth) {
    664     m_iCurYear = m_iYear;
    665     m_iCurMonth = m_iMonth;
    666     ChangeToMonth(m_iYear, m_iMonth);
    667     AddSelDay(m_iDay);
    668     return;
    669   }
    670 
    671   if (!pdfium::ContainsValue(m_arrSelDays, m_iDay))
    672     AddSelDay(m_iDay);
    673 }
    674 
    675 CFX_WideString CFWL_MonthCalendar::GetHeadText(int32_t iYear, int32_t iMonth) {
    676   ASSERT(iMonth > 0 && iMonth < 13);
    677   static const FX_WCHAR* const pMonth[] = {
    678       L"January",   L"February", L"March",    L"April",
    679       L"May",       L"June",     L"July",     L"August",
    680       L"September", L"October",  L"November", L"December"};
    681   CFX_WideString wsHead;
    682   wsHead.Format(L"%s, %d", pMonth[iMonth - 1], iYear);
    683   return wsHead;
    684 }
    685 
    686 CFX_WideString CFWL_MonthCalendar::GetTodayText(int32_t iYear,
    687                                                 int32_t iMonth,
    688                                                 int32_t iDay) {
    689   CFX_WideString wsToday;
    690   wsToday.Format(L", %d/%d/%d", iDay, iMonth, iYear);
    691   return wsToday;
    692 }
    693 
    694 int32_t CFWL_MonthCalendar::GetDayAtPoint(const CFX_PointF& point) const {
    695   int i = 1;  // one-based day values.
    696   for (const auto& pDateInfo : m_arrDates) {
    697     if (pDateInfo->rect.Contains(point))
    698       return i;
    699     ++i;
    700   }
    701   return -1;
    702 }
    703 
    704 CFX_RectF CFWL_MonthCalendar::GetDayRect(int32_t iDay) {
    705   if (iDay <= 0 || iDay > pdfium::CollectionSize<int32_t>(m_arrDates))
    706     return CFX_RectF();
    707 
    708   DATEINFO* pDateInfo = m_arrDates[iDay - 1].get();
    709   return pDateInfo ? pDateInfo->rect : CFX_RectF();
    710 }
    711 
    712 void CFWL_MonthCalendar::OnProcessMessage(CFWL_Message* pMessage) {
    713   if (!pMessage)
    714     return;
    715 
    716   switch (pMessage->GetType()) {
    717     case CFWL_Message::Type::SetFocus:
    718     case CFWL_Message::Type::KillFocus:
    719       GetOuter()->GetDelegate()->OnProcessMessage(pMessage);
    720       break;
    721     case CFWL_Message::Type::Key:
    722       break;
    723     case CFWL_Message::Type::Mouse: {
    724       CFWL_MessageMouse* pMouse = static_cast<CFWL_MessageMouse*>(pMessage);
    725       switch (pMouse->m_dwCmd) {
    726         case FWL_MouseCommand::LeftButtonDown:
    727           OnLButtonDown(pMouse);
    728           break;
    729         case FWL_MouseCommand::LeftButtonUp:
    730           OnLButtonUp(pMouse);
    731           break;
    732         case FWL_MouseCommand::Move:
    733           OnMouseMove(pMouse);
    734           break;
    735         case FWL_MouseCommand::Leave:
    736           OnMouseLeave(pMouse);
    737           break;
    738         default:
    739           break;
    740       }
    741       break;
    742     }
    743     default:
    744       break;
    745   }
    746   CFWL_Widget::OnProcessMessage(pMessage);
    747 }
    748 
    749 void CFWL_MonthCalendar::OnDrawWidget(CFX_Graphics* pGraphics,
    750                                       const CFX_Matrix* pMatrix) {
    751   DrawWidget(pGraphics, pMatrix);
    752 }
    753 
    754 void CFWL_MonthCalendar::OnLButtonDown(CFWL_MessageMouse* pMsg) {
    755   if (m_rtLBtn.Contains(pMsg->m_pos)) {
    756     m_iLBtnPartStates = CFWL_PartState_Pressed;
    757     PrevMonth();
    758     RepaintRect(m_rtClient);
    759   } else if (m_rtRBtn.Contains(pMsg->m_pos)) {
    760     m_iRBtnPartStates |= CFWL_PartState_Pressed;
    761     NextMonth();
    762     RepaintRect(m_rtClient);
    763   } else if (m_rtToday.Contains(pMsg->m_pos)) {
    764     JumpToToday();
    765     RepaintRect(m_rtClient);
    766   } else {
    767     CFWL_DateTimePicker* pIPicker = static_cast<CFWL_DateTimePicker*>(m_pOuter);
    768     if (pIPicker->IsMonthCalendarVisible())
    769       m_bFlag = true;
    770   }
    771 }
    772 
    773 void CFWL_MonthCalendar::OnLButtonUp(CFWL_MessageMouse* pMsg) {
    774   if (m_pWidgetMgr->IsFormDisabled())
    775     return DisForm_OnLButtonUp(pMsg);
    776 
    777   if (m_rtLBtn.Contains(pMsg->m_pos)) {
    778     m_iLBtnPartStates = 0;
    779     RepaintRect(m_rtLBtn);
    780     return;
    781   }
    782   if (m_rtRBtn.Contains(pMsg->m_pos)) {
    783     m_iRBtnPartStates = 0;
    784     RepaintRect(m_rtRBtn);
    785     return;
    786   }
    787   if (m_rtToday.Contains(pMsg->m_pos))
    788     return;
    789 
    790   int32_t iOldSel = 0;
    791   if (!m_arrSelDays.empty())
    792     iOldSel = m_arrSelDays[0];
    793 
    794   int32_t iCurSel = GetDayAtPoint(pMsg->m_pos);
    795   CFWL_DateTimePicker* pIPicker = static_cast<CFWL_DateTimePicker*>(m_pOuter);
    796   if (iCurSel > 0) {
    797     DATEINFO* lpDatesInfo = m_arrDates[iCurSel - 1].get();
    798     CFX_RectF rtInvalidate(lpDatesInfo->rect);
    799     if (iOldSel > 0 && iOldSel <= pdfium::CollectionSize<int32_t>(m_arrDates)) {
    800       lpDatesInfo = m_arrDates[iOldSel - 1].get();
    801       rtInvalidate.Union(lpDatesInfo->rect);
    802     }
    803     AddSelDay(iCurSel);
    804     if (!m_pOuter)
    805       return;
    806 
    807     pIPicker->ProcessSelChanged(m_iCurYear, m_iCurMonth, iCurSel);
    808     pIPicker->ShowMonthCalendar(false);
    809   } else if (m_bFlag &&
    810              (!CFX_RectF(0, 0, pIPicker->GetFormProxy()->GetWidgetRect().Size())
    811                    .Contains(pMsg->m_pos))) {
    812     pIPicker->ShowMonthCalendar(false);
    813   }
    814   m_bFlag = false;
    815 }
    816 
    817 void CFWL_MonthCalendar::DisForm_OnLButtonUp(CFWL_MessageMouse* pMsg) {
    818   if (m_rtLBtn.Contains(pMsg->m_pos)) {
    819     m_iLBtnPartStates = 0;
    820     RepaintRect(m_rtLBtn);
    821     return;
    822   }
    823   if (m_rtRBtn.Contains(pMsg->m_pos)) {
    824     m_iRBtnPartStates = 0;
    825     RepaintRect(m_rtRBtn);
    826     return;
    827   }
    828   if (m_rtToday.Contains(pMsg->m_pos))
    829     return;
    830 
    831   int32_t iOldSel = 0;
    832   if (!m_arrSelDays.empty())
    833     iOldSel = m_arrSelDays[0];
    834 
    835   int32_t iCurSel = GetDayAtPoint(pMsg->m_pos);
    836   if (iCurSel > 0) {
    837     DATEINFO* lpDatesInfo = m_arrDates[iCurSel - 1].get();
    838     CFX_RectF rtInvalidate(lpDatesInfo->rect);
    839     if (iOldSel > 0 && iOldSel <= pdfium::CollectionSize<int32_t>(m_arrDates)) {
    840       lpDatesInfo = m_arrDates[iOldSel - 1].get();
    841       rtInvalidate.Union(lpDatesInfo->rect);
    842     }
    843     AddSelDay(iCurSel);
    844     CFWL_DateTimePicker* pDateTime =
    845         static_cast<CFWL_DateTimePicker*>(m_pOuter);
    846     pDateTime->ProcessSelChanged(m_iCurYear, m_iCurMonth, iCurSel);
    847     pDateTime->ShowMonthCalendar(false);
    848   }
    849 }
    850 
    851 void CFWL_MonthCalendar::OnMouseMove(CFWL_MessageMouse* pMsg) {
    852   bool bRepaint = false;
    853   CFX_RectF rtInvalidate;
    854   if (m_rtDates.Contains(pMsg->m_pos)) {
    855     int32_t iHover = GetDayAtPoint(pMsg->m_pos);
    856     bRepaint = m_iHovered != iHover;
    857     if (bRepaint) {
    858       if (m_iHovered > 0)
    859         rtInvalidate = GetDayRect(m_iHovered);
    860       if (iHover > 0) {
    861         CFX_RectF rtDay = GetDayRect(iHover);
    862         if (rtInvalidate.IsEmpty())
    863           rtInvalidate = rtDay;
    864         else
    865           rtInvalidate.Union(rtDay);
    866       }
    867     }
    868     m_iHovered = iHover;
    869   } else {
    870     bRepaint = m_iHovered > 0;
    871     if (bRepaint)
    872       rtInvalidate = GetDayRect(m_iHovered);
    873 
    874     m_iHovered = -1;
    875   }
    876   if (bRepaint && !rtInvalidate.IsEmpty())
    877     RepaintRect(rtInvalidate);
    878 }
    879 
    880 void CFWL_MonthCalendar::OnMouseLeave(CFWL_MessageMouse* pMsg) {
    881   if (m_iHovered <= 0)
    882     return;
    883 
    884   CFX_RectF rtInvalidate = GetDayRect(m_iHovered);
    885   m_iHovered = -1;
    886   if (!rtInvalidate.IsEmpty())
    887     RepaintRect(rtInvalidate);
    888 }
    889 
    890 CFWL_MonthCalendar::DATEINFO::DATEINFO(int32_t day,
    891                                        int32_t dayofweek,
    892                                        uint32_t dwSt,
    893                                        CFX_RectF rc,
    894                                        CFX_WideString& wsday)
    895     : iDay(day),
    896       iDayOfWeek(dayofweek),
    897       dwStates(dwSt),
    898       rect(rc),
    899       wsDay(wsday) {}
    900 
    901 CFWL_MonthCalendar::DATEINFO::~DATEINFO() {}
    902