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 |