]> git.saurik.com Git - wxWidgets.git/blame - src/msw/spinctrl.cpp
guard against nullptr
[wxWidgets.git] / src / msw / spinctrl.cpp
CommitLineData
b782f2e0 1/////////////////////////////////////////////////////////////////////////////
623d5f80 2// Name: src/msw/spinctrl.cpp
b782f2e0
VZ
3// Purpose: wxSpinCtrl class implementation for Win32
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 22.07.99
7// RCS-ID: $Id$
74124ea9 8// Copyright: (c) 1999-2005 Vadim Zeitlin
65571936 9// Licence: wxWindows licence
b782f2e0
VZ
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
b782f2e0
VZ
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// for compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
0e528b99
JS
27#if wxUSE_SPINCTRL
28
b782f2e0 29#include "wx/spinctrl.h"
623d5f80
WS
30
31#ifndef WX_PRECOMP
7ee21e3a 32 #include "wx/hashmap.h"
57bd4c60 33 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
623d5f80
WS
34 #include "wx/event.h"
35 #include "wx/textctrl.h"
193d0c93 36 #include "wx/wxcrtvararg.h"
623d5f80
WS
37#endif
38
b782f2e0
VZ
39#include "wx/msw/private.h"
40
74124ea9
VZ
41#if wxUSE_TOOLTIPS
42 #include "wx/tooltip.h"
43#endif // wxUSE_TOOLTIPS
b782f2e0 44
678cd6de
VZ
45#include <limits.h> // for INT_MIN
46
b782f2e0
VZ
47// ----------------------------------------------------------------------------
48// macros
49// ----------------------------------------------------------------------------
50
9750fc42 51BEGIN_EVENT_TABLE(wxSpinCtrl, wxSpinButton)
e3582f7f 52 EVT_CHAR(wxSpinCtrl::OnChar)
c5c04fab 53 EVT_SET_FOCUS(wxSpinCtrl::OnSetFocus)
4a528443 54 EVT_KILL_FOCUS(wxSpinCtrl::OnKillFocus)
9750fc42 55END_EVENT_TABLE()
b782f2e0 56
6fe19057
VZ
57#define GetBuddyHwnd() (HWND)(m_hwndBuddy)
58
b782f2e0
VZ
59// ----------------------------------------------------------------------------
60// constants
61// ----------------------------------------------------------------------------
62
baccb514
VZ
63// the margin between the up-down control and its buddy (can be arbitrary,
64// choose what you like - or may be decide during run-time depending on the
65// font size?)
66static const int MARGIN_BETWEEN = 1;
b782f2e0 67
7ee21e3a
VZ
68
69// ---------------------------------------------------------------------------
70// global vars
71// ---------------------------------------------------------------------------
72
73namespace
74{
75
76// Global hash used to find the spin control corresponding to the given buddy
77// text control HWND.
78WX_DECLARE_HASH_MAP(HWND, wxSpinCtrl *,
79 wxPointerHash, wxPointerEqual,
80 SpinForTextCtrl);
81
82SpinForTextCtrl gs_spinForTextCtrl;
83
84} // anonymous namespace
85
b782f2e0
VZ
86// ============================================================================
87// implementation
88// ============================================================================
89
f6bcfd97
BP
90// ----------------------------------------------------------------------------
91// wnd proc for the buddy text ctrl
92// ----------------------------------------------------------------------------
93
94LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd,
95 UINT message,
96 WPARAM wParam,
97 LPARAM lParam)
98{
7ee21e3a 99 wxSpinCtrl * const spin = wxSpinCtrl::GetSpinForTextCtrl(hwnd);
f6bcfd97 100
5a0b1008 101 // forward some messages (mostly the key and focus ones) to the spin ctrl
f6bcfd97
BP
102 switch ( message )
103 {
93c4157c 104 case WM_SETFOCUS:
c5c04fab
VZ
105 // if the focus comes from the spin control itself, don't set it
106 // back to it -- we don't want to go into an infinite loop
c140b7e7 107 if ( (WXHWND)wParam == spin->GetHWND() )
c5c04fab
VZ
108 break;
109 //else: fall through
110
93c4157c 111 case WM_KILLFOCUS:
f6bcfd97
BP
112 case WM_CHAR:
113 case WM_DEADCHAR:
114 case WM_KEYUP:
115 case WM_KEYDOWN:
5a0b1008
VZ
116#ifdef WM_HELP
117 // we need to forward WM_HELP too to ensure that the context help
118 // associated with wxSpinCtrl is shown when the text control part of it
119 // is clicked with the "?" cursor
120 case WM_HELP:
121#endif
f6bcfd97 122 spin->MSWWindowProc(message, wParam, lParam);
e3582f7f
JS
123
124 // The control may have been deleted at this point, so check.
7ee21e3a 125 if ( !::IsWindow(hwnd) )
e3582f7f 126 return 0;
f6bcfd97 127 break;
350ba193
VZ
128
129 case WM_GETDLGCODE:
242ec2f7
VZ
130 if ( spin->HasFlag(wxTE_PROCESS_ENTER) )
131 {
132 long dlgCode = ::CallWindowProc
133 (
134 CASTWNDPROC spin->GetBuddyWndProc(),
135 hwnd,
136 message,
137 wParam,
138 lParam
139 );
140 dlgCode |= DLGC_WANTMESSAGE;
141 return dlgCode;
142 }
143 break;
f6bcfd97 144 }
350ba193 145
f6bcfd97
BP
146 return ::CallWindowProc(CASTWNDPROC spin->GetBuddyWndProc(),
147 hwnd, message, wParam, lParam);
148}
149
6fe19057
VZ
150/* static */
151wxSpinCtrl *wxSpinCtrl::GetSpinForTextCtrl(WXHWND hwndBuddy)
152{
7ee21e3a
VZ
153 const SpinForTextCtrl::const_iterator
154 it = gs_spinForTextCtrl.find(hwndBuddy);
155 if ( it == gs_spinForTextCtrl.end() )
6fe19057
VZ
156 return NULL;
157
7ee21e3a
VZ
158 wxSpinCtrl * const spin = it->second;
159
6fe19057
VZ
160 // sanity check
161 wxASSERT_MSG( spin->m_hwndBuddy == hwndBuddy,
9a83f860 162 wxT("wxSpinCtrl has incorrect buddy HWND!") );
6fe19057
VZ
163
164 return spin;
165}
166
167// process a WM_COMMAND generated by the buddy text control
168bool wxSpinCtrl::ProcessTextCommand(WXWORD cmd, WXWORD WXUNUSED(id))
169{
ea6cbf48 170 if ( (cmd == EN_CHANGE) && (!m_blockEvent ))
6fe19057 171 {
0ca3c231
VZ
172 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
173 event.SetEventObject(this);
174 wxString val = wxGetWindowText(m_hwndBuddy);
175 event.SetString(val);
176 event.SetInt(GetValue());
937013e0 177 return HandleWindowEvent(event);
6fe19057
VZ
178 }
179
180 // not processed
57f4f925 181 return false;
6fe19057
VZ
182}
183
e3582f7f
JS
184void wxSpinCtrl::OnChar(wxKeyEvent& event)
185{
77e00fe9 186 switch ( event.GetKeyCode() )
e3582f7f
JS
187 {
188 case WXK_RETURN:
189 {
190 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
191 InitCommandEvent(event);
192 wxString val = wxGetWindowText(m_hwndBuddy);
193 event.SetString(val);
194 event.SetInt(GetValue());
937013e0 195 if ( HandleWindowEvent(event) )
e3582f7f
JS
196 return;
197 break;
198 }
199
200 case WXK_TAB:
201 // always produce navigation event - even if we process TAB
202 // ourselves the fact that we got here means that the user code
203 // decided to skip processing of this TAB - probably to let it
204 // do its default job.
205 {
206 wxNavigationKeyEvent eventNav;
207 eventNav.SetDirection(!event.ShiftDown());
208 eventNav.SetWindowChange(event.ControlDown());
209 eventNav.SetEventObject(this);
210
937013e0 211 if ( GetParent()->HandleWindowEvent(eventNav) )
e3582f7f
JS
212 return;
213 }
214 break;
215 }
216
217 // no, we didn't process it
218 event.Skip();
219}
220
4a528443
JS
221void wxSpinCtrl::OnKillFocus(wxFocusEvent& event)
222{
5ec60151
VZ
223 // ensure that a correct value is shown by the control
224 NormalizeValue();
4a528443
JS
225 event.Skip();
226}
227
c5c04fab
VZ
228void wxSpinCtrl::OnSetFocus(wxFocusEvent& event)
229{
230 // when we get focus, give it to our buddy window as it needs it more than
231 // we do
232 ::SetFocus((HWND)m_hwndBuddy);
233
234 event.Skip();
235}
236
1e8dba5e
RR
237void wxSpinCtrl::NormalizeValue()
238{
e816f5c7 239 const int value = GetValue();
d40e9e06 240 const bool changed = value != m_oldValue;
e816f5c7 241
b6d83018
VZ
242 // notice that we have to call SetValue() even if the value didn't change
243 // because otherwise we could be left with empty buddy control when value
244 // is 0, see comment in SetValue()
e816f5c7
VZ
245 SetValue(value);
246
b6d83018
VZ
247 if ( changed )
248 {
d40e9e06 249 SendSpinUpdate(value);
b6d83018 250 }
1e8dba5e
RR
251}
252
b782f2e0
VZ
253// ----------------------------------------------------------------------------
254// construction
255// ----------------------------------------------------------------------------
256
257bool wxSpinCtrl::Create(wxWindow *parent,
258 wxWindowID id,
678cd6de 259 const wxString& value,
b782f2e0
VZ
260 const wxPoint& pos,
261 const wxSize& size,
262 long style,
263 int min, int max, int initial,
264 const wxString& name)
265{
ea6cbf48
RR
266 m_blockEvent = false;
267
d40e9e06
VZ
268 // this should be in ctor/init function but I don't want to add one to 2.8
269 // to avoid problems with default ctor which can be inlined in the user
270 // code and so might not get this fix without recompilation
271 m_oldValue = INT_MIN;
272
b782f2e0
VZ
273 // before using DoGetBestSize(), have to set style to let the base class
274 // know whether this is a horizontal or vertical control (we're always
275 // vertical)
882a8f40 276 style |= wxSP_VERTICAL;
c76b1a30
JS
277
278 if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
5d72f195
RR
279#ifdef __WXWINCE__
280 style |= wxBORDER_SIMPLE;
281#else
c76b1a30 282 style |= wxBORDER_SUNKEN;
5d72f195 283#endif
c76b1a30 284
882a8f40 285 SetWindowStyle(style);
b782f2e0 286
fe3d9123
JS
287 WXDWORD exStyle = 0;
288 WXDWORD msStyle = MSWGetStyle(GetWindowStyle(), & exStyle) ;
289
7e4952db 290 // propagate text alignment style to text ctrl
f1ddb476 291 if ( style & wxALIGN_RIGHT )
7e4952db 292 msStyle |= ES_RIGHT;
f1ddb476 293 else if ( style & wxALIGN_CENTER )
7e4952db 294 msStyle |= ES_CENTER;
f1ddb476 295
8e190f0e 296 // calculate the sizes: the size given is the total size for both controls
b782f2e0
VZ
297 // and we need to fit them both in the given width (height is the same)
298 wxSize sizeText(size), sizeBtn(size);
299 sizeBtn.x = wxSpinButton::DoGetBestSize().x;
baccb514
VZ
300 if ( sizeText.x <= 0 )
301 {
302 // DEFAULT_ITEM_WIDTH is the default width for the text control
303 sizeText.x = DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN + sizeBtn.x;
304 }
305
b782f2e0
VZ
306 sizeText.x -= sizeBtn.x + MARGIN_BETWEEN;
307 if ( sizeText.x <= 0 )
308 {
9a83f860 309 wxLogDebug(wxT("not enough space for wxSpinCtrl!"));
b782f2e0
VZ
310 }
311
312 wxPoint posBtn(pos);
313 posBtn.x += sizeText.x + MARGIN_BETWEEN;
314
c5c04fab
VZ
315 // we must create the text control before the spin button for the purpose
316 // of the dialog navigation: if there is a static text just before the spin
317 // control, activating it by Alt-letter should give focus to the text
318 // control, not the spin and the dialog navigation code will give focus to
319 // the next control (at Windows level), not the one after it
b782f2e0 320
c5c04fab 321 // create the text window
b782f2e0 322
b782f2e0
VZ
323 m_hwndBuddy = (WXHWND)::CreateWindowEx
324 (
c5c04fab 325 exStyle, // sunken border
9a83f860 326 wxT("EDIT"), // window class
baccb514 327 NULL, // no window title
c5c04fab 328 msStyle, // style (will be shown later)
baccb514
VZ
329 pos.x, pos.y, // position
330 0, 0, // size (will be set later)
331 GetHwndOf(parent), // parent
332 (HMENU)-1, // control id
333 wxGetInstance(), // app instance
334 NULL // unused client data
b782f2e0
VZ
335 );
336
337 if ( !m_hwndBuddy )
338 {
f6bcfd97 339 wxLogLastError(wxT("CreateWindow(buddy text window)"));
b782f2e0 340
57f4f925 341 return false;
b782f2e0
VZ
342 }
343
c5c04fab
VZ
344
345 // create the spin button
346 if ( !wxSpinButton::Create(parent, id, posBtn, sizeBtn, style, name) )
347 {
57f4f925 348 return false;
c5c04fab
VZ
349 }
350
23ec96e3 351 wxSpinButtonBase::SetRange(min, max);
e816f5c7 352
f6bcfd97 353 // subclass the text ctrl to be able to intercept some events
7ee21e3a
VZ
354 gs_spinForTextCtrl[GetBuddyHwnd()] = this;
355
975b6bcf
VZ
356 m_wndProcBuddy = (WXFARPROC)wxSetWindowProc(GetBuddyHwnd(),
357 wxBuddyTextWndProc);
f6bcfd97 358
8d2e831b
RD
359 // set up fonts and colours (This is nomally done in MSWCreateControl)
360 InheritAttributes();
e0176dd9
VZ
361 if (!m_hasFont)
362 SetFont(GetDefaultAttributes().font);
8d2e831b 363
baccb514
VZ
364 // set the size of the text window - can do it only now, because we
365 // couldn't call DoGetBestSize() before as font wasn't set
366 if ( sizeText.y <= 0 )
367 {
882a8f40 368 int cx, cy;
7a5e53ab 369 wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
882a8f40
VZ
370
371 sizeText.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
baccb514
VZ
372 }
373
170acdc9 374 SetInitialSize(size);
baccb514 375
6fe19057 376 (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
b782f2e0
VZ
377
378 // associate the text window with the spin button
baccb514
VZ
379 (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)m_hwndBuddy, 0);
380
549cc3a8
JS
381 SetValue(initial);
382
383 // Set the range in the native control
384 SetRange(min, max);
385
3d104ec3 386 if ( !value.empty() )
678cd6de
VZ
387 {
388 SetValue(value);
5bcdac45
JS
389 m_oldValue = (int) wxAtol(value);
390 }
391 else
392 {
393 SetValue(wxString::Format(wxT("%d"), initial));
394 m_oldValue = initial;
678cd6de
VZ
395 }
396
57f4f925 397 return true;
baccb514
VZ
398}
399
f6bcfd97
BP
400wxSpinCtrl::~wxSpinCtrl()
401{
402 // destroy the buddy window because this pointer which wxBuddyTextWndProc
403 // uses will not soon be valid any more
7ee21e3a
VZ
404 ::DestroyWindow( GetBuddyHwnd() );
405
406 gs_spinForTextCtrl.erase(GetBuddyHwnd());
f6bcfd97
BP
407}
408
678cd6de
VZ
409// ----------------------------------------------------------------------------
410// wxTextCtrl-like methods
411// ----------------------------------------------------------------------------
412
413void wxSpinCtrl::SetValue(const wxString& text)
414{
6fe19057 415 if ( !::SetWindowText(GetBuddyHwnd(), text.c_str()) )
678cd6de 416 {
f6bcfd97 417 wxLogLastError(wxT("SetWindowText(buddy)"));
678cd6de
VZ
418 }
419}
420
eeea41ab
VZ
421void wxSpinCtrl::SetValue(int val)
422{
ea6cbf48 423 m_blockEvent = true;
f1ddb476 424
eeea41ab
VZ
425 wxSpinButton::SetValue(val);
426
427 // normally setting the value of the spin button is enough as it updates
428 // its buddy control automatically ...
429 if ( wxGetWindowText(m_hwndBuddy).empty() )
430 {
431 // ... but sometimes it doesn't, notably when the value is 0 and the
432 // text control is currently empty, the spin button seems to be happy
433 // to leave it like this, while we really want to always show the
434 // current value in the control, so do it manually
e0a050e3 435 ::SetWindowText(GetBuddyHwnd(),
9a83f860 436 wxString::Format(wxT("%d"), val).wx_str());
eeea41ab 437 }
e816f5c7 438
25dff19c 439 m_oldValue = GetValue();
f1ddb476 440
ea6cbf48 441 m_blockEvent = false;
eeea41ab
VZ
442}
443
678cd6de
VZ
444int wxSpinCtrl::GetValue() const
445{
446 wxString val = wxGetWindowText(m_hwndBuddy);
447
448 long n;
eeea41ab 449 if ( (wxSscanf(val, wxT("%ld"), &n) != 1) )
678cd6de 450 n = INT_MIN;
3d104ec3 451
eeea41ab
VZ
452 if ( n < m_min )
453 n = m_min;
454 if ( n > m_max )
455 n = m_max;
678cd6de
VZ
456
457 return n;
458}
459
07901ec9
VZ
460void wxSpinCtrl::SetSelection(long from, long to)
461{
77ffb593 462 // if from and to are both -1, it means (in wxWidgets) that all text should
07901ec9
VZ
463 // be selected - translate into Windows convention
464 if ( (from == -1) && (to == -1) )
465 {
466 from = 0;
467 }
468
7d86a2d4 469 ::SendMessage(GetBuddyHwnd(), EM_SETSEL, (WPARAM)from, (LPARAM)to);
07901ec9
VZ
470}
471
345ff9c6
VZ
472// ----------------------------------------------------------------------------
473// wxSpinButton methods
474// ----------------------------------------------------------------------------
475
476void wxSpinCtrl::SetRange(int minVal, int maxVal)
477{
478 wxSpinButton::SetRange(minVal, maxVal);
479
480 // this control is used for numeric entry so restrict the input to numeric
481 // keys only -- but only if we don't need to be able to enter "-" in it as
482 // otherwise this would become impossible
483 const DWORD styleOld = ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE);
484 DWORD styleNew;
485 if ( minVal < 0 )
486 styleNew = styleOld & ~ES_NUMBER;
487 else
488 styleNew = styleOld | ES_NUMBER;
489
490 if ( styleNew != styleOld )
491 ::SetWindowLong(GetBuddyHwnd(), GWL_STYLE, styleNew);
492}
493
baccb514 494// ----------------------------------------------------------------------------
882a8f40 495// forward some methods to subcontrols
baccb514
VZ
496// ----------------------------------------------------------------------------
497
498bool wxSpinCtrl::SetFont(const wxFont& font)
499{
500 if ( !wxWindowBase::SetFont(font) )
501 {
502 // nothing to do
57f4f925 503 return false;
baccb514
VZ
504 }
505
506 WXHANDLE hFont = GetFont().GetResourceHandle();
6fe19057 507 (void)::SendMessage(GetBuddyHwnd(), WM_SETFONT, (WPARAM)hFont, TRUE);
b782f2e0 508
57f4f925 509 return true;
b782f2e0
VZ
510}
511
882a8f40
VZ
512bool wxSpinCtrl::Show(bool show)
513{
514 if ( !wxControl::Show(show) )
515 {
57f4f925 516 return false;
882a8f40
VZ
517 }
518
6fe19057 519 ::ShowWindow(GetBuddyHwnd(), show ? SW_SHOW : SW_HIDE);
882a8f40 520
57f4f925 521 return true;
882a8f40
VZ
522}
523
03d4194d
VZ
524bool wxSpinCtrl::Reparent(wxWindowBase *newParent)
525{
526 // Reparenting both the updown control and its buddy does not seem to work:
527 // they continue to be connected somehow, but visually there is no feedback
528 // on the buddy edit control. To avoid this problem, we reparent the buddy
529 // window normally, but we recreate the updown control and reassign its
530 // buddy.
531
03263ff7
VZ
532 // Get the position before changing the parent as it would be offset after
533 // changing it.
534 const wxRect rect = GetRect();
535
03d4194d
VZ
536 if ( !wxWindowBase::Reparent(newParent) )
537 return false;
538
539 newParent->GetChildren().DeleteObject(this);
540
bc73fe96
VZ
541 // destroy the old spin button after detaching it from this wxWindow object
542 // (notice that m_hWnd will be reset by UnsubclassWin() so save it first)
543 const HWND hwndOld = GetHwnd();
03d4194d 544 UnsubclassWin();
bc73fe96 545 if ( !::DestroyWindow(hwndOld) )
43b2d5e7 546 {
03d4194d 547 wxLogLastError(wxT("DestroyWindow"));
43b2d5e7 548 }
03d4194d
VZ
549
550 // create and initialize the new one
551 if ( !wxSpinButton::Create(GetParent(), GetId(),
03263ff7 552 rect.GetPosition(), rect.GetSize(),
03d4194d
VZ
553 GetWindowStyle(), GetName()) )
554 return false;
555
03263ff7
VZ
556 // reapply our values to wxSpinButton
557 wxSpinButton::SetValue(GetValue());
03d4194d 558 SetRange(m_min, m_max);
03263ff7
VZ
559
560 // also set the size again with wxSIZE_ALLOW_MINUS_ONE flag: this is
561 // necessary if our original position used -1 for either x or y
562 SetSize(rect, wxSIZE_ALLOW_MINUS_ONE);
03d4194d
VZ
563
564 // associate it with the buddy control again
565 ::SetParent(GetBuddyHwnd(), GetHwndOf(GetParent()));
566 (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)GetBuddyHwnd(), 0);
567
568 return true;
569}
570
882a8f40
VZ
571bool wxSpinCtrl::Enable(bool enable)
572{
573 if ( !wxControl::Enable(enable) )
574 {
57f4f925 575 return false;
882a8f40
VZ
576 }
577
0826c4d3 578 MSWEnableHWND(GetBuddyHwnd(), enable);
882a8f40 579
57f4f925 580 return true;
882a8f40
VZ
581}
582
8bf3196d
GRG
583void wxSpinCtrl::SetFocus()
584{
6fe19057 585 ::SetFocus(GetBuddyHwnd());
8bf3196d
GRG
586}
587
74124ea9
VZ
588#if wxUSE_TOOLTIPS
589
590void wxSpinCtrl::DoSetToolTip(wxToolTip *tip)
591{
592 wxSpinButton::DoSetToolTip(tip);
593
594 if ( tip )
595 tip->Add(m_hwndBuddy);
596}
597
598#endif // wxUSE_TOOLTIPS
599
9750fc42 600// ----------------------------------------------------------------------------
d40e9e06 601// events processing and generation
9750fc42
VZ
602// ----------------------------------------------------------------------------
603
d40e9e06 604void wxSpinCtrl::SendSpinUpdate(int value)
9750fc42
VZ
605{
606 wxCommandEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED, GetId());
607 event.SetEventObject(this);
d40e9e06 608 event.SetInt(value);
e816f5c7 609
937013e0 610 (void)HandleWindowEvent(event);
d40e9e06
VZ
611
612 m_oldValue = value;
613}
9750fc42 614
177e38e6
RR
615bool wxSpinCtrl::MSWOnScroll(int WXUNUSED(orientation), WXWORD wParam,
616 WXWORD pos, WXHWND control)
d40e9e06 617{
177e38e6
RR
618 wxCHECK_MSG( control, false, wxT("scrolling what?") );
619
620 if ( wParam != SB_THUMBPOSITION )
9750fc42 621 {
177e38e6
RR
622 // probable SB_ENDSCROLL - we don't react to it
623 return false;
9750fc42 624 }
177e38e6
RR
625
626 int new_value = (short) pos;
627 if (m_oldValue != new_value)
628 SendSpinUpdate( new_value );
629
630 return TRUE;
631}
632
633bool wxSpinCtrl::MSWOnNotify(int WXUNUSED(idCtrl), WXLPARAM lParam, WXLPARAM *result)
634{
635 NM_UPDOWN *lpnmud = (NM_UPDOWN *)lParam;
636
637 if (lpnmud->hdr.hwndFrom != GetHwnd()) // make sure it is the right control
638 return false;
639
640 *result = 0; // never reject UP and DOWN events
641
642 return TRUE;
9750fc42
VZ
643}
644
177e38e6 645
b782f2e0
VZ
646// ----------------------------------------------------------------------------
647// size calculations
648// ----------------------------------------------------------------------------
649
f68586e5 650wxSize wxSpinCtrl::DoGetBestSize() const
baccb514
VZ
651{
652 wxSize sizeBtn = wxSpinButton::DoGetBestSize();
653 sizeBtn.x += DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN;
654
655 int y;
7a5e53ab 656 wxGetCharSize(GetHWND(), NULL, &y, GetFont());
baccb514
VZ
657 y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(y);
658
09f27917
JS
659 // JACS: we should always use the height calculated
660 // from above, because otherwise we'll get a spin control
661 // that's too big. So never use the height calculated
662 // from wxSpinButton::DoGetBestSize().
57f4f925 663
09f27917 664 // if ( sizeBtn.y < y )
baccb514
VZ
665 {
666 // make the text tall enough
667 sizeBtn.y = y;
668 }
669
670 return sizeBtn;
671}
672
b782f2e0
VZ
673void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
674{
baccb514 675 int widthBtn = wxSpinButton::DoGetBestSize().x;
b782f2e0
VZ
676 int widthText = width - widthBtn - MARGIN_BETWEEN;
677 if ( widthText <= 0 )
678 {
9a83f860 679 wxLogDebug(wxT("not enough space for wxSpinCtrl!"));
b782f2e0
VZ
680 }
681
8e44f3ca 682 // 1) The buddy window
7d86a2d4 683 DoMoveSibling(m_hwndBuddy, x, y, widthText, height);
b782f2e0 684
8e44f3ca 685 // 2) The button window
b782f2e0 686 x += widthText + MARGIN_BETWEEN;
7d86a2d4 687 wxSpinButton::DoMoveWindow(x, y, widthBtn, height);
b782f2e0
VZ
688}
689
f6bcfd97
BP
690// get total size of the control
691void wxSpinCtrl::DoGetSize(int *x, int *y) const
692{
693 RECT spinrect, textrect, ctrlrect;
694 GetWindowRect(GetHwnd(), &spinrect);
6fe19057 695 GetWindowRect(GetBuddyHwnd(), &textrect);
f6bcfd97
BP
696 UnionRect(&ctrlrect,&textrect, &spinrect);
697
698 if ( x )
699 *x = ctrlrect.right - ctrlrect.left;
700 if ( y )
701 *y = ctrlrect.bottom - ctrlrect.top;
702}
703
b7527dde
VS
704void wxSpinCtrl::DoGetClientSize(int *x, int *y) const
705{
706 RECT spinrect = wxGetClientRect(GetHwnd());
707 RECT textrect = wxGetClientRect(GetBuddyHwnd());
708 RECT ctrlrect;
709 UnionRect(&ctrlrect,&textrect, &spinrect);
710
711 if ( x )
712 *x = ctrlrect.right - ctrlrect.left;
713 if ( y )
714 *y = ctrlrect.bottom - ctrlrect.top;
715}
716
f6bcfd97
BP
717void wxSpinCtrl::DoGetPosition(int *x, int *y) const
718{
719 // hack: pretend that our HWND is the text control just for a moment
720 WXHWND hWnd = GetHWND();
721 wxConstCast(this, wxSpinCtrl)->m_hWnd = m_hwndBuddy;
722
723 wxSpinButton::DoGetPosition(x, y);
724
725 wxConstCast(this, wxSpinCtrl)->m_hWnd = hWnd;
726}
727
74124ea9 728#endif // wxUSE_SPINCTRL