Index: chrome/browser/cocoa/translate/translate_infobar_base.mm |
diff --git a/chrome/browser/cocoa/translate/translate_infobar_base.mm b/chrome/browser/cocoa/translate/translate_infobar_base.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f93a733073091dbcd2518c70d30e3c18c95df63b |
--- /dev/null |
+++ b/chrome/browser/cocoa/translate/translate_infobar_base.mm |
@@ -0,0 +1,579 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import <Cocoa/Cocoa.h> |
+#import "chrome/browser/cocoa/translate/translate_infobar_base.h" |
+ |
+#include "app/l10n_util.h" |
+#include "base/histogram.h" |
+#include "base/logging.h" // for NOTREACHED() |
+#include "base/mac_util.h" |
+#include "base/sys_string_conversions.h" |
+#include "chrome/app/chrome_dll_resource.h" |
+#import "chrome/browser/cocoa/hover_close_button.h" |
+#include "chrome/browser/cocoa/infobar.h" |
+#import "chrome/browser/cocoa/infobar_controller.h" |
+#import "chrome/browser/cocoa/infobar_gradient_view.h" |
+#include "chrome/browser/cocoa/translate/after_translate_infobar_controller.h" |
+#import "chrome/browser/cocoa/translate/before_translate_infobar_controller.h" |
+#include "chrome/browser/cocoa/translate/translate_message_infobar_controller.h" |
+#include "chrome/browser/translate/translate_infobars_delegates.h" |
+#include "grit/generated_resources.h" |
+#include "grit/locale_settings.h" |
+#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
+ |
+using TranslateInfoBarUtilities::MoveControl; |
+using TranslateInfoBarUtilities::VerticallyCenterView; |
+using TranslateInfoBarUtilities::VerifyControlOrderAndSpacing; |
+using TranslateInfoBarUtilities::CreateLabel; |
+using TranslateInfoBarUtilities::AddMenuItem; |
+ |
+// Colors for translate infobar gradient background. |
+const int kGreyTopColor[] = {0xC0, 0xC0, 0xC0}; |
+const int kGreyBottomColor[] = {0xCC, 0xCC, 0xCC}; |
+ |
+#pragma mark TranslateInfoBarUtilities helper functions. |
+ |
+namespace TranslateInfoBarUtilities { |
+ |
+// Move the |toMove| view |spacing| pixels before/after the |anchor| view. |
+// |after| signifies the side of |anchor| on which to place |toMove|. |
+void MoveControl(NSView* anchor, NSView* toMove, int spacing, bool after) { |
+ NSRect anchorFrame = [anchor frame]; |
+ NSRect toMoveFrame = [toMove frame]; |
+ |
+ // At the time of this writing, OS X doesn't natively support BiDi UIs, but |
+ // it doesn't hurt to be forward looking. |
+ bool toRight = after; |
+ |
+ if (toRight) { |
+ toMoveFrame.origin.x = NSMaxX(anchorFrame) + spacing; |
+ } else { |
+ // Place toMove to theleft of anchor. |
+ toMoveFrame.origin.x = NSMinX(anchorFrame) - |
+ spacing - NSWidth(toMoveFrame); |
+ } |
+ [toMove setFrame:toMoveFrame]; |
+} |
+ |
+// Check that the control |before| is ordered visually before the |after| |
+// control. |
+// Also, check that there is space between them. |
+bool VerifyControlOrderAndSpacing(id before, id after) { |
+ NSRect beforeFrame = [before frame]; |
+ NSRect afterFrame = [after frame]; |
+ return NSMinX(afterFrame) >= NSMaxX(beforeFrame); |
+} |
+ |
+// Vertically center |toMove| in its container. |
+void VerticallyCenterView(NSView *toMove) { |
+ NSRect superViewFrame = [[toMove superview] frame]; |
+ NSRect viewFrame = [toMove frame]; |
+ viewFrame.origin.y = |
+ floor((NSHeight(superViewFrame) - NSHeight(viewFrame))/2.0); |
+ [toMove setFrame:viewFrame]; |
+} |
+ |
+// Creates a label control in the style we need for the translate infobar's |
+// labels within |bounds|. |
+NSTextField* CreateLabel(NSRect bounds) { |
+ NSTextField* ret = [[NSTextField alloc] initWithFrame:bounds]; |
+ [ret setEditable:NO]; |
+ [ret setDrawsBackground:NO]; |
+ [ret setBordered:NO]; |
+ return ret; |
+} |
+ |
+// Adds an item with the specified properties to |menu|. |
+void AddMenuItem(NSMenu *menu, id target, SEL selector, NSString* title, |
+ int tag, bool enabled, bool checked) { |
+ NSMenuItem* item = [[[NSMenuItem alloc] |
+ initWithTitle:title |
+ action:selector |
+ keyEquivalent:@""] autorelease]; |
+ [item setTag:tag]; |
+ [menu addItem:item]; |
+ [item setTarget:target]; |
+ if (checked) |
+ [item setState:NSOnState]; |
+ if (!enabled) |
+ [item setEnabled:NO]; |
+} |
+ |
+} // namespace TranslateInfoBarUtilities |
+ |
+// For compilation purposes until the linux port goes through and |
+// the files can be removed. |
+InfoBar* TranslateInfoBarDelegate::CreateInfoBar() { |
+ NOTREACHED(); |
+ return NULL; |
+} |
+ |
+// TranslateInfoBarDelegate views specific method: |
+InfoBar* TranslateInfoBarDelegate2::CreateInfoBar() { |
+ TranslateInfoBarControllerBase* infobar_controller = NULL; |
+ switch (type_) { |
+ case BEFORE_TRANSLATE: |
+ infobar_controller = |
+ [[BeforeTranslateInfobarController alloc] initWithDelegate:this]; |
+ break; |
+ case AFTER_TRANSLATE: |
+ infobar_controller = |
+ [[AfterTranslateInfobarController alloc] initWithDelegate:this]; |
+ break; |
+ case TRANSLATING: |
+ case TRANSLATION_ERROR: |
+ infobar_controller = |
+ [[TranslateMessageInfobarController alloc] initWithDelegate:this]; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ } |
+ return new InfoBar(infobar_controller); |
+} |
+ |
+@interface TranslateInfoBarControllerBase (Private) |
+ |
+// Removes all controls so that layout can add in only the controls |
+// required. |
+- (void)clearAllControls; |
+ |
+// Create all the various controls we need for the toolbar. |
+- (void)constructViews; |
+ |
+// Reloads text for all labels for the current state. |
+- (void)loadLabelText:(TranslateErrors::Type)error; |
+ |
+// Makes the infobar grey. |
+- (void)setInfoBarGradientColor; |
+ |
+// Main function to update the toolbar graphic state and data model after |
+// the state has changed. |
+// Controls are moved around as needed and visibility changed to match the |
+// current state. |
+- (void)updateState; |
+ |
+// Called when the source or target language selection changes in a menu. |
+// |newLanguageIdx| is the index of the newly selected item in the appropriate |
+// menu. |
+- (void)sourceLanguageModified:(NSInteger)newLanguageIdx; |
+- (void)targetLanguageModified:(NSInteger)newLanguageIdx; |
+ |
+// Completely rebuild "from" and "to" language menus from the data model. |
+- (void)populateLanguageMenus; |
+ |
+@end |
+ |
+#pragma mark TranslateInfoBarController class |
+ |
+@implementation TranslateInfoBarControllerBase |
+ |
+- (id)initWithDelegate:(InfoBarDelegate*)delegate { |
+ if ((self = [super initWithDelegate:delegate])) { |
+ originalLanguageMenuModel_.reset( |
+ new LanguagesMenuModel2([self delegate], |
+ LanguagesMenuModel2::ORIGINAL)); |
+ |
+ targetLanguageMenuModel_.reset( |
+ new LanguagesMenuModel2([self delegate], |
+ LanguagesMenuModel2::TARGET)); |
+ optionsMenuModel_.reset(new OptionsMenuModel2([self delegate])); |
+ } |
+ return self; |
+} |
+ |
+- (TranslateInfoBarDelegate2*)delegate { |
+ return reinterpret_cast<TranslateInfoBarDelegate2*>(delegate_); |
+} |
+ |
+- (void)constructViews { |
+ // Using a zero or very large frame causes GTMUILocalizerAndLayoutTweaker |
+ // to not resize the view properly so we take the bounds of the first label |
+ // which is contained in the nib. |
+ NSRect bogusFrame = [label_ frame]; |
+ label1_.reset(CreateLabel(bogusFrame)); |
+ label2_.reset(CreateLabel(bogusFrame)); |
+ label3_.reset(CreateLabel(bogusFrame)); |
+ |
+ optionsPopUp_.reset([[NSPopUpButton alloc] initWithFrame:bogusFrame |
+ pullsDown:YES]); |
+ fromLanguagePopUp_.reset([[NSPopUpButton alloc] initWithFrame:bogusFrame |
+ pullsDown:NO]); |
+ toLanguagePopUp_.reset([[NSPopUpButton alloc] initWithFrame:bogusFrame |
+ pullsDown:NO]); |
+ showOriginalButton_.reset([[NSButton alloc] initWithFrame:bogusFrame]); |
+ tryAgainButton_.reset([[NSButton alloc] initWithFrame:bogusFrame]); |
+} |
+ |
+- (void)sourceLanguageModified:(NSInteger)newLanguageIdx { |
+ DCHECK_GT(newLanguageIdx, -1); |
+ if (newLanguageIdx == [self delegate]->original_language_index()) |
+ return; |
+ [self delegate]->SetOriginalLanguage(newLanguageIdx); |
+ int commandId = IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE + newLanguageIdx; |
+ int newMenuIdx = [fromLanguagePopUp_ indexOfItemWithTag:commandId]; |
+ [fromLanguagePopUp_ selectItemAtIndex:newMenuIdx]; |
+} |
+ |
+- (void)targetLanguageModified:(NSInteger)newLanguageIdx { |
+ DCHECK_GT(newLanguageIdx, -1); |
+ if (newLanguageIdx == [self delegate]->target_language_index()) |
+ return; |
+ [self delegate]->SetTargetLanguage(newLanguageIdx); |
+ int commandId = IDC_TRANSLATE_TARGET_LANGUAGE_BASE + newLanguageIdx; |
+ int newMenuIdx = [toLanguagePopUp_ indexOfItemWithTag:commandId]; |
+ [toLanguagePopUp_ selectItemAtIndex:newMenuIdx]; |
+} |
+ |
+- (void)loadLabelText { |
+ // Do nothing by default, should be implemented by subclasses. |
+} |
+ |
+- (void)updateState { |
+ [self loadLabelText]; |
+ [self clearAllControls]; |
+ [self showVisibleControls:[self visibleControls]]; |
+ [self layout]; |
+} |
+ |
+- (void)setInfoBarGradientColor { |
+ // Use grey gradient for the infobars. |
+ NSColor* startingColor = |
+ [NSColor colorWithCalibratedRed:kGreyTopColor[0] / 255.0 |
+ green:kGreyTopColor[1] / 255.0 |
+ blue:kGreyTopColor[2] / 255.0 |
+ alpha:1.0]; |
+ NSColor* endingColor = |
+ [NSColor colorWithCalibratedRed:kGreyBottomColor[0] / 255.0 |
+ green:kGreyBottomColor[1] / 255.0 |
+ blue:kGreyBottomColor[2] / 255.0 |
+ alpha:1.0]; |
+ NSGradient* translateInfoBarGradient = |
+ [[[NSGradient alloc] initWithStartingColor:startingColor |
+ endingColor:endingColor] autorelease]; |
+ |
+ [infoBarView_ setGradient:translateInfoBarGradient]; |
+} |
+ |
+- (void)removeOkCancelButtons { |
+ // Removing okButton_ & cancelButton_ from the view may cause them |
+ // to be released and since we can still access them from other areas |
+ // in the code later, we need them to be nil when this happens. |
+ [okButton_ removeFromSuperview]; |
+ okButton_ = nil; |
+ [cancelButton_ removeFromSuperview]; |
+ cancelButton_ = nil; |
+} |
+ |
+- (void)clearAllControls { |
+ // Step 1: remove all controls from the infobar so we have a clean slate. |
+ NSArray *allControls = [NSArray arrayWithObjects:label2_.get(), label3_.get(), |
+ fromLanguagePopUp_.get(), toLanguagePopUp_.get(), |
+ showOriginalButton_.get(), tryAgainButton_.get(), nil]; |
+ |
+ for (NSControl* control in allControls) { |
+ if ([control superview]) |
+ [control removeFromSuperview]; |
+ } |
+} |
+ |
+- (void)showVisibleControls:(NSArray*)visibleControls { |
+ NSRect optionsFrame = [optionsPopUp_ frame]; |
+ for (NSControl* control in visibleControls) { |
+ [GTMUILocalizerAndLayoutTweaker sizeToFitView:control]; |
+ [control setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin | |
+ NSViewMaxYMargin]; |
+ |
+ // Need to check if a view is already attached since |label1_| is always |
+ // parented and we don't want to add it again. |
+ if (![control superview]) |
+ [infoBarView_ addSubview:control]; |
+ |
+ if ([control isKindOfClass:[NSButton class]]) |
+ VerticallyCenterView(control); |
+ |
+ // Make "from" and "to" language popup menus the same size as the options |
+ // menu. |
+ // We don't autosize since some languages names are really long causing |
+ // the toolbar to overflow. |
+ if ([control isKindOfClass:[NSPopUpButton class]]) |
+ [control setFrame:optionsFrame]; |
+ } |
+} |
+ |
+- (void)layout { |
+ |
+} |
+ |
+- (NSArray*)visibleControls { |
+ return [NSArray array]; |
+} |
+ |
+- (void) rebuildOptionsMenu { |
+ // The options model doesn't know how to handle state transitions, so rebuild |
+ // it each time through here. |
+ optionsMenuModel_.reset( |
+ new OptionsMenuModel2([self delegate])); |
+ |
+ [optionsPopUp_ removeAllItems]; |
+ // Set title. |
+ NSString* optionsLabel = |
+ l10n_util::GetNSString(IDS_TRANSLATE_INFOBAR_OPTIONS); |
+ [optionsPopUp_ addItemWithTitle:optionsLabel]; |
+ |
+ // Populate options menu. |
+ NSMenu* optionsMenu = [optionsPopUp_ menu]; |
+ [optionsMenu setAutoenablesItems:NO]; |
+ for (int i = 0; i < optionsMenuModel_->GetItemCount(); ++i) { |
+ NSString* title = base::SysUTF16ToNSString( |
+ optionsMenuModel_->GetLabelAt(i)); |
+ int cmd = optionsMenuModel_->GetCommandIdAt(i); |
+ bool checked = optionsMenuModel_->IsItemCheckedAt(i); |
+ bool enabled = optionsMenuModel_->IsEnabledAt(i); |
+ AddMenuItem(optionsMenu, |
+ self, |
+ @selector(optionsMenuChanged:), |
+ title, |
+ cmd, |
+ enabled, |
+ checked); |
+ } |
+} |
+ |
+- (void)populateLanguageMenus { |
+ NSMenu* originalLanguageMenu = [fromLanguagePopUp_ menu]; |
+ [originalLanguageMenu setAutoenablesItems:NO]; |
+ int selectedMenuIndex = 0; |
+ int selectedLangIndex = [self delegate]->original_language_index(); |
+ for (int i = 0; i < originalLanguageMenuModel_->GetItemCount(); ++i) { |
+ NSString* title = base::SysUTF16ToNSString( |
+ originalLanguageMenuModel_->GetLabelAt(i)); |
+ int cmd = originalLanguageMenuModel_->GetCommandIdAt(i); |
+ bool checked = (cmd == selectedLangIndex); |
+ if (checked) |
+ selectedMenuIndex = i; |
+ bool enabled = originalLanguageMenuModel_->IsEnabledAt(i); |
+ cmd += IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE; |
+ AddMenuItem(originalLanguageMenu, |
+ self, |
+ @selector(languageMenuChanged:), |
+ title, |
+ cmd, |
+ enabled, |
+ checked); |
+ } |
+ [fromLanguagePopUp_ selectItemAtIndex:selectedMenuIndex]; |
+ |
+ NSMenu* targetLanguageMenu = [toLanguagePopUp_ menu]; |
+ [targetLanguageMenu setAutoenablesItems:NO]; |
+ selectedLangIndex = [self delegate]->target_language_index(); |
+ for (int i = 0; i < targetLanguageMenuModel_->GetItemCount(); ++i) { |
+ NSString* title = base::SysUTF16ToNSString( |
+ targetLanguageMenuModel_->GetLabelAt(i)); |
+ int cmd = targetLanguageMenuModel_->GetCommandIdAt(i); |
+ bool checked = (cmd == selectedLangIndex); |
+ if (checked) |
+ selectedMenuIndex = i; |
+ bool enabled = targetLanguageMenuModel_->IsEnabledAt(i); |
+ cmd += IDC_TRANSLATE_TARGET_LANGUAGE_BASE; |
+ AddMenuItem(targetLanguageMenu, |
+ self, |
+ @selector(languageMenuChanged:), |
+ title, |
+ cmd, |
+ enabled, |
+ checked); |
+ } |
+ [toLanguagePopUp_ selectItemAtIndex:selectedMenuIndex]; |
+} |
+ |
+- (void)addAdditionalControls { |
+ using l10n_util::GetNSString; |
+ using l10n_util::GetNSStringWithFixup; |
+ |
+ // Get layout information from the NIB. |
+ NSRect okButtonFrame = [okButton_ frame]; |
+ NSRect cancelButtonFrame = [cancelButton_ frame]; |
+ spaceBetweenControls_ = NSMinX(cancelButtonFrame) - NSMaxX(okButtonFrame); |
+ |
+ // Set infobar background color. |
+ [self setInfoBarGradientColor]; |
+ |
+ // Instantiate additional controls. |
+ [self constructViews]; |
+ |
+ // Set ourselves as the delegate for the options menu so we can populate it |
+ // dynamically. |
+ [[optionsPopUp_ menu] setDelegate:self]; |
+ |
+ // Replace label_ with label1_ so we get a consistent look between all the |
+ // labels we display in the translate view. |
+ [[label_ superview] replaceSubview:label_ with:label1_.get()]; |
+ label_.reset(); // Now released. |
+ |
+ // Populate contextual menus. |
+ [self rebuildOptionsMenu]; |
+ [self populateLanguageMenus]; |
+ |
+ // Set OK & Cancel text. |
+ [okButton_ setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_ACCEPT)]; |
+ [cancelButton_ setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_DENY)]; |
+ |
+ // Set up "Show original" and "Try again" buttons. |
+ [showOriginalButton_ setBezelStyle:NSRoundRectBezelStyle]; |
+ [showOriginalButton_ setFrame:okButtonFrame]; |
+ [tryAgainButton_ setBezelStyle:NSRoundRectBezelStyle]; |
+ [tryAgainButton_ setFrame:okButtonFrame]; |
+ |
+ [showOriginalButton_ setTarget:self]; |
+ [showOriginalButton_ setAction:@selector(showOriginal:)]; |
+ [tryAgainButton_ setTarget:self]; |
+ [tryAgainButton_ setAction:@selector(ok:)]; |
+ |
+ [showOriginalButton_ |
+ setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_REVERT)]; |
+ [tryAgainButton_ |
+ setTitle:GetNSStringWithFixup(IDS_TRANSLATE_INFOBAR_RETRY)]; |
+ |
+ // Add and configure controls that are visible in all modes. |
+ [optionsPopUp_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin | |
+ NSViewMaxYMargin]; |
+ // Add "options" popup z-ordered below all other controls so when we |
+ // resize the toolbar it doesn't hide them. |
+ [infoBarView_ addSubview:optionsPopUp_ |
+ positioned:NSWindowBelow |
+ relativeTo:nil]; |
+ [GTMUILocalizerAndLayoutTweaker sizeToFitView:optionsPopUp_]; |
+ MoveControl(closeButton_, optionsPopUp_, spaceBetweenControls_, false); |
+ VerticallyCenterView(optionsPopUp_); |
+ |
+ // Show and place GUI elements. |
+ [self updateState]; |
+} |
+ |
+// Called when "Translate" button is clicked. |
+- (IBAction)ok:(id)sender { |
+ TranslateInfoBarDelegate2* delegate = [self delegate]; |
+ TranslateInfoBarDelegate2::Type state = delegate->type(); |
+ DCHECK(state == TranslateInfoBarDelegate2::BEFORE_TRANSLATE || |
+ state == TranslateInfoBarDelegate2::TRANSLATION_ERROR); |
+ delegate->Translate(); |
+ UMA_HISTOGRAM_COUNTS("Translate.Translate", 1); |
+} |
+ |
+// Called when someone clicks on the "Nope" button. |
+- (IBAction)cancel:(id)sender { |
+ DCHECK( |
+ [self delegate]->type() == TranslateInfoBarDelegate2::BEFORE_TRANSLATE); |
+ [self delegate]->TranslationDeclined(); |
+ UMA_HISTOGRAM_COUNTS("Translate.DeclineTranslate", 1); |
+ [super dismiss:nil]; |
+} |
+ |
+- (IBAction)showOriginal:(id)sender { |
+ [self delegate]->RevertTranslation(); |
+} |
+ |
+// Called when any of the language drop down menus are changed. |
+- (void)languageMenuChanged:(id)item { |
+ if ([item respondsToSelector:@selector(tag)]) { |
+ int cmd = [item tag]; |
+ if (cmd >= IDC_TRANSLATE_TARGET_LANGUAGE_BASE) { |
+ cmd -= IDC_TRANSLATE_TARGET_LANGUAGE_BASE; |
+ [self targetLanguageModified:cmd]; |
+ return; |
+ } else if (cmd >= IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE) { |
+ cmd -= IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE; |
+ [self sourceLanguageModified:cmd]; |
+ return; |
+ } |
+ } |
+ NOTREACHED() << "Language menu was changed with a bad language ID"; |
+} |
+ |
+// Called when the options menu is changed. |
+- (void)optionsMenuChanged:(id)item { |
+ if ([item respondsToSelector:@selector(tag)]) { |
+ int cmd = [item tag]; |
+ // Danger Will Robinson! : This call can release the infobar (e.g. invoking |
+ // "About Translate" can open a new tab). |
+ // Do not access member variables after this line! |
+ optionsMenuModel_->ExecuteCommand(cmd); |
+ } else { |
+ NOTREACHED(); |
+ } |
+} |
+ |
+#pragma mark NSMenuDelegate |
+ |
+// Invoked by virtue of us being set as the delegate for the options menu. |
+- (void)menuNeedsUpdate:(NSMenu *)menu { |
+ [self rebuildOptionsMenu]; |
+} |
+ |
+@end |
+ |
+@implementation TranslateInfoBarControllerBase (TestingAPI) |
+ |
+- (NSMenu*)optionsMenu { |
+ return [optionsPopUp_ menu]; |
+} |
+ |
+- (NSButton*)tryAgainButton { |
+ return tryAgainButton_.get(); |
+} |
+ |
+- (bool)verifyLayout { |
+ // All the controls available to translate infobars, except the options popup. |
+ // The options popup is shown/hidden instead of actually removed. This gets |
+ // checked in the subclasses. |
+ NSArray* allControls = [NSArray arrayWithObjects:label1_.get(), |
+ fromLanguagePopUp_.get(), label2_.get(), toLanguagePopUp_.get(), |
+ label3_.get(), showOriginalButton_.get(), tryAgainButton_.get(), nil]; |
+ NSArray* visibleControls = [self visibleControls]; |
+ |
+ // Step 1: Make sure control visibility is what we expect. |
+ for (NSUInteger i = 0; i < [allControls count]; ++i) { |
+ id control = [allControls objectAtIndex:i]; |
+ bool hasSuperView = [control superview]; |
+ bool expectedVisibility = [visibleControls containsObject:control]; |
+ |
+ if (expectedVisibility != hasSuperView) { |
+ NSString *title = @""; |
+ if ([control isKindOfClass:[NSPopUpButton class]]) { |
+ title = [[[control menu] itemAtIndex:0] title]; |
+ } |
+ |
+ LOG(ERROR) << |
+ "State: " << [self description] << |
+ " Control @" << i << (hasSuperView ? " has" : " doesn't have") << |
+ " a superview" << [[control description] UTF8String] << |
+ " Title=" << [title UTF8String]; |
+ return false; |
+ } |
+ } |
+ |
+ // Step 2: Check that controls are ordered correctly with no overlap. |
+ id previousControl = nil; |
+ for (NSUInteger i = 0; i < [visibleControls count]; ++i) { |
+ id control = [visibleControls objectAtIndex:i]; |
+ if (previousControl && !VerifyControlOrderAndSpacing(previousControl, control)) { |
+ NSString *title = @""; |
+ if ([control isKindOfClass:[NSPopUpButton class]]) { |
+ title = [[[control menu] itemAtIndex:0] title]; |
+ } |
+ LOG(ERROR) << |
+ "State: " << [self description] << |
+ " Control @" << i << " not ordered correctly: " << |
+ [[control description] UTF8String] <<[title UTF8String]; |
+ return false; |
+ } |
+ previousControl = control; |
+ } |
+ |
+ return true; |
+} |
+ |
+@end // TranslateInfoBarControllerBase (TestingAPI) |
+ |