xref: /aosp_15_r20/external/pdfium/xfa/fwl/cfwl_datetimepicker.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2014 The PDFium Authors
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_datetimepicker.h"
8 
9 #include "xfa/fwl/cfwl_app.h"
10 #include "xfa/fwl/cfwl_event.h"
11 #include "xfa/fwl/cfwl_eventselectchanged.h"
12 #include "xfa/fwl/cfwl_messagemouse.h"
13 #include "xfa/fwl/cfwl_messagesetfocus.h"
14 #include "xfa/fwl/cfwl_notedriver.h"
15 #include "xfa/fwl/cfwl_themebackground.h"
16 #include "xfa/fwl/cfwl_widgetmgr.h"
17 #include "xfa/fwl/ifwl_themeprovider.h"
18 
19 namespace {
20 
21 constexpr int kDateTimePickerHeight = 20;
22 
23 }  // namespace
24 
CFWL_DateTimePicker(CFWL_App * app)25 CFWL_DateTimePicker::CFWL_DateTimePicker(CFWL_App* app)
26     : CFWL_Widget(app,
27                   Properties{0, FWL_STYLEEXT_DTP_ShortDateFormat, 0},
28                   nullptr),
29       m_pEdit(cppgc::MakeGarbageCollected<CFWL_DateTimeEdit>(
30           app->GetHeap()->GetAllocationHandle(),
31           app,
32           Properties(),
33           this)),
34       m_pMonthCal(cppgc::MakeGarbageCollected<CFWL_MonthCalendar>(
35           app->GetHeap()->GetAllocationHandle(),
36           app,
37           Properties{FWL_STYLE_WGT_Popup | FWL_STYLE_WGT_Border, 0,
38                      FWL_STATE_WGT_Invisible},
39           this)) {
40   m_pMonthCal->SetWidgetRect(
41       CFX_RectF(0, 0, m_pMonthCal->GetAutosizedWidgetRect().Size()));
42 
43   CFWL_NoteDriver* pNoteDriver = GetFWLApp()->GetNoteDriver();
44   pNoteDriver->RegisterEventTarget(this, m_pMonthCal);
45   pNoteDriver->RegisterEventTarget(this, m_pEdit);
46 }
47 
48 CFWL_DateTimePicker::~CFWL_DateTimePicker() = default;
49 
PreFinalize()50 void CFWL_DateTimePicker::PreFinalize() {
51   UnregisterEventTarget();
52   CFWL_Widget::PreFinalize();
53 }
54 
Trace(cppgc::Visitor * visitor) const55 void CFWL_DateTimePicker::Trace(cppgc::Visitor* visitor) const {
56   CFWL_Widget::Trace(visitor);
57   visitor->Trace(m_pEdit);
58   visitor->Trace(m_pMonthCal);
59 }
60 
GetClassID() const61 FWL_Type CFWL_DateTimePicker::GetClassID() const {
62   return FWL_Type::DateTimePicker;
63 }
64 
Update()65 void CFWL_DateTimePicker::Update() {
66   if (IsLocked())
67     return;
68 
69   m_ClientRect = GetClientRect();
70   m_pEdit->SetWidgetRect(m_ClientRect);
71   ResetEditAlignment();
72   m_pEdit->Update();
73 
74   m_fBtn = GetThemeProvider()->GetScrollBarWidth();
75   CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
76   CFX_RectF rtPopUp(rtMonthCal.left, rtMonthCal.top + kDateTimePickerHeight,
77                     rtMonthCal.width, rtMonthCal.height);
78   m_pMonthCal->SetWidgetRect(rtPopUp);
79   m_pMonthCal->Update();
80 }
81 
HitTest(const CFX_PointF & point)82 FWL_WidgetHit CFWL_DateTimePicker::HitTest(const CFX_PointF& point) {
83   CFX_RectF rect(0, 0, m_WidgetRect.width, m_WidgetRect.height);
84   if (rect.Contains(point))
85     return FWL_WidgetHit::Edit;
86   if (NeedsToShowButton())
87     rect.width += m_fBtn;
88   if (rect.Contains(point))
89     return FWL_WidgetHit::Client;
90   if (IsMonthCalendarVisible()) {
91     if (m_pMonthCal->GetWidgetRect().Contains(point))
92       return FWL_WidgetHit::Client;
93   }
94   return FWL_WidgetHit::Unknown;
95 }
96 
DrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)97 void CFWL_DateTimePicker::DrawWidget(CFGAS_GEGraphics* pGraphics,
98                                      const CFX_Matrix& matrix) {
99   if (!pGraphics)
100     return;
101 
102   if (HasBorder())
103     DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
104 
105   if (!m_BtnRect.IsEmpty())
106     DrawDropDownButton(pGraphics, matrix);
107 
108   if (m_pEdit) {
109     CFX_RectF rtEdit = m_pEdit->GetWidgetRect();
110     CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top);
111     mt.Concat(matrix);
112     m_pEdit->DrawWidget(pGraphics, mt);
113   }
114   if (!IsMonthCalendarVisible())
115     return;
116 
117   CFX_RectF rtMonth = m_pMonthCal->GetWidgetRect();
118   CFX_Matrix mt(1, 0, 0, 1, rtMonth.left, rtMonth.top);
119   mt.Concat(matrix);
120   m_pMonthCal->DrawWidget(pGraphics, mt);
121 }
122 
GetCurSel(int32_t & iYear,int32_t & iMonth,int32_t & iDay)123 void CFWL_DateTimePicker::GetCurSel(int32_t& iYear,
124                                     int32_t& iMonth,
125                                     int32_t& iDay) {
126   iYear = m_iYear;
127   iMonth = m_iMonth;
128   iDay = m_iDay;
129 }
130 
SetCurSel(int32_t iYear,int32_t iMonth,int32_t iDay)131 void CFWL_DateTimePicker::SetCurSel(int32_t iYear,
132                                     int32_t iMonth,
133                                     int32_t iDay) {
134   if (iYear <= 0 || iYear >= 3000)
135     return;
136   if (iMonth <= 0 || iMonth >= 13)
137     return;
138   if (iDay <= 0 || iDay >= 32)
139     return;
140 
141   m_iYear = iYear;
142   m_iMonth = iMonth;
143   m_iDay = iDay;
144   m_pMonthCal->SetSelect(iYear, iMonth, iDay);
145 }
146 
SetEditText(const WideString & wsText)147 void CFWL_DateTimePicker::SetEditText(const WideString& wsText) {
148   if (!m_pEdit)
149     return;
150 
151   m_pEdit->SetText(wsText);
152   RepaintRect(m_ClientRect);
153 
154   CFWL_Event ev(CFWL_Event::Type::EditChanged);
155   DispatchEvent(&ev);
156 }
157 
GetEditText() const158 WideString CFWL_DateTimePicker::GetEditText() const {
159   return m_pEdit ? m_pEdit->GetText() : WideString();
160 }
161 
GetEditTextLength() const162 size_t CFWL_DateTimePicker::GetEditTextLength() const {
163   return m_pEdit ? m_pEdit->GetTextLength() : 0;
164 }
165 
GetBBox() const166 CFX_RectF CFWL_DateTimePicker::GetBBox() const {
167   CFX_RectF rect = m_WidgetRect;
168   if (NeedsToShowButton())
169     rect.width += m_fBtn;
170   if (!IsMonthCalendarVisible())
171     return rect;
172 
173   CFX_RectF rtMonth = m_pMonthCal->GetWidgetRect();
174   rtMonth.Offset(m_WidgetRect.left, m_WidgetRect.top);
175   rect.Union(rtMonth);
176   return rect;
177 }
178 
ModifyEditStyleExts(uint32_t dwStyleExtsAdded,uint32_t dwStyleExtsRemoved)179 void CFWL_DateTimePicker::ModifyEditStyleExts(uint32_t dwStyleExtsAdded,
180                                               uint32_t dwStyleExtsRemoved) {
181   m_pEdit->ModifyStyleExts(dwStyleExtsAdded, dwStyleExtsRemoved);
182 }
183 
DrawDropDownButton(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & mtMatrix)184 void CFWL_DateTimePicker::DrawDropDownButton(CFGAS_GEGraphics* pGraphics,
185                                              const CFX_Matrix& mtMatrix) {
186   CFWL_ThemeBackground param(CFWL_ThemePart::Part::kDropDownButton, this,
187                              pGraphics);
188   param.m_dwStates = m_iBtnState;
189   param.m_PartRect = m_BtnRect;
190   param.m_matrix = mtMatrix;
191   GetThemeProvider()->DrawBackground(param);
192 }
193 
FormatDateString(int32_t iYear,int32_t iMonth,int32_t iDay)194 WideString CFWL_DateTimePicker::FormatDateString(int32_t iYear,
195                                                  int32_t iMonth,
196                                                  int32_t iDay) {
197   if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_ShortDateFormat)
198     return WideString::Format(L"%d-%d-%d", iYear, iMonth, iDay);
199 
200   return WideString::Format(L"%d Year %d Month %d Day", iYear, iMonth, iDay);
201 }
202 
ShowMonthCalendar()203 void CFWL_DateTimePicker::ShowMonthCalendar() {
204   if (IsMonthCalendarVisible())
205     return;
206 
207   CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
208   float fPopupMin = rtMonthCal.height;
209   float fPopupMax = rtMonthCal.height;
210   CFX_RectF rtAnchor = m_WidgetRect;
211   rtAnchor.width = rtMonthCal.width;
212   rtMonthCal.left = m_ClientRect.left;
213   rtMonthCal.top = rtAnchor.Height();
214   GetPopupPos(fPopupMin, fPopupMax, rtAnchor, &rtMonthCal);
215   m_pMonthCal->SetWidgetRect(rtMonthCal);
216   if (m_iYear > 0 && m_iMonth > 0 && m_iDay > 0)
217     m_pMonthCal->SetSelect(m_iYear, m_iMonth, m_iDay);
218   m_pMonthCal->Update();
219   m_pMonthCal->RemoveStates(FWL_STATE_WGT_Invisible);
220 
221   CFWL_MessageSetFocus msg(m_pMonthCal);
222   m_pEdit->GetDelegate()->OnProcessMessage(&msg);
223   RepaintInflatedMonthCalRect();
224 }
225 
HideMonthCalendar()226 void CFWL_DateTimePicker::HideMonthCalendar() {
227   if (!IsMonthCalendarVisible())
228     return;
229 
230   m_pMonthCal->SetStates(FWL_STATE_WGT_Invisible);
231   RepaintInflatedMonthCalRect();
232 }
233 
RepaintInflatedMonthCalRect()234 void CFWL_DateTimePicker::RepaintInflatedMonthCalRect() {
235   CFX_RectF rtInvalidate(0, 0, m_WidgetRect.width, m_WidgetRect.height);
236   CFX_RectF rtCal = m_pMonthCal->GetWidgetRect();
237   rtInvalidate.Union(rtCal);
238   rtInvalidate.Inflate(2, 2);
239   RepaintRect(rtInvalidate);
240 }
241 
IsMonthCalendarVisible() const242 bool CFWL_DateTimePicker::IsMonthCalendarVisible() const {
243   return m_pMonthCal && m_pMonthCal->IsVisible();
244 }
245 
ResetEditAlignment()246 void CFWL_DateTimePicker::ResetEditAlignment() {
247   if (!m_pEdit)
248     return;
249 
250   uint32_t dwAdd = 0;
251   switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_EditHAlignMask) {
252     case FWL_STYLEEXT_DTP_EditHCenter: {
253       dwAdd |= FWL_STYLEEXT_EDT_HCenter;
254       break;
255     }
256     case FWL_STYLEEXT_DTP_EditHFar: {
257       dwAdd |= FWL_STYLEEXT_EDT_HFar;
258       break;
259     }
260     default: {
261       dwAdd |= FWL_STYLEEXT_EDT_HNear;
262       break;
263     }
264   }
265   switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_EditVAlignMask) {
266     case FWL_STYLEEXT_DTP_EditVCenter: {
267       dwAdd |= FWL_STYLEEXT_EDT_VCenter;
268       break;
269     }
270     case FWL_STYLEEXT_DTP_EditVFar: {
271       dwAdd |= FWL_STYLEEXT_EDT_VFar;
272       break;
273     }
274     default: {
275       dwAdd |= FWL_STYLEEXT_EDT_VNear;
276       break;
277     }
278   }
279   if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_EditJustified)
280     dwAdd |= FWL_STYLEEXT_EDT_Justified;
281 
282   m_pEdit->ModifyStyleExts(dwAdd, FWL_STYLEEXT_EDT_HAlignMask |
283                                       FWL_STYLEEXT_EDT_HAlignModeMask |
284                                       FWL_STYLEEXT_EDT_VAlignMask);
285 }
286 
ProcessSelChanged(int32_t iYear,int32_t iMonth,int32_t iDay)287 void CFWL_DateTimePicker::ProcessSelChanged(int32_t iYear,
288                                             int32_t iMonth,
289                                             int32_t iDay) {
290   m_iYear = iYear;
291   m_iMonth = iMonth;
292   m_iDay = iDay;
293   m_pEdit->SetText(FormatDateString(m_iYear, m_iMonth, m_iDay));
294   m_pEdit->Update();
295   RepaintRect(m_ClientRect);
296 
297   CFWL_EventSelectChanged ev(this, m_iYear, m_iMonth, m_iDay);
298   DispatchEvent(&ev);
299 }
300 
NeedsToShowButton() const301 bool CFWL_DateTimePicker::NeedsToShowButton() const {
302   return m_Properties.m_dwStates & FWL_STATE_WGT_Focused ||
303          m_pMonthCal->GetStates() & FWL_STATE_WGT_Focused ||
304          m_pEdit->GetStates() & FWL_STATE_WGT_Focused;
305 }
306 
OnProcessMessage(CFWL_Message * pMessage)307 void CFWL_DateTimePicker::OnProcessMessage(CFWL_Message* pMessage) {
308   switch (pMessage->GetType()) {
309     case CFWL_Message::Type::kSetFocus:
310       OnFocusGained(pMessage);
311       break;
312     case CFWL_Message::Type::kKillFocus:
313       OnFocusLost(pMessage);
314       break;
315     case CFWL_Message::Type::kMouse: {
316       CFWL_MessageMouse* pMouse = static_cast<CFWL_MessageMouse*>(pMessage);
317       switch (pMouse->m_dwCmd) {
318         case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
319           OnLButtonDown(pMouse);
320           break;
321         case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
322           OnLButtonUp(pMouse);
323           break;
324         case CFWL_MessageMouse::MouseCommand::kMove:
325           OnMouseMove(pMouse);
326           break;
327         case CFWL_MessageMouse::MouseCommand::kLeave:
328           OnMouseLeave(pMouse);
329           break;
330         default:
331           break;
332       }
333       break;
334     }
335     case CFWL_Message::Type::kKey: {
336       if (m_pEdit->GetStates() & FWL_STATE_WGT_Focused) {
337         m_pEdit->GetDelegate()->OnProcessMessage(pMessage);
338         return;
339       }
340       break;
341     }
342     default:
343       break;
344   }
345   // Dst target could be |this|, continue only if not destroyed by above.
346   if (pMessage->GetDstTarget())
347     CFWL_Widget::OnProcessMessage(pMessage);
348 }
349 
OnDrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)350 void CFWL_DateTimePicker::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
351                                        const CFX_Matrix& matrix) {
352   DrawWidget(pGraphics, matrix);
353 }
354 
OnFocusGained(CFWL_Message * pMsg)355 void CFWL_DateTimePicker::OnFocusGained(CFWL_Message* pMsg) {
356   m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
357   if (m_pEdit && !(m_pEdit->GetStyleExts() & FWL_STYLEEXT_EDT_ReadOnly)) {
358     m_BtnRect =
359         CFX_RectF(m_WidgetRect.width, 0, m_fBtn, m_WidgetRect.height - 1);
360   }
361   CFX_RectF rtInvalidate(m_BtnRect);
362   pMsg->SetDstTarget(m_pEdit);
363   m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
364   rtInvalidate.Inflate(2, 2);
365   RepaintRect(rtInvalidate);
366 }
367 
OnFocusLost(CFWL_Message * pMsg)368 void CFWL_DateTimePicker::OnFocusLost(CFWL_Message* pMsg) {
369   CFX_RectF rtInvalidate(m_BtnRect);
370   m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
371   m_BtnRect = CFX_RectF();
372   HideMonthCalendar();
373   if (m_pEdit->GetStates() & FWL_STATE_WGT_Focused)
374     m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
375   rtInvalidate.Inflate(2, 2);
376   RepaintRect(rtInvalidate);
377 }
378 
OnLButtonDown(CFWL_MessageMouse * pMsg)379 void CFWL_DateTimePicker::OnLButtonDown(CFWL_MessageMouse* pMsg) {
380   if (!pMsg)
381     return;
382   if (!m_BtnRect.Contains(pMsg->m_pos))
383     return;
384 
385   if (IsMonthCalendarVisible()) {
386     HideMonthCalendar();
387     return;
388   }
389   ShowMonthCalendar();
390   m_bLBtnDown = true;
391   RepaintRect(m_ClientRect);
392 }
393 
OnLButtonUp(CFWL_MessageMouse * pMsg)394 void CFWL_DateTimePicker::OnLButtonUp(CFWL_MessageMouse* pMsg) {
395   if (!pMsg)
396     return;
397 
398   m_bLBtnDown = false;
399   if (m_BtnRect.Contains(pMsg->m_pos))
400     m_iBtnState = CFWL_PartState::kHovered;
401   else
402     m_iBtnState = CFWL_PartState::kNormal;
403   RepaintRect(m_BtnRect);
404 }
405 
OnMouseMove(CFWL_MessageMouse * pMsg)406 void CFWL_DateTimePicker::OnMouseMove(CFWL_MessageMouse* pMsg) {
407   if (!m_BtnRect.Contains(pMsg->m_pos))
408     m_iBtnState = CFWL_PartState::kNormal;
409   RepaintRect(m_BtnRect);
410 }
411 
OnMouseLeave(CFWL_MessageMouse * pMsg)412 void CFWL_DateTimePicker::OnMouseLeave(CFWL_MessageMouse* pMsg) {
413   if (!pMsg)
414     return;
415   m_iBtnState = CFWL_PartState::kNormal;
416   RepaintRect(m_BtnRect);
417 }
418 
GetPopupPos(float fMinHeight,float fMaxHeight,const CFX_RectF & rtAnchor,CFX_RectF * pPopupRect)419 void CFWL_DateTimePicker::GetPopupPos(float fMinHeight,
420                                       float fMaxHeight,
421                                       const CFX_RectF& rtAnchor,
422                                       CFX_RectF* pPopupRect) {
423   GetWidgetMgr()->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
424                                      pPopupRect);
425 }
426 
ClearText()427 void CFWL_DateTimePicker::ClearText() {
428   m_pEdit->ClearText();
429 }
430 
SelectAll()431 void CFWL_DateTimePicker::SelectAll() {
432   m_pEdit->SelectAll();
433 }
434 
ClearSelection()435 void CFWL_DateTimePicker::ClearSelection() {
436   m_pEdit->ClearSelection();
437 }
438 
Copy()439 absl::optional<WideString> CFWL_DateTimePicker::Copy() {
440   return m_pEdit->Copy();
441 }
442 
Cut()443 absl::optional<WideString> CFWL_DateTimePicker::Cut() {
444   return m_pEdit->Cut();
445 }
446 
Paste(const WideString & wsPaste)447 bool CFWL_DateTimePicker::Paste(const WideString& wsPaste) {
448   return m_pEdit->Paste(wsPaste);
449 }
450 
Undo()451 bool CFWL_DateTimePicker::Undo() {
452   return m_pEdit->Undo();
453 }
454 
Redo()455 bool CFWL_DateTimePicker::Redo() {
456   return m_pEdit->Redo();
457 }
458 
CanUndo()459 bool CFWL_DateTimePicker::CanUndo() {
460   return m_pEdit->CanUndo();
461 }
462 
CanRedo()463 bool CFWL_DateTimePicker::CanRedo() {
464   return m_pEdit->CanRedo();
465 }
466