+BEGIN_EVENT_TABLE(wxComboBox, wxControl)
+ EVT_MENU(wxID_CUT, wxComboBox::OnCut)
+ EVT_MENU(wxID_COPY, wxComboBox::OnCopy)
+ EVT_MENU(wxID_PASTE, wxComboBox::OnPaste)
+ EVT_MENU(wxID_UNDO, wxComboBox::OnUndo)
+ EVT_MENU(wxID_REDO, wxComboBox::OnRedo)
+ EVT_MENU(wxID_CLEAR, wxComboBox::OnDelete)
+ EVT_MENU(wxID_SELECTALL, wxComboBox::OnSelectAll)
+
+ EVT_UPDATE_UI(wxID_CUT, wxComboBox::OnUpdateCut)
+ EVT_UPDATE_UI(wxID_COPY, wxComboBox::OnUpdateCopy)
+ EVT_UPDATE_UI(wxID_PASTE, wxComboBox::OnUpdatePaste)
+ EVT_UPDATE_UI(wxID_UNDO, wxComboBox::OnUpdateUndo)
+ EVT_UPDATE_UI(wxID_REDO, wxComboBox::OnUpdateRedo)
+ EVT_UPDATE_UI(wxID_CLEAR, wxComboBox::OnUpdateDelete)
+ EVT_UPDATE_UI(wxID_SELECTALL, wxComboBox::OnUpdateSelectAll)
+END_EVENT_TABLE()
+
+// ----------------------------------------------------------------------------
+// function prototypes
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+
+// ---------------------------------------------------------------------------
+// global vars
+// ---------------------------------------------------------------------------
+
+// the pointer to standard radio button wnd proc
+static WNDPROC gs_wndprocEdit = (WNDPROC)NULL;
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wnd proc for subclassed edit control
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ HWND hwndCombo = ::GetParent(hWnd);
+ wxWindow *win = wxFindWinFromHandle((WXHWND)hwndCombo);
+
+ switch ( message )
+ {
+ // forward some messages to the combobox to generate the appropriate
+ // wxEvents from them
+ case WM_KEYUP:
+ case WM_KEYDOWN:
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ {
+ wxComboBox *combo = wxDynamicCast(win, wxComboBox);
+ if ( !combo )
+ {
+ // we can get WM_KILLFOCUS while our parent is already half
+ // destroyed and hence doesn't look like a combobx any
+ // longer, check for it to avoid bogus assert failures
+ if ( !win->IsBeingDeleted() )
+ {
+ wxFAIL_MSG( _T("should have combo as parent") );
+ }
+ }
+ else if ( combo->MSWProcessEditMsg(message, wParam, lParam) )
+ {
+ // handled by parent
+ return 0;
+ }
+ }
+ break;
+
+ case WM_GETDLGCODE:
+ {
+ wxCHECK_MSG( win, 0, _T("should have a parent") );
+
+ if ( win->GetWindowStyle() & wxPROCESS_ENTER )
+ {
+ // need to return a custom dlg code or we'll never get it
+ return DLGC_WANTMESSAGE;
+ }
+ }
+ break;
+
+ // deal with tooltips here
+#if wxUSE_TOOLTIPS && defined(TTN_NEEDTEXT)
+ case WM_NOTIFY:
+ {
+ wxCHECK_MSG( win, 0, _T("should have a parent") );
+
+ NMHDR* hdr = (NMHDR *)lParam;
+ if ( hdr->code == TTN_NEEDTEXT )
+ {
+ wxToolTip *tooltip = win->GetToolTip();
+ if ( tooltip )
+ {
+ TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
+ ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
+ }
+
+ // processed
+ return 0;
+ }
+ }
+ break;
+#endif // wxUSE_TOOLTIPS
+ }
+
+ return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit, hWnd, message, wParam, lParam);
+}
+
+// ----------------------------------------------------------------------------
+// wxComboBox callbacks
+// ----------------------------------------------------------------------------
+
+WXLRESULT wxComboBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ // TODO: handle WM_CTLCOLOR messages from our EDIT control to be able to
+ // set its colour correctly (to be the same as our own one)
+
+ switch ( nMsg )
+ {
+ case CB_SETCURSEL:
+ // Selection was set with SetSelection. Update the value too.
+ if ((int)wParam > GetCount())
+ m_value.clear();
+ else
+ m_value = GetString(wParam);
+ m_selectionOld = -1;
+ break;
+
+ case WM_SIZE:
+ // wxStaticBox can generate this message, when modifying the control's style.
+ // This causes the content of the combobox to be selected, for some reason.
+ case WM_STYLECHANGED:
+ {
+ // combobox selection sometimes spontaneously changes when its
+ // size changes, restore it to the old value if necessary
+ long fromOld, toOld;
+ GetSelection(&fromOld, &toOld);
+ WXLRESULT result = wxChoice::MSWWindowProc(nMsg, wParam, lParam);
+
+ long fromNew, toNew;
+ GetSelection(&fromNew, &toNew);
+
+ if ( fromOld != fromNew || toOld != toNew )
+ {
+ SetSelection(fromOld, toOld);
+ }
+
+ return result;
+ }
+ }
+
+ return wxChoice::MSWWindowProc(nMsg, wParam, lParam);
+}
+
+bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ switch ( msg )
+ {
+ case WM_CHAR:
+ // for compatibility with wxTextCtrl, generate a special message
+ // when Enter is pressed
+ if ( wParam == VK_RETURN )
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
+ InitCommandEvent(event);
+ event.SetString(GetValue());
+ event.SetInt(GetSelection());
+ if ( ProcessCommand(event) )
+ {
+ // don't let the event through to the native control
+ // because it doesn't need it and may generate an annoying
+ // beep if it gets it
+ return true;
+ }
+ }
+ // fall through
+
+ case WM_SYSCHAR:
+ return HandleChar(wParam, lParam, true /* isASCII */);
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ return HandleKeyDown(wParam, lParam);
+
+ case WM_SYSKEYUP:
+ case WM_KEYUP:
+ return HandleKeyUp(wParam, lParam);
+
+ case WM_SETFOCUS:
+ return HandleSetFocus((WXHWND)wParam);
+
+ case WM_KILLFOCUS:
+ return HandleKillFocus((WXHWND)wParam);
+ }
+
+ return false;
+}
+
+bool wxComboBox::MSWCommand(WXUINT param, WXWORD id)
+{
+ wxString value;
+ int sel = -1;
+ switch ( param )
+ {
+ case CBN_SELENDOK:
+ case CBN_SELCHANGE:
+ sel = GetSelection();
+
+ // we may sometimes get 2 CBN_SELCHANGE events or a CBN_SELENDOK
+ // before CBN_SELCHANGE with the same index when the user selects
+ // an item in the combobox -- ignore duplicates
+ if ( sel > -1 && sel != m_selectionOld )
+ {
+ m_selectionOld = sel;
+
+ // GetValue() would still return the old value from here but
+ // according to the docs we should return the new value if the
+ // user calls it in his event handler, so update internal
+ // m_value
+ m_value = GetString(sel);
+
+ wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, GetId());
+ event.SetInt(sel);
+ event.SetEventObject(this);
+ event.SetString(m_value);
+ ProcessCommand(event);
+ }
+ else // no valid selection
+ {
+ m_selectionOld = sel;
+
+ // hence no EVT_TEXT neither
+ break;
+ }
+
+ // fall through: for compability with wxGTK, also send the text
+ // update event when the selection changes (this also seems more
+ // logical as the text does change)
+
+ case CBN_EDITCHANGE:
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
+
+ // if sel != -1, value was initialized above (and we can't use
+ // GetValue() here as it would return the old selection and we
+ // want the new one)
+ if ( sel == -1 )
+ {
+ m_value = wxGetWindowText(GetHwnd());
+ m_selectionOld = -1;
+ }
+ else // we're synthesizing text updated event from sel change
+ {
+ // We need to retrieve the current selection because the
+ // user may have changed it in the previous handler (for
+ // CBN_SELCHANGE above).
+ sel = GetSelection();
+ if ( sel > -1 )
+ {
+ m_value = GetString(sel);
+ }
+ }
+
+ event.SetString(m_value);
+ event.SetEventObject(this);
+ ProcessCommand(event);
+ }
+ break;
+
+ default:
+ return wxChoice::MSWCommand(param, id);
+ }
+
+ // let the def window proc have it by returning false, but do not pass the
+ // message we've already handled here (notably CBN_SELCHANGE) to the base
+ // class as it would generate another event for them
+ return false;
+}
+
+WXHWND wxComboBox::GetEditHWND() const
+{
+ // this function should not be called for wxCB_READONLY controls, it is
+ // the callers responsability to check this
+ wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY),
+ _T("read-only combobox doesn't have any edit control") );
+
+ POINT pt;
+ pt.x = pt.y = 4;
+ HWND hwndEdit = ::ChildWindowFromPoint(GetHwnd(), pt);
+ if ( !hwndEdit || hwndEdit == GetHwnd() )
+ {
+ wxFAIL_MSG(_T("not read only combobox without edit control?"));
+ }
+
+ return (WXHWND)hwndEdit;
+}
+
+// ----------------------------------------------------------------------------
+// wxComboBox creation
+// ----------------------------------------------------------------------------
+
+bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
+ const wxString& value,
+ const wxPoint& pos,
+ const wxSize& size,
+ int n, const wxString choices[],
+ long style,
+ const wxValidator& validator,
+ const wxString& name)
+{
+ // pretend that wxComboBox is hidden while it is positioned and resized and
+ // show it only right before leaving this method because otherwise there is
+ // some noticeable flicker while the control rearranges itself
+ m_isShown = false;
+
+ if ( !CreateAndInit(parent, id, pos, size, n, choices, style,
+ validator, name) )
+ return false;
+
+ // we shouldn't call SetValue() for an empty string because this would
+ // (correctly) result in an assert with a read only combobox and is useless
+ // for the other ones anyhow
+ if ( !value.empty() )
+ SetValue(value);
+
+ // a (not read only) combobox is, in fact, 2 controls: the combobox itself
+ // and an edit control inside it and if we want to catch events from this
+ // edit control, we must subclass it as well
+ if ( !(style & wxCB_READONLY) )
+ {
+ gs_wndprocEdit = wxSetWindowProc((HWND)GetEditHWND(),
+ wxComboEditWndProc);
+ }
+
+ // and finally, show the control
+ Show(true);
+
+ return true;