From ea59ac5528a27705be64c2db735424f7a887b17e Mon Sep 17 00:00:00 2001 From: ssp Date: Sun, 11 Oct 2009 02:11:40 +0200 Subject: [PATCH 1/4] Fix wrong data type in prototype. --- Plugin/CTFKillerVideo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugin/CTFKillerVideo.h b/Plugin/CTFKillerVideo.h index 4a852133..2950fa1e 100644 --- a/Plugin/CTFKillerVideo.h +++ b/Plugin/CTFKillerVideo.h @@ -109,7 +109,7 @@ enum CTFKVLookupStatus { - (NSString *) cleanURLString: (NSString*) URLString; - (BOOL) isVideoElementAvailable; - (void) finishedLookups; -- (BOOL) canPlayResponseResult: (NSURLResponse *) result; +- (BOOL) canPlayResponseResult: (NSHTTPURLResponse *) result; // Accessors - (BOOL) autoPlay; From 4fa5e84cc9503feee1926bcdd5905b12e695dd16 Mon Sep 17 00:00:00 2001 From: ssp Date: Sun, 11 Oct 2009 02:15:56 +0200 Subject: [PATCH 2/4] Move drawing and mouse handling to controls. Use separate NSButtons for the gear menu and the clicking on the whole view. This saves us the effort of handling the mouse movements. Splitting things up also separates the drawing more clearly and should be a first step towards making the plug-in accessible (as controls provide most of the AX stuff out of the box). --- ClickToFlash.xcodeproj/project.pbxproj | 12 + Plugin/CTFActionButton.h | 28 ++ Plugin/CTFActionButton.m | 156 +++++++ Plugin/CTFMainButton.h | 33 ++ Plugin/CTFMainButton.m | 380 +++++++++++++++ Plugin/Plugin.h | 3 +- Plugin/Plugin.m | 610 ++++++------------------- 7 files changed, 746 insertions(+), 476 deletions(-) create mode 100644 Plugin/CTFActionButton.h create mode 100644 Plugin/CTFActionButton.m create mode 100644 Plugin/CTFMainButton.h create mode 100644 Plugin/CTFMainButton.m diff --git a/ClickToFlash.xcodeproj/project.pbxproj b/ClickToFlash.xcodeproj/project.pbxproj index 50b56323..5b296bd5 100755 --- a/ClickToFlash.xcodeproj/project.pbxproj +++ b/ClickToFlash.xcodeproj/project.pbxproj @@ -45,6 +45,8 @@ 6953E4360F3EDE9D0014ECF7 /* CTFMenubarMenuController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6953E4340F3EDE9D0014ECF7 /* CTFMenubarMenuController.m */; }; 6953E43C0F3EDEB50014ECF7 /* MenubarMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6953E43A0F3EDEB50014ECF7 /* MenubarMenu.xib */; }; 69A26D0C0F302C10006648BC /* NSBezierPath-RoundedRectangle.m in Sources */ = {isa = PBXBuildFile; fileRef = 69A26D0B0F302C10006648BC /* NSBezierPath-RoundedRectangle.m */; }; + 6C09630C107FCA610006923A /* CTFActionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C09630B107FCA610006923A /* CTFActionButton.m */; }; + 6C09648010808D4B0006923A /* CTFMainButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C09647F10808D4B0006923A /* CTFMainButton.m */; }; 6C2C5A6A1068CE8700A90A54 /* Credits.css in Resources */ = {isa = PBXBuildFile; fileRef = A40485B110629B0E00FDC4E2 /* Credits.css */; }; 6C436DAF107830A200A0D525 /* QTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C436DAE107830A200A0D525 /* QTKit.framework */; }; 6C8E03FF10795EB900E21A50 /* CTFLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C8E03FE10795EB900E21A50 /* CTFLoader.m */; }; @@ -154,6 +156,10 @@ 6953E43B0F3EDEB50014ECF7 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Plugin/English.lproj/MenubarMenu.xib; sourceTree = ""; }; 69A26D0A0F302C10006648BC /* NSBezierPath-RoundedRectangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSBezierPath-RoundedRectangle.h"; path = "Plugin/NSBezierPath-RoundedRectangle.h"; sourceTree = ""; }; 69A26D0B0F302C10006648BC /* NSBezierPath-RoundedRectangle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBezierPath-RoundedRectangle.m"; path = "Plugin/NSBezierPath-RoundedRectangle.m"; sourceTree = ""; }; + 6C09630A107FCA610006923A /* CTFActionButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTFActionButton.h; path = Plugin/CTFActionButton.h; sourceTree = ""; }; + 6C09630B107FCA610006923A /* CTFActionButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CTFActionButton.m; path = Plugin/CTFActionButton.m; sourceTree = ""; }; + 6C09647E10808D4B0006923A /* CTFMainButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTFMainButton.h; path = Plugin/CTFMainButton.h; sourceTree = ""; }; + 6C09647F10808D4B0006923A /* CTFMainButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CTFMainButton.m; path = Plugin/CTFMainButton.m; sourceTree = ""; }; 6C436DAE107830A200A0D525 /* QTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QTKit.framework; path = /System/Library/Frameworks/QTKit.framework; sourceTree = ""; }; 6C8E03FD10795EB900E21A50 /* CTFLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTFLoader.h; path = Plugin/CTFLoader.h; sourceTree = ""; }; 6C8E03FE10795EB900E21A50 /* CTFLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CTFLoader.m; path = Plugin/CTFLoader.m; sourceTree = ""; }; @@ -246,6 +252,10 @@ children = ( 55EB703D0E04A84F0016593D /* Plugin.m */, 55EB703C0E04A84F0016593D /* Plugin.h */, + 6C09647F10808D4B0006923A /* CTFMainButton.m */, + 6C09647E10808D4B0006923A /* CTFMainButton.h */, + 6C09630B107FCA610006923A /* CTFActionButton.m */, + 6C09630A107FCA610006923A /* CTFActionButton.h */, 6C8E03FE10795EB900E21A50 /* CTFLoader.m */, 6C8E03FD10795EB900E21A50 /* CTFLoader.h */, 6C8EC6F310764F810053587F /* CTFKiller.m */, @@ -515,6 +525,8 @@ 6C8EC7E9107686780053587F /* CTFKillerSIFR.m in Sources */, 6CC18AE61076D09B00D9E1A0 /* CTFKillerVimeo.m in Sources */, 6C8E03FF10795EB900E21A50 /* CTFLoader.m in Sources */, + 6C09630C107FCA610006923A /* CTFActionButton.m in Sources */, + 6C09648010808D4B0006923A /* CTFMainButton.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Plugin/CTFActionButton.h b/Plugin/CTFActionButton.h new file mode 100644 index 00000000..291aa358 --- /dev/null +++ b/Plugin/CTFActionButton.h @@ -0,0 +1,28 @@ +// +// CTFActionButton.h +// ClickToFlash +// +// Created by Sven on 09.10.09. +// Copyright 2009 earthlingsoft. All rights reserved. +// + +#import + + + + +@interface CTFActionButton : NSButton { +} + ++ (id) actionButton; + +@end + + +@interface CTFActionButtonCell : NSButtonCell { + +} + +- (BOOL) gearVisible; + +@end \ No newline at end of file diff --git a/Plugin/CTFActionButton.m b/Plugin/CTFActionButton.m new file mode 100644 index 00000000..6d253080 --- /dev/null +++ b/Plugin/CTFActionButton.m @@ -0,0 +1,156 @@ +// +// CTFActionButton.m +// ClickToFlash +// +// Created by Sven on 09.10.09. +// Copyright 2009 earthlingsoft. All rights reserved. +// + +#import "CTFActionButton.h" +#import "CTFUtilities.h" + +static CGFloat padding = 3.; +static CGFloat leftMargin = 5.; +static CGFloat backgroundOpacity = .8; + + +@implementation CTFActionButton + ++ (id) actionButton { + CGFloat margin = 5.; + CGFloat size = 20.; + NSRect gearButtonRect = NSMakeRect( .0, .0, size + 2.*margin , size + 2.*margin ); + + CTFActionButton * gearButton = [[[CTFActionButton alloc] initWithFrame: gearButtonRect] autorelease]; + [gearButton setButtonType: NSMomentaryPushInButton]; + + return gearButton; +} + + + +#pragma mark NSButton subclassing + ++ (Class) cellClass { + return NSClassFromString(@"CTFActionButtonCell"); +} + + + +- (void) mouseDown: (NSEvent *) event { + [NSMenu popUpContextMenu:[self menuForEvent:event] withEvent:event forView:self]; +} + + + +- (NSMenu*) menuForEvent: (NSEvent*) event { + return [[self superview] menuForEvent: event]; +} + + + +- (void) resizeWithOldSuperviewSize:(NSSize) oldBoundsSize { + NSPoint newOrigin; + + if ( [[self cell ] gearVisible] ) { + NSSize superSize = [[self superview] bounds].size; + NSRect myRect = [self bounds]; + newOrigin = NSMakePoint(myRect.origin.x, superSize.height - myRect.size.height); + } + else { + newOrigin = NSMakePoint( -1000. , -1000. ); + } + + [self setFrameOrigin: newOrigin]; +} + + +@end + + + + + + +#pragma mark - +#pragma mark NSView subclassing + + + +@implementation CTFActionButtonCell + +#pragma mark NSCell subclassing + +- (void) drawWithFrame: (NSRect) rect inView:(NSView *) controlView { + NSRect bounds = [[self controlView] bounds]; + + NSImage * gearImage = [NSImage imageNamed:@"NSActionTemplate"]; + // On systems older than 10.5 we need to supply our own image. + if (gearImage == nil) { + NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"NSActionTemplate" ofType:@"png"]; + gearImage = [[[NSImage alloc] initWithContentsOfFile:path] autorelease]; + } + + if( gearImage ) { + CGFloat gearSize = [gearImage size].width; // assumes the gear to be square + CGFloat size = gearSize + 2.0 * padding; + CGFloat x = round(bounds.size.width * .5) - round(size * .5); + CGFloat y = round(bounds.size.height * .5 ) - round(size * .5); + NSRect backgroundFrame = NSMakeRect(x, y, size, size); + + NSBezierPath * circle = [NSBezierPath bezierPathWithOvalInRect:backgroundFrame]; + CGFloat alpha = ( [self isHighlighted] ) ? .9 : .7 ; + [[NSColor colorWithDeviceWhite:1.0 alpha:alpha] set]; + [circle fill]; + + // draw the gear image + [gearImage drawAtPoint:NSMakePoint(x + padding, y + padding) + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:.9]; + } +} + + + + +#pragma mark - +#pragma mark Helper + +- (BOOL) gearVisible { + NSRect bounds = [[[self controlView] superview] bounds ]; + return NSWidth( bounds ) > 32 && NSHeight( bounds ) > 32; +} + + + + + +#pragma mark - +#pragma mark Accessibility + +- (NSArray *) accessibilityAttributeNames { + NSMutableArray * attributes = [[[super accessibilityAttributeNames] mutableCopy] autorelease]; + [attributes addObject: NSAccessibilityDescriptionAttribute]; + return attributes; +} + + + +- (id) accessibilityAttributeValue: (NSString *) attribute { + id value; + + if ( [attribute isEqualToString: NSAccessibilityDescriptionAttribute] ) { + value = CtFLocalizedString( @"ClickTo Flash Contextual menu", @"Accessibility: CTFActionButton, Title of Contextual Menu"); + } + else if ( [attribute isEqualToString: NSAccessibilityParentAttribute] ){ + value = NSAccessibilityUnignoredAncestor([[self controlView] superview]); + } + else { + value = [super accessibilityAttributeValue:attribute]; + } + return value; +} + + +@end diff --git a/Plugin/CTFMainButton.h b/Plugin/CTFMainButton.h new file mode 100644 index 00000000..3cb31a4a --- /dev/null +++ b/Plugin/CTFMainButton.h @@ -0,0 +1,33 @@ +// +// CTFMainButton.h +// ClickToFlash +// +// Created by Sven on 10.10.09. +// Copyright 2009 earthlingsoft. All rights reserved. +// + +#import + + +@interface CTFMainButton : NSButton { + +} + +@end + + + +#pragma mark - + +@interface CTFMainButtonCell : NSButtonCell { + +} + +- (void) drawGradientInRect: (NSRect) rect; +- (void) drawPreviewInRect: (NSRect) rect; +- (void) drawBadgeForBounds: (NSRect) bounds; +- (void) drawGlossForBounds: (NSRect) bounds; + +- (NSString*) badgeLabelText; + +@end diff --git a/Plugin/CTFMainButton.m b/Plugin/CTFMainButton.m new file mode 100644 index 00000000..bc27a165 --- /dev/null +++ b/Plugin/CTFMainButton.m @@ -0,0 +1,380 @@ +// +// CTFMainButton.m +// ClickToFlash +// +// Created by Sven on 10.10.09. +// Copyright 2009 earthlingsoft. All rights reserved. +// + +#import "CTFMainButton.h" +#import "CTFGradient.h" +#import "Plugin.h" +#import "CTFUtilities.h" +#import "NSBezierPath-RoundedRectangle.h" + + + +@implementation CTFMainButton + ++ (Class) cellClass { + return NSClassFromString(@"CTFMainButtonCell"); +} + + +- (BOOL) isFlipped { + return NO; +} + + +- (NSMenu*) menuForEvent: (NSEvent*) event { + return [[self superview] menuForEvent: event]; +} + +@end + + + + + + +#pragma mark - + +@implementation CTFMainButtonCell + + +#pragma mark NSCell subclassing + +- (void) drawWithFrame: (NSRect) rect inView:(NSView *) controlView { + NSRect bounds = [[self controlView] bounds]; + NSRect fillRect = NSInsetRect(bounds, 1.0, 1.0); + + [self drawGradientInRect: fillRect]; + [self drawPreviewInRect: fillRect]; + + // Draw stroke + [[NSColor colorWithCalibratedWhite:0.0 alpha:0.50] set]; + [NSBezierPath setDefaultLineWidth:2.0]; + [NSBezierPath setDefaultLineCapStyle:NSSquareLineCapStyle]; + [[NSBezierPath bezierPathWithRect:bounds] stroke]; + + // Draw label + [self drawBadgeForBounds: bounds]; + + // Draw 'glossy' overlay which can give some visual feedback on clicks when an preview image is set. + if ([(CTFClickToFlashPlugin*)[controlView superview] previewImage] != nil) { + [self drawGlossForBounds: bounds]; + } + +} + + + +#pragma mark - +#pragma mark Drawing + +- (void) drawGradientInRect: (NSRect) rect { + NSColor *startingColor = [NSColor colorWithDeviceWhite: 1.0 alpha: 0.15]; + NSColor *endingColor = [NSColor colorWithDeviceWhite: 0.0 alpha: 0.15]; + + // When the view is 'On', use a convex look: draw the gradient downwards (90+180=270 degrees). + // When the view is 'Off' use a concave look: draw the gradient upwards (90 degrees). + CGFloat angle; + if ( [self isHighlighted] ) { + angle = 90.; + } + else { + angle = 270.; + } + + NSBezierPath * rectPath = [NSBezierPath bezierPathWithRect:rect]; + id gradient = [NSClassFromString(@"NSGradient") alloc]; + + if (gradient != nil) { + gradient = [gradient initWithStartingColor:startingColor endingColor:endingColor]; + [gradient drawInBezierPath:rectPath angle:angle]; + + [gradient release]; + } + else { + //tweak the opacity of the endingColor for compatibility with CTGradient + endingColor = [NSColor colorWithDeviceWhite:0.0 alpha:0.00]; + + gradient = [CTFGradient gradientWithBeginningColor:startingColor endingColor:endingColor]; + + //angle is reversed compared to NSGradient + [gradient fillBezierPath:rectPath angle:-angle]; + } +} + + +- (void) drawPreviewInRect: (NSRect) rect { + // Overlay the preview image if there is one + NSImage * image = [(CTFClickToFlashPlugin*) [[self controlView] superview] previewImage]; + + if ( image != nil ) { + // Determine the destination rect. The approach is to scale the preview image until it fills the view horizontally. This risks losing pixels at the top and bottom but seems to match what the sites providing preview images do for widescreen movies, thus giving better results than 'clean' scaling to fit the whole image inside the view. + NSRect destinationRect; + NSSize imageSize = [image size]; + CGFloat scale = rect.size.width / imageSize.width; + CGFloat destinationWidth = imageSize.width * scale; + CGFloat destinationHeight = imageSize.height * scale; + CGFloat destinationBottom = rect.origin.y + ( rect.size.height - destinationHeight) / 2.0; + + destinationRect = NSMakeRect(rect.origin.x, destinationBottom, destinationWidth, destinationHeight); + + [image drawInRect:destinationRect fromRect:NSZeroRect operation:NSCompositeSourceIn fraction: 0.8]; + } +} + + + + +- (NSString*) badgeLabelText +{ + NSString * labelText = nil; + CTFKiller * killer = [(CTFClickToFlashPlugin*)[[self controlView] superview] killer]; + + + if (killer != nil) { + labelText = [killer badgeLabelText]; + } + + if (labelText == nil) { + labelText = CtFLocalizedString( @"Flash", @"Flash badge text" ); + } + + return labelText; +} + + + +- (void) drawBadgeForBounds: (NSRect) bounds { + static BOOL _fromFlickr = NO; // have to figure this out + + // What and how are we going to draw? + const float kFrameXInset = 10; + const float kFrameYInset = 4; + const float kMinMargin = 11; + const float kMinHeight = 6; + + NSString* str = [ self badgeLabelText ]; + + NSShadow *superAwesomeShadow = [[NSShadow alloc] init]; + [superAwesomeShadow setShadowOffset:NSMakeSize(2.0, -2.0)]; + [superAwesomeShadow setShadowColor:[NSColor whiteColor]]; + [superAwesomeShadow autorelease]; + NSDictionary* attrs = [ NSDictionary dictionaryWithObjectsAndKeys: + [ NSFont boldSystemFontOfSize: 20 ], NSFontAttributeName, + [ NSNumber numberWithInt: -1 ], NSKernAttributeName, + [ NSColor blackColor ], NSForegroundColorAttributeName, + superAwesomeShadow, NSShadowAttributeName, + nil ]; + + // How large would this text be? + + NSSize strSize = [ str sizeWithAttributes: attrs ]; + + float w = strSize.width + kFrameXInset * 2; + float h = strSize.height + kFrameYInset * 2; + + // Compute a scale factor based on the view's size. + + float maxW = NSWidth( bounds ) - kMinMargin; + // the 9/10 factor here is to account for the 60% vertical top-biasing + float maxH = _fromFlickr ? NSHeight( bounds )*9/10 - kMinMargin : NSHeight( bounds ) - kMinMargin; + float minW = kMinHeight * w / h; + + BOOL rotate = NO; + if( maxW <= minW ) // too narrow in width, so rotate it + rotate = YES; + + if( rotate ) { // swap the dimensions to scale into + float temp = maxW; + maxW = maxH; + maxH = temp; + } + + if( maxH <= kMinHeight ) { + // Too short in height for full margin. + + // Draw at the smallest size, with less margin, + // unless even that would get clipped off. + + if( maxH + kMinMargin < kMinHeight ) + return; + + maxH = kMinHeight; + } + + float scaleFactor = 1.0; + + if( maxW < w ) + scaleFactor = maxW / w; + + if( maxH < h && maxH / h < scaleFactor ) + scaleFactor = maxH / h; + + // Apply the scale, and a transform so the result is centered in the view. + + [ NSGraphicsContext saveGraphicsState ]; + + NSAffineTransform* xform = [ NSAffineTransform transform ]; + // vertical top-bias by 60% here + if (_fromFlickr) { + [ xform translateXBy: NSWidth( bounds ) / 2 yBy: NSHeight( bounds ) / 10 * 6 ]; + } else { + [ xform translateXBy: NSWidth( bounds ) / 2 yBy: NSHeight( bounds ) / 2 ]; + } + + [ xform scaleBy: scaleFactor ]; + if( rotate ) + [ xform rotateByDegrees: 90 ]; + [ xform concat ]; + + CGContextRef context = [ [ NSGraphicsContext currentContext ] graphicsPort ]; + + CGFloat opacity = 0.45; + // Make Badge more opaque when we have a background image + if ( [(CTFClickToFlashPlugin*) [[self controlView] superview] previewImage] != nil ) { + opacity = 0.8; + } + + CGContextSetAlpha( context, ( [self isHighlighted] ) ? opacity : opacity - 0.15 ); + CGContextBeginTransparencyLayer( context, nil ); + + // Draw everything at full size, centered on the origin. + + NSPoint loc = { -strSize.width / 2, -strSize.height / 2 }; + NSRect borderRect = NSMakeRect( loc.x - kFrameXInset, loc.y - kFrameYInset, w, h ); + + NSBezierPath* fillPath = bezierPathWithRoundedRectCornerRadius( borderRect, 4 ); + [ [ NSColor colorWithCalibratedWhite: 1.0 alpha: opacity ] set ]; + [ fillPath fill ]; + + NSBezierPath* darkBorderPath = bezierPathWithRoundedRectCornerRadius( borderRect, 4 ); + [[NSColor blackColor] set]; + [ darkBorderPath setLineWidth: 3 ]; + [ darkBorderPath stroke ]; + + NSBezierPath* lightBorderPath = bezierPathWithRoundedRectCornerRadius( NSInsetRect(borderRect, -2, -2), 6 ); + [ [ NSColor colorWithCalibratedWhite: 1.0 alpha: opacity ] set ]; + [ lightBorderPath setLineWidth: 2 ]; + [ lightBorderPath stroke ]; + + [ str drawAtPoint: loc withAttributes: attrs ]; + + // Now restore the graphics state: + CGContextEndTransparencyLayer( context ); + [NSGraphicsContext restoreGraphicsState ]; +} + + + +- (void) drawGlossForBounds: (NSRect) bounds { + NSBezierPath * bP = [NSBezierPath bezierPath]; + const CGFloat glowStartFraction = .3; + const CGFloat cP1YFraction = .5; + const CGFloat cP2XFraction = .0; + const CGFloat cP2YFraction = .48; + + CGFloat startY = .0; + if ([self isHighlighted]) { + startY = NSMaxY(bounds); + } + + [bP moveToPoint: NSMakePoint( .0, startY ) ]; + [bP lineToPoint: NSMakePoint( .0, NSMaxY(bounds) * glowStartFraction )]; + [bP curveToPoint: NSMakePoint( NSMidX(bounds), NSMidY(bounds) ) + controlPoint1: NSMakePoint( .0 , cP1YFraction * NSMaxY(bounds)) + controlPoint2: NSMakePoint( cP2XFraction * NSMaxX(bounds), cP2YFraction * NSMaxY(bounds)) ]; + [bP curveToPoint: NSMakePoint( NSMaxX(bounds), (1. - glowStartFraction) * NSMaxY(bounds) ) + controlPoint1: NSMakePoint( (1. - cP2XFraction) * NSMaxX(bounds) , (1. - cP2YFraction) * NSMaxY(bounds) ) + controlPoint2: NSMakePoint( NSMaxX(bounds), (1. - cP1YFraction) * NSMaxY(bounds) ) ]; + [bP lineToPoint: NSMakePoint( NSMaxX(bounds), startY ) ]; + [bP closePath]; + + [[NSColor colorWithCalibratedWhite:1.0 alpha:0.07] set]; + [bP fill]; +} + + + + +#pragma mark - +#pragma mark Helper + +- (BOOL) gearVisibleInView: (NSView *) view { + NSRect bounds = [view bounds]; + return NSWidth( bounds ) > 32 && NSHeight( bounds ) > 32; +} + + + +#pragma mark - +#pragma mark Accessibility + +- (BOOL)accessibilityIsIgnored { + return NO; +} + + + +- (NSArray *) accessibilityAttributeNames { + NSMutableArray * attributes = [[[super accessibilityAttributeNames] mutableCopy] autorelease]; + [attributes addObject: NSAccessibilityTitleAttribute]; + [attributes addObject: NSAccessibilityDescriptionAttribute]; + return attributes; +} + + + +- (id) accessibilityAttributeValue: (NSString *) attribute { + id value = nil; + + if ( [attribute isEqualToString: NSAccessibilityTitleAttribute] ) { + value = [self badgeLabelText]; + } + else if ( [attribute isEqualToString: NSAccessibilityDescriptionAttribute] ) { + value = CtFLocalizedString( @"Load Flash Content", @"NSAccessibilityDescriptionAttribute for CTFMainButton"); + } + else if ( [attribute isEqualToString: NSAccessibilityParentAttribute] ){ + value = NSAccessibilityUnignoredAncestor([[self controlView] superview]); + } + else if ( [attribute isEqualToString: NSAccessibilityRoleAttribute] ) { + value = NSAccessibilityButtonRole; + } + else if ( [attribute isEqualToString: NSAccessibilityRoleDescriptionAttribute] ) { + value = NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil); + } + else { + value = [super accessibilityAttributeValue:attribute]; + } + + return value; +} + + + + - (NSArray *) accessibilityActionNames { + NSArray * actionNames = [NSArray arrayWithObjects: NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil]; + return actionNames; + } + + /* + - (void) accessibilityPerformAction: (NSString *) action { + if ( [action isEqualToString: NSAccessibilityPressAction] ) { + [self convertTypesForContainer]; + } + + else { + [super accessibilityPerformAction: action]; + } + } +*/ + + + + + + +@end diff --git a/Plugin/Plugin.h b/Plugin/Plugin.h index 645579e9..2a0af08c 100755 --- a/Plugin/Plugin.h +++ b/Plugin/Plugin.h @@ -38,7 +38,6 @@ THE SOFTWARE. id trackingArea; NSAlert* _activeAlert; BOOL mouseIsDown; - BOOL mouseInside; BOOL _isLoadingFromWhitelist; BOOL _fromYouTube; BOOL _fromFlickr; @@ -65,6 +64,8 @@ THE SOFTWARE. - (NSMenuItem*) addContextualMenuItemWithTitle: (NSString*) title action: (SEL) selector; - (NSMenuItem *) addContextualMenuItemWithTitle: (NSString*) title action: (SEL) selector target:(id) target; +- (IBAction) clicked: (id) sender; + - (IBAction)loadFlash:(id)sender; - (IBAction)loadAllOnPage:(id)sender; - (IBAction)removeFlash: (id) sender; diff --git a/Plugin/Plugin.m b/Plugin/Plugin.m index 98a32741..8210c771 100755 --- a/Plugin/Plugin.m +++ b/Plugin/Plugin.m @@ -27,33 +27,40 @@ of this software and associated documentation files (the "Software"), to deal #import "Plugin.h" #import "CTFUserDefaultsController.h" #import "CTFPreferencesDictionary.h" - -#import "MATrackingArea.h" #import "CTFMenubarMenuController.h" #import "CTFUtilities.h" #import "CTFWhitelist.h" -#import "NSBezierPath-RoundedRectangle.h" #import "CTFGradient.h" #import "SparkleManager.h" #import "CTFKiller.h" #import "CTFLoader.h" +#import "CTFActionButton.h" +#import "CTFMainButton.h" + #define LOGGING_ENABLED 0 - // MIME types +// MIME types static NSString *sFlashOldMIMEType = @"application/x-shockwave-flash"; static NSString *sFlashNewMIMEType = @"application/futuresplash"; - // CTFUserDefaultsController keys +// CTFUserDefaultsController keys static NSString *sAutoLoadInvisibleFlashViewsKey = @"autoLoadInvisibleViews"; static NSString *sPluginEnabled = @"pluginEnabled"; static NSString *sApplicationWhitelist = @"applicationWhitelist"; static NSString *sDrawGearImageOnlyOnMouseOverHiddenPref = @"drawGearImageOnlyOnMouseOver"; - // Info.plist key for app developers +// Info.plist key for app developers static NSString *sCTFOptOutKey = @"ClickToFlashOptOut"; -BOOL usingMATrackingArea = NO; +// Subview Tags +enum subviewTags { + CTFGradientViewTag, + CTFImageViewTag, + CTFMainButtonTag, + CTFActionButtonTag +}; + @interface CTFClickToFlashPlugin (Internal) - (void) _convertTypesForFlashContainer; @@ -62,10 +69,6 @@ - (void) _convertTypesForFlashContainerAfterDelay; - (void) _drawBackground; - (BOOL) _isOptionPressed; - (BOOL) _isCommandPressed; -- (void) _checkMouseLocation; -- (void) _addTrackingAreaForCTF; -- (void) _removeTrackingAreaForCTF; - - (void) _loadContent: (NSNotification*) notification; - (void) _loadContentForWindow: (NSNotification*) notification; @@ -308,10 +311,33 @@ - (id) initWithArguments:(NSDictionary *)arguments } [self setOriginalOpacityAttributes:originalOpacityDict]; - - [self _checkMouseLocation]; - [self _addTrackingAreaForCTF]; - } + + // Add main control button + CTFMainButton * mainButton = [[[CTFMainButton alloc] initWithFrame: [self bounds]] autorelease]; + [mainButton setTag: CTFMainButtonTag]; + [mainButton setAutoresizingMask: (NSViewHeightSizable | NSViewWidthSizable) ]; + [mainButton setButtonType: NSMomentaryPushInButton]; + [mainButton setTarget: self]; + [mainButton setAction: @selector(clicked:)]; + [self addSubview: mainButton]; + + /* // Doesn't work for us as NSImageView can only scale proportionally to _fit_ but not to _fit width_ + // Add Image View + NSImageView * imageView = [[[NSImageView alloc] initWithFrame: [self bounds]] autorelease]; + [imageView setTag: CTFImageViewTag]; + [imageView setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable]; + [imageView setImage: NSImageFrameNone]; + [imageView setImageAlignment: NSImageAlignCenter]; + [imageView setImageScaling: NSImageScaleProportionally]; + [self addSubview: imageView]; + */ + + // Add action button control + CTFActionButton * actionButton = [CTFActionButton actionButton]; + [actionButton setTag: CTFActionButtonTag]; + [actionButton setAutoresizingMask: NSViewMaxXMargin | NSViewMinYMargin]; + [self addSubview: actionButton]; + } return self; } @@ -320,7 +346,6 @@ - (id) initWithArguments:(NSDictionary *)arguments - (void)webPlugInDestroy { - [self _removeTrackingAreaForCTF]; [NSObject cancelPreviousPerformRequestsWithTarget:self]; [self _abortAlert]; // to be on the safe side @@ -357,114 +382,7 @@ - (void) dealloc } - -- (void) drawRect:(NSRect)rect -{ - if(!_isLoadingFromWhitelist) - [self _drawBackground]; -} - -- (BOOL) _gearVisible -{ - NSRect bounds = [ self bounds ]; - return NSWidth( bounds ) > 32 && NSHeight( bounds ) > 32; -} - -- (BOOL) mouseEventIsWithinGearIconBorders:(NSEvent *)event -{ - float margin = 5.0; - float gearImageHeight = 16.0; - float gearImageWidth = 16.0; - - BOOL xCoordWithinGearImage = NO; - BOOL yCoordWithinGearImage = NO; - - // if the view is 32 pixels or smaller in either direction, - // the gear image is not drawn, so we shouldn't pop-up the contextual - // menu on a single-click either - if ( [ self _gearVisible ] ) { - float viewHeight = NSHeight( [ self bounds ] ); - NSPoint mouseLocation = [event locationInWindow]; - NSPoint localMouseLocation = [self convertPoint:mouseLocation fromView:nil]; - - xCoordWithinGearImage = ( (localMouseLocation.x >= (0 + margin)) && - (localMouseLocation.x <= (0 + margin + gearImageWidth)) ); - - yCoordWithinGearImage = ( (localMouseLocation.y >= (viewHeight - margin - gearImageHeight)) && - (localMouseLocation.y <= (viewHeight - margin)) ); - } - - return (xCoordWithinGearImage && yCoordWithinGearImage); -} - -- (void) mouseDown:(NSEvent *)event -{ - if ([self mouseEventIsWithinGearIconBorders:event]) { - _contextMenuIsVisible = YES; - [NSMenu popUpContextMenu:[self menuForEvent:event] withEvent:event forView:self]; - } else { - mouseIsDown = YES; - mouseInside = YES; - [self setNeedsDisplay:YES]; - - // Track the mouse so that we can undo our pressed-in look if the user drags the mouse outside the view, and reinstate it if the user drags it back in. - //[self _addTrackingAreaForCTF]; - // Now that we track the mouse for mouse-over when the mouse is up - // for drawing the gear only on mouse-over, we don't need to add it here. - } -} - -- (void) mouseEntered:(NSEvent *)event -{ - mouseInside = YES; - [self setNeedsDisplay:YES]; -} -- (void) mouseExited:(NSEvent *)event -{ - mouseInside = NO; - [self setNeedsDisplay:YES]; -} - -- (void) mouseUp:(NSEvent *)event -{ - mouseIsDown = NO; - // Display immediately because we don't want to end up drawing after we've swapped in the Flash movie. - [self display]; - - // We're done tracking. - //[self _removeTrackingAreaForCTF]; - // Now that we track the mouse for mouse-over when the mouse is up - // for drawing the gear only on mouse-over, we don't remove it here. - - if (mouseInside && (! _contextMenuIsVisible) ) { - if ([self _isCommandPressed]) { - if ([self _isOptionPressed]) { - [self removeFlash:self]; - } else { - [self hideFlash:self]; - } - } else if ([self _isOptionPressed] && ![self _isHostWhitelisted]) { - [self _askToAddCurrentSiteToWhitelist]; - } else { - [self convertTypesForContainer]; - } - } else { - _contextMenuIsVisible = NO; - } -} - -- (BOOL) _isOptionPressed -{ - BOOL isOptionPressed = (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0); - return isOptionPressed; -} - -- (BOOL) _isCommandPressed -{ - BOOL isCommandPressed = (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) != 0); - return isCommandPressed; -} - + - (BOOL) isConsideredInvisible { int height = (int)([[self webView] frame].size.height); @@ -494,6 +412,12 @@ - (BOOL) isConsideredInvisible return NO; } + + + + + + #pragma mark - #pragma mark Contextual menu @@ -568,9 +492,28 @@ - (BOOL) validateMenuItem: (NSMenuItem *)menuItem return YES; } + + + + #pragma mark - #pragma mark Loading +- (IBAction) clicked: (id) sender { + if ([self _isCommandPressed]) { + if ([self _isOptionPressed]) { + [self removeFlash:self]; + } else { + [self hideFlash:self]; + } + } else if ([self _isOptionPressed] && ![self _isHostWhitelisted]) { + [self _askToAddCurrentSiteToWhitelist]; + } else { + [self convertTypesForContainer]; + } +} + + - (IBAction)removeFlash: (id) sender; { DOMCSSStyleDeclaration *style = [[self container] style]; @@ -593,6 +536,7 @@ - (IBAction)loadAllOnPage:(id)sender [[CTFMenubarMenuController sharedController] loadFlashForWindow: [self window]]; } + - (void) _loadContent: (NSNotification*) notification { [self convertTypesForContainer]; @@ -611,358 +555,7 @@ - (void) _loadInvisibleContentForWindow: (NSNotification*) notification } } -#pragma mark - -#pragma mark Drawing - -- (NSString*) badgeLabelText -{ - NSString * labelText = nil; - - if ([self killer] != nil) { - labelText = [[self killer] badgeLabelText]; - } - - if (labelText == nil) { - labelText = CtFLocalizedString( @"Flash", @"Flash badge text" ); - } - - return labelText; -} - - -- (void) _drawBadgeWithPressed: (BOOL) pressed -{ - // What and how are we going to draw? - - const float kFrameXInset = 10; - const float kFrameYInset = 4; - const float kMinMargin = 11; - const float kMinHeight = 6; - - NSString* str = [ self badgeLabelText ]; - - NSShadow *superAwesomeShadow = [[NSShadow alloc] init]; - [superAwesomeShadow setShadowOffset:NSMakeSize(2.0, -2.0)]; - [superAwesomeShadow setShadowColor:[NSColor whiteColor]]; - [superAwesomeShadow autorelease]; - NSDictionary* attrs = [ NSDictionary dictionaryWithObjectsAndKeys: - [ NSFont boldSystemFontOfSize: 20 ], NSFontAttributeName, - [ NSNumber numberWithInt: -1 ], NSKernAttributeName, - [ NSColor blackColor ], NSForegroundColorAttributeName, - superAwesomeShadow, NSShadowAttributeName, - nil ]; - - // Set up for drawing. - - NSRect bounds = [ self bounds ]; - - // How large would this text be? - - NSSize strSize = [ str sizeWithAttributes: attrs ]; - - float w = strSize.width + kFrameXInset * 2; - float h = strSize.height + kFrameYInset * 2; - - // Compute a scale factor based on the view's size. - - float maxW = NSWidth( bounds ) - kMinMargin; - // the 9/10 factor here is to account for the 60% vertical top-biasing - float maxH = _fromFlickr ? NSHeight( bounds )*9/10 - kMinMargin : NSHeight( bounds ) - kMinMargin; - float minW = kMinHeight * w / h; - - BOOL rotate = NO; - if( maxW <= minW ) // too narrow in width, so rotate it - rotate = YES; - - if( rotate ) { // swap the dimensions to scale into - float temp = maxW; - maxW = maxH; - maxH = temp; - } - - if( maxH <= kMinHeight ) { - // Too short in height for full margin. - - // Draw at the smallest size, with less margin, - // unless even that would get clipped off. - - if( maxH + kMinMargin < kMinHeight ) - return; - - maxH = kMinHeight; - } - - float scaleFactor = 1.0; - - if( maxW < w ) - scaleFactor = maxW / w; - - if( maxH < h && maxH / h < scaleFactor ) - scaleFactor = maxH / h; - - // Apply the scale, and a transform so the result is centered in the view. - - [ NSGraphicsContext saveGraphicsState ]; - - NSAffineTransform* xform = [ NSAffineTransform transform ]; - // vertical top-bias by 60% here - if (_fromFlickr) { - [ xform translateXBy: NSWidth( bounds ) / 2 yBy: NSHeight( bounds ) / 10 * 6 ]; - } else { - [ xform translateXBy: NSWidth( bounds ) / 2 yBy: NSHeight( bounds ) / 2 ]; - } - [ xform scaleBy: scaleFactor ]; - if( rotate ) - [ xform rotateByDegrees: 90 ]; - [ xform concat ]; - - CGContextRef context = [ [ NSGraphicsContext currentContext ] graphicsPort ]; - - CGFloat opacity = 0.45; - // Make Badge more opaque when we have a background image - if ( [self previewImage] != nil ) { - opacity = 0.8; - } - - CGContextSetAlpha( context, pressed ? opacity : opacity - 0.15 ); - CGContextBeginTransparencyLayer( context, nil ); - - // Draw everything at full size, centered on the origin. - - NSPoint loc = { -strSize.width / 2, -strSize.height / 2 }; - NSRect borderRect = NSMakeRect( loc.x - kFrameXInset, loc.y - kFrameYInset, w, h ); - - NSBezierPath* fillPath = bezierPathWithRoundedRectCornerRadius( borderRect, 4 ); - [ [ NSColor colorWithCalibratedWhite: 1.0 alpha: opacity ] set ]; - [ fillPath fill ]; - - NSBezierPath* darkBorderPath = bezierPathWithRoundedRectCornerRadius( borderRect, 4 ); - [[NSColor blackColor] set]; - [ darkBorderPath setLineWidth: 3 ]; - [ darkBorderPath stroke ]; - - NSBezierPath* lightBorderPath = bezierPathWithRoundedRectCornerRadius( NSInsetRect(borderRect, -2, -2), 6 ); - [ [ NSColor colorWithCalibratedWhite: 1.0 alpha: opacity ] set ]; - [ lightBorderPath setLineWidth: 2 ]; - [ lightBorderPath stroke ]; - - [ str drawAtPoint: loc withAttributes: attrs ]; - - // Now restore the graphics state: - - CGContextEndTransparencyLayer( context ); - - [ NSGraphicsContext restoreGraphicsState ]; -} - -- (void) _drawGearIcon -{ - // add the gear for the contextual menu, but only if the view is - // greater than a certain size - - if ([self _gearVisible]) { - NSRect bounds = [ self bounds ]; - - float margin = 5.0; - NSImage *gearImage = [NSImage imageNamed:@"NSActionTemplate"]; - // On systems older than 10.5 we need to supply our own image. - if (gearImage == nil) - { - NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"NSActionTemplate" ofType:@"png"]; - gearImage = [[[NSImage alloc] initWithContentsOfFile:path] autorelease]; - } - - if( gearImage ) { - CGContextRef context = [ [ NSGraphicsContext currentContext ] graphicsPort ]; - - CGContextSetAlpha( context, 0.65 ); - CGContextBeginTransparencyLayer( context, nil ); - - CGFloat padding = 3.0; - NSPoint gearImageCenter = NSMakePoint(NSMinX( bounds ) + ( padding + margin + [gearImage size].width/2 ), - NSMaxY( bounds ) - ( padding + margin + [gearImage size].height/2 )); - - NSRect backgroundFrame = NSMakeRect(NSMinX(bounds) + margin, - NSMaxY(bounds) - margin - [gearImage size].height - 2.0 * padding, - [gearImage size].width + 2.0 * padding, - [gearImage size].height + 2.0 * padding ); - - NSBezierPath * circle = [NSBezierPath bezierPathWithOvalInRect:backgroundFrame]; - [[NSColor whiteColor] set]; - [circle fill]; - - // draw the gear image - [gearImage drawAtPoint:NSMakePoint(gearImageCenter.x - [gearImage size].width/2, - gearImageCenter.y - [gearImage size].height/2) - fromRect:NSZeroRect - operation:NSCompositeSourceOver - fraction:1.0]; - - CGContextEndTransparencyLayer( context ); - } - } -} - - -- (void) drawGlossyWithPressed: (BOOL) pressed { - NSRect bounds = [self bounds]; - - NSBezierPath * bP = [NSBezierPath bezierPath]; - const CGFloat glowStartFraction = .3; - const CGFloat cP1YFraction = .5; - const CGFloat cP2XFraction = .0; - const CGFloat cP2YFraction = .48; - - CGFloat startY = .0; - if (pressed) { - startY = NSMaxY(bounds); - } - - [bP moveToPoint: NSMakePoint( .0, startY ) ]; - [bP lineToPoint: NSMakePoint( .0, NSMaxY(bounds) * glowStartFraction )]; - [bP curveToPoint: NSMakePoint( NSMidX(bounds), NSMidY(bounds) ) - controlPoint1: NSMakePoint( .0 , cP1YFraction * NSMaxY(bounds)) - controlPoint2: NSMakePoint( cP2XFraction * NSMaxX(bounds), cP2YFraction * NSMaxY(bounds)) ]; - [bP curveToPoint: NSMakePoint( NSMaxX(bounds), (1. - glowStartFraction) * NSMaxY(bounds) ) - controlPoint1: NSMakePoint( (1. - cP2XFraction) * NSMaxX(bounds) , (1. - cP2YFraction) * NSMaxY(bounds) ) - controlPoint2: NSMakePoint( NSMaxX(bounds), (1. - cP1YFraction) * NSMaxY(bounds) ) ]; - [bP lineToPoint: NSMakePoint( NSMaxX(bounds), startY ) ]; - [bP closePath]; - - [[NSColor colorWithCalibratedWhite:1.0 alpha:0.07] set]; - [bP fill]; -} - - - -- (void) _drawBackground -{ - NSRect selfBounds = [self bounds]; - - NSRect fillRect = NSInsetRect(selfBounds, 1.0, 1.0); - NSRect strokeRect = selfBounds; - - NSColor *startingColor = [NSColor colorWithDeviceWhite:1.0 alpha:0.15]; - NSColor *endingColor = [NSColor colorWithDeviceWhite:0.0 alpha:0.15]; - - // When the mouse is up or outside the view, we want a convex look, so we draw the gradient downward (90+180=270 degrees). - // When the mouse is down and inside the view, we want a concave look, so we draw the gradient upward (90 degrees). - id gradient = [NSClassFromString(@"NSGradient") alloc]; - if (gradient != nil) - { - gradient = [gradient initWithStartingColor:startingColor endingColor:endingColor]; - - [gradient drawInBezierPath:[NSBezierPath bezierPathWithRect:fillRect] angle:90.0 + ((mouseIsDown && mouseInside) ? 0.0 : 180.0)]; - - [gradient release]; - } - else - { - //tweak the opacity of the endingColor for compatibility with CTGradient - endingColor = [NSColor colorWithDeviceWhite:0.0 alpha:0.00]; - - gradient = [CTFGradient gradientWithBeginningColor:startingColor - endingColor:endingColor]; - - //angle is reversed compared to NSGradient - [gradient fillBezierPath:[NSBezierPath bezierPathWithRect:fillRect] angle:-90.0 - ((mouseIsDown && mouseInside) ? 0.0 : 180.0)]; - - //CTGradient instances are returned autoreleased - no need for explicit release here - } - - // Overlay the preview image if there is one - NSImage * image = [self previewImage]; - if ( image != nil ) { - // Determine the destination rect. The approach is to scale the preview image until it fills the view horizontally. This risks losing pixels at the top and bottom but seems to match what the sites providing preview images do, thus giving better results than 'clean' scaling to fit the whole image inside the view. - NSRect destinationRect; - NSSize imageSize = [image size]; - CGFloat scale = fillRect.size.width / imageSize.width; - CGFloat destinationWidth = imageSize.width * scale; - CGFloat destinationHeight = imageSize.height * scale; - CGFloat destinationBottom = fillRect.origin.y + ( fillRect.size.height - destinationHeight) / 2.0; - - destinationRect = NSMakeRect(fillRect.origin.x, destinationBottom, destinationWidth, destinationHeight); - - [[self previewImage] drawInRect:destinationRect fromRect:NSZeroRect operation:NSCompositeSourceIn fraction: 0.8]; - } - - // Draw stroke - [[NSColor colorWithCalibratedWhite:0.0 alpha:0.50] set]; - [NSBezierPath setDefaultLineWidth:2.0]; - [NSBezierPath setDefaultLineCapStyle:NSSquareLineCapStyle]; - [[NSBezierPath bezierPathWithRect:strokeRect] stroke]; - - // Draw label - [self _drawBadgeWithPressed: mouseIsDown && mouseInside ]; - - // Draw 'glossy' overlay which can give some visual feedback on clicks when an preview image is set. - if ([self previewImage] != nil) { - [self drawGlossyWithPressed: mouseIsDown && mouseInside]; - } - - // Draw the gear icon - if ([[CTFUserDefaultsController standardUserDefaults] boolForKey:sDrawGearImageOnlyOnMouseOverHiddenPref]) { - if( mouseInside && !mouseIsDown ) - [ self _drawGearIcon ]; - } else { - [ self _drawGearIcon ]; - } -} - -- (void) _checkMouseLocation -{ - NSPoint mouseLoc = [NSEvent mouseLocation]; - - BOOL nowInside = NSPointInRect(mouseLoc, [_webView bounds]); - if (nowInside) { - mouseInside = YES; - } else { - mouseInside = NO; - } -} - -- (void) _addTrackingAreaForCTF -{ - if (trackingArea) - return; - - trackingArea = [NSClassFromString(@"NSTrackingArea") alloc]; - if (trackingArea != nil) - { - [(MATrackingArea *)trackingArea initWithRect:[self bounds] - options:MATrackingMouseEnteredAndExited | MATrackingActiveInKeyWindow | MATrackingEnabledDuringMouseDrag | MATrackingInVisibleRect - owner:self - userInfo:nil]; - [self addTrackingArea:trackingArea]; - } - else - { - trackingArea = [NSClassFromString(@"MATrackingArea") alloc]; - [(MATrackingArea *)trackingArea initWithRect:[self bounds] - options:MATrackingMouseEnteredAndExited | MATrackingActiveInKeyWindow | MATrackingEnabledDuringMouseDrag | MATrackingInVisibleRect - owner:self - userInfo:nil]; - [MATrackingArea addTrackingArea:trackingArea toView:self]; - usingMATrackingArea = YES; - } -} -- (void) _removeTrackingAreaForCTF -{ - if (trackingArea) - { - if (usingMATrackingArea) - { - [MATrackingArea removeTrackingArea:trackingArea fromView:self]; - } - else - { - [self removeTrackingArea:trackingArea]; - } - [trackingArea release]; - trackingArea = nil; - } -} #pragma mark - @@ -1032,6 +625,17 @@ + (NSString *)launchedAppBundleIdentifier } +- (BOOL) _isOptionPressed { + BOOL isOptionPressed = (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0); + return isOptionPressed; +} + +- (BOOL) _isCommandPressed { + BOOL isCommandPressed = (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) != 0); + return isCommandPressed; +} + + - (void) browseToURLString: (NSString*) URLString { [_webView setMainFrameURL:URLString]; } @@ -1150,6 +754,62 @@ - (void) revertToOriginalOpacityAttributes } +#pragma mark - +#pragma mark Accessibility + + +- (BOOL)accessibilityIsIgnored { + return NO; +} + + + +- (NSArray *) accessibilityAttributeNames { + NSMutableArray * attributes = [[[super accessibilityAttributeNames] mutableCopy] autorelease]; + // [attributes addObject: NSAccessibilityTitleAttribute]; + [attributes addObject: NSAccessibilityDescriptionAttribute]; +// [attributes addObject: NSAccessibilityHelpAttribute]; +// [attributes addObject: NSAccessibilityParentAttribute]; +// [attributes addObject: NSAccessibilityChildrenAttribute]; +// [attributes addObject: NSAccessibilityRoleAttribute]; +// [attributes addObject: NSAccessibilityRoleDescriptionAttribute]; + return attributes; +} + + + +- (id) accessibilityAttributeValue: (NSString *) attribute { + id value = nil; + + if ( [attribute isEqualToString: NSAccessibilityTitleAttribute] ) { + // value = [self badgeLabelText]; + } + else if ( [attribute isEqualToString: NSAccessibilityDescriptionAttribute] ) { +// value = CtFLocalizedString( @"Blocked Flash content", @"Accessibility: NSAccessibilityDescriptionAttribute for Plugin.m"); + } + else if ( [attribute isEqualToString: NSAccessibilityHelpAttribute] ) { +// value = CtFLocalizedString( @"A film or other interactive content is implemented in Flash should appear here. ClickToFlash prevented it from loading automatically.", @"Accessibility: NSAccessibilityHelpAttribute for Plugin.m"); + } + else if ( [attribute isEqualToString: NSAccessibilityParentAttribute] ){ + value = NSAccessibilityUnignoredAncestor([[[self webView] mainFrame] frameView]); + } + else if ( [attribute isEqualToString: NSAccessibilityChildrenAttribute] ){ + value = [NSArray arrayWithObjects: [self viewWithTag: CTFMainButtonTag], [self viewWithTag:CTFActionButtonTag], nil]; + } + else if ( [attribute isEqualToString: NSAccessibilityRoleAttribute] ) { + value = NSAccessibilityGroupRole; + } + else if ( [attribute isEqualToString: NSAccessibilityRoleDescriptionAttribute] ) { + value = NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil); + } + else { + value = [super accessibilityAttributeValue:attribute]; + } + + return value; +} + + #pragma mark - From 698555f41d9289e9dd8d86822130502c8d0809f2 Mon Sep 17 00:00:00 2001 From: ssp Date: Sun, 11 Oct 2009 02:18:25 +0200 Subject: [PATCH 3/4] Better checking for nil parameters. Hoping that this may solve http://rentzsch.lighthouseapp.com/projects/24342/tickets/350-whitelist-improvement#ticket-350-11 --- Plugin/CTFWhitelist.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugin/CTFWhitelist.m b/Plugin/CTFWhitelist.m index b3ead240..4450bdbb 100644 --- a/Plugin/CTFWhitelist.m +++ b/Plugin/CTFWhitelist.m @@ -46,7 +46,7 @@ of this software and associated documentation files (the "Software"), to deal static BOOL nameMatchesDomainName ( NSString* name, NSString* domainName ) { BOOL result = NO; - if ( name != nil ) { + if ( name != nil && domainName != nil ) { NSRange domainRange = [name rangeOfString: domainName options: NSCaseInsensitiveSearch || NSAnchoredSearch || NSBackwardsSearch]; if ( domainRange.location != NSNotFound ) { // if the match doesn't reach to the beginning of the string, make sure that the preceding character is a dot, to avoid matching other domain names From cdf7c5214833a6f04643acb905d3c9f05c9a06a5 Mon Sep 17 00:00:00 2001 From: ssp Date: Sun, 11 Oct 2009 02:52:55 +0200 Subject: [PATCH 4/4] Fix the previous fix. Do everything with NSURLResponses even though we expect NSHTTPURLResponses which we should be receiving (otherwise the request for the status code will fail). --- Plugin/CTFKillerVideo.h | 2 +- Plugin/CTFKillerVideo.m | 6 +++--- Plugin/CTFLoader.m | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Plugin/CTFKillerVideo.h b/Plugin/CTFKillerVideo.h index 2950fa1e..4a852133 100644 --- a/Plugin/CTFKillerVideo.h +++ b/Plugin/CTFKillerVideo.h @@ -109,7 +109,7 @@ enum CTFKVLookupStatus { - (NSString *) cleanURLString: (NSString*) URLString; - (BOOL) isVideoElementAvailable; - (void) finishedLookups; -- (BOOL) canPlayResponseResult: (NSHTTPURLResponse *) result; +- (BOOL) canPlayResponseResult: (NSURLResponse *) result; // Accessors - (BOOL) autoPlay; diff --git a/Plugin/CTFKillerVideo.m b/Plugin/CTFKillerVideo.m index 4a99266b..a517afa7 100644 --- a/Plugin/CTFKillerVideo.m +++ b/Plugin/CTFKillerVideo.m @@ -661,11 +661,11 @@ - (void) finishedLookups { } - -- (BOOL) canPlayResponseResult: (NSHTTPURLResponse *) response { +// We don't handle anything but HTTP requests. +- (BOOL) canPlayResponseResult: (NSURLResponse *) response { BOOL result = NO; - if ( [response statusCode] == 200 ) { + if ( [((NSHTTPURLResponse*)response) statusCode] == 200 ) { if ( [[response MIMEType] isEqualToString:@"video/mp4"] ) { result = YES; } diff --git a/Plugin/CTFLoader.m b/Plugin/CTFLoader.m index 0a84c344..e2091c13 100644 --- a/Plugin/CTFLoader.m +++ b/Plugin/CTFLoader.m @@ -86,11 +86,11 @@ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *) newDa -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)theResponse { +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)theResponse { [self setResponse: theResponse]; - // We need to cancel HEAD fetching connections here as 10.5 may proceed to download the whole file otherwise (http://openradar.appspot.com/7019347) - if ( [self HEADOnly] && [theResponse statusCode] == 200 ) { + // We need to cancel HEAD fetching connections here as 10.5 may proceed to download the whole file otherwise ( http://openradar.appspot.com/7019347 ) + if ( [self HEADOnly] && [(NSHTTPURLResponse*) theResponse statusCode] == 200 ) { [self finish]; [connection cancel]; }