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#include "chrome/browser/ui/cocoa/tab_contents/sad_tab_view.h"
6
7#include "base/logging.h"
8#include "base/strings/sys_string_conversions.h"
9#include "chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.h"
10#include "chrome/common/url_constants.h"
11#include "chrome/grit/generated_resources.h"
12#include "grit/theme_resources.h"
13#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
14#import "ui/base/cocoa/controls/hyperlink_text_view.h"
15#include "ui/base/l10n/l10n_util.h"
16#include "ui/base/l10n/l10n_util_mac.h"
17#include "ui/base/resource/resource_bundle.h"
18#include "ui/gfx/image/image.h"
19
20// Offset above vertical middle of page where contents of page start.
21static const CGFloat kSadTabOffset = -64;
22// Padding between icon and title.
23static const CGFloat kIconTitleSpacing = 20;
24// Padding between title and message.
25static const CGFloat kTitleMessageSpacing = 15;
26// Padding between message and link.
27static const CGFloat kMessageLinkSpacing = 15;
28// Paddings on left and right of page.
29static const CGFloat kTabHorzMargin = 13;
30
31@interface SadTabTextView : NSTextField
32
33- (id)initWithView:(SadTabView*)view withText:(int)textIds;
34
35@end
36
37@implementation SadTabTextView
38
39- (id)initWithView:(SadTabView*)view withText:(int)textIds {
40  if (self = [super init]) {
41    [self setTextColor:[NSColor whiteColor]];
42    [self setAlignment:NSCenterTextAlignment];
43    [self setStringValue:l10n_util::GetNSString(textIds)];
44    [self setEditable:NO];
45    [self setBezeled:NO];
46    [self setAutoresizingMask:
47        NSViewMinXMargin|NSViewWidthSizable|NSViewMaxXMargin|NSViewMinYMargin];
48    [view addSubview:self];
49  }
50  return self;
51}
52
53- (BOOL)isOpaque {
54  return YES;
55}
56
57@end
58
59@implementation SadTabView
60
61- (void)awakeFromNib {
62  // Load resource for image and set it.
63  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
64  NSImage* image = rb.GetNativeImageNamed(IDR_SAD_TAB).ToNSImage();
65  [image_ setImage:image];
66
67
68  // Initialize background color.
69  NSColor* backgroundColor = [[NSColor colorWithCalibratedRed:(35.0f/255.0f)
70                                                        green:(48.0f/255.0f)
71                                                         blue:(64.0f/255.0f)
72                                                        alpha:1.0] retain];
73  backgroundColor_.reset(backgroundColor);
74
75  // Set up the title.
76  title_.reset([[SadTabTextView alloc]
77      initWithView:self withText:IDS_SAD_TAB_TITLE]);
78  [title_ setFont:[NSFont boldSystemFontOfSize:[NSFont systemFontSize]]];
79  [title_ setBackgroundColor:backgroundColor];
80
81  // Set up the message.
82  message_.reset([[SadTabTextView alloc]
83      initWithView:self withText:IDS_SAD_TAB_MESSAGE]);
84  [message_ setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
85  [message_ setBackgroundColor:backgroundColor];
86
87  DCHECK(controller_);
88  [self initializeHelpText];
89}
90
91- (void)drawRect:(NSRect)dirtyRect {
92  // Paint background.
93  [backgroundColor_ set];
94  NSRectFill(dirtyRect);
95}
96
97- (void)resizeSubviewsWithOldSize:(NSSize)oldSize {
98  NSRect newBounds = [self bounds];
99  CGFloat maxWidth = NSWidth(newBounds) - (kTabHorzMargin * 2);
100  BOOL callSizeToFit = (messageSize_.width == 0);
101
102  // Set new frame origin for image.
103  NSRect iconFrame = [image_ frame];
104  CGFloat iconX = floorf((maxWidth - NSWidth(iconFrame)) / 2);
105  CGFloat iconY =
106      MIN(floorf((NSHeight(newBounds) - NSHeight(iconFrame)) / 2) -
107              kSadTabOffset,
108          NSHeight(newBounds) - NSHeight(iconFrame));
109  iconX = floorf(iconX);
110  iconY = floorf(iconY);
111  [image_ setFrameOrigin:NSMakePoint(iconX, iconY)];
112
113  // Set new frame origin for title.
114  if (callSizeToFit)
115    [title_ sizeToFit];
116  NSRect titleFrame = [title_ frame];
117  CGFloat titleX = floorf((maxWidth - NSWidth(titleFrame)) / 2);
118  CGFloat titleY = iconY - kIconTitleSpacing - NSHeight(titleFrame);
119  [title_ setFrameOrigin:NSMakePoint(titleX, titleY)];
120
121  // Set new frame for message, wrapping or unwrapping the text if necessary.
122  if (callSizeToFit) {
123    [message_ sizeToFit];
124    messageSize_ = [message_ frame].size;
125  }
126  NSRect messageFrame = [message_ frame];
127  if (messageSize_.width > maxWidth) {  // Need to wrap message.
128    [message_ setFrameSize:NSMakeSize(maxWidth, messageSize_.height)];
129    CGFloat heightChange =
130        [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:message_];
131    messageFrame.size.width = maxWidth;
132    messageFrame.size.height = messageSize_.height + heightChange;
133    messageFrame.origin.x = kTabHorzMargin;
134  } else {
135    if (!callSizeToFit) {
136      [message_ sizeToFit];
137      messageFrame = [message_ frame];
138    }
139    messageFrame.origin.x = floorf((maxWidth - NSWidth(messageFrame)) / 2);
140  }
141  messageFrame.origin.y =
142      titleY - kTitleMessageSpacing - NSHeight(messageFrame);
143  [message_ setFrame:messageFrame];
144
145  // Set new frame for help text and link.
146  if (help_) {
147    if (callSizeToFit)
148      [help_ sizeToFit];
149    CGFloat helpHeight = [help_ frame].size.height;
150    [help_ setFrameSize:NSMakeSize(maxWidth, helpHeight)];
151    // Set new frame origin for link.
152    NSRect helpFrame = [help_ frame];
153    CGFloat helpX = floorf((maxWidth - NSWidth(helpFrame)) / 2);
154    CGFloat helpY =
155        NSMinY(messageFrame) - kMessageLinkSpacing - NSHeight(helpFrame);
156    [help_ setFrameOrigin:NSMakePoint(helpX, helpY)];
157  }
158}
159
160- (void)removeHelpText {
161  if (help_) {
162    [help_ removeFromSuperview];
163    help_.reset(nil);
164  }
165}
166
167- (void)initializeHelpText {
168  // Programmatically create the help link. Note that the frame's initial
169  // height must be set for the programmatic resizing to work.
170  help_.reset(
171      [[HyperlinkTextView alloc] initWithFrame:NSMakeRect(0, 0, 1, 17)]);
172  [help_ setAutoresizingMask:
173      NSViewMinXMargin|NSViewWidthSizable|NSViewMaxXMargin|NSViewMinYMargin];
174  [self addSubview:help_];
175  [help_ setDelegate:self];
176
177  // Get the help text and link.
178  size_t linkOffset = 0;
179  NSString* helpMessage(base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(
180      IDS_SAD_TAB_HELP_MESSAGE, base::string16(), &linkOffset)));
181  NSString* helpLink = l10n_util::GetNSString(IDS_SAD_TAB_HELP_LINK);
182  NSFont* font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
183  [help_ setMessageAndLink:helpMessage
184                  withLink:helpLink
185                  atOffset:linkOffset
186                      font:font
187              messageColor:[NSColor whiteColor]
188                 linkColor:[NSColor whiteColor]];
189  [help_ setAlignment:NSCenterTextAlignment];
190}
191
192// Called when someone clicks on the embedded link.
193- (BOOL)textView:(NSTextView*)textView
194   clickedOnLink:(id)link
195         atIndex:(NSUInteger)charIndex {
196  if (controller_)
197    [controller_ openLearnMoreAboutCrashLink:nil];
198  return YES;
199}
200
201@end
202