OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
6 | 6 |
7 #include "base/logging.h" // for NOTREACHED() | 7 #include "base/logging.h" // for NOTREACHED() |
8 #include "base/mac/mac_util.h" | 8 #include "base/mac/mac_util.h" |
9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
10 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h" | 10 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h" |
11 #include "chrome/browser/tab_contents/link_infobar_delegate.h" | 11 #include "chrome/browser/tab_contents/link_infobar_delegate.h" |
12 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 12 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
13 #import "chrome/browser/ui/cocoa/animatable_view.h" | 13 #import "chrome/browser/ui/cocoa/animatable_view.h" |
14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
15 #include "chrome/browser/ui/cocoa/event_utils.h" | 15 #include "chrome/browser/ui/cocoa/event_utils.h" |
16 #include "chrome/browser/ui/cocoa/infobars/infobar.h" | 16 #include "chrome/browser/ui/cocoa/infobars/infobar.h" |
17 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h" | 17 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h" |
18 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h" | 18 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h" |
19 #import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h" | 19 #import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h" |
20 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" | 20 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" |
| 21 #import "chrome/browser/ui/cocoa/static_text_view.h" |
21 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" | 22 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
22 #include "ui/gfx/image/image.h" | 23 #include "ui/gfx/image/image.h" |
23 #include "webkit/glue/window_open_disposition.h" | 24 #include "webkit/glue/window_open_disposition.h" |
24 | 25 |
25 namespace { | 26 namespace { |
26 // Durations set to match the default SlideAnimation duration. | 27 // Durations set to match the default SlideAnimation duration. |
27 const float kAnimateOpenDuration = 0.12; | 28 const float kAnimateOpenDuration = 0.12; |
28 const float kAnimateCloseDuration = 0.12; | 29 const float kAnimateCloseDuration = 0.12; |
29 | |
30 // The baseline shift for text in the NSTextView. | |
31 const float kTextBaselineShift = -1.0; | |
32 } | 30 } |
33 | 31 |
34 // This simple subclass of |NSTextView| just doesn't show the (text) cursor | |
35 // (|NSTextView| displays the cursor with full keyboard accessibility enabled). | |
36 @interface InfoBarTextView : NSTextView | |
37 - (void)fixupCursor; | |
38 @end | |
39 | |
40 @implementation InfoBarTextView | |
41 | |
42 // Never draw the insertion point (otherwise, it shows up without any user | |
43 // action if full keyboard accessibility is enabled). | |
44 - (BOOL)shouldDrawInsertionPoint { | |
45 return NO; | |
46 } | |
47 | |
48 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange | |
49 granularity:(NSSelectionGranularity)granularity { | |
50 // Do not allow selections. | |
51 return NSMakeRange(0, 0); | |
52 } | |
53 | |
54 // Convince NSTextView to not show an I-Beam cursor when the cursor is over the | |
55 // text view but not over actual text. | |
56 // | |
57 // http://www.mail-archive.com/cocoa-dev@lists.apple.com/msg10791.html | |
58 // "NSTextView sets the cursor over itself dynamically, based on considerations | |
59 // including the text under the cursor. It does so in -mouseEntered:, | |
60 // -mouseMoved:, and -cursorUpdate:, so those would be points to consider | |
61 // overriding." | |
62 - (void)mouseMoved:(NSEvent*)e { | |
63 [super mouseMoved:e]; | |
64 [self fixupCursor]; | |
65 } | |
66 | |
67 - (void)mouseEntered:(NSEvent*)e { | |
68 [super mouseEntered:e]; | |
69 [self fixupCursor]; | |
70 } | |
71 | |
72 - (void)cursorUpdate:(NSEvent*)e { | |
73 [super cursorUpdate:e]; | |
74 [self fixupCursor]; | |
75 } | |
76 | |
77 - (void)fixupCursor { | |
78 if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]]) | |
79 [[NSCursor arrowCursor] set]; | |
80 } | |
81 | |
82 @end | |
83 | |
84 @interface InfoBarController (PrivateMethods) | 32 @interface InfoBarController (PrivateMethods) |
85 // Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil. | 33 // Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil. |
86 - (void)initializeLabel; | 34 - (void)initializeLabel; |
87 | 35 |
88 // Asks the container controller to remove the infobar for this delegate. This | 36 // Asks the container controller to remove the infobar for this delegate. This |
89 // call will trigger a notification that starts the infobar animating closed. | 37 // call will trigger a notification that starts the infobar animating closed. |
90 - (void)removeSelf; | 38 - (void)removeSelf; |
91 | 39 |
92 // Performs final cleanup after an animation is finished or stopped, including | 40 // Performs final cleanup after an animation is finished or stopped, including |
93 // notifying the InfoBarDelegate that the infobar was closed and removing the | 41 // notifying the InfoBarDelegate that the infobar was closed and removing the |
94 // infobar from its container, if necessary. | 42 // infobar from its container, if necessary. |
95 - (void)cleanUpAfterAnimation:(BOOL)finished; | 43 - (void)cleanUpAfterAnimation:(BOOL)finished; |
96 | 44 |
97 // Sets the info bar message to the specified |message|, with a hypertext | |
98 // style link. |link| will be inserted into message at |linkOffset|. | |
99 - (void)setLabelToMessage:(NSString*)message | |
100 withLink:(NSString*)link | |
101 atOffset:(NSUInteger)linkOffset; | |
102 | |
103 // Returns the point, in gradient view coordinates, at which the apex of the | 45 // Returns the point, in gradient view coordinates, at which the apex of the |
104 // infobar tip should be drawn. | 46 // infobar tip should be drawn. |
105 - (NSPoint)pointForTipApex; | 47 - (NSPoint)pointForTipApex; |
106 @end | 48 @end |
107 | 49 |
108 @implementation InfoBarController | 50 @implementation InfoBarController |
109 | 51 |
110 @synthesize containerController = containerController_; | 52 @synthesize containerController = containerController_; |
111 @synthesize delegate = delegate_; | 53 @synthesize delegate = delegate_; |
112 | 54 |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 } | 163 } |
222 | 164 |
223 - (void)addAdditionalControls { | 165 - (void)addAdditionalControls { |
224 // Default implementation does nothing. | 166 // Default implementation does nothing. |
225 } | 167 } |
226 | 168 |
227 - (void)infobarWillClose { | 169 - (void)infobarWillClose { |
228 // Default implementation does nothing. | 170 // Default implementation does nothing. |
229 } | 171 } |
230 | 172 |
231 - (void)setLabelToMessage:(NSString*)message { | |
232 NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; | |
233 NSFont* font = [NSFont labelFontOfSize: | |
234 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; | |
235 [attributes setObject:font | |
236 forKey:NSFontAttributeName]; | |
237 [attributes setObject:[NSCursor arrowCursor] | |
238 forKey:NSCursorAttributeName]; | |
239 [attributes setObject:[NSNumber numberWithFloat:kTextBaselineShift] | |
240 forKey:NSBaselineOffsetAttributeName]; | |
241 scoped_nsobject<NSAttributedString> attributedString( | |
242 [[NSAttributedString alloc] initWithString:message | |
243 attributes:attributes]); | |
244 [[label_.get() textStorage] setAttributedString:attributedString]; | |
245 } | |
246 | |
247 - (void)removeButtons { | 173 - (void)removeButtons { |
248 // Extend the label all the way across. | 174 // Extend the label all the way across. |
249 NSRect labelFrame = [label_.get() frame]; | 175 NSRect labelFrame = [label_.get() frame]; |
250 labelFrame.size.width = NSMaxX([cancelButton_ frame]) - NSMinX(labelFrame); | 176 labelFrame.size.width = NSMaxX([cancelButton_ frame]) - NSMinX(labelFrame); |
251 [okButton_ removeFromSuperview]; | 177 [okButton_ removeFromSuperview]; |
252 [cancelButton_ removeFromSuperview]; | 178 [cancelButton_ removeFromSuperview]; |
253 [label_.get() setFrame:labelFrame]; | 179 [label_.get() setFrame:labelFrame]; |
254 } | 180 } |
255 | 181 |
256 @end | 182 @end |
257 | 183 |
258 @implementation InfoBarController (PrivateMethods) | 184 @implementation InfoBarController (PrivateMethods) |
259 | 185 |
260 - (void)initializeLabel { | 186 - (void)initializeLabel { |
261 // Replace the label placeholder NSTextField with the real label NSTextView. | 187 // Replace the label placeholder NSTextField with the real label NSTextView. |
262 // The former doesn't show links in a nice way, but the latter can't be added | 188 // The former doesn't show links in a nice way, but the latter can't be added |
263 // in IB without a containing scroll view, so create the NSTextView | 189 // in IB without a containing scroll view, so create the NSTextView |
264 // programmatically. | 190 // programmatically. |
265 label_.reset([[InfoBarTextView alloc] | 191 label_.reset([[StaticTextView alloc] |
266 initWithFrame:[labelPlaceholder_ frame]]); | 192 initWithFrame:[labelPlaceholder_ frame]]); |
267 [label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]]; | 193 [label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]]; |
268 [[labelPlaceholder_ superview] | 194 [[labelPlaceholder_ superview] |
269 replaceSubview:labelPlaceholder_ with:label_.get()]; | 195 replaceSubview:labelPlaceholder_ with:label_.get()]; |
270 labelPlaceholder_ = nil; // Now released. | 196 labelPlaceholder_ = nil; // Now released. |
271 [label_.get() setDelegate:self]; | 197 [label_.get() setDelegate:self]; |
272 [label_.get() setEditable:NO]; | 198 [label_.get() setEditable:NO]; |
273 [label_.get() setDrawsBackground:NO]; | 199 [label_.get() setDrawsBackground:NO]; |
274 [label_.get() setHorizontallyResizable:NO]; | 200 [label_.get() setHorizontallyResizable:NO]; |
275 [label_.get() setVerticallyResizable:NO]; | 201 [label_.get() setVerticallyResizable:NO]; |
(...skipping 30 matching lines...) Expand all Loading... |
306 } | 232 } |
307 | 233 |
308 - (void)animationDidStop:(NSAnimation*)animation { | 234 - (void)animationDidStop:(NSAnimation*)animation { |
309 [self cleanUpAfterAnimation:NO]; | 235 [self cleanUpAfterAnimation:NO]; |
310 } | 236 } |
311 | 237 |
312 - (void)animationDidEnd:(NSAnimation*)animation { | 238 - (void)animationDidEnd:(NSAnimation*)animation { |
313 [self cleanUpAfterAnimation:YES]; | 239 [self cleanUpAfterAnimation:YES]; |
314 } | 240 } |
315 | 241 |
316 // TODO(joth): This method factors out some common functionality between the | |
317 // various derived infobar classes, however the class hierarchy itself could | |
318 // use refactoring to reduce this duplication. http://crbug.com/38924 | |
319 - (void)setLabelToMessage:(NSString*)message | |
320 withLink:(NSString*)link | |
321 atOffset:(NSUInteger)linkOffset { | |
322 if (linkOffset == std::wstring::npos) { | |
323 // linkOffset == std::wstring::npos means the link should be right-aligned, | |
324 // which is not supported on Mac (http://crbug.com/47728). | |
325 NOTIMPLEMENTED(); | |
326 linkOffset = [message length]; | |
327 } | |
328 // Create an attributes dictionary for the entire message. We have | |
329 // to explicitly set the control's font. We also override the cursor to give | |
330 // us the normal cursor rather than the text insertion cursor. | |
331 NSMutableDictionary* linkAttributes = [NSMutableDictionary dictionary]; | |
332 [linkAttributes setObject:[NSCursor arrowCursor] | |
333 forKey:NSCursorAttributeName]; | |
334 NSFont* font = [NSFont labelFontOfSize: | |
335 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; | |
336 [linkAttributes setObject:font | |
337 forKey:NSFontAttributeName]; | |
338 | |
339 // Create the attributed string for the main message text. | |
340 scoped_nsobject<NSMutableAttributedString> infoText( | |
341 [[NSMutableAttributedString alloc] initWithString:message]); | |
342 [infoText.get() addAttributes:linkAttributes | |
343 range:NSMakeRange(0, [infoText.get() length])]; | |
344 // Add additional attributes to style the link text appropriately as | |
345 // well as linkify it. | |
346 [linkAttributes setObject:[NSColor blueColor] | |
347 forKey:NSForegroundColorAttributeName]; | |
348 [linkAttributes setObject:[NSNumber numberWithBool:YES] | |
349 forKey:NSUnderlineStyleAttributeName]; | |
350 [linkAttributes setObject:[NSCursor pointingHandCursor] | |
351 forKey:NSCursorAttributeName]; | |
352 [linkAttributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle] | |
353 forKey:NSUnderlineStyleAttributeName]; | |
354 [linkAttributes setObject:[NSString string] // dummy value | |
355 forKey:NSLinkAttributeName]; | |
356 | |
357 // Insert the link text into the string at the appropriate offset. | |
358 scoped_nsobject<NSAttributedString> attributedString( | |
359 [[NSAttributedString alloc] initWithString:link | |
360 attributes:linkAttributes]); | |
361 [infoText.get() insertAttributedString:attributedString.get() | |
362 atIndex:linkOffset]; | |
363 // The entire text needs a baseline shift. | |
364 [infoText addAttribute:NSBaselineOffsetAttributeName | |
365 value:[NSNumber numberWithDouble:kTextBaselineShift] | |
366 range:NSMakeRange(0, [infoText length])]; | |
367 | |
368 // Update the label view with the new text. | |
369 [[label_.get() textStorage] setAttributedString:infoText]; | |
370 } | |
371 | |
372 - (NSPoint)pointForTipApex { | 242 - (NSPoint)pointForTipApex { |
373 BrowserWindowController* windowController = | 243 BrowserWindowController* windowController = |
374 [containerController_ browserWindowController]; | 244 [containerController_ browserWindowController]; |
375 if (!windowController) { | 245 if (!windowController) { |
376 // This should only happen in unit tests. | 246 // This should only happen in unit tests. |
377 return NSZeroPoint; | 247 return NSZeroPoint; |
378 } | 248 } |
379 | 249 |
380 LocationBarViewMac* locationBar = [windowController locationBarBridge]; | 250 LocationBarViewMac* locationBar = [windowController locationBarBridge]; |
381 NSPoint point = locationBar->GetPageInfoBubblePoint(); | 251 NSPoint point = locationBar->GetPageInfoBubblePoint(); |
(...skipping 16 matching lines...) Expand all Loading... |
398 // textView:clickedOnLink:atIndex: and intercept clicks. | 268 // textView:clickedOnLink:atIndex: and intercept clicks. |
399 // | 269 // |
400 - (void)addAdditionalControls { | 270 - (void)addAdditionalControls { |
401 // No buttons. | 271 // No buttons. |
402 [self removeButtons]; | 272 [self removeButtons]; |
403 | 273 |
404 LinkInfoBarDelegate* delegate = delegate_->AsLinkInfoBarDelegate(); | 274 LinkInfoBarDelegate* delegate = delegate_->AsLinkInfoBarDelegate(); |
405 DCHECK(delegate); | 275 DCHECK(delegate); |
406 size_t offset = std::wstring::npos; | 276 size_t offset = std::wstring::npos; |
407 string16 message = delegate->GetMessageTextWithOffset(&offset); | 277 string16 message = delegate->GetMessageTextWithOffset(&offset); |
408 [self setLabelToMessage:base::SysUTF16ToNSString(message) | 278 string16 link = delegate->GetLinkText(); |
409 withLink:base::SysUTF16ToNSString(delegate->GetLinkText()) | 279 NSFont* font = [NSFont labelFontOfSize: |
410 atOffset:offset]; | 280 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; |
| 281 StaticTextView* view = (StaticTextView*)label_.get(); |
| 282 [view setMessageAndLink:base::SysUTF16ToNSString(message) |
| 283 withLink:base::SysUTF16ToNSString(link) |
| 284 atOffset:offset |
| 285 font:font |
| 286 messageColor:[NSColor blackColor] |
| 287 linkColor:[NSColor blueColor]]; |
411 } | 288 } |
412 | 289 |
413 // Called when someone clicks on the link in the infobar. This method | 290 // Called when someone clicks on the link in the infobar. This method |
414 // is called by the InfobarTextField on its delegate (the | 291 // is called by the InfobarTextField on its delegate (the |
415 // LinkInfoBarController). | 292 // LinkInfoBarController). |
416 - (void)linkClicked { | 293 - (void)linkClicked { |
417 WindowOpenDisposition disposition = | 294 WindowOpenDisposition disposition = |
418 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); | 295 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); |
419 if (delegate_ && | 296 if (delegate_ && |
420 delegate_->AsLinkInfoBarDelegate()->LinkClicked(disposition)) { | 297 delegate_->AsLinkInfoBarDelegate()->LinkClicked(disposition)) { |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 | 394 |
518 NSRect frame = [label_.get() frame]; | 395 NSRect frame = [label_.get() frame]; |
519 DCHECK(rightEdge > NSMinX(frame)) | 396 DCHECK(rightEdge > NSMinX(frame)) |
520 << "Need to make the xib larger to handle buttons with text this long"; | 397 << "Need to make the xib larger to handle buttons with text this long"; |
521 frame.size.width = rightEdge - NSMinX(frame); | 398 frame.size.width = rightEdge - NSMinX(frame); |
522 [label_.get() setFrame:frame]; | 399 [label_.get() setFrame:frame]; |
523 | 400 |
524 // Set the text and link. | 401 // Set the text and link. |
525 NSString* message = base::SysUTF16ToNSString(delegate->GetMessageText()); | 402 NSString* message = base::SysUTF16ToNSString(delegate->GetMessageText()); |
526 string16 link = delegate->GetLinkText(); | 403 string16 link = delegate->GetLinkText(); |
527 if (link.empty()) { | 404 if (!link.empty()) { |
528 // Simple case: no link, so just set the message directly. | |
529 [self setLabelToMessage:message]; | |
530 } else { | |
531 // Inserting the link unintentionally causes the text to have a slightly | |
532 // different result to the simple case above: text is truncated on word | |
533 // boundaries (if needed) rather than elided with ellipses. | |
534 | |
535 // Add spacing between the label and the link. | 405 // Add spacing between the label and the link. |
536 message = [message stringByAppendingString:@" "]; | 406 message = [message stringByAppendingString:@" "]; |
537 [self setLabelToMessage:message | |
538 withLink:base::SysUTF16ToNSString(link) | |
539 atOffset:[message length]]; | |
540 } | 407 } |
| 408 NSFont* font = [NSFont labelFontOfSize: |
| 409 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; |
| 410 StaticTextView* view = (StaticTextView*)label_.get(); |
| 411 [view setMessageAndLink:message |
| 412 withLink:base::SysUTF16ToNSString(link) |
| 413 atOffset:[message length] |
| 414 font:font |
| 415 messageColor:[NSColor blackColor] |
| 416 linkColor:[NSColor blueColor]]; |
541 } | 417 } |
542 | 418 |
543 // Called when someone clicks on the link in the infobar. This method | 419 // Called when someone clicks on the link in the infobar. This method |
544 // is called by the InfobarTextField on its delegate (the | 420 // is called by the InfobarTextField on its delegate (the |
545 // LinkInfoBarController). | 421 // LinkInfoBarController). |
546 - (void)linkClicked { | 422 - (void)linkClicked { |
547 WindowOpenDisposition disposition = | 423 WindowOpenDisposition disposition = |
548 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); | 424 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); |
549 if (delegate_ && | 425 if (delegate_ && |
550 delegate_->AsConfirmInfoBarDelegate()->LinkClicked(disposition)) | 426 delegate_->AsConfirmInfoBarDelegate()->LinkClicked(disposition)) |
(...skipping 10 matching lines...) Expand all Loading... |
561 LinkInfoBarController* controller = | 437 LinkInfoBarController* controller = |
562 [[LinkInfoBarController alloc] initWithDelegate:this owner:owner]; | 438 [[LinkInfoBarController alloc] initWithDelegate:this owner:owner]; |
563 return new InfoBar(controller, this); | 439 return new InfoBar(controller, this); |
564 } | 440 } |
565 | 441 |
566 InfoBar* ConfirmInfoBarDelegate::CreateInfoBar(TabContentsWrapper* owner) { | 442 InfoBar* ConfirmInfoBarDelegate::CreateInfoBar(TabContentsWrapper* owner) { |
567 ConfirmInfoBarController* controller = | 443 ConfirmInfoBarController* controller = |
568 [[ConfirmInfoBarController alloc] initWithDelegate:this owner:owner]; | 444 [[ConfirmInfoBarController alloc] initWithDelegate:this owner:owner]; |
569 return new InfoBar(controller, this); | 445 return new InfoBar(controller, this); |
570 } | 446 } |
OLD | NEW |