X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6d0ce565ff6a0db2e7c6e3240ef4cf6fe7950a35..739c5499fcab1c165b8dd0591c9fd9e2329b3cf9:/src/msw/combo.cpp?ds=sidebyside diff --git a/src/msw/combo.cpp b/src/msw/combo.cpp index 34c40001a7..2ef7d8925a 100644 --- a/src/msw/combo.cpp +++ b/src/msw/combo.cpp @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: combo.cpp -// Purpose: wxMSW wxComboControl +// Name: src/msw/combo.cpp +// Purpose: wxMSW wxComboCtrl // Author: Jaakko Salli // Modified by: // Created: Apr-30-2006 @@ -23,7 +23,7 @@ #pragma hdrstop #endif -#if wxUSE_COMBOCONTROL +#if wxUSE_COMBOCTRL #ifndef WX_PRECOMP #include "wx/log.h" @@ -31,19 +31,23 @@ #include "wx/dcclient.h" #include "wx/settings.h" #include "wx/dialog.h" + #include "wx/stopwatch.h" #endif #include "wx/dcbuffer.h" - #include "wx/combo.h" - +#include "wx/msw/registry.h" +#if wxUSE_UXTHEME #include "wx/msw/uxtheme.h" +#endif +#include "wx/msw/dc.h" // Change to #if 1 to include tmschema.h for easier testing of theme // parameters. #if 0 #include + #include #else //---------------------------------- #define EP_EDITTEXT 1 @@ -58,17 +62,66 @@ #define TMT_TEXTCOLOR 3803 #define TMT_BORDERCOLOR 3801 #define TMT_EDGEFILLCOLOR 3808 - //---------------------------------- + #define TMT_BGTYPE 4001 + + #define BT_IMAGEFILE 0 + #define BT_BORDERFILL 1 + + #define CP_DROPDOWNBUTTON 1 + #define CP_BACKGROUND 2 // This and above are Vista and later only + #define CP_TRANSPARENTBACKGROUND 3 + #define CP_BORDER 4 + #define CP_READONLY 5 + #define CP_DROPDOWNBUTTONRIGHT 6 + #define CP_DROPDOWNBUTTONLEFT 7 + #define CP_CUEBANNER 8 + + #define CBXS_NORMAL 1 + #define CBXS_HOT 2 + #define CBXS_PRESSED 3 + #define CBXS_DISABLED 4 + + #define CBXSR_NORMAL 1 + #define CBXSR_HOT 2 + #define CBXSR_PRESSED 3 + #define CBXSR_DISABLED 4 + + #define CBXSL_NORMAL 1 + #define CBXSL_HOT 2 + #define CBXSL_PRESSED 3 + #define CBXSL_DISABLED 4 + + #define CBTBS_NORMAL 1 + #define CBTBS_HOT 2 + #define CBTBS_DISABLED 3 + #define CBTBS_FOCUSED 4 + + #define CBB_NORMAL 1 + #define CBB_HOT 2 + #define CBB_FOCUSED 3 + #define CBB_DISABLED 4 + + #define CBRO_NORMAL 1 + #define CBRO_HOT 2 + #define CBRO_PRESSED 3 + #define CBRO_DISABLED 4 + + #define CBCB_NORMAL 1 + #define CBCB_HOT 2 + #define CBCB_PRESSED 3 + #define CBCB_DISABLED 4 + #endif #define NATIVE_TEXT_INDENT_XP 4 #define NATIVE_TEXT_INDENT_CLASSIC 2 -#define TEXTCTRLXADJUST_XP 1 -#define TEXTCTRLYADJUST_XP 3 -#define TEXTCTRLXADJUST_CLASSIC 1 -#define TEXTCTRLYADJUST_CLASSIC 2 +#define COMBOBOX_ANIMATION_RESOLUTION 10 + +#define COMBOBOX_ANIMATION_DURATION 200 // In milliseconds + +#define wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM (1<<2) // ============================================================================ @@ -76,19 +129,22 @@ // ============================================================================ -BEGIN_EVENT_TABLE(wxComboControl, wxComboControlBase) - EVT_PAINT(wxComboControl::OnPaintEvent) - EVT_MOUSE_EVENTS(wxComboControl::OnMouseEvent) +BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase) + EVT_PAINT(wxComboCtrl::OnPaintEvent) + EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent) +#if wxUSE_COMBOCTRL_POPUP_ANIMATION + EVT_TIMER(wxID_ANY, wxComboCtrl::OnTimerEvent) +#endif END_EVENT_TABLE() -IMPLEMENT_DYNAMIC_CLASS(wxComboControl, wxComboControlBase) +IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxComboCtrlBase) -void wxComboControl::Init() +void wxComboCtrl::Init() { } -bool wxComboControl::Create(wxWindow *parent, +bool wxComboCtrl::Create(wxWindow *parent, wxWindowID id, const wxString& value, const wxPoint& pos, @@ -101,101 +157,82 @@ bool wxComboControl::Create(wxWindow *parent, // Set border long border = style & wxBORDER_MASK; +#if wxUSE_UXTHEME wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); +#endif if ( !border ) { - // For XP, have 1-width custom border, for older version use sunken +#if wxUSE_UXTHEME if ( theme ) { + // For XP, have 1-width custom border, for older version use sunken border = wxBORDER_NONE; m_widthCustomBorder = 1; } else +#endif border = wxBORDER_SUNKEN; style = (style & ~(wxBORDER_MASK)) | border; } // create main window - if ( !wxComboControlBase::Create(parent, - id, - value, - wxDefaultPosition, - wxDefaultSize, - style | wxFULL_REPAINT_ON_RESIZE, - wxDefaultValidator, - name) ) + if ( !wxComboCtrlBase::Create(parent, + id, + value, + pos, + size, + style | wxFULL_REPAINT_ON_RESIZE, + validator, + name) ) return false; +#if wxUSE_UXTHEME + if ( theme ) + { + if ( ::wxGetWinVersion() >= wxWinVersion_Vista ) + m_iFlags |= wxCC_BUTTON_STAYS_DOWN |wxCC_BUTTON_COVERS_BORDER; + } +#endif + if ( style & wxCC_STD_BUTTON ) m_iFlags |= wxCC_POPUP_ON_MOUSE_UP; + // Prepare background for double-buffering or better background theme + // support, whichever is possible. + SetDoubleBuffered(true); + if ( !IsDoubleBuffered() ) + SetBackgroundStyle( wxBG_STYLE_PAINT ); + // Create textctrl, if necessary - CreateTextCtrl( wxNO_BORDER, validator ); + CreateTextCtrl( wxNO_BORDER ); // Add keyboard input handlers for main control and textctrl - InstallInputHandlers( true ); - - // Prepare background for double-buffering - SetBackgroundStyle( wxBG_STYLE_CUSTOM ); + InstallInputHandlers(); - // SetSize should be called last - SetSize(pos.x,pos.y,size.x,size.y); + // SetInitialSize should be called last + SetInitialSize(size); return true; } -wxComboControl::~wxComboControl() +wxComboCtrl::~wxComboCtrl() { } -void wxComboControl::OnThemeChange() -{ - wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); - if ( theme ) - { - wxUxThemeHandle hTheme(this, L"COMBOBOX"); - - COLORREF col; - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&col); - SetBackgroundColour(wxRGBToColour(col)); - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&col); - SetForegroundColour(wxRGBToColour(col)); - } - else - { - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - } -} - -void wxComboControl::OnResize() +void wxComboCtrl::OnResize() { // // Recalculates button and textctrl areas - int textCtrlXAdjust; - int textCtrlYAdjust; - - if ( wxUxThemeEngine::GetIfActive() ) - { - textCtrlXAdjust = TEXTCTRLXADJUST_XP; - textCtrlYAdjust = TEXTCTRLYADJUST_XP; - } - else - { - textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC; - textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC; - } - // Technically Classic Windows style combo has more narrow button, // but the native renderer doesn't paint it well like that. int btnWidth = 17; CalculateAreas(btnWidth); // Position textctrl using standard routine - PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust); + PositionTextCtrl(); } // Draws non-XP GUI dotted line around the focus area @@ -218,7 +255,7 @@ static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect ) // it employs wxCAP_BUTT hack to have line of width 1. dc.SetLogicalFunction(wxINVERT); - wxPen pen(*wxBLACK,1,wxDOT); + wxPen pen(*wxBLACK, 1, wxPENSTYLE_DOT); pen.SetCap(wxCAP_BUTT); dc.SetPen(pen); dc.SetBrush(*wxTRANSPARENT_BRUSH); @@ -239,15 +276,16 @@ static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect ) } // draw focus background on area in a way typical on platform -void wxComboControl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags ) +void +wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const { - wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL; +#if wxUSE_UXTHEME wxUxThemeHandle hTheme(this, L"COMBOBOX"); - //COLORREF cref; +#endif wxSize sz = GetClientSize(); bool isEnabled; - bool isFocused; // also selected + bool doDrawFocusRect; // also selected // For smaller size control (and for disabled background) use less spacing int focusSpacingX; @@ -256,9 +294,10 @@ void wxComboControl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flag if ( !(flags & wxCONTROL_ISSUBMENU) ) { // Drawing control - isEnabled = IsEnabled(); - isFocused = ShouldDrawFocus(); + isEnabled = IsThisEnabled(); + doDrawFocusRect = ShouldDrawFocus(); +#if wxUSE_UXTHEME // Windows-style: for smaller size control (and for disabled background) use less spacing if ( hTheme ) { @@ -267,6 +306,7 @@ void wxComboControl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flag focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1; } else +#endif { // Classic Theme if ( isEnabled ) @@ -285,7 +325,7 @@ void wxComboControl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flag { // Drawing a list item isEnabled = true; // they are never disabled - isFocused = flags & wxCONTROL_SELECTED ? true : false; + doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false; focusSpacingX = 0; focusSpacingY = 0; @@ -295,165 +335,284 @@ void wxComboControl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flag wxRect selRect(rect); selRect.y += focusSpacingY; selRect.height -= (focusSpacingY*2); - selRect.x += m_widthCustomPaint + focusSpacingX; - selRect.width -= m_widthCustomPaint + (focusSpacingX*2); - if ( hTheme ) - theme = wxUxThemeEngine::GetIfActive(); + int wcp = 0; + if ( !(flags & wxCONTROL_ISSUBMENU) ) + wcp += m_widthCustomPaint; + + selRect.x += wcp + focusSpacingX; + selRect.width -= wcp + (focusSpacingX*2); + + //wxUxThemeEngine* theme = NULL; + //if ( hTheme ) + // theme = wxUxThemeEngine::GetIfActive(); + + wxColour fgCol; wxColour bgCol; - bool drawDottedEdge = false; + bool doDrawDottedEdge = false; + bool doDrawSelRect = true; + + // TODO: doDrawDottedEdge = true when focus has arrived to control via tab. + // (and other cases which are not that apparent). if ( isEnabled ) { // If popup is hidden and this control is focused, // then draw the focus-indicator (selbgcolor background etc.). - if ( isFocused ) + if ( doDrawFocusRect ) { - #if 0 - // TODO: Proper theme color getting (JMS: I don't know which parts/colors to use, - // those below don't work) - if ( hTheme ) + // NB: We can't really use XP visual styles to get TMT_TEXTCOLOR since + // it is not properly defined for combo boxes. Instead, they expect + // you to use DrawThemeText. + // + // Here is, however, sample code how to get theme colours: + // + // COLORREF cref; + // theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref); + // dc.SetTextForeground( wxRGBToColour(cref) ); + if ( (m_iFlags & wxCC_FULL_BUTTON) && !(flags & wxCONTROL_ISSUBMENU) ) { - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_TEXTCOLOR,&cref); - dc.SetTextForeground( wxRGBToColour(cref) ); - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_FILLCOLOR,&cref); - bgCol = wxRGBToColour(cref); + // Vista style read-only combo + fgCol = GetForegroundColour(); + bgCol = GetBackgroundColour(); + doDrawSelRect = false; + doDrawDottedEdge = true; } else - #endif { - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) ); + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); - if ( m_windowStyle & wxCB_READONLY ) - drawDottedEdge = true; } } else { - /*if ( hTheme ) - { - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref); - dc.SetTextForeground( wxRGBToColour(cref) ); - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&cref); - bgCol = wxRGBToColour(cref); - } - else - {*/ - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) ); - bgCol = GetBackgroundColour(); - //} + fgCol = GetForegroundColour(); + bgCol = GetBackgroundColour(); + doDrawSelRect = false; } } else { - /*if ( hTheme ) - { - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_TEXTCOLOR,&cref); - dc.SetTextForeground( wxRGBToColour(cref) ); - theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_EDGEFILLCOLOR,&cref); - bgCol = wxRGBToColour(cref); - } - else - {*/ - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) ); - bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); - //} + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); + bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); } + dc.SetTextForeground(fgCol); dc.SetBrush(bgCol); - dc.SetPen(bgCol); - dc.DrawRectangle(selRect); - if ( drawDottedEdge ) - wxMSWDrawFocusRect(dc,selRect); + if ( doDrawSelRect ) + { + dc.SetPen(bgCol); + dc.DrawRectangle(selRect); + } + + if ( doDrawDottedEdge ) + wxMSWDrawFocusRect(dc, selRect); + // Don't clip exactly to the selection rectangle so we can draw + // to the non-selected area in front of it. + wxRect clipRect(rect.x,rect.y, + (selRect.x+selRect.width)-rect.x-1,rect.height); + dc.SetClippingRegion(clipRect); } -void wxComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) ) +void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) ) { // TODO: Convert drawing in this function to Windows API Code wxSize sz = GetClientSize(); - wxBufferedPaintDC dc(this,GetBufferBitmap(sz)); + wxDC* dcPtr = wxAutoBufferedPaintDCFactory(this); + wxDC& dc = *dcPtr; - const wxRect& rectb = m_btnArea; - wxRect rect = m_tcArea; - bool isEnabled = IsEnabled(); - wxColour bgCol = GetBackgroundColour(); - wxColour fgCol; + const wxRect& rectButton = m_btnArea; + wxRect rectTextField = m_tcArea; + + // FIXME: Either SetBackgroundColour or GetBackgroundColour + // doesn't work under Vista, so here's a temporary + // workaround. + // In the theme-less rendering code below, this fixes incorrect + // background on read-only comboboxes (they are gray, but should be + // white). + wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + +#if wxUSE_UXTHEME + const bool isEnabled = IsThisEnabled(); + + wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); + HDC hDc = GetHdcOf(*impl); + HWND hWnd = GetHwndOf(this); wxUxThemeEngine* theme = NULL; wxUxThemeHandle hTheme(this, L"COMBOBOX"); - int etsState; - // area around both controls - wxRect rect2(0,0,sz.x,sz.y); + if ( hTheme ) + theme = wxUxThemeEngine::GetIfActive(); +#endif // wxUSE_UXTHEME + + wxRect borderRect(0,0,sz.x,sz.y); + if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE ) { - rect2 = m_tcArea; - rect2.Inflate(1); + borderRect = m_tcArea; + borderRect.Inflate(1); } - // Use theme to draw border on XP + int drawButFlags = 0; + +#if wxUSE_UXTHEME if ( hTheme ) { - theme = wxUxThemeEngine::GetIfActive(); - COLORREF cref; + const bool useVistaComboBox = ::wxGetWinVersion() >= wxWinVersion_Vista; + + RECT rFull; + wxCopyRectToRECT(borderRect, rFull); + + RECT rButton; + wxCopyRectToRECT(rectButton, rButton); + + RECT rBorder; + wxCopyRectToRECT(borderRect, rBorder); + + bool isNonStdButton = (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) || + (m_iFlags & wxCC_IFLAG_HAS_NONSTANDARD_BUTTON); + + // + // Get some states for themed drawing + int butState; - // Select correct border colour if ( !isEnabled ) - etsState = ETS_DISABLED; + { + butState = CBXS_DISABLED; + } + // Vista will display the drop-button as depressed always + // when the popup window is visilbe + else if ( (m_btnState & wxCONTROL_PRESSED) || + (useVistaComboBox && !IsPopupWindowState(Hidden)) ) + { + butState = CBXS_PRESSED; + } + else if ( m_btnState & wxCONTROL_CURRENT ) + { + butState = CBXS_HOT; + } else - etsState = ETS_NORMAL; + { + butState = CBXS_NORMAL; + } + + int comboBoxPart = 0; // For XP, use the 'default' part + RECT* rUseForBg = &rBorder; + + bool drawFullButton = false; + int bgState = butState; + const bool isFocused = (FindFocus() == GetMainWindowOfCompositeControl()) ? true : false; - if ( m_widthCustomBorder ) + if ( useVistaComboBox ) { - theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_BORDERCOLOR,&cref); + // Draw the entire control as a single button? + if ( !isNonStdButton ) + { + if ( HasFlag(wxCB_READONLY) ) + drawFullButton = true; + } - // Set border colour - dc.SetPen( wxRGBToColour(cref) ); + if ( drawFullButton ) + { + comboBoxPart = CP_READONLY; + rUseForBg = &rFull; - dc.SetBrush( *wxTRANSPARENT_BRUSH ); - dc.DrawRectangle(rect2); + // It should be safe enough to update this flag here. + m_iFlags |= wxCC_FULL_BUTTON; + } + else + { + comboBoxPart = CP_BORDER; + m_iFlags &= ~wxCC_FULL_BUTTON; + + if ( isFocused ) + bgState = CBB_FOCUSED; + else + bgState = CBB_NORMAL; + } } - theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_TEXTCOLOR,&cref); - fgCol = wxRGBToColour(cref); - } - else - { - // draw regular background - fgCol = GetForegroundColour(); - } + // + // Draw parent's background, if necessary + RECT* rUseForTb = NULL; - rect2.Deflate(m_widthCustomBorder); + if ( theme->IsThemeBackgroundPartiallyTransparent( hTheme, comboBoxPart, bgState ) ) + rUseForTb = &rFull; + else if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE ) + rUseForTb = &rButton; - dc.SetBrush(bgCol); - dc.SetPen(bgCol); + if ( rUseForTb ) + theme->DrawThemeParentBackground( hWnd, hDc, rUseForTb ); - // clear main background - dc.DrawRectangle(rect); + // + // Draw the control background (including the border) + if ( m_widthCustomBorder > 0 ) + { + theme->DrawThemeBackground( hTheme, hDc, comboBoxPart, bgState, rUseForBg, NULL ); + } + else + { + // No border. We can't use theme, since it cannot be relied on + // to deliver borderless drawing, even with DrawThemeBackgroundEx. + dc.SetBrush(bgCol); + dc.SetPen(bgCol); + dc.DrawRectangle(borderRect); + } - // Button background with theme? - bool drawButBg = true; - if ( hTheme && m_blankButtonBg ) - { - RECT r; - wxCopyRectToRECT(rectb, r); + // + // Draw the drop-button + if ( !isNonStdButton ) + { + drawButFlags = Button_BitmapOnly; - // Draw parent background if needed (since button looks like its out of - // the combo, this is preferred). - theme->DrawThemeParentBackground(GetHwndOf(this), - GetHdcOf(dc), - &r); + int butPart = CP_DROPDOWNBUTTON; - drawButBg = false; - } + if ( useVistaComboBox ) + { + if ( drawFullButton ) + { + // We need to alter the button style slightly before + // drawing the actual button (but it was good above + // when background etc was done). + if ( butState == CBXS_HOT || butState == CBXS_PRESSED ) + butState = CBXS_NORMAL; + } + + if ( m_btnSide == wxRIGHT ) + butPart = CP_DROPDOWNBUTTONRIGHT; + else + butPart = CP_DROPDOWNBUTTONLEFT; - // Standard button rendering - DrawButton(dc,rectb,drawButBg); + } + theme->DrawThemeBackground( hTheme, hDc, butPart, butState, &rButton, NULL ); + } + else if ( useVistaComboBox && + (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) ) + { + // We'll do this, because DrawThemeParentBackground + // doesn't seem to be reliable on Vista. + drawButFlags |= Button_PaintBackground; + } + } + else +#endif + { + // Windows 2000 and earlier + drawButFlags = Button_PaintBackground; - // paint required portion on the control + dc.SetBrush(bgCol); + dc.SetPen(bgCol); + dc.DrawRectangle(borderRect); + } + + // Button rendering (may only do the bitmap on button, depending on the flags) + DrawButton( dc, rectButton, drawButFlags ); + + // Paint required portion of the custom image on the control if ( (!m_text || m_widthCustomPaint) ) { wxASSERT( m_widthCustomPaint >= 0 ); @@ -461,25 +620,26 @@ void wxComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) ) // this is intentionally here to allow drawed rectangle's // right edge to be hidden if ( m_text ) - rect.width = m_widthCustomPaint; + rectTextField.width = m_widthCustomPaint; dc.SetFont( GetFont() ); - dc.SetClippingRegion(rect); + dc.SetClippingRegion(rectTextField); if ( m_popupInterface ) - m_popupInterface->PaintComboControl(dc,rect); + m_popupInterface->PaintComboControl(dc,rectTextField); else - wxComboPopup::DefaultPaintComboControl(this,dc,rect); + wxComboPopup::DefaultPaintComboControl(this,dc,rectTextField); } + + delete dcPtr; } -void wxComboControl::OnMouseEvent( wxMouseEvent& event ) +void wxComboCtrl::OnMouseEvent( wxMouseEvent& event ) { - bool isOnButtonArea = m_btnArea.Inside(event.m_x,event.m_y); + int mx = event.m_x; + bool isOnButtonArea = m_btnArea.Contains(mx,event.m_y); int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0; - // Preprocessing fabricates double-clicks and prevents - // (it may also do other common things in future) if ( PreprocessMouseEvent(event,isOnButtonArea) ) return; @@ -493,8 +653,11 @@ void wxComboControl::OnMouseEvent( wxMouseEvent& event ) } else { - if ( isOnButtonArea || HasCapture() ) + if ( isOnButtonArea || HasCapture() || + (m_widthCustomPaint && mx < (m_tcArea.x+m_widthCustomPaint)) ) { + handlerFlags |= wxCC_MF_ON_CLICK_AREA; + if ( HandleButtonMouseEvent(event,handlerFlags) ) return; } @@ -513,12 +676,169 @@ void wxComboControl::OnMouseEvent( wxMouseEvent& event ) } -wxCoord wxComboControl::GetNativeTextIndent() const +#if wxUSE_COMBOCTRL_POPUP_ANIMATION +static wxUint32 GetUserPreferencesMask() +{ + static wxUint32 userPreferencesMask = 0; + static bool valueSet = false; + + if ( valueSet ) + return userPreferencesMask; + + wxRegKey* pKey = NULL; + wxRegKey key1(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Control Panel")); + wxRegKey key2(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Windows\\Control Panel")); + wxRegKey key3(wxRegKey::HKCU, wxT("Control Panel\\Desktop")); + + if ( key1.Exists() ) + pKey = &key1; + else if ( key2.Exists() ) + pKey = &key2; + else if ( key3.Exists() ) + pKey = &key3; + + if ( pKey && pKey->Open(wxRegKey::Read) ) + { + wxMemoryBuffer buf; + if ( pKey->HasValue(wxT("UserPreferencesMask")) && + pKey->QueryValue(wxT("UserPreferencesMask"), buf) ) + { + if ( buf.GetDataLen() >= 4 ) + { + wxUint32* p = (wxUint32*) buf.GetData(); + userPreferencesMask = *p; + } + } + } + + valueSet = true; + + return userPreferencesMask; +} +#endif + +#if wxUSE_COMBOCTRL_POPUP_ANIMATION +void wxComboCtrl::DoTimerEvent() { + bool stopTimer = false; + + wxWindow* win = GetPopupWindow(); + wxWindow* popup = GetPopupControl()->GetControl(); + + // Popup was hidden before it was fully shown? + if ( IsPopupWindowState(Hidden) ) + { + stopTimer = true; + } + else + { + wxLongLong t = ::wxGetLocalTimeMillis(); + const wxRect& rect = m_animRect; + + int pos = (int) (t-m_animStart).GetLo(); + if ( pos < COMBOBOX_ANIMATION_DURATION ) + { + int height = rect.height; + //int h0 = rect.height; + int h = (((pos*256)/COMBOBOX_ANIMATION_DURATION)*height)/256; + int y = (height - h); + if ( y < 0 ) + y = 0; + + if ( m_animFlags & ShowAbove ) + { + win->SetSize( rect.x, rect.y + height - h, rect.width, h ); + } + else + { + // Note that apparently Move() should be called after + // SetSize() to reduce (or even eliminate) animation garbage + win->SetSize( rect.x, rect.y, rect.width, h ); + popup->Move( 0, -y ); + } + } + else + { + stopTimer = true; + } + } + + if ( stopTimer ) + { + m_animTimer.Stop(); + DoShowPopup( m_animRect, m_animFlags ); + popup->Move( 0, 0 ); + + // Do a one final refresh to clean up the rare cases of animation + // garbage + win->Refresh(); + } +} +#endif + +#if wxUSE_COMBOCTRL_POPUP_ANIMATION +bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags ) +{ + if ( GetUserPreferencesMask() & wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM ) + { + m_animStart = ::wxGetLocalTimeMillis(); + m_animRect = rect; + m_animFlags = flags; + + wxWindow* win = GetPopupWindow(); + win->SetSize( rect.x, rect.y, rect.width, 0 ); + win->Show(); + + m_animTimer.SetOwner( this, wxID_ANY ); + m_animTimer.Start( COMBOBOX_ANIMATION_RESOLUTION, wxTIMER_CONTINUOUS ); + + DoTimerEvent(); + + return false; + } + + return true; +} +#endif + +wxCoord wxComboCtrl::GetNativeTextIndent() const +{ +#if wxUSE_UXTHEME if ( wxUxThemeEngine::GetIfActive() ) return NATIVE_TEXT_INDENT_XP; +#endif return NATIVE_TEXT_INDENT_CLASSIC; } +bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const +{ + const bool isPopupShown = IsPopupShown(); + + switch ( event.GetKeyCode() ) + { + case WXK_F4: + // F4 toggles the popup in the native comboboxes, so emulate them + if ( !event.AltDown() ) + return true; + break; + + case WXK_ESCAPE: + if ( isPopupShown ) + return true; + break; + + case WXK_DOWN: + case WXK_UP: + case WXK_NUMPAD_DOWN: + case WXK_NUMPAD_UP: + // Arrow keys (and mouse wheel) toggle the popup in the native + // combo boxes + if ( event.AltDown() ) + return true; + break; + } + + return false; +} -#endif // wxUSE_COMBOCONTROL +#endif // wxUSE_COMBOCTRL