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