]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/combobox.cpp
Fix duplicate wxContextMenuEvent generation in wxMSW.
[wxWidgets.git] / src / msw / combobox.cpp
index 9537a60acc0e509a0354fb52311325521123e4c3..d98241f0c415e44e8e8bc1cd8be3a81609d8f174 100644 (file)
@@ -32,7 +32,7 @@
     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
     #include "wx/settings.h"
     #include "wx/log.h"
-    // for wxEVT_COMMAND_TEXT_ENTER
+    // for wxEVT_TEXT_ENTER
     #include "wx/textctrl.h"
     #include "wx/app.h"
     #include "wx/brush.h"
 // wxWin macros
 // ----------------------------------------------------------------------------
 
-#if wxUSE_EXTENDED_RTTI
-WX_DEFINE_FLAGS( wxComboBoxStyle )
-
-wxBEGIN_FLAGS( wxComboBoxStyle )
-    // new style border flags, we put them first to
-    // use them for streaming out
-    wxFLAGS_MEMBER(wxBORDER_SIMPLE)
-    wxFLAGS_MEMBER(wxBORDER_SUNKEN)
-    wxFLAGS_MEMBER(wxBORDER_DOUBLE)
-    wxFLAGS_MEMBER(wxBORDER_RAISED)
-    wxFLAGS_MEMBER(wxBORDER_STATIC)
-    wxFLAGS_MEMBER(wxBORDER_NONE)
-
-    // old style border flags
-    wxFLAGS_MEMBER(wxSIMPLE_BORDER)
-    wxFLAGS_MEMBER(wxSUNKEN_BORDER)
-    wxFLAGS_MEMBER(wxDOUBLE_BORDER)
-    wxFLAGS_MEMBER(wxRAISED_BORDER)
-    wxFLAGS_MEMBER(wxSTATIC_BORDER)
-    wxFLAGS_MEMBER(wxBORDER)
-
-    // standard window styles
-    wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
-    wxFLAGS_MEMBER(wxCLIP_CHILDREN)
-    wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
-    wxFLAGS_MEMBER(wxWANTS_CHARS)
-    wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
-    wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
-    wxFLAGS_MEMBER(wxVSCROLL)
-    wxFLAGS_MEMBER(wxHSCROLL)
-
-    wxFLAGS_MEMBER(wxCB_SIMPLE)
-    wxFLAGS_MEMBER(wxCB_SORT)
-    wxFLAGS_MEMBER(wxCB_READONLY)
-    wxFLAGS_MEMBER(wxCB_DROPDOWN)
-
-wxEND_FLAGS( wxComboBoxStyle )
-
-IMPLEMENT_DYNAMIC_CLASS_XTI(wxComboBox, wxChoice,"wx/combobox.h")
-
-wxBEGIN_PROPERTIES_TABLE(wxComboBox)
-    wxEVENT_PROPERTY( Select , wxEVT_COMMAND_COMBOBOX_SELECTED , wxCommandEvent )
-    wxEVENT_PROPERTY( TextEnter , wxEVT_COMMAND_TEXT_ENTER , wxCommandEvent )
-
-    // TODO DELEGATES
-    wxPROPERTY( Font , wxFont , SetFont , GetFont  , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-    wxPROPERTY_COLLECTION( Choices , wxArrayString , wxString , AppendString , GetStrings , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-    wxPROPERTY( Value ,wxString, SetValue, GetValue, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-    wxPROPERTY( Selection ,int, SetSelection, GetSelection, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-    wxPROPERTY_FLAGS( WindowStyle , wxComboBoxStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxComboBox)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_5( wxComboBox , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size )
-
-#else
-
-IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxChoice)
-
-#endif
-
 BEGIN_EVENT_TABLE(wxComboBox, wxControl)
     EVT_MENU(wxID_CUT, wxComboBox::OnCut)
     EVT_MENU(wxID_COPY, wxComboBox::OnCopy)
@@ -155,6 +92,36 @@ static WNDPROC gs_wndprocEdit = (WNDPROC)NULL;
 // implementation
 // ============================================================================
 
+namespace
+{
+
+// Check if the given message should be forwarded from the edit control which
+// is part of the combobox to wxComboBox itself. All messages generating the
+// events that the code using wxComboBox could be interested in must be
+// forwarded.
+bool ShouldForwardFromEditToCombo(UINT message)
+{
+    switch ( message )
+    {
+        case WM_KEYUP:
+        case WM_KEYDOWN:
+        case WM_CHAR:
+        case WM_SYSCHAR:
+        case WM_SYSKEYDOWN:
+        case WM_SYSKEYUP:
+        case WM_SETFOCUS:
+        case WM_KILLFOCUS:
+        case WM_CUT:
+        case WM_COPY:
+        case WM_PASTE:
+            return true;
+    }
+
+    return false;
+}
+
+} // anonymous namespace
+
 // ----------------------------------------------------------------------------
 // wnd proc for subclassed edit control
 // ----------------------------------------------------------------------------
@@ -167,49 +134,34 @@ LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
     HWND hwndCombo = ::GetParent(hWnd);
     wxWindow *win = wxFindWinFromHandle((WXHWND)hwndCombo);
 
-    switch ( message )
+    if ( ShouldForwardFromEditToCombo(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() )
             {
-                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( wxT("should have combo as parent") );
-                    }
-                }
-                else if ( combo->MSWProcessEditMsg(message, wParam, lParam) )
-                {
-                    // handled by parent
-                    return 0;
-                }
+                wxFAIL_MSG( wxT("should have combo as parent") );
             }
-            break;
-
-        case WM_GETDLGCODE:
-            {
-                wxCHECK_MSG( win, 0, wxT("should have a parent") );
+        }
+        else if ( combo->MSWProcessEditMsg(message, wParam, lParam) )
+        {
+            // handled by parent
+            return 0;
+        }
+    }
+    else if ( message == WM_GETDLGCODE )
+    {
+        wxCHECK_MSG( win, 0, wxT("should have a parent") );
 
-                if ( win->GetWindowStyle() & wxTE_PROCESS_ENTER )
-                {
-                    // need to return a custom dlg code or we'll never get it
-                    return DLGC_WANTMESSAGE;
-                }
-            }
-            break;
+        if ( win->GetWindowStyle() & wxTE_PROCESS_ENTER )
+        {
+            // need to return a custom dlg code or we'll never get it
+            return DLGC_WANTMESSAGE;
+        }
     }
 
     return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit, hWnd, message, wParam, lParam);
@@ -282,7 +234,7 @@ bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
                 if (SendMessage(GetHwnd(), CB_GETDROPPEDSTATE, 0, 0))
                     return false;
 
-                wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
+                wxCommandEvent event(wxEVT_TEXT_ENTER, m_windowId);
 
                 const int sel = GetSelection();
                 event.SetInt(sel);
@@ -297,29 +249,16 @@ bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
                     return true;
                 }
             }
-            // fall through
+            // fall through, WM_CHAR is one of the message we should forward.
 
-        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);
-
-        case WM_CUT:
-        case WM_COPY:
-        case WM_PASTE:
-            return HandleClipboardEvent(msg);
+        default:
+            if ( ShouldForwardFromEditToCombo(msg) )
+            {
+                // For all the messages forward from the edit control the
+                // result is not used.
+                WXLRESULT result;
+                return MSWHandleMessage(&result, msg, wParam, lParam);
+            }
     }
 
     return false;
@@ -335,26 +274,27 @@ bool wxComboBox::MSWCommand(WXUINT param, WXWORD id)
         case CBN_DROPDOWN:
             // remember the last selection, just as wxChoice does
             m_lastAcceptedSelection = GetCurrentSelection();
-            if ( m_lastAcceptedSelection == -1 )
             {
-                // but unlike with wxChoice we may have no selection but still
-                // have some text and we should avoid erasing it if the drop
-                // down is cancelled (see #8474)
-                m_lastAcceptedSelection = wxID_NONE;
-            }
-            {
-                wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_DROPDOWN, GetId());
+                wxCommandEvent event(wxEVT_COMBOBOX_DROPDOWN, GetId());
                 event.SetEventObject(this);
                 ProcessCommand(event);
             }
             break;
+
         case CBN_CLOSEUP:
+            // Do the same thing as in wxChoice but using different event type.
+            if ( m_pendingSelection != wxID_NONE )
             {
-                wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_CLOSEUP, GetId());
+                SendSelectionChangedEvent(wxEVT_COMBOBOX);
+                m_pendingSelection = wxID_NONE;
+            }
+            {
+                wxCommandEvent event(wxEVT_COMBOBOX_CLOSEUP, GetId());
                 event.SetEventObject(this);
                 ProcessCommand(event);
             }
             break;
+
         case CBN_SELENDOK:
 #ifndef __SMARTPHONE__
             // we need to reset this to prevent the selection from being undone
@@ -370,25 +310,18 @@ bool wxComboBox::MSWCommand(WXUINT param, WXWORD id)
             // this string is going to become the new combobox value soon but
             // we need it to be done right now, otherwise the event handler
             // could get a wrong value when it calls our GetValue()
-            ::SetWindowText(GetHwnd(), value.wx_str());
+            ::SetWindowText(GetHwnd(), value.t_str());
 
-            {
-                wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, GetId());
-                event.SetInt(sel);
-                event.SetString(value);
-                InitCommandEventWithItems(event, sel);
-
-                ProcessCommand(event);
-            }
+            SendSelectionChangedEvent(wxEVT_COMBOBOX);
 
-            // fall through: for compability with wxGTK, also send the text
+            // fall through: for compatibility 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:
             if ( m_allowTextEvents )
             {
-                wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
+                wxCommandEvent event(wxEVT_TEXT, GetId());
 
                 // if sel != -1, value was already initialized above
                 if ( sel == -1 )
@@ -438,24 +371,13 @@ bool wxComboBox::MSWShouldPreProcessMessage(WXMSG *pMsg)
 
 WXHWND wxComboBox::GetEditHWNDIfAvailable() const
 {
+    // FIXME-VC6: Only VC6 needs this guard, see WINVER definition in
+    //            include/wx/msw/wrapwin.h
 #if defined(WINVER) && WINVER >= 0x0500
-    typedef BOOL (WINAPI *GetComboBoxInfo_t)(HWND, COMBOBOXINFO*);
-    static GetComboBoxInfo_t s_pfnGetComboBoxInfo = NULL;
-    static bool s_triedToLoad = false;
-    if ( !s_triedToLoad )
-    {
-        s_triedToLoad = true;
-        wxLoadedDLL dllUser32("user32.dll");
-        wxDL_INIT_FUNC(s_pfn, GetComboBoxInfo, dllUser32);
-    }
-
-    if ( s_pfnGetComboBoxInfo )
-    {
-        WinStruct<COMBOBOXINFO> info;
-        (*s_pfnGetComboBoxInfo)(GetHwnd(), &info);
+    WinStruct<COMBOBOXINFO> info;
+    if ( MSWGetComboBoxInfo(&info) )
         return info.hwndItem;
-    }
-#endif
+#endif // WINVER >= 0x0500
 
     if (HasFlag(wxCB_SIMPLE))
     {
@@ -608,6 +530,11 @@ void wxComboBox::Clear()
         wxTextEntry::Clear();
 }
 
+bool wxComboBox::ContainsHWND(WXHWND hWnd) const
+{
+    return hWnd == GetEditHWNDIfAvailable();
+}
+
 void wxComboBox::GetSelection(long *from, long *to) const
 {
     if ( !HasFlag(wxCB_READONLY) )
@@ -702,22 +629,6 @@ void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
     event.Enable(IsEditable() && !wxTextEntry::IsEmpty());
 }
 
-void wxComboBox::MSWDoPopupOrDismiss(bool show)
-{
-    wxASSERT_MSG( !HasFlag(wxCB_SIMPLE),
-                  wxT("can't popup/dismiss the list for simple combo box") );
-
-    // we *must* set focus to the combobox before showing or hiding the drop
-    // down as without this we get WM_LBUTTONDOWN messages with invalid HWND
-    // when hiding it (whether programmatically or manually) resulting in a
-    // crash when we pass them to IsDialogMessage()
-    //
-    // this can be seen in the combo page of the widgets sample under Windows 7
-    SetFocus();
-
-    ::SendMessage(GetHwnd(), CB_SHOWDROPDOWN, show, 0);
-}
-
 #if wxUSE_TOOLTIPS
 
 void wxComboBox::DoSetToolTip(wxToolTip *tip)
@@ -725,7 +636,7 @@ void wxComboBox::DoSetToolTip(wxToolTip *tip)
     wxChoice::DoSetToolTip(tip);
 
     if ( tip && !HasFlag(wxCB_READONLY) )
-        tip->Add(GetEditHWND());
+        tip->AddOtherWindow(GetEditHWND());
 }
 
 #endif // wxUSE_TOOLTIPS
@@ -748,4 +659,20 @@ bool wxComboBox::SetHint(const wxString& hintOrig)
 
 #endif // wxUSE_UXTHEME
 
+wxSize wxComboBox::DoGetSizeFromTextSize(int xlen, int ylen) const
+{
+    wxSize tsize( wxChoice::DoGetSizeFromTextSize(xlen, ylen) );
+
+    if ( !HasFlag(wxCB_READONLY) )
+    {
+        // Add the margins we have previously set
+        wxPoint marg( GetMargins() );
+        marg.x = wxMax(0, marg.x);
+        marg.y = wxMax(0, marg.y);
+        tsize.IncBy( marg );
+    }
+
+    return tsize;
+}
+
 #endif // wxUSE_COMBOBOX