Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(563)

Side by Side Diff: chrome/browser/cocoa/translate_infobar.mm

Issue 2815013: Refactor the translate infobars on mac to match the new windows code. (Closed)
Patch Set: Move unittest stuff back to class files Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 #import "chrome/browser/cocoa/translate_infobar.h"
7
8 #include "app/l10n_util.h"
9 #include "base/logging.h" // for NOTREACHED()
10 #include "base/mac_util.h"
11 #include "base/sys_string_conversions.h"
12 #include "chrome/app/chrome_dll_resource.h"
13 #import "chrome/browser/cocoa/hover_close_button.h"
14 #include "chrome/browser/cocoa/infobar.h"
15 #import "chrome/browser/cocoa/infobar_controller.h"
16 #import "chrome/browser/cocoa/infobar_gradient_view.h"
17 #include "chrome/browser/tab_contents/tab_contents.h"
18 #include "chrome/browser/translate/page_translated_details.h"
19 #include "chrome/common/notification_service.h"
20 #include "grit/generated_resources.h"
21 #include "grit/locale_settings.h"
22 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
23
24 // http://crbug.com/46663 disabled since it never worked.
25 #define DISABLE_VERIFY_CONTROL_ORDER 1
26
27 // Colors for translate infobar gradient background.
28 const int kGreyTopColor[] = {0xC0, 0xC0, 0xC0};
29 const int kGreyBottomColor[] = {0xCC, 0xCC, 0xCC};
30
31 #pragma mark Anonymous helper functions.
32 namespace {
33
34 // Move the |toMove| view |spacing| pixels before/after the |anchor| view.
35 // |after| signifies the side of |anchor| on which to place |toMove|.
36 void MoveControl(NSView* anchor, NSView* toMove, int spacing, bool after) {
37 NSRect anchorFrame = [anchor frame];
38 NSRect toMoveFrame = [toMove frame];
39
40 // At the time of this writing, OS X doesn't natively support BiDi UIs, but
41 // it doesn't hurt to be forward looking.
42 bool toRight = after;
43
44 if (toRight) {
45 toMoveFrame.origin.x = NSMaxX(anchorFrame) + spacing;
46 } else {
47 // Place toMove to theleft of anchor.
48 toMoveFrame.origin.x = NSMinX(anchorFrame) -
49 spacing - NSWidth(toMoveFrame);
50 }
51 [toMove setFrame:toMoveFrame];
52 }
53
54 // Vertically center |toMove| in its container.
55 void VerticallyCenterView(NSView *toMove) {
56 NSRect superViewFrame = [[toMove superview] frame];
57 NSRect viewFrame = [toMove frame];
58
59 viewFrame.origin.y =
60 floor((NSHeight(superViewFrame) - NSHeight(viewFrame))/2.0);
61 [toMove setFrame:viewFrame];
62 }
63
64 // Check that the control |before| is ordered visually before the |after|
65 // control.
66 // Also, check that there is space between them.
67 // http://crbug.com/46663 the code below seems to be the reverse of this
68 // comment.
69 #if !defined(DISABLE_VERIFY_CONTROL_ORDER)
70 bool VerifyControlOrderAndSpacing(id before, id after) {
71 CGFloat spaceBetweenControls = 0;
72 if (before && after) {
73 // When messaging nil, the rects won't always be zeroed (only sizeof(id) is
74 // going to be zeroed by the Objective-C runtime, rest will be uninitialized
75 // memory).
76 NSRect beforeFrame = [before frame];
77 NSRect afterFrame = [after frame];
78 spaceBetweenControls = NSMaxX(beforeFrame) - NSMinX(afterFrame);
79 // RTL case to be used when we have an RTL version of this UI.
80 // spaceBetweenControls = NSMaxX(afterFrame) - NSMinX(beforeFrame);
81
82 }
83
84 return (spaceBetweenControls >= 0);
85 }
86 #endif // !defined(DISABLE_VERIFY_CONTROL_ORDER)
87
88 // Creates a label control in the style we need for the translate infobar's
89 // labels within |bounds|.
90 NSTextField* CreateLabel(NSRect bounds) {
91 NSTextField* ret = [[NSTextField alloc] initWithFrame:bounds];
92 [ret setEditable:NO];
93 [ret setDrawsBackground:NO];
94 [ret setBordered:NO];
95 return ret;
96 }
97
98 // Adds an item with the specified properties to |menu|.
99 void AddMenuItem(NSMenu *menu, id target, NSString* title, int tag,
100 bool enabled, bool checked) {
101 NSMenuItem* item = [[[NSMenuItem alloc]
102 initWithTitle:title
103 action:@selector(menuItemSelected:)
104 keyEquivalent:@""] autorelease];
105 [item setTag:tag];
106 [menu addItem:item];
107 [item setTarget:target];
108 if (checked)
109 [item setState:NSOnState];
110 if (!enabled)
111 [item setEnabled:NO];
112 }
113
114 } // namespace
115
116 #pragma mark TranslateInfoBarMenuModel class definition
117 // Bridge class to handle interfacing with menu controllers from popup
118 // menus in infobar.
119 class TranslateInfoBarMenuModel : public menus::SimpleMenuModel::Delegate {
120 public:
121 TranslateInfoBarMenuModel(TranslateInfoBarDelegate* delegate,
122 TranslateInfoBarController* controller) :
123 translate_delegate_(delegate),
124 controller_(controller) {}
125
126 // Overridden from menus::SimpleMenuModel::Delegate:
127 virtual bool IsCommandIdChecked(int command_id) const;
128 virtual bool IsCommandIdEnabled(int command_id) const;
129 virtual bool GetAcceleratorForCommandId(int command_id,
130 menus::Accelerator* accelerator);
131 virtual void ExecuteCommand(int command_id);
132
133 private:
134 TranslateInfoBarDelegate* translate_delegate_; // weak
135 TranslateInfoBarController* controller_; // weak
136 DISALLOW_COPY_AND_ASSIGN(TranslateInfoBarMenuModel);
137 };
138
139 #pragma mark TranslateNotificationObserverBridge class definition
140 // Bridge class to allow obj-c TranslateInfoBarController to observe
141 // notifications.
142 class TranslateNotificationObserverBridge :
143 public NotificationObserver {
144 public:
145 TranslateNotificationObserverBridge(
146 TranslateInfoBarDelegate* delegate,
147 TranslateInfoBarController* controller);
148
149 // Overridden from NotificationObserver:
150 virtual void Observe(NotificationType type,
151 const NotificationSource& source,
152 const NotificationDetails& details);
153
154 private:
155 TranslateInfoBarDelegate* translate_delegate_; // weak
156 TranslateInfoBarController* controller_; // weak
157 NotificationRegistrar notification_registrar_;
158 DISALLOW_COPY_AND_ASSIGN(TranslateNotificationObserverBridge);
159 };
160
161 @interface TranslateInfoBarController (Private)
162
163 // Returns the main translate delegate.
164 - (TranslateInfoBarDelegate*)delegate;
165
166 // Make the infobar grey.
167 - (void)setInfoBarGradientColor;
168
169 // Reloads text for all labels for the current state.
170 - (void)loadLabelText:(TranslateErrors::Type)error;
171
172 // Resizes controls and hides/shows them based on state transition.
173 // Called before layout;
174 - (void)resizeAndSetControlVisibility;
175
176 // Move all the currently visible views into the correct place for the
177 // current mode.
178 - (void)layout;
179
180 // Create all the various controls we need for the toolbar.
181 - (void)constructViews;
182
183 // Called when the source or target language selection changes in a menu.
184 // |newLanguageIdx| is the index of the newly selected item in the appropriate
185 // menu.
186 - (void)sourceLanguageModified:(NSInteger)newLanguageIdx;
187 - (void)targetLanguageModified:(NSInteger)newLanguageIdx;
188
189 // Called when the source or target language have changed to update the
190 // model state and refresh the GUI.
191 - (void)languageModified;
192
193 // Completely rebuild "from" and "to" language menus from the data model.
194 - (void)populateLanguageMenus;
195
196 @end
197
198 #pragma mark TranslateInfoBarController class
199 @implementation TranslateInfoBarController
200
201 - (id)initWithDelegate:(InfoBarDelegate*)delegate {
202 if ((self = [super initWithDelegate:delegate])) {
203 state_ = TranslateInfoBarDelegate::kTranslateNone;
204
205 observer_bridge_.reset(
206 new TranslateNotificationObserverBridge([self delegate], self));
207
208 original_language_menu_model_.reset(
209 new LanguagesMenuModel(menu_model_.get(), [self delegate],
210 /*original_language=*/true));
211
212 target_language_menu_model_.reset(
213 new LanguagesMenuModel(menu_model_.get(), [self delegate],
214 /*original_language=*/false));
215
216 menu_model_.reset(new TranslateInfoBarMenuModel([self delegate], self));
217 }
218 return self;
219 }
220
221 - (TranslateInfoBarDelegate*)delegate {
222 return reinterpret_cast<TranslateInfoBarDelegate*>(delegate_);
223 }
224
225 - (void)constructViews {
226 // Using a zero or very large frame causes GTMUILocalizerAndLayoutTweaker
227 // to not resize the view properly so we take the bounds of the first label
228 // which is contained in the nib.
229 NSRect bogusFrame = [label_ frame];
230 label1_.reset(CreateLabel(bogusFrame));
231 label2_.reset(CreateLabel(bogusFrame));
232 label3_.reset(CreateLabel(bogusFrame));
233 translatingLabel_.reset(CreateLabel(bogusFrame));
234
235 optionsPopUp_.reset([[NSPopUpButton alloc] initWithFrame:bogusFrame
236 pullsDown:YES]);
237 fromLanguagePopUp_.reset([[NSPopUpButton alloc] initWithFrame:bogusFrame
238 pullsDown:NO]);
239 toLanguagePopUp_.reset([[NSPopUpButton alloc] initWithFrame:bogusFrame
240 pullsDown:NO]);
241
242 showOriginalButton_.reset([[NSButton alloc] initWithFrame:bogusFrame]);
243 tryAgainButton_.reset([[NSButton alloc] initWithFrame:bogusFrame]);
244 }
245
246 - (void)sourceLanguageModified:(NSInteger)newLanguageIdx {
247 DCHECK_GT(newLanguageIdx, 0);
248
249 if (newLanguageIdx == [self delegate]->original_lang_index())
250 return;
251
252 [self delegate]->ModifyOriginalLanguage(newLanguageIdx);
253
254 int commandId = IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE + newLanguageIdx;
255 int newMenuIdx = [fromLanguagePopUp_ indexOfItemWithTag:commandId];
256 [fromLanguagePopUp_ selectItemAtIndex:newMenuIdx];
257
258 [self languageModified];
259 }
260
261 - (void)targetLanguageModified:(NSInteger)newLanguageIdx {
262 DCHECK_GT(newLanguageIdx, 0);
263 if (newLanguageIdx == [self delegate]->target_lang_index())
264 return;
265
266 [self delegate]->ModifyTargetLanguage(newLanguageIdx);
267
268 int commandId = IDC_TRANSLATE_TARGET_LANGUAGE_BASE + newLanguageIdx;
269 int newMenuIdx = [toLanguagePopUp_ indexOfItemWithTag:commandId];
270 [toLanguagePopUp_ selectItemAtIndex:newMenuIdx];
271
272 [self languageModified];
273 }
274
275 - (void)languageModified {
276 // Selecting an item from the "from language" menu in the before translate
277 // phase shouldn't trigger translation - http://crbug.com/36666
278 TranslateInfoBarDelegate* delegate = [self delegate];
279 if (delegate->state() == TranslateInfoBarDelegate::kAfterTranslate) {
280 delegate->Translate();
281 [self updateState:delegate->state()
282 translationPending:delegate->translation_pending()
283 error:delegate->error_type()];
284 }
285 }
286
287 - (void)updateState:(TranslateInfoBarDelegate::TranslateState)newState
288 translationPending:(bool)newTranslationPending
289 error:(TranslateErrors::Type)error {
290 if (state_ == newState && translationPending_ == newTranslationPending)
291 return;
292
293 state_ = newState;
294 translationPending_ = newTranslationPending;
295
296 [self loadLabelText:error];
297
298 [self resizeAndSetControlVisibility];
299 [self layout];
300 }
301
302 - (void)setInfoBarGradientColor {
303 // Use grey gradient for the infobars.
304 NSColor* startingColor =
305 [NSColor colorWithCalibratedRed:kGreyTopColor[0] / 255.0
306 green:kGreyTopColor[1] / 255.0
307 blue:kGreyTopColor[2] / 255.0
308 alpha:1.0];
309 NSColor* endingColor =
310 [NSColor colorWithCalibratedRed:kGreyBottomColor[0] / 255.0
311 green:kGreyBottomColor[1] / 255.0
312 blue:kGreyBottomColor[2] / 255.0
313 alpha:1.0];
314 NSGradient* translateInfoBarGradient =
315 [[[NSGradient alloc] initWithStartingColor:startingColor
316 endingColor:endingColor] autorelease];
317
318 [infoBarView_ setGradient:translateInfoBarGradient];
319 }
320
321 - (void)resizeAndSetControlVisibility {
322 // Step 1: remove all controls from the infobar so we have a clean slate.
323 NSArray *allControls = [NSArray arrayWithObjects:label2_.get(), label3_.get(),
324 translatingLabel_.get(), fromLanguagePopUp_.get(), toLanguagePopUp_.get(),
325 showOriginalButton_.get(), tryAgainButton_.get(), nil];
326
327 for (NSControl* control in allControls) {
328 if ([control superview])
329 [control removeFromSuperview];
330 }
331
332 // OK & Cancel buttons are only visible in "before translate" mode when no
333 // translation is in progress.
334 if (state_ != TranslateInfoBarDelegate::kBeforeTranslate ||
335 translationPending_) {
336 // Removing okButton_ & cancelButton_ from the view may cause them
337 // to be released and since we can still access them from other areas
338 // in the code later, we need them to be nil when this happens.
339 [okButton_ removeFromSuperview];
340 okButton_ = nil;
341 [cancelButton_ removeFromSuperview];
342 cancelButton_ = nil;
343
344 }
345
346 // Step 2: Resize all visible controls and add them to the infobar.
347 NSMutableArray *visibleControls = nil;
348
349 switch (state_) {
350 case TranslateInfoBarDelegate::kBeforeTranslate:
351 visibleControls = [NSMutableArray arrayWithObjects:label1_.get(),
352 label2_.get(), fromLanguagePopUp_.get(), nil];
353
354 if (!translationPending_) {
355 [visibleControls addObject:okButton_];
356 [visibleControls addObject:cancelButton_];
357 }
358 break;
359 case TranslateInfoBarDelegate::kAfterTranslate:
360 visibleControls = [NSMutableArray arrayWithObjects:label1_.get(),
361 label2_.get(), fromLanguagePopUp_.get(), toLanguagePopUp_.get(), nil];
362 if (!translationPending_) {
363 [visibleControls addObject:showOriginalButton_.get()];
364 }
365 break;
366 case TranslateInfoBarDelegate::kTranslateError:
367 visibleControls = [NSMutableArray arrayWithObjects:label1_.get(), nil];
368
369 if (!translationPending_) {
370 [visibleControls addObject:tryAgainButton_.get()];
371 }
372 break;
373 default:
374 NOTREACHED() << "Invalid translate infobar state";
375 break;
376 }
377
378 if (translationPending_) {
379 [visibleControls addObject:translatingLabel_];
380 }
381
382 if (numLabelsDisplayed_ >= 3) {
383 [visibleControls addObject:label3_.get()];
384 }
385
386 // The options popup is only hidden in the translateError view.
387 BOOL optionsPopuUpHidden =
388 (state_ == TranslateInfoBarDelegate::kTranslateError) ? YES : NO;
389 [optionsPopUp_ setHidden:optionsPopuUpHidden];
390
391 NSRect optionsFrame = [optionsPopUp_ frame];
392 for (NSControl* control in visibleControls) {
393 [GTMUILocalizerAndLayoutTweaker sizeToFitView:control];
394 [control setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin |
395 NSViewMaxYMargin];
396
397 // Need to check if a view is already attached since |label1_| is always
398 // parented and we don't want to add it again.
399 if (![control superview])
400 [infoBarView_ addSubview:control];
401
402 if ([control isKindOfClass:[NSButton class]])
403 VerticallyCenterView(control);
404
405 // Make "from" and "to" language popup menus the same size as the options
406 // menu.
407 // We don't autosize since some languages names are really long causing
408 // the toolbar to overflow.
409 if ([control isKindOfClass:[NSPopUpButton class]])
410 [control setFrame:optionsFrame];
411 }
412 }
413
414 - (void)layout {
415 if (state_ != TranslateInfoBarDelegate::kAfterTranslate) {
416 // 3rd label is only displayed in some locales, but should never be
417 // visible in this stage.
418 // If it ever is visible then we need to move it into position here.
419 DCHECK(numLabelsDisplayed_ < 3);
420 }
421
422 switch (state_) {
423 case TranslateInfoBarDelegate::kBeforeTranslate:
424 MoveControl(label1_, fromLanguagePopUp_, 0, true);
425 MoveControl(fromLanguagePopUp_, label2_, 0, true);
426
427 if (!translationPending_) {
428 MoveControl(label2_, okButton_, spaceBetweenControls_, true);
429 MoveControl(okButton_, cancelButton_, spaceBetweenControls_, true);
430 } else {
431 MoveControl(label2_, translatingLabel_, spaceBetweenControls_, true);
432 }
433 break;
434
435 case TranslateInfoBarDelegate::kAfterTranslate: {
436 NSView* lastControl = toLanguagePopUp_;
437 MoveControl(label1_, fromLanguagePopUp_, 0, true);
438 MoveControl(fromLanguagePopUp_, label2_, 0, true);
439 MoveControl(label2_, toLanguagePopUp_, 0, true);
440 if (numLabelsDisplayed_ == 3) {
441 MoveControl(toLanguagePopUp_, label3_, 0, true);
442 lastControl = label3_;
443 }
444
445 if (translationPending_) {
446 MoveControl(lastControl, translatingLabel_, spaceBetweenControls_ * 2,
447 true);
448 } else {
449 MoveControl(lastControl, showOriginalButton_, spaceBetweenControls_ * 2,
450 true);
451 }
452
453 break;
454 }
455
456 case TranslateInfoBarDelegate::kTranslateError:
457 if (translationPending_) {
458 MoveControl(label1_, translatingLabel_, 0, true);
459 } else {
460 MoveControl(label1_, tryAgainButton_, spaceBetweenControls_ * 2, true);
461 }
462 break;
463
464 default:
465 NOTREACHED() << "Invalid translate infobar state";
466 break;
467 }
468 }
469
470 - (void) rebuildOptionsMenu {
471 // The options model doesn't know how to handle state transitions, so rebuild
472 // it each time through here.
473 options_menu_model_.reset(
474 new OptionsMenuModel(menu_model_.get(), [self delegate]));
475
476 [optionsPopUp_ removeAllItems];
477 // Set title.
478 NSString* optionsLabel =
479 l10n_util::GetNSString(IDS_TRANSLATE_INFOBAR_OPTIONS);
480 [optionsPopUp_ addItemWithTitle:optionsLabel];
481
482 // Populate options menu.
483 NSMenu* optionsMenu = [optionsPopUp_ menu];
484 [optionsMenu setAutoenablesItems:NO];
485 for (int i = 0; i < options_menu_model_->GetItemCount(); ++i) {
486 NSString* title = base::SysUTF16ToNSString(
487 options_menu_model_->GetLabelAt(i));
488 int cmd = options_menu_model_->GetCommandIdAt(i);
489 bool checked = options_menu_model_->IsItemCheckedAt(i);
490 bool enabled = options_menu_model_->IsEnabledAt(i);
491 AddMenuItem(optionsMenu, self, title, cmd, enabled, checked);
492 }
493 }
494
495 - (void)populateLanguageMenus {
496 NSMenu* originalLanguageMenu = [fromLanguagePopUp_ menu];
497 [originalLanguageMenu setAutoenablesItems:NO];
498 int selectedMenuIndex = 0;
499 int selectedLangIndex = [self delegate]->original_lang_index();
500 for (int i = 0; i < original_language_menu_model_->GetItemCount(); ++i) {
501 NSString* title = base::SysUTF16ToNSString(
502 original_language_menu_model_->GetLabelAt(i));
503 int cmd = original_language_menu_model_->GetCommandIdAt(i);
504 bool checked =
505 (cmd - IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE) == selectedLangIndex;
506 if (checked)
507 selectedMenuIndex = i;
508 bool enabled = original_language_menu_model_->IsEnabledAt(i);
509 AddMenuItem(originalLanguageMenu, self, title, cmd, enabled, checked);
510 }
511 [fromLanguagePopUp_ selectItemAtIndex:selectedMenuIndex];
512
513 NSMenu* targetLanguageMenu = [toLanguagePopUp_ menu];
514 [targetLanguageMenu setAutoenablesItems:NO];
515 selectedLangIndex = [self delegate]->target_lang_index();
516 for (int i = 0; i < target_language_menu_model_->GetItemCount(); ++i) {
517 NSString* title = base::SysUTF16ToNSString(
518 target_language_menu_model_->GetLabelAt(i));
519 int cmd = target_language_menu_model_->GetCommandIdAt(i);
520 bool checked =
521 (cmd - IDC_TRANSLATE_TARGET_LANGUAGE_BASE) == selectedLangIndex;
522 if (checked)
523 selectedMenuIndex = i;
524 bool enabled = target_language_menu_model_->IsEnabledAt(i);
525 AddMenuItem(targetLanguageMenu, self, title, cmd, enabled, checked);
526 }
527 [toLanguagePopUp_ selectItemAtIndex:selectedMenuIndex];
528 }
529
530 - (void)loadLabelText:(TranslateErrors::Type)error {
531 numLabelsDisplayed_ = 2;
532
533 NSString* label1Text = @"";
534 NSString* label2Text = @"";
535 NSString* label3Text = @"";
536
537 if (state_ == TranslateInfoBarDelegate::kTranslateError) {
538 // Load an error message, if an error occured and the user clicked
539 // "try again" then blank all labels.
540 if (!translationPending_) {
541 string16 message_text_utf16 = [self delegate]->GetErrorMessage(error);
542 label1Text = base::SysUTF16ToNSString(message_text_utf16);
543 }
544 } else {
545 string16 message_text_utf16;
546 std::vector<size_t> offsets;
547 [self delegate]->GetMessageText(state_, &message_text_utf16,
548 &offsets, &swappedLanguagePlaceholders_);
549
550 NSString* message_text = base::SysUTF16ToNSString(message_text_utf16);
551 NSRange label1Range = NSMakeRange(0, offsets[0]);
552 label1Text = [message_text substringWithRange:label1Range];
553 NSRange label2Range = NSMakeRange(offsets[0],
554 offsets[1] - offsets[0]);
555 label2Text = [message_text substringWithRange:label2Range];
556
557 // If this locale requires a 3rd label for the status message.
558 if (offsets.size() == 3) {
559 NSRange label3Range = NSMakeRange(offsets[1],
560 offsets[2] - offsets[1]);
561 label3Text = [message_text substringWithRange:label3Range];
562 numLabelsDisplayed_ = 3;
563 }
564 }
565
566 [label1_ setStringValue:label1Text];
567 [label2_ setStringValue:label2Text];
568 [label3_ setStringValue:label3Text];
569 }
570
571 - (void)addAdditionalControls {
572 using l10n_util::GetNSString;
573 using l10n_util::GetNSStringWithFixup;
574
575 // Get layout information from the NIB.
576 NSRect okButtonFrame = [okButton_ frame];
577 NSRect cancelButtonFrame = [cancelButton_ frame];
578 spaceBetweenControls_ = NSMinX(cancelButtonFrame) - NSMaxX(okButtonFrame);
579
580 // Set infobar background color.
581 [self setInfoBarGradientColor];
582
583 // Instantiate additional controls.
584 [self constructViews];
585
586 // Set ourselves as the delegate for the options menu so we can populate it
587 // dynamically.
588 [[optionsPopUp_ menu] setDelegate:self];
589
590 // Replace label_ with label1_ so we get a consistent look between all the
591 // labels we display in the translate view.
592 [[label_ superview] replaceSubview:label_ with:label1_.get()];
593 label_.reset(); // Now released.
594
595 // Populate contextual menus.
596 [self rebuildOptionsMenu];
597 [self populateLanguageMenus];
598
599 // Set OK & Cancel text.
600 [okButton_ setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_ACCEPT)];
601 [cancelButton_ setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_DENY)];
602 [translatingLabel_
603 setStringValue:GetNSString(IDS_TRANSLATE_INFOBAR_TRANSLATING)];
604
605 // Set up "Show original" and "Try again" buttons.
606 [showOriginalButton_ setBezelStyle:NSRoundRectBezelStyle];
607 [showOriginalButton_ setFrame:okButtonFrame];
608 [tryAgainButton_ setBezelStyle:NSRoundRectBezelStyle];
609 [tryAgainButton_ setFrame:okButtonFrame];
610
611 [showOriginalButton_ setTarget:self];
612 [showOriginalButton_ setAction:@selector(showOriginal:)];
613 [tryAgainButton_ setTarget:self];
614 [tryAgainButton_ setAction:@selector(ok:)];
615
616 [showOriginalButton_
617 setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_REVERT)];
618 [tryAgainButton_
619 setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_RETRY)];
620
621 // Add and configure controls that are visible in all modes.
622 [optionsPopUp_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin |
623 NSViewMaxYMargin];
624 // Add "options" popup z-ordered below all other controls so when we
625 // resize the toolbar it doesn't hide them.
626 [infoBarView_ addSubview:optionsPopUp_
627 positioned:NSWindowBelow
628 relativeTo:nil];
629 [GTMUILocalizerAndLayoutTweaker sizeToFitView:optionsPopUp_];
630 MoveControl(closeButton_, optionsPopUp_, spaceBetweenControls_, false);
631 VerticallyCenterView(optionsPopUp_);
632
633 // Show and place GUI elements.
634 TranslateInfoBarDelegate* delegate = [self delegate];
635 [self updateState:delegate->state()
636 translationPending:delegate->translation_pending()
637 error:delegate->error_type()];
638 }
639
640 // Called when "Translate" button is clicked.
641 - (IBAction)ok:(id)sender {
642 TranslateInfoBarDelegate* delegate = [self delegate];
643 TranslateInfoBarDelegate::TranslateState state = delegate->state();
644 DCHECK(state == TranslateInfoBarDelegate::kBeforeTranslate ||
645 state == TranslateInfoBarDelegate::kTranslateError);
646 delegate->Translate();
647 [self updateState:state
648 translationPending:delegate->translation_pending()
649 error:delegate->error_type()];
650 UMA_HISTOGRAM_COUNTS("Translate.Translate", 1);
651 }
652
653 // Called when someone clicks on the "Nope" button.
654 - (IBAction)cancel:(id)sender {
655 DCHECK(
656 [self delegate]->state() == TranslateInfoBarDelegate::kBeforeTranslate);
657 [self delegate]->TranslationDeclined();
658 UMA_HISTOGRAM_COUNTS("Translate.DeclineTranslate", 1);
659 [super dismiss:nil];
660 }
661
662 - (IBAction)showOriginal:(id)sender {
663 [self delegate]->RevertTranslation();
664 }
665
666 - (void)menuItemSelected:(id)item {
667 if ([item respondsToSelector:@selector(tag)]) {
668 int cmd = [item tag];
669 // Danger Will Robinson! : This call can release the infobar (e.g. invoking
670 // "About Translate" can open a new tab).
671 // Do not access member variables after this line!
672 menu_model_->ExecuteCommand(cmd);
673 } else {
674 NOTREACHED();
675 }
676 }
677
678 #pragma mark NSMenuDelegate
679
680 // Invoked by virtue of us being set as the delegate for the options menu.
681 - (void)menuNeedsUpdate:(NSMenu *)menu {
682 [self rebuildOptionsMenu];
683 }
684
685 #pragma mark TestingAPI
686 - (NSMenu*)optionsMenu {
687 return [optionsPopUp_ menu];
688 }
689
690 - (NSButton*)tryAgainButton {
691 return tryAgainButton_.get();
692 }
693
694 - (TranslateInfoBarDelegate::TranslateState)state {
695 return state_;
696 }
697
698 - (bool)verifyLayout:(TranslateInfoBarDelegate::TranslateState)state
699 translationPending:(bool)translationPending {
700 NSArray* allControls = [NSArray arrayWithObjects:label1_.get(), label2_.get(),
701 label3_.get(), translatingLabel_.get(), fromLanguagePopUp_.get(),
702 toLanguagePopUp_.get(), optionsPopUp_.get(), closeButton_,
703 showOriginalButton_.get(), tryAgainButton_.get(), nil];
704
705 // Sanity check - parameters should match internal state.
706 if (state != state_) {
707 LOG(ERROR) << "State mismatch: " << state << " vs " << state_;
708 return false;
709 }
710
711 if (translationPending != translationPending_) {
712 LOG(ERROR) << "Pending Translation mismatch: " <<
713 translationPending << " vs " << translationPending_;
714 return false;
715 }
716
717 // Array of all visible controls ordered from start -> end.
718 NSArray* visibleControls = nil;
719
720 switch (state) {
721 case TranslateInfoBarDelegate::kBeforeTranslate:
722 if (translationPending) {
723 visibleControls = [NSArray arrayWithObjects:label1_.get(),
724 fromLanguagePopUp_.get(), label2_.get(), translatingLabel_.get(),
725 optionsPopUp_.get(), closeButton_, nil];
726 } else {
727 visibleControls = [NSArray arrayWithObjects:label1_.get(),
728 fromLanguagePopUp_.get(), label2_.get(), optionsPopUp_.get(),
729 closeButton_, nil];
730 }
731 break;
732 case TranslateInfoBarDelegate::kAfterTranslate:
733 if (translationPending) {
734 visibleControls = [NSArray arrayWithObjects:label1_.get(),
735 fromLanguagePopUp_.get(), label2_.get(), toLanguagePopUp_.get(),
736 translatingLabel_.get(), optionsPopUp_.get(), closeButton_, nil];
737 } else {
738 visibleControls = [NSArray arrayWithObjects:label1_.get(),
739 fromLanguagePopUp_.get(), label2_.get(), toLanguagePopUp_.get(),
740 showOriginalButton_.get(), optionsPopUp_.get(), closeButton_, nil];
741 }
742 break;
743 case TranslateInfoBarDelegate::kTranslateError:
744 if (translationPending) {
745 visibleControls = [NSArray arrayWithObjects:label1_.get(),
746 translatingLabel_.get(), closeButton_, nil];
747 } else {
748 visibleControls = [NSArray arrayWithObjects:label1_.get(),
749 tryAgainButton_.get(), closeButton_, nil];
750 }
751 break;
752 default:
753 NOTREACHED() << "Unknown state";
754 return false;
755 }
756
757 // Step 1: Make sure control visibility is what we expect.
758 for (NSUInteger i = 0; i < [allControls count]; ++i) {
759 id control = [allControls objectAtIndex:i];
760 bool hasSuperView = [control superview];
761 bool expectedVisibility = [visibleControls containsObject:control];
762
763
764 // Special case the options popup, which we hide rather than removing
765 // from the superview.
766 if (control == optionsPopUp_.get())
767 hasSuperView = [control isHidden] == NO;
768
769 if (expectedVisibility != hasSuperView) {
770 NSString *title = @"";
771
772 if ([control isKindOfClass:[NSPopUpButton class]]) {
773 title = [[[control menu] itemAtIndex:0] title];
774 }
775
776 LOG(ERROR) <<
777 "State: " << state << " translationPending " << translationPending <<
778 " Control @" << i << (hasSuperView ? " has" : " doesn't have") <<
779 " a superview" << [[control description] UTF8String] <<
780 " Title=" << [title UTF8String];
781 return false;
782 }
783 }
784
785 // Step 2: Check that controls are ordered correctly with no overlap.
786 #if !defined(DISABLE_VERIFY_CONTROL_ORDER)
787 // http://crbug.com/46663 this appears to be invalid.
788 // VerifyControlOrderAndSpacing had an unsigned >= 0 bug, so it used to always
789 // return true. With that bug fixed, this loop now can return a failure.
790 // Scanning the code, it's not clear how this would pass since not all
791 // controls are visible and it needs the array order to always match display
792 // order.
793 id previousControl = nil;
794 for (NSUInteger i = 0; i < [allControls count]; ++i) {
795 id control = [allControls objectAtIndex:i];
796 if (!VerifyControlOrderAndSpacing(previousControl, control)) {
797 LOG(ERROR) <<
798 "State: " << state << " translationPending " << translationPending <<
799 " Control @" << i << " not ordered correctly: " <<
800 [[control description] UTF8String];
801 return false;
802 }
803 previousControl = control;
804 }
805 #endif // !defined(DISABLE_VERIFY_CONTROL_ORDER)
806
807 // Step 3: Check other misc. attributes of layout.
808 if (state == TranslateInfoBarDelegate::kTranslateError && translationPending)
809 {
810 if ([[label1_ stringValue] length] != 0) {
811 LOG(ERROR) << "Expected empty label1_, instead got" <<
812 [[label1_ description] UTF8String];
813 return false;
814 }
815 }
816
817 return true;
818 }
819
820 @end
821
822 #pragma mark CreateInfoBar implementation.
823 InfoBar* TranslateInfoBarDelegate::CreateInfoBar() {
824 TranslateInfoBarController* controller =
825 [[TranslateInfoBarController alloc] initWithDelegate:this];
826 return new InfoBar(controller);
827 }
828
829 #pragma mark menus::SimpleMenuModel::Delegates
830
831 bool TranslateInfoBarMenuModel::IsCommandIdChecked(int command_id) const {
832 switch (command_id) {
833 case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG :
834 return translate_delegate_->IsLanguageBlacklisted();
835
836 case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE :
837 return translate_delegate_->IsSiteBlacklisted();
838
839 case IDC_TRANSLATE_OPTIONS_ALWAYS :
840 return translate_delegate_->ShouldAlwaysTranslate();
841
842 default:
843 NOTREACHED() << "Invalid command_id from menu";
844 break;
845 }
846 return false;
847 }
848
849 bool TranslateInfoBarMenuModel::IsCommandIdEnabled(int command_id) const {
850 switch (command_id) {
851 case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG :
852 case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE :
853 return !translate_delegate_->ShouldAlwaysTranslate();
854
855 case IDC_TRANSLATE_OPTIONS_ALWAYS :
856 return (!translate_delegate_->IsLanguageBlacklisted() &&
857 !translate_delegate_->IsSiteBlacklisted());
858
859 default:
860 break;
861 }
862 return true;
863 }
864
865 bool TranslateInfoBarMenuModel::GetAcceleratorForCommandId(int command_id,
866 menus::Accelerator* accelerator) {
867 return false;
868 }
869
870 void TranslateInfoBarMenuModel::ExecuteCommand(int command_id) {
871 if (command_id >= IDC_TRANSLATE_TARGET_LANGUAGE_BASE) {
872 int language_command_id =
873 command_id - IDC_TRANSLATE_TARGET_LANGUAGE_BASE;
874 [controller_
875 targetLanguageModified:language_command_id];
876 } else if (command_id >= IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE) {
877 int language_command_id =
878 command_id - IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE;
879 [controller_
880 sourceLanguageModified:language_command_id];
881 } else {
882 switch (command_id) {
883 case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG:
884 translate_delegate_->ToggleLanguageBlacklist();
885 break;
886
887 case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE:
888 translate_delegate_->ToggleSiteBlacklist();
889 break;
890
891 case IDC_TRANSLATE_OPTIONS_ALWAYS:
892 translate_delegate_->ToggleAlwaysTranslate();
893 break;
894
895 case IDC_TRANSLATE_OPTIONS_ABOUT: {
896 TabContents* tab_contents = translate_delegate_->tab_contents();
897 if (tab_contents) {
898 string16 url = l10n_util::GetStringUTF16(
899 IDS_ABOUT_GOOGLE_TRANSLATE_URL);
900 tab_contents->OpenURL(GURL(url), GURL(), NEW_FOREGROUND_TAB,
901 PageTransition::LINK);
902 }
903 break;
904 }
905
906 default:
907 NOTREACHED() << "Invalid command id from menu.";
908 break;
909 }
910 }
911 }
912
913 # pragma mark TranslateInfoBarNotificationObserverBridge
914
915 TranslateNotificationObserverBridge::TranslateNotificationObserverBridge(
916 TranslateInfoBarDelegate* delegate,
917 TranslateInfoBarController* controller) :
918 translate_delegate_(delegate),
919 controller_(controller) {
920 // Register for PAGE_TRANSLATED notification.
921 notification_registrar_.Add(this, NotificationType::PAGE_TRANSLATED,
922 Source<TabContents>(translate_delegate_->tab_contents()));
923 };
924
925 void TranslateNotificationObserverBridge::Observe(NotificationType type,
926 const NotificationSource& source, const NotificationDetails& details) {
927 if (type.value != NotificationType::PAGE_TRANSLATED)
928 return;
929 TabContents* tab = Source<TabContents>(source).ptr();
930 if (tab != translate_delegate_->tab_contents())
931 return;
932 PageTranslatedDetails* page_translated_details =
933 Details<PageTranslatedDetails>(details).ptr();
934 TranslateErrors::Type error = page_translated_details->error_type;
935 TranslateInfoBarDelegate::TranslateState newState =
936 TranslateInfoBarDelegate::kAfterTranslate;
937 if (page_translated_details->error_type != TranslateErrors::NONE)
938 newState = TranslateInfoBarDelegate::kTranslateError;
939 [controller_ updateState:newState translationPending:false error:error];
940
941 }
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/translate_infobar.h ('k') | chrome/browser/translate/translate_infobar_delegate2.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698