mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 11:10:49 +00:00
493 lines
11 KiB
Objective-C
493 lines
11 KiB
Objective-C
/** <title>NSStepperCell</title>
|
|
|
|
Copyright (C) 2001 Free Software Foundation, Inc.
|
|
|
|
Author: Pierre-Yves Rivaille <pyrivail@ens-lyon.fr>
|
|
Date: 2001
|
|
Author: Fred Kiefer <FredKiefer@gmx.de>
|
|
Date: August 2006
|
|
|
|
This file is part of the GNUstep GUI Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; see the file COPYING.LIB.
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#import <Foundation/NSValue.h>
|
|
#import "AppKit/NSApplication.h"
|
|
#import "AppKit/NSControl.h"
|
|
#import "AppKit/NSEvent.h"
|
|
#import "AppKit/NSStepperCell.h"
|
|
#import "AppKit/NSWindow.h"
|
|
#import "GNUstepGUI/GSTheme.h"
|
|
#import "GSGuiPrivate.h"
|
|
|
|
@interface NSStepperCell (Private)
|
|
- (void) _increment;
|
|
- (void) _decrement;
|
|
- (void) setHighlighted: (BOOL)highlight
|
|
upButton: (BOOL)upButton
|
|
withFrame: (NSRect)frame
|
|
inView: (NSView*)controlView;
|
|
@end
|
|
|
|
@implementation NSStepperCell
|
|
|
|
+ (void) initialize
|
|
{
|
|
if (self == [NSStepperCell class])
|
|
{
|
|
[self setVersion: 1];
|
|
}
|
|
}
|
|
|
|
+ (BOOL) prefersTrackingUntilMouseUp
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
- (id) init
|
|
{
|
|
self = [super init];
|
|
if (!self)
|
|
return nil;
|
|
|
|
[self setIntValue: 0];
|
|
[self setAlignment: NSRightTextAlignment];
|
|
[self setWraps: NO];
|
|
_autorepeat = YES;
|
|
_valueWraps = YES;
|
|
[self setMaxValue: 59];
|
|
[self setMinValue: 0];
|
|
_increment = 1;
|
|
|
|
highlightUp = NO;
|
|
highlightDown = NO;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (double) maxValue
|
|
{
|
|
return _maxValue;
|
|
}
|
|
|
|
- (void) setMaxValue: (double)maxValue
|
|
{
|
|
_maxValue = maxValue;
|
|
if ([self doubleValue] > _maxValue)
|
|
[self setDoubleValue: _maxValue];
|
|
}
|
|
|
|
- (double) minValue
|
|
{
|
|
return _minValue;
|
|
}
|
|
|
|
- (void) setMinValue: (double)minValue
|
|
{
|
|
_minValue = minValue;
|
|
if ([self doubleValue] < _minValue)
|
|
[self setDoubleValue: _minValue];
|
|
}
|
|
|
|
- (void) setObjectValue: (id)anObject
|
|
{
|
|
// NOTE: valueWraps has no effect on setObjectValue:
|
|
// FIXME: Copied from NSSliderCell.. can we share the code somehow?
|
|
|
|
// If the provided object doesn't respond to doubeValue, or our minValue
|
|
// is greater than our maxValue, we set our value to our minValue
|
|
// (this arbitrary choice matches OS X)
|
|
if ([anObject respondsToSelector: @selector(doubleValue)] == NO ||
|
|
_minValue > _maxValue)
|
|
[super setObjectValue: [NSNumber numberWithDouble: _minValue]];
|
|
else
|
|
{
|
|
double aDouble = [anObject doubleValue];
|
|
if (aDouble < _minValue)
|
|
[super setObjectValue: [NSNumber numberWithDouble: _minValue]];
|
|
else if (aDouble > _maxValue)
|
|
[super setObjectValue: [NSNumber numberWithDouble: _maxValue]];
|
|
else
|
|
[super setObjectValue: anObject];
|
|
}
|
|
}
|
|
|
|
- (double) increment
|
|
{
|
|
return _increment;
|
|
}
|
|
|
|
- (void) setIncrement: (double)increment
|
|
{
|
|
_increment = increment;
|
|
}
|
|
|
|
- (BOOL) autorepeat
|
|
{
|
|
return _autorepeat;
|
|
}
|
|
|
|
- (void) setAutorepeat: (BOOL)autorepeat
|
|
{
|
|
_autorepeat = autorepeat;
|
|
}
|
|
|
|
- (BOOL) valueWraps
|
|
{
|
|
return _valueWraps;
|
|
}
|
|
|
|
- (void) setValueWraps: (BOOL)valueWraps
|
|
{
|
|
_valueWraps = valueWraps;
|
|
}
|
|
|
|
- (void) drawInteriorWithFrame: (NSRect)cellFrame
|
|
inView: (NSView*)controlView
|
|
{
|
|
[[GSTheme theme] drawStepperCell: self
|
|
withFrame: cellFrame
|
|
inView: controlView
|
|
highlightUp: highlightUp
|
|
highlightDown: highlightDown];
|
|
}
|
|
|
|
- (void) getPeriodicDelay: (float*)delay interval: (float*)interval
|
|
{
|
|
*delay = 0.5;
|
|
*interval = 0.025;
|
|
}
|
|
|
|
- (BOOL) trackMouse: (NSEvent*)theEvent
|
|
inRect: (NSRect)cellFrame
|
|
ofView: (NSView*)controlView
|
|
untilMouseUp: (BOOL)flag
|
|
{
|
|
NSPoint location = [theEvent locationInWindow];
|
|
NSPoint point = [controlView convertPoint: location fromView: nil];
|
|
NSPoint last_point = point;
|
|
NSRect upRect;
|
|
NSRect downRect;
|
|
NSRect rect;
|
|
float delay;
|
|
float interval;
|
|
BOOL overButton = YES;
|
|
unsigned int event_mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
|
unsigned int periodCount = 0;
|
|
BOOL isDirectionUp;
|
|
BOOL autorepeat = [self autorepeat];
|
|
BOOL mouseWentUp = NO;
|
|
BOOL tracking;
|
|
|
|
_mouse_down_flags = [theEvent modifierFlags];
|
|
|
|
if ([self isEnabled] == NO)
|
|
return NO;
|
|
|
|
if (![controlView mouse: point inRect: cellFrame])
|
|
return NO; // point is not in cell
|
|
|
|
tracking = [self startTrackingAt: point inView: controlView];
|
|
|
|
if ([theEvent type] != NSLeftMouseDown)
|
|
return NO;
|
|
|
|
upRect = [[GSTheme theme] stepperUpButtonRectWithFrame: cellFrame];
|
|
downRect = [[GSTheme theme] stepperDownButtonRectWithFrame: cellFrame];
|
|
|
|
// Did the mouse go down in the up or in the down part?
|
|
if (NSMouseInRect(point, upRect, NO))
|
|
{
|
|
isDirectionUp = YES;
|
|
rect = upRect;
|
|
}
|
|
else if (NSMouseInRect(point, downRect, NO))
|
|
{
|
|
isDirectionUp = NO;
|
|
rect = downRect;
|
|
}
|
|
else
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
[self setHighlighted: YES
|
|
upButton: isDirectionUp
|
|
withFrame: cellFrame
|
|
inView: controlView];
|
|
|
|
if (autorepeat)
|
|
{
|
|
[self getPeriodicDelay: &delay interval: &interval];
|
|
[NSEvent startPeriodicEventsAfterDelay: delay withPeriod: interval];
|
|
event_mask |= NSPeriodicMask;
|
|
}
|
|
|
|
while (YES)
|
|
{
|
|
NSEventType eventType;
|
|
|
|
theEvent = [NSApp nextEventMatchingMask: event_mask
|
|
untilDate: [NSDate distantFuture]
|
|
inMode: NSEventTrackingRunLoopMode
|
|
dequeue: YES];
|
|
eventType = [theEvent type];
|
|
|
|
// Did the mouse go up?
|
|
if (eventType == NSLeftMouseUp)
|
|
{
|
|
mouseWentUp = YES;
|
|
break;
|
|
}
|
|
else if (eventType == NSPeriodic)
|
|
{
|
|
if (periodCount == 4)
|
|
{
|
|
if (isDirectionUp)
|
|
[self _increment];
|
|
else
|
|
[self _decrement];
|
|
|
|
[(NSControl*)controlView sendAction: [self action] to: [self target]];
|
|
location = [[controlView window] mouseLocationOutsideOfEventStream];
|
|
last_point = point;
|
|
point = [controlView convertPoint: location fromView: nil];
|
|
periodCount = 0;
|
|
}
|
|
else
|
|
{
|
|
periodCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
location = [theEvent locationInWindow];
|
|
last_point = point;
|
|
point = [controlView convertPoint: location fromView: nil];
|
|
}
|
|
|
|
if (!flag && ![controlView mouse: point inRect: cellFrame])
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (tracking)
|
|
{
|
|
tracking = [self continueTracking: last_point
|
|
at: point
|
|
inView: controlView];
|
|
}
|
|
|
|
if (NSMouseInRect(point, rect, NO) != overButton)
|
|
{
|
|
overButton = !overButton;
|
|
if (overButton && autorepeat)
|
|
{
|
|
[NSEvent startPeriodicEventsAfterDelay: delay withPeriod: interval];
|
|
periodCount = 0;
|
|
}
|
|
else
|
|
{
|
|
[NSEvent stopPeriodicEvents];
|
|
}
|
|
[self setHighlighted: overButton
|
|
upButton: isDirectionUp
|
|
withFrame: cellFrame
|
|
inView: controlView];
|
|
}
|
|
}
|
|
|
|
if (tracking)
|
|
{
|
|
[self stopTracking: last_point
|
|
at: point
|
|
inView: controlView
|
|
mouseIsUp: mouseWentUp];
|
|
}
|
|
|
|
if (overButton && autorepeat)
|
|
{
|
|
[NSEvent stopPeriodicEvents];
|
|
}
|
|
|
|
if (overButton)
|
|
{
|
|
if (isDirectionUp)
|
|
[self _increment];
|
|
else
|
|
[self _decrement];
|
|
[(NSControl*)controlView sendAction: [self action] to: [self target]];
|
|
}
|
|
|
|
[self setHighlighted: NO
|
|
upButton: isDirectionUp
|
|
withFrame: cellFrame
|
|
inView: controlView];
|
|
|
|
return mouseWentUp;
|
|
}
|
|
|
|
- (NSSize) cellSize
|
|
{
|
|
NSRect upRect = [[GSTheme theme]
|
|
stepperUpButtonRectWithFrame: [_control_view bounds]];
|
|
NSRect downRect = [[GSTheme theme]
|
|
stepperDownButtonRectWithFrame: [_control_view bounds]];
|
|
NSSize size;
|
|
|
|
if (upRect.size.width > downRect.size.width)
|
|
size.width = upRect.size.width;
|
|
else
|
|
size.width = downRect.size.width;
|
|
|
|
size.height = upRect.size.height + downRect.size.height;
|
|
|
|
return size;
|
|
}
|
|
|
|
//
|
|
// NSCoding protocol
|
|
//
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
{
|
|
[super encodeWithCoder: aCoder];
|
|
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
[aCoder encodeDouble: [self increment] forKey: @"NSIncrement"];
|
|
[aCoder encodeDouble: [self maxValue] forKey: @"NSMaxValue"];
|
|
[aCoder encodeDouble: [self minValue] forKey: @"NSMinValue"];
|
|
[aCoder encodeBool: [self autorepeat] forKey: @"NSAutorepeat"];
|
|
[aCoder encodeBool: [self valueWraps] forKey: @"NSValueWraps"];
|
|
}
|
|
else
|
|
{
|
|
NSInteger tmp;
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(double)
|
|
at: &_maxValue];
|
|
[aCoder encodeValueOfObjCType: @encode(double)
|
|
at: &_minValue];
|
|
[aCoder encodeValueOfObjCType: @encode(double)
|
|
at: &_increment];
|
|
tmp = _autorepeat;
|
|
encode_NSInteger(aCoder, &tmp);
|
|
tmp = _valueWraps;
|
|
encode_NSInteger(aCoder, &tmp);
|
|
}
|
|
}
|
|
|
|
- (id) initWithCoder: (NSCoder*)aDecoder
|
|
{
|
|
self = [super initWithCoder: aDecoder];
|
|
if (nil == self)
|
|
return nil;
|
|
|
|
if ([aDecoder allowsKeyedCoding])
|
|
{
|
|
_autorepeat = [aDecoder decodeBoolForKey: @"NSAutorepeat"];
|
|
_valueWraps = [aDecoder decodeBoolForKey: @"NSValueWraps"];
|
|
_increment = [aDecoder decodeDoubleForKey: @"NSIncrement"];
|
|
_maxValue = [aDecoder decodeDoubleForKey: @"NSMaxValue"];
|
|
if ([aDecoder containsValueForKey: @"NSMinValue"])
|
|
{
|
|
_minValue = [aDecoder decodeDoubleForKey: @"NSMinValue"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NSInteger tmp;
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(double)
|
|
at: &_maxValue];
|
|
[aDecoder decodeValueOfObjCType: @encode(double)
|
|
at: &_minValue];
|
|
[aDecoder decodeValueOfObjCType: @encode(double)
|
|
at: &_increment];
|
|
decode_NSInteger(aDecoder, &tmp);
|
|
_autorepeat = (BOOL)tmp;
|
|
decode_NSInteger(aDecoder, &tmp);
|
|
_valueWraps = (BOOL)tmp;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation NSStepperCell (Private)
|
|
|
|
- (void) _increment
|
|
{
|
|
double newValue;
|
|
double maxValue = [self maxValue];
|
|
double minValue = [self minValue];
|
|
double increment = [self increment];
|
|
|
|
newValue = [self doubleValue] + increment;
|
|
if ([self valueWraps])
|
|
{
|
|
if (newValue > maxValue)
|
|
newValue = newValue - maxValue + minValue - 1;
|
|
else if (newValue < minValue)
|
|
newValue = newValue + maxValue - minValue + 1;
|
|
}
|
|
[self setDoubleValue: newValue];
|
|
}
|
|
|
|
- (void) _decrement
|
|
{
|
|
double newValue;
|
|
double maxValue = [self maxValue];
|
|
double minValue = [self minValue];
|
|
double increment = [self increment];
|
|
|
|
newValue = [self doubleValue] - increment;
|
|
if ([self valueWraps])
|
|
{
|
|
if (newValue > maxValue)
|
|
newValue = newValue - maxValue + minValue - 1;
|
|
else if (newValue < minValue)
|
|
newValue = newValue + maxValue - minValue + 1;
|
|
}
|
|
[self setDoubleValue: newValue];
|
|
}
|
|
|
|
- (void) setHighlighted: (BOOL)highlight
|
|
upButton: (BOOL)upButton
|
|
withFrame: (NSRect)frame
|
|
inView: (NSView*)controlView
|
|
{
|
|
if (upButton)
|
|
{
|
|
highlightUp = highlight;
|
|
}
|
|
else
|
|
{
|
|
highlightDown = highlight;
|
|
}
|
|
|
|
[controlView setNeedsDisplayInRect: frame];
|
|
}
|
|
|
|
@end
|