X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5de69dd366b43dc43259b34728272ae550ade92b..14ca3a3b0ead6b9f521368d35ca34c87678544a8:/src/msw/choice.cpp diff --git a/src/msw/choice.cpp b/src/msw/choice.cpp index 7b73e6f6f1..5b7f131e0b 100644 --- a/src/msw/choice.cpp +++ b/src/msw/choice.cpp @@ -30,6 +30,7 @@ #ifndef WX_PRECOMP #include "wx/utils.h" + #include "wx/app.h" #include "wx/log.h" #include "wx/brush.h" #include "wx/settings.h" @@ -117,8 +118,8 @@ bool wxChoice::Create(wxWindow *parent, wxASSERT_MSG( !(style & wxCB_DROPDOWN) && !(style & wxCB_READONLY) && !(style & wxCB_SIMPLE), - _T("this style flag is ignored by wxChoice, you ") - _T("probably want to use a wxComboBox") ); + wxT("this style flag is ignored by wxChoice, you ") + wxT("probably want to use a wxComboBox") ); return CreateAndInit(parent, id, pos, size, n, choices, style, validator, name); @@ -142,10 +143,6 @@ bool wxChoice::CreateAndInit(wxWindow *parent, return false; - // choice/combobox normally has "white" (depends on colour scheme, of - // course) background rather than inheriting the parent's background - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - // initialize the controls contents for ( int i = 0; i < n; i++ ) { @@ -224,6 +221,39 @@ WXDWORD wxChoice::MSWGetStyle(long style, WXDWORD *exstyle) const return msStyle; } +#ifndef EP_EDITTEXT + #define EP_EDITTEXT 1 + #define ETS_NORMAL 1 +#endif + +wxVisualAttributes +wxChoice::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) +{ + // it is important to return valid values for all attributes from here, + // GetXXX() below rely on this + wxVisualAttributes attrs; + + // FIXME: Use better dummy window? + wxWindow* wnd = wxTheApp->GetTopWindow(); + if (!wnd) + return attrs; + + attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + + // there doesn't seem to be any way to get the text colour using themes + // API: TMT_TEXTCOLOR doesn't work neither for EDIT nor COMBOBOX + attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + + // NB: use EDIT, not COMBOBOX (the latter works in XP but not Vista) + attrs.colBg = wnd->MSWGetThemeColour(L"EDIT", + EP_EDITTEXT, + ETS_NORMAL, + ThemeColourBackground, + wxSYS_COLOUR_WINDOW); + + return attrs; +} + wxChoice::~wxChoice() { Clear(); @@ -265,7 +295,7 @@ int wxChoice::DoInsertItems(const wxArrayStringsAdapter& items, // we need to refresh our size in order to have enough space for the // newly added items if ( !IsFrozen() ) - UpdateVisibleHeight(); + MSWUpdateDropDownHeight(); InvalidateBestSize(); @@ -279,7 +309,7 @@ void wxChoice::DoDeleteOneItem(unsigned int n) SendMessage(GetHwnd(), CB_DELETESTRING, n, 0); if ( !IsFrozen() ) - UpdateVisibleHeight(); + MSWUpdateDropDownHeight(); InvalidateBestSize(); } @@ -289,7 +319,7 @@ void wxChoice::DoClear() SendMessage(GetHwnd(), CB_RESETCONTENT, 0, 0); if ( !IsFrozen() ) - UpdateVisibleHeight(); + MSWUpdateDropDownHeight(); InvalidateBestSize(); } @@ -448,13 +478,45 @@ void* wxChoice::DoGetItemClientData(unsigned int n) const } // ---------------------------------------------------------------------------- -// wxMSW specific helpers +// wxMSW-specific geometry management // ---------------------------------------------------------------------------- -void wxChoice::UpdateVisibleHeight() +namespace +{ + +// there is a difference between the height passed to CB_SETITEMHEIGHT and the +// real height of the combobox; it is probably not constant for all Windows +// versions/settings but right now I don't know how to find what it is so it is +// temporarily hardcoded to its value under XP systems with normal fonts sizes +const int COMBO_HEIGHT_ADJ = 6; + +} // anonymous namespace + +void wxChoice::MSWUpdateVisibleHeight() +{ + if ( m_heightOwn != wxDefaultCoord ) + { + ::SendMessage(GetHwnd(), CB_SETITEMHEIGHT, + (WPARAM)-1, m_heightOwn - COMBO_HEIGHT_ADJ); + } +} + +#if wxUSE_DEFERRED_SIZING +void wxChoice::MSWEndDeferWindowPos() +{ + // we can only set the height of the choice itself now as it is reset to + // default every time the control is resized + MSWUpdateVisibleHeight(); + + wxChoiceBase::MSWEndDeferWindowPos(); +} +#endif // wxUSE_DEFERRED_SIZING + +void wxChoice::MSWUpdateDropDownHeight() { // be careful to not change the width here - DoSetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, GetSize().y, wxSIZE_USE_EXISTING); + DoSetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, GetSize().y, + wxSIZE_USE_EXISTING); } void wxChoice::DoMoveWindow(int x, int y, int width, int height) @@ -477,133 +539,106 @@ void wxChoice::DoMoveWindow(int x, int y, int width, int height) void wxChoice::DoGetSize(int *w, int *h) const { + wxControl::DoGetSize(w, h); + // this is weird: sometimes, the height returned by Windows is clearly the // total height of the control including the drop down list -- but only - // sometimes, and normally it isn't... I have no idea about what to do with - // this - wxControl::DoGetSize(w, h); + // sometimes, and sometimes it isn't so work around this here by using our + // own stored value if we have it + if ( h && m_heightOwn != wxDefaultCoord ) + *h = m_heightOwn; } void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags) { - int heightOrig = height; + const int heightBest = GetBestSize().y; - // the height which we must pass to Windows should be the total height of - // the control including the drop down list while the height given to us - // is, of course, just the height of the permanently visible part of it - if ( height != wxDefaultCoord ) + // we need the real height below so get the current one if it's not given + if ( height == wxDefaultCoord ) { - // don't make the drop down list too tall, arbitrarily limit it to 40 - // items max and also don't leave it empty - size_t nItems = GetCount(); - if ( !nItems ) - nItems = 9; - else if ( nItems > 24 ) - nItems = 24; - - // add space for the drop down list - const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0); - height += hItem*(nItems + 1); + // height not specified, use the same as before + DoGetSize(NULL, &height); } - else + else if ( height == heightBest ) { - // We cannot pass wxDefaultCoord as height to wxControl. wxControl uses - // wxGetWindowRect() to determine the current height of the combobox, - // and then again sets the combobox's height to that value. Unfortunately, - // wxGetWindowRect doesn't include the dropdown list's height (at least - // on Win2K), so this would result in a combobox with dropdown height of - // 1 pixel. We have to determine the default height ourselves and call - // wxControl with that value instead. - int w, h; - RECT r; - DoGetSize(&w, &h); - if (::SendMessage(GetHwnd(), CB_GETDROPPEDCONTROLRECT, 0, (LPARAM) &r) != 0) - { - height = h + r.bottom - r.top; - } + // we don't need to manually manage our height, let the system use the + // default one + m_heightOwn = wxDefaultCoord; + } + else // non-default height specified + { + // set our new own height but be careful not to make it too big: the + // native control apparently stores it as a single byte and so setting + // own height to 256 pixels results in default height being used (255 + // is still ok) + m_heightOwn = height; + + if ( m_heightOwn > UCHAR_MAX ) + m_heightOwn = UCHAR_MAX; + // nor too small: see MSWUpdateVisibleHeight() + else if ( m_heightOwn < COMBO_HEIGHT_ADJ ) + m_heightOwn = COMBO_HEIGHT_ADJ; } - wxControl::DoSetSize(x, y, width, height, sizeFlags); - - // If we're storing a pending size, make sure we store - // the original size for reporting back to the app. - if (m_pendingSize != wxDefaultSize) - m_pendingSize = wxSize(width, heightOrig); - - // This solution works on XP, but causes choice/combobox lists to be - // too short on W2K and earlier. -#if 0 - int widthCurrent, heightCurrent; - DoGetSize(&widthCurrent, &heightCurrent); // the height which we must pass to Windows should be the total height of // the control including the drop down list while the height given to us - // is, of course, just the height of the permanently visible part of it - if ( height != wxDefaultCoord && height != heightCurrent ) + // is, of course, just the height of the permanently visible part of it so + // add the drop down height to it + + // don't make the drop down list too tall, arbitrarily limit it to 30 + // items max and also don't make it too small if it's currently empty + size_t nItems = GetCount(); + if (!HasFlag(wxCB_SIMPLE)) { - // don't make the drop down list too tall, arbitrarily limit it to 40 - // items max and also don't leave it empty - unsigned int nItems = GetCount(); if ( !nItems ) nItems = 9; - else if ( nItems > 24 ) - nItems = 24; + else if ( nItems > 30 ) + nItems = 30; + } + + const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0); + int heightWithItems = 0; + if (!HasFlag(wxCB_SIMPLE)) + heightWithItems = height + hItem*nItems; + else + heightWithItems = SetHeightSimpleComboBox(nItems); - // add space for the drop down list - const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0); - height += hItem*(nItems + 1); + + // do resize the native control + wxControl::DoSetSize(x, y, width, heightWithItems, sizeFlags); + + + // 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 + // will be done from MSWEndDeferWindowPos() +#if wxUSE_DEFERRED_SIZING + if ( m_pendingSize == wxDefaultSize ) + { + // not using deferred sizing, update it immediately + MSWUpdateVisibleHeight(); } - else // keep the same height as now + else // in the middle of deferred sizing { - // normally wxWindow::DoSetSize() checks if we set the same size as the - // window already has and does nothing in this case, but for us the - // check fails as the size we pass to it includes the dropdown while - // the size returned by our GetSize() does not, so test if the size - // didn't really change ourselves here - if ( width == wxDefaultCoord || width == widthCurrent ) - { - // size doesn't change, what about position? - int xCurrent, yCurrent; - DoGetPosition(&xCurrent, &yCurrent); - const bool defMeansUnchanged = !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE); - if ( ((x == wxDefaultCoord && defMeansUnchanged) || x == xCurrent) - && - ((y == wxDefaultCoord && defMeansUnchanged) || y == yCurrent) ) - { - // nothing changes, nothing to do - return; - } - } - - // We cannot pass wxDefaultCoord as height to wxControl. wxControl uses - // wxGetWindowRect() to determine the current height of the combobox, - // and then again sets the combobox's height to that value. Unfortunately, - // wxGetWindowRect doesn't include the dropdown list's height (at least - // on Win2K), so this would result in a combobox with dropdown height of - // 1 pixel. We have to determine the default height ourselves and call - // wxControl with that value instead. - // - // Also notice that sometimes CB_GETDROPPEDCONTROLRECT seems to return - // wildly incorrect values (~32000) which looks like a bug in it, just - // ignore them in this case - RECT r; - if ( ::SendMessage(GetHwnd(), CB_GETDROPPEDCONTROLRECT, 0, (LPARAM) &r) - && r.bottom < 30000 ) - { - height = heightCurrent + r.bottom - r.top; - } + // we need to report the size of the visible part of the control back + // in GetSize() and not height stored by DoSetSize() in m_pendingSize + m_pendingSize = wxSize(width, height); } - - wxControl::DoSetSize(x, y, width, height, sizeFlags); -#endif +#else // !wxUSE_DEFERRED_SIZING + // always update the visible height immediately + MSWUpdateVisibleHeight(); +#endif // wxUSE_DEFERRED_SIZING } 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++ ) { @@ -620,12 +655,30 @@ wxSize wxChoice::DoGetBestSize() const // the combobox should be slightly larger than the widest string wChoice += 5*GetCharWidth(); + if( HasFlag( wxCB_SIMPLE ) ) + { + hChoice = SetHeightSimpleComboBox( nItems ); + } + else + hChoice = EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight()); - wxSize best(wChoice, EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight())); + wxSize best(wChoice, hChoice); CacheBestSize(best); return best; } +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; +} + +// ---------------------------------------------------------------------------- +// MSW message handlers +// ---------------------------------------------------------------------------- + WXLRESULT wxChoice::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { switch ( nMsg ) @@ -703,13 +756,6 @@ bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) // need to reset the selection back to it if it's eventually // cancelled by user m_lastAcceptedSelection = GetCurrentSelection(); - if ( m_lastAcceptedSelection == -1 ) - { - // no current selection so no need to restore it later (this - // happens when opening a combobox containing text not from its - // list of items and we shouldn't erase this text) - m_lastAcceptedSelection = wxID_NONE; - } break; case CBN_CLOSEUP: