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
33namespace {
34
35CFX_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
55CFX_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
87CFWL_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
117CFWL_MonthCalendar::~CFWL_MonthCalendar() {
118  ClearDateItem();
119  m_arrSelDays.clear();
120}
121
122FWL_Type CFWL_MonthCalendar::GetClassID() const {
123  return FWL_Type::MonthCalendar;
124}
125
126CFX_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
133void 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
150void 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
175void CFWL_MonthCalendar::SetSelect(int32_t iYear,
176                                   int32_t iMonth,
177                                   int32_t iDay) {
178  ChangeToMonth(iYear, iMonth);
179  AddSelDay(iDay);
180}
181
182void 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
196void 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
210void 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
224void 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
238void 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
258void 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
272void 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
302void 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
327void 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
348void 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
373void 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
387void 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
411CFX_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
474void 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
483void 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
495void 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
527void 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
548void CFWL_MonthCalendar::GetCapValue() {
549  if (!m_pProperties->m_pThemeProvider)
550    m_pProperties->m_pThemeProvider = GetAvailableTheme();
551}
552
553void 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
568void CFWL_MonthCalendar::ClearDateItem() {
569  m_arrDates.clear();
570}
571
572void 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
595void 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
612void 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
630void 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
641void 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
650void 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
662void 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
675CFX_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
686CFX_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
694int32_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
704CFX_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
712void 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
749void CFWL_MonthCalendar::OnDrawWidget(CFX_Graphics* pGraphics,
750                                      const CFX_Matrix* pMatrix) {
751  DrawWidget(pGraphics, pMatrix);
752}
753
754void 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
773void 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
817void 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
851void 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
880void 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
890CFWL_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
901CFWL_MonthCalendar::DATEINFO::~DATEINFO() {}
902