1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/cocoa/slider.mm
3// Purpose:     wxSlider
4// Author:      David Elliott
5//              Mark Oxenham
6// Modified by:
7// Created:     2003/06/19
8// RCS-ID:      $Id: slider.mm 48590 2007-09-06 16:47:01Z DE $
9// Copyright:   (c) 2003 David Elliott
10//              (c) 2007 Software 2000 Ltd.
11// Licence:     wxWidgets licence
12/////////////////////////////////////////////////////////////////////////////
13
14#include "wx/wxprec.h"
15
16#if wxUSE_SLIDER
17
18#include "wx/slider.h"
19
20#ifndef WX_PRECOMP
21    #include "wx/app.h"
22#endif //WX_PRECOMP
23
24#import <Foundation/NSString.h>
25#include "wx/cocoa/objc/NSSlider.h"
26#import <AppKit/NSEvent.h>
27#import <AppKit/NSWindow.h>
28
29IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl)
30    BEGIN_EVENT_TABLE(wxSlider, wxSliderBase)
31END_EVENT_TABLE()
32WX_IMPLEMENT_COCOA_OWNER(wxSlider,NSSlider,NSControl,NSView)
33
34
35inline void AdjustDimension(
36                bool            isTicksStyle,
37                int             &pos,
38                wxSize          &size,
39                int             (wxSize::*GetDimension)() const,
40                void            (wxSize::*SetDimension)(int))
41{
42    const int dimension = (size.*GetDimension)();
43    const int minSize = (isTicksStyle) ? 23 : 20;
44
45    // prevent clipping of overly "thin" sliders
46    if (dimension < minSize)
47    {
48        (size.*SetDimension)(minSize);
49    }
50
51    // move the slider control to the middle of the dimension that is not
52    // being used to define its length
53    pos += (dimension - (size.*GetDimension)() + 1) / 2;
54}
55
56bool wxSlider::Create(wxWindow *parent, wxWindowID winid,
57            int value, int minValue, int maxValue,
58            const wxPoint& pos, const wxSize& size, long style,
59            const wxValidator& validator, const wxString& name)
60{
61    wxSize adjustedSize(size);
62    wxPoint adjustedPos(pos);
63    const bool isTicksStyle = (style & wxSL_TICKS) != 0;
64
65    if ((style & wxSL_HORIZONTAL) && (size.GetHeight() != wxDefaultCoord))
66    {
67        AdjustDimension(isTicksStyle, adjustedPos.y, adjustedSize, &wxSize::GetHeight, &wxSize::SetHeight);
68    }
69    else if ((style & wxSL_VERTICAL) && (size.GetWidth() != wxDefaultCoord))
70    {
71        AdjustDimension(isTicksStyle, adjustedPos.x, adjustedSize, &wxSize::GetWidth, &wxSize::SetWidth);
72    }
73    
74    if(!CreateControl(parent,winid,adjustedPos,adjustedSize,style,validator,name))
75        return false;
76    SetNSSlider([[WX_GET_OBJC_CLASS(WXNSSlider) alloc] initWithFrame: MakeDefaultNSRect(adjustedSize)]);
77    [m_cocoaNSView release];
78    
79    if(m_parent)
80        m_parent->CocoaAddChild(this);
81    SetInitialFrameRect(adjustedPos,adjustedSize);
82    
83    SetRange(minValue, maxValue);
84    SetValue(value);
85    
86    // -1 default for wxSL_AUTOTICKS == false
87    int tickMarks = -1;
88    // minValue > maxValue not handled, tickMarks set to 0
89    if ( style & wxSL_AUTOTICKS )
90        tickMarks = ((maxValue - minValue >= 0) ? (maxValue - minValue) : 0);
91    // arg2 needed a value, doesnt do anything
92    SetTickFreq(tickMarks,1);
93
94    return true;
95}
96
97wxSlider::~wxSlider()
98{
99    DisassociateNSSlider(GetNSSlider());
100}
101
102// NOTE: We don't derive from wxCocoaNSSlider in 2.8 due to ABI
103
104void wxSlider::AssociateNSSlider(WX_NSSlider theSlider)
105{
106    // Set the target/action.. we don't really need to unset these
107    [theSlider setTarget:wxCocoaNSControl::sm_cocoaTarget];
108    [theSlider setAction:@selector(wxNSControlAction:)];
109}
110
111void wxSlider::DisassociateNSSlider(WX_NSSlider theSlider)
112{
113}
114
115void wxSlider::ProcessEventType(wxEventType commandType)
116{
117    wxScrollEvent event(commandType, GetId(), GetValue(), HasFlag(wxSL_VERTICAL)?wxVERTICAL:wxHORIZONTAL);
118    event.SetEventObject(this);
119    GetEventHandler()->ProcessEvent(event);
120}
121
122static inline wxEventType wxSliderEventTypeForKeyFromEvent(NSEvent *theEvent)
123{
124    NSString *theEventCharacters = [theEvent charactersIgnoringModifiers];
125
126    if ([theEventCharacters length] == 1)
127    {
128        switch ([theEventCharacters characterAtIndex:0])
129        {
130            case NSUpArrowFunctionKey:
131            case NSRightArrowFunctionKey:   return wxEVT_SCROLL_PAGEDOWN;
132            case NSDownArrowFunctionKey:
133            case NSLeftArrowFunctionKey:    return wxEVT_SCROLL_PAGEUP;
134            case NSPageUpFunctionKey:       return wxEVT_SCROLL_BOTTOM;
135            case NSPageDownFunctionKey:     return wxEVT_SCROLL_TOP;
136        }
137    }
138    return wxEVT_NULL;
139}
140
141void wxSlider::CocoaTarget_action()
142{
143    wxEventType sliderEventType;
144    SEL theSelector = wxCocoaNSSlider::GetLastResponderSelector();
145    
146    if(         theSelector == @selector(moveUp:)
147            ||  theSelector == @selector(moveRight:))
148        sliderEventType = wxEVT_SCROLL_PAGEDOWN;
149    else if(    theSelector == @selector(moveDown:)
150            ||  theSelector == @selector(moveLeft:))
151        sliderEventType = wxEVT_SCROLL_PAGEUP;
152    else if(    theSelector == @selector(pageUp:))
153        sliderEventType = wxEVT_SCROLL_BOTTOM;
154    else if(    theSelector == @selector(pageDown:))
155        sliderEventType = wxEVT_SCROLL_TOP;
156    else if(    theSelector == @selector(keyDown:))
157        // This case should ideally never be reached.
158        sliderEventType = wxSliderEventTypeForKeyFromEvent([[GetNSSlider() window] currentEvent]);
159    else
160        // Don't generate an event.
161        return;
162    if(sliderEventType != wxEVT_NULL)
163        ProcessEventType(sliderEventType);
164}
165
166void wxSlider::CocoaNotification_startTracking(WX_NSNotification notification)
167{
168    CocoaNotification_continueTracking(notification);
169}
170
171void wxSlider::CocoaNotification_continueTracking(WX_NSNotification notification)
172{
173    const double realValue = [GetNSSlider() doubleValue];
174
175    if (realValue != [GetNSSlider() intValue])
176    {
177        SetValue(rint(realValue));
178    }
179
180    ProcessEventType(wxEVT_SCROLL_THUMBTRACK);
181}
182
183void wxSlider::CocoaNotification_stopTracking(WX_NSNotification notification)
184{
185    ProcessEventType(wxEVT_SCROLL_THUMBRELEASE);
186}
187
188int wxSlider::GetValue() const
189{
190    return [GetNSSlider() intValue];
191}
192
193void wxSlider::SetValue(int value)
194{
195    [GetNSSlider() setIntValue:value];
196}
197
198void wxSlider::SetRange(int minValue, int maxValue)
199{
200    [GetNSSlider() setMinValue:minValue];
201    [GetNSSlider() setMaxValue:maxValue];
202}
203
204int wxSlider::GetMin() const
205{
206    return [GetNSSlider() minValue];
207}
208
209int wxSlider::GetMax() const
210{
211    return [GetNSSlider() maxValue];
212}
213
214void wxSlider::SetTickFreq(int n, int pos)
215{
216    const int numTicks = (n > 0) ? ((GetMax() - GetMin()) / n) + 1 : 0;
217    [GetNSSlider() setNumberOfTickMarks:numTicks];
218}
219
220int wxSlider::GetTickFreq() const
221{
222    const int numTicks = [GetNSSlider() numberOfTickMarks];
223    return ((numTicks != 0) ? (GetMax() - GetMin()) / (numTicks - 1) : 0);
224}
225
226void wxSlider::SetTickPos(int pos)
227{
228    NSTickMarkPosition thePos = NSTickMarkBelow;
229    wxSize size = GetSize();
230
231    if (size.GetWidth() < size.GetHeight()) // NSSlider isVertical method can return -1 if it has not been displayed.
232    {
233        thePos = (pos != 1) ? NSTickMarkLeft : NSTickMarkRight;
234    }
235    else
236    {
237        thePos = (pos != 1) ? NSTickMarkBelow : NSTickMarkAbove;
238    }
239
240    [GetNSSlider() setTickMarkPosition:thePos];
241}
242
243void wxSlider::SetLineSize(int lineSize)
244{
245    // to do
246}
247
248void wxSlider::SetPageSize(int pageSize)
249{
250    // to do
251}
252
253int wxSlider::GetLineSize() const
254{
255    return 1;
256}
257
258int wxSlider::GetPageSize() const
259{
260    return 1;
261}
262
263int wxSlider::GetThumbLength() const
264{
265    return 1;
266}
267
268void wxSlider::SetThumbLength(int lenPixels)
269{
270    // to do
271}
272
273#endif // wxUSE_SLIDER
274