Home | History | Annotate | Download | only in host
      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 <Cocoa/Cocoa.h>
      6 
      7 #import "remoting/host/disconnect_window_mac.h"
      8 
      9 #include "base/bind.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/i18n/rtl.h"
     12 #include "base/memory/weak_ptr.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/sys_string_conversions.h"
     15 #include "remoting/base/string_resources.h"
     16 #include "remoting/host/client_session_control.h"
     17 #include "remoting/host/host_window.h"
     18 #include "ui/base/l10n/l10n_util_mac.h"
     19 
     20 @interface DisconnectWindowController()
     21 - (BOOL)isRToL;
     22 - (void)Hide;
     23 @end
     24 
     25 const int kMaximumConnectedNameWidthInPixels = 600;
     26 
     27 namespace remoting {
     28 
     29 class DisconnectWindowMac : public HostWindow {
     30  public:
     31   DisconnectWindowMac();
     32   virtual ~DisconnectWindowMac();
     33 
     34   // HostWindow overrides.
     35   virtual void Start(
     36       const base::WeakPtr<ClientSessionControl>& client_session_control)
     37       OVERRIDE;
     38 
     39  private:
     40   DisconnectWindowController* window_controller_;
     41 
     42   DISALLOW_COPY_AND_ASSIGN(DisconnectWindowMac);
     43 };
     44 
     45 DisconnectWindowMac::DisconnectWindowMac()
     46     : window_controller_(nil) {
     47 }
     48 
     49 DisconnectWindowMac::~DisconnectWindowMac() {
     50   DCHECK(CalledOnValidThread());
     51 
     52   // DisconnectWindowController is responsible for releasing itself in its
     53   // windowWillClose: method.
     54   [window_controller_ Hide];
     55   window_controller_ = nil;
     56 }
     57 
     58 void DisconnectWindowMac::Start(
     59     const base::WeakPtr<ClientSessionControl>& client_session_control) {
     60   DCHECK(CalledOnValidThread());
     61   DCHECK(client_session_control);
     62   DCHECK(window_controller_ == nil);
     63 
     64   // Create the window.
     65   base::Closure disconnect_callback =
     66       base::Bind(&ClientSessionControl::DisconnectSession,
     67                  client_session_control);
     68   std::string client_jid = client_session_control->client_jid();
     69   std::string username = client_jid.substr(0, client_jid.find('/'));
     70   window_controller_ =
     71       [[DisconnectWindowController alloc] initWithCallback:disconnect_callback
     72                                                   username:username];
     73   [window_controller_ showWindow:nil];
     74 }
     75 
     76 // static
     77 scoped_ptr<HostWindow> HostWindow::CreateDisconnectWindow() {
     78   return scoped_ptr<HostWindow>(new DisconnectWindowMac());
     79 }
     80 
     81 }  // namespace remoting
     82 
     83 @implementation DisconnectWindowController
     84 - (id)initWithCallback:(const base::Closure&)disconnect_callback
     85               username:(const std::string&)username {
     86   self = [super initWithWindowNibName:@"disconnect_window"];
     87   if (self) {
     88     disconnect_callback_ = disconnect_callback;
     89     username_ = base::UTF8ToUTF16(username);
     90   }
     91   return self;
     92 }
     93 
     94 - (void)dealloc {
     95   [super dealloc];
     96 }
     97 
     98 - (IBAction)stopSharing:(id)sender {
     99   if (!disconnect_callback_.is_null()) {
    100     disconnect_callback_.Run();
    101   }
    102 }
    103 
    104 - (BOOL)isRToL {
    105   return base::i18n::IsRTL();
    106 }
    107 
    108 - (void)Hide {
    109   disconnect_callback_.Reset();
    110   [self close];
    111 }
    112 
    113 - (void)windowDidLoad {
    114   [connectedToField_ setStringValue:l10n_util::GetNSStringF(IDS_MESSAGE_SHARED,
    115                                                             username_)];
    116   [disconnectButton_ setTitle:l10n_util::GetNSString(IDS_STOP_SHARING_BUTTON)];
    117 
    118   // Resize the window dynamically based on the content.
    119   CGFloat oldConnectedWidth = NSWidth([connectedToField_ bounds]);
    120   [connectedToField_ sizeToFit];
    121   NSRect connectedToFrame = [connectedToField_ frame];
    122   CGFloat newConnectedWidth = NSWidth(connectedToFrame);
    123 
    124   // Set a max width for the connected to text field.
    125   if (newConnectedWidth > kMaximumConnectedNameWidthInPixels) {
    126     newConnectedWidth = kMaximumConnectedNameWidthInPixels;
    127     connectedToFrame.size.width = newConnectedWidth;
    128     [connectedToField_ setFrame:connectedToFrame];
    129   }
    130 
    131   CGFloat oldDisconnectWidth = NSWidth([disconnectButton_ bounds]);
    132   [disconnectButton_ sizeToFit];
    133   NSRect disconnectFrame = [disconnectButton_ frame];
    134   CGFloat newDisconnectWidth = NSWidth(disconnectFrame);
    135 
    136   // Move the disconnect button appropriately.
    137   disconnectFrame.origin.x += newConnectedWidth - oldConnectedWidth;
    138   [disconnectButton_ setFrame:disconnectFrame];
    139 
    140   // Then resize the window appropriately
    141   NSWindow *window = [self window];
    142   NSRect windowFrame = [window frame];
    143   windowFrame.size.width += (newConnectedWidth - oldConnectedWidth +
    144                              newDisconnectWidth - oldDisconnectWidth);
    145   [window setFrame:windowFrame display:NO];
    146 
    147   if ([self isRToL]) {
    148     // Handle right to left case
    149     CGFloat buttonInset = NSWidth(windowFrame) - NSMaxX(disconnectFrame);
    150     CGFloat buttonTextSpacing
    151         = NSMinX(disconnectFrame) - NSMaxX(connectedToFrame);
    152     disconnectFrame.origin.x = buttonInset;
    153     connectedToFrame.origin.x = NSMaxX(disconnectFrame) + buttonTextSpacing;
    154     [connectedToField_ setFrame:connectedToFrame];
    155     [disconnectButton_ setFrame:disconnectFrame];
    156   }
    157 
    158   // Center the window at the bottom of the screen, above the dock (if present).
    159   NSRect desktopRect = [[NSScreen mainScreen] visibleFrame];
    160   NSRect windowRect = [[self window] frame];
    161   CGFloat x = (NSWidth(desktopRect) - NSWidth(windowRect)) / 2;
    162   CGFloat y = NSMinY(desktopRect);
    163   [[self window] setFrameOrigin:NSMakePoint(x, y)];
    164 }
    165 
    166 - (void)windowWillClose:(NSNotification*)notification {
    167   [self stopSharing:self];
    168   [self autorelease];
    169 }
    170 
    171 @end
    172 
    173 
    174 @interface DisconnectWindow()
    175 - (BOOL)isRToL;
    176 @end
    177 
    178 @implementation DisconnectWindow
    179 
    180 - (id)initWithContentRect:(NSRect)contentRect
    181                 styleMask:(NSUInteger)aStyle
    182                   backing:(NSBackingStoreType)bufferingType
    183                   defer:(BOOL)flag {
    184   // Pass NSBorderlessWindowMask for the styleMask to remove the title bar.
    185   self = [super initWithContentRect:contentRect
    186                           styleMask:NSBorderlessWindowMask
    187                             backing:bufferingType
    188                               defer:flag];
    189 
    190   if (self) {
    191     // Set window to be clear and non-opaque so we can see through it.
    192     [self setBackgroundColor:[NSColor clearColor]];
    193     [self setOpaque:NO];
    194     [self setMovableByWindowBackground:YES];
    195 
    196     // Pull the window up to Status Level so that it always displays.
    197     [self setLevel:NSStatusWindowLevel];
    198   }
    199   return self;
    200 }
    201 
    202 - (BOOL)isRToL {
    203   DCHECK([[self windowController] respondsToSelector:@selector(isRToL)]);
    204   return [[self windowController] isRToL];
    205 }
    206 
    207 @end
    208 
    209 
    210 @interface DisconnectView()
    211 - (BOOL)isRToL;
    212 @end
    213 
    214 @implementation DisconnectView
    215 
    216 - (BOOL)isRToL {
    217   DCHECK([[self window] isKindOfClass:[DisconnectWindow class]]);
    218   return [static_cast<DisconnectWindow*>([self window]) isRToL];
    219 }
    220 
    221 - (void)drawRect:(NSRect)rect {
    222   // All magic numbers taken from screen shots provided by UX.
    223   NSRect bounds = NSInsetRect([self bounds], 1, 1);
    224 
    225   NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:bounds
    226                                                        xRadius:5
    227                                                        yRadius:5];
    228   NSColor *gray = [NSColor colorWithCalibratedWhite:0.91 alpha:1.0];
    229   [gray setFill];
    230   [path fill];
    231   [path setLineWidth:4];
    232   NSColor *green = [NSColor colorWithCalibratedRed:0.13
    233                                              green:0.69
    234                                               blue:0.11
    235                                              alpha:1.0];
    236   [green setStroke];
    237   [path stroke];
    238 
    239 
    240   // Draw drag handle on proper side
    241   const CGFloat kHeight = 21.0;
    242   const CGFloat kBaseInset = 12.0;
    243   const CGFloat kDragHandleWidth = 5.0;
    244 
    245   NSColor *dark = [NSColor colorWithCalibratedWhite:0.70 alpha:1.0];
    246   NSColor *light = [NSColor colorWithCalibratedWhite:0.97 alpha:1.0];
    247 
    248   // Turn off aliasing so it's nice and crisp.
    249   NSGraphicsContext *context = [NSGraphicsContext currentContext];
    250   BOOL alias = [context shouldAntialias];
    251   [context setShouldAntialias:NO];
    252 
    253   // Handle bidirectional locales properly.
    254   CGFloat inset = [self isRToL] ? NSMaxX(bounds) - kBaseInset - kDragHandleWidth
    255                                 : kBaseInset;
    256 
    257   NSPoint top = NSMakePoint(inset, NSMidY(bounds) - kHeight / 2.0);
    258   NSPoint bottom = NSMakePoint(inset, top.y + kHeight);
    259 
    260   path = [NSBezierPath bezierPath];
    261   [path moveToPoint:top];
    262   [path lineToPoint:bottom];
    263   [dark setStroke];
    264   [path stroke];
    265 
    266   top.x += 1;
    267   bottom.x += 1;
    268   path = [NSBezierPath bezierPath];
    269   [path moveToPoint:top];
    270   [path lineToPoint:bottom];
    271   [light setStroke];
    272   [path stroke];
    273 
    274   top.x += 2;
    275   bottom.x += 2;
    276   path = [NSBezierPath bezierPath];
    277   [path moveToPoint:top];
    278   [path lineToPoint:bottom];
    279   [dark setStroke];
    280   [path stroke];
    281 
    282   top.x += 1;
    283   bottom.x += 1;
    284   path = [NSBezierPath bezierPath];
    285   [path moveToPoint:top];
    286   [path lineToPoint:bottom];
    287   [light setStroke];
    288   [path stroke];
    289 
    290   [context setShouldAntialias:alias];
    291 }
    292 
    293 @end
    294