]> git.saurik.com Git - wxWidgets.git/blobdiff - src/os2/spinctrl.cpp
Fix for crash when opening empty node
[wxWidgets.git] / src / os2 / spinctrl.cpp
index 2739992b99699293cb9887bd93f3e3fe036f64f1..2aeef5f3d78cd26f6a432c0b9483500f10115c66 100644 (file)
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        msw/spinctrl.cpp
-// Purpose:     wxSpinCtrl class implementation for Win32
+// Name:        src/os2/spinctrl.cpp
+// Purpose:     wxSpinCtrl class implementation for OS/2
 // Author:      David Webster
 // Modified by:
 // Created:     10/15/99
@@ -13,7 +13,6 @@
 // declarations
 // ============================================================================
 
-
 // ----------------------------------------------------------------------------
 // headers
 // ----------------------------------------------------------------------------
@@ -26,6 +25,8 @@
     #include "wx/wx.h"
 #endif
 
+#if wxUSE_SPINCTRL
+
 #include "wx/spinctrl.h"
 #include "wx/os2/private.h"
 
 // macros
 // ----------------------------------------------------------------------------
 
-#if !USE_SHARED_LIBRARY
-    IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl, wxControl)
-#endif
+extern void  wxAssociateWinWithHandle( HWND         hWnd
+                                      ,wxWindowOS2* pWin
+                                     );
+static WXFARPROC fnWndProcSpinCtrl = (WXFARPROC)NULL;
+wxArraySpins                        wxSpinCtrl::m_svAllSpins;
 
+IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl, wxControl)
+
+BEGIN_EVENT_TABLE(wxSpinCtrl, wxSpinButton)
+    EVT_CHAR(wxSpinCtrl::OnChar)
+    EVT_SPIN(wxID_ANY, wxSpinCtrl::OnSpinChange)
+    EVT_SET_FOCUS(wxSpinCtrl::OnSetFocus)
+END_EVENT_TABLE()
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
@@ -47,112 +57,434 @@ static const int MARGIN_BETWEEN = 5;
 // ============================================================================
 // implementation
 // ============================================================================
+MRESULT EXPENTRY wxSpinCtrlWndProc(
+  HWND                              hWnd
+, UINT                              uMessage
+, MPARAM                            wParam
+, MPARAM                            lParam
+)
+{
+    wxSpinCtrl*                    pSpin = (wxSpinCtrl *)::WinQueryWindowULong( hWnd
+                                                                               ,QWL_USER
+                                                                              );
+
+    //
+    // Forward some messages (the key ones only so far) to the spin ctrl
+    //
+    switch (uMessage )
+    {
+        case WM_CHAR:
+            pSpin->OS2WindowProc( uMessage
+                                 ,wParam
+                                 ,lParam
+                                );
+
+            //
+            // The control may have been deleted at this point, so check.
+            //
+            if (!(::WinIsWindow(vHabmain, hWnd) && ((wxSpinCtrl *)::WinQueryWindowULong( hWnd
+                                                                                        ,QWL_USER
+                                                                                       )
+                                                   ) == pSpin))
+                return 0;
+            break;
+
+    }
+    return (fnWndProcSpinCtrl( hWnd
+                              ,(ULONG)uMessage
+                              ,(MPARAM)wParam
+                              ,(MPARAM)lParam
+                             )
+           );
+} // end of wxSpinCtrlWndProc
+
+wxSpinCtrl::~wxSpinCtrl()
+{
+    m_svAllSpins.Remove(this);
+
+    // This removes spurious memory leak reporting
+    if (m_svAllSpins.GetCount() == 0)
+        m_svAllSpins.Clear();
+} // end of wxSpinCtrl::~wxSpinCtrl
 
 // ----------------------------------------------------------------------------
 // construction
 // ----------------------------------------------------------------------------
 
-bool wxSpinCtrl::Create(wxWindow *parent,
-                        wxWindowID id,
-                        const wxPoint& pos,
-                        const wxSize& size,
-                        long style,
-                        int min, int max, int initial,
-                        const wxString& name)
-{
-    // TODO:
-/*
-    // before using DoGetBestSize(), have to set style to let the base class
-    // know whether this is a horizontal or vertical control (we're always
-    // vertical)
-    SetWindowStyle(style | wxSP_VERTICAL);
-
-    // calculate the sizes: the size given is the toal size for both controls
-    // and we need to fit them both in the given width (height is the same)
-    wxSize sizeText(size), sizeBtn(size);
-    sizeBtn.x = wxSpinButton::DoGetBestSize().x;
-    sizeText.x -= sizeBtn.x + MARGIN_BETWEEN;
-    if ( sizeText.x <= 0 )
+bool wxSpinCtrl::Create( wxWindow*       pParent,
+                         wxWindowID      vId,
+                         const wxString& WXUNUSED(rsValue),
+                         const wxPoint&  rPos,
+                         const wxSize&   rSize,
+                         long            lStyle,
+                         int             nMin,
+                         int             nMax,
+                         int             nInitial,
+                         const wxString& rsName )
+{
+    if (vId == wxID_ANY)
+        m_windowId = NewControlId();
+    else
+        m_windowId = vId;
+    m_backgroundColour = pParent->GetBackgroundColour();
+    m_foregroundColour = pParent->GetForegroundColour();
+    SetName(rsName);
+    SetParent(pParent);
+    m_windowStyle      = lStyle;
+
+    int lSstyle = 0L;
+
+    lSstyle = WS_VISIBLE      |
+              WS_TABSTOP      |
+              SPBS_MASTER     | // We use only single field spin buttons
+              SPBS_NUMERICONLY; // We default to numeric data
+
+    if (m_windowStyle & wxCLIP_SIBLINGS )
+        lSstyle |= WS_CLIPSIBLINGS;
+
+    SPBCDATA                        vCtrlData;
+
+    vCtrlData.cbSize = sizeof(SPBCDATA);
+    vCtrlData.ulTextLimit = 10L;
+    vCtrlData.lLowerLimit = 0L;
+    vCtrlData.lUpperLimit = 100L;
+    vCtrlData.idMasterSpb = vId;
+    vCtrlData.pHWXCtlData = NULL;
+
+    m_hWnd = (WXHWND)::WinCreateWindow( GetWinHwnd(pParent)
+                                       ,WC_SPINBUTTON
+                                       ,(PSZ)NULL
+                                       ,lSstyle
+                                       ,0L, 0L, 0L, 0L
+                                       ,GetWinHwnd(pParent)
+                                       ,HWND_TOP
+                                       ,(HMENU)vId
+                                       ,(PVOID)&vCtrlData
+                                       ,NULL
+                                      );
+    if (m_hWnd == 0)
     {
-        wxLogDebug(_T("not enough space for wxSpinCtrl!"));
+        return false;
     }
+    m_hWndBuddy = m_hWnd; // One in the same for OS/2
+    if(pParent)
+        pParent->AddChild((wxSpinButton *)this);
 
-    wxPoint posBtn(pos);
-    posBtn.x += sizeText.x + MARGIN_BETWEEN;
+    SetFont(*wxSMALL_FONT);
+    SetXComp(0);
+    SetYComp(0);
+    SetSize( rPos.x, rPos.y, rSize.x, rSize.y );
 
-    // create the spin button
-    if ( !wxSpinButton::Create(parent, id, posBtn, sizeBtn, style, name) )
+    SetRange(nMin, nMax);
+    SetValue(nInitial);
+
+    //
+    // For OS/2 we'll just set our handle into our long data
+    //
+    wxAssociateWinWithHandle( m_hWnd
+                             ,(wxWindowOS2*)this
+                            );
+    ::WinSetWindowULong(GetHwnd(), QWL_USER, (LONG)this);
+    fnWndProcSpinCtrl = (WXFARPROC)::WinSubclassWindow(m_hWnd, (PFNWP)wxSpinCtrlWndProc);
+    m_svAllSpins.Add(this);
+    return true;
+} // end of wxSpinCtrl::Create
+
+wxSize wxSpinCtrl::DoGetBestSize() const
+{
+    wxSize                          vSizeBtn = wxSpinButton::DoGetBestSize();
+    int                             nHeight;
+    wxFont                          vFont = (wxFont)GetFont();
+
+    vSizeBtn.x += DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN;
+
+    wxGetCharSize( GetHWND()
+                  ,NULL
+                  ,&nHeight
+                  ,&vFont
+                 );
+    nHeight = EDIT_HEIGHT_FROM_CHAR_HEIGHT(nHeight)+4;
+
+    if (vSizeBtn.y < nHeight)
     {
-        return FALSE;
+        //
+        // Make the text tall enough
+        //
+        vSizeBtn.y = nHeight;
     }
+    return vSizeBtn;
+} // end of wxSpinCtrl::DoGetBestSize
+
+void wxSpinCtrl::DoGetPosition(
+  int*                              pnX
+, int*                              pnY
+) const
+{
+    wxSpinButton::DoGetPosition( pnX,pnY );
+} // end of wxpinCtrl::DoGetPosition
+
+void wxSpinCtrl::DoGetSize(
+  int*                              pnWidth
+, int*                              pnHeight
+) const
+{
+    RECTL                           vSpinrect;
 
-    SetRange(min, max);
-    SetValue(initial);
+    ::WinQueryWindowRect(GetHwnd(), &vSpinrect);
 
-    // create the text window
-    if ( sizeText.y <= 0 )
+    if (pnWidth)
+        *pnWidth = vSpinrect.xRight - vSpinrect.xLeft;
+    if (pnHeight)
+        *pnHeight = vSpinrect.yTop - vSpinrect.yBottom;
+} // end of wxSpinCtrl::DoGetSize
+
+void wxSpinCtrl::DoMoveWindow(
+  int                               nX
+, int                               nY
+, int                               nWidth
+, int                               nHeight
+)
+{
+    wxWindowOS2*                    pParent = (wxWindowOS2*)GetParent();
+
+    if (pParent)
     {
-        // make it the same height as the button then
-        int x, y;
-        wxSpinButton::DoGetSize(&x, &y);
+        int                         nOS2Height = GetOS2ParentHeight(pParent);
 
-        sizeText.y = y;
+        nY = nOS2Height - (nY + nHeight);
     }
+    else
+    {
+        RECTL                       vRect;
 
-    m_hwndBuddy = (WXHWND)::CreateWindowEx
-                    (
-                     WS_EX_CLIENTEDGE,                  // sunken border
-                     _T("EDIT"),                        // window class
-                     NULL,                              // no window title
-                     WS_CHILD | WS_VISIBLE | WS_BORDER, // style
-                     pos.x, pos.y,                      // position
-                     sizeText.x, sizeText.y,            // size
-                     GetHwndOf(parent),                 // parent
-                     (HMENU)-1,                         // control id
-                     wxGetInstance(),                   // app instance
-                     NULL                               // unused client data
-                    );
-
-    if ( !m_hwndBuddy )
+        ::WinQueryWindowRect(HWND_DESKTOP, &vRect);
+        nY = vRect.yTop - (nY + nHeight);
+    }
+    ::WinSetWindowPos( GetHwnd()
+                      ,HWND_TOP
+                      ,nX
+                      ,nY
+                      ,nWidth
+                      ,nHeight
+                      ,SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW
+                     );
+} // end of wxSpinCtrl::DoMoveWindow
+
+bool wxSpinCtrl::Enable(
+  bool                              bEnable
+)
+{
+    if (!wxControl::Enable(bEnable))
     {
-        wxLogLastError("CreateWindow(buddy text window)");
+        return false;
+    }
+    ::WinEnableWindow(GetHwnd(), bEnable);
+    return true;
+} // end of wxSpinCtrl::Enable
 
-        return FALSE;
+wxSpinCtrl* wxSpinCtrl::GetSpinForTextCtrl(
+  WXHWND                            hWndBuddy
+)
+{
+    wxSpinCtrl*                     pSpin = (wxSpinCtrl *)::WinQueryWindowULong( (HWND)hWndBuddy
+                                                                                ,QWL_USER
+                                                                               );
+    int                             i = m_svAllSpins.Index(pSpin);
+
+    if (i == wxNOT_FOUND)
+        return NULL;
+
+    // sanity check
+    wxASSERT_MSG( pSpin->m_hWndBuddy == hWndBuddy,
+                  _T("wxSpinCtrl has incorrect buddy HWND!") );
+
+    return pSpin;
+} // end of wxSpinCtrl::GetSpinForTextCtrl
+
+int wxSpinCtrl::GetValue() const
+{
+    long                            lVal = 0L;
+    char                            zVal[10];
+
+    ::WinSendMsg( GetHwnd()
+                 ,SPBM_QUERYVALUE
+                 ,MPFROMP(zVal)
+                 ,MPFROM2SHORT( (USHORT)10
+                               ,SPBQ_UPDATEIFVALID
+                              )
+                );
+    lVal = atol(zVal);
+    return (int)lVal;
+} // end of wxSpinCtrl::GetValue
+
+void wxSpinCtrl::OnChar (
+  wxKeyEvent&                       rEvent
+)
+{
+    switch (rEvent.GetKeyCode())
+    {
+        case WXK_RETURN:
+            {
+                wxCommandEvent              vEvent( wxEVT_COMMAND_TEXT_ENTER
+                                                   ,m_windowId
+                                                  );
+                wxString                    sVal = wxGetWindowText(m_hWndBuddy);
+
+                InitCommandEvent(vEvent);
+                vEvent.SetString(sVal);
+                vEvent.SetInt(GetValue());
+                if (GetEventHandler()->ProcessEvent(vEvent))
+                    return;
+                break;
+            }
+
+        case WXK_TAB:
+            //
+            // Always produce navigation event - even if we process TAB
+            // ourselves the fact that we got here means that the user code
+            // decided to skip processing of this TAB - probably to let it
+            // do its default job.
+            //
+            {
+                wxNavigationKeyEvent        vEventNav;
+
+                vEventNav.SetDirection(!rEvent.ShiftDown());
+                vEventNav.SetWindowChange(rEvent.ControlDown());
+                vEventNav.SetEventObject(this);
+                if (GetParent()->GetEventHandler()->ProcessEvent(vEventNav))
+                    return;
+            }
+            break;
     }
 
-    // should have the same font as the other controls
-    WXHANDLE hFont = GetParent()->GetFont().GetResourceHandle();
-    ::SendMessage((HWND)m_hwndBuddy, WM_SETFONT, (WPARAM)hFont, TRUE);
+    //
+    // No, we didn't process it
+    //
+    rEvent.Skip();
+} // end of wxSpinCtrl::OnChar
 
-    // associate the text window with the spin button
-    (void)SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)m_hwndBuddy, 0);
-*/
-    return FALSE;
-}
+void wxSpinCtrl::OnSpinChange(
+  wxSpinEvent&                      rEventSpin
+)
+{
+    wxCommandEvent                  vEvent( wxEVT_COMMAND_SPINCTRL_UPDATED
+                                           ,GetId()
+                                          );
 
-// ----------------------------------------------------------------------------
-// size calculations
-// ----------------------------------------------------------------------------
+    vEvent.SetEventObject(this);
+    vEvent.SetInt(rEventSpin.GetPosition());
+    (void)GetEventHandler()->ProcessEvent(vEvent);
+    if (rEventSpin.GetSkipped())
+    {
+        vEvent.Skip();
+    }
+} // end of wxSpinCtrl::OnSpinChange
+
+void wxSpinCtrl::OnSetFocus (
+  wxFocusEvent&                     rEvent
+)
+{
+    //
+    // When we get focus, give it to our buddy window as it needs it more than
+    // we do
+    //
+    ::WinSetFocus(HWND_DESKTOP, (HWND)m_hWndBuddy);
+    rEvent.Skip();
+} // end of wxSpinCtrl::OnSetFocus
 
-void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
+bool wxSpinCtrl::ProcessTextCommand( WXWORD wCmd,
+                                     WXWORD WXUNUSED(wId) )
 {
-    int widthBtn = DoGetBestSize().x;
-    int widthText = width - widthBtn - MARGIN_BETWEEN;
-    if ( widthText <= 0 )
+    switch (wCmd)
     {
-        wxLogDebug(_T("not enough space for wxSpinCtrl!"));
+        case SPBN_CHANGE:
+        {
+            wxCommandEvent vEvent( wxEVT_COMMAND_TEXT_UPDATED, GetId() );
+            vEvent.SetEventObject(this);
+
+            wxString sVal = wxGetWindowText(m_hWndBuddy);
+
+            vEvent.SetString(sVal);
+            vEvent.SetInt(GetValue());
+            return (GetEventHandler()->ProcessEvent(vEvent));
+        }
+
+        case SPBN_SETFOCUS:
+        case SPBN_KILLFOCUS:
+        {
+            wxFocusEvent vEvent( wCmd == EN_KILLFOCUS ? wxEVT_KILL_FOCUS : wxEVT_SET_FOCUS
+                                ,m_windowId
+                               );
+
+            vEvent.SetEventObject(this);
+            return(GetEventHandler()->ProcessEvent(vEvent));
+        }
+        default:
+            break;
     }
-// TODO:
-/*
-    if ( !::MoveWindow((HWND)m_hwndBuddy, x, y, widthText, height, TRUE) )
+
+    //
+    // Not processed
+    //
+    return false;
+} // end of wxSpinCtrl::ProcessTextCommand
+
+void wxSpinCtrl::SetFocus()
+{
+    ::WinSetFocus(HWND_DESKTOP, GetHwnd());
+} // end of wxSpinCtrl::SetFocus
+
+bool wxSpinCtrl::SetFont(
+  const wxFont&                     rFont
+)
+{
+    if (!wxWindowBase::SetFont(rFont))
     {
-        wxLogLastError("MoveWindow(buddy)");
+        // nothing to do
+        return false;
     }
 
-    x += widthText + MARGIN_BETWEEN;
-    if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) )
+    wxOS2SetFont( m_hWnd
+                 ,rFont
+                );
+    return true;
+} // end of wxSpinCtrl::SetFont
+
+void wxSpinCtrl::SetValue(
+  const wxString&                   rsText
+)
+{
+    long                            lVal;
+
+    lVal = atol(rsText.c_str());
+    wxSpinButton::SetValue(lVal);
+} // end of wxSpinCtrl::SetValue
+
+bool wxSpinCtrl::Show(
+  bool                              bShow
+)
+{
+    if (!wxControl::Show(bShow))
     {
-        wxLogLastError("MoveWindow");
+        return false;
     }
-*/
-}
+    return true;
+} // end of wxSpinCtrl::Show
+
+void wxSpinCtrl::SetSelection (
+  long                              lFrom
+, long                              lTo
+)
+{
+    //
+    // If from and to are both -1, it means (in wxWidgets) that all text should
+    // be selected - translate into Windows convention
+    //
+    if ((lFrom == -1) && (lTo == -1))
+    {
+        lFrom = 0;
+    }
+    ::WinSendMsg(m_hWnd, EM_SETSEL, MPFROM2SHORT((USHORT)lFrom, (USHORT)lTo), (MPARAM)0);
+} // end of wxSpinCtrl::SetSelection
+
+#endif //wxUSE_SPINCTRL