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.
5 #import <Cocoa/Cocoa.h>
7 #import "remoting/host/disconnect_window_mac.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"
20 @interface DisconnectWindowController()
25 const int kMaximumConnectedNameWidthInPixels = 600;
29 class DisconnectWindowMac : public HostWindow {
31 DisconnectWindowMac();
32 virtual ~DisconnectWindowMac();
34 // HostWindow overrides.
36 const base::WeakPtr<ClientSessionControl>& client_session_control)
40 DisconnectWindowController* window_controller_;
42 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowMac);
45 DisconnectWindowMac::DisconnectWindowMac()
46 : window_controller_(nil) {
49 DisconnectWindowMac::~DisconnectWindowMac() {
50 DCHECK(CalledOnValidThread());
52 // DisconnectWindowController is responsible for releasing itself in its
53 // windowWillClose: method.
54 [window_controller_ Hide];
55 window_controller_ = nil;
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);
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('/'));
71 [[DisconnectWindowController alloc] initWithCallback:disconnect_callback
73 [window_controller_ showWindow:nil];
77 scoped_ptr<HostWindow> HostWindow::CreateDisconnectWindow() {
78 return scoped_ptr<HostWindow>(new DisconnectWindowMac());
81 } // namespace remoting
83 @implementation DisconnectWindowController
84 - (id)initWithCallback:(const base::Closure&)disconnect_callback
85 username:(const std::string&)username {
86 self = [super initWithWindowNibName:@"disconnect_window"];
88 disconnect_callback_ = disconnect_callback;
89 username_ = UTF8ToUTF16(username);
98 - (IBAction)stopSharing:(id)sender {
99 if (!disconnect_callback_.is_null()) {
100 disconnect_callback_.Run();
105 return base::i18n::IsRTL();
109 disconnect_callback_.Reset();
113 - (void)windowDidLoad {
114 [connectedToField_ setStringValue:l10n_util::GetNSStringF(IDR_MESSAGE_SHARED,
116 [disconnectButton_ setTitle:l10n_util::GetNSString(IDR_STOP_SHARING_BUTTON)];
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);
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];
131 CGFloat oldDisconnectWidth = NSWidth([disconnectButton_ bounds]);
132 [disconnectButton_ sizeToFit];
133 NSRect disconnectFrame = [disconnectButton_ frame];
134 CGFloat newDisconnectWidth = NSWidth(disconnectFrame);
136 // Move the disconnect button appropriately.
137 disconnectFrame.origin.x += newConnectedWidth - oldConnectedWidth;
138 [disconnectButton_ setFrame:disconnectFrame];
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];
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];
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)];
166 - (void)windowWillClose:(NSNotification*)notification {
167 [self stopSharing:self];
174 @interface DisconnectWindow()
178 @implementation DisconnectWindow
180 - (id)initWithContentRect:(NSRect)contentRect
181 styleMask:(NSUInteger)aStyle
182 backing:(NSBackingStoreType)bufferingType
184 // Pass NSBorderlessWindowMask for the styleMask to remove the title bar.
185 self = [super initWithContentRect:contentRect
186 styleMask:NSBorderlessWindowMask
187 backing:bufferingType
191 // Set window to be clear and non-opaque so we can see through it.
192 [self setBackgroundColor:[NSColor clearColor]];
194 [self setMovableByWindowBackground:YES];
196 // Pull the window up to Status Level so that it always displays.
197 [self setLevel:NSStatusWindowLevel];
203 DCHECK([[self windowController] respondsToSelector:@selector(isRToL)]);
204 return [[self windowController] isRToL];
210 @interface DisconnectView()
214 @implementation DisconnectView
217 DCHECK([[self window] isKindOfClass:[DisconnectWindow class]]);
218 return [static_cast<DisconnectWindow*>([self window]) isRToL];
221 - (void)drawRect:(NSRect)rect {
222 // All magic numbers taken from screen shots provided by UX.
223 NSRect bounds = NSInsetRect([self bounds], 1, 1);
225 NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:bounds
228 NSColor *gray = [NSColor colorWithCalibratedWhite:0.91 alpha:1.0];
231 [path setLineWidth:4];
232 NSColor *green = [NSColor colorWithCalibratedRed:0.13
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;
245 NSColor *dark = [NSColor colorWithCalibratedWhite:0.70 alpha:1.0];
246 NSColor *light = [NSColor colorWithCalibratedWhite:0.97 alpha:1.0];
248 // Turn off aliasing so it's nice and crisp.
249 NSGraphicsContext *context = [NSGraphicsContext currentContext];
250 BOOL alias = [context shouldAntialias];
251 [context setShouldAntialias:NO];
253 // Handle bidirectional locales properly.
254 CGFloat inset = [self isRToL] ? NSMaxX(bounds) - kBaseInset - kDragHandleWidth
257 NSPoint top = NSMakePoint(inset, NSMidY(bounds) - kHeight / 2.0);
258 NSPoint bottom = NSMakePoint(inset, top.y + kHeight);
260 path = [NSBezierPath bezierPath];
261 [path moveToPoint:top];
262 [path lineToPoint:bottom];
268 path = [NSBezierPath bezierPath];
269 [path moveToPoint:top];
270 [path lineToPoint:bottom];
276 path = [NSBezierPath bezierPath];
277 [path moveToPoint:top];
278 [path lineToPoint:bottom];
284 path = [NSBezierPath bezierPath];
285 [path moveToPoint:top];
286 [path lineToPoint:bottom];
290 [context setShouldAntialias:alias];