1// Copyright 2014 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/src/foxitlib.h"
8#include "xfa/src/fwl/src/core/include/fwl_threadimp.h"
9#include "xfa/src/fwl/src/core/include/fwl_appimp.h"
10#include "xfa/src/fwl/src/core/include/fwl_targetimp.h"
11#include "xfa/src/fwl/src/core/include/fwl_noteimp.h"
12#include "xfa/src/fwl/src/core/include/fwl_widgetimp.h"
13#include "xfa/src/fwl/src/core/include/fwl_panelimp.h"
14#include "xfa/src/fwl/src/core/include/fwl_formimp.h"
15#include "xfa/src/fwl/src/core/include/fwl_widgetmgrimp.h"
16#include "xfa/src/fwl/src/basewidget/include/fwl_scrollbarimp.h"
17#include "xfa/src/fwl/src/basewidget/include/fwl_editimp.h"
18#include "xfa/src/fwl/src/basewidget/include/fwl_listboximp.h"
19#include "xfa/src/fwl/src/basewidget/include/fwl_formproxyimp.h"
20#include "xfa/src/fwl/src/basewidget/include/fwl_comboboximp.h"
21
22// static
23IFWL_ComboBox* IFWL_ComboBox::Create(
24    const CFWL_WidgetImpProperties& properties) {
25  IFWL_ComboBox* pComboBox = new IFWL_ComboBox;
26  CFWL_ComboBoxImp* pComboBoxImpl = new CFWL_ComboBoxImp(properties, nullptr);
27  pComboBox->SetImpl(pComboBoxImpl);
28  pComboBoxImpl->SetInterface(pComboBox);
29  return pComboBox;
30}
31IFWL_ComboBox::IFWL_ComboBox() {}
32int32_t IFWL_ComboBox::GetCurSel() {
33  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->GetCurSel();
34}
35FWL_ERR IFWL_ComboBox::SetCurSel(int32_t iSel) {
36  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->SetCurSel(iSel);
37}
38FWL_ERR IFWL_ComboBox::SetEditText(const CFX_WideString& wsText) {
39  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->SetEditText(wsText);
40}
41int32_t IFWL_ComboBox::GetEditTextLength() const {
42  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->GetEditTextLength();
43}
44FWL_ERR IFWL_ComboBox::GetEditText(CFX_WideString& wsText,
45                                   int32_t nStart,
46                                   int32_t nCount) const {
47  return static_cast<CFWL_ComboBoxImp*>(GetImpl())
48      ->GetEditText(wsText, nStart, nCount);
49}
50FWL_ERR IFWL_ComboBox::SetEditSelRange(int32_t nStart, int32_t nCount) {
51  return static_cast<CFWL_ComboBoxImp*>(GetImpl())
52      ->SetEditSelRange(nStart, nCount);
53}
54int32_t IFWL_ComboBox::GetEditSelRange(int32_t nIndex, int32_t& nStart) {
55  return static_cast<CFWL_ComboBoxImp*>(GetImpl())
56      ->GetEditSelRange(nIndex, nStart);
57}
58int32_t IFWL_ComboBox::GetEditLimit() {
59  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->GetEditLimit();
60}
61FWL_ERR IFWL_ComboBox::SetEditLimit(int32_t nLimit) {
62  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->SetEditLimit(nLimit);
63}
64FWL_ERR IFWL_ComboBox::EditDoClipboard(int32_t iCmd) {
65  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditDoClipboard(iCmd);
66}
67FX_BOOL IFWL_ComboBox::EditRedo(const CFX_ByteStringC& bsRecord) {
68  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditRedo(bsRecord);
69}
70FX_BOOL IFWL_ComboBox::EditUndo(const CFX_ByteStringC& bsRecord) {
71  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditUndo(bsRecord);
72}
73IFWL_ListBox* IFWL_ComboBox::GetListBoxt() {
74  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->GetListBoxt();
75}
76FX_BOOL IFWL_ComboBox::AfterFocusShowDropList() {
77  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->AfterFocusShowDropList();
78}
79FX_ERR IFWL_ComboBox::OpenDropDownList(FX_BOOL bActivate) {
80  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->OpenDropDownList(bActivate);
81}
82FX_BOOL IFWL_ComboBox::EditCanUndo() {
83  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditCanUndo();
84}
85FX_BOOL IFWL_ComboBox::EditCanRedo() {
86  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditCanRedo();
87}
88FX_BOOL IFWL_ComboBox::EditUndo() {
89  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditUndo();
90}
91FX_BOOL IFWL_ComboBox::EditRedo() {
92  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditRedo();
93}
94FX_BOOL IFWL_ComboBox::EditCanCopy() {
95  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditCanCopy();
96}
97FX_BOOL IFWL_ComboBox::EditCanCut() {
98  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditCanCut();
99}
100FX_BOOL IFWL_ComboBox::EditCanSelectAll() {
101  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditCanSelectAll();
102}
103FX_BOOL IFWL_ComboBox::EditCopy(CFX_WideString& wsCopy) {
104  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditCopy(wsCopy);
105}
106FX_BOOL IFWL_ComboBox::EditCut(CFX_WideString& wsCut) {
107  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditCut(wsCut);
108}
109FX_BOOL IFWL_ComboBox::EditPaste(const CFX_WideString& wsPaste) {
110  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditPaste(wsPaste);
111}
112FX_BOOL IFWL_ComboBox::EditSelectAll() {
113  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditSelectAll();
114}
115FX_BOOL IFWL_ComboBox::EditDelete() {
116  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditDelete();
117}
118FX_BOOL IFWL_ComboBox::EditDeSelect() {
119  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->EditDeSelect();
120}
121FWL_ERR IFWL_ComboBox::GetBBox(CFX_RectF& rect) {
122  return static_cast<CFWL_ComboBoxImp*>(GetImpl())->GetBBox(rect);
123}
124FWL_ERR IFWL_ComboBox::EditModifyStylesEx(FX_DWORD dwStylesExAdded,
125                                          FX_DWORD dwStylesExRemoved) {
126  return static_cast<CFWL_ComboBoxImp*>(GetImpl())
127      ->EditModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
128}
129
130CFWL_ComboEditImp::CFWL_ComboEditImp(const CFWL_WidgetImpProperties& properties,
131                                     IFWL_Widget* pOuter)
132    : CFWL_EditImp(properties, pOuter) {
133  m_pOuter = static_cast<CFWL_ComboBoxImp*>(pOuter->GetImpl());
134}
135
136CFWL_ComboEditImpDelegate::CFWL_ComboEditImpDelegate(CFWL_ComboEditImp* pOwner)
137    : CFWL_EditImpDelegate(pOwner), m_pOwner(pOwner) {
138}
139int32_t CFWL_ComboEditImpDelegate::OnProcessMessage(CFWL_Message* pMessage) {
140  if (!pMessage)
141    return 0;
142  FX_DWORD dwMsgCode = pMessage->GetClassID();
143  FX_BOOL backDefault = TRUE;
144  switch (dwMsgCode) {
145    case FWL_MSGHASH_SetFocus:
146    case FWL_MSGHASH_KillFocus: {
147      if (dwMsgCode == FWL_MSGHASH_SetFocus) {
148        m_pOwner->m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
149      } else {
150        m_pOwner->m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
151      }
152      backDefault = FALSE;
153      break;
154    }
155    case FWL_MSGHASH_Mouse: {
156      CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
157      if ((pMsg->m_dwCmd == FWL_MSGMOUSECMD_LButtonDown) &&
158          ((m_pOwner->m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)) {
159        m_pOwner->SetSelected();
160        m_pOwner->SetComboBoxFocus(TRUE);
161      }
162      break;
163    }
164    default: {}
165  }
166  if (!backDefault) {
167    return 1;
168  }
169  return CFWL_EditImpDelegate::OnProcessMessage(pMessage);
170}
171void CFWL_ComboEditImp::ClearSelected() {
172  ClearSelections();
173  Repaint(&m_rtClient);
174}
175void CFWL_ComboEditImp::SetSelected() {
176  FlagFocus(TRUE);
177  EndCaret();
178  AddSelRange(0);
179}
180void CFWL_ComboEditImp::EndCaret() {
181  m_pEdtEngine->MoveCaretPos(MC_End);
182}
183void CFWL_ComboEditImp::FlagFocus(FX_BOOL bSet) {
184  if (bSet) {
185    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
186  } else {
187    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
188    ShowCaret(FALSE);
189  }
190};
191void CFWL_ComboEditImp::SetComboBoxFocus(FX_BOOL bSet) {
192  m_pOuter->SetFocus(bSet);
193}
194CFWL_ComboListImp::CFWL_ComboListImp(const CFWL_WidgetImpProperties& properties,
195                                     IFWL_Widget* pOuter)
196    : CFWL_ListBoxImp(properties, pOuter), m_bNotifyOwner(TRUE) {
197  FXSYS_assert(pOuter != NULL);
198}
199FWL_ERR CFWL_ComboListImp::Initialize() {
200  if (CFWL_ListBoxImp::Initialize() != FWL_ERR_Succeeded)
201    return FWL_ERR_Indefinite;
202  delete m_pDelegate;
203  m_pDelegate = new CFWL_ComboListImpDelegate(this);
204  return FWL_ERR_Succeeded;
205}
206FWL_ERR CFWL_ComboListImp::Finalize() {
207  delete m_pDelegate;
208  m_pDelegate = nullptr;
209  return CFWL_ListBoxImp::Finalize();
210}
211int32_t CFWL_ComboListImp::MatchItem(const CFX_WideString& wsMatch) {
212  if (wsMatch.IsEmpty()) {
213    return -1;
214  }
215  if (!m_pProperties->m_pDataProvider)
216    return -1;
217  IFWL_ListBoxDP* pData =
218      static_cast<IFWL_ListBoxDP*>(m_pProperties->m_pDataProvider);
219  int32_t iCount = pData->CountItems(m_pInterface);
220  for (int32_t i = 0; i < iCount; i++) {
221    FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, i);
222    CFX_WideString wsText;
223    pData->GetItemText(m_pInterface, hItem, wsText);
224    FX_STRSIZE pos = wsText.Find(wsMatch.c_str());
225    if (!pos) {
226      return i;
227    }
228  }
229  return -1;
230}
231void CFWL_ComboListImp::ChangeSelected(int32_t iSel) {
232  if (!m_pProperties->m_pDataProvider)
233    return;
234  IFWL_ListBoxDP* pData =
235      static_cast<IFWL_ListBoxDP*>(m_pProperties->m_pDataProvider);
236  FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, iSel);
237  CFX_RectF rtInvalidate;
238  rtInvalidate.Reset();
239  FWL_HLISTITEM hOld = GetSelItem(0);
240  int32_t iOld = pData->GetItemIndex(m_pInterface, hOld);
241  if (iOld == iSel) {
242    return;
243  } else if (iOld > -1) {
244    GetItemRect(iOld, rtInvalidate);
245    SetSelItem(hOld, FALSE);
246  }
247  if (hItem) {
248    CFX_RectF rect;
249    GetItemRect(iSel, rect);
250    rtInvalidate.Union(rect);
251    FWL_HLISTITEM hSel = pData->GetItem(m_pInterface, iSel);
252    SetSelItem(hSel, TRUE);
253  }
254  if (!rtInvalidate.IsEmpty()) {
255    Repaint(&rtInvalidate);
256  }
257}
258int32_t CFWL_ComboListImp::CountItems() {
259  IFWL_ListBoxDP* pData =
260      static_cast<IFWL_ListBoxDP*>(m_pProperties->m_pDataProvider);
261  return pData ? pData->CountItems(m_pInterface) : 0;
262}
263void CFWL_ComboListImp::GetItemRect(int32_t nIndex, CFX_RectF& rtItem) {
264  IFWL_ListBoxDP* pData =
265      static_cast<IFWL_ListBoxDP*>(m_pProperties->m_pDataProvider);
266  FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, nIndex);
267  pData->GetItemRect(m_pInterface, hItem, rtItem);
268}
269void CFWL_ComboListImp::ClientToOuter(FX_FLOAT& fx, FX_FLOAT& fy) {
270  fx += m_pProperties->m_rtWidget.left, fy += m_pProperties->m_rtWidget.top;
271  IFWL_Widget* pOwner = GetOwner();
272  if (!pOwner)
273    return;
274  pOwner->TransformTo(m_pOuter, fx, fy);
275}
276void CFWL_ComboListImp::SetFocus(FX_BOOL bSet) {
277  CFWL_WidgetImp::SetFocus(bSet);
278}
279CFWL_ComboListImpDelegate::CFWL_ComboListImpDelegate(CFWL_ComboListImp* pOwner)
280    : CFWL_ListBoxImpDelegate(pOwner), m_pOwner(pOwner) {
281}
282int32_t CFWL_ComboListImpDelegate::OnProcessMessage(CFWL_Message* pMessage) {
283  if (!pMessage)
284    return 0;
285  FX_DWORD dwHashCode = pMessage->GetClassID();
286  FX_BOOL backDefault = TRUE;
287  if (dwHashCode == FWL_MSGHASH_SetFocus ||
288      dwHashCode == FWL_MSGHASH_KillFocus) {
289    OnDropListFocusChanged(pMessage, dwHashCode == FWL_MSGHASH_SetFocus);
290  } else if (dwHashCode == FWL_MSGHASH_Mouse) {
291    CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
292    if (m_pOwner->IsShowScrollBar(TRUE) && m_pOwner->m_pVertScrollBar) {
293      CFX_RectF rect;
294      m_pOwner->m_pVertScrollBar->GetWidgetRect(rect);
295      if (rect.Contains(pMsg->m_fx, pMsg->m_fy)) {
296        pMsg->m_fx -= rect.left;
297        pMsg->m_fy -= rect.top;
298        IFWL_WidgetDelegate* pDelegate =
299            m_pOwner->m_pVertScrollBar->SetDelegate(NULL);
300        return pDelegate->OnProcessMessage(pMsg);
301      }
302    }
303    FX_DWORD dwCmd = pMsg->m_dwCmd;
304    switch (dwCmd) {
305      case FWL_MSGMOUSECMD_MouseMove: {
306        backDefault = FALSE;
307        OnDropListMouseMove(pMsg);
308        break;
309      }
310      case FWL_MSGMOUSECMD_LButtonDown: {
311        backDefault = FALSE;
312        OnDropListLButtonDown(pMsg);
313        break;
314      }
315      case FWL_MSGMOUSECMD_LButtonUp: {
316        backDefault = FALSE;
317        OnDropListLButtonUp(pMsg);
318        break;
319      }
320      default: {}
321    }
322  } else if (dwHashCode == FWL_MSGHASH_Key) {
323    backDefault = !OnDropListKey(static_cast<CFWL_MsgKey*>(pMessage));
324  }
325  if (!backDefault) {
326    return 1;
327  }
328  return CFWL_ListBoxImpDelegate::OnProcessMessage(pMessage);
329}
330void CFWL_ComboListImpDelegate::OnDropListFocusChanged(CFWL_Message* pMsg,
331                                                       FX_BOOL bSet) {
332  if (!bSet) {
333    CFWL_MsgKillFocus* pKill = static_cast<CFWL_MsgKillFocus*>(pMsg);
334    CFWL_ComboBoxImp* pOuter =
335        static_cast<CFWL_ComboBoxImp*>(m_pOwner->m_pOuter->GetImpl());
336    if (pKill->m_pSetFocus == m_pOwner->m_pOuter ||
337        pKill->m_pSetFocus == pOuter->m_pEdit.get()) {
338      pOuter->ShowDropList(FALSE);
339    }
340  }
341}
342int32_t CFWL_ComboListImpDelegate::OnDropListMouseMove(CFWL_MsgMouse* pMsg) {
343  if (m_pOwner->m_rtClient.Contains(pMsg->m_fx, pMsg->m_fy)) {
344    if (m_pOwner->m_bNotifyOwner) {
345      m_pOwner->m_bNotifyOwner = FALSE;
346    }
347    if (m_pOwner->IsShowScrollBar(TRUE) && m_pOwner->m_pVertScrollBar) {
348      CFX_RectF rect;
349      m_pOwner->m_pVertScrollBar->GetWidgetRect(rect);
350      if (rect.Contains(pMsg->m_fx, pMsg->m_fy)) {
351        return 1;
352      }
353    }
354    FWL_HLISTITEM hItem = m_pOwner->GetItemAtPoint(pMsg->m_fx, pMsg->m_fy);
355    if (hItem) {
356      if (!m_pOwner->m_pProperties->m_pDataProvider)
357        return 0;
358      IFWL_ListBoxDP* pData = static_cast<IFWL_ListBoxDP*>(
359          m_pOwner->m_pProperties->m_pDataProvider);
360      int32_t iSel = pData->GetItemIndex(m_pOwner->m_pInterface, hItem);
361      CFWL_EvtCmbHoverChanged event;
362      event.m_pSrcTarget = m_pOwner->m_pOuter;
363      event.m_iCurHover = iSel;
364      m_pOwner->DispatchEvent(&event);
365      m_pOwner->ChangeSelected(iSel);
366    }
367  } else if (m_pOwner->m_bNotifyOwner) {
368    m_pOwner->ClientToOuter(pMsg->m_fx, pMsg->m_fy);
369    CFWL_ComboBoxImp* pOuter =
370        static_cast<CFWL_ComboBoxImp*>(m_pOwner->m_pOuter->GetImpl());
371    pOuter->m_pDelegate->OnProcessMessage(pMsg);
372  }
373  return 1;
374}
375int32_t CFWL_ComboListImpDelegate::OnDropListLButtonDown(CFWL_MsgMouse* pMsg) {
376  if (m_pOwner->m_rtClient.Contains(pMsg->m_fx, pMsg->m_fy)) {
377    return 0;
378  }
379  CFWL_ComboBoxImp* pOuter =
380      static_cast<CFWL_ComboBoxImp*>(m_pOwner->m_pOuter->GetImpl());
381  pOuter->ShowDropList(FALSE);
382  return 1;
383}
384int32_t CFWL_ComboListImpDelegate::OnDropListLButtonUp(CFWL_MsgMouse* pMsg) {
385  CFWL_ComboBoxImp* pOuter =
386      static_cast<CFWL_ComboBoxImp*>(m_pOwner->m_pOuter->GetImpl());
387  if (m_pOwner->m_bNotifyOwner) {
388    m_pOwner->ClientToOuter(pMsg->m_fx, pMsg->m_fy);
389    pOuter->m_pDelegate->OnProcessMessage(pMsg);
390  } else {
391    if (m_pOwner->IsShowScrollBar(TRUE) && m_pOwner->m_pVertScrollBar) {
392      CFX_RectF rect;
393      m_pOwner->m_pVertScrollBar->GetWidgetRect(rect);
394      if (rect.Contains(pMsg->m_fx, pMsg->m_fy)) {
395        return 1;
396      }
397    }
398    pOuter->ShowDropList(FALSE);
399    FWL_HLISTITEM hItem = m_pOwner->GetItemAtPoint(pMsg->m_fx, pMsg->m_fy);
400    if (hItem) {
401      pOuter->ProcessSelChanged(TRUE);
402    }
403  }
404  return 1;
405}
406int32_t CFWL_ComboListImpDelegate::OnDropListKey(CFWL_MsgKey* pKey) {
407  CFWL_ComboBoxImp* pOuter =
408      static_cast<CFWL_ComboBoxImp*>(m_pOwner->m_pOuter->GetImpl());
409  FX_BOOL bPropagate = FALSE;
410  if (pKey->m_dwCmd == FWL_MSGKEYCMD_KeyDown) {
411    FX_DWORD dwKeyCode = pKey->m_dwKeyCode;
412    switch (dwKeyCode) {
413      case FWL_VKEY_Return:
414      case FWL_VKEY_Escape: {
415        pOuter->ShowDropList(FALSE);
416        return 1;
417      }
418      case FWL_VKEY_Up:
419      case FWL_VKEY_Down: {
420        OnDropListKeyDown(pKey);
421        pOuter->SetDelegate(nullptr);
422        pOuter->ProcessSelChanged(FALSE);
423        return 1;
424      }
425      default: { bPropagate = TRUE; }
426    }
427  } else if (pKey->m_dwCmd == FWL_MSGKEYCMD_Char) {
428    bPropagate = TRUE;
429  }
430  if (bPropagate) {
431    pKey->m_pDstTarget = m_pOwner->m_pOuter;
432    pOuter->m_pDelegate->OnProcessMessage(pKey);
433    return 1;
434  }
435  return 0;
436}
437void CFWL_ComboListImpDelegate::OnDropListKeyDown(CFWL_MsgKey* pKey) {
438  FX_DWORD dwKeyCode = pKey->m_dwKeyCode;
439  switch (dwKeyCode) {
440    case FWL_VKEY_Up:
441    case FWL_VKEY_Down:
442    case FWL_VKEY_Home:
443    case FWL_VKEY_End: {
444      CFWL_ComboBoxImp* pOuter =
445          static_cast<CFWL_ComboBoxImp*>(m_pOwner->m_pOuter->GetImpl());
446      IFWL_ListBoxDP* pData = static_cast<IFWL_ListBoxDP*>(
447          m_pOwner->m_pProperties->m_pDataProvider);
448      FWL_HLISTITEM hItem =
449          pData->GetItem(m_pOwner->m_pInterface, pOuter->m_iCurSel);
450      hItem = m_pOwner->GetItem(hItem, dwKeyCode);
451      if (!hItem) {
452        break;
453      }
454      m_pOwner->SetSelection(hItem, hItem, TRUE);
455      m_pOwner->ScrollToVisible(hItem);
456      CFX_RectF rtInvalidate;
457      rtInvalidate.Set(0, 0, m_pOwner->m_pProperties->m_rtWidget.width,
458                       m_pOwner->m_pProperties->m_rtWidget.height);
459      m_pOwner->Repaint(&rtInvalidate);
460      break;
461    }
462    default: {}
463  }
464}
465CFWL_ComboBoxImp::CFWL_ComboBoxImp(const CFWL_WidgetImpProperties& properties,
466                                   IFWL_Widget* pOuter)
467    : CFWL_WidgetImp(properties, pOuter),
468      m_pForm(NULL),
469      m_bLButtonDown(FALSE),
470      m_iCurSel(-1),
471      m_iBtnState(FWL_PARTSTATE_CMB_Normal),
472      m_fComboFormHandler(0),
473      m_bNeedShowList(FALSE) {
474  m_rtClient.Reset();
475  m_rtBtn.Reset();
476  m_rtHandler.Reset();
477}
478CFWL_ComboBoxImp::~CFWL_ComboBoxImp() {
479}
480FWL_ERR CFWL_ComboBoxImp::GetClassName(CFX_WideString& wsClass) const {
481  wsClass = FWL_CLASS_ComboBox;
482  return FWL_ERR_Succeeded;
483}
484FX_DWORD CFWL_ComboBoxImp::GetClassID() const {
485  return FWL_CLASSHASH_ComboBox;
486}
487FWL_ERR CFWL_ComboBoxImp::Initialize() {
488  if (m_pWidgetMgr->IsFormDisabled()) {
489    return DisForm_Initialize();
490  }
491  if (CFWL_WidgetImp::Initialize() != FWL_ERR_Succeeded)
492    return FWL_WGTSTATE_Invisible;  // Probably a bug; not a FWL_ERR_ value.
493  m_pDelegate = new CFWL_ComboBoxImpDelegate(this);
494  CFWL_WidgetImpProperties prop;
495  prop.m_pThemeProvider = m_pProperties->m_pThemeProvider;
496  prop.m_dwStyles |= FWL_WGTSTYLE_Border | FWL_WGTSTYLE_VScroll;
497  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_ListItemIconText) {
498    prop.m_dwStyleExes |= FWL_STYLEEXT_LTB_Icon;
499  }
500  prop.m_pDataProvider = m_pProperties->m_pDataProvider;
501  m_pListBox.reset(IFWL_ListBox::CreateComboList(prop, m_pInterface));
502  m_pListBox->Initialize();
503  if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_DropDown) && !m_pEdit) {
504    CFWL_WidgetImpProperties prop2;
505    m_pEdit.reset(IFWL_Edit::CreateComboEdit(prop2, m_pInterface));
506    m_pEdit->Initialize();
507    static_cast<CFWL_EditImp*>(m_pEdit->GetImpl())->SetOuter(m_pInterface);
508  }
509  if (m_pEdit) {
510    m_pEdit->SetParent(m_pInterface);
511  }
512  SetStates(m_pProperties->m_dwStates);
513  return FWL_ERR_Succeeded;
514}
515FWL_ERR CFWL_ComboBoxImp::Finalize() {
516  if (m_pEdit) {
517    m_pEdit->Finalize();
518  }
519  m_pListBox->Finalize();
520  delete m_pDelegate;
521  m_pDelegate = nullptr;
522  return CFWL_WidgetImp::Finalize();
523}
524FWL_ERR CFWL_ComboBoxImp::GetWidgetRect(CFX_RectF& rect, FX_BOOL bAutoSize) {
525  if (bAutoSize) {
526    rect.Reset();
527    FX_BOOL bIsDropDown = IsDropDownStyle();
528    if (bIsDropDown && m_pEdit) {
529      m_pEdit->GetWidgetRect(rect, TRUE);
530    } else {
531      rect.width = 100;
532      rect.height = 16;
533    }
534    if (!m_pProperties->m_pThemeProvider) {
535      ReSetTheme();
536    }
537    FX_FLOAT* pFWidth = static_cast<FX_FLOAT*>(
538        GetThemeCapacity(FWL_WGTCAPACITY_ScrollBarWidth));
539    if (!pFWidth)
540      return FWL_ERR_Indefinite;
541    rect.Inflate(0, 0, *pFWidth, 0);
542    CFWL_WidgetImp::GetWidgetRect(rect, TRUE);
543  } else {
544    rect = m_pProperties->m_rtWidget;
545  }
546  return FWL_ERR_Succeeded;
547}
548FWL_ERR CFWL_ComboBoxImp::ModifyStylesEx(FX_DWORD dwStylesExAdded,
549                                         FX_DWORD dwStylesExRemoved) {
550  if (m_pWidgetMgr->IsFormDisabled()) {
551    return DisForm_ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
552  }
553  FX_BOOL bAddDropDown = dwStylesExAdded & FWL_STYLEEXT_CMB_DropDown;
554  FX_BOOL bRemoveDropDown = dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown;
555  if (bAddDropDown && !m_pEdit) {
556    CFWL_WidgetImpProperties prop;
557    m_pEdit.reset(IFWL_Edit::CreateComboEdit(prop, nullptr));
558    m_pEdit->Initialize();
559    static_cast<CFWL_EditImp*>(m_pEdit->GetImpl())->SetOuter(m_pInterface);
560    m_pEdit->SetParent(m_pInterface);
561  } else if (bRemoveDropDown && m_pEdit) {
562    m_pEdit->SetStates(FWL_WGTSTATE_Invisible, TRUE);
563  }
564  return CFWL_WidgetImp::ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
565}
566FWL_ERR CFWL_ComboBoxImp::Update() {
567  if (m_pWidgetMgr->IsFormDisabled()) {
568    return DisForm_Update();
569  }
570  if (IsLocked()) {
571    return FWL_ERR_Indefinite;
572  }
573  ReSetTheme();
574  FX_BOOL bDropDown = IsDropDownStyle();
575  if (bDropDown && m_pEdit) {
576    ReSetEditAlignment();
577  }
578  if (m_pProperties->m_pThemeProvider == NULL) {
579    m_pProperties->m_pThemeProvider = GetAvailableTheme();
580  }
581  Layout();
582  CFWL_ThemePart part;
583  part.m_pWidget = m_pInterface;
584  m_fComboFormHandler =
585      *static_cast<FX_FLOAT*>(m_pProperties->m_pThemeProvider->GetCapacity(
586          &part, FWL_WGTCAPACITY_CMB_ComboFormHandler));
587  return FWL_ERR_Succeeded;
588}
589FX_DWORD CFWL_ComboBoxImp::HitTest(FX_FLOAT fx, FX_FLOAT fy) {
590  if (m_pWidgetMgr->IsFormDisabled()) {
591    return DisForm_HitTest(fx, fy);
592  }
593  return CFWL_WidgetImp::HitTest(fx, fy);
594}
595FWL_ERR CFWL_ComboBoxImp::DrawWidget(CFX_Graphics* pGraphics,
596                                     const CFX_Matrix* pMatrix) {
597  if (m_pWidgetMgr->IsFormDisabled()) {
598    return DisForm_DrawWidget(pGraphics, pMatrix);
599  }
600  if (!pGraphics)
601    return FWL_ERR_Indefinite;
602  if (!m_pProperties->m_pThemeProvider)
603    return FWL_ERR_Indefinite;
604  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
605  FX_BOOL bIsDropDown = IsDropDownStyle();
606  if (HasBorder()) {
607    DrawBorder(pGraphics, FWL_PART_CMB_Border, pTheme, pMatrix);
608  }
609  if (HasEdge()) {
610    DrawEdge(pGraphics, FWL_PART_CMB_Edge, pTheme, pMatrix);
611  }
612  if (!bIsDropDown) {
613    CFX_RectF rtTextBk(m_rtClient);
614    rtTextBk.width -= m_rtBtn.width;
615    CFWL_ThemeBackground param;
616    param.m_pWidget = m_pInterface;
617    param.m_iPart = FWL_PART_CMB_Background;
618    param.m_pGraphics = pGraphics;
619    if (pMatrix) {
620      param.m_matrix.Concat(*pMatrix);
621    }
622    param.m_rtPart = rtTextBk;
623    if (m_iCurSel >= 0) {
624      IFWL_ListBoxDP* pData = static_cast<IFWL_ListBoxDP*>(
625          static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
626              ->m_pProperties->m_pDataProvider);
627      void* p = pData->GetItemData(m_pListBox.get(),
628                                   pData->GetItem(m_pListBox.get(), m_iCurSel));
629      if (p != NULL) {
630        param.m_pData = p;
631      }
632    }
633    if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) {
634      param.m_dwStates = FWL_PARTSTATE_CMB_Disabled;
635    } else if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) &&
636               (m_iCurSel >= 0)) {
637      param.m_dwStates = FWL_PARTSTATE_CMB_Selected;
638    } else {
639      param.m_dwStates = FWL_PARTSTATE_CMB_Normal;
640    }
641    pTheme->DrawBackground(&param);
642    if (m_iCurSel >= 0) {
643      if (!m_pListBox)
644        return FWL_ERR_Indefinite;
645      CFX_WideString wsText;
646      IFWL_ComboBoxDP* pData =
647          static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
648      FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, m_iCurSel);
649      static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
650          ->GetItemText(hItem, wsText);
651      CFWL_ThemeText param;
652      param.m_pWidget = m_pInterface;
653      param.m_iPart = FWL_PART_CMB_Caption;
654      param.m_dwStates = m_iBtnState;
655      param.m_pGraphics = pGraphics;
656      param.m_matrix.Concat(*pMatrix);
657      param.m_rtPart = rtTextBk;
658      param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
659                             ? FWL_PARTSTATE_CMB_Selected
660                             : FWL_PARTSTATE_CMB_Normal;
661      param.m_wsText = wsText;
662      param.m_dwTTOStyles = FDE_TTOSTYLE_SingleLine;
663      param.m_iTTOAlign = FDE_TTOALIGNMENT_CenterLeft;
664      pTheme->DrawText(&param);
665    }
666  }
667  {
668    CFWL_ThemeBackground param;
669    param.m_pWidget = m_pInterface;
670    param.m_iPart = FWL_PART_CMB_DropDownButton;
671    param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
672                           ? FWL_PARTSTATE_CMB_Disabled
673                           : m_iBtnState;
674    param.m_pGraphics = pGraphics;
675    param.m_matrix.Concat(*pMatrix);
676    param.m_rtPart = m_rtBtn;
677    pTheme->DrawBackground(&param);
678  }
679  return FWL_ERR_Succeeded;
680}
681FWL_ERR CFWL_ComboBoxImp::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
682  if (!pThemeProvider)
683    return FWL_ERR_Indefinite;
684  m_pProperties->m_pThemeProvider = pThemeProvider;
685  if (m_pListBox && pThemeProvider->IsValidWidget(m_pListBox.get())) {
686    m_pListBox->SetThemeProvider(pThemeProvider);
687  }
688  if (m_pEdit && pThemeProvider->IsValidWidget(m_pEdit.get())) {
689    m_pEdit->SetThemeProvider(pThemeProvider);
690  }
691  return FWL_ERR_Succeeded;
692}
693int32_t CFWL_ComboBoxImp::GetCurSel() {
694  return m_iCurSel;
695}
696FWL_ERR CFWL_ComboBoxImp::SetCurSel(int32_t iSel) {
697  int32_t iCount =
698      static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())->CountItems();
699  FX_BOOL bClearSel = iSel < 0 || iSel >= iCount;
700  FX_BOOL bDropDown = IsDropDownStyle();
701  if (bDropDown && m_pEdit) {
702    if (bClearSel) {
703      m_pEdit->SetText(CFX_WideString());
704    } else {
705      CFX_WideString wsText;
706      IFWL_ComboBoxDP* pData =
707          static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
708      FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, iSel);
709      static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
710          ->GetItemText(hItem, wsText);
711      m_pEdit->SetText(wsText);
712    }
713    m_pEdit->Update();
714  }
715  m_iCurSel = bClearSel ? -1 : iSel;
716  return FWL_ERR_Succeeded;
717}
718FWL_ERR CFWL_ComboBoxImp::SetStates(FX_DWORD dwStates, FX_BOOL bSet) {
719  FX_BOOL bIsDropDown = IsDropDownStyle();
720  if (bIsDropDown && m_pEdit) {
721    m_pEdit->SetStates(dwStates, bSet);
722  }
723  if (m_pListBox) {
724    m_pListBox->SetStates(dwStates, bSet);
725  }
726  return CFWL_WidgetImp::SetStates(dwStates, bSet);
727}
728FWL_ERR CFWL_ComboBoxImp::SetEditText(const CFX_WideString& wsText) {
729  if (!m_pEdit)
730    return FWL_ERR_Indefinite;
731  m_pEdit->SetText(wsText);
732  return m_pEdit->Update();
733}
734int32_t CFWL_ComboBoxImp::GetEditTextLength() const {
735  if (!m_pEdit)
736    return -1;
737  return m_pEdit->GetTextLength();
738}
739FWL_ERR CFWL_ComboBoxImp::GetEditText(CFX_WideString& wsText,
740                                      int32_t nStart,
741                                      int32_t nCount) const {
742  if (m_pEdit) {
743    return m_pEdit->GetText(wsText, nStart, nCount);
744  } else if (m_pListBox) {
745    IFWL_ComboBoxDP* pData =
746        static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
747    FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, m_iCurSel);
748    return m_pListBox->GetItemText(hItem, wsText);
749  }
750  return FWL_ERR_Indefinite;
751}
752FWL_ERR CFWL_ComboBoxImp::SetEditSelRange(int32_t nStart, int32_t nCount) {
753  if (!m_pEdit)
754    return FWL_ERR_Indefinite;
755  static_cast<CFWL_ComboEditImp*>(m_pEdit->GetImpl())->ClearSelected();
756  m_pEdit->AddSelRange(nStart, nCount);
757  return FWL_ERR_Succeeded;
758}
759int32_t CFWL_ComboBoxImp::GetEditSelRange(int32_t nIndex, int32_t& nStart) {
760  if (!m_pEdit)
761    return -1;
762  return m_pEdit->GetSelRange(nIndex, nStart);
763}
764int32_t CFWL_ComboBoxImp::GetEditLimit() {
765  if (!m_pEdit)
766    return -1;
767  return m_pEdit->GetLimit();
768}
769FWL_ERR CFWL_ComboBoxImp::SetEditLimit(int32_t nLimit) {
770  if (!m_pEdit)
771    return FWL_ERR_Indefinite;
772  return m_pEdit->SetLimit(nLimit);
773}
774FWL_ERR CFWL_ComboBoxImp::EditDoClipboard(int32_t iCmd) {
775  if (!m_pEdit)
776    return FWL_ERR_Indefinite;
777  return m_pEdit->DoClipboard(iCmd);
778}
779FX_BOOL CFWL_ComboBoxImp::EditRedo(const CFX_ByteStringC& bsRecord) {
780  if (!m_pEdit)
781    return FALSE;
782  return m_pEdit->Redo(bsRecord);
783}
784FX_BOOL CFWL_ComboBoxImp::EditUndo(const CFX_ByteStringC& bsRecord) {
785  if (!m_pEdit)
786    return FALSE;
787  return m_pEdit->Undo(bsRecord);
788}
789IFWL_ListBox* CFWL_ComboBoxImp::GetListBoxt() {
790  return m_pListBox.get();
791}
792FX_BOOL CFWL_ComboBoxImp::AfterFocusShowDropList() {
793  if (!m_bNeedShowList) {
794    return FALSE;
795  }
796  if (m_pEdit) {
797    MatchEditText();
798  }
799  ShowDropList(TRUE);
800  m_bNeedShowList = FALSE;
801  return TRUE;
802}
803FX_ERR CFWL_ComboBoxImp::OpenDropDownList(FX_BOOL bActivate) {
804  ShowDropList(bActivate);
805  return FWL_ERR_Succeeded;
806}
807FX_BOOL CFWL_ComboBoxImp::EditCanUndo() {
808  return m_pEdit->CanUndo();
809}
810FX_BOOL CFWL_ComboBoxImp::EditCanRedo() {
811  return m_pEdit->CanRedo();
812}
813FX_BOOL CFWL_ComboBoxImp::EditUndo() {
814  return m_pEdit->Undo();
815}
816FX_BOOL CFWL_ComboBoxImp::EditRedo() {
817  return m_pEdit->Redo();
818}
819FX_BOOL CFWL_ComboBoxImp::EditCanCopy() {
820  return m_pEdit->CountSelRanges() > 0;
821}
822FX_BOOL CFWL_ComboBoxImp::EditCanCut() {
823  if (m_pEdit->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly) {
824    return FALSE;
825  }
826  return m_pEdit->CountSelRanges() > 0;
827}
828FX_BOOL CFWL_ComboBoxImp::EditCanSelectAll() {
829  return m_pEdit->GetTextLength() > 0;
830}
831FX_BOOL CFWL_ComboBoxImp::EditCopy(CFX_WideString& wsCopy) {
832  return m_pEdit->Copy(wsCopy);
833}
834FX_BOOL CFWL_ComboBoxImp::EditCut(CFX_WideString& wsCut) {
835  return m_pEdit->Cut(wsCut);
836}
837FX_BOOL CFWL_ComboBoxImp::EditPaste(const CFX_WideString& wsPaste) {
838  return m_pEdit->Paste(wsPaste);
839}
840FX_BOOL CFWL_ComboBoxImp::EditSelectAll() {
841  return m_pEdit->AddSelRange(0) == FWL_ERR_Succeeded;
842}
843FX_BOOL CFWL_ComboBoxImp::EditDelete() {
844  return m_pEdit->ClearText() == FWL_ERR_Succeeded;
845}
846FX_BOOL CFWL_ComboBoxImp::EditDeSelect() {
847  return m_pEdit->ClearSelections() == FWL_ERR_Succeeded;
848}
849FWL_ERR CFWL_ComboBoxImp::GetBBox(CFX_RectF& rect) {
850  if (m_pWidgetMgr->IsFormDisabled()) {
851    return DisForm_GetBBox(rect);
852  }
853  rect = m_pProperties->m_rtWidget;
854  if (m_pListBox && IsDropListShowed()) {
855    CFX_RectF rtList;
856    m_pListBox->GetWidgetRect(rtList);
857    rtList.Offset(rect.left, rect.top);
858    rect.Union(rtList);
859  }
860  return FWL_ERR_Succeeded;
861}
862FWL_ERR CFWL_ComboBoxImp::EditModifyStylesEx(FX_DWORD dwStylesExAdded,
863                                             FX_DWORD dwStylesExRemoved) {
864  if (m_pEdit != NULL) {
865    return m_pEdit->ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
866  } else {
867    return FWL_ERR_Parameter_Invalid;
868  }
869}
870FX_FLOAT CFWL_ComboBoxImp::GetListHeight() {
871  return static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider)
872      ->GetListHeight(m_pInterface);
873}
874void CFWL_ComboBoxImp::DrawStretchHandler(CFX_Graphics* pGraphics,
875                                          const CFX_Matrix* pMatrix) {
876  CFWL_ThemeBackground param;
877  param.m_pGraphics = pGraphics;
878  param.m_iPart = FWL_PART_CMB_StretcgHandler;
879  param.m_dwStates = FWL_PARTSTATE_CMB_Normal;
880  param.m_pWidget = m_pInterface;
881  if (pMatrix) {
882    param.m_matrix.Concat(*pMatrix);
883  }
884  param.m_rtPart = m_rtHandler;
885  m_pProperties->m_pThemeProvider->DrawBackground(&param);
886}
887void CFWL_ComboBoxImp::ShowDropList(FX_BOOL bActivate) {
888  if (m_pWidgetMgr->IsFormDisabled()) {
889    return DisForm_ShowDropList(bActivate);
890  }
891  FX_BOOL bDropList = IsDropListShowed();
892  if (bDropList == bActivate) {
893    return;
894  }
895  if (!m_pForm) {
896    InitProxyForm();
897  }
898  m_pListProxyDelegate->Reset();
899  if (bActivate) {
900    static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
901        ->ChangeSelected(m_iCurSel);
902    ReSetListItemAlignment();
903    FX_DWORD dwStyleAdd = m_pProperties->m_dwStyleExes &
904                          (FWL_STYLEEXT_CMB_Sort | FWL_STYLEEXT_CMB_OwnerDraw);
905    m_pListBox->ModifyStylesEx(dwStyleAdd, 0);
906    m_pListBox->GetWidgetRect(m_rtList, TRUE);
907    FX_FLOAT fHeight = GetListHeight();
908    if (fHeight > 0) {
909      if (m_rtList.height > GetListHeight()) {
910        m_rtList.height = GetListHeight();
911        m_pListBox->ModifyStyles(FWL_WGTSTYLE_VScroll, 0);
912      }
913    }
914    CFX_RectF rtAnchor;
915    rtAnchor.Set(0, 0, m_pProperties->m_rtWidget.width,
916                 m_pProperties->m_rtWidget.height);
917    FX_FLOAT fMinHeight = 0;
918    if (m_rtList.width < m_rtClient.width) {
919      m_rtList.width = m_rtClient.width;
920    }
921    m_rtProxy = m_rtList;
922    if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_ListDrag) {
923      m_rtProxy.height += m_fComboFormHandler;
924    }
925    GetPopupPos(fMinHeight, m_rtProxy.height, rtAnchor, m_rtProxy);
926    if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_ListDrag) {
927      FX_FLOAT fx = 0;
928      FX_FLOAT fy = m_rtClient.top + m_rtClient.height / 2;
929      TransformTo(NULL, fx, fy);
930      m_bUpFormHandler = fy > m_rtProxy.top;
931      if (m_bUpFormHandler) {
932        m_rtHandler.Set(0, 0, m_rtList.width, m_fComboFormHandler);
933        m_rtList.top = m_fComboFormHandler;
934      } else {
935        m_rtHandler.Set(0, m_rtList.height, m_rtList.width,
936                        m_fComboFormHandler);
937      }
938    }
939    m_pForm->SetWidgetRect(m_rtProxy);
940    m_pForm->Update();
941    m_pListBox->SetWidgetRect(m_rtList);
942    m_pListBox->Update();
943    CFWL_EvtCmbPreDropDown ev;
944    ev.m_pSrcTarget = m_pInterface;
945    DispatchEvent(&ev);
946    m_fItemHeight =
947        static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())->m_fItemHeight;
948    static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())->SetFocus(TRUE);
949    m_pForm->DoModal();
950    static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())->SetFocus(FALSE);
951  } else {
952    m_pForm->EndDoModal();
953    CFWL_EvtCmbCloseUp ev;
954    ev.m_pSrcTarget = m_pInterface;
955    DispatchEvent(&ev);
956    m_bLButtonDown = FALSE;
957    static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())->m_bNotifyOwner =
958        TRUE;
959    SetFocus(TRUE);
960  }
961}
962FX_BOOL CFWL_ComboBoxImp::IsDropListShowed() {
963  return m_pForm && !(m_pForm->GetStates() & FWL_WGTSTATE_Invisible);
964}
965FX_BOOL CFWL_ComboBoxImp::IsDropDownStyle() const {
966  return m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_DropDown;
967}
968void CFWL_ComboBoxImp::MatchEditText() {
969  CFX_WideString wsText;
970  m_pEdit->GetText(wsText);
971  int32_t iMatch =
972      static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())->MatchItem(wsText);
973  if (iMatch != m_iCurSel) {
974    static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
975        ->ChangeSelected(iMatch);
976    if (iMatch >= 0) {
977      SynchrEditText(iMatch);
978    }
979  } else if (iMatch >= 0) {
980    static_cast<CFWL_ComboEditImp*>(m_pEdit->GetImpl())->SetSelected();
981  }
982  m_iCurSel = iMatch;
983}
984void CFWL_ComboBoxImp::SynchrEditText(int32_t iListItem) {
985  CFX_WideString wsText;
986  IFWL_ComboBoxDP* pData =
987      static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
988  FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, iListItem);
989  static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
990      ->GetItemText(hItem, wsText);
991  m_pEdit->SetText(wsText);
992  m_pEdit->Update();
993  static_cast<CFWL_ComboEditImp*>(m_pEdit->GetImpl())->SetSelected();
994}
995void CFWL_ComboBoxImp::Layout() {
996  if (m_pWidgetMgr->IsFormDisabled()) {
997    return DisForm_Layout();
998  }
999  GetClientRect(m_rtClient);
1000  FX_FLOAT* pFWidth =
1001      static_cast<FX_FLOAT*>(GetThemeCapacity(FWL_WGTCAPACITY_ScrollBarWidth));
1002  if (!pFWidth)
1003    return;
1004  FX_FLOAT fBtn = *pFWidth;
1005  m_rtBtn.Set(m_rtClient.right() - fBtn, m_rtClient.top, fBtn,
1006              m_rtClient.height);
1007  FX_BOOL bIsDropDown = IsDropDownStyle();
1008  if (bIsDropDown && m_pEdit) {
1009    CFX_RectF rtEdit;
1010    rtEdit.Set(m_rtClient.left, m_rtClient.top, m_rtClient.width - fBtn,
1011               m_rtClient.height);
1012    m_pEdit->SetWidgetRect(rtEdit);
1013    if (m_iCurSel >= 0) {
1014      CFX_WideString wsText;
1015      IFWL_ComboBoxDP* pData =
1016          static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
1017      FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, m_iCurSel);
1018      static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
1019          ->GetItemText(hItem, wsText);
1020      m_pEdit->LockUpdate();
1021      m_pEdit->SetText(wsText);
1022      m_pEdit->UnlockUpdate();
1023    }
1024    m_pEdit->Update();
1025  }
1026}
1027void CFWL_ComboBoxImp::ReSetTheme() {
1028  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
1029  if (!pTheme) {
1030    pTheme = GetAvailableTheme();
1031    m_pProperties->m_pThemeProvider = pTheme;
1032  }
1033  if (m_pListBox) {
1034    if (!m_pListBox->GetThemeProvider() &&
1035        pTheme->IsValidWidget(m_pListBox.get())) {
1036      m_pListBox->SetThemeProvider(pTheme);
1037    }
1038  }
1039  if (m_pEdit) {
1040    if (!m_pEdit->GetThemeProvider() && pTheme->IsValidWidget(m_pEdit.get())) {
1041      m_pEdit->SetThemeProvider(pTheme);
1042    }
1043  }
1044}
1045void CFWL_ComboBoxImp::ReSetEditAlignment() {
1046  if (!m_pEdit)
1047    return;
1048  FX_DWORD dwStylExes = m_pProperties->m_dwStyleExes;
1049  FX_DWORD dwAdd = 0;
1050  switch (dwStylExes & FWL_STYLEEXT_CMB_EditHAlignMask) {
1051    case FWL_STYLEEXT_CMB_EditHCenter: {
1052      dwAdd |= FWL_STYLEEXT_EDT_HCenter;
1053      break;
1054    }
1055    case FWL_STYLEEXT_CMB_EditHFar: {
1056      dwAdd |= FWL_STYLEEXT_EDT_HFar;
1057      break;
1058    }
1059    default: { dwAdd |= FWL_STYLEEXT_EDT_HNear; }
1060  }
1061  switch (dwStylExes & FWL_STYLEEXT_CMB_EditVAlignMask) {
1062    case FWL_STYLEEXT_CMB_EditVCenter: {
1063      dwAdd |= FWL_STYLEEXT_EDT_VCenter;
1064      break;
1065    }
1066    case FWL_STYLEEXT_CMB_EditVFar: {
1067      dwAdd |= FWL_STYLEEXT_EDT_VFar;
1068      break;
1069    }
1070    default: { dwAdd |= FWL_STYLEEXT_EDT_VNear; }
1071  }
1072  if (dwStylExes & FWL_STYLEEXT_CMB_EditJustified) {
1073    dwAdd |= FWL_STYLEEXT_EDT_Justified;
1074  }
1075  if (dwStylExes & FWL_STYLEEXT_CMB_EditDistributed) {
1076    dwAdd |= FWL_STYLEEXT_EDT_Distributed;
1077  }
1078  m_pEdit->ModifyStylesEx(dwAdd, FWL_STYLEEXT_EDT_HAlignMask |
1079                                     FWL_STYLEEXT_EDT_HAlignModeMask |
1080                                     FWL_STYLEEXT_EDT_VAlignMask);
1081}
1082void CFWL_ComboBoxImp::ReSetListItemAlignment() {
1083  if (!m_pListBox)
1084    return;
1085  FX_DWORD dwStylExes = m_pProperties->m_dwStyleExes;
1086  FX_DWORD dwAdd = 0;
1087  switch (dwStylExes & FWL_STYLEEXT_CMB_ListItemAlignMask) {
1088    case FWL_STYLEEXT_CMB_ListItemCenterAlign: {
1089      dwAdd |= FWL_STYLEEXT_LTB_CenterAlign;
1090    }
1091    case FWL_STYLEEXT_CMB_ListItemRightAlign: {
1092      dwAdd |= FWL_STYLEEXT_LTB_RightAlign;
1093    }
1094    default: { dwAdd |= FWL_STYLEEXT_LTB_LeftAlign; }
1095  }
1096  m_pListBox->ModifyStylesEx(dwAdd, FWL_STYLEEXT_CMB_ListItemAlignMask);
1097}
1098void CFWL_ComboBoxImp::ProcessSelChanged(FX_BOOL bLButtonUp) {
1099  IFWL_ComboBoxDP* pDatas =
1100      static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
1101  m_iCurSel = pDatas->GetItemIndex(m_pInterface, m_pListBox->GetSelItem(0));
1102  FX_BOOL bDropDown = IsDropDownStyle();
1103  if (bDropDown) {
1104    IFWL_ComboBoxDP* pData =
1105        static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
1106    FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, m_iCurSel);
1107    if (hItem) {
1108      CFX_WideString wsText;
1109      pData->GetItemText(m_pInterface, hItem, wsText);
1110      if (m_pEdit) {
1111        m_pEdit->SetText(wsText);
1112        m_pEdit->Update();
1113        static_cast<CFWL_ComboEditImp*>(m_pEdit->GetImpl())->SetSelected();
1114      }
1115      CFWL_EvtCmbSelChanged ev;
1116      ev.bLButtonUp = bLButtonUp;
1117      ev.m_pSrcTarget = m_pInterface;
1118      ev.iArraySels.Add(m_iCurSel);
1119      DispatchEvent(&ev);
1120    }
1121  } else {
1122    Repaint(&m_rtClient);
1123  }
1124}
1125void CFWL_ComboBoxImp::InitProxyForm() {
1126  if (m_pForm)
1127    return;
1128  if (!m_pListBox)
1129    return;
1130  CFWL_WidgetImpProperties propForm;
1131  propForm.m_pOwner = m_pInterface;
1132  propForm.m_dwStyles = FWL_WGTSTYLE_Popup;
1133  propForm.m_dwStates = FWL_WGTSTATE_Invisible;
1134  CFX_WideString className;
1135  m_pForm = IFWL_Form::CreateFormProxy(propForm, &className, m_pListBox.get());
1136  m_pForm->Initialize();
1137  m_pProxy = static_cast<CFWL_FormProxyImp*>(m_pForm->GetImpl());
1138  m_pListBox->SetParent(m_pForm);
1139  m_pListProxyDelegate = new CFWL_ComboProxyImpDelegate(m_pForm, this);
1140  m_pProxy->SetDelegate(m_pListProxyDelegate);
1141}
1142FWL_ERR CFWL_ComboBoxImp::DisForm_Initialize() {
1143  if (CFWL_WidgetImp::Initialize() != FWL_ERR_Succeeded)
1144    return FWL_WGTSTATE_Invisible;  // Ditto.
1145  m_pDelegate = new CFWL_ComboBoxImpDelegate(this);
1146  DisForm_InitComboList();
1147  DisForm_InitComboEdit();
1148  return FWL_ERR_Succeeded;
1149}
1150void CFWL_ComboBoxImp::DisForm_InitComboList() {
1151  if (m_pListBox) {
1152    return;
1153  }
1154  CFWL_WidgetImpProperties prop;
1155  prop.m_pParent = m_pInterface;
1156  prop.m_dwStyles = FWL_WGTSTYLE_Border | FWL_WGTSTYLE_VScroll;
1157  prop.m_dwStates = FWL_WGTSTATE_Invisible;
1158  prop.m_pDataProvider = m_pProperties->m_pDataProvider;
1159  prop.m_pThemeProvider = m_pProperties->m_pThemeProvider;
1160  m_pListBox.reset(IFWL_ListBox::CreateComboList(prop, m_pInterface));
1161  m_pListBox->Initialize();
1162}
1163void CFWL_ComboBoxImp::DisForm_InitComboEdit() {
1164  if (m_pEdit) {
1165    return;
1166  }
1167  CFWL_WidgetImpProperties prop;
1168  prop.m_pParent = m_pInterface;
1169  prop.m_pThemeProvider = m_pProperties->m_pThemeProvider;
1170  m_pEdit.reset(IFWL_Edit::CreateComboEdit(prop, m_pInterface));
1171  m_pEdit->Initialize();
1172  static_cast<CFWL_ComboEditImp*>(m_pEdit->GetImpl())->SetOuter(m_pInterface);
1173}
1174void CFWL_ComboBoxImp::DisForm_ShowDropList(FX_BOOL bActivate) {
1175  FX_BOOL bDropList = DisForm_IsDropListShowed();
1176  if (bDropList == bActivate) {
1177    return;
1178  }
1179  if (bActivate) {
1180    CFWL_EvtCmbPreDropDown preEvent;
1181    preEvent.m_pSrcTarget = m_pInterface;
1182    DispatchEvent(&preEvent);
1183    CFWL_ComboListImp* pComboList =
1184        static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl());
1185    int32_t iItems = pComboList->CountItems();
1186    if (iItems < 1) {
1187      return;
1188    }
1189    ReSetListItemAlignment();
1190    pComboList->ChangeSelected(m_iCurSel);
1191    FX_FLOAT fItemHeight = pComboList->GetItemHeigt();
1192    FX_FLOAT fBorder = GetBorderSize();
1193    FX_FLOAT fPopupMin = 0.0f;
1194    if (iItems > 3) {
1195      fPopupMin = fItemHeight * 3 + fBorder * 2;
1196    }
1197    FX_FLOAT fPopupMax = fItemHeight * iItems + fBorder * 2;
1198    CFX_RectF rtList;
1199    rtList.left = m_rtClient.left;
1200    rtList.width = m_pProperties->m_rtWidget.width;
1201    rtList.top = 0;
1202    rtList.height = 0;
1203    GetPopupPos(fPopupMin, fPopupMax, m_pProperties->m_rtWidget, rtList);
1204    m_pListBox->SetWidgetRect(rtList);
1205    m_pListBox->Update();
1206  } else {
1207    SetFocus(TRUE);
1208  }
1209  m_pListBox->SetStates(FWL_WGTSTATE_Invisible, !bActivate);
1210  if (bActivate) {
1211    CFWL_EvtCmbPostDropDown postEvent;
1212    postEvent.m_pSrcTarget = m_pInterface;
1213    DispatchEvent(&postEvent);
1214  }
1215  CFX_RectF rect;
1216  m_pListBox->GetWidgetRect(rect);
1217  rect.Inflate(2, 2);
1218  Repaint(&rect);
1219}
1220FX_BOOL CFWL_ComboBoxImp::DisForm_IsDropListShowed() {
1221  return !(m_pListBox->GetStates() & FWL_WGTSTATE_Invisible);
1222}
1223FWL_ERR CFWL_ComboBoxImp::DisForm_ModifyStylesEx(FX_DWORD dwStylesExAdded,
1224                                                 FX_DWORD dwStylesExRemoved) {
1225  if (!m_pEdit) {
1226    DisForm_InitComboEdit();
1227  }
1228  FX_BOOL bAddDropDown = dwStylesExAdded & FWL_STYLEEXT_CMB_DropDown;
1229  FX_BOOL bDelDropDown = dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown;
1230  dwStylesExRemoved &= ~FWL_STYLEEXT_CMB_DropDown;
1231  m_pProperties->m_dwStyleExes |= FWL_STYLEEXT_CMB_DropDown;
1232  if (bAddDropDown) {
1233    m_pEdit->ModifyStylesEx(0, FWL_STYLEEXT_EDT_ReadOnly);
1234  } else if (bDelDropDown) {
1235    m_pEdit->ModifyStylesEx(FWL_STYLEEXT_EDT_ReadOnly, 0);
1236  }
1237  return CFWL_WidgetImp::ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
1238}
1239FWL_ERR CFWL_ComboBoxImp::DisForm_Update() {
1240  if (m_iLock) {
1241    return FWL_ERR_Indefinite;
1242  }
1243  if (m_pEdit) {
1244    ReSetEditAlignment();
1245  }
1246  ReSetTheme();
1247  Layout();
1248  return FWL_ERR_Succeeded;
1249}
1250FX_DWORD CFWL_ComboBoxImp::DisForm_HitTest(FX_FLOAT fx, FX_FLOAT fy) {
1251  CFX_RectF rect;
1252  rect.Set(0, 0, m_pProperties->m_rtWidget.width - m_rtBtn.width,
1253           m_pProperties->m_rtWidget.height);
1254  if (rect.Contains(fx, fy)) {
1255    return FWL_WGTHITTEST_Edit;
1256  }
1257  if (m_rtBtn.Contains(fx, fy)) {
1258    return FWL_WGTHITTEST_Client;
1259  }
1260  if (DisForm_IsDropListShowed()) {
1261    m_pListBox->GetWidgetRect(rect);
1262    if (rect.Contains(fx, fy)) {
1263      return FWL_WGTHITTEST_Client;
1264    }
1265  }
1266  return FWL_WGTHITTEST_Unknown;
1267}
1268FWL_ERR CFWL_ComboBoxImp::DisForm_DrawWidget(CFX_Graphics* pGraphics,
1269                                             const CFX_Matrix* pMatrix) {
1270  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
1271  CFX_Matrix mtOrg;
1272  mtOrg.Set(1, 0, 0, 1, 0, 0);
1273  if (pMatrix) {
1274    mtOrg = *pMatrix;
1275  }
1276  FX_BOOL bListShowed = m_pListBox && DisForm_IsDropListShowed();
1277  pGraphics->SaveGraphState();
1278  pGraphics->ConcatMatrix(&mtOrg);
1279  if (!m_rtBtn.IsEmpty(0.1f)) {
1280    CFWL_ThemeBackground param;
1281    param.m_pWidget = m_pInterface;
1282    param.m_iPart = FWL_PART_CMB_DropDownButton;
1283    param.m_dwStates = m_iBtnState;
1284    param.m_pGraphics = pGraphics;
1285    param.m_rtPart = m_rtBtn;
1286    pTheme->DrawBackground(&param);
1287  }
1288  pGraphics->RestoreGraphState();
1289  if (m_pEdit) {
1290    CFX_RectF rtEdit;
1291    m_pEdit->GetWidgetRect(rtEdit);
1292    CFX_Matrix mt;
1293    mt.Set(1, 0, 0, 1, rtEdit.left, rtEdit.top);
1294    mt.Concat(mtOrg);
1295    m_pEdit->DrawWidget(pGraphics, &mt);
1296  }
1297  if (bListShowed) {
1298    CFX_RectF rtList;
1299    m_pListBox->GetWidgetRect(rtList);
1300    CFX_Matrix mt;
1301    mt.Set(1, 0, 0, 1, rtList.left, rtList.top);
1302    mt.Concat(mtOrg);
1303    m_pListBox->DrawWidget(pGraphics, &mt);
1304  }
1305  return FWL_ERR_Succeeded;
1306}
1307FWL_ERR CFWL_ComboBoxImp::DisForm_GetBBox(CFX_RectF& rect) {
1308  rect = m_pProperties->m_rtWidget;
1309  if (m_pListBox && DisForm_IsDropListShowed()) {
1310    CFX_RectF rtList;
1311    m_pListBox->GetWidgetRect(rtList);
1312    rtList.Offset(rect.left, rect.top);
1313    rect.Union(rtList);
1314  }
1315  return FWL_ERR_Succeeded;
1316}
1317void CFWL_ComboBoxImp::DisForm_Layout() {
1318  GetClientRect(m_rtClient);
1319  m_rtContent = m_rtClient;
1320  FX_FLOAT* pFWidth =
1321      static_cast<FX_FLOAT*>(GetThemeCapacity(FWL_WGTCAPACITY_ScrollBarWidth));
1322  if (!pFWidth)
1323    return;
1324  FX_FLOAT borderWidth = 0;
1325  { borderWidth = FWL_PART_CMB_Border; }
1326  FX_FLOAT fBtn = *pFWidth;
1327  if (!(this->GetStylesEx() & FWL_STYLEEXT_CMB_ReadOnly)) {
1328    m_rtBtn.Set(m_rtClient.right() - fBtn, m_rtClient.top + borderWidth,
1329                fBtn - borderWidth, m_rtClient.height - 2 * borderWidth);
1330  }
1331  CFX_RectF* pUIMargin =
1332      static_cast<CFX_RectF*>(GetThemeCapacity(FWL_WGTCAPACITY_UIMargin));
1333  if (pUIMargin) {
1334    m_rtContent.Deflate(pUIMargin->left, pUIMargin->top, pUIMargin->width,
1335                        pUIMargin->height);
1336  }
1337  FX_BOOL bIsDropDown = IsDropDownStyle();
1338  if (bIsDropDown && m_pEdit) {
1339    CFX_RectF rtEdit;
1340    rtEdit.Set(m_rtContent.left, m_rtContent.top, m_rtContent.width - fBtn,
1341               m_rtContent.height);
1342    m_pEdit->SetWidgetRect(rtEdit);
1343    if (m_iCurSel >= 0) {
1344      CFX_WideString wsText;
1345      IFWL_ComboBoxDP* pData =
1346          static_cast<IFWL_ComboBoxDP*>(m_pProperties->m_pDataProvider);
1347      FWL_HLISTITEM hItem = pData->GetItem(m_pInterface, m_iCurSel);
1348      static_cast<CFWL_ComboListImp*>(m_pListBox->GetImpl())
1349          ->GetItemText(hItem, wsText);
1350      m_pEdit->LockUpdate();
1351      m_pEdit->SetText(wsText);
1352      m_pEdit->UnlockUpdate();
1353    }
1354    m_pEdit->Update();
1355  }
1356}
1357CFWL_ComboBoxImpDelegate::CFWL_ComboBoxImpDelegate(CFWL_ComboBoxImp* pOwner)
1358    : m_pOwner(pOwner) {}
1359int32_t CFWL_ComboBoxImpDelegate::OnProcessMessage(CFWL_Message* pMessage) {
1360  if (m_pOwner->m_pWidgetMgr->IsFormDisabled()) {
1361    return DisForm_OnProcessMessage(pMessage);
1362  }
1363  if (!pMessage)
1364    return 0;
1365  FX_DWORD dwMsgCode = pMessage->GetClassID();
1366  FX_BOOL iRet = 1;
1367  switch (dwMsgCode) {
1368    case FWL_MSGHASH_SetFocus:
1369    case FWL_MSGHASH_KillFocus: {
1370      OnFocusChanged(pMessage, dwMsgCode == FWL_MSGHASH_SetFocus);
1371      break;
1372    }
1373    case FWL_MSGHASH_Mouse: {
1374      CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
1375      FX_DWORD dwCmd = pMsg->m_dwCmd;
1376      switch (dwCmd) {
1377        case FWL_MSGMOUSECMD_LButtonDown: {
1378          OnLButtonDown(pMsg);
1379          break;
1380        }
1381        case FWL_MSGMOUSECMD_LButtonUp: {
1382          OnLButtonUp(pMsg);
1383          break;
1384        }
1385        case FWL_MSGMOUSECMD_MouseMove: {
1386          OnMouseMove(pMsg);
1387          break;
1388        }
1389        case FWL_MSGMOUSECMD_MouseLeave: {
1390          OnMouseLeave(pMsg);
1391          break;
1392        }
1393        default: {}
1394      }
1395      break;
1396    }
1397    case FWL_MSGHASH_Key: {
1398      OnKey(static_cast<CFWL_MsgKey*>(pMessage));
1399      break;
1400    }
1401    default: { iRet = 0; }
1402  }
1403  CFWL_WidgetImpDelegate::OnProcessMessage(pMessage);
1404  return iRet;
1405}
1406FWL_ERR CFWL_ComboBoxImpDelegate::OnProcessEvent(CFWL_Event* pEvent) {
1407  FX_DWORD dwFlag = pEvent->GetClassID();
1408  if (dwFlag == FWL_EVTHASH_LTB_DrawItem) {
1409    CFWL_EvtLtbDrawItem* pDrawItemEvent =
1410        static_cast<CFWL_EvtLtbDrawItem*>(pEvent);
1411    CFWL_EvtCmbDrawItem pTemp;
1412    pTemp.m_pSrcTarget = m_pOwner->m_pInterface;
1413    pTemp.m_pGraphics = pDrawItemEvent->m_pGraphics;
1414    pTemp.m_index = pDrawItemEvent->m_index;
1415    pTemp.m_rtItem = pDrawItemEvent->m_rect;
1416    m_pOwner->DispatchEvent(&pTemp);
1417  } else if (dwFlag == FWL_EVTHASH_Scroll) {
1418    CFWL_EvtScroll* pScrollEvent = static_cast<CFWL_EvtScroll*>(pEvent);
1419    CFWL_EvtScroll pScrollEv;
1420    pScrollEv.m_pSrcTarget = m_pOwner->m_pInterface;
1421    pScrollEv.m_iScrollCode = pScrollEvent->m_iScrollCode;
1422    pScrollEv.m_fPos = pScrollEvent->m_fPos;
1423    m_pOwner->DispatchEvent(&pScrollEv);
1424  } else if (dwFlag == FWL_EVTHASH_EDT_TextChanged) {
1425    CFWL_EvtEdtTextChanged* pTextChangedEvent =
1426        static_cast<CFWL_EvtEdtTextChanged*>(pEvent);
1427    CFWL_EvtCmbEditChanged pTemp;
1428    pTemp.m_pSrcTarget = m_pOwner->m_pInterface;
1429    pTemp.wsInsert = pTextChangedEvent->wsInsert;
1430    pTemp.wsDelete = pTextChangedEvent->wsDelete;
1431    pTemp.nChangeType = pTextChangedEvent->nChangeType;
1432    m_pOwner->DispatchEvent(&pTemp);
1433  }
1434  return FWL_ERR_Succeeded;
1435}
1436FWL_ERR CFWL_ComboBoxImpDelegate::OnDrawWidget(CFX_Graphics* pGraphics,
1437                                               const CFX_Matrix* pMatrix) {
1438  return m_pOwner->DrawWidget(pGraphics, pMatrix);
1439}
1440void CFWL_ComboBoxImpDelegate::OnFocusChanged(CFWL_Message* pMsg,
1441                                              FX_BOOL bSet) {
1442  IFWL_Target* pDstTarget = pMsg->m_pDstTarget;
1443  IFWL_Target* pSrcTarget = pMsg->m_pSrcTarget;
1444  FX_BOOL bDropDown = m_pOwner->IsDropDownStyle();
1445  if (bSet) {
1446    m_pOwner->m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
1447    if (bDropDown && pSrcTarget != m_pOwner->m_pListBox.get()) {
1448      if (!m_pOwner->m_pEdit)
1449        return;
1450      static_cast<CFWL_ComboEditImp*>(m_pOwner->m_pEdit->GetImpl())
1451          ->SetSelected();
1452    } else {
1453      m_pOwner->Repaint(&m_pOwner->m_rtClient);
1454    }
1455  } else {
1456    m_pOwner->m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
1457    if (bDropDown && pDstTarget != m_pOwner->m_pListBox.get()) {
1458      if (!m_pOwner->m_pEdit)
1459        return;
1460      static_cast<CFWL_ComboEditImp*>(m_pOwner->m_pEdit->GetImpl())
1461          ->FlagFocus(FALSE);
1462      static_cast<CFWL_ComboEditImp*>(m_pOwner->m_pEdit->GetImpl())
1463          ->ClearSelected();
1464    } else {
1465      m_pOwner->Repaint(&m_pOwner->m_rtClient);
1466    }
1467  }
1468}
1469void CFWL_ComboBoxImpDelegate::OnLButtonDown(CFWL_MsgMouse* pMsg) {
1470  if (m_pOwner->m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) {
1471    return;
1472  }
1473  FX_BOOL bDropDown = m_pOwner->IsDropDownStyle();
1474  CFX_RectF& rtBtn = bDropDown ? m_pOwner->m_rtBtn : m_pOwner->m_rtClient;
1475  FX_BOOL bClickBtn = rtBtn.Contains(pMsg->m_fx, pMsg->m_fy);
1476  if (bClickBtn) {
1477    if (bDropDown && m_pOwner->m_pEdit) {
1478      m_pOwner->MatchEditText();
1479    }
1480    m_pOwner->m_bLButtonDown = TRUE;
1481    m_pOwner->m_iBtnState = FWL_PARTSTATE_CMB_Pressed;
1482    m_pOwner->Repaint(&m_pOwner->m_rtClient);
1483    m_pOwner->ShowDropList(TRUE);
1484    m_pOwner->m_iBtnState = FWL_PARTSTATE_CMB_Normal;
1485    m_pOwner->Repaint(&m_pOwner->m_rtClient);
1486  }
1487}
1488void CFWL_ComboBoxImpDelegate::OnLButtonUp(CFWL_MsgMouse* pMsg) {
1489  m_pOwner->m_bLButtonDown = FALSE;
1490  if (m_pOwner->m_rtBtn.Contains(pMsg->m_fx, pMsg->m_fy)) {
1491    m_pOwner->m_iBtnState = FWL_PARTSTATE_CMB_Hovered;
1492  } else {
1493    m_pOwner->m_iBtnState = FWL_PARTSTATE_CMB_Normal;
1494  }
1495  m_pOwner->Repaint(&m_pOwner->m_rtBtn);
1496}
1497void CFWL_ComboBoxImpDelegate::OnMouseMove(CFWL_MsgMouse* pMsg) {
1498  int32_t iOldState = m_pOwner->m_iBtnState;
1499  if (m_pOwner->m_rtBtn.Contains(pMsg->m_fx, pMsg->m_fy)) {
1500    m_pOwner->m_iBtnState = m_pOwner->m_bLButtonDown
1501                                ? FWL_PARTSTATE_CMB_Pressed
1502                                : FWL_PARTSTATE_CMB_Hovered;
1503  } else {
1504    m_pOwner->m_iBtnState = FWL_PARTSTATE_CMB_Normal;
1505  }
1506  if ((iOldState != m_pOwner->m_iBtnState) &&
1507      !((m_pOwner->m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) ==
1508        FWL_WGTSTATE_Disabled)) {
1509    m_pOwner->Repaint(&m_pOwner->m_rtBtn);
1510  }
1511}
1512void CFWL_ComboBoxImpDelegate::OnMouseLeave(CFWL_MsgMouse* pMsg) {
1513  if (!m_pOwner->IsDropListShowed() &&
1514      !((m_pOwner->m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) ==
1515        FWL_WGTSTATE_Disabled)) {
1516    m_pOwner->m_iBtnState = FWL_PARTSTATE_CMB_Normal;
1517    m_pOwner->Repaint(&m_pOwner->m_rtBtn);
1518  }
1519}
1520void CFWL_ComboBoxImpDelegate::OnKey(CFWL_MsgKey* pMsg) {
1521  FX_DWORD dwKeyCode = pMsg->m_dwKeyCode;
1522  if (dwKeyCode == FWL_VKEY_Tab) {
1523    m_pOwner->DispatchKeyEvent(pMsg);
1524    return;
1525  }
1526  FX_BOOL bSubCtrlKey = pMsg->m_pDstTarget == m_pOwner->m_pInterface;
1527  if (bSubCtrlKey) {
1528    DoSubCtrlKey(pMsg);
1529  }
1530}
1531void CFWL_ComboBoxImpDelegate::DoSubCtrlKey(CFWL_MsgKey* pMsg) {
1532  FX_DWORD dwKeyCode = pMsg->m_dwKeyCode;
1533  FX_BOOL bUp = dwKeyCode == FWL_VKEY_Up;
1534  FX_BOOL bDown = dwKeyCode == FWL_VKEY_Down;
1535  if (bUp || bDown) {
1536    int32_t iCount = static_cast<CFWL_ComboListImp*>(
1537                         m_pOwner->m_pListBox->GetImpl())->CountItems();
1538    if (iCount < 1) {
1539      return;
1540    }
1541    FX_BOOL bMatchEqual = FALSE;
1542    int32_t iCurSel = m_pOwner->m_iCurSel;
1543    FX_BOOL bDropDown = m_pOwner->IsDropDownStyle();
1544    if (bDropDown && m_pOwner->m_pEdit) {
1545      CFX_WideString wsText;
1546      m_pOwner->m_pEdit->GetText(wsText);
1547      iCurSel = static_cast<CFWL_ComboListImp*>(m_pOwner->m_pListBox->GetImpl())
1548                    ->MatchItem(wsText);
1549      if (iCurSel >= 0) {
1550        CFX_WideString wsTemp;
1551        IFWL_ComboBoxDP* pData = static_cast<IFWL_ComboBoxDP*>(
1552            m_pOwner->m_pProperties->m_pDataProvider);
1553        FWL_HLISTITEM hItem = pData->GetItem(m_pOwner->m_pInterface, iCurSel);
1554        static_cast<CFWL_ComboListImp*>(m_pOwner->m_pListBox->GetImpl())
1555            ->GetItemText(hItem, wsTemp);
1556        bMatchEqual = wsText.Equal(wsTemp);
1557      }
1558    }
1559    if (iCurSel < 0) {
1560      iCurSel = 0;
1561    } else if (!bDropDown || bMatchEqual) {
1562      if ((bUp && iCurSel == 0) || (bDown && iCurSel == iCount - 1)) {
1563        return;
1564      }
1565      if (bUp) {
1566        iCurSel--;
1567      } else {
1568        iCurSel++;
1569      }
1570    }
1571    m_pOwner->m_iCurSel = iCurSel;
1572    if (bDropDown && m_pOwner->m_pEdit) {
1573      m_pOwner->SynchrEditText(m_pOwner->m_iCurSel);
1574    } else {
1575      m_pOwner->Repaint(&m_pOwner->m_rtClient);
1576    }
1577    return;
1578  }
1579  FX_BOOL bDropDown = m_pOwner->IsDropDownStyle();
1580  if (bDropDown) {
1581    IFWL_WidgetDelegate* pDelegate = m_pOwner->m_pEdit->SetDelegate(NULL);
1582    pDelegate->OnProcessMessage(pMsg);
1583  }
1584}
1585int32_t CFWL_ComboBoxImpDelegate::DisForm_OnProcessMessage(
1586    CFWL_Message* pMessage) {
1587  if (!pMessage)
1588    return 0;
1589  FX_DWORD dwMsgCode = pMessage->GetClassID();
1590  FX_BOOL backDefault = TRUE;
1591  switch (dwMsgCode) {
1592    case FWL_MSGHASH_SetFocus:
1593    case FWL_MSGHASH_KillFocus: {
1594      backDefault = FALSE;
1595      DisForm_OnFocusChanged(pMessage, dwMsgCode == FWL_MSGHASH_SetFocus);
1596      break;
1597    }
1598    case FWL_MSGHASH_Mouse: {
1599      backDefault = FALSE;
1600      CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
1601      FX_DWORD dwCmd = pMsg->m_dwCmd;
1602      switch (dwCmd) {
1603        case FWL_MSGMOUSECMD_LButtonDown: {
1604          DisForm_OnLButtonDown(pMsg);
1605          break;
1606        }
1607        case FWL_MSGMOUSECMD_LButtonUp: {
1608          OnLButtonUp(pMsg);
1609          break;
1610        }
1611        default: {}
1612      }
1613      break;
1614    }
1615    case FWL_MSGHASH_Key: {
1616      backDefault = FALSE;
1617      CFWL_MsgKey* pKey = static_cast<CFWL_MsgKey*>(pMessage);
1618      if (pKey->m_dwCmd == FWL_MSGKEYCMD_KeyUp) {
1619        break;
1620      }
1621      if (m_pOwner->DisForm_IsDropListShowed() &&
1622          pKey->m_dwCmd == FWL_MSGKEYCMD_KeyDown) {
1623        FX_DWORD dwKeyCode = pKey->m_dwKeyCode;
1624        FX_BOOL bListKey =
1625            dwKeyCode == FWL_VKEY_Up || dwKeyCode == FWL_VKEY_Down ||
1626            dwKeyCode == FWL_VKEY_Return || dwKeyCode == FWL_VKEY_Escape;
1627        if (bListKey) {
1628          IFWL_WidgetDelegate* pDelegate =
1629              m_pOwner->m_pListBox->SetDelegate(NULL);
1630          pDelegate->OnProcessMessage(pMessage);
1631          break;
1632        }
1633      }
1634      DisForm_OnKey(pKey);
1635      break;
1636    }
1637    default: {}
1638  }
1639  if (!backDefault) {
1640    return 1;
1641  }
1642  return CFWL_WidgetImpDelegate::OnProcessMessage(pMessage);
1643}
1644void CFWL_ComboBoxImpDelegate::DisForm_OnLButtonDown(CFWL_MsgMouse* pMsg) {
1645  FX_BOOL bDropDown = m_pOwner->DisForm_IsDropListShowed();
1646  CFX_RectF& rtBtn = bDropDown ? m_pOwner->m_rtBtn : m_pOwner->m_rtClient;
1647  FX_BOOL bClickBtn = rtBtn.Contains(pMsg->m_fx, pMsg->m_fy);
1648  if (bClickBtn) {
1649    if (m_pOwner->DisForm_IsDropListShowed()) {
1650      m_pOwner->DisForm_ShowDropList(FALSE);
1651      return;
1652    }
1653    {
1654      if (m_pOwner->m_pEdit) {
1655        m_pOwner->MatchEditText();
1656      }
1657      m_pOwner->DisForm_ShowDropList(TRUE);
1658    }
1659  }
1660}
1661void CFWL_ComboBoxImpDelegate::DisForm_OnFocusChanged(CFWL_Message* pMsg,
1662                                                      FX_BOOL bSet) {
1663  if (bSet) {
1664    m_pOwner->m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
1665    if ((m_pOwner->m_pEdit->GetStates() & FWL_WGTSTATE_Focused) == 0) {
1666      CFWL_MsgSetFocus msg;
1667      msg.m_pDstTarget = m_pOwner->m_pEdit.get();
1668      msg.m_pSrcTarget = NULL;
1669      IFWL_WidgetDelegate* pDelegate = m_pOwner->m_pEdit->SetDelegate(NULL);
1670      pDelegate->OnProcessMessage(&msg);
1671    }
1672  } else {
1673    m_pOwner->m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
1674    m_pOwner->DisForm_ShowDropList(FALSE);
1675    CFWL_MsgKillFocus msg;
1676    msg.m_pDstTarget = NULL;
1677    msg.m_pSrcTarget = m_pOwner->m_pEdit.get();
1678    IFWL_WidgetDelegate* pDelegate = m_pOwner->m_pEdit->SetDelegate(NULL);
1679    pDelegate->OnProcessMessage(&msg);
1680  }
1681}
1682void CFWL_ComboBoxImpDelegate::DisForm_OnKey(CFWL_MsgKey* pMsg) {
1683  FX_DWORD dwKeyCode = pMsg->m_dwKeyCode;
1684  FX_BOOL bUp = dwKeyCode == FWL_VKEY_Up;
1685  FX_BOOL bDown = dwKeyCode == FWL_VKEY_Down;
1686  if (bUp || bDown) {
1687    CFWL_ComboListImp* pComboList =
1688        static_cast<CFWL_ComboListImp*>(m_pOwner->m_pListBox->GetImpl());
1689    int32_t iCount = pComboList->CountItems();
1690    if (iCount < 1) {
1691      return;
1692    }
1693    FX_BOOL bMatchEqual = FALSE;
1694    int32_t iCurSel = m_pOwner->m_iCurSel;
1695    if (m_pOwner->m_pEdit) {
1696      CFX_WideString wsText;
1697      m_pOwner->m_pEdit->GetText(wsText);
1698      iCurSel = pComboList->MatchItem(wsText);
1699      if (iCurSel >= 0) {
1700        CFX_WideString wsTemp;
1701        FWL_HLISTITEM item = m_pOwner->m_pListBox->GetSelItem(iCurSel);
1702        m_pOwner->m_pListBox->GetItemText(item, wsTemp);
1703        bMatchEqual = wsText.Equal(wsTemp);
1704      }
1705    }
1706    if (iCurSel < 0) {
1707      iCurSel = 0;
1708    } else if (bMatchEqual) {
1709      if ((bUp && iCurSel == 0) || (bDown && iCurSel == iCount - 1)) {
1710        return;
1711      }
1712      if (bUp) {
1713        iCurSel--;
1714      } else {
1715        iCurSel++;
1716      }
1717    }
1718    m_pOwner->m_iCurSel = iCurSel;
1719    m_pOwner->SynchrEditText(m_pOwner->m_iCurSel);
1720    return;
1721  }
1722  if (m_pOwner->m_pEdit) {
1723    IFWL_WidgetDelegate* pDelegate = m_pOwner->m_pEdit->SetDelegate(NULL);
1724    pDelegate->OnProcessMessage(pMsg);
1725  }
1726}
1727CFWL_ComboProxyImpDelegate::CFWL_ComboProxyImpDelegate(
1728    IFWL_Form* pForm,
1729    CFWL_ComboBoxImp* pComboBox)
1730    : m_bLButtonDown(FALSE),
1731      m_bLButtonUpSelf(FALSE),
1732      m_fStartPos(0),
1733      m_pForm(pForm),
1734      m_pComboBox(pComboBox) {
1735}
1736int32_t CFWL_ComboProxyImpDelegate::OnProcessMessage(CFWL_Message* pMessage) {
1737  if (!pMessage)
1738    return 0;
1739  FX_DWORD dwMsgCode = pMessage->GetClassID();
1740  if (dwMsgCode == FWL_MSGHASH_Mouse) {
1741    CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
1742    FX_DWORD dwCmd = pMsg->m_dwCmd;
1743    switch (dwCmd) {
1744      case FWL_MSGMOUSECMD_LButtonDown: {
1745        OnLButtonDown(pMsg);
1746        break;
1747      }
1748      case FWL_MSGMOUSECMD_LButtonUp: {
1749        OnLButtonUp(pMsg);
1750        break;
1751      }
1752      case FWL_MSGMOUSECMD_MouseMove: {
1753        OnMouseMove(pMsg);
1754        break;
1755      }
1756      default: {}
1757    }
1758  }
1759  if (dwMsgCode == FWL_MSGHASH_Deactivate) {
1760    OnDeactive(static_cast<CFWL_MsgDeactivate*>(pMessage));
1761  }
1762  if (dwMsgCode == FWL_MSGHASH_KillFocus || dwMsgCode == FWL_MSGHASH_SetFocus) {
1763    OnFocusChanged(static_cast<CFWL_MsgKillFocus*>(pMessage),
1764                   dwMsgCode == FWL_MSGHASH_SetFocus);
1765  }
1766  return CFWL_WidgetImpDelegate::OnProcessMessage(pMessage);
1767}
1768FWL_ERR CFWL_ComboProxyImpDelegate::OnDrawWidget(CFX_Graphics* pGraphics,
1769                                                 const CFX_Matrix* pMatrix) {
1770  m_pComboBox->DrawStretchHandler(pGraphics, pMatrix);
1771  return FWL_ERR_Succeeded;
1772}
1773void CFWL_ComboProxyImpDelegate::OnLButtonDown(CFWL_MsgMouse* pMsg) {
1774  IFWL_NoteThread* pThread = m_pForm->GetOwnerThread();
1775  if (!pThread)
1776    return;
1777  CFWL_NoteDriver* pDriver =
1778      static_cast<CFWL_NoteDriver*>(pThread->GetNoteDriver());
1779  CFX_RectF rtWidget;
1780  m_pForm->GetWidgetRect(rtWidget);
1781  rtWidget.left = rtWidget.top = 0;
1782  if (rtWidget.Contains(pMsg->m_fx, pMsg->m_fy)) {
1783    m_bLButtonDown = TRUE;
1784    pDriver->SetGrab(m_pForm, TRUE);
1785  } else {
1786    m_bLButtonDown = FALSE;
1787    pDriver->SetGrab(m_pForm, FALSE);
1788    m_pComboBox->ShowDropList(FALSE);
1789    return;
1790  }
1791  IFWL_AdapterNative* pNative = FWL_GetAdapterNative();
1792  IFWL_AdapterCursorMgr* pCursorMgr = pNative->GetCursorMgr();
1793  FWL_HCURSOR hCursor = pCursorMgr->GetSystemCursor(FWL_CURSORTYPE_SizeNS);
1794  pCursorMgr->SetCursor(hCursor);
1795  pCursorMgr->ShowCursor(TRUE);
1796  m_pForm->TransformTo(NULL, pMsg->m_fx, pMsg->m_fy);
1797  m_fStartPos = pMsg->m_fy;
1798}
1799void CFWL_ComboProxyImpDelegate::OnLButtonUp(CFWL_MsgMouse* pMsg) {
1800  m_bLButtonDown = FALSE;
1801  IFWL_NoteThread* pThread = m_pForm->GetOwnerThread();
1802  if (!pThread)
1803    return;
1804  CFWL_NoteDriver* pDriver =
1805      static_cast<CFWL_NoteDriver*>(pThread->GetNoteDriver());
1806  pDriver->SetGrab(m_pForm, FALSE);
1807  if (m_bLButtonUpSelf) {
1808    CFX_RectF rect;
1809    m_pForm->GetWidgetRect(rect);
1810    rect.left = rect.top = 0;
1811    if (!rect.Contains(pMsg->m_fx, pMsg->m_fy) &&
1812        m_pComboBox->IsDropListShowed()) {
1813      m_pComboBox->ShowDropList(FALSE);
1814    }
1815  } else {
1816    m_bLButtonUpSelf = TRUE;
1817  }
1818}
1819void CFWL_ComboProxyImpDelegate::OnMouseMove(CFWL_MsgMouse* pMsg) {
1820  IFWL_AdapterNative* pNative = FWL_GetAdapterNative();
1821  IFWL_AdapterCursorMgr* pCursorMgr = pNative->GetCursorMgr();
1822  FWL_CURSORTYPE cursorType = FWL_CURSORTYPE_Arrow;
1823  if (m_pComboBox->m_rtHandler.Contains(pMsg->m_fx, pMsg->m_fy)) {
1824    cursorType = FWL_CURSORTYPE_SizeNS;
1825  }
1826  FWL_HCURSOR hCursor = pCursorMgr->GetSystemCursor(cursorType);
1827  pCursorMgr->SetCursor(hCursor);
1828  pCursorMgr->ShowCursor(TRUE);
1829  if (!m_bLButtonDown) {
1830    return;
1831  }
1832  m_pForm->TransformTo(NULL, pMsg->m_fx, pMsg->m_fy);
1833  FX_FLOAT fChanged = pMsg->m_fy - m_fStartPos;
1834  if (m_pComboBox->m_bUpFormHandler) {
1835    fChanged = m_fStartPos - pMsg->m_fy;
1836  }
1837  if (m_pComboBox->m_rtList.height + fChanged < m_pComboBox->m_fItemHeight) {
1838    return;
1839  }
1840  m_pComboBox->m_rtList.height += fChanged;
1841  m_pComboBox->m_rtProxy.height += fChanged;
1842  if (m_pComboBox->m_bUpFormHandler) {
1843    m_pComboBox->m_rtProxy.top -= fChanged;
1844    m_pComboBox->m_rtHandler.Set(0, 0, m_pComboBox->m_rtList.width,
1845                                 m_pComboBox->m_fComboFormHandler);
1846  } else {
1847    m_pComboBox->m_rtHandler.Set(0, m_pComboBox->m_rtList.height,
1848                                 m_pComboBox->m_rtList.width,
1849                                 m_pComboBox->m_fComboFormHandler);
1850  }
1851  m_pForm->SetWidgetRect(m_pComboBox->m_rtProxy);
1852  m_pComboBox->m_pListBox->SetWidgetRect(m_pComboBox->m_rtList);
1853  m_pComboBox->m_pListBox->Update();
1854  m_fStartPos = pMsg->m_fy;
1855}
1856void CFWL_ComboProxyImpDelegate::OnDeactive(CFWL_MsgDeactivate* pMsg) {
1857  m_pComboBox->ShowDropList(FALSE);
1858}
1859void CFWL_ComboProxyImpDelegate::OnFocusChanged(CFWL_MsgKillFocus* pMsg,
1860                                                FX_BOOL bSet) {
1861  if (!bSet) {
1862    if (pMsg->m_pSetFocus == NULL) {
1863      m_pComboBox->ShowDropList(FALSE);
1864    }
1865  }
1866}
1867