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