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