X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/289532452089421ddadbd4726a8469511a19ab76..88932ec82d048d006cdc81bd309f5e59aee799ac:/src/msw/choice.cpp diff --git a/src/msw/choice.cpp b/src/msw/choice.cpp index 64c3929328..e7c17023c1 100644 --- a/src/msw/choice.cpp +++ b/src/msw/choice.cpp @@ -36,6 +36,8 @@ #include "wx/settings.h" #endif +#include "wx/dynlib.h" + #include "wx/msw/private.h" // ============================================================================ @@ -86,10 +88,7 @@ bool wxChoice::CreateAndInit(wxWindow *parent, // initialize the controls contents - for ( int i = 0; i < n; i++ ) - { - Append(choices[i]); - } + Append(n, choices); // and now we may finally size the control properly (if needed) SetInitialSize(size); @@ -201,6 +200,28 @@ wxChoice::~wxChoice() Clear(); } +bool wxChoice::MSWGetComboBoxInfo(tagCOMBOBOXINFO* info) const +{ + // TODO-Win9x: Get rid of this once we officially drop support for Win9x + // and just call the function directly. +#if wxUSE_DYNLIB_CLASS + typedef BOOL (WINAPI *GetComboBoxInfo_t)(HWND, tagCOMBOBOXINFO*); + 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 ) + return (*s_pfnGetComboBoxInfo)(GetHwnd(), info) != 0; +#endif // wxUSE_DYNLIB_CLASS + + return false; +} + // ---------------------------------------------------------------------------- // adding/deleting items to/from the list // ---------------------------------------------------------------------------- @@ -336,7 +357,7 @@ int wxChoice::FindString(const wxString& s, bool bCase) const else { int pos = (int)SendMessage(GetHwnd(), CB_FINDSTRINGEXACT, - (WPARAM)-1, (LPARAM)s.wx_str()); + (WPARAM)-1, wxMSW_CONV_LPARAM(s)); return pos == LB_ERR ? wxNOT_FOUND : pos; } @@ -358,8 +379,12 @@ void wxChoice::SetString(unsigned int n, const wxString& s) else if ( HasClientObjectData() ) oldObjData = GetClientObject(n); + // and also the selection if we're going to delete the item that was + // selected + const bool wasSelected = static_cast(n) == GetSelection(); + ::SendMessage(GetHwnd(), CB_DELETESTRING, n, 0); - ::SendMessage(GetHwnd(), CB_INSERTSTRING, n, (LPARAM)s.wx_str() ); + ::SendMessage(GetHwnd(), CB_INSERTSTRING, n, wxMSW_CONV_LPARAM(s) ); // restore the client data if ( oldData ) @@ -367,6 +392,11 @@ void wxChoice::SetString(unsigned int n, const wxString& s) else if ( oldObjData ) SetClientObject(n, oldObjData); + // and the selection + if ( wasSelected ) + SetSelection(n); + + // the width could have changed so the best size needs to be recomputed InvalidateBestSize(); } @@ -408,7 +438,7 @@ void wxChoice::DoSetItemClientData(unsigned int n, void* clientData) void* wxChoice::DoGetItemClientData(unsigned int n) const { LPARAM rc = SendMessage(GetHwnd(), CB_GETITEMDATA, n, 0); - if ( rc == CB_ERR ) + if ( rc == CB_ERR && GetLastError() != ERROR_SUCCESS ) { wxLogLastError(wxT("CB_GETITEMDATA")); @@ -544,7 +574,10 @@ void wxChoice::DoSetSize(int x, int y, const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0); int heightWithItems = 0; if (!HasFlag(wxCB_SIMPLE)) - heightWithItems = height + hItem*nItems; + // The extra item (" + 1") is required to prevent a vertical + // scrollbar from appearing with comctl32.dll versions earlier + // than 6.0 (such as found in Win2k). + heightWithItems = height + hItem*(nItems + 1); else heightWithItems = SetHeightSimpleComboBox(nItems); @@ -556,7 +589,7 @@ void wxChoice::DoSetSize(int x, int y, // make the control itself of the requested height: notice that this // must be done after changing its size or it has no effect (apparently // the height is reset to default during the control layout) and that it's - // useless to to do it when using the deferred sizing -- in this case it + // useless to do it when using the deferred sizing -- in this case it // will be done from MSWEndDeferWindowPos() #if wxUSE_DEFERRED_SIZING if ( m_pendingSize == wxDefaultSize ) @@ -578,43 +611,85 @@ void wxChoice::DoSetSize(int x, int y, wxSize wxChoice::DoGetBestSize() const { - // find the widest string - int wChoice = 0; - int hChoice; - const unsigned int nItems = GetCount(); - for ( unsigned int i = 0; i < nItems; i++ ) - { - int wLine; - GetTextExtent(GetString(i), &wLine, NULL); - if ( wLine > wChoice ) - wChoice = wLine; - } + // The base version returns the size of the largest string + return GetSizeFromTextSize(wxChoiceBase::DoGetBestSize().x); +} + +int wxChoice::SetHeightSimpleComboBox(int nItems) const +{ + int cx, cy; + wxGetCharSize( GetHWND(), &cx, &cy, GetFont() ); + int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, (WPARAM)-1, 0); + return EDIT_HEIGHT_FROM_CHAR_HEIGHT( cy ) * wxMin( wxMax( nItems, 3 ), 6 ) + hItem - 1; +} - // give it some reasonable default value if there are no strings in the - // list - if ( wChoice == 0 ) - wChoice = 100; +wxSize wxChoice::DoGetSizeFromTextSize(int xlen, int ylen) const +{ + int cHeight = GetCharHeight(); - // the combobox should be slightly larger than the widest string - wChoice += 5*GetCharWidth(); - if( HasFlag( wxCB_SIMPLE ) ) + // We are interested in the difference of sizes between the whole control + // and its child part. I.e. arrow, separators, etc. + wxSize tsize(xlen, 0); + + // FIXME-VC6: Only VC6 needs this guard, see WINVER definition in + // include/wx/msw/wrapwin.h +#if defined(WINVER) && WINVER >= 0x0500 + WinStruct info; + if ( MSWGetComboBoxInfo(&info) ) + { + tsize.x += info.rcItem.left + info.rcButton.right - info.rcItem.right + + info.rcItem.left + 3; // right and extra margins + } + else // Just use some rough approximation. +#endif // WINVER >= 0x0500 { - hChoice = SetHeightSimpleComboBox( nItems ); + tsize.x += 4*cHeight; } + + // set height on our own + if( HasFlag( wxCB_SIMPLE ) ) + tsize.y = SetHeightSimpleComboBox(GetCount()); else - hChoice = EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight()); + tsize.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cHeight); + + // Perhaps the user wants something different from CharHeight + if ( ylen > 0 ) + tsize.IncBy(0, ylen - cHeight); - wxSize best(wChoice, hChoice); - CacheBestSize(best); - return best; + return tsize; } -int wxChoice::SetHeightSimpleComboBox(int nItems) const +// ---------------------------------------------------------------------------- +// Popup operations +// ---------------------------------------------------------------------------- + +void wxChoice::MSWDoPopupOrDismiss(bool show) { - int cx, cy; - wxGetCharSize( GetHWND(), &cx, &cy, GetFont() ); - int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, (WPARAM)-1, 0); - return EDIT_HEIGHT_FROM_CHAR_HEIGHT( cy ) * wxMin( wxMax( nItems, 3 ), 6 ) + hItem - 1; + 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); +} + +bool wxChoice::Show(bool show) +{ + if ( !wxChoiceBase::Show(show) ) + return false; + + // When hiding the combobox, we also need to hide its popup part as it + // doesn't happen automatically. + if ( !show && ::SendMessage(GetHwnd(), CB_GETDROPPEDSTATE, 0, 0) ) + MSWDoPopupOrDismiss(false); + + return true; } // ---------------------------------------------------------------------------- @@ -749,7 +824,7 @@ bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) WXHBRUSH wxChoice::MSWControlColor(WXHDC hDC, WXHWND hWnd) { - if ( !IsEnabled() ) + if ( !IsThisEnabled() ) return MSWControlColorDisabled(hDC); return wxChoiceBase::MSWControlColor(hDC, hWnd);