1 // Copyright 2016 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_combolist.h"
8
9 #include "third_party/base/check.h"
10 #include "xfa/fwl/cfwl_combobox.h"
11 #include "xfa/fwl/cfwl_comboedit.h"
12 #include "xfa/fwl/cfwl_listbox.h"
13 #include "xfa/fwl/cfwl_messagekey.h"
14 #include "xfa/fwl/cfwl_messagekillfocus.h"
15 #include "xfa/fwl/cfwl_messagemouse.h"
16 #include "xfa/fwl/fwl_widgetdef.h"
17
CFWL_ComboList(CFWL_App * app,const Properties & properties,CFWL_Widget * pOuter)18 CFWL_ComboList::CFWL_ComboList(CFWL_App* app,
19 const Properties& properties,
20 CFWL_Widget* pOuter)
21 : CFWL_ListBox(app, properties, pOuter) {
22 DCHECK(pOuter);
23 }
24
25 CFWL_ComboList::~CFWL_ComboList() = default;
26
MatchItem(WideStringView wsMatch)27 int32_t CFWL_ComboList::MatchItem(WideStringView wsMatch) {
28 if (wsMatch.IsEmpty())
29 return -1;
30
31 int32_t iCount = CountItems(this);
32 for (int32_t i = 0; i < iCount; i++) {
33 CFWL_ListBox::Item* hItem = GetItem(this, i);
34 WideString wsText = hItem ? hItem->GetText() : WideString();
35 auto pos = wsText.Find(wsMatch);
36 if (pos.has_value() && pos.value() == 0)
37 return i;
38 }
39 return -1;
40 }
41
ChangeSelected(int32_t iSel)42 void CFWL_ComboList::ChangeSelected(int32_t iSel) {
43 CFWL_ListBox::Item* hItem = GetItem(this, iSel);
44 CFWL_ListBox::Item* hOld = GetSelItem(0);
45 int32_t iOld = GetItemIndex(this, hOld);
46 if (iOld == iSel)
47 return;
48
49 CFX_RectF rtInvalidate;
50 if (iOld > -1) {
51 if (CFWL_ListBox::Item* hOldItem = GetItem(this, iOld))
52 rtInvalidate = hOldItem->GetRect();
53 SetSelItem(hOld, false);
54 }
55 if (hItem) {
56 if (CFWL_ListBox::Item* hOldItem = GetItem(this, iSel))
57 rtInvalidate.Union(hOldItem->GetRect());
58 CFWL_ListBox::Item* hSel = GetItem(this, iSel);
59 SetSelItem(hSel, true);
60 }
61 if (!rtInvalidate.IsEmpty())
62 RepaintRect(rtInvalidate);
63 }
64
ClientToOuter(const CFX_PointF & point)65 CFX_PointF CFWL_ComboList::ClientToOuter(const CFX_PointF& point) {
66 return point + CFX_PointF(m_WidgetRect.left, m_WidgetRect.top);
67 }
68
OnProcessMessage(CFWL_Message * pMessage)69 void CFWL_ComboList::OnProcessMessage(CFWL_Message* pMessage) {
70 CFWL_Message::Type type = pMessage->GetType();
71 bool backDefault = true;
72 if (type == CFWL_Message::Type::kSetFocus ||
73 type == CFWL_Message::Type::kKillFocus) {
74 OnDropListFocusChanged(pMessage, type == CFWL_Message::Type::kSetFocus);
75 } else if (type == CFWL_Message::Type::kMouse) {
76 CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
77 CFWL_ScrollBar* vertSB = GetVertScrollBar();
78 if (IsShowVertScrollBar() && vertSB) {
79 CFX_RectF rect = vertSB->GetWidgetRect();
80 if (rect.Contains(pMsg->m_pos)) {
81 pMsg->m_pos -= rect.TopLeft();
82 vertSB->GetDelegate()->OnProcessMessage(pMsg);
83 return;
84 }
85 }
86 switch (pMsg->m_dwCmd) {
87 case CFWL_MessageMouse::MouseCommand::kMove:
88 backDefault = false;
89 OnDropListMouseMove(pMsg);
90 break;
91 case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
92 backDefault = false;
93 OnDropListLButtonDown(pMsg);
94 break;
95 case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
96 backDefault = false;
97 OnDropListLButtonUp(pMsg);
98 break;
99 default:
100 break;
101 }
102 } else if (type == CFWL_Message::Type::kKey) {
103 backDefault = !OnDropListKey(static_cast<CFWL_MessageKey*>(pMessage));
104 }
105 if (backDefault)
106 CFWL_ListBox::OnProcessMessage(pMessage);
107 }
108
OnDropListFocusChanged(CFWL_Message * pMsg,bool bSet)109 void CFWL_ComboList::OnDropListFocusChanged(CFWL_Message* pMsg, bool bSet) {
110 if (bSet)
111 return;
112
113 CFWL_MessageKillFocus* pKill = static_cast<CFWL_MessageKillFocus*>(pMsg);
114 CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
115 if (pKill->IsFocusedOnWidget(pOuter) ||
116 pKill->IsFocusedOnWidget(pOuter->GetComboEdit())) {
117 pOuter->HideDropDownList();
118 }
119 }
120
OnDropListMouseMove(CFWL_MessageMouse * pMsg)121 void CFWL_ComboList::OnDropListMouseMove(CFWL_MessageMouse* pMsg) {
122 if (GetRTClient().Contains(pMsg->m_pos)) {
123 if (m_bNotifyOwner)
124 m_bNotifyOwner = false;
125
126 CFWL_ScrollBar* vertSB = GetVertScrollBar();
127 if (IsShowVertScrollBar() && vertSB) {
128 CFX_RectF rect = vertSB->GetWidgetRect();
129 if (rect.Contains(pMsg->m_pos))
130 return;
131 }
132
133 CFWL_ListBox::Item* hItem = GetItemAtPoint(pMsg->m_pos);
134 if (!hItem)
135 return;
136
137 ChangeSelected(GetItemIndex(this, hItem));
138 } else if (m_bNotifyOwner) {
139 pMsg->m_pos = ClientToOuter(pMsg->m_pos);
140
141 CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
142 pOuter->GetDelegate()->OnProcessMessage(pMsg);
143 }
144 }
145
OnDropListLButtonDown(CFWL_MessageMouse * pMsg)146 void CFWL_ComboList::OnDropListLButtonDown(CFWL_MessageMouse* pMsg) {
147 if (GetRTClient().Contains(pMsg->m_pos))
148 return;
149
150 CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
151 pOuter->HideDropDownList();
152 }
153
OnDropListLButtonUp(CFWL_MessageMouse * pMsg)154 void CFWL_ComboList::OnDropListLButtonUp(CFWL_MessageMouse* pMsg) {
155 CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
156 if (m_bNotifyOwner) {
157 pMsg->m_pos = ClientToOuter(pMsg->m_pos);
158 pOuter->GetDelegate()->OnProcessMessage(pMsg);
159 return;
160 }
161
162 CFWL_ScrollBar* vertSB = GetVertScrollBar();
163 if (IsShowVertScrollBar() && vertSB) {
164 CFX_RectF rect = vertSB->GetWidgetRect();
165 if (rect.Contains(pMsg->m_pos))
166 return;
167 }
168 pOuter->HideDropDownList();
169
170 CFWL_ListBox::Item* hItem = GetItemAtPoint(pMsg->m_pos);
171 if (hItem)
172 pOuter->ProcessSelChanged(true);
173 }
174
OnDropListKey(CFWL_MessageKey * pKey)175 bool CFWL_ComboList::OnDropListKey(CFWL_MessageKey* pKey) {
176 CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
177 bool bPropagate = false;
178 if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown) {
179 uint32_t dwKeyCode = pKey->m_dwKeyCodeOrChar;
180 switch (dwKeyCode) {
181 case XFA_FWL_VKEY_Return:
182 case XFA_FWL_VKEY_Escape: {
183 pOuter->HideDropDownList();
184 return true;
185 }
186 case XFA_FWL_VKEY_Up:
187 case XFA_FWL_VKEY_Down: {
188 OnDropListKeyDown(pKey);
189 pOuter->ProcessSelChanged(false);
190 return true;
191 }
192 default: {
193 bPropagate = true;
194 break;
195 }
196 }
197 } else if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kChar) {
198 bPropagate = true;
199 }
200 if (bPropagate) {
201 pKey->SetDstTarget(GetOuter());
202 pOuter->GetDelegate()->OnProcessMessage(pKey);
203 return true;
204 }
205 return false;
206 }
207
OnDropListKeyDown(CFWL_MessageKey * pKey)208 void CFWL_ComboList::OnDropListKeyDown(CFWL_MessageKey* pKey) {
209 auto dwKeyCode = static_cast<XFA_FWL_VKEYCODE>(pKey->m_dwKeyCodeOrChar);
210 switch (dwKeyCode) {
211 case XFA_FWL_VKEY_Up:
212 case XFA_FWL_VKEY_Down:
213 case XFA_FWL_VKEY_Home:
214 case XFA_FWL_VKEY_End: {
215 CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
216 CFWL_ListBox::Item* hItem = GetItem(this, pOuter->GetCurrentSelection());
217 hItem = GetListItem(hItem, dwKeyCode);
218 if (!hItem)
219 break;
220
221 SetSelection(hItem, hItem, true);
222 ScrollToVisible(hItem);
223 RepaintRect(CFX_RectF(0, 0, m_WidgetRect.width, m_WidgetRect.height));
224 break;
225 }
226 default:
227 break;
228 }
229 }
230