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