| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "chrome/browser/ui/cocoa/wrench_menu_controller.h" | |
| 6 | |
| 7 #include "app/l10n_util.h" | |
| 8 #include "base/sys_string_conversions.h" | |
| 9 #include "chrome/app/chrome_command_ids.h" | |
| 10 #include "chrome/browser/background_page_tracker.h" | |
| 11 #include "chrome/browser/metrics/user_metrics.h" | |
| 12 #import "chrome/browser/ui/cocoa/menu_tracked_root_view.h" | |
| 13 #import "chrome/browser/ui/cocoa/toolbar_controller.h" | |
| 14 #include "chrome/browser/ui/browser.h" | |
| 15 #include "chrome/browser/ui/browser_window.h" | |
| 16 #include "chrome/browser/ui/toolbar/wrench_menu_model.h" | |
| 17 #include "chrome/common/notification_observer.h" | |
| 18 #include "chrome/common/notification_service.h" | |
| 19 #include "chrome/common/notification_source.h" | |
| 20 #include "chrome/common/notification_type.h" | |
| 21 #include "grit/chromium_strings.h" | |
| 22 #include "grit/generated_resources.h" | |
| 23 #include "ui/base/models/menu_model.h" | |
| 24 | |
| 25 @interface WrenchMenuController (Private) | |
| 26 - (void)adjustPositioning; | |
| 27 - (void)performCommandDispatch:(NSNumber*)tag; | |
| 28 - (NSButton*)zoomDisplay; | |
| 29 @end | |
| 30 | |
| 31 namespace WrenchMenuControllerInternal { | |
| 32 | |
| 33 class ZoomLevelObserver : public NotificationObserver { | |
| 34 public: | |
| 35 explicit ZoomLevelObserver(WrenchMenuController* controller) | |
| 36 : controller_(controller) { | |
| 37 registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED, | |
| 38 NotificationService::AllSources()); | |
| 39 } | |
| 40 | |
| 41 void Observe(NotificationType type, | |
| 42 const NotificationSource& source, | |
| 43 const NotificationDetails& details) { | |
| 44 DCHECK_EQ(type.value, NotificationType::ZOOM_LEVEL_CHANGED); | |
| 45 WrenchMenuModel* wrenchMenuModel = [controller_ wrenchMenuModel]; | |
| 46 wrenchMenuModel->UpdateZoomControls(); | |
| 47 const string16 level = | |
| 48 wrenchMenuModel->GetLabelForCommandId(IDC_ZOOM_PERCENT_DISPLAY); | |
| 49 [[controller_ zoomDisplay] setTitle:SysUTF16ToNSString(level)]; | |
| 50 } | |
| 51 | |
| 52 private: | |
| 53 NotificationRegistrar registrar_; | |
| 54 WrenchMenuController* controller_; // Weak; owns this. | |
| 55 }; | |
| 56 | |
| 57 } // namespace WrenchMenuControllerInternal | |
| 58 | |
| 59 @implementation WrenchMenuController | |
| 60 | |
| 61 - (id)init { | |
| 62 if ((self = [super init])) { | |
| 63 observer_.reset(new WrenchMenuControllerInternal::ZoomLevelObserver(self)); | |
| 64 } | |
| 65 return self; | |
| 66 } | |
| 67 | |
| 68 - (void)addItemToMenu:(NSMenu*)menu | |
| 69 atIndex:(NSInteger)index | |
| 70 fromModel:(ui::MenuModel*)model | |
| 71 modelIndex:(int)modelIndex { | |
| 72 // Non-button item types should be built as normal items. | |
| 73 ui::MenuModel::ItemType type = model->GetTypeAt(modelIndex); | |
| 74 if (type != ui::MenuModel::TYPE_BUTTON_ITEM) { | |
| 75 [super addItemToMenu:menu | |
| 76 atIndex:index | |
| 77 fromModel:model | |
| 78 modelIndex:modelIndex]; | |
| 79 return; | |
| 80 } | |
| 81 | |
| 82 // Handle the special-cased menu items. | |
| 83 int command_id = model->GetCommandIdAt(modelIndex); | |
| 84 scoped_nsobject<NSMenuItem> customItem( | |
| 85 [[NSMenuItem alloc] initWithTitle:@"" | |
| 86 action:nil | |
| 87 keyEquivalent:@""]); | |
| 88 switch (command_id) { | |
| 89 case IDC_EDIT_MENU: | |
| 90 DCHECK(editItem_); | |
| 91 [customItem setView:editItem_]; | |
| 92 [editItem_ setMenuItem:customItem]; | |
| 93 break; | |
| 94 case IDC_ZOOM_MENU: | |
| 95 DCHECK(zoomItem_); | |
| 96 [customItem setView:zoomItem_]; | |
| 97 [zoomItem_ setMenuItem:customItem]; | |
| 98 break; | |
| 99 default: | |
| 100 NOTREACHED(); | |
| 101 break; | |
| 102 } | |
| 103 [self adjustPositioning]; | |
| 104 [menu insertItem:customItem.get() atIndex:index]; | |
| 105 } | |
| 106 | |
| 107 - (NSMenu*)menu { | |
| 108 NSMenu* menu = [super menu]; | |
| 109 if (![menu delegate]) { | |
| 110 [menu setDelegate:self]; | |
| 111 } | |
| 112 return menu; | |
| 113 } | |
| 114 | |
| 115 - (void)menuWillOpen:(NSMenu*)menu { | |
| 116 NSString* title = base::SysUTF16ToNSString( | |
| 117 [self wrenchMenuModel]->GetLabelForCommandId(IDC_ZOOM_PERCENT_DISPLAY)); | |
| 118 [[zoomItem_ viewWithTag:IDC_ZOOM_PERCENT_DISPLAY] setTitle:title]; | |
| 119 UserMetrics::RecordAction(UserMetricsAction("ShowAppMenu")); | |
| 120 | |
| 121 NSImage* icon = [self wrenchMenuModel]->browser()->window()->IsFullscreen() ? | |
| 122 [NSImage imageNamed:NSImageNameExitFullScreenTemplate] : | |
| 123 [NSImage imageNamed:NSImageNameEnterFullScreenTemplate]; | |
| 124 [zoomFullScreen_ setImage:icon]; | |
| 125 } | |
| 126 | |
| 127 - (void)menuDidClose:(NSMenu*)menu { | |
| 128 // When the menu is closed, acknowledge the background pages so the badges go | |
| 129 // away. | |
| 130 BackgroundPageTracker::GetInstance()->AcknowledgeBackgroundPages(); | |
| 131 } | |
| 132 | |
| 133 // Used to dispatch commands from the Wrench menu. The custom items within the | |
| 134 // menu cannot be hooked up directly to First Responder because the window in | |
| 135 // which the controls reside is not the BrowserWindowController, but a | |
| 136 // NSCarbonMenuWindow; this screws up the typical |-commandDispatch:| system. | |
| 137 - (IBAction)dispatchWrenchMenuCommand:(id)sender { | |
| 138 NSInteger tag = [sender tag]; | |
| 139 if (sender == zoomPlus_ || sender == zoomMinus_) { | |
| 140 // Do a direct dispatch rather than scheduling on the outermost run loop, | |
| 141 // which would not get hit until after the menu had closed. | |
| 142 [self performCommandDispatch:[NSNumber numberWithInt:tag]]; | |
| 143 | |
| 144 // The zoom buttons should not close the menu if opened sticky. | |
| 145 if ([sender respondsToSelector:@selector(isTracking)] && | |
| 146 [sender performSelector:@selector(isTracking)]) { | |
| 147 [menu_ cancelTracking]; | |
| 148 } | |
| 149 } else { | |
| 150 // The custom views within the Wrench menu are abnormal and keep the menu | |
| 151 // open after a target-action. Close the menu manually. | |
| 152 [menu_ cancelTracking]; | |
| 153 [self dispatchCommandInternal:tag]; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 - (void)dispatchCommandInternal:(NSInteger)tag { | |
| 158 // Executing certain commands from the nested run loop of the menu can lead | |
| 159 // to wonky behavior (e.g. http://crbug.com/49716). To avoid this, schedule | |
| 160 // the dispatch on the outermost run loop. | |
| 161 [self performSelector:@selector(performCommandDispatch:) | |
| 162 withObject:[NSNumber numberWithInt:tag] | |
| 163 afterDelay:0.0]; | |
| 164 } | |
| 165 | |
| 166 // Used to perform the actual dispatch on the outermost runloop. | |
| 167 - (void)performCommandDispatch:(NSNumber*)tag { | |
| 168 [self wrenchMenuModel]->ExecuteCommand([tag intValue]); | |
| 169 } | |
| 170 | |
| 171 - (WrenchMenuModel*)wrenchMenuModel { | |
| 172 return static_cast<WrenchMenuModel*>(model_); | |
| 173 } | |
| 174 | |
| 175 // Fit the localized strings into the Cut/Copy/Paste control, then resize the | |
| 176 // whole menu item accordingly. | |
| 177 - (void)adjustPositioning { | |
| 178 const CGFloat kButtonPadding = 12; | |
| 179 CGFloat delta = 0; | |
| 180 | |
| 181 // Go through the three buttons from right-to-left, adjusting the size to fit | |
| 182 // the localized strings while keeping them all aligned on their horizontal | |
| 183 // edges. | |
| 184 const size_t kAdjustViewCount = 3; | |
| 185 NSButton* views[kAdjustViewCount] = { editPaste_, editCopy_, editCut_ }; | |
| 186 for (size_t i = 0; i < kAdjustViewCount; ++i) { | |
| 187 NSButton* button = views[i]; | |
| 188 CGFloat originalWidth = NSWidth([button frame]); | |
| 189 | |
| 190 // Do not let |-sizeToFit| change the height of the button. | |
| 191 NSSize size = [button frame].size; | |
| 192 [button sizeToFit]; | |
| 193 size.width = [button frame].size.width + kButtonPadding; | |
| 194 [button setFrameSize:size]; | |
| 195 | |
| 196 CGFloat newWidth = size.width; | |
| 197 delta += newWidth - originalWidth; | |
| 198 | |
| 199 NSRect frame = [button frame]; | |
| 200 frame.origin.x -= delta; | |
| 201 [button setFrame:frame]; | |
| 202 } | |
| 203 | |
| 204 // Resize the menu item by the total amound the buttons changed so that the | |
| 205 // spacing between the buttons and the title remains the same. | |
| 206 NSRect itemFrame = [editItem_ frame]; | |
| 207 itemFrame.size.width += delta; | |
| 208 [editItem_ setFrame:itemFrame]; | |
| 209 | |
| 210 // Also resize the superview of the buttons, which is an NSView used to slide | |
| 211 // when the item title is too big and GTM resizes it. | |
| 212 NSRect parentFrame = [[editCut_ superview] frame]; | |
| 213 parentFrame.size.width += delta; | |
| 214 parentFrame.origin.x -= delta; | |
| 215 [[editCut_ superview] setFrame:parentFrame]; | |
| 216 } | |
| 217 | |
| 218 - (NSButton*)zoomDisplay { | |
| 219 return zoomDisplay_; | |
| 220 } | |
| 221 | |
| 222 @end // @implementation WrenchMenuController | |
| OLD | NEW |