1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/spinbutt.cpp
3 // Purpose:     wxSpinButton
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     04/01/98
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 #ifdef __BORLANDC__
23     #pragma hdrstop
24 #endif
25 
26 #ifndef WX_PRECOMP
27     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
28     #include "wx/app.h"
29 #endif
30 
31 #if wxUSE_SPINBTN
32 
33 #include "wx/spinbutt.h"
34 
35 #include "wx/msw/private.h"
36 
37 #ifndef UDM_SETRANGE32
38     #define UDM_SETRANGE32 (WM_USER+111)
39 #endif
40 
41 #ifndef UDM_SETPOS32
42     #define UDM_SETPOS32 (WM_USER+113)
43     #define UDM_GETPOS32 (WM_USER+114)
44 #endif
45 
46 // ============================================================================
47 // implementation
48 // ============================================================================
49 
50 // ----------------------------------------------------------------------------
51 // wxWin macros
52 // ----------------------------------------------------------------------------
53 
54 // ----------------------------------------------------------------------------
55 // wxSpinButton
56 // ----------------------------------------------------------------------------
57 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)58 bool wxSpinButton::Create(wxWindow *parent,
59                           wxWindowID id,
60                           const wxPoint& pos,
61                           const wxSize& size,
62                           long style,
63                           const wxString& name)
64 {
65     // basic initialization
66     m_windowId = (id == wxID_ANY) ? NewControlId() : id;
67 
68     SetName(name);
69 
70     int x = pos.x;
71     int y = pos.y;
72     int width = size.x;
73     int height = size.y;
74 
75     m_windowStyle = style;
76 
77     SetParent(parent);
78 
79     // get the right size for the control
80     if ( width <= 0 || height <= 0 )
81     {
82         wxSize bestSize = DoGetBestSize();
83         if ( width <= 0 )
84             width = bestSize.x;
85         if ( height <= 0 )
86             height = bestSize.y;
87     }
88 
89     if ( x < 0 )
90         x = 0;
91     if ( y < 0 )
92         y = 0;
93 
94     // translate the styles
95     DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP | /*  WS_CLIPSIBLINGS | */
96                    UDS_NOTHOUSANDS | // never useful, sometimes harmful
97                    UDS_SETBUDDYINT;  // it doesn't harm if we don't have buddy
98 
99     if ( m_windowStyle & wxCLIP_SIBLINGS )
100         wstyle |= WS_CLIPSIBLINGS;
101     if ( m_windowStyle & wxSP_HORIZONTAL )
102         wstyle |= UDS_HORZ;
103     if ( m_windowStyle & wxSP_ARROW_KEYS )
104         wstyle |= UDS_ARROWKEYS;
105     if ( m_windowStyle & wxSP_WRAP )
106         wstyle |= UDS_WRAP;
107 
108     // create the UpDown control.
109     m_hWnd = (WXHWND)CreateUpDownControl
110                      (
111                        wstyle,
112                        x, y, width, height,
113                        GetHwndOf(parent),
114                        m_windowId,
115                        wxGetInstance(),
116                        NULL, // no buddy
117                        m_max, m_min,
118                        m_min // initial position
119                      );
120 
121     if ( !m_hWnd )
122     {
123         wxLogLastError(wxT("CreateUpDownControl"));
124 
125         return false;
126     }
127 
128     if ( parent )
129     {
130         parent->AddChild(this);
131     }
132 
133     SubclassWin(m_hWnd);
134 
135     SetInitialSize(size);
136 
137     return true;
138 }
139 
~wxSpinButton()140 wxSpinButton::~wxSpinButton()
141 {
142 }
143 
144 // ----------------------------------------------------------------------------
145 // size calculation
146 // ----------------------------------------------------------------------------
147 
DoGetBestSize() const148 wxSize wxSpinButton::DoGetBestSize() const
149 {
150     return GetBestSpinnerSize( (GetWindowStyle() & wxSP_VERTICAL) != 0 );
151 }
152 
153 // ----------------------------------------------------------------------------
154 // Attributes
155 // ----------------------------------------------------------------------------
156 
GetValue() const157 int wxSpinButton::GetValue() const
158 {
159     int n;
160 #ifdef UDM_GETPOS32
161     if ( wxApp::GetComCtl32Version() >= 580 )
162     {
163         // use the full 32 bit range if available
164         n = ::SendMessage(GetHwnd(), UDM_GETPOS32, 0, 0);
165     }
166     else
167 #endif // UDM_GETPOS32
168     {
169         // we're limited to 16 bit
170         n = (short)LOWORD(::SendMessage(GetHwnd(), UDM_GETPOS, 0, 0));
171     }
172 
173     if (n < m_min) n = m_min;
174     if (n > m_max) n = m_max;
175 
176     return n;
177 }
178 
SetValue(int val)179 void wxSpinButton::SetValue(int val)
180 {
181     // wxSpinButtonBase::SetValue(val); -- no, it is pure virtual
182 
183 #ifdef UDM_SETPOS32
184     if ( wxApp::GetComCtl32Version() >= 580 )
185     {
186         // use the full 32 bit range if available
187         ::SendMessage(GetHwnd(), UDM_SETPOS32, 0, val);
188     }
189     else // we're limited to 16 bit
190 #endif // UDM_SETPOS32
191     {
192         ::SendMessage(GetHwnd(), UDM_SETPOS, 0, MAKELONG((short) val, 0));
193     }
194 }
195 
NormalizeValue()196 void wxSpinButton::NormalizeValue()
197 {
198     SetValue( GetValue() );
199 }
200 
SetRange(int minVal,int maxVal)201 void wxSpinButton::SetRange(int minVal, int maxVal)
202 {
203     const bool hadRange = m_min < m_max;
204 
205     wxSpinButtonBase::SetRange(minVal, maxVal);
206 
207 #ifdef UDM_SETRANGE32
208     if ( wxApp::GetComCtl32Version() >= 471 )
209     {
210         // use the full 32 bit range if available
211         ::SendMessage(GetHwnd(), UDM_SETRANGE32, minVal, maxVal);
212     }
213     else // we're limited to 16 bit
214 #endif // UDM_SETRANGE32
215     {
216         ::SendMessage(GetHwnd(), UDM_SETRANGE, 0,
217                       (LPARAM) MAKELONG((short)maxVal, (short)minVal));
218     }
219 
220     // the current value might be out of the new range, force it to be in it
221     NormalizeValue();
222 
223     // if range was valid but becomes degenerated (min == max) now or vice
224     // versa then the spin buttons are automatically disabled/enabled back
225     // but don't update themselves for some reason, so do it manually
226     if ( hadRange != (m_min < m_max) )
227     {
228         // update the visual state of the button
229         Refresh();
230     }
231 }
232 
MSWOnScroll(int WXUNUSED (orientation),WXWORD wParam,WXWORD WXUNUSED (pos),WXHWND control)233 bool wxSpinButton::MSWOnScroll(int WXUNUSED(orientation), WXWORD wParam,
234                                WXWORD WXUNUSED(pos), WXHWND control)
235 {
236     wxCHECK_MSG( control, false, wxT("scrolling what?") );
237 
238     if ( wParam != SB_THUMBPOSITION )
239     {
240         // probable SB_ENDSCROLL - we don't react to it
241         return false;
242     }
243 
244     wxSpinEvent event(wxEVT_SCROLL_THUMBTRACK, m_windowId);
245     // We can't use 16 bit position provided in this message for spin buttons
246     // using 32 bit range.
247     event.SetPosition(GetValue());
248     event.SetEventObject(this);
249 
250     return HandleWindowEvent(event);
251 }
252 
MSWOnNotify(int WXUNUSED (idCtrl),WXLPARAM lParam,WXLPARAM * result)253 bool wxSpinButton::MSWOnNotify(int WXUNUSED(idCtrl), WXLPARAM lParam, WXLPARAM *result)
254 {
255     NM_UPDOWN *lpnmud = (NM_UPDOWN *)lParam;
256 
257     if (lpnmud->hdr.hwndFrom != GetHwnd()) // make sure it is the right control
258         return false;
259 
260     wxSpinEvent event(lpnmud->iDelta > 0 ? wxEVT_SCROLL_LINEUP
261                                          : wxEVT_SCROLL_LINEDOWN,
262                       m_windowId);
263     event.SetPosition(lpnmud->iPos + lpnmud->iDelta);
264     event.SetEventObject(this);
265 
266     bool processed = HandleWindowEvent(event);
267 
268     *result = event.IsAllowed() ? 0 : 1;
269 
270     return processed;
271 }
272 
MSWCommand(WXUINT WXUNUSED (cmd),WXWORD WXUNUSED (id))273 bool wxSpinButton::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD WXUNUSED(id))
274 {
275     // No command messages
276     return false;
277 }
278 
279 #endif // wxUSE_SPINBTN
280