490 lines
13 KiB
Objective-C
490 lines
13 KiB
Objective-C
//
|
|
// PSMTabBarCell.m
|
|
// PSMTabBarControl
|
|
//
|
|
// Created by John Pannell on 10/13/05.
|
|
// Copyright 2005 Positive Spin Media. All rights reserved.
|
|
//
|
|
|
|
#import "PSMTabBarCell.h"
|
|
#import "PSMTabBarControl.h"
|
|
#import "PSMTabStyle.h"
|
|
#import "PSMProgressIndicator.h"
|
|
#import "PSMTabDragAssistant.h"
|
|
|
|
@interface PSMTabBarControl (Private)
|
|
- (void)update;
|
|
@end
|
|
|
|
@implementation PSMTabBarCell
|
|
|
|
#pragma mark -
|
|
#pragma mark Creation/Destruction
|
|
- (id)initWithControlView:(PSMTabBarControl *)controlView {
|
|
if((self = [super init])) {
|
|
_controlView = controlView;
|
|
_closeButtonTrackingTag = 0;
|
|
_cellTrackingTag = 0;
|
|
_closeButtonOver = NO;
|
|
_closeButtonPressed = NO;
|
|
_indicator = [[PSMProgressIndicator alloc] initWithFrame:NSMakeRect(0.0, 0.0, kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth)];
|
|
[_indicator setStyle:NSProgressIndicatorSpinningStyle];
|
|
[_indicator setAutoresizingMask:NSViewMinYMargin];
|
|
_hasCloseButton = YES;
|
|
_isCloseButtonSuppressed = NO;
|
|
_count = 0;
|
|
_countColor = nil;
|
|
_isEdited = NO;
|
|
_isPlaceholder = NO;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (id)initPlaceholderWithFrame:(NSRect)frame expanded:(BOOL)value inControlView:(PSMTabBarControl *)controlView {
|
|
if((self = [super init])) {
|
|
_controlView = controlView;
|
|
_isPlaceholder = YES;
|
|
if(!value) {
|
|
if([controlView orientation] == PSMTabBarHorizontalOrientation) {
|
|
frame.size.width = 0.0;
|
|
} else {
|
|
frame.size.height = 0.0;
|
|
}
|
|
}
|
|
[self setFrame:frame];
|
|
_closeButtonTrackingTag = 0;
|
|
_cellTrackingTag = 0;
|
|
_closeButtonOver = NO;
|
|
_closeButtonPressed = NO;
|
|
_indicator = nil;
|
|
_hasCloseButton = YES;
|
|
_isCloseButtonSuppressed = NO;
|
|
_count = 0;
|
|
_countColor = nil;
|
|
_isEdited = NO;
|
|
|
|
if(value) {
|
|
[self setCurrentStep:(kPSMTabDragAnimationSteps - 1)];
|
|
} else {
|
|
[self setCurrentStep:0];
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[_countColor release];
|
|
|
|
[_indicator removeFromSuperviewWithoutNeedingDisplay];
|
|
|
|
[_indicator release];
|
|
[super dealloc];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Accessors
|
|
|
|
- (id)controlView {
|
|
return _controlView;
|
|
}
|
|
|
|
- (void)setControlView:(id)view {
|
|
// no retain release pattern, as this simply switches a tab to another view.
|
|
_controlView = view;
|
|
}
|
|
|
|
- (NSTrackingRectTag)closeButtonTrackingTag {
|
|
return _closeButtonTrackingTag;
|
|
}
|
|
|
|
- (void)setCloseButtonTrackingTag:(NSTrackingRectTag)tag {
|
|
_closeButtonTrackingTag = tag;
|
|
}
|
|
|
|
- (NSTrackingRectTag)cellTrackingTag {
|
|
return _cellTrackingTag;
|
|
}
|
|
|
|
- (void)setCellTrackingTag:(NSTrackingRectTag)tag {
|
|
_cellTrackingTag = tag;
|
|
}
|
|
|
|
- (CGFloat)width {
|
|
return _frame.size.width;
|
|
}
|
|
|
|
- (NSRect)frame {
|
|
return _frame;
|
|
}
|
|
|
|
- (void)setFrame:(NSRect)rect {
|
|
_frame = rect;
|
|
|
|
//move the status indicator along with the rest of the cell
|
|
if(![[self indicator] isHidden] && ![_controlView isTabBarHidden]) {
|
|
[[self indicator] setFrame:[self indicatorRectForFrame:rect]];
|
|
}
|
|
}
|
|
|
|
- (void)setStringValue:(NSString *)aString {
|
|
[super setStringValue:aString];
|
|
_stringSize = [[self attributedStringValue] size];
|
|
// need to redisplay now - binding observation was too quick.
|
|
[_controlView update];
|
|
}
|
|
|
|
- (NSSize)stringSize {
|
|
return _stringSize;
|
|
}
|
|
|
|
- (NSAttributedString *)attributedStringValue {
|
|
return [(id < PSMTabStyle >)[_controlView style] attributedStringValueForTabCell:self];
|
|
}
|
|
|
|
- (NSInteger)tabState {
|
|
return _tabState;
|
|
}
|
|
|
|
- (void)setTabState:(NSInteger)state {
|
|
_tabState = state;
|
|
}
|
|
|
|
- (NSProgressIndicator *)indicator {
|
|
return _indicator;
|
|
}
|
|
|
|
- (BOOL)isInOverflowMenu {
|
|
return _isInOverflowMenu;
|
|
}
|
|
|
|
- (void)setIsInOverflowMenu:(BOOL)value {
|
|
if(_isInOverflowMenu != value) {
|
|
_isInOverflowMenu = value;
|
|
if([[[self controlView] delegate] respondsToSelector:@selector(tabView:tabViewItem:isInOverflowMenu:)]) {
|
|
[[[self controlView] delegate] tabView:[self controlView] tabViewItem:[self representedObject] isInOverflowMenu:_isInOverflowMenu];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)closeButtonPressed {
|
|
return _closeButtonPressed;
|
|
}
|
|
|
|
- (void)setCloseButtonPressed:(BOOL)value {
|
|
_closeButtonPressed = value;
|
|
}
|
|
|
|
- (BOOL)closeButtonOver {
|
|
return(_closeButtonOver && ([_controlView allowsBackgroundTabClosing] || ([self tabState] & PSMTab_SelectedMask) || [[NSApp currentEvent] modifierFlags] & NSCommandKeyMask));
|
|
}
|
|
|
|
- (void)setCloseButtonOver:(BOOL)value {
|
|
_closeButtonOver = value;
|
|
}
|
|
|
|
- (BOOL)hasCloseButton {
|
|
return _hasCloseButton;
|
|
}
|
|
|
|
- (void)setHasCloseButton:(BOOL)set;
|
|
{
|
|
_hasCloseButton = set;
|
|
}
|
|
|
|
- (void)setCloseButtonSuppressed:(BOOL)suppress;
|
|
{
|
|
_isCloseButtonSuppressed = suppress;
|
|
}
|
|
|
|
- (BOOL)isCloseButtonSuppressed;
|
|
{
|
|
return _isCloseButtonSuppressed;
|
|
}
|
|
|
|
- (BOOL)hasIcon {
|
|
return _hasIcon;
|
|
}
|
|
|
|
- (void)setHasIcon:(BOOL)value {
|
|
_hasIcon = value;
|
|
//[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
|
|
}
|
|
|
|
- (BOOL)hasLargeImage {
|
|
return _hasLargeImage;
|
|
}
|
|
|
|
- (void)setHasLargeImage:(BOOL)value {
|
|
_hasLargeImage = value;
|
|
}
|
|
|
|
|
|
- (NSInteger)count {
|
|
return _count;
|
|
}
|
|
|
|
- (void)setCount:(NSInteger)value {
|
|
_count = value;
|
|
//[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
|
|
}
|
|
|
|
- (NSColor *)countColor {
|
|
return _countColor;
|
|
}
|
|
|
|
- (void)setCountColor:(NSColor *)color {
|
|
[_countColor release];
|
|
_countColor = [color retain];
|
|
}
|
|
|
|
- (BOOL)isPlaceholder {
|
|
return _isPlaceholder;
|
|
}
|
|
|
|
- (void)setIsPlaceholder:(BOOL)value;
|
|
{
|
|
_isPlaceholder = value;
|
|
}
|
|
|
|
- (NSInteger)currentStep {
|
|
return _currentStep;
|
|
}
|
|
|
|
- (void)setCurrentStep:(NSInteger)value {
|
|
if(value < 0) {
|
|
value = 0;
|
|
}
|
|
|
|
if(value > (kPSMTabDragAnimationSteps - 1)) {
|
|
value = (kPSMTabDragAnimationSteps - 1);
|
|
}
|
|
|
|
_currentStep = value;
|
|
}
|
|
|
|
- (BOOL)isEdited {
|
|
return _isEdited;
|
|
}
|
|
|
|
- (void)setIsEdited:(BOOL)value {
|
|
_isEdited = value;
|
|
//[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Bindings
|
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
|
// the progress indicator, label, icon, or count has changed - redraw the control view
|
|
//[_controlView update];
|
|
//I seem to have run into some odd issue with update not being called at the right time. This seems to avoid the problem.
|
|
[_controlView performSelector:@selector(update) withObject:nil afterDelay:0.0];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Component Attributes
|
|
|
|
- (NSRect)indicatorRectForFrame:(NSRect)cellFrame {
|
|
return [(id < PSMTabStyle >)[_controlView style] indicatorRectForTabCell:self];
|
|
}
|
|
|
|
- (NSRect)closeButtonRectForFrame:(NSRect)cellFrame {
|
|
return [(id < PSMTabStyle >)[_controlView style] closeButtonRectForTabCell:self withFrame:cellFrame];
|
|
}
|
|
|
|
- (CGFloat)minimumWidthOfCell {
|
|
return [(id < PSMTabStyle >)[_controlView style] minimumWidthOfTabCell:self];
|
|
}
|
|
|
|
- (CGFloat)desiredWidthOfCell {
|
|
return [(id < PSMTabStyle >)[_controlView style] desiredWidthOfTabCell:self];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Drawing
|
|
|
|
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
|
|
if(_isPlaceholder) {
|
|
[[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
|
|
NSRectFillUsingOperation(cellFrame, NSCompositeSourceAtop);
|
|
return;
|
|
}
|
|
|
|
[(id < PSMTabStyle >)[_controlView style] drawTabCell:self];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Tracking
|
|
|
|
- (void)mouseEntered:(NSEvent *)theEvent {
|
|
// check for which tag
|
|
if([theEvent trackingNumber] == _closeButtonTrackingTag) {
|
|
_closeButtonOver = YES;
|
|
}
|
|
if([theEvent trackingNumber] == _cellTrackingTag) {
|
|
[self setHighlighted:YES];
|
|
[_controlView setNeedsDisplay:NO];
|
|
}
|
|
|
|
// scrubtastic
|
|
if([_controlView allowsScrubbing] && ([theEvent modifierFlags] & NSAlternateKeyMask)) {
|
|
[_controlView performSelector:@selector(tabClick:) withObject:self];
|
|
}
|
|
|
|
// tell the control we only need to redraw the affected tab
|
|
[_controlView setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)];
|
|
}
|
|
|
|
- (void)mouseExited:(NSEvent *)theEvent {
|
|
// check for which tag
|
|
if([theEvent trackingNumber] == _closeButtonTrackingTag) {
|
|
_closeButtonOver = NO;
|
|
}
|
|
|
|
if([theEvent trackingNumber] == _cellTrackingTag) {
|
|
[self setHighlighted:NO];
|
|
[_controlView setNeedsDisplay:NO];
|
|
}
|
|
|
|
//tell the control we only need to redraw the affected tab
|
|
[_controlView setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Drag Support
|
|
|
|
- (NSImage *)dragImage {
|
|
NSRect cellFrame = [(id < PSMTabStyle >)[(PSMTabBarControl *)_controlView style] dragRectForTabCell:self orientation:(PSMTabBarOrientation)[(PSMTabBarControl *)_controlView orientation]];
|
|
//NSRect cellFrame = [self frame];
|
|
|
|
[_controlView lockFocus];
|
|
NSBitmapImageRep *rep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:cellFrame] autorelease];
|
|
[_controlView unlockFocus];
|
|
NSImage *image = [[[NSImage alloc] initWithSize:[rep size]] autorelease];
|
|
[image addRepresentation:rep];
|
|
NSImage *returnImage = [[[NSImage alloc] initWithSize:[rep size]] autorelease];
|
|
[returnImage lockFocus];
|
|
[image compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeSourceOver fraction:1.0];
|
|
[returnImage unlockFocus];
|
|
if(![[self indicator] isHidden]) {
|
|
NSImage *pi = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"pi"]];
|
|
[returnImage lockFocus];
|
|
NSPoint indicatorPoint = NSMakePoint([self frame].size.width - MARGIN_X - kPSMTabBarIndicatorWidth, MARGIN_Y);
|
|
[pi compositeToPoint:indicatorPoint operation:NSCompositeSourceOver fraction:1.0];
|
|
[returnImage unlockFocus];
|
|
[pi release];
|
|
}
|
|
return returnImage;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Archiving
|
|
|
|
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
|
[super encodeWithCoder:aCoder];
|
|
if([aCoder allowsKeyedCoding]) {
|
|
[aCoder encodeRect:_frame forKey:@"frame"];
|
|
[aCoder encodeSize:_stringSize forKey:@"stringSize"];
|
|
[aCoder encodeInteger:_currentStep forKey:@"currentStep"];
|
|
[aCoder encodeBool:_isPlaceholder forKey:@"isPlaceholder"];
|
|
[aCoder encodeInteger:_tabState forKey:@"tabState"];
|
|
[aCoder encodeInteger:_closeButtonTrackingTag forKey:@"closeButtonTrackingTag"];
|
|
[aCoder encodeInteger:_cellTrackingTag forKey:@"cellTrackingTag"];
|
|
[aCoder encodeBool:_closeButtonOver forKey:@"closeButtonOver"];
|
|
[aCoder encodeBool:_closeButtonPressed forKey:@"closeButtonPressed"];
|
|
[aCoder encodeObject:_indicator forKey:@"indicator"];
|
|
[aCoder encodeBool:_isInOverflowMenu forKey:@"isInOverflowMenu"];
|
|
[aCoder encodeBool:_hasCloseButton forKey:@"hasCloseButton"];
|
|
[aCoder encodeBool:_isCloseButtonSuppressed forKey:@"isCloseButtonSuppressed"];
|
|
[aCoder encodeBool:_hasIcon forKey:@"hasIcon"];
|
|
[aCoder encodeBool:_hasLargeImage forKey:@"hasLargeImage"];
|
|
[aCoder encodeInteger:_count forKey:@"count"];
|
|
[aCoder encodeBool:_isEdited forKey:@"isEdited"];
|
|
}
|
|
}
|
|
|
|
- (id)initWithCoder:(NSCoder *)aDecoder {
|
|
self = [super initWithCoder:aDecoder];
|
|
if(self) {
|
|
if([aDecoder allowsKeyedCoding]) {
|
|
_frame = [aDecoder decodeRectForKey:@"frame"];
|
|
_stringSize = [aDecoder decodeSizeForKey:@"stringSize"];
|
|
_currentStep = [aDecoder decodeIntegerForKey:@"currentStep"];
|
|
_isPlaceholder = [aDecoder decodeBoolForKey:@"isPlaceholder"];
|
|
_tabState = [aDecoder decodeIntegerForKey:@"tabState"];
|
|
_closeButtonTrackingTag = [aDecoder decodeIntegerForKey:@"closeButtonTrackingTag"];
|
|
_cellTrackingTag = [aDecoder decodeIntegerForKey:@"cellTrackingTag"];
|
|
_closeButtonOver = [aDecoder decodeBoolForKey:@"closeButtonOver"];
|
|
_closeButtonPressed = [aDecoder decodeBoolForKey:@"closeButtonPressed"];
|
|
_indicator = [[aDecoder decodeObjectForKey:@"indicator"] retain];
|
|
_isInOverflowMenu = [aDecoder decodeBoolForKey:@"isInOverflowMenu"];
|
|
_hasCloseButton = [aDecoder decodeBoolForKey:@"hasCloseButton"];
|
|
_isCloseButtonSuppressed = [aDecoder decodeBoolForKey:@"isCloseButtonSuppressed"];
|
|
_hasIcon = [aDecoder decodeBoolForKey:@"hasIcon"];
|
|
_hasLargeImage = [aDecoder decodeBoolForKey:@"hasLargeImage"];
|
|
_count = [aDecoder decodeIntegerForKey:@"count"];
|
|
_isEdited = [aDecoder decodeBoolForKey:@"isEdited"];
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Accessibility
|
|
|
|
-(BOOL)accessibilityIsIgnored {
|
|
return NO;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
|
id attributeValue = nil;
|
|
|
|
if([attribute isEqualToString: NSAccessibilityRoleAttribute]) {
|
|
attributeValue = NSAccessibilityButtonRole;
|
|
} else if([attribute isEqualToString: NSAccessibilityHelpAttribute]) {
|
|
if([[[self controlView] delegate] respondsToSelector:@selector(accessibilityStringForTabView:objectCount:)]) {
|
|
attributeValue = [NSString stringWithFormat:@"%@, %lu %@", [self stringValue],
|
|
(unsigned long)[self count],
|
|
[[[self controlView] delegate] accessibilityStringForTabView:[[self controlView] tabView] objectCount:[self count]]];
|
|
} else {
|
|
attributeValue = [self stringValue];
|
|
}
|
|
} else if([attribute isEqualToString: NSAccessibilityFocusedAttribute]) {
|
|
attributeValue = [NSNumber numberWithBool:([self tabState] == 2)];
|
|
} else {
|
|
attributeValue = [super accessibilityAttributeValue:attribute];
|
|
}
|
|
|
|
return attributeValue;
|
|
}
|
|
|
|
- (NSArray *)accessibilityActionNames {
|
|
static NSArray *actions;
|
|
|
|
if(!actions) {
|
|
actions = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction, nil];
|
|
}
|
|
return actions;
|
|
}
|
|
|
|
- (NSString *)accessibilityActionDescription:(NSString *)action {
|
|
return NSAccessibilityActionDescription(action);
|
|
}
|
|
|
|
- (void)accessibilityPerformAction:(NSString *)action {
|
|
if([action isEqualToString:NSAccessibilityPressAction]) {
|
|
// this tab was selected
|
|
[_controlView performSelector:@selector(tabClick:) withObject:self];
|
|
}
|
|
}
|
|
|
|
- (id)accessibilityHitTest:(NSPoint)point {
|
|
return NSAccessibilityUnignoredAncestor(self);
|
|
}
|
|
|
|
- (id)accessibilityFocusedUIElement:(NSPoint)point {
|
|
return NSAccessibilityUnignoredAncestor(self);
|
|
}
|
|
|
|
@end
|