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/check.h"
14 #include "third_party/base/notreached.h"
15 #include "third_party/base/stl_util.h"
16 #include "xfa/fde/cfde_textout.h"
17 #include "xfa/fwl/cfwl_datetimepicker.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_HMARGIN 3
26 #define MONTHCAL_VMARGIN 2
27 #define MONTHCAL_ROWS 9
28 #define MONTHCAL_COLUMNS 7
29 #define MONTHCAL_HEADER_BTN_VMARGIN 7
30 #define MONTHCAL_HEADER_BTN_HMARGIN 5
31 
32 namespace {
33 
GetAbbreviatedDayOfWeek(int day)34 WideString GetAbbreviatedDayOfWeek(int day) {
35   switch (day) {
36     case 0:
37       return L"Sun";
38     case 1:
39       return L"Mon";
40     case 2:
41       return L"Tue";
42     case 3:
43       return L"Wed";
44     case 4:
45       return L"Thu";
46     case 5:
47       return L"Fri";
48     case 6:
49       return L"Sat";
50     default:
51       NOTREACHED();
52       return L"";
53   }
54 }
55 
GetMonth(int month)56 WideString GetMonth(int month) {
57   switch (month) {
58     case 0:
59       return L"January";
60     case 1:
61       return L"February";
62     case 2:
63       return L"March";
64     case 3:
65       return L"April";
66     case 4:
67       return L"May";
68     case 5:
69       return L"June";
70     case 6:
71       return L"July";
72     case 7:
73       return L"August";
74     case 8:
75       return L"September";
76     case 9:
77       return L"October";
78     case 10:
79       return L"November";
80     case 11:
81       return L"December";
82     default:
83       NOTREACHED();
84       return L"";
85   }
86 }
87 
88 }  // namespace
89 
CFWL_MonthCalendar(CFWL_App * app,const Properties & properties,CFWL_Widget * pOuter)90 CFWL_MonthCalendar::CFWL_MonthCalendar(CFWL_App* app,
91                                        const Properties& properties,
92                                        CFWL_Widget* pOuter)
93     : CFWL_Widget(app, properties, pOuter) {}
94 
95 CFWL_MonthCalendar::~CFWL_MonthCalendar() = default;
96 
GetClassID() const97 FWL_Type CFWL_MonthCalendar::GetClassID() const {
98   return FWL_Type::MonthCalendar;
99 }
100 
GetAutosizedWidgetRect()101 CFX_RectF CFWL_MonthCalendar::GetAutosizedWidgetRect() {
102   CFX_SizeF fs = CalcSize();
103   CFX_RectF rect(0, 0, fs.width, fs.height);
104   InflateWidgetRect(rect);
105   return rect;
106 }
107 
Update()108 void CFWL_MonthCalendar::Update() {
109   if (IsLocked())
110     return;
111 
112   if (!m_bInitialized) {
113     InitDate();
114     m_bInitialized = true;
115   }
116   ClearDateItem();
117   ResetDateItem();
118   Layout();
119 }
120 
DrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)121 void CFWL_MonthCalendar::DrawWidget(CFGAS_GEGraphics* pGraphics,
122                                     const CFX_Matrix& matrix) {
123   if (!pGraphics)
124     return;
125 
126   if (HasBorder())
127     DrawBorder(pGraphics, CFWL_Part::Border, matrix);
128 
129   DrawBackground(pGraphics, &matrix);
130   DrawHeadBK(pGraphics, &matrix);
131   DrawLButton(pGraphics, &matrix);
132   DrawRButton(pGraphics, &matrix);
133   DrawSeparator(pGraphics, &matrix);
134   DrawDatesInBK(pGraphics, &matrix);
135   DrawDatesInCircle(pGraphics, &matrix);
136   DrawCaption(pGraphics, &matrix);
137   DrawWeek(pGraphics, &matrix);
138   DrawDatesIn(pGraphics, &matrix);
139   DrawDatesOut(pGraphics, &matrix);
140   DrawToday(pGraphics, &matrix);
141 }
142 
SetSelect(int32_t iYear,int32_t iMonth,int32_t iDay)143 void CFWL_MonthCalendar::SetSelect(int32_t iYear,
144                                    int32_t iMonth,
145                                    int32_t iDay) {
146   ChangeToMonth(iYear, iMonth);
147   AddSelDay(iDay);
148 }
149 
DrawBackground(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)150 void CFWL_MonthCalendar::DrawBackground(CFGAS_GEGraphics* pGraphics,
151                                         const CFX_Matrix* pMatrix) {
152   CFWL_ThemeBackground params;
153   params.m_pWidget = this;
154   params.m_iPart = CFWL_Part::Background;
155   params.m_pGraphics = pGraphics;
156   params.m_dwStates = CFWL_PartState_Normal;
157   params.m_PartRect = m_ClientRect;
158   if (pMatrix)
159     params.m_matrix.Concat(*pMatrix);
160   GetThemeProvider()->DrawBackground(params);
161 }
162 
DrawHeadBK(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)163 void CFWL_MonthCalendar::DrawHeadBK(CFGAS_GEGraphics* pGraphics,
164                                     const CFX_Matrix* pMatrix) {
165   CFWL_ThemeBackground params;
166   params.m_pWidget = this;
167   params.m_iPart = CFWL_Part::Header;
168   params.m_pGraphics = pGraphics;
169   params.m_dwStates = CFWL_PartState_Normal;
170   params.m_PartRect = m_HeadRect;
171   if (pMatrix)
172     params.m_matrix.Concat(*pMatrix);
173   GetThemeProvider()->DrawBackground(params);
174 }
175 
DrawLButton(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)176 void CFWL_MonthCalendar::DrawLButton(CFGAS_GEGraphics* pGraphics,
177                                      const CFX_Matrix* pMatrix) {
178   CFWL_ThemeBackground params;
179   params.m_pWidget = this;
180   params.m_iPart = CFWL_Part::LBtn;
181   params.m_pGraphics = pGraphics;
182   params.m_dwStates = m_iLBtnPartStates;
183   params.m_PartRect = m_LBtnRect;
184   if (pMatrix)
185     params.m_matrix.Concat(*pMatrix);
186   GetThemeProvider()->DrawBackground(params);
187 }
188 
DrawRButton(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)189 void CFWL_MonthCalendar::DrawRButton(CFGAS_GEGraphics* pGraphics,
190                                      const CFX_Matrix* pMatrix) {
191   CFWL_ThemeBackground params;
192   params.m_pWidget = this;
193   params.m_iPart = CFWL_Part::RBtn;
194   params.m_pGraphics = pGraphics;
195   params.m_dwStates = m_iRBtnPartStates;
196   params.m_PartRect = m_RBtnRect;
197   if (pMatrix)
198     params.m_matrix.Concat(*pMatrix);
199   GetThemeProvider()->DrawBackground(params);
200 }
201 
DrawCaption(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)202 void CFWL_MonthCalendar::DrawCaption(CFGAS_GEGraphics* pGraphics,
203                                      const CFX_Matrix* pMatrix) {
204   CFWL_ThemeText textParam;
205   textParam.m_pWidget = this;
206   textParam.m_iPart = CFWL_Part::Caption;
207   textParam.m_dwStates = CFWL_PartState_Normal;
208   textParam.m_pGraphics = pGraphics;
209   textParam.m_wsText = GetHeadText(m_iCurYear, m_iCurMonth);
210   m_HeadSize = CalcTextSize(textParam.m_wsText, false);
211   CalcHeadSize();
212   textParam.m_PartRect = m_HeadTextRect;
213   textParam.m_dwTTOStyles.single_line_ = true;
214   textParam.m_iTTOAlign = FDE_TextAlignment::kCenter;
215   if (pMatrix)
216     textParam.m_matrix.Concat(*pMatrix);
217   GetThemeProvider()->DrawText(textParam);
218 }
219 
DrawSeparator(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)220 void CFWL_MonthCalendar::DrawSeparator(CFGAS_GEGraphics* pGraphics,
221                                        const CFX_Matrix* pMatrix) {
222   CFWL_ThemeBackground params;
223   params.m_pWidget = this;
224   params.m_iPart = CFWL_Part::HSeparator;
225   params.m_pGraphics = pGraphics;
226   params.m_dwStates = CFWL_PartState_Normal;
227   params.m_PartRect = m_HSepRect;
228   if (pMatrix)
229     params.m_matrix.Concat(*pMatrix);
230   GetThemeProvider()->DrawBackground(params);
231 }
232 
DrawDatesInBK(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)233 void CFWL_MonthCalendar::DrawDatesInBK(CFGAS_GEGraphics* pGraphics,
234                                        const CFX_Matrix* pMatrix) {
235   CFWL_ThemeBackground params;
236   params.m_pWidget = this;
237   params.m_iPart = CFWL_Part::DateInBK;
238   params.m_pGraphics = pGraphics;
239   if (pMatrix)
240     params.m_matrix.Concat(*pMatrix);
241 
242   IFWL_ThemeProvider* pTheme = GetThemeProvider();
243   int32_t iCount = pdfium::CollectionSize<int32_t>(m_DateArray);
244   for (int32_t j = 0; j < iCount; j++) {
245     DATEINFO* pDataInfo = m_DateArray[j].get();
246     if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Selected) {
247       params.m_dwStates |= CFWL_PartState_Selected;
248       if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Flag) {
249         params.m_dwStates |= CFWL_PartState_Flagged;
250       }
251     } else if (j == m_iHovered - 1) {
252       params.m_dwStates |= CFWL_PartState_Hovered;
253     } else if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Flag) {
254       params.m_dwStates = CFWL_PartState_Flagged;
255       pTheme->DrawBackground(params);
256     }
257     params.m_PartRect = pDataInfo->rect;
258     pTheme->DrawBackground(params);
259     params.m_dwStates = 0;
260   }
261 }
262 
DrawWeek(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)263 void CFWL_MonthCalendar::DrawWeek(CFGAS_GEGraphics* pGraphics,
264                                   const CFX_Matrix* pMatrix) {
265   CFWL_ThemeText params;
266   params.m_pWidget = this;
267   params.m_iPart = CFWL_Part::Week;
268   params.m_pGraphics = pGraphics;
269   params.m_dwStates = CFWL_PartState_Normal;
270   params.m_iTTOAlign = FDE_TextAlignment::kCenter;
271   params.m_dwTTOStyles.single_line_ = true;
272 
273   if (pMatrix)
274     params.m_matrix.Concat(*pMatrix);
275 
276   IFWL_ThemeProvider* pTheme = GetThemeProvider();
277   CFX_RectF rtDayOfWeek;
278   for (int32_t i = 0; i < 7; ++i) {
279     rtDayOfWeek = CFX_RectF(
280         m_WeekRect.left + i * (m_CellSize.width + MONTHCAL_HMARGIN * 2),
281         m_WeekRect.top, m_CellSize);
282 
283     params.m_PartRect = rtDayOfWeek;
284     params.m_wsText = GetAbbreviatedDayOfWeek(i);
285     pTheme->DrawText(params);
286   }
287 }
288 
DrawToday(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)289 void CFWL_MonthCalendar::DrawToday(CFGAS_GEGraphics* pGraphics,
290                                    const CFX_Matrix* pMatrix) {
291   CFWL_ThemeText params;
292   params.m_pWidget = this;
293   params.m_iPart = CFWL_Part::Today;
294   params.m_pGraphics = pGraphics;
295   params.m_dwStates = CFWL_PartState_Normal;
296   params.m_iTTOAlign = FDE_TextAlignment::kCenterLeft;
297   params.m_wsText = GetTodayText(m_iYear, m_iMonth, m_iDay);
298   m_TodaySize = CalcTextSize(params.m_wsText, false);
299   CalcTodaySize();
300   params.m_PartRect = m_TodayRect;
301   params.m_dwTTOStyles.single_line_ = true;
302   if (pMatrix)
303     params.m_matrix.Concat(*pMatrix);
304   GetThemeProvider()->DrawText(params);
305 }
306 
DrawDatesIn(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)307 void CFWL_MonthCalendar::DrawDatesIn(CFGAS_GEGraphics* pGraphics,
308                                      const CFX_Matrix* pMatrix) {
309   CFWL_ThemeText params;
310   params.m_pWidget = this;
311   params.m_iPart = CFWL_Part::DatesIn;
312   params.m_pGraphics = pGraphics;
313   params.m_dwStates = CFWL_PartState_Normal;
314   params.m_iTTOAlign = FDE_TextAlignment::kCenter;
315   if (pMatrix)
316     params.m_matrix.Concat(*pMatrix);
317 
318   IFWL_ThemeProvider* pTheme = GetThemeProvider();
319   int32_t iCount = pdfium::CollectionSize<int32_t>(m_DateArray);
320   for (int32_t j = 0; j < iCount; j++) {
321     DATEINFO* pDataInfo = m_DateArray[j].get();
322     params.m_wsText = pDataInfo->wsDay;
323     params.m_PartRect = pDataInfo->rect;
324     params.m_dwStates = pDataInfo->dwStates;
325     if (j + 1 == m_iHovered)
326       params.m_dwStates |= CFWL_PartState_Hovered;
327 
328     params.m_dwTTOStyles.single_line_ = true;
329     pTheme->DrawText(params);
330   }
331 }
332 
DrawDatesOut(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)333 void CFWL_MonthCalendar::DrawDatesOut(CFGAS_GEGraphics* pGraphics,
334                                       const CFX_Matrix* pMatrix) {
335   CFWL_ThemeText params;
336   params.m_pWidget = this;
337   params.m_iPart = CFWL_Part::DatesOut;
338   params.m_pGraphics = pGraphics;
339   params.m_dwStates = CFWL_PartState_Normal;
340   params.m_iTTOAlign = FDE_TextAlignment::kCenter;
341   if (pMatrix)
342     params.m_matrix.Concat(*pMatrix);
343   GetThemeProvider()->DrawText(params);
344 }
345 
DrawDatesInCircle(CFGAS_GEGraphics * pGraphics,const CFX_Matrix * pMatrix)346 void CFWL_MonthCalendar::DrawDatesInCircle(CFGAS_GEGraphics* pGraphics,
347                                            const CFX_Matrix* pMatrix) {
348   if (m_iMonth != m_iCurMonth || m_iYear != m_iCurYear)
349     return;
350 
351   if (m_iDay < 1 || m_iDay > pdfium::CollectionSize<int32_t>(m_DateArray))
352     return;
353 
354   DATEINFO* pDate = m_DateArray[m_iDay - 1].get();
355   if (!pDate)
356     return;
357 
358   CFWL_ThemeBackground params;
359   params.m_pWidget = this;
360   params.m_iPart = CFWL_Part::DateInCircle;
361   params.m_pGraphics = pGraphics;
362   params.m_PartRect = pDate->rect;
363   params.m_dwStates = CFWL_PartState_Normal;
364   if (pMatrix)
365     params.m_matrix.Concat(*pMatrix);
366   GetThemeProvider()->DrawBackground(params);
367 }
368 
CalcSize()369 CFX_SizeF CFWL_MonthCalendar::CalcSize() {
370   float fMaxWeekW = 0.0f;
371   float fMaxWeekH = 0.0f;
372   for (int i = 0; i < 7; ++i) {
373     CFX_SizeF sz = CalcTextSize(GetAbbreviatedDayOfWeek(i), false);
374     fMaxWeekW = (fMaxWeekW >= sz.width) ? fMaxWeekW : sz.width;
375     fMaxWeekH = (fMaxWeekH >= sz.height) ? fMaxWeekH : sz.height;
376   }
377   float fDayMaxW = 0.0f;
378   float fDayMaxH = 0.0f;
379   for (int day = 10; day <= 31; day++) {
380     CFX_SizeF sz = CalcTextSize(WideString::Format(L"%d", day), false);
381     fDayMaxW = (fDayMaxW >= sz.width) ? fDayMaxW : sz.width;
382     fDayMaxH = (fDayMaxH >= sz.height) ? fDayMaxH : sz.height;
383   }
384   m_CellSize.width =
385       static_cast<int>(0.5 + (fMaxWeekW >= fDayMaxW ? fMaxWeekW : fDayMaxW));
386   m_CellSize.height = fMaxWeekH >= fDayMaxH ? fMaxWeekH : fDayMaxH;
387 
388   CFX_SizeF fs;
389   fs.width = m_CellSize.width * MONTHCAL_COLUMNS +
390              MONTHCAL_HMARGIN * MONTHCAL_COLUMNS * 2 +
391              MONTHCAL_HEADER_BTN_HMARGIN * 2;
392 
393   float fMonthMaxW = 0.0f;
394   float fMonthMaxH = 0.0f;
395   for (int i = 0; i < 12; ++i) {
396     CFX_SizeF sz = CalcTextSize(GetMonth(i), false);
397     fMonthMaxW = (fMonthMaxW >= sz.width) ? fMonthMaxW : sz.width;
398     fMonthMaxH = (fMonthMaxH >= sz.height) ? fMonthMaxH : sz.height;
399   }
400 
401   CFX_SizeF szYear = CalcTextSize(GetHeadText(m_iYear, m_iMonth), false);
402   fMonthMaxH = std::max(fMonthMaxH, szYear.height);
403   m_HeadSize = CFX_SizeF(fMonthMaxW + szYear.width, fMonthMaxH);
404   fMonthMaxW =
405       m_HeadSize.width + MONTHCAL_HEADER_BTN_HMARGIN * 2 + m_CellSize.width * 2;
406   fs.width = std::max(fs.width, fMonthMaxW);
407 
408   m_wsToday = GetTodayText(m_iYear, m_iMonth, m_iDay);
409   m_TodaySize = CalcTextSize(m_wsToday, false);
410   m_TodaySize.height = (m_TodaySize.height >= m_CellSize.height)
411                            ? m_TodaySize.height
412                            : m_CellSize.height;
413   fs.height = m_CellSize.width + m_CellSize.height * (MONTHCAL_ROWS - 2) +
414               m_TodaySize.height + MONTHCAL_VMARGIN * MONTHCAL_ROWS * 2 +
415               MONTHCAL_HEADER_BTN_VMARGIN * 4;
416   return fs;
417 }
418 
CalcHeadSize()419 void CFWL_MonthCalendar::CalcHeadSize() {
420   float fHeadHMargin = (m_ClientRect.width - m_HeadSize.width) / 2;
421   float fHeadVMargin = (m_CellSize.width - m_HeadSize.height) / 2;
422   m_HeadTextRect = CFX_RectF(m_ClientRect.left + fHeadHMargin,
423                              m_ClientRect.top + MONTHCAL_HEADER_BTN_VMARGIN +
424                                  MONTHCAL_VMARGIN + fHeadVMargin,
425                              m_HeadSize);
426 }
427 
CalcTodaySize()428 void CFWL_MonthCalendar::CalcTodaySize() {
429   m_TodayFlagRect = CFX_RectF(
430       m_ClientRect.left + MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN,
431       m_DatesRect.bottom() + MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN,
432       m_CellSize.width, m_TodaySize.height);
433   m_TodayRect = CFX_RectF(
434       m_ClientRect.left + MONTHCAL_HEADER_BTN_HMARGIN + m_CellSize.width +
435           MONTHCAL_HMARGIN * 2,
436       m_DatesRect.bottom() + MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN,
437       m_TodaySize);
438 }
439 
Layout()440 void CFWL_MonthCalendar::Layout() {
441   m_ClientRect = GetClientRect();
442 
443   m_HeadRect = CFX_RectF(
444       m_ClientRect.left + MONTHCAL_HEADER_BTN_HMARGIN, m_ClientRect.top,
445       m_ClientRect.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
446       m_CellSize.width + (MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN) * 2);
447   m_WeekRect = CFX_RectF(m_ClientRect.left + MONTHCAL_HEADER_BTN_HMARGIN,
448                          m_HeadRect.bottom(),
449                          m_ClientRect.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
450                          m_CellSize.height + MONTHCAL_VMARGIN * 2);
451   m_LBtnRect = CFX_RectF(m_ClientRect.left + MONTHCAL_HEADER_BTN_HMARGIN,
452                          m_ClientRect.top + MONTHCAL_HEADER_BTN_VMARGIN,
453                          m_CellSize.width, m_CellSize.width);
454   m_RBtnRect = CFX_RectF(m_ClientRect.left + m_ClientRect.width -
455                              MONTHCAL_HEADER_BTN_HMARGIN - m_CellSize.width,
456                          m_ClientRect.top + MONTHCAL_HEADER_BTN_VMARGIN,
457                          m_CellSize.width, m_CellSize.width);
458   m_HSepRect = CFX_RectF(
459       m_ClientRect.left + MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN,
460       m_WeekRect.bottom() - MONTHCAL_VMARGIN,
461       m_ClientRect.width - (MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN) * 2,
462       MONTHCAL_HSEP_HEIGHT);
463   m_DatesRect = CFX_RectF(m_ClientRect.left + MONTHCAL_HEADER_BTN_HMARGIN,
464                           m_WeekRect.bottom(),
465                           m_ClientRect.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
466                           m_CellSize.height * (MONTHCAL_ROWS - 3) +
467                               MONTHCAL_VMARGIN * (MONTHCAL_ROWS - 3) * 2);
468 
469   CalDateItem();
470 }
471 
CalDateItem()472 void CFWL_MonthCalendar::CalDateItem() {
473   bool bNewWeek = false;
474   int32_t iWeekOfMonth = 0;
475   float fLeft = m_DatesRect.left;
476   float fTop = m_DatesRect.top;
477   for (const auto& pDateInfo : m_DateArray) {
478     if (bNewWeek) {
479       iWeekOfMonth++;
480       bNewWeek = false;
481     }
482     pDateInfo->rect = CFX_RectF(
483         fLeft +
484             pDateInfo->iDayOfWeek * (m_CellSize.width + (MONTHCAL_HMARGIN * 2)),
485         fTop + iWeekOfMonth * (m_CellSize.height + (MONTHCAL_VMARGIN * 2)),
486         m_CellSize.width + (MONTHCAL_HMARGIN * 2),
487         m_CellSize.height + (MONTHCAL_VMARGIN * 2));
488     if (pDateInfo->iDayOfWeek >= 6)
489       bNewWeek = true;
490   }
491 }
492 
InitDate()493 void CFWL_MonthCalendar::InitDate() {
494   CFX_DateTime now = CFX_DateTime::Now();
495 
496   m_iYear = now.GetYear();
497   m_iMonth = now.GetMonth();
498   m_iDay = now.GetDay();
499   m_iCurYear = m_iYear;
500   m_iCurMonth = m_iMonth;
501 
502   m_wsToday = GetTodayText(m_iYear, m_iMonth, m_iDay);
503   m_wsHead = GetHeadText(m_iCurYear, m_iCurMonth);
504   m_dtMin = DATE(1500, 12, 1);
505   m_dtMax = DATE(2200, 1, 1);
506 }
507 
ClearDateItem()508 void CFWL_MonthCalendar::ClearDateItem() {
509   m_DateArray.clear();
510 }
511 
ResetDateItem()512 void CFWL_MonthCalendar::ResetDateItem() {
513   int32_t iDays = FX_DaysInMonth(m_iCurYear, m_iCurMonth);
514   int32_t iDayOfWeek =
515       CFX_DateTime(m_iCurYear, m_iCurMonth, 1, 0, 0, 0, 0).GetDayOfWeek();
516   for (int32_t i = 0; i < iDays; i++) {
517     if (iDayOfWeek >= 7)
518       iDayOfWeek = 0;
519 
520     uint32_t dwStates = 0;
521     if (m_iYear == m_iCurYear && m_iMonth == m_iCurMonth && m_iDay == (i + 1))
522       dwStates |= FWL_ITEMSTATE_MCD_Flag;
523     if (pdfium::Contains(m_SelDayArray, i + 1))
524       dwStates |= FWL_ITEMSTATE_MCD_Selected;
525 
526     CFX_RectF rtDate;
527     m_DateArray.push_back(std::make_unique<DATEINFO>(
528         i + 1, iDayOfWeek, dwStates, rtDate, WideString::Format(L"%d", i + 1)));
529     iDayOfWeek++;
530   }
531 }
532 
NextMonth()533 void CFWL_MonthCalendar::NextMonth() {
534   int32_t iYear = m_iCurYear;
535   int32_t iMonth = m_iCurMonth;
536   if (iMonth >= 12) {
537     iMonth = 1;
538     iYear++;
539   } else {
540     iMonth++;
541   }
542   DATE dt(m_iCurYear, m_iCurMonth, 1);
543   if (!(dt < m_dtMax))
544     return;
545 
546   m_iCurYear = iYear, m_iCurMonth = iMonth;
547   ChangeToMonth(m_iCurYear, m_iCurMonth);
548 }
549 
PrevMonth()550 void CFWL_MonthCalendar::PrevMonth() {
551   int32_t iYear = m_iCurYear;
552   int32_t iMonth = m_iCurMonth;
553   if (iMonth <= 1) {
554     iMonth = 12;
555     iYear--;
556   } else {
557     iMonth--;
558   }
559 
560   DATE dt(m_iCurYear, m_iCurMonth, 1);
561   if (!(dt > m_dtMin))
562     return;
563 
564   m_iCurYear = iYear, m_iCurMonth = iMonth;
565   ChangeToMonth(m_iCurYear, m_iCurMonth);
566 }
567 
ChangeToMonth(int32_t iYear,int32_t iMonth)568 void CFWL_MonthCalendar::ChangeToMonth(int32_t iYear, int32_t iMonth) {
569   m_iCurYear = iYear;
570   m_iCurMonth = iMonth;
571   m_iHovered = -1;
572 
573   ClearDateItem();
574   ResetDateItem();
575   CalDateItem();
576   m_wsHead = GetHeadText(m_iCurYear, m_iCurMonth);
577 }
578 
RemoveSelDay()579 void CFWL_MonthCalendar::RemoveSelDay() {
580   int32_t iDatesCount = pdfium::CollectionSize<int32_t>(m_DateArray);
581   for (int32_t iSelDay : m_SelDayArray) {
582     if (iSelDay <= iDatesCount)
583       m_DateArray[iSelDay - 1]->dwStates &= ~FWL_ITEMSTATE_MCD_Selected;
584   }
585   m_SelDayArray.clear();
586 }
587 
AddSelDay(int32_t iDay)588 void CFWL_MonthCalendar::AddSelDay(int32_t iDay) {
589   DCHECK(iDay > 0);
590   if (!pdfium::Contains(m_SelDayArray, iDay))
591     return;
592 
593   RemoveSelDay();
594   if (iDay <= pdfium::CollectionSize<int32_t>(m_DateArray))
595     m_DateArray[iDay - 1]->dwStates |= FWL_ITEMSTATE_MCD_Selected;
596 
597   m_SelDayArray.push_back(iDay);
598 }
599 
JumpToToday()600 void CFWL_MonthCalendar::JumpToToday() {
601   if (m_iYear != m_iCurYear || m_iMonth != m_iCurMonth) {
602     m_iCurYear = m_iYear;
603     m_iCurMonth = m_iMonth;
604     ChangeToMonth(m_iYear, m_iMonth);
605     AddSelDay(m_iDay);
606     return;
607   }
608 
609   if (!pdfium::Contains(m_SelDayArray, m_iDay))
610     AddSelDay(m_iDay);
611 }
612 
GetHeadText(int32_t iYear,int32_t iMonth)613 WideString CFWL_MonthCalendar::GetHeadText(int32_t iYear, int32_t iMonth) {
614   DCHECK(iMonth > 0);
615   DCHECK(iMonth < 13);
616 
617   static const wchar_t* const pMonth[] = {L"January", L"February", L"March",
618                                           L"April",   L"May",      L"June",
619                                           L"July",    L"August",   L"September",
620                                           L"October", L"November", L"December"};
621   return WideString::Format(L"%ls, %d", pMonth[iMonth - 1], iYear);
622 }
623 
GetTodayText(int32_t iYear,int32_t iMonth,int32_t iDay)624 WideString CFWL_MonthCalendar::GetTodayText(int32_t iYear,
625                                             int32_t iMonth,
626                                             int32_t iDay) {
627   return WideString::Format(L"Today, %d/%d/%d", iDay, iMonth, iYear);
628 }
629 
GetDayAtPoint(const CFX_PointF & point) const630 int32_t CFWL_MonthCalendar::GetDayAtPoint(const CFX_PointF& point) const {
631   int i = 1;  // one-based day values.
632   for (const auto& pDateInfo : m_DateArray) {
633     if (pDateInfo->rect.Contains(point))
634       return i;
635     ++i;
636   }
637   return -1;
638 }
639 
GetDayRect(int32_t iDay)640 CFX_RectF CFWL_MonthCalendar::GetDayRect(int32_t iDay) {
641   if (iDay <= 0 || iDay > pdfium::CollectionSize<int32_t>(m_DateArray))
642     return CFX_RectF();
643 
644   DATEINFO* pDateInfo = m_DateArray[iDay - 1].get();
645   return pDateInfo ? pDateInfo->rect : CFX_RectF();
646 }
647 
OnProcessMessage(CFWL_Message * pMessage)648 void CFWL_MonthCalendar::OnProcessMessage(CFWL_Message* pMessage) {
649   switch (pMessage->GetType()) {
650     case CFWL_Message::Type::kSetFocus:
651     case CFWL_Message::Type::kKillFocus:
652       GetOuter()->GetDelegate()->OnProcessMessage(pMessage);
653       break;
654     case CFWL_Message::Type::kKey:
655       break;
656     case CFWL_Message::Type::kMouse: {
657       CFWL_MessageMouse* pMouse = static_cast<CFWL_MessageMouse*>(pMessage);
658       switch (pMouse->m_dwCmd) {
659         case FWL_MouseCommand::LeftButtonDown:
660           OnLButtonDown(pMouse);
661           break;
662         case FWL_MouseCommand::LeftButtonUp:
663           OnLButtonUp(pMouse);
664           break;
665         case FWL_MouseCommand::Move:
666           OnMouseMove(pMouse);
667           break;
668         case FWL_MouseCommand::Leave:
669           OnMouseLeave(pMouse);
670           break;
671         default:
672           break;
673       }
674       break;
675     }
676     default:
677       break;
678   }
679   // Dst target could be |this|, continue only if not destroyed by above.
680   if (pMessage->GetDstTarget())
681     CFWL_Widget::OnProcessMessage(pMessage);
682 }
683 
OnDrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)684 void CFWL_MonthCalendar::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
685                                       const CFX_Matrix& matrix) {
686   DrawWidget(pGraphics, matrix);
687 }
688 
OnLButtonDown(CFWL_MessageMouse * pMsg)689 void CFWL_MonthCalendar::OnLButtonDown(CFWL_MessageMouse* pMsg) {
690   if (m_LBtnRect.Contains(pMsg->m_pos)) {
691     m_iLBtnPartStates = CFWL_PartState_Pressed;
692     PrevMonth();
693     RepaintRect(m_ClientRect);
694   } else if (m_RBtnRect.Contains(pMsg->m_pos)) {
695     m_iRBtnPartStates |= CFWL_PartState_Pressed;
696     NextMonth();
697     RepaintRect(m_ClientRect);
698   } else if (m_TodayRect.Contains(pMsg->m_pos)) {
699     JumpToToday();
700     RepaintRect(m_ClientRect);
701   }
702 }
703 
OnLButtonUp(CFWL_MessageMouse * pMsg)704 void CFWL_MonthCalendar::OnLButtonUp(CFWL_MessageMouse* pMsg) {
705   if (m_LBtnRect.Contains(pMsg->m_pos)) {
706     m_iLBtnPartStates = 0;
707     RepaintRect(m_LBtnRect);
708     return;
709   }
710   if (m_RBtnRect.Contains(pMsg->m_pos)) {
711     m_iRBtnPartStates = 0;
712     RepaintRect(m_RBtnRect);
713     return;
714   }
715   if (m_TodayRect.Contains(pMsg->m_pos))
716     return;
717 
718   int32_t iOldSel = 0;
719   if (!m_SelDayArray.empty())
720     iOldSel = m_SelDayArray[0];
721 
722   int32_t iCurSel = GetDayAtPoint(pMsg->m_pos);
723   if (iCurSel > 0) {
724     DATEINFO* lpDatesInfo = m_DateArray[iCurSel - 1].get();
725     CFX_RectF rtInvalidate(lpDatesInfo->rect);
726     if (iOldSel > 0 &&
727         iOldSel <= pdfium::CollectionSize<int32_t>(m_DateArray)) {
728       lpDatesInfo = m_DateArray[iOldSel - 1].get();
729       rtInvalidate.Union(lpDatesInfo->rect);
730     }
731     AddSelDay(iCurSel);
732     CFWL_DateTimePicker* pDateTime =
733         static_cast<CFWL_DateTimePicker*>(GetOuter());
734     pDateTime->ProcessSelChanged(m_iCurYear, m_iCurMonth, iCurSel);
735     pDateTime->ShowMonthCalendar(false);
736   }
737 }
738 
OnMouseMove(CFWL_MessageMouse * pMsg)739 void CFWL_MonthCalendar::OnMouseMove(CFWL_MessageMouse* pMsg) {
740   bool bRepaint = false;
741   CFX_RectF rtInvalidate;
742   if (m_DatesRect.Contains(pMsg->m_pos)) {
743     int32_t iHover = GetDayAtPoint(pMsg->m_pos);
744     bRepaint = m_iHovered != iHover;
745     if (bRepaint) {
746       if (m_iHovered > 0)
747         rtInvalidate = GetDayRect(m_iHovered);
748       if (iHover > 0) {
749         CFX_RectF rtDay = GetDayRect(iHover);
750         if (rtInvalidate.IsEmpty())
751           rtInvalidate = rtDay;
752         else
753           rtInvalidate.Union(rtDay);
754       }
755     }
756     m_iHovered = iHover;
757   } else {
758     bRepaint = m_iHovered > 0;
759     if (bRepaint)
760       rtInvalidate = GetDayRect(m_iHovered);
761 
762     m_iHovered = -1;
763   }
764   if (bRepaint && !rtInvalidate.IsEmpty())
765     RepaintRect(rtInvalidate);
766 }
767 
OnMouseLeave(CFWL_MessageMouse * pMsg)768 void CFWL_MonthCalendar::OnMouseLeave(CFWL_MessageMouse* pMsg) {
769   if (m_iHovered <= 0)
770     return;
771 
772   CFX_RectF rtInvalidate = GetDayRect(m_iHovered);
773   m_iHovered = -1;
774   if (!rtInvalidate.IsEmpty())
775     RepaintRect(rtInvalidate);
776 }
777 
DATEINFO(int32_t day,int32_t dayofweek,uint32_t dwSt,CFX_RectF rc,const WideString & wsday)778 CFWL_MonthCalendar::DATEINFO::DATEINFO(int32_t day,
779                                        int32_t dayofweek,
780                                        uint32_t dwSt,
781                                        CFX_RectF rc,
782                                        const WideString& wsday)
783     : iDay(day),
784       iDayOfWeek(dayofweek),
785       dwStates(dwSt),
786       rect(rc),
787       wsDay(wsday) {}
788 
789 CFWL_MonthCalendar::DATEINFO::~DATEINFO() = default;
790