X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7798a18ec81d3733082bc60a958fac1d4510faca..f0e5d5d22ce012e9312a63175d79c0f4a8d444be:/src/msw/slider.cpp diff --git a/src/msw/slider.cpp b/src/msw/slider.cpp index 1114477aa2..7c1c7103f2 100644 --- a/src/msw/slider.cpp +++ b/src/msw/slider.cpp @@ -1,790 +1,774 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: slider.cpp -// Purpose: wxSlider +// Name: src/msw/slider.cpp +// Purpose: wxSlider, using the Win95 (and later) trackbar control // Author: Julian Smart // Modified by: // Created: 04/01/98 // RCS-ID: $Id$ -// Copyright: (c) Julian Smart and Markus Holzem -// Licence: wxWindows license +// Copyright: (c) Julian Smart 1998 +// Vadim Zeitlin 2004 +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "slider.h" -#endif +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif -#ifndef WX_PRECOMP -#include +#if wxUSE_SLIDER + #include "wx/slider.h" -#endif -#include "wx/msw/private.h" +#ifndef WX_PRECOMP + #include "wx/msw/wrapcctl.h" // include "properly" + #include "wx/brush.h" +#endif -// Can opt to not use track bar under Win95 if you prefer it - set to 0 -#define USE_TRACK_BAR 1 +#include "wx/msw/subwin.h" -#if defined(__WIN95__) && !defined(__GNUWIN32__) -#include -#endif +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- -#if !USE_SHARED_LIBRARY -IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl) +namespace +{ -#if WXWIN_COMPATIBILITY -BEGIN_EVENT_TABLE(wxSlider, wxControl) - EVT_SCROLL(wxSlider::OnScroll) -END_EVENT_TABLE() -#endif +// indices of labels in wxSlider::m_labels +enum +{ + SliderLabel_Min, + SliderLabel_Max, + SliderLabel_Value, + SliderLabel_Last +}; + +// the gaps between the slider and the labels, in pixels +const int HGAP = 5; +const int VGAP = 4; +// the width of the borders including white space +const int BORDERPAD = 8; +// these 2 values are arbitrary: +const int THUMB = 24; +const int TICK = 8; + +} // anonymous namespace + +// ============================================================================ +// wxSlider implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// construction +// ---------------------------------------------------------------------------- + +void wxSlider::Init() +{ + m_labels = NULL; -#endif + m_pageSize = 1; + m_lineSize = 1; + m_rangeMax = 0; + m_rangeMin = 0; + m_tickFreq = 0; -// Slider -wxSlider::wxSlider(void) -{ - m_staticValue = 0; - m_staticMin = 0; - m_staticMax = 0; - m_pageSize = 1; - m_lineSize = 1; - m_rangeMax = 0; - m_rangeMin = 0; - m_tickFreq = 0; + m_isDragging = false; } -bool wxSlider::Create(wxWindow *parent, const wxWindowID id, - const int value, const int minValue, const int maxValue, - const wxPoint& pos, - const wxSize& size, const long style, - const wxValidator& validator, - const wxString& name) -{ - SetName(name); - SetValidator(validator); - - if (parent) parent->AddChild(this); - SetBackgroundColour(parent->GetDefaultBackgroundColour()) ; - SetForegroundColour(parent->GetDefaultForegroundColour()) ; - - m_staticValue = 0; - m_staticMin = 0; - m_staticMax = 0; - m_pageSize = 1; - m_lineSize = 1; - m_windowStyle = style; - m_tickFreq = 0; - - if ( id == -1 ) - m_windowId = (int)NewControlId(); - else - m_windowId = id; - - int x = pos.x; - int y = pos.y; - int width = size.x; - int height = size.y; - -#if defined(__WIN95__) && USE_TRACK_BAR - long msStyle ; - - if ( m_windowStyle & wxSL_LABELS ) - { - msStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | SS_CENTER; - - bool want3D; - WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D) ; - - m_staticValue = (WXHWND) CreateWindowEx(exStyle, "STATIC", NULL, - msStyle, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)NewControlId(), - wxGetInstance(), NULL); - - // Now create min static control - sprintf(wxBuffer, "%d", minValue); - m_staticMin = (WXHWND) CreateWindowEx(0, "STATIC", wxBuffer, - STATIC_FLAGS, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)NewControlId(), - wxGetInstance(), NULL); - } - - msStyle = 0; - if (m_windowStyle & wxSL_VERTICAL) - msStyle = TBS_VERT | WS_CHILD | WS_VISIBLE | WS_TABSTOP ; - else - msStyle = TBS_HORZ | WS_CHILD | WS_VISIBLE | WS_TABSTOP ; - - if ( m_windowStyle & wxSL_AUTOTICKS ) - msStyle |= TBS_AUTOTICKS ; - - if ( m_windowStyle & wxSL_LEFT ) - msStyle |= TBS_LEFT; - else if ( m_windowStyle & wxSL_RIGHT ) - msStyle |= TBS_RIGHT; - else if ( m_windowStyle & wxSL_TOP ) - msStyle |= TBS_TOP; - else if ( m_windowStyle & wxSL_BOTTOM ) - msStyle |= TBS_BOTTOM; - else if ( m_windowStyle & wxSL_BOTH ) - msStyle |= TBS_BOTH; - else if ( ! (m_windowStyle & wxSL_AUTOTICKS) ) - msStyle |= TBS_NOTICKS; - - if ( m_windowStyle & wxSL_SELRANGE ) - msStyle |= TBS_ENABLESELRANGE ; - - HWND scroll_bar = CreateWindowEx(MakeExtendedStyle(m_windowStyle), TRACKBAR_CLASS, wxBuffer, - msStyle, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)m_windowId, - wxGetInstance(), NULL); - - m_rangeMax = maxValue; - m_rangeMin = minValue; - - m_pageSize = (int)((maxValue-minValue)/10); - - ::SendMessage(scroll_bar, TBM_SETRANGE, TRUE, MAKELONG(minValue, maxValue)); - ::SendMessage(scroll_bar, TBM_SETPOS, TRUE, (LPARAM)value); - ::SendMessage(scroll_bar, TBM_SETPAGESIZE, 0, (LPARAM)m_pageSize); - - m_hWnd = (WXHWND)scroll_bar; - - SubclassWin(GetHWND()); - - SetFont(parent->GetFont()); - - if ( m_windowStyle & wxSL_LABELS ) - { - // Finally, create max value static item - sprintf(wxBuffer, "%d", maxValue); - m_staticMax = (WXHWND) CreateWindowEx(0, "STATIC", wxBuffer, - STATIC_FLAGS, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)NewControlId(), - wxGetInstance(), NULL); - - if (GetFont()) - { -// GetFont()->RealizeResource(); - if (GetFont()->GetResourceHandle()) +bool wxSlider::Create(wxWindow *parent, + wxWindowID id, + int value, + int minValue, + int maxValue, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + wxCHECK_MSG( minValue < maxValue, false, + wxT("Slider minimum must be strictly less than the maximum.") ); + + // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and + // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility + // reasons we can't really change it, instead try to infer the orientation + // from the flags given to us here + switch ( style & (wxSL_LEFT | wxSL_RIGHT | wxSL_TOP | wxSL_BOTTOM) ) + { + case wxSL_LEFT: + case wxSL_RIGHT: + style |= wxSL_VERTICAL; + break; + + case wxSL_TOP: + case wxSL_BOTTOM: + style |= wxSL_HORIZONTAL; + break; + + case 0: + // no specific direction, do we have at least the orientation? + if ( !(style & (wxSL_HORIZONTAL | wxSL_VERTICAL)) ) + { + // no, choose default + style |= wxSL_BOTTOM | wxSL_HORIZONTAL; + } + }; + + wxASSERT_MSG( !(style & wxSL_VERTICAL) || !(style & wxSL_HORIZONTAL), + wxT("incompatible slider direction and orientation") ); + + + // initialize everything + if ( !CreateControl(parent, id, pos, size, style, validator, name) ) + return false; + + // ensure that we have correct values for GetLabelsSize() + m_rangeMin = minValue; + m_rangeMax = maxValue; + + // create the labels first, so that our DoGetBestSize() could take them + // into account + // + // note that we could simply create 3 wxStaticTexts here but it could + // result in some observable side effects at wx level (e.g. the parent of + // wxSlider would have 3 more children than expected) and so we prefer not + // to do it like this + if ( m_windowStyle & wxSL_LABELS ) + { + m_labels = new wxSubwindows(SliderLabel_Last); + + HWND hwndParent = GetHwndOf(parent); + for ( size_t n = 0; n < SliderLabel_Last; n++ ) { - if ( m_staticMin ) - SendMessage((HWND)m_staticMin,WM_SETFONT, - (WPARAM)GetFont()->GetResourceHandle(),0L); - if ( m_staticMax ) - SendMessage((HWND)m_staticMax,WM_SETFONT, - (WPARAM)GetFont()->GetResourceHandle(),0L); - if (m_staticValue) - SendMessage((HWND)m_staticValue,WM_SETFONT, - (WPARAM)GetFont()->GetResourceHandle(),0L); + wxWindowIDRef lblid = NewControlId(); + + HWND wnd = ::CreateWindow + ( + wxT("STATIC"), + NULL, + WS_CHILD | WS_VISIBLE | SS_CENTER, + 0, 0, 0, 0, + hwndParent, + (HMENU)wxUIntToPtr(lblid.GetValue()), + wxGetInstance(), + NULL + ); + + m_labels->Set(n, wnd, lblid); } - } - } -#else - // non-Win95 implementation - - long msStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | SS_CENTER; - - bool want3D; - WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D) ; - - m_staticValue = (WXHWND) CreateWindowEx(exStyle, "STATIC", NULL, - msStyle, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)NewControlId(), - wxGetInstance(), NULL); - - // Now create min static control - sprintf(wxBuffer, "%d", minValue); - m_staticMin = (WXHWND) CreateWindowEx(0, "STATIC", wxBuffer, - STATIC_FLAGS, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)NewControlId(), - wxGetInstance(), NULL); - - // Now create slider - m_windowId = (int)NewControlId(); - - msStyle = 0; - if (m_windowStyle & wxSL_VERTICAL) - msStyle = SBS_VERT | WS_CHILD | WS_VISIBLE | WS_TABSTOP ; - else - msStyle = SBS_HORZ | WS_CHILD | WS_VISIBLE | WS_TABSTOP ; - - HWND scroll_bar = CreateWindowEx(MakeExtendedStyle(m_windowStyle), "SCROLLBAR", wxBuffer, - msStyle, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)m_windowId, - wxGetInstance(), NULL); - - m_pageSize = (int)((maxValue-minValue)/10); - m_rangeMax = maxValue; - m_rangeMin = minValue; - - ::SetScrollRange(scroll_bar, SB_CTL, minValue, maxValue, FALSE); - ::SetScrollPos(scroll_bar, SB_CTL, value, FALSE); - ShowWindow(scroll_bar, SW_SHOW); - - m_hWnd = (WXHWND)scroll_bar; - - // Subclass again for purposes of dialog editing mode - SubclassWin(GetHWND()); - - // Finally, create max value static item - sprintf(wxBuffer, "%d", maxValue); - m_staticMax = (WXHWND) CreateWindowEx(0, "STATIC", wxBuffer, - STATIC_FLAGS, - 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)NewControlId(), - wxGetInstance(), NULL); - - SetFont(* parent->GetFont()); - - if (GetFont()) - { -// GetFont()->RealizeResource(); - if (GetFont()->GetResourceHandle()) - { - if ( m_staticMin ) - SendMessage((HWND)m_staticMin,WM_SETFONT, - (WPARAM)GetFont()->GetResourceHandle(),0L); - if ( m_staticMax ) - SendMessage((HWND)m_staticMax,WM_SETFONT, - (WPARAM)GetFont()->GetResourceHandle(),0L); - if (m_staticValue) - SendMessage((HWND)m_staticValue,WM_SETFONT, - (WPARAM)GetFont()->GetResourceHandle(),0L); + m_labels->SetFont(GetFont()); } - } -#endif - SetSize(x, y, width, height); - SetValue(value); + // now create the main control too + if ( !MSWCreateControl(TRACKBAR_CLASS, wxEmptyString, pos, size) ) + return false; + + // and initialize everything + SetRange(minValue, maxValue); + SetValue(value); + SetPageSize((maxValue - minValue)/10); - return TRUE; + // we need to position the labels correctly if we have them and if + // SetSize() hadn't been called before (when best size was determined by + // MSWCreateControl()) as in this case they haven't been put in place yet + if ( m_labels && size.x != wxDefaultCoord && size.y != wxDefaultCoord ) + { + SetSize(size); + } + + return true; } -void wxSlider::MSWOnVScroll(const WXWORD wParam, const WXWORD pos, const WXHWND control) +WXDWORD wxSlider::MSWGetStyle(long style, WXDWORD *exstyle) const { -#if defined(__WIN95__) && USE_TRACK_BAR - int position = 0; // Dummy - not used in this mode -#else - int position = ::GetScrollPos((HWND)control, SB_CTL); -#endif + WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle); - int nScrollInc; - wxEventType scrollEvent = wxEVT_NULL; - switch ( wParam ) - { - case SB_TOP: - nScrollInc = m_rangeMax - position; - scrollEvent = wxEVT_SCROLL_TOP; - break; - - case SB_BOTTOM: - nScrollInc = - position; - scrollEvent = wxEVT_SCROLL_BOTTOM; - break; - - case SB_LINEUP: - nScrollInc = - GetLineSize(); - scrollEvent = wxEVT_SCROLL_LINEUP; - break; - - case SB_LINEDOWN: - nScrollInc = GetLineSize(); - scrollEvent = wxEVT_SCROLL_LINEDOWN; - break; - - case SB_PAGEUP: - nScrollInc = -GetPageSize(); - scrollEvent = wxEVT_SCROLL_PAGEUP; - break; - - case SB_PAGEDOWN: - nScrollInc = GetPageSize(); - scrollEvent = wxEVT_SCROLL_PAGEDOWN; - break; - - case SB_THUMBTRACK: - case SB_THUMBPOSITION: -#ifdef __WIN32__ - nScrollInc = (signed short)pos - position; -#else - nScrollInc = pos - position; -#endif - scrollEvent = wxEVT_SCROLL_THUMBTRACK; - break; + // TBS_HORZ, TBS_RIGHT and TBS_BOTTOM are 0 but do include them for clarity + msStyle |= style & wxSL_VERTICAL ? TBS_VERT : TBS_HORZ; - default: - nScrollInc = 0; - return; + if ( style & wxSL_BOTH ) + { + // this fully specifies the style combined with TBS_VERT/HORZ above + msStyle |= TBS_BOTH; } - -#if !(WIN95 && USE_TRACK_BAR) - if (nScrollInc != 0) -#endif + else // choose one direction { - -#if defined(__WIN95__) && USE_TRACK_BAR - int newPos = (int)::SendMessage((HWND) control, TBM_GETPOS, 0, 0); -#else - int newPos = position + nScrollInc; -#endif - if (!(newPos < GetMin() || newPos > GetMax())) - { - SetValue(newPos); - - wxScrollEvent event(scrollEvent, m_windowId); - event.SetPosition(newPos); - event.SetEventObject( this ); - GetEventHandler()->ProcessEvent(event); - } + if ( style & wxSL_LEFT ) + msStyle |= TBS_LEFT; + else if ( style & wxSL_RIGHT ) + msStyle |= TBS_RIGHT; + else if ( style & wxSL_TOP ) + msStyle |= TBS_TOP; + else if ( style & wxSL_BOTTOM ) + msStyle |= TBS_BOTTOM; } -} -void wxSlider::MSWOnHScroll(const WXWORD wParam, const WXWORD pos, const WXHWND control) -{ - MSWOnVScroll(wParam, pos, control); -} + if ( style & wxSL_AUTOTICKS ) + msStyle |= TBS_AUTOTICKS; + else + msStyle |= TBS_NOTICKS; -wxSlider::~wxSlider(void) -{ - if (m_staticMin) - DestroyWindow((HWND) m_staticMin); - if (m_staticMax) - DestroyWindow((HWND) m_staticMax); - if (m_staticValue) - DestroyWindow((HWND) m_staticValue); -} + if ( style & wxSL_SELRANGE ) + msStyle |= TBS_ENABLESELRANGE; -int wxSlider::GetValue(void) const -{ -#if defined(__WIN95__) && USE_TRACK_BAR - return ::SendMessage((HWND) GetHWND(), TBM_GETPOS, 0, 0); -#else - return ::GetScrollPos((HWND) GetHWND(), SB_CTL); -#endif + return msStyle; } -void wxSlider::SetValue(const int value) +wxSlider::~wxSlider() { -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage((HWND) GetHWND(), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)value); -#else - ::SetScrollPos((HWND) GetHWND(), SB_CTL, value, TRUE); -#endif - if (m_staticValue) - { - sprintf(wxBuffer, "%d", value); - SetWindowText((HWND) m_staticValue, wxBuffer); - } + delete m_labels; } -void wxSlider::GetSize(int *width, int *height) const +// ---------------------------------------------------------------------------- +// event handling +// ---------------------------------------------------------------------------- + +bool wxSlider::MSWOnScroll(int WXUNUSED(orientation), + WXWORD wParam, + WXWORD WXUNUSED(pos), + WXHWND control) { - RECT rect; - rect.left = -1; rect.right = -1; rect.top = -1; rect.bottom = -1; + wxEventType scrollEvent; + switch ( wParam ) + { + case SB_TOP: + scrollEvent = wxEVT_SCROLL_TOP; + break; + + case SB_BOTTOM: + scrollEvent = wxEVT_SCROLL_BOTTOM; + break; + + case SB_LINEUP: + scrollEvent = wxEVT_SCROLL_LINEUP; + break; + + case SB_LINEDOWN: + scrollEvent = wxEVT_SCROLL_LINEDOWN; + break; + + case SB_PAGEUP: + scrollEvent = wxEVT_SCROLL_PAGEUP; + break; + + case SB_PAGEDOWN: + scrollEvent = wxEVT_SCROLL_PAGEDOWN; + break; + + case SB_THUMBTRACK: + scrollEvent = wxEVT_SCROLL_THUMBTRACK; + m_isDragging = true; + break; + + case SB_THUMBPOSITION: + if ( m_isDragging ) + { + scrollEvent = wxEVT_SCROLL_THUMBRELEASE; + m_isDragging = false; + } + else + { + // this seems to only happen when the mouse wheel is used: in + // this case, as it might be unexpected to get THUMBRELEASE + // without preceding THUMBTRACKs, we don't generate it at all + // but generate CHANGED event because the control itself does + // not send us SB_ENDSCROLL for whatever reason when mouse + // wheel is used + scrollEvent = wxEVT_SCROLL_CHANGED; + } + break; + + case SB_ENDSCROLL: + scrollEvent = wxEVT_SCROLL_CHANGED; + break; + + default: + // unknown scroll event? + return false; + } + + int newPos = ValueInvertOrNot((int) ::SendMessage((HWND) control, TBM_GETPOS, 0, 0)); + if ( (newPos < GetMin()) || (newPos > GetMax()) ) + { + // out of range - but we did process it + return true; + } - wxFindMaxSize(GetHWND(), &rect); + SetValue(newPos); - if (m_staticMin) - wxFindMaxSize(m_staticMin, &rect); - if (m_staticMax) - wxFindMaxSize(m_staticMax, &rect); - if (m_staticValue) - wxFindMaxSize(m_staticValue, &rect); + wxScrollEvent event(scrollEvent, m_windowId); + event.SetPosition(newPos); + event.SetEventObject( this ); + HandleWindowEvent(event); - *width = rect.right - rect.left; - *height = rect.bottom - rect.top; + wxCommandEvent cevent( wxEVT_COMMAND_SLIDER_UPDATED, GetId() ); + cevent.SetInt( newPos ); + cevent.SetEventObject( this ); + + return HandleWindowEvent( cevent ); } -void wxSlider::GetPosition(int *x, int *y) const +void wxSlider::Command (wxCommandEvent & event) { - wxWindow *parent = GetParent(); - RECT rect; - rect.left = -1; rect.right = -1; rect.top = -1; rect.bottom = -1; + SetValue (event.GetInt()); + ProcessCommand (event); +} - wxFindMaxSize(GetHWND(), &rect); +// ---------------------------------------------------------------------------- +// geometry stuff +// ---------------------------------------------------------------------------- - if (m_staticMin) - wxFindMaxSize(m_staticMin, &rect); - if (m_staticMax) - wxFindMaxSize(m_staticMax, &rect); - if (m_staticValue) - wxFindMaxSize(m_staticValue, &rect); +wxRect wxSlider::GetBoundingBox() const +{ + // take care not to call our own functions which would call us recursively + int x, y, w, h; + wxSliderBase::DoGetPosition(&x, &y); + wxSliderBase::DoGetSize(&w, &h); - // Since we now have the absolute screen coords, - // if there's a parent we must subtract its top left corner - POINT point; - point.x = rect.left; - point.y = rect.top; - if (parent) - ::ScreenToClient((HWND) parent->GetHWND(), &point); + wxRect rect(x, y, w, h); + if ( m_labels ) + { + wxRect lrect = m_labels->GetBoundingBox(); + GetParent()->ScreenToClient(&lrect.x, &lrect.y); + rect.Union(lrect); + } - *x = point.x; - *y = point.y; + return rect; } -void wxSlider::SetSize(const int x, const int y, const int width, const int height, const int sizeFlags) +void wxSlider::DoGetSize(int *width, int *height) const { - int x1 = x; - int y1 = y; - int w1 = width; - int h1 = height; - - int currentX, currentY; - GetPosition(¤tX, ¤tY); - if (x == -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE)) - x1 = currentX; - if (y == -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE)) - y1 = currentY; - - char buf[300]; + wxRect rect = GetBoundingBox(); - int x_offset = x; - int y_offset = y; - - int cx; // slider,min,max sizes - int cy; - float cyf; + if ( width ) + *width = rect.width; + if ( height ) + *height = rect.height; +} - wxGetCharSize(GetHWND(), &cx, &cy,GetFont()); +void wxSlider::DoGetPosition(int *x, int *y) const +{ + wxRect rect = GetBoundingBox(); - if ((m_windowStyle & wxSL_VERTICAL) != wxSL_VERTICAL) - { - if ( m_windowStyle & wxSL_LABELS ) - { - float min_len = 0.0; + if ( x ) + *x = rect.x; + if ( y ) + *y = rect.y; +} - GetWindowText((HWND) m_staticMin, buf, 300); - GetTextExtent(buf, &min_len, &cyf,NULL,NULL, GetFont()); +int wxSlider::GetLabelsSize(int *widthMin, int *widthMax) const +{ + if ( widthMin && widthMax ) + { + *widthMin = GetTextExtent(Format(m_rangeMin)).x; + *widthMax = GetTextExtent(Format(m_rangeMax)).x; + } - float max_len = 0.0; + return HasFlag(wxSL_LABELS) ? GetCharHeight() : 0; +} - GetWindowText((HWND) m_staticMax, buf, 300); - GetTextExtent(buf, &max_len, &cyf,NULL,NULL, GetFont()); - if (m_staticValue) +void wxSlider::DoMoveWindow(int x, int y, int width, int height) +{ + // all complications below are because we need to position the labels, + // without them everything is easy + if ( !m_labels ) { - int new_width = (int)(wxMax(min_len, max_len)); - int valueHeight = (int)cyf; -#ifdef __WIN32__ - // For some reason, under Win95, the text edit control has - // a lot of space before the first character - new_width += 3*cx; -#endif -#if defined(__WIN95__) - // The height needs to be a bit bigger under Win95 if using native - // 3D effects. - valueHeight = (int) (valueHeight * 1.5) ; -#endif - MoveWindow((HWND) m_staticValue, x_offset, y_offset, new_width, valueHeight, TRUE); - x_offset += new_width + cx; + wxSliderBase::DoMoveWindow(x, y, width, height); + return; } - MoveWindow((HWND) m_staticMin, x_offset, y_offset, (int)min_len, cy, TRUE); - x_offset += (int)(min_len + cx); + int minLabelWidth, + maxLabelWidth; + const int labelHeight = GetLabelsSize(&minLabelWidth, &maxLabelWidth); + const int longestLabelWidth = wxMax(minLabelWidth, maxLabelWidth); + if ( !HasFlag(wxSL_MIN_MAX_LABELS) ) + { + minLabelWidth = + maxLabelWidth = 0; + } - int slider_length = (int)(w1 - x_offset - max_len - cx); + int labelOffset = 0; + int tickOffset = 0; + if ( HasFlag(wxSL_TICKS)) + tickOffset = TICK; + if ( HasFlag(wxSL_BOTH)) + tickOffset *= 2; + + // be careful to position the slider itself after moving the labels as + // otherwise our GetBoundingBox(), which is called from WM_SIZE handler, + // would return a wrong result and wrong size would be cached internally + if ( HasFlag(wxSL_VERTICAL) ) + { + int holdTopWidth; + int holdTopX; + int holdBottomWidth; + int holdBottomX; + int xLabel = (wxMax((THUMB + (BORDERPAD * 2)), longestLabelWidth) / 2) - + (longestLabelWidth / 2) + x; + if ( HasFlag(wxSL_LEFT) ) + { + if ( HasFlag(wxSL_MIN_MAX_LABELS) ) + { + holdTopX = xLabel; + holdTopWidth = minLabelWidth; + holdBottomX = xLabel - ((maxLabelWidth - minLabelWidth) / 2); + holdBottomWidth = maxLabelWidth; + if ( HasFlag(wxSL_INVERSE) ) + { + wxSwap(holdTopWidth, holdBottomWidth); + wxSwap(holdTopX, holdBottomX); + } + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min], + holdTopX, + y, + holdTopWidth, labelHeight); + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max], + holdBottomX, + y + height - labelHeight, + holdBottomWidth, labelHeight); + } + if ( HasFlag(wxSL_VALUE_LABEL) ) + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value], + x + THUMB + tickOffset + HGAP, + y + (height - labelHeight)/2, + longestLabelWidth, labelHeight); + } + else // wxSL_RIGHT + { + if ( HasFlag(wxSL_MIN_MAX_LABELS) ) + { + holdTopX = xLabel + longestLabelWidth + ((maxLabelWidth - minLabelWidth) / 2); + holdTopWidth = minLabelWidth; + holdBottomX = xLabel + longestLabelWidth; + holdBottomWidth = maxLabelWidth; + if ( HasFlag(wxSL_INVERSE) ) + { + wxSwap(holdTopWidth, holdBottomWidth); + wxSwap(holdTopX, holdBottomX); + } + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min], + holdTopX, + y, + holdTopWidth, labelHeight); + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max], + holdBottomX, + y + height - labelHeight, + holdBottomWidth, labelHeight); + } + if ( HasFlag(wxSL_VALUE_LABEL) ) + labelOffset = longestLabelWidth + HGAP; + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value], + x, + y + (height - labelHeight)/2, + longestLabelWidth, labelHeight); + } -#if defined(__WIN95__) && USE_TRACK_BAR - int slider_height = h1; - if (slider_height < 0 ) - slider_height = 20; -#else - int slider_height = cy; -#endif + // position the slider itself along the left/right edge + wxSliderBase::DoMoveWindow( + x + labelOffset, + y + labelHeight, + THUMB + tickOffset + HGAP, + height - (labelHeight * 2)); + } + else // horizontal + { + int holdLeftWidth; + int holdLeftX; + int holdRightWidth; + int holdRightX; + int yLabelMinMax = + (y + ((THUMB + tickOffset) / 2)) - (labelHeight / 2); + int xLabelValue = + x + minLabelWidth + + ((width - (minLabelWidth + maxLabelWidth)) / 2) - + (longestLabelWidth / 2); + + int ySlider = y; + + if ( HasFlag(wxSL_BOTTOM) ) + { + if ( HasFlag(wxSL_VALUE_LABEL) ) + { + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value], + xLabelValue, + y, + longestLabelWidth, labelHeight); + + ySlider += labelHeight; + yLabelMinMax += labelHeight; + } + + if ( HasFlag(wxSL_MIN_MAX_LABELS) ) + { + holdLeftX = x; + holdLeftWidth = minLabelWidth; + holdRightX = x + width - maxLabelWidth; + holdRightWidth = maxLabelWidth; + if ( HasFlag(wxSL_INVERSE) ) + { + wxSwap(holdLeftWidth, holdRightWidth); + wxSwap(holdLeftX, holdRightX); + } + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min], + holdLeftX, + yLabelMinMax, + holdLeftWidth, labelHeight); + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max], + holdRightX, + yLabelMinMax, + holdRightWidth, labelHeight); + } + } + else // wxSL_TOP + { + if ( HasFlag(wxSL_VALUE_LABEL) ) + { + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value], + xLabelValue, + y + THUMB + tickOffset, + longestLabelWidth, labelHeight); + } + + if ( HasFlag(wxSL_MIN_MAX_LABELS) ) + { + holdLeftX = x; + holdLeftWidth = minLabelWidth; + holdRightX = x + width - maxLabelWidth; + holdRightWidth = maxLabelWidth; + if ( HasFlag(wxSL_INVERSE) ) + { + wxSwap(holdLeftWidth, holdRightWidth); + wxSwap(holdLeftX, holdRightX); + } + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min], + holdLeftX, + yLabelMinMax, + holdLeftWidth, labelHeight); + DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max], + holdRightX, + yLabelMinMax, + holdRightWidth, labelHeight); + } + } - // Slider must have a minimum/default length/height - if (slider_length < 100) - slider_length = 100; + // position the slider itself along the top/bottom edge + if ( HasFlag(wxSL_MIN_MAX_LABELS) || HasFlag(wxSL_VALUE_LABEL) ) + labelOffset = labelHeight; + wxSliderBase::DoMoveWindow( + x + minLabelWidth + VGAP, + ySlider, + width - (minLabelWidth + maxLabelWidth + (VGAP*2)), + THUMB + tickOffset); + } +} - MoveWindow((HWND) GetHWND(), x_offset, y_offset, slider_length, slider_height, TRUE); - x_offset += slider_length + cx; +wxSize wxSlider::DoGetBestSize() const +{ + // this value is arbitrary: + static const int length = 100; - MoveWindow((HWND) m_staticMax, x_offset, y_offset, (int)max_len, cy, TRUE); - } - else - { - // No labels - if ( w1 < 0 ) - w1 = 200; - if ( h1 < 0 ) - h1 = 20; - MoveWindow((HWND) GetHWND(), x1, y1, w1, h1, TRUE); - } - } - else - { - if ( m_windowStyle & wxSL_LABELS ) - { - float min_len; - GetWindowText((HWND) m_staticMin, buf, 300); - GetTextExtent(buf, &min_len, &cyf,NULL,NULL,GetFont()); - - float max_len; - GetWindowText((HWND) m_staticMax, buf, 300); - GetTextExtent(buf, &max_len, &cyf,NULL,NULL, GetFont()); - - if (m_staticValue) + int *width; + wxSize size; + if ( HasFlag(wxSL_VERTICAL) ) { - int new_width = (int)(wxMax(min_len, max_len)); - int valueHeight = (int)cyf; -/*** Suggested change by George Tasker - remove this block... -#ifdef __WIN32__ - // For some reason, under Win95, the text edit control has - // a lot of space before the first character - new_width += 3*cx; -#endif - ... and replace with following line: */ - new_width += cx; + size.x = THUMB; + size.y = length; + width = &size.x; -#if defined(__WIN95__) - // The height needs to be a bit bigger under Win95 if using native - // 3D effects. - valueHeight = (int) (valueHeight * 1.5) ; -#endif + if ( m_labels ) + { + int widthMin, + widthMax; + int hLabel = GetLabelsSize(&widthMin, &widthMax); - MoveWindow((HWND) m_staticValue, x_offset, y_offset, new_width, valueHeight, TRUE); - y_offset += valueHeight; + // account for the labels + if ( HasFlag(wxSL_MIN_MAX_LABELS) ) + size.x += HGAP + wxMax(widthMin, widthMax); + + // labels are indented relative to the slider itself + size.y += hLabel; + } } + else // horizontal + { + size.x = length; + size.y = THUMB; + width = &size.y; - MoveWindow((HWND) m_staticMin, x_offset, y_offset, (int)min_len, cy, TRUE); - y_offset += cy; - - int slider_length = (int)(h1 - y_offset - cy - cy); -#if defined(__WIN95__) && USE_TRACK_BAR - int slider_width = w1; - if (slider_width < 0 ) - slider_width = 20; -#else - // Use character height as an estimate of slider width (yes, width) - int slider_width = cy; -#endif + if ( m_labels ) + { + int labelSize = GetLabelsSize(); - // Slider must have a minimum/default length - if (slider_length < 100) - slider_length = 100; + // Min/max labels are compensated by the ticks so we don't need + // extra space for them if we're also showing ticks. + if ( HasFlag(wxSL_MIN_MAX_LABELS) && !HasFlag(wxSL_TICKS) ) + size.y += labelSize; - MoveWindow((HWND) GetHWND(), x_offset, y_offset, slider_width, slider_length, TRUE); - y_offset += slider_length; + // The value label is always on top of the control and so does need + // extra space in any case. + if ( HasFlag(wxSL_VALUE_LABEL) ) + size.y += labelSize; + } + } - MoveWindow((HWND) m_staticMax, x_offset, y_offset, (int)max_len, cy, TRUE); + // need extra space to show ticks + if ( HasFlag(wxSL_TICKS) ) + { + *width += TICK; + // and maybe twice as much if we show them on both sides + if ( HasFlag(wxSL_BOTH) ) + *width += TICK; } - else - { - // No labels - if ( w1 < 0 ) - w1 = 20; - if ( h1 < 0 ) - h1 = 200; - MoveWindow((HWND) GetHWND(), x1, y1, w1, h1, TRUE); - } - } - -/* -#if WXWIN_COMPATIBILITY - GetEventHandler()->OldOnSize(width, height); -#else - wxSizeEvent event(wxSize(width, height), m_windowId); - event.eventObject = this; - GetEventHandler()->ProcessEvent(event); -#endif -*/ + return size; +} +// ---------------------------------------------------------------------------- +// slider-specific methods +// ---------------------------------------------------------------------------- + +int wxSlider::GetValue() const +{ + return ValueInvertOrNot(::SendMessage(GetHwnd(), TBM_GETPOS, 0, 0)); } -void wxSlider::SetRange(const int minValue, const int maxValue) +void wxSlider::SetValue(int value) { - m_rangeMin = minValue; - m_rangeMax = maxValue; + ::SendMessage(GetHwnd(), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)ValueInvertOrNot(value)); -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage((HWND) GetHWND(), TBM_SETRANGE, TRUE, MAKELONG(minValue, maxValue)); -#else - ::SetScrollRange((HWND) GetHWND(), SB_CTL, m_rangeMin, m_rangeMax, TRUE); -#endif - char buf[40]; - if ( m_staticMin ) - { - sprintf(buf, "%d", m_rangeMin); - SetWindowText((HWND) m_staticMin, buf); - } - - if ( m_staticMax ) - { - sprintf(buf, "%d", m_rangeMax); - SetWindowText((HWND) m_staticMax, buf); - } + if ( m_labels ) + { + ::SetWindowText((*m_labels)[SliderLabel_Value], Format(value).wx_str()); + } } -WXHBRUSH wxSlider::OnCtlColor(const WXHDC pDC, const WXHWND pWnd, const WXUINT nCtlColor, - WXUINT message, WXWPARAM wParam, WXLPARAM lParam) +void wxSlider::SetRange(int minValue, int maxValue) { - if ( nCtlColor == CTLCOLOR_SCROLLBAR ) - return 0; + // Remember the old logical value if we need to update the physical control + // value after changing its range in wxSL_INVERSE case (and avoid an + // unnecessary call to GetValue() otherwise as it's just not needed). + const int valueOld = HasFlag(wxSL_INVERSE) ? GetValue() : 0; - // Otherwise, it's a static - if (GetParent()->GetTransparentBackground()) - SetBkMode((HDC) pDC, TRANSPARENT); - else - SetBkMode((HDC) pDC, OPAQUE); + m_rangeMin = minValue; + m_rangeMax = maxValue; - ::SetBkColor((HDC) pDC, RGB(GetBackgroundColour().Red(), GetBackgroundColour().Green(), GetBackgroundColour().Blue())); - ::SetTextColor((HDC) pDC, RGB(GetForegroundColour().Red(), GetForegroundColour().Green(), GetForegroundColour().Blue())); + ::SendMessage(GetHwnd(), TBM_SETRANGEMIN, TRUE, m_rangeMin); + ::SendMessage(GetHwnd(), TBM_SETRANGEMAX, TRUE, m_rangeMax); - wxBrush *backgroundBrush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID); + if ( m_labels ) + { + ::SetWindowText((*m_labels)[SliderLabel_Min], + Format(ValueInvertOrNot(m_rangeMin)).wx_str()); + ::SetWindowText((*m_labels)[SliderLabel_Max], + Format(ValueInvertOrNot(m_rangeMax)).wx_str()); + } - // Note that this will be cleaned up in wxApp::OnIdle, if backgroundBrush - // has a zero usage count. -// backgroundBrush->RealizeResource(); - return (WXHBRUSH) backgroundBrush->GetResourceHandle(); + // When emulating wxSL_INVERSE style in wxWidgets, we need to update the + // value after changing the range to ensure that the value seen by the user + // code, i.e. the one returned by GetValue(), does not change. + if ( HasFlag(wxSL_INVERSE) ) + { + ::SendMessage(GetHwnd(), TBM_SETPOS, TRUE, ValueInvertOrNot(valueOld)); + } } -// For trackbars only -void wxSlider::SetTickFreq(const int n, const int pos) +void wxSlider::DoSetTickFreq(int n) { -#if defined(__WIN95__) && USE_TRACK_BAR - m_tickFreq = n; - ::SendMessage( (HWND) GetHWND(), TBM_SETTICFREQ, (WPARAM) n, (LPARAM) pos ); -#endif + m_tickFreq = n; + ::SendMessage( GetHwnd(), TBM_SETTICFREQ, (WPARAM) n, (LPARAM) 0 ); } -void wxSlider::SetPageSize(const int pageSize) +void wxSlider::SetPageSize(int pageSize) { -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage( (HWND) GetHWND(), TBM_SETPAGESIZE, (WPARAM) 0, (LPARAM) pageSize ); -#endif - m_pageSize = pageSize; + ::SendMessage( GetHwnd(), TBM_SETPAGESIZE, (WPARAM) 0, (LPARAM) pageSize ); + m_pageSize = pageSize; } -int wxSlider::GetPageSize(void) const +int wxSlider::GetPageSize() const { - return m_pageSize; + return m_pageSize; } -void wxSlider::ClearSel(void) +void wxSlider::ClearSel() { -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage( (HWND) GetHWND(), TBM_CLEARSEL, (WPARAM) TRUE, (LPARAM) 0 ); -#endif + ::SendMessage(GetHwnd(), TBM_CLEARSEL, (WPARAM) TRUE, (LPARAM) 0); } -void wxSlider::ClearTicks(void) +void wxSlider::ClearTicks() { -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage( (HWND) GetHWND(), TBM_CLEARTICS, (WPARAM) TRUE, (LPARAM) 0 ); -#endif + ::SendMessage(GetHwnd(), TBM_CLEARTICS, (WPARAM) TRUE, (LPARAM) 0); } -void wxSlider::SetLineSize(const int lineSize) +void wxSlider::SetLineSize(int lineSize) { - m_lineSize = lineSize; -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage( (HWND) GetHWND(), TBM_SETLINESIZE, (WPARAM) 0, (LPARAM) lineSize ); -#endif + m_lineSize = lineSize; + ::SendMessage(GetHwnd(), TBM_SETLINESIZE, (WPARAM) 0, (LPARAM) lineSize); } -int wxSlider::GetLineSize(void) const +int wxSlider::GetLineSize() const { -#if defined(__WIN95__) && USE_TRACK_BAR - return (int) ::SendMessage( (HWND) GetHWND(), TBM_GETLINESIZE, (WPARAM) 0, (LPARAM) 0 ); -#else - return m_lineSize; -#endif + return (int)::SendMessage(GetHwnd(), TBM_GETLINESIZE, 0, 0); } -int wxSlider::GetSelEnd(void) const +int wxSlider::GetSelEnd() const { -#if defined(__WIN95__) && USE_TRACK_BAR - return (int) ::SendMessage( (HWND) GetHWND(), TBM_SETSELEND, (WPARAM) 0, (LPARAM) 0 ); -#else - return 0; -#endif + return (int)::SendMessage(GetHwnd(), TBM_GETSELEND, 0, 0); } -int wxSlider::GetSelStart(void) const +int wxSlider::GetSelStart() const { -#if defined(__WIN95__) && USE_TRACK_BAR - return (int) ::SendMessage( (HWND) GetHWND(), TBM_GETSELSTART, (WPARAM) 0, (LPARAM) 0 ); -#else - return 0; -#endif + return (int)::SendMessage(GetHwnd(), TBM_GETSELSTART, 0, 0); } -void wxSlider::SetSelection(const int minPos, const int maxPos) +void wxSlider::SetSelection(int minPos, int maxPos) { -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage( (HWND) GetHWND(), TBM_SETSEL, (WPARAM) TRUE, (LPARAM) MAKELONG( minPos, maxPos) ); -#endif + ::SendMessage(GetHwnd(), TBM_SETSEL, + (WPARAM) TRUE /* redraw */, + (LPARAM) MAKELONG( minPos, maxPos) ); } -void wxSlider::SetThumbLength(const int len) +void wxSlider::SetThumbLength(int len) { -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage( (HWND) GetHWND(), TBM_SETTHUMBLENGTH, (WPARAM) len, (LPARAM) 0 ); -#endif + ::SendMessage(GetHwnd(), TBM_SETTHUMBLENGTH, (WPARAM) len, (LPARAM) 0); } -int wxSlider::GetThumbLength(void) const +int wxSlider::GetThumbLength() const { -#if defined(__WIN95__) && USE_TRACK_BAR - return (int) ::SendMessage( (HWND) GetHWND(), TBM_GETTHUMBLENGTH, (WPARAM) 0, (LPARAM) 0 ); -#else - return 0; -#endif + return (int)::SendMessage( GetHwnd(), TBM_GETTHUMBLENGTH, 0, 0); } -void wxSlider::SetTick(const int tickPos) +void wxSlider::SetTick(int tickPos) { -#if defined(__WIN95__) && USE_TRACK_BAR - ::SendMessage( (HWND) GetHWND(), TBM_SETTIC, (WPARAM) 0, (LPARAM) tickPos ); -#endif + ::SendMessage( GetHwnd(), TBM_SETTIC, (WPARAM) 0, (LPARAM) tickPos ); } -bool wxSlider::ContainsHWND(WXHWND hWnd) const -{ - return ( hWnd == GetStaticMin() || hWnd == GetStaticMax() || hWnd == GetEditValue() ); -} +// ---------------------------------------------------------------------------- +// composite control methods +// ---------------------------------------------------------------------------- -#if WXWIN_COMPATIBILITY -// Backward compatibility -void wxSlider::OnScroll(wxScrollEvent& event) +WXHWND wxSlider::GetStaticMin() const { - wxEventType oldEvent = event.GetEventType(); - event.SetEventType( wxEVT_COMMAND_SLIDER_UPDATED ); - if ( !GetEventHandler()->ProcessEvent(event) ) - { - event.SetEventType( oldEvent ); - if (!GetParent()->GetEventHandler()->ProcessEvent(event)) - event.Skip(); - } + return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Min] : NULL; } -#endif -void wxSlider::Command (wxCommandEvent & event) +WXHWND wxSlider::GetStaticMax() const { - SetValue (event.GetInt()); - ProcessCommand (event); + return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Max] : NULL; } -bool wxSlider::Show(const bool show) +WXHWND wxSlider::GetEditValue() const { - wxWindow::Show(show); - - int cshow; - if (show) - cshow = SW_SHOW; - else - cshow = SW_HIDE; - - if(m_staticValue) - ShowWindow((HWND) m_staticValue, (BOOL)cshow); - if(m_staticMin) - ShowWindow((HWND) m_staticMin, (BOOL)cshow); - if(m_staticMax) - ShowWindow((HWND) m_staticMax, (BOOL)cshow); - return TRUE; + return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Value] : NULL; } +WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxSlider, wxSliderBase, m_labels) +#endif // wxUSE_SLIDER