Home | History | Annotate | Download | only in cocoa
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #import "chrome/browser/ui/cocoa/custom_frame_view.h"
      6 
      7 #import <objc/runtime.h>
      8 #import <Carbon/Carbon.h>
      9 
     10 #include "base/logging.h"
     11 #include "base/mac/mac_util.h"
     12 #include "base/mac/scoped_nsautorelease_pool.h"
     13 
     14 namespace {
     15 BOOL gCanDrawTitle = NO;
     16 BOOL gCanGetCornerRadius = NO;
     17 }  // namespace
     18 
     19 @interface NSView (Swizzles)
     20 - (void)drawRectOriginal:(NSRect)rect;
     21 - (NSPoint)_fullScreenButtonOriginOriginal;
     22 @end
     23 
     24 @interface NSWindow (FramedBrowserWindow)
     25 - (NSPoint)fullScreenButtonOriginAdjustment;
     26 @end
     27 
     28 @implementation NSWindow (CustomFrameView)
     29 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
     30   [view drawRectOriginal:rect];
     31 }
     32 @end
     33 
     34 @interface CustomFrameView : NSView
     35 
     36 @end
     37 
     38 @implementation CustomFrameView
     39 
     40 // This is where we swizzle drawRect, and add in two methods that we
     41 // need. If any of these fail it shouldn't affect the functionality of the
     42 // others. If they all fail, we will lose window frame theming and
     43 // roll overs for our close widgets, but things should still function
     44 // correctly.
     45 + (void)load {
     46   base::mac::ScopedNSAutoreleasePool pool;
     47 
     48   // On 10.8+ the background for textured windows are no longer drawn by
     49   // NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>.
     50   Class borderViewClass = NSClassFromString(
     51       base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame");
     52   DCHECK(borderViewClass);
     53   if (!borderViewClass) return;
     54 
     55   // Exchange draw rect.
     56   Method m0 = class_getInstanceMethod([self class], @selector(drawRect:));
     57   DCHECK(m0);
     58   if (m0) {
     59     BOOL didAdd = class_addMethod(borderViewClass,
     60                                   @selector(drawRectOriginal:),
     61                                   method_getImplementation(m0),
     62                                   method_getTypeEncoding(m0));
     63     DCHECK(didAdd);
     64     if (didAdd) {
     65       Method m1 = class_getInstanceMethod(borderViewClass,
     66                                           @selector(drawRect:));
     67       Method m2 = class_getInstanceMethod(borderViewClass,
     68                                           @selector(drawRectOriginal:));
     69       DCHECK(m1 && m2);
     70       if (m1 && m2) {
     71         method_exchangeImplementations(m1, m2);
     72       }
     73     }
     74   }
     75 
     76   // Swizzle the method that sets the origin for the Lion fullscreen button. Do
     77   // nothing if it cannot be found.
     78   m0 = class_getInstanceMethod([self class],
     79                                @selector(_fullScreenButtonOrigin));
     80   if (m0) {
     81     BOOL didAdd = class_addMethod(borderViewClass,
     82                                   @selector(_fullScreenButtonOriginOriginal),
     83                                   method_getImplementation(m0),
     84                                   method_getTypeEncoding(m0));
     85     if (didAdd) {
     86       Method m1 = class_getInstanceMethod(borderViewClass,
     87                                           @selector(_fullScreenButtonOrigin));
     88       Method m2 = class_getInstanceMethod(borderViewClass,
     89           @selector(_fullScreenButtonOriginOriginal));
     90       if (m1 && m2) {
     91         method_exchangeImplementations(m1, m2);
     92       }
     93     }
     94   }
     95 }
     96 
     97 + (BOOL)canDrawTitle {
     98   return gCanDrawTitle;
     99 }
    100 
    101 + (BOOL)canGetCornerRadius {
    102   return gCanGetCornerRadius;
    103 }
    104 
    105 - (id)initWithFrame:(NSRect)frame {
    106   // This class is not for instantiating.
    107   [self doesNotRecognizeSelector:_cmd];
    108   return nil;
    109 }
    110 
    111 - (id)initWithCoder:(NSCoder*)coder {
    112   // This class is not for instantiating.
    113   [self doesNotRecognizeSelector:_cmd];
    114   return nil;
    115 }
    116 
    117 // Here is our custom drawing for our frame.
    118 - (void)drawRect:(NSRect)rect {
    119   // Delegate drawing to the window, whose default implementation (above) is to
    120   // call into the original implementation.
    121   [[self window] drawCustomFrameRect:rect forView:self];
    122 }
    123 
    124 // Override to move the fullscreen button to the left of the profile avatar.
    125 - (NSPoint)_fullScreenButtonOrigin {
    126   NSWindow* window = [self window];
    127   NSPoint offset = NSZeroPoint;
    128 
    129   if ([window respondsToSelector:@selector(fullScreenButtonOriginAdjustment)])
    130     offset = [window fullScreenButtonOriginAdjustment];
    131 
    132   NSPoint origin = [self _fullScreenButtonOriginOriginal];
    133   origin.x += offset.x;
    134   origin.y += offset.y;
    135   return origin;
    136 }
    137 
    138 @end
    139