]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/combog.cpp
Ensure there is valid context for DrawRectangle
[wxWidgets.git] / src / generic / combog.cpp
index 3b62ca9bc5b85ec703b39a94f4b6710a044b9800..615106085c867a398baeebc34a36d0c861e94030 100644 (file)
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        combog.cpp
-// Purpose:     Generic wxComboControl
+// Name:        src/generic/combog.cpp
+// Purpose:     Generic wxComboCtrl
 // Author:      Jaakko Salli
 // Modified by:
 // Created:     Apr-30-2006
     #pragma hdrstop
 #endif
 
-#if wxUSE_COMBOCONTROL
+#if wxUSE_COMBOCTRL
+
+#include "wx/combo.h"
 
 #ifndef WX_PRECOMP
     #include "wx/log.h"
     #include "wx/combobox.h"
     #include "wx/dcclient.h"
     #include "wx/settings.h"
+    #include "wx/textctrl.h"
 #endif
 
 #include "wx/dcbuffer.h"
 
-#include "wx/combo.h"
-
-
 // ----------------------------------------------------------------------------
 // Some constant adjustments to make the generic more bearable
 
 #if defined(__WXUNIVERSAL__)
 
-#define TEXTCTRLXADJUST                 0 // position adjustment for wxTextCtrl, with zero indent
-#define TEXTCTRLYADJUST                 0
+// position adjustment for wxTextCtrl, to achieve zero left margin
+// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
+#define TEXTCTRLXADJUST                 0
+
 #define TEXTXADJUST                     0 // how much is read-only text's x adjusted
 #define DEFAULT_DROPBUTTON_WIDTH        19
 
 #elif defined(__WXMSW__)
 
-#define TEXTCTRLXADJUST                 2 // position adjustment for wxTextCtrl, with zero indent
-#define TEXTCTRLYADJUST                 3
+// position adjustment for wxTextCtrl, to achieve zero left margin
+// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
+#define TEXTCTRLXADJUST                 2
+
 #define TEXTXADJUST                     0 // how much is read-only text's x adjusted
 #define DEFAULT_DROPBUTTON_WIDTH        17
 
 #elif defined(__WXGTK__)
 
-#define TEXTCTRLXADJUST                 -1 // position adjustment for wxTextCtrl, with zero indent
-#define TEXTCTRLYADJUST                 0
+// position adjustment for wxTextCtrl, to achieve zero left margin
+// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
+#define TEXTCTRLXADJUST                 -1
+
 #define TEXTXADJUST                     1 // how much is read-only text's x adjusted
 #define DEFAULT_DROPBUTTON_WIDTH        23
 
 #elif defined(__WXMAC__)
 
-#define TEXTCTRLXADJUST                 0 // position adjustment for wxTextCtrl, with zero indent
-#define TEXTCTRLYADJUST                 0
+// position adjustment for wxTextCtrl, to achieve zero left margin
+// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
+#define TEXTCTRLXADJUST                 0
+
 #define TEXTXADJUST                     0 // how much is read-only text's x adjusted
-#define DEFAULT_DROPBUTTON_WIDTH        19
+#define DEFAULT_DROPBUTTON_WIDTH        22
 
 #else
 
-#define TEXTCTRLXADJUST                 0 // position adjustment for wxTextCtrl, with zero indent
-#define TEXTCTRLYADJUST                 0
+// position adjustment for wxTextCtrl, to achieve zero left margin
+// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
+#define TEXTCTRLXADJUST                 0
+
 #define TEXTXADJUST                     0 // how much is read-only text's x adjusted
 #define DEFAULT_DROPBUTTON_WIDTH        19
 
 
 
 // ----------------------------------------------------------------------------
-// wxGenericComboControl
+// wxGenericComboCtrl
 // ----------------------------------------------------------------------------
 
-BEGIN_EVENT_TABLE(wxGenericComboControl, wxComboControlBase)
-    EVT_PAINT(wxGenericComboControl::OnPaintEvent)
-    EVT_MOUSE_EVENTS(wxGenericComboControl::OnMouseEvent)
+BEGIN_EVENT_TABLE(wxGenericComboCtrl, wxComboCtrlBase)
+    EVT_PAINT(wxGenericComboCtrl::OnPaintEvent)
+    EVT_MOUSE_EVENTS(wxGenericComboCtrl::OnMouseEvent)
 END_EVENT_TABLE()
 
 
-IMPLEMENT_DYNAMIC_CLASS(wxGenericComboControl, wxComboControlBase)
+IMPLEMENT_DYNAMIC_CLASS(wxGenericComboCtrl, wxComboCtrlBase)
 
-void wxGenericComboControl::Init()
+void wxGenericComboCtrl::Init()
 {
 }
 
-bool wxGenericComboControl::Create(wxWindow *parent,
-                                   wxWindowID id,
-                                   const wxString& value,
-                                   const wxPoint& pos,
-                                   const wxSize& size,
-                                   long style,
-                                   const wxValidator& validator,
-                                   const wxString& name)
+bool wxGenericComboCtrl::Create(wxWindow *parent,
+                                wxWindowID id,
+                                const wxString& value,
+                                const wxPoint& pos,
+                                const wxSize& size,
+                                long style,
+                                const wxValidator& validator,
+                                const wxString& name)
 {
-
-    // Set border
+    //
+    // Note that technically we only support 'default' border and wxNO_BORDER.
     long border = style & wxBORDER_MASK;
+    int tcBorder = wxNO_BORDER;
 
-    if ( !border )
-    {
 #if defined(__WXUNIVERSAL__)
+    if ( !border )
         border = wxBORDER_SIMPLE;
 #elif defined(__WXMSW__)
+    if ( !border )
         // For XP, have 1-width custom border, for older version use sunken
-        if ( wxUxThemeEngine::GetIfActive() )
+        /*if ( wxUxThemeEngine::GetIfActive() )
         {
             border = wxBORDER_NONE;
             m_widthCustomBorder = 1;
         }
-        else
+        else*/
             border = wxBORDER_SUNKEN;
-#elif defined(__WXGTK__)
-        border = wxBORDER_NONE;
-        //m_widthCustomBorder = 2;
-        m_widthCustomBorder = 1;
 #else
-        border = wxBORDER_SIMPLE;
-#endif
 
-        style = (style & ~(wxBORDER_MASK)) | border;
+    //
+    // Generic version is optimized for wxGTK
+    //
+
+    #define UNRELIABLE_TEXTCTRL_BORDER
+
+    if ( !border )
+    {
+        if ( style & wxCB_READONLY )
+        {
+            m_widthCustomBorder = 1;
+        }
+        else
+        {
+            m_widthCustomBorder = 0;
+            tcBorder = 0;
+        }
+    }
+    else
+    {
+        // Have textctrl instead use the border given.
+        tcBorder = border;
     }
 
-#if defined(__WXGTK__)
+    // Because we are going to have button outside the border,
+    // let's use wxBORDER_NONE for the whole control.
+    border = wxBORDER_NONE;
+
     Customize( wxCC_BUTTON_OUTSIDE_BORDER |
-               wxCC_NO_TEXT_AUTO_SELECT );
+               wxCC_NO_TEXT_AUTO_SELECT |
+               wxCC_BUTTON_STAYS_DOWN );
+
 #endif
 
+    style = (style & ~(wxBORDER_MASK)) | border;
     if ( style & wxCC_STD_BUTTON )
         m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
 
     // 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;
 
     // Create textctrl, if necessary
-    CreateTextCtrl( wxNO_BORDER, validator );
+    CreateTextCtrl( tcBorder );
 
     // Add keyboard input handlers for main control and textctrl
-    InstallInputHandlers( true );
+    InstallInputHandlers();
 
-    // Set background
-    SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // for double-buffering
+    // Set background style for double-buffering, when needed
+    // (cannot use when system draws background automatically)
+    if ( !HasTransparentBackground() )
+        SetBackgroundStyle( wxBG_STYLE_PAINT );
 
-    // SetSize should be called last
-    SetSize(pos.x,pos.y,size.x,size.y);
+    // SetInitialSize should be called last
+    SetInitialSize(size);
 
     return true;
 }
 
-wxGenericComboControl::~wxGenericComboControl()
+wxGenericComboCtrl::~wxGenericComboCtrl()
 {
 }
 
-void wxGenericComboControl::OnResize()
+void wxGenericComboCtrl::OnResize()
 {
 
     // Recalculates button and textctrl areas
@@ -196,16 +230,24 @@ void wxGenericComboControl::OnResize()
 #endif
 
     // Move textctrl, if any, accordingly
-    PositionTextCtrl( TEXTCTRLXADJUST, TEXTCTRLYADJUST );
+    PositionTextCtrl( TEXTCTRLXADJUST );
 }
 
-void wxGenericComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
+void wxGenericComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
 {
-    wxSize sz = GetClientSize();
-    wxBufferedPaintDC dc(this,GetBufferBitmap(sz));
+    // Determine wxDC to use based on need to double-buffer or
+    // use system-generated transparent background portions
+    wxDC* dcPtr;
+    if ( HasTransparentBackground() )
+        dcPtr = new wxPaintDC(this);
+    else
+        dcPtr = new wxAutoBufferedPaintDC(this);
+    wxDC& dc = *dcPtr;
 
-    const wxRect& rectb = m_btnArea;
-    wxRect rect = m_tcArea;
+    wxSize sz = GetClientSize();
+    const wxRect& butRect = m_btnArea;
+    wxRect tcRect = m_tcArea;
+    wxRect fullRect(0, 0, sz.x, sz.y);
 
     // artificial simple border
     if ( m_widthCustomBorder )
@@ -213,16 +255,22 @@ void wxGenericComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
         int customBorder = m_widthCustomBorder;
 
         // Set border colour
-        wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
+#ifdef __WXMAC__
+        wxPen pen1( wxColour(133,133,133),
                     customBorder,
                     wxSOLID );
+#else
+        wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
+                    customBorder,
+                    wxPENSTYLE_SOLID);
+#endif
         dc.SetPen( pen1 );
 
         // area around both controls
-        wxRect rect2(0,0,sz.x,sz.y);
+        wxRect rect2(fullRect);
         if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
         {
-            rect2 = m_tcArea;
+            rect2 = tcRect;
             if ( customBorder == 1 )
             {
                 rect2.Inflate(1);
@@ -245,48 +293,64 @@ void wxGenericComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
         dc.DrawRectangle(rect2);
     }
 
-    wxColour winCol = GetBackgroundColour();
-    dc.SetBrush(winCol);
-    dc.SetPen(winCol);
-
-    //wxLogDebug(wxT("hei: %i tcy: %i tchei: %i"),GetClientSize().y,m_tcArea.y,m_tcArea.height);
-    //wxLogDebug(wxT("btnx: %i tcx: %i tcwid: %i"),m_btnArea.x,m_tcArea.x,m_tcArea.width);
+    // Clear the main background if the system doesn't do it by itself
+    if ( !HasTransparentBackground() &&
+         (tcRect.x > 0 || tcRect.y > 0) )
+    {
+        wxColour winCol = GetParent()->GetBackgroundColour();
+        dc.SetBrush(winCol);
+        dc.SetPen(winCol);
 
-    // clear main background
-    dc.DrawRectangle(rect);
+        dc.DrawRectangle(fullRect);
+    }
 
     if ( !m_btn )
+    {
         // Standard button rendering
-        DrawButton(dc,rectb,true);
+        DrawButton(dc, butRect);
+    }
 
     // paint required portion on the control
     if ( !m_text || m_widthCustomPaint )
     {
         wxASSERT( m_widthCustomPaint >= 0 );
 
+        // Clear the text-control area background
+        wxColour tcCol = GetBackgroundColour();
+        dc.SetBrush(tcCol);
+        dc.SetPen(tcCol);
+        dc.DrawRectangle(tcRect);
+
         // this is intentionally here to allow drawed rectangle's
         // right edge to be hidden
         if ( m_text )
-            rect.width = m_widthCustomPaint;
+            tcRect.width = m_widthCustomPaint;
 
         dc.SetFont( GetFont() );
 
-        dc.SetClippingRegion(rect);
-        m_popupInterface->PaintComboControl(dc,rect);
+        dc.SetClippingRegion(tcRect);
+        if ( m_popupInterface )
+            m_popupInterface->PaintComboControl(dc, tcRect);
+        else
+            wxComboPopup::DefaultPaintComboControl(this, dc, tcRect);
     }
+
+    delete dcPtr;
 }
 
-void wxGenericComboControl::OnMouseEvent( wxMouseEvent& event )
+void wxGenericComboCtrl::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,handlerFlags) )
         return;
 
-    if ( (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
+    const bool ctrlIsButton = wxPlatformIs(wxOS_WINDOWS);
+
+    if ( ctrlIsButton &&
+         (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
     {
         // if no textctrl and no special double-click, then the entire control acts
         // as a button
@@ -296,8 +360,11 @@ void wxGenericComboControl::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;
         }
@@ -316,16 +383,90 @@ void wxGenericComboControl::OnMouseEvent( wxMouseEvent& event )
 
 }
 
+void wxGenericComboCtrl::SetCustomPaintWidth( int width )
+{
+#ifdef UNRELIABLE_TEXTCTRL_BORDER
+    //
+    // If starting/stopping to show an image in front
+    // of a writable text-field, then re-create textctrl
+    // with different kind of border (because we can't
+    // assume that textctrl fully supports wxNO_BORDER).
+    //
+    wxTextCtrl* tc = GetTextCtrl();
+
+    if ( tc && (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) )
+    {
+        int borderType = tc->GetWindowStyle() & wxBORDER_MASK;
+        int tcCreateStyle = -1;
+
+        if ( width > 0 )
+        {
+            // Re-create textctrl with no border
+            if ( borderType != wxNO_BORDER )
+            {
+                m_widthCustomBorder = 1;
+                tcCreateStyle = wxNO_BORDER;
+            }
+        }
+        else if ( width == 0 )
+        {
+            // Re-create textctrl with normal border
+            if ( borderType == wxNO_BORDER )
+            {
+                m_widthCustomBorder = 0;
+                tcCreateStyle = 0;
+            }
+        }
+
+        // Common textctrl re-creation code
+        if ( tcCreateStyle != -1 )
+        {
+            tc->RemoveEventHandler(m_textEvtHandler);
+            delete m_textEvtHandler;
+
+            CreateTextCtrl( tcCreateStyle );
+
+            InstallInputHandlers();
+        }
+    }
+#endif // UNRELIABLE_TEXTCTRL_BORDER
+
+    wxComboCtrlBase::SetCustomPaintWidth( width );
+}
+
+bool wxGenericComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
+{
+    int keycode = event.GetKeyCode();
+    bool isPopupShown = IsPopupShown();
+
+    // This code is AFAIK appropriate for wxGTK.
+
+    if ( isPopupShown )
+    {
+        if ( keycode == WXK_ESCAPE ||
+             ( keycode == WXK_UP && event.AltDown() ) )
+            return true;
+    }
+    else
+    {
+        if ( (keycode == WXK_DOWN && event.AltDown()) ||
+             (keycode == WXK_F4) )
+            return true;
+    }
+
+    return false;
+}
+
 #ifdef __WXUNIVERSAL__
 
-bool wxGenericComboControl::PerformAction(const wxControlAction& action,
-                                          long numArg,
-                                          const wxString& strArg)
+bool wxGenericComboCtrl::PerformAction(const wxControlAction& action,
+                                       long numArg,
+                                       const wxString& strArg)
 {
     bool processed = false;
     if ( action == wxACTION_COMBOBOX_POPUP )
     {
-        if ( !m_isPopupShown )
+        if ( !IsPopupShown() )
         {
             ShowPopup();
 
@@ -334,7 +475,7 @@ bool wxGenericComboControl::PerformAction(const wxControlAction& action,
     }
     else if ( action == wxACTION_COMBOBOX_DISMISS )
     {
-        if ( m_isPopupShown )
+        if ( IsPopupShown() )
         {
             HidePopup();
 
@@ -353,12 +494,12 @@ bool wxGenericComboControl::PerformAction(const wxControlAction& action,
 
 #endif // __WXUNIVERSAL__
 
-// If native wxComboControl was not defined, then prepare a simple
+// If native wxComboCtrl was not defined, then prepare a simple
 // front-end so that wxRTTI works as expected.
 #ifndef _WX_COMBOCONTROL_H_
-IMPLEMENT_DYNAMIC_CLASS(wxComboControl, wxGenericComboControl)
+IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxGenericComboCtrl)
 #endif
 
 #endif // !wxCOMBOCONTROL_FULLY_FEATURED
 
-#endif // wxUSE_COMBOCONTROL
+#endif // wxUSE_COMBOCTRL