Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[NEW] Start restoring compatiblity with 10.4.

This builds and works under 10.5, and works under 10.4 - except for a crash in -mouseDown (Symbol not found: _objc_assign_weak).

The code also builds under 10.4, with a couple of tweaks, and does not crash in this case, but also is missing some features and will not build universal for some reason.

(Michael Baltaks)
  • Loading branch information...
commit 6d1803dfb903e87af13674d4944e4bcbb29df1a4 1 parent 9a8b777
Jonathan 'Wolf' Rentzsch authored
30 ClickToFlash.xcodeproj/project.pbxproj 100644 → 100755
@@ -34,6 +34,8 @@
34 34 /* End PBXAggregateTarget section */
35 35
36 36 /* Begin PBXBuildFile section */
  37 + 0038DE240FC0CCF0007B54E9 /* MATrackingArea.m in Sources */ = {isa = PBXBuildFile; fileRef = 0038DE230FC0CCF0007B54E9 /* MATrackingArea.m */; };
  38 + 0038DE320FC0CE7B007B54E9 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0038DE310FC0CE7B007B54E9 /* Carbon.framework */; };
37 39 072189BE0F30D9C3008C8944 /* ContextualMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 072189BA0F30D9C3008C8944 /* ContextualMenu.xib */; };
38 40 072189BF0F30D9C3008C8944 /* WhitelistPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 072189BC0F30D9C3008C8944 /* WhitelistPanel.xib */; };
39 41 55EB70480E04A8850016593D /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 55EB703D0E04A84F0016593D /* Plugin.m */; };
@@ -124,6 +126,9 @@
124 126 /* End PBXCopyFilesBuildPhase section */
125 127
126 128 /* Begin PBXFileReference section */
  129 + 0038DE220FC0CCF0007B54E9 /* MATrackingArea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MATrackingArea.h; sourceTree = "<group>"; };
  130 + 0038DE230FC0CCF0007B54E9 /* MATrackingArea.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MATrackingArea.m; sourceTree = "<group>"; };
  131 + 0038DE310FC0CE7B007B54E9 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
127 132 072189BB0F30D9C3008C8944 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Plugin/English.lproj/ContextualMenu.xib; sourceTree = "<group>"; };
128 133 072189BD0F30D9C3008C8944 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Plugin/English.lproj/WhitelistPanel.xib; sourceTree = "<group>"; };
129 134 55EB703C0E04A84F0016593D /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = Plugin/Plugin.h; sourceTree = "<group>"; };
@@ -167,6 +172,7 @@
167 172 files = (
168 173 55EB70580E04A8B80016593D /* Cocoa.framework in Frameworks */,
169 174 55EB70590E04A8B80016593D /* WebKit.framework in Frameworks */,
  175 + 0038DE320FC0CE7B007B54E9 /* Carbon.framework in Frameworks */,
170 176 );
171 177 runOnlyForDeploymentPostprocessing = 0;
172 178 };
@@ -180,9 +186,19 @@
180 186 /* End PBXFrameworksBuildPhase section */
181 187
182 188 /* Begin PBXGroup section */
  189 + 0038DE210FC0CCF0007B54E9 /* MATrackingArea */ = {
  190 + isa = PBXGroup;
  191 + children = (
  192 + 0038DE220FC0CCF0007B54E9 /* MATrackingArea.h */,
  193 + 0038DE230FC0CCF0007B54E9 /* MATrackingArea.m */,
  194 + );
  195 + path = MATrackingArea;
  196 + sourceTree = "<group>";
  197 + };
183 198 55EB70300E04A8410016593D = {
184 199 isa = PBXGroup;
185 200 children = (
  201 + 0038DE210FC0CCF0007B54E9 /* MATrackingArea */,
186 202 55EB70540E04A89C0016593D /* Plugin */,
187 203 B5CFF1810F40EF98005DB9CC /* Debugging Harness */,
188 204 55EB70550E04A8A40016593D /* Frameworks */,
@@ -233,6 +249,7 @@
233 249 55EB70550E04A8A40016593D /* Frameworks */ = {
234 250 isa = PBXGroup;
235 251 children = (
  252 + 0038DE310FC0CE7B007B54E9 /* Carbon.framework */,
236 253 55EB70560E04A8B80016593D /* Cocoa.framework */,
237 254 55EB70570E04A8B80016593D /* WebKit.framework */,
238 255 );
@@ -436,6 +453,7 @@
436 453 845704550F4792320017F3F4 /* CTFWhitelist.m in Sources */,
437 454 8457045A0F47BC170017F3F4 /* CTFUtilities.m in Sources */,
438 455 79E2EB930F86AAD3005CF170 /* SparkleManager.m in Sources */,
  456 + 0038DE240FC0CCF0007B54E9 /* MATrackingArea.m in Sources */,
439 457 );
440 458 runOnlyForDeploymentPostprocessing = 0;
441 459 };
@@ -514,10 +532,13 @@
514 532 55EB70330E04A8410016593D /* Debug */ = {
515 533 isa = XCBuildConfiguration;
516 534 buildSettings = {
  535 + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
  536 + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
517 537 COPY_PHASE_STRIP = NO;
518 538 GCC_TREAT_WARNINGS_AS_ERRORS = YES;
519 539 GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
520 540 GCC_WARN_SIGN_COMPARE = YES;
  541 + MACOSX_DEPLOYMENT_TARGET = 10.4;
521 542 PRODUCT_VERSION = 1.4.1;
522 543 WARNING_CFLAGS = "-Wall";
523 544 };
@@ -526,10 +547,13 @@
526 547 55EB70340E04A8410016593D /* Release */ = {
527 548 isa = XCBuildConfiguration;
528 549 buildSettings = {
  550 + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
  551 + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
529 552 COPY_PHASE_STRIP = YES;
530 553 GCC_TREAT_WARNINGS_AS_ERRORS = YES;
531 554 GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
532 555 GCC_WARN_SIGN_COMPARE = YES;
  556 + MACOSX_DEPLOYMENT_TARGET = 10.4;
533 557 PRODUCT_VERSION = 1.4.1;
534 558 WARNING_CFLAGS = "-Wall";
535 559 };
@@ -538,6 +562,8 @@
538 562 55EB70450E04A8640016593D /* Debug */ = {
539 563 isa = XCBuildConfiguration;
540 564 buildSettings = {
  565 + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
  566 + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
541 567 COPY_PHASE_STRIP = NO;
542 568 FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)";
543 569 GCC_DYNAMIC_NO_PIC = NO;
@@ -561,8 +587,8 @@
561 587 55EB70460E04A8640016593D /* Release */ = {
562 588 isa = XCBuildConfiguration;
563 589 buildSettings = {
564   - ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)";
565   - ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "ppc i386 ppc64 x86_64";
  590 + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
  591 + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
566 592 COPY_PHASE_STRIP = YES;
567 593 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
568 594 FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)";
89 MATrackingArea/MATrackingArea.h
... ... @@ -0,0 +1,89 @@
  1 +//
  2 +// MATrackingArea.h
  3 +// MATrackingArea
  4 +//
  5 +// Created by Matt Gemmell on 25/09/2007.
  6 +//
  7 +
  8 +#import <Cocoa/Cocoa.h>
  9 +
  10 +/*
  11 + Type of tracking area. You must specify one or more types from this list in the
  12 + MATrackingAreaOptions argument of -initWithRect:options:owner:userInfo:
  13 + */
  14 +enum {
  15 + // Owner receives mouseEntered when mouse enters area, and mouseExited when mouse leaves area.
  16 + MATrackingMouseEnteredAndExited = 0x01,
  17 +
  18 + // Owner receives mouseMoved while mouse is within area. Note that mouseMoved events do not
  19 + // contain userInfo.
  20 + MATrackingMouseMoved = 0x02,
  21 +};
  22 +
  23 +/*
  24 + When the tracking area is active. You must specify exactly one of the following in the
  25 + MATrackingAreaOptions argument of -initWithRect:options:owner:userInfo:
  26 + */
  27 +enum {
  28 + // Owner receives mouseEntered/Exited or mouseMoved when view is first responder.
  29 + MATrackingActiveWhenFirstResponder = 0x10,
  30 +
  31 + // Owner receives mouseEntered/Exited or mouseMoved when view is in key window.
  32 + MATrackingActiveInKeyWindow = 0x20,
  33 +
  34 + // Owner receives mouseEntered/Exited or mouseMoved when app is active.
  35 + MATrackingActiveInActiveApp = 0x40,
  36 +
  37 + // Owner receives mouseEntered/Exited or mouseMoved regardless of activation.
  38 + MATrackingActiveAlways = 0x80,
  39 +};
  40 +
  41 +/*
  42 + Behavior of tracking area. You may specify any number of the following in the
  43 + MATrackingAreaOptions argument of -initWithRect:options:owner:userInfo:
  44 + */
  45 +enum {
  46 + // If set, generate mouseExited event when mouse leaves area (same as assumeInside argument
  47 + // in NSView's addtrackingArea:owner:userData:assumeInside: method).
  48 + MATrackingAssumeInside = 0x100,
  49 +
  50 + // If set, tracking occurs in visibleRect of view and rect is ignored.
  51 + MATrackingInVisibleRect = 0x200,
  52 +
  53 + // If set, mouseEntered events will be generated as mouse is dragged. If not set, mouseEntered
  54 + // events will be generated as mouse is moved, and on mouseUp after a drag. mouseExited
  55 + // events are paired with mouseEntered events so their delivery is affected indirectly.
  56 + // That is, if a mouseEntered event is generated and the mouse subsequently moves out of the
  57 + // trackingArea, a mouseExited event will be generated whether the mouse is being moved or
  58 + // dragged, independent of this flag.
  59 + MATrackingEnabledDuringMouseDrag = 0x400
  60 +};
  61 +
  62 +typedef unsigned int MATrackingAreaOptions;
  63 +
  64 +@interface MATrackingArea : NSObject <NSCopying, NSCoding>
  65 +{
  66 + @private
  67 + NSRect _rect;
  68 + MATrackingAreaOptions _options;
  69 + __weak id _owner;
  70 + NSDictionary * _userInfo;
  71 + NSPoint _lastMovedPoint;
  72 + BOOL _inside;
  73 +}
  74 +
  75 ++ (void)addTrackingArea:(MATrackingArea *)trackingArea toView:(NSView *)view;
  76 ++ (void)removeTrackingArea:(MATrackingArea *)trackingArea fromView:(NSView *)view;
  77 ++ (NSArray *)trackingAreasForView:(NSView *)view;
  78 +
  79 +- (MATrackingArea *)initWithRect:(NSRect)rect
  80 + options:(MATrackingAreaOptions)options
  81 + owner:(id)owner
  82 + userInfo:(NSDictionary *)userInfo;
  83 +- (NSRect)rect;
  84 +- (void)setRect:(NSRect)newRect;
  85 +- (MATrackingAreaOptions)options;
  86 +- (id)owner;
  87 +- (NSDictionary *)userInfo;
  88 +
  89 +@end
426 MATrackingArea/MATrackingArea.m
... ... @@ -0,0 +1,426 @@
  1 +//
  2 +// MATrackingArea.m
  3 +// MATrackingArea
  4 +//
  5 +// Created by Matt Gemmell on 25/09/2007.
  6 +// Copyright 2007 Magic Aubergine.
  7 +//
  8 +
  9 +#import "MATrackingArea.h"
  10 +#import <Carbon/Carbon.h>
  11 +
  12 +#define MA_POLLING_INTERVAL 0.1
  13 +
  14 +static NSTimer *_pollingTimer;
  15 +static NSMutableArray *_views;
  16 +static NSMutableArray *_trackingAreas; // 2-dimensional
  17 +
  18 +/* NSWindow category by Jonathan 'Wolf' Rentzsch http://rentzsch.com */
  19 +@interface NSWindow (liveFrame)
  20 +- (NSRect)liveFrame;
  21 +- (NSRect)convertLiveBaseRectToScreen:(NSRect)rect;
  22 +@end
  23 +@implementation NSWindow (liveFrame)
  24 +// This method is because -[NSWindow frame] isn't updated continually during a drag.
  25 +- (NSRect)liveFrame {
  26 + Rect qdRect;
  27 + GetWindowBounds([self windowRef], kWindowStructureRgn, &qdRect);
  28 +
  29 + return NSMakeRect(qdRect.left,
  30 + (float)CGDisplayPixelsHigh(kCGDirectMainDisplay) - qdRect.bottom,
  31 + qdRect.right - qdRect.left,
  32 + qdRect.bottom - qdRect.top);
  33 +}
  34 +- (NSRect)convertLiveBaseRectToScreen:(NSRect)rect {
  35 + NSRect liveFrame = [self liveFrame];
  36 + return NSMakeRect(liveFrame.origin.x + rect.origin.x,
  37 + liveFrame.origin.y + rect.origin.y,
  38 + rect.size.width,
  39 + rect.size.height);
  40 +}
  41 +@end
  42 +
  43 +@interface MATrackingArea (PrivateMethods)
  44 +
  45 ++ (void)checkMouseLocation:(NSTimer *)theTimer;
  46 +- (NSPoint)_lastMovedPoint;
  47 +- (void)_setLastMovedPoint:(NSPoint)pt;
  48 +- (BOOL)_inside;
  49 +- (void)_setInside:(BOOL)inside;
  50 +- (void)_setNotInside;
  51 +
  52 +@end
  53 +
  54 +@implementation MATrackingArea
  55 +
  56 +
  57 +#pragma mark Class methods
  58 +
  59 +
  60 ++ (void)initialize
  61 +{
  62 + _views = [[NSMutableArray alloc] initWithCapacity:0];
  63 + _trackingAreas = [[NSMutableArray alloc] initWithCapacity:0];
  64 +}
  65 +
  66 +
  67 ++ (void)checkMouseLocation:(NSTimer*)theTimer
  68 +{
  69 + // This is where the action happens.
  70 + NSPoint mouseLoc = [NSEvent mouseLocation];
  71 + NSEnumerator *viewsEnumerator = [_views objectEnumerator];
  72 + NSView *view;
  73 + int index = 0;
  74 +
  75 + while ((view = [viewsEnumerator nextObject])) {
  76 + NSWindow *window = [view window];
  77 + if (!window) {
  78 + // Pointer can't be inside a view with no window.
  79 + int viewIndex = [_views indexOfObject:view];
  80 + [[_trackingAreas objectAtIndex:viewIndex] makeObjectsPerformSelector:@selector(_setNotInside)];
  81 + continue;
  82 + }
  83 +
  84 + NSPoint mouseInWindow = [window convertScreenToBase:mouseLoc];
  85 + NSPoint mouseInView = [view convertPoint:mouseInWindow fromView:nil];
  86 + NSEnumerator *trackingAreaEnumerator = [[_trackingAreas objectAtIndex:index] objectEnumerator];
  87 + MATrackingArea *area;
  88 +
  89 + while ((area = [trackingAreaEnumerator nextObject])) {
  90 + MATrackingAreaOptions options = [area options];
  91 + NSRect trackingRect = (options & MATrackingInVisibleRect)
  92 + ? [view visibleRect]
  93 + : [area rect];
  94 + BOOL nowInside = NSPointInRect(mouseLoc, [window convertLiveBaseRectToScreen:[view convertRect:trackingRect toView:nil]]);
  95 + BOOL wasInside = [area _inside];
  96 + NSEventType eventType = NSApplicationDefined;
  97 +
  98 + // Determine whether to inform the view.
  99 + if (!(options & MATrackingActiveAlways)) {
  100 + if (options & MATrackingActiveInActiveApp) {
  101 + if (![NSApp isActive]) {
  102 + continue;
  103 + }
  104 + } else if (options & MATrackingActiveInKeyWindow) {
  105 + if (![NSApp isActive] || ![window isKeyWindow]) {
  106 + continue;
  107 + }
  108 + } else if (options & MATrackingActiveWhenFirstResponder) {
  109 + if (![NSApp isActive] || ![window isKeyWindow] ||
  110 + !([window firstResponder] == view)) {
  111 + continue;
  112 + }
  113 + }
  114 + }
  115 + // Check whether the mouse is currently being dragged
  116 + UInt32 state;
  117 + BOOL dragging = NO;
  118 + if ([NSApp isActive]) {
  119 + state = GetCurrentEventButtonState();
  120 + } else {
  121 + state = GetCurrentButtonState();
  122 + }
  123 + if ((state & 0x01) || (state & 0x02) || (state & 0x03)) {
  124 + dragging = YES;
  125 + }
  126 + if (!(options & MATrackingEnabledDuringMouseDrag) && dragging) {
  127 + continue;
  128 + }
  129 +
  130 + // Determine what happened.
  131 + if (nowInside && !wasInside && (options & MATrackingMouseEnteredAndExited)) {
  132 + // Entered
  133 + eventType = NSMouseEntered;
  134 + [area _setInside:YES];
  135 + } else if (!nowInside && wasInside && (options & MATrackingMouseEnteredAndExited)) {
  136 + // Exited
  137 + eventType = NSMouseExited;
  138 + [area _setInside:NO];
  139 + } else if (nowInside && (options & MATrackingMouseMoved)) {
  140 + if (wasInside && !NSEqualPoints(mouseInView, [area _lastMovedPoint])) {
  141 + // Moved
  142 + eventType = NSMouseMoved;
  143 + [area _setLastMovedPoint:mouseInView];
  144 + } else if (!wasInside) {
  145 + // Make sure we get a moved event next time
  146 + [area _setInside:YES];
  147 + }
  148 + }
  149 +
  150 + // Construct an appropriate event.
  151 + NSEvent *event = nil;
  152 + switch (eventType) {
  153 + case NSMouseEntered:
  154 + case NSMouseExited:
  155 + event = [NSEvent enterExitEventWithType:eventType
  156 + location:mouseInWindow
  157 + modifierFlags:eventType
  158 + timestamp:0
  159 + windowNumber:[window windowNumber]
  160 + context:nil
  161 + eventNumber:0
  162 + trackingNumber:0
  163 + userData:[area userInfo]];
  164 + break;
  165 + case NSMouseMoved:
  166 + event = [NSEvent mouseEventWithType:eventType
  167 + location:mouseInWindow
  168 + modifierFlags:eventType
  169 + timestamp:0
  170 + windowNumber:[window windowNumber]
  171 + context:nil
  172 + eventNumber:0
  173 + clickCount:0
  174 + pressure:0.0];
  175 + break;
  176 + }
  177 +
  178 + // Send event.
  179 + id owner = [area owner];
  180 + switch (eventType) {
  181 + case NSMouseEntered:
  182 + [owner mouseEntered:event];
  183 + break;
  184 + case NSMouseExited:
  185 + [owner mouseExited:event];
  186 + break;
  187 + case NSMouseMoved:
  188 + [owner mouseMoved:event];
  189 + break;
  190 + }
  191 + }
  192 +
  193 + index++;
  194 + }
  195 +}
  196 +
  197 +
  198 ++ (void)addTrackingArea:(MATrackingArea *)trackingArea toView:(NSView *)view
  199 +{
  200 + if (!trackingArea || !view) {
  201 + return;
  202 + }
  203 +
  204 + // Validate options
  205 + MATrackingAreaOptions options = [trackingArea options];
  206 + if (!(options & MATrackingMouseEnteredAndExited) &&
  207 + !(options & MATrackingMouseMoved)) {
  208 + // trackingArea's options don't contain any of the 'type' masks.
  209 + return;
  210 + } else if (!(options & MATrackingActiveAlways) &&
  211 + !(options & MATrackingActiveInActiveApp) &&
  212 + !(options & MATrackingActiveInKeyWindow) &&
  213 + !(options & MATrackingActiveWhenFirstResponder)) {
  214 + // trackingArea's options don't contain any of the 'when' masks.
  215 + return;
  216 + }
  217 +
  218 + int index = [_views indexOfObject:view];
  219 + if (index == NSNotFound) {
  220 + // Add view to _views and create appropriate entry in _trackingAreas.
  221 + [_views addObject:view];
  222 + NSMutableArray *trackingAreasForView = [NSMutableArray arrayWithCapacity:1];
  223 + [trackingAreasForView addObject:trackingArea];
  224 + [_trackingAreas addObject:trackingAreasForView];
  225 + } else {
  226 + // Add trackingArea to appropriate entry in _trackingAreas.
  227 + NSMutableArray *trackingAreasForView = [_trackingAreas objectAtIndex:index];
  228 + if (![trackingAreasForView containsObject:trackingArea]) {
  229 + [trackingAreasForView addObject:trackingArea];
  230 + }
  231 + }
  232 +
  233 + // Support for MATrackingAssumeInside
  234 + if (options & MATrackingAssumeInside) {
  235 + [trackingArea _setInside:YES];
  236 + }
  237 +
  238 + // Create Polling Timer Of Extreme Evil if appropriate.
  239 + if (!_pollingTimer) {
  240 + _pollingTimer = [[NSTimer scheduledTimerWithTimeInterval:MA_POLLING_INTERVAL
  241 + target:[self class]
  242 + selector:@selector(checkMouseLocation:)
  243 + userInfo:nil
  244 + repeats:YES] retain];
  245 + }
  246 +}
  247 +
  248 +
  249 ++ (void)removeTrackingArea:(MATrackingArea *)trackingArea fromView:(NSView *)view
  250 +{
  251 + if (!trackingArea || !view) {
  252 + return;
  253 + }
  254 +
  255 + int index = [_views indexOfObject:view];
  256 + if (index == NSNotFound) {
  257 + // We don't have any trackingAreas for that view.
  258 + return;
  259 + }
  260 +
  261 + NSMutableArray *trackingAreasForView = [_trackingAreas objectAtIndex:index];
  262 + if (![trackingAreasForView containsObject:trackingArea]) {
  263 + // We don't know anything about that trackingArea.
  264 + return;
  265 + } else {
  266 + // Remove the trackingArea as requested.
  267 + [trackingAreasForView removeObject:trackingArea];
  268 + }
  269 +
  270 + // If there are no more trackingAreas for this view, remove it.
  271 + if ([trackingAreasForView count] == 0) {
  272 + [_trackingAreas removeObjectAtIndex:index];
  273 + [_views removeObjectAtIndex:index];
  274 + }
  275 +
  276 + // Destroy timer if appropriate.
  277 + if ([_views count] == 0) {
  278 + [_pollingTimer invalidate];
  279 + [_pollingTimer release];
  280 + _pollingTimer = nil;
  281 + }
  282 +}
  283 +
  284 +
  285 ++ (NSArray *)trackingAreasForView:(NSView *)view
  286 +{
  287 + if (view) {
  288 + int index = [_views indexOfObject:view];
  289 + if (index != NSNotFound) {
  290 + return [NSArray arrayWithArray:[_trackingAreas objectAtIndex:index]];
  291 + }
  292 + }
  293 + return nil;
  294 +}
  295 +
  296 +
  297 +#pragma mark Instance methods
  298 +
  299 +
  300 +- (MATrackingArea *)initWithRect:(NSRect)rect
  301 + options:(MATrackingAreaOptions)options
  302 + owner:(id)owner
  303 + userInfo:(NSDictionary *)userInfo
  304 +{
  305 + if ((self = [super init])) {
  306 + _rect = rect;
  307 + _options = options;
  308 + _owner = owner;
  309 + _userInfo = [userInfo retain];
  310 + _lastMovedPoint = NSZeroPoint;
  311 + _inside = NO;
  312 + }
  313 + return self;
  314 +}
  315 +
  316 +
  317 +- (void)dealloc
  318 +{
  319 + [_userInfo release];
  320 + [super dealloc];
  321 +}
  322 +
  323 +
  324 +- (NSRect)rect
  325 +{
  326 + return _rect;
  327 +}
  328 +
  329 +
  330 +- (void)setRect:(NSRect)newRect
  331 +{
  332 + _rect = newRect;
  333 + [MATrackingArea checkMouseLocation:nil];
  334 +}
  335 +
  336 +
  337 +- (MATrackingAreaOptions)options
  338 +{
  339 + return _options;
  340 +}
  341 +
  342 +
  343 +- (id)owner
  344 +{
  345 + return _owner;
  346 +}
  347 +
  348 +
  349 +- (NSDictionary *)userInfo
  350 +{
  351 + return [[_userInfo retain] autorelease];
  352 +}
  353 +
  354 +
  355 +- (NSPoint)_lastMovedPoint
  356 +{
  357 + return _lastMovedPoint;
  358 +}
  359 +
  360 +
  361 +- (void)_setLastMovedPoint:(NSPoint)pt
  362 +{
  363 + _lastMovedPoint = pt;
  364 +}
  365 +
  366 +
  367 +- (BOOL)_inside
  368 +{
  369 + return _inside;
  370 +}
  371 +
  372 +
  373 +- (void)_setInside:(BOOL)inside
  374 +{
  375 + _inside = inside;
  376 +}
  377 +
  378 +
  379 +- (void)_setNotInside
  380 +{
  381 + [self _setInside:NO];
  382 +}
  383 +
  384 +
  385 +#pragma mark NSCopying
  386 +
  387 +
  388 +- (id)copyWithZone:(NSZone *)zone
  389 +{
  390 + MATrackingArea *copy = (MATrackingArea *)[[[self class] allocWithZone:zone]
  391 + initWithRect:[self rect]
  392 + options:[self options]
  393 + owner:[self owner]
  394 + userInfo:[self userInfo]];
  395 + return copy;
  396 +}
  397 +
  398 +
  399 +#pragma mark NSCoding
  400 +
  401 +
  402 +- (id)initWithCoder:(NSCoder *)coder
  403 +{
  404 + NSRect rect = [coder decodeRectForKey:@"_rect"];
  405 + MATrackingAreaOptions options = [coder decodeIntForKey:@"_options"];
  406 + NSDictionary *userInfo = [coder decodeObjectForKey:@"_userInfo"];
  407 + id owner = [coder decodeObjectForKey:@"_owner"];
  408 +
  409 + self = (MATrackingArea *)[[MATrackingArea alloc] initWithRect:rect
  410 + options:options
  411 + owner:owner
  412 + userInfo:userInfo];
  413 + return self;
  414 +}
  415 +
  416 +
  417 +- (void)encodeWithCoder:(NSCoder *)coder
  418 +{
  419 + [coder encodeRect:_rect forKey:@"_rect"];
  420 + [coder encodeInt:_options forKey:@"_options"];
  421 + [coder encodeObject:_userInfo forKey:@"_userInfo"];
  422 + [coder encodeConditionalObject:_owner forKey:@"_owner"];
  423 +}
  424 +
  425 +
  426 +@end
12 Plugin/CTFWhitelist.m 100644 → 100755
@@ -122,7 +122,7 @@ - (void) _abortAlert
122 122 - (void) _askToAddCurrentSiteToWhitelist
123 123 {
124 124 NSString *title = NSLocalizedString(@"Always load Flash for this site?", @"Always load Flash for this site? alert title");
125   - NSString *message = [NSString stringWithFormat:NSLocalizedString(@"Add %@ to the whitelist?", @"Add <sitename> to the whitelist? alert message"), self.host];
  125 + NSString *message = [NSString stringWithFormat:NSLocalizedString(@"Add %@ to the whitelist?", @"Add <sitename> to the whitelist? alert message"), [self host]];
126 126
127 127 NSAlert *alert = [[NSAlert alloc] init];
128 128 [alert addButtonWithTitle:NSLocalizedString(@"Add to Whitelist", @"Add to Whitelist button")];
@@ -150,12 +150,12 @@ - (void) _addToWhitelistAlertDidEnd: (NSAlert *)alert returnCode: (int)returnCod
150 150 - (BOOL) _isHostWhitelisted
151 151 {
152 152 // Nil hosts whitelisted by default (e.g. Dashboard)
153   - if (!self.host)
  153 + if (![self host])
154 154 {
155 155 return YES;
156 156 }
157 157
158   - return [self _isWhiteListedForHostString: self.host];
  158 + return [self _isWhiteListedForHostString: [self host]];
159 159 }
160 160
161 161 - (BOOL) _isWhiteListedForHostString:(NSString *)hostString
@@ -176,7 +176,7 @@ - (NSMutableArray *) _mutableSiteInfo
176 176 - (void) _addHostToWhitelist
177 177 {
178 178 NSMutableArray *siteInfo = [self _mutableSiteInfo];
179   - [siteInfo addObject: whitelistItemForSite(self.host)];
  179 + [siteInfo addObject: whitelistItemForSite([self host])];
180 180 [[NSUserDefaults standardUserDefaults] setObject: siteInfo forKey: sHostSiteInfoDefaultsKey];
181 181 [[NSNotificationCenter defaultCenter] postNotificationName: sCTFWhitelistAdditionMade object: self];
182 182 }
@@ -184,7 +184,7 @@ - (void) _addHostToWhitelist
184 184 - (void) _removeHostFromWhitelist
185 185 {
186 186 NSMutableArray *siteInfo = [self _mutableSiteInfo];
187   - NSUInteger foundIndex = indexOfItemForSite(siteInfo, self.host);
  187 + NSUInteger foundIndex = indexOfItemForSite(siteInfo, [self host]);
188 188
189 189 if(foundIndex != NSNotFound) {
190 190 [siteInfo removeObjectAtIndex: foundIndex];
@@ -212,7 +212,7 @@ - (IBAction) removeFromWhitelist: (id)sender
212 212 return;
213 213
214 214 NSString *title = NSLocalizedString(@"Stop always loading Flash?", @"Stop always loading Flash? alert title");
215   - NSString *message = [NSString stringWithFormat:NSLocalizedString(@"Remove %@ from the whitelist?", @"Remove %@ from the whitelist? alert message"), self.host];
  215 + NSString *message = [NSString stringWithFormat:NSLocalizedString(@"Remove %@ from the whitelist?", @"Remove %@ from the whitelist? alert message"), [self host]];
216 216
217 217 NSAlert *alert = [[NSAlert alloc] init];
218 218 [alert addButtonWithTitle:NSLocalizedString(@"Remove from Whitelist", @"Remove from Whitelist button")];
4 Plugin/CTFsIFRSupport.m 100644 → 100755
@@ -46,7 +46,7 @@ @implementation CTFClickToFlashPlugin( sIFRSupport )
46 46 - (NSUInteger) _sifrVersionInstalled
47 47 {
48 48 // get the container's WebView
49   - WebView *sifrWebView = self.webView;
  49 + WebView *sifrWebView = [self webView];
50 50 NSUInteger version = 0;
51 51
52 52 if (sifrWebView) {
@@ -79,7 +79,7 @@ - (BOOL) _shouldAutoLoadSIFR
79 79 - (void) _disableSIFR
80 80 {
81 81 // get the container's WebView
82   - WebView *sifrWebView = self.webView;
  82 + WebView *sifrWebView = [self webView];
83 83
84 84 // if sifr add-ons are not installed, load version-appropriate version into page
85 85 if ([[sifrWebView stringByEvaluatingJavaScriptFromString: sSifrAddOnTest] isEqualToString: @"true"]) {
29 Plugin/Plugin.h 100644 → 100755
@@ -32,7 +32,7 @@ THE SOFTWARE.
32 32 DOMElement *_container;
33 33 NSString *_host;
34 34 NSDictionary* _flashVars;
35   - NSTrackingArea *trackingArea;
  35 + id trackingArea;
36 36 NSAlert* _activeAlert;
37 37 NSString* _badgeText;
38 38 BOOL mouseIsDown;
@@ -53,15 +53,24 @@ THE SOFTWARE.
53 53
54 54 - (id) initWithArguments:(NSDictionary *)arguments;
55 55
56   -@property (nonatomic, retain) DOMElement *container;
57   -@property (nonatomic, retain) NSString *host;
58   -@property (nonatomic, retain) WebView *webView;
59   -@property (retain) NSString *baseURL;
60   -@property (nonatomic, retain) NSDictionary *attributes;
61   -@property (retain) NSDictionary *originalOpacityAttributes;
62   -@property (retain) NSString *src;
63   -@property (retain) NSString *videoId;
64   -@property (retain) NSString *launchedAppBundleIdentifier;
  56 +- (DOMElement *)container;
  57 +- (void)setContainer:(DOMElement *)newValue;
  58 +- (NSString *)host;
  59 +- (void)setHost:(NSString *)newValue;
  60 +- (WebView *)webView;
  61 +- (void)setWebView:(WebView *)newValue;
  62 +- (NSString *)baseURL;
  63 +- (void)setBaseURL:(NSString *)newValue;
  64 +- (NSDictionary *)attributes;
  65 +- (void)setAttributes:(NSDictionary *)newValue;
  66 +- (NSDictionary *)originalOpacityAttributes;
  67 +- (void)setOriginalOpacityAttributes:(NSDictionary *)newValue;
  68 +- (NSString *)src;
  69 +- (void)setSrc:(NSString *)newValue;
  70 +- (NSString *)videoId;
  71 +- (void)setVideoId:(NSString *)newValue;
  72 +- (NSString *)launchedAppBundleIdentifier;
  73 +- (void)setLaunchedAppBundleIdentifier:(NSString *)newValue;
65 74
66 75 - (IBAction)loadFlash:(id)sender;
67 76 - (IBAction)loadH264:(id)sender;
449 Plugin/Plugin.m 100644 → 100755
@@ -26,6 +26,7 @@ of this software and associated documentation files (the "Software"), to deal
26 26
27 27 #import "Plugin.h"
28 28
  29 +#import "MATrackingArea.h"
29 30 #import "CTFMenubarMenuController.h"
30 31 #import "CTFsIFRSupport.h"
31 32 #import "CTFUtilities.h"
@@ -44,6 +45,14 @@ of this software and associated documentation files (the "Software"), to deal
44 45 static NSString *sAutoLoadInvisibleFlashViewsKey = @"ClickToFlash_autoLoadInvisibleViews";
45 46 static NSString *sPluginEnabled = @"ClickToFlash_pluginEnabled";
46 47
  48 +BOOL usingMATrackingArea = NO;
  49 +
  50 +@interface NSBezierPath(MRGradientFill)
  51 +-(void)linearGradientFill:(NSRect)thisRect
  52 + startColor:(NSColor *)startColor
  53 + endColor:(NSColor *)endColor;
  54 +@end
  55 +
47 56 @interface CTFClickToFlashPlugin (Internal)
48 57 - (void) _convertTypesForFlashContainer;
49 58 - (void) _convertTypesForFlashContainerAfterDelay;
@@ -105,11 +114,11 @@ - (id) initWithArguments:(NSDictionary *)arguments
105 114 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:sPluginEnabled];
106 115 }
107 116
108   - self.launchedAppBundleIdentifier = [self launchedAppBundleIdentifier];
  117 + [self setLaunchedAppBundleIdentifier:[self launchedAppBundleIdentifier]];
109 118
110   - self.webView = [[[arguments objectForKey:WebPlugInContainerKey] webFrame] webView];
  119 + [self setWebView:[[[arguments objectForKey:WebPlugInContainerKey] webFrame] webView]];
111 120
112   - self.container = [arguments objectForKey:WebPlugInContainingElementKey];
  121 + [self setContainer:[arguments objectForKey:WebPlugInContainingElementKey]];
113 122
114 123 [self _migrateWhitelist];
115 124
@@ -117,28 +126,28 @@ - (id) initWithArguments:(NSDictionary *)arguments
117 126 // Get URL
118 127
119 128 NSURL *base = [arguments objectForKey:WebPlugInBaseURLKey];
120   - self.baseURL = [base absoluteString];
121   - self.host = [base host];
  129 + [self setBaseURL:[base absoluteString]];
  130 + [self setHost:[base host]];
122 131
123   - self.attributes = [arguments objectForKey:WebPlugInAttributesKey];
124   - NSString *srcAttribute = [self.attributes objectForKey:@"src"];
  132 + [self setAttributes:[arguments objectForKey:WebPlugInAttributesKey]];
  133 + NSString *srcAttribute = [[self attributes] objectForKey:@"src"];
125 134
126 135 if (srcAttribute) {
127   - self.src = srcAttribute;
  136 + [self setSrc:srcAttribute];
128 137 } else {
129   - NSString *dataAttribute = [self.attributes objectForKey:@"data"];
130   - if (dataAttribute) self.src = dataAttribute;
  138 + NSString *dataAttribute = [[self attributes] objectForKey:@"data"];
  139 + if (dataAttribute) [self setSrc:dataAttribute];
131 140 }
132 141
133 142
134 143 // set tooltip
135 144
136   - if (self.src) [self setToolTip:self.src];
  145 + if ([self src]) [self setToolTip:[self src]];
137 146
138 147
139 148 // Read in flashvars (needed to determine YouTube videos)
140 149
141   - NSString* flashvars = [ self.attributes objectForKey: @"flashvars" ];
  150 + NSString* flashvars = [[self attributes] objectForKey: @"flashvars" ];
142 151 if( flashvars != nil )
143 152 _flashVars = [ [ self _flashVarDictionary: flashvars ] retain ];
144 153
@@ -149,25 +158,25 @@ - (id) initWithArguments:(NSDictionary *)arguments
149 158
150 159 // check whether it's from YouTube and get the video_id
151 160
152   - _fromYouTube = [self.host isEqualToString:@"www.youtube.com"]
  161 + _fromYouTube = [[self host] isEqualToString:@"www.youtube.com"]
153 162 || ( flashvars != nil && [flashvars rangeOfString: @"www.youtube.com"].location != NSNotFound )
154   - || (self.src != nil && [self.src rangeOfString: @"youtube.com"].location != NSNotFound );
  163 + || ([self src] != nil && [[self src] rangeOfString: @"youtube.com"].location != NSNotFound );
155 164
156 165 if (_fromYouTube) {
157 166 NSString *videoId = [ self flashvarWithName: @"video_id" ];
158 167 if (videoId != nil) {
159   - self.videoId = videoId;
  168 + [self setVideoId:videoId];
160 169 } else {
161 170 // scrub the URL to determine the video_id
162 171
163 172 NSString *videoIdFromURL = nil;
164   - NSScanner *URLScanner = [[NSScanner alloc] initWithString:self.src];
  173 + NSScanner *URLScanner = [[NSScanner alloc] initWithString:[self src]];
165 174 [URLScanner scanUpToString:@"youtube.com/v/" intoString:nil];
166 175 if ([URLScanner scanString:@"youtube.com/v/" intoString:nil]) {
167 176 // URL is in required format, next characters are the id
168 177
169 178 [URLScanner scanUpToString:@"&" intoString:&videoIdFromURL];
170   - if (videoIdFromURL) self.videoId = videoIdFromURL;
  179 + if (videoIdFromURL) [self setVideoId:videoIdFromURL];
171 180 }
172 181 [URLScanner release];
173 182 }
@@ -283,34 +292,34 @@ - (id) initWithArguments:(NSDictionary *)arguments
283 292 NSMutableDictionary *originalOpacityDict = [NSMutableDictionary dictionary];
284 293 NSString *opacityResetString = @"; opacity: 1.000 !important; -moz-opacity: 1 !important; filter: alpha(opacity=1) !important;";
285 294
286   - NSString *originalWmode = [self.container getAttribute:@"wmode"];
287   - NSString *originalStyle = [self.container getAttribute:@"style"];
288   - NSString *originalParentWmode = [(DOMElement *)[self.container parentNode] getAttribute:@"wmode"];
289   - NSString *originalParentStyle = [(DOMElement *)[self.container parentNode] getAttribute:@"style"];
  295 + NSString *originalWmode = [[self container] getAttribute:@"wmode"];
  296 + NSString *originalStyle = [[self container] getAttribute:@"style"];
  297 + NSString *originalParentWmode = [(DOMElement *)[[self container] parentNode] getAttribute:@"wmode"];
  298 + NSString *originalParentStyle = [(DOMElement *)[[self container] parentNode] getAttribute:@"style"];
290 299
291 300 if (originalWmode != nil && [originalWmode length] > 0u && ![originalWmode isEqualToString:@"opaque"]) {
292 301 [originalOpacityDict setObject:originalWmode forKey:@"self-wmode"];
293   - [self.container setAttribute:@"wmode" value:@"opaque"];
  302 + [[self container] setAttribute:@"wmode" value:@"opaque"];
294 303 }
295 304
296 305 if (originalStyle != nil && [originalStyle length] > 0u && ![originalStyle hasSuffix:opacityResetString]) {
297 306 [originalOpacityDict setObject:originalStyle forKey:@"self-style"];
298 307 [originalOpacityDict setObject:[originalStyle stringByAppendingString:opacityResetString] forKey:@"modified-self-style"];
299   - [self.container setAttribute:@"style" value:[originalStyle stringByAppendingString:opacityResetString]];
  308 + [[self container] setAttribute:@"style" value:[originalStyle stringByAppendingString:opacityResetString]];
300 309 }
301 310
302 311 if (originalParentWmode != nil && [originalParentWmode length] > 0u && ![originalParentWmode isEqualToString:@"opaque"]) {
303 312 [originalOpacityDict setObject:originalParentWmode forKey:@"parent-wmode"];
304   - [(DOMElement *)[self.container parentNode] setAttribute:@"wmode" value:@"opaque"];
  313 + [(DOMElement *)[[self container] parentNode] setAttribute:@"wmode" value:@"opaque"];
305 314 }
306 315
307 316 if (originalParentStyle != nil && [originalParentStyle length] > 0u && ![originalParentStyle hasSuffix:opacityResetString]) {
308 317 [originalOpacityDict setObject:originalParentStyle forKey:@"parent-style"];
309 318 [originalOpacityDict setObject:[originalParentStyle stringByAppendingString:opacityResetString] forKey:@"modified-parent-style"];
310   - [(DOMElement *)[self.container parentNode] setAttribute:@"style" value:[originalParentStyle stringByAppendingString:opacityResetString]];
  319 + [(DOMElement *)[[self container] parentNode] setAttribute:@"style" value:[originalParentStyle stringByAppendingString:opacityResetString]];
311 320 }
312 321
313   - self.originalOpacityAttributes = originalOpacityDict;
  322 + [self setOriginalOpacityAttributes:originalOpacityDict];
314 323 }
315 324
316 325 return self;
@@ -325,11 +334,11 @@ - (void) dealloc
325 334 // notify that this ClickToFlash plugin is going away
326 335 [[CTFMenubarMenuController sharedController] unregisterView: self];
327 336
328   - self.container = nil;
329   - self.host = nil;
330   - self.webView = nil;
331   - self.baseURL = nil;
332   - self.attributes = nil;
  337 + [self setContainer:nil];
  338 + [self setHost:nil];
  339 + [self setWebView:nil];
  340 + [self setBaseURL:nil];
  341 + [self setAttributes:nil];
333 342
334 343 [_flashVars release];
335 344 [_badgeText release];
@@ -382,11 +391,25 @@ - (void) mouseDown:(NSEvent *)event
382 391 [self setNeedsDisplay:YES];
383 392
384 393 // 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.
385   - trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
386   - options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingEnabledDuringMouseDrag
387   - owner:self
388   - userInfo:nil];
389   - [self addTrackingArea:trackingArea];
  394 + trackingArea = [NSClassFromString(@"NSTrackingArea") alloc];
  395 + if (trackingArea != nil)
  396 + {
  397 + [trackingArea initWithRect:[self bounds]
  398 + options:MATrackingMouseEnteredAndExited | MATrackingActiveInKeyWindow | MATrackingEnabledDuringMouseDrag
  399 + owner:self
  400 + userInfo:nil];
  401 + [self addTrackingArea:trackingArea];
  402 + }
  403 + else
  404 + {
  405 + trackingArea = [NSClassFromString(@"MATrackingArea") alloc];
  406 + [trackingArea initWithRect:[self bounds]
  407 + options:MATrackingMouseEnteredAndExited | MATrackingActiveInKeyWindow | MATrackingEnabledDuringMouseDrag
  408 + owner:self
  409 + userInfo:nil];
  410 + [MATrackingArea addTrackingArea:trackingArea toView:self];
  411 + usingMATrackingArea = YES;
  412 + }
390 413 }
391 414 }
392 415
@@ -408,7 +431,14 @@ - (void) mouseUp:(NSEvent *)event
408 431 [self display];
409 432
410 433 // We're done tracking.
411   - [self removeTrackingArea:trackingArea];
  434 + if (usingMATrackingArea)
  435 + {
  436 + [MATrackingArea removeTrackingArea:trackingArea fromView:self];
  437 + }
  438 + else
  439 + {
  440 + [self removeTrackingArea:trackingArea];
  441 + }
412 442 [trackingArea release];
413 443 trackingArea = nil;
414 444
@@ -429,15 +459,15 @@ - (BOOL) _isOptionPressed
429 459
430 460 - (BOOL) isConsideredInvisible
431 461 {
432   - int height = (int)([self webView].frame.size.height);
433   - int width = (int)([self webView].frame.size.width);
  462 + int height = (int)([[self webView] frame].size.height);
  463 + int width = (int)([[self webView] frame].size.width);
434 464
435 465 if ( (height <= maxInvisibleDimension) && (width <= maxInvisibleDimension) )
436 466 {
437 467 return YES;
438 468 }
439 469
440   - NSDictionary *attributes = self.attributes;
  470 + NSDictionary *attributes = [self attributes];
441 471 if ( attributes != nil )
442 472 {
443 473 NSString *heightObject = [attributes objectForKey:@"height"];
@@ -496,7 +526,7 @@ - (BOOL) validateMenuItem: (NSMenuItem *)menuItem
496 526 {
497 527 NSString* title = [NSString stringWithFormat:
498 528 NSLocalizedString(@"Add %@ to Whitelist", @"Add <sitename> to Whitelist menu title"),
499   - self.host];
  529 + [self host]];
500 530 [menuItem setTitle: title];
501 531 if ([self _isHostWhitelisted])
502 532 enabled = NO;
@@ -674,22 +704,27 @@ - (void) _drawBadgeWithPressed: (BOOL) pressed
674 704
675 705 NSColor *startingColor = [NSColor colorWithDeviceWhite:1.0 alpha:1.0];
676 706 NSColor *endingColor = [NSColor colorWithDeviceWhite:1.0 alpha:0.0];
677   - NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startingColor endingColor:endingColor];
678   -
679   - NSPoint gearImageCenter = NSMakePoint(0 - viewWidth/2 + margin + gearImage.size.height/2,
680   - viewHeight/2 - margin - gearImage.size.height/2);
681   -
682   - // draw gradient behind gear so that it's visible even on dark backgrounds
683   - [gradient drawFromCenter:gearImageCenter
684   - radius:0.0
685   - toCenter:gearImageCenter
686   - radius:gearImage.size.height/2*1.5
687   - options:0];
688   -
689   - [gradient release];
690   -
  707 +
  708 + NSPoint gearImageCenter = NSMakePoint(0 - viewWidth/2 + margin + [gearImage size].height/2,
  709 + viewHeight/2 - margin - [gearImage size].height/2);
  710 +
  711 + id gradient = [NSClassFromString(@"NSGradient") alloc];
  712 + if (gradient != nil)
  713 + {
  714 + [gradient initWithStartingColor:startingColor endingColor:endingColor];
  715 +
  716 + // draw gradient behind gear so that it's visible even on dark backgrounds
  717 + [gradient drawFromCenter:gearImageCenter
  718 + radius:0.0
  719 + toCenter:gearImageCenter
  720 + radius:[gearImage size].height/2*1.5
  721 + options:0];
  722 +
  723 + [gradient release];
  724 + }
  725 +
691 726 // draw the gear image
692   - [gearImage drawAtPoint:NSMakePoint(0 - viewWidth/2 + margin,viewHeight/2 - margin - gearImage.size.height)
  727 + [gearImage drawAtPoint:NSMakePoint(0 - viewWidth/2 + margin,viewHeight/2 - margin - [gearImage size].height)
693 728 fromRect:NSZeroRect
694 729 operation:NSCompositeSourceOver
695 730 fraction:1.0];
@@ -712,19 +747,37 @@ - (void) _drawBackground
712 747
713 748 NSColor *startingColor = [NSColor colorWithDeviceWhite:1.0 alpha:0.15];
714 749 NSColor *endingColor = [NSColor colorWithDeviceWhite:0.0 alpha:0.15];
715   - NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startingColor endingColor:endingColor];
716   -
  750 +
717 751 // When the mouse is up or outside the view, we want a convex look, so we draw the gradient downward (90+180=270 degrees).
718 752 // When the mouse is down and inside the view, we want a concave look, so we draw the gradient upward (90 degrees).
719   - [gradient drawInBezierPath:[NSBezierPath bezierPathWithRect:fillRect] angle:90.0 + ((mouseIsDown && mouseInside) ? 0.0 : 180.0)];
  753 + id gradient = [NSClassFromString(@"NSGradient") alloc];
  754 + if (gradient != nil)
  755 + {
  756 + [gradient initWithStartingColor:startingColor endingColor:endingColor];
  757 +
  758 + [gradient drawInBezierPath:[NSBezierPath bezierPathWithRect:fillRect] angle:90.0 + ((mouseIsDown && mouseInside) ? 0.0 : 180.0)];
  759 +
  760 + [gradient release];
  761 + }
  762 + else
  763 + {
  764 + //tweak colors for better compatibility with linearGradientFill
  765 + startingColor = [NSColor colorWithDeviceWhite:0.633 alpha:0.15];
  766 + endingColor = [NSColor colorWithDeviceWhite:0.333 alpha:0.15];
  767 + NSBezierPath *path = [NSBezierPath bezierPath];
  768 +
  769 + //Draw Gradient
  770 + [path linearGradientFill:fillRect
  771 + startColor:((mouseIsDown && mouseInside) ? endingColor : startingColor)
  772 + endColor:((mouseIsDown && mouseInside) ? startingColor : endingColor)];
  773 + [path stroke];
  774 + }
720 775
721 776 // Draw stroke
722 777 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.50] set];
723 778 [NSBezierPath setDefaultLineWidth:2.0];
724 779 [NSBezierPath setDefaultLineCapStyle:NSSquareLineCapStyle];
725 780 [[NSBezierPath bezierPathWithRect:strokeRect] stroke];
726   -
727   - [gradient release];
728 781
729 782 // Draw label
730 783 [ self _drawBadgeWithPressed: mouseIsDown && mouseInside ];
@@ -772,7 +825,7 @@ - (NSString*) _videoHash
772 825 - (BOOL) _hasH264Version
773 826 {
774 827 if( _fromYouTube )
775   - return self.videoId != nil && [ self _videoHash ] != nil;
  828 + return [self videoId] != nil && [ self _videoHash ] != nil;
776 829 else
777 830 return NO;
778 831 }
@@ -786,7 +839,7 @@ - (BOOL) _useH264Version
786 839
787 840 - (void) _convertElementForMP4: (DOMElement*) element
788 841 {
789   - NSString* video_id = self.videoId;
  842 + NSString* video_id = [self videoId];
790 843 NSString* video_hash = [ self _videoHash ];
791 844
792 845 NSString* src = [ NSString stringWithFormat: @"http://www.youtube.com/get_video?fmt=18&video_id=%@&t=%@",
@@ -818,7 +871,7 @@ - (void) _convertToMP4Container
818 871
819 872 - (void) _convertToMP4ContainerAfterDelay
820 873 {
821   - DOMElement* newElement = (DOMElement*) [ self.container cloneNode: NO ];
  874 + DOMElement* newElement = (DOMElement*) [ [self container] cloneNode: NO ];
822 875
823 876 [ self _convertElementForMP4: newElement ];
824 877
@@ -826,8 +879,8 @@ - (void) _convertToMP4ContainerAfterDelay
826 879 [[self retain] autorelease];
827 880
828 881 // Replace self with element.
829   - [self.container.parentNode replaceChild:newElement oldChild:self.container];
830   - self.container = nil;
  882 + [[[self container] parentNode] replaceChild:newElement oldChild:[self container]];
  883 + [self setContainer:nil];
831 884 }
832 885
833 886 - (NSString *)launchedAppBundleIdentifier
@@ -868,14 +921,14 @@ - (NSString *)launchedAppBundleIdentifier
868 921
869 922 - (IBAction)downloadH264:(id)sender
870 923 {
871   - NSString* video_id = self.videoId;
  924 + NSString* video_id = [self videoId];
872 925 NSString* video_hash = [ self _videoHash ];
873 926
874 927 NSString* src = [ NSString stringWithFormat: @"http://www.youtube.com/get_video?fmt=18&video_id=%@&t=%@",
875 928 video_id, video_hash ];
876 929
877 930 [[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:[NSURL URLWithString:src]]
878   - withAppBundleIdentifier:self.launchedAppBundleIdentifier
  931 + withAppBundleIdentifier:[self launchedAppBundleIdentifier]
879 932 options:NSWorkspaceLaunchDefault
880 933 additionalEventParamDescriptor:[NSAppleEventDescriptor nullDescriptor]
881 934 launchIdentifiers:nil];
@@ -883,10 +936,10 @@ - (IBAction)downloadH264:(id)sender
883 936
884 937 - (IBAction)loadYouTubePage:(id)sender
885 938 {
886   - NSString* YouTubePageURL = [ NSString stringWithFormat: @"http://www.youtube.com/watch?v=%@",self.videoId ];
  939 + NSString* YouTubePageURL = [ NSString stringWithFormat: @"http://www.youtube.com/watch?v=%@", [self videoId] ];
887 940
888 941 [[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:[NSURL URLWithString:YouTubePageURL]]
889   - withAppBundleIdentifier:self.launchedAppBundleIdentifier
  942 + withAppBundleIdentifier:[self launchedAppBundleIdentifier]
890 943 options:NSWorkspaceLaunchDefault
891 944 additionalEventParamDescriptor:[NSAppleEventDescriptor nullDescriptor]
892 945 launchIdentifiers:nil];
@@ -928,24 +981,24 @@ - (void) _convertTypesForFlashContainerAfterDelay
928 981 DOMNodeList *nodeList = nil;
929 982 NSUInteger i;
930 983
931   - [self _convertTypesForElement:self.container];
  984 + [self _convertTypesForElement:[self container]];
932 985
933   - nodeList = [self.container getElementsByTagName:@"object"];
934   - for (i = 0; i < nodeList.length; i++) {
  986 + nodeList = [[self container] getElementsByTagName:@"object"];
  987 + for (i = 0; i < [nodeList length]; i++) {
935 988 [self _convertTypesForElement:(DOMElement *)[nodeList item:i]];
936 989 }
937 990
938   - nodeList = [self.container getElementsByTagName:@"embed"];
939   - for (i = 0; i < nodeList.length; i++) {
  991 + nodeList = [[self container] getElementsByTagName:@"embed"];
  992 + for (i = 0; i < [nodeList length]; i++) {
940 993 [self _convertTypesForElement:(DOMElement *)[nodeList item:i]];
941 994 }
942 995
943 996 // Remove & reinsert the node to persuade the plugin system to notice the type change:
944   - id parent = self.container.parentNode;
945   - id successor = self.container.nextSibling;