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