// ============================================================================
+#ifdef __GNUG__
+ #pragma implementation "spinctrlbase.h"
+ #pragma implementation "spinctrl.h"
+#endif
+
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#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
// ----------------------------------------------------------------------------
// ============================================================================
// 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& rsValue
+, const wxPoint& rPos
+, const wxSize& rSize
+, long lStyle
+, int nMin
+, int nMax
+, int nInitial
+, const wxString& rsName
+)
+{
+ SWP vSwp;
+
+ 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);
+ wxFont* pTextFont = new wxFont( 10
+ ,wxMODERN
+ ,wxNORMAL
+ ,wxNORMAL
+ );
+ SetFont(*pTextFont);
+ ::WinQueryWindowPos(m_hWnd, &vSwp);
+ SetXComp(vSwp.x);
+ SetYComp(vSwp.y);
+ SetSize( rPos.x
+ ,rPos.y
+ ,rSize.x
+ ,rSize.y
+ );
+
+ 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);
+ delete pTextFont;
+ 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;
- wxPoint posBtn(pos);
- posBtn.x += sizeText.x + MARGIN_BETWEEN;
+ wxGetCharSize( GetHWND()
+ ,NULL
+ ,&nHeight
+ ,&vFont
+ );
+ nHeight = EDIT_HEIGHT_FROM_CHAR_HEIGHT(nHeight);
- // create the spin button
- if ( !wxSpinButton::Create(parent, id, posBtn, sizeBtn, style, name) )
+ 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
+{
+ WXHWND hWnd = GetHWND();
+
+ wxConstCast(this, wxSpinCtrl)->m_hWnd = m_hWndBuddy;
+ wxSpinButton::DoGetPosition( pnX
+ ,pnY
+ );
+ wxConstCast(this, wxSpinCtrl)->m_hWnd = hWnd;
+} // end of wxpinCtrl::DoGetPosition
- SetRange(min, max);
- SetValue(initial);
+void wxSpinCtrl::DoGetSize(
+ int* pnWidth
+, int* pnHeight
+) const
+{
+ RECTL vSpinrect;
+
+ ::WinQueryWindowRect(GetHwnd(), &vSpinrect);
+
+ 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();
- // create the text window
- if ( sizeText.y <= 0 )
+ 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;
+
+ ::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
- 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 )
+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((char*)sVal.c_str());
+ 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 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((char*)sVal.c_str());
+ 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