| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/glue/webmenurunner_mac.h" | 5 #include "webkit/glue/webmenurunner_mac.h" |
| 6 | 6 |
| 7 #include "base/sys_string_conversions.h" | 7 #include "base/sys_string_conversions.h" |
| 8 | 8 |
| 9 namespace { | |
| 10 | |
| 11 const CGFloat kPopupXOffset = -10.0f; | |
| 12 BOOL gNewNSMenuAPI; | |
| 13 | |
| 14 } // namespace | |
| 15 | |
| 16 #if !defined(MAC_OS_X_VERSION_10_6) || \ | 9 #if !defined(MAC_OS_X_VERSION_10_6) || \ |
| 17 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 | 10 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 |
| 18 @interface NSMenu (SnowLeopardSDKDeclarations) | 11 enum { |
| 19 - (BOOL)popUpMenuPositioningItem:(NSMenuItem *)item | 12 NSUserInterfaceLayoutDirectionLeftToRight = 0, |
| 20 atLocation:(NSPoint)location | 13 NSUserInterfaceLayoutDirectionRightToLeft = 1 |
| 21 inView:(NSView *)view; | 14 }; |
| 22 - (void)setFont:(NSFont *)font; | 15 typedef NSInteger NSUserInterfaceLayoutDirection; |
| 16 |
| 17 @interface NSCell (SnowLeopardSDKDeclarations) |
| 18 - (void)setUserInterfaceLayoutDirection: |
| 19 (NSUserInterfaceLayoutDirection)layoutDirection; |
| 23 @end | 20 @end |
| 21 |
| 22 enum { |
| 23 NSTextWritingDirectionEmbedding = (0 << 1), |
| 24 NSTextWritingDirectionOverride = (1 << 1) |
| 25 }; |
| 26 |
| 27 static NSString* NSWritingDirectionAttributeName = @"NSWritingDirection"; |
| 24 #endif | 28 #endif |
| 25 | 29 |
| 26 @interface WebMenuRunner (PrivateAPI) | 30 @interface WebMenuRunner (PrivateAPI) |
| 27 | 31 |
| 28 // Worker function used during initialization. | 32 // Worker function used during initialization. |
| 29 - (void)addItem:(const WebMenuItem&)item | 33 - (void)addItem:(const WebMenuItem&)item; |
| 30 withAttributes:(NSDictionary*)attrs; | |
| 31 | 34 |
| 32 // A callback for the menu controller object to call when an item is selected | 35 // A callback for the menu controller object to call when an item is selected |
| 33 // from the menu. This is not called if the menu is dismissed without a | 36 // from the menu. This is not called if the menu is dismissed without a |
| 34 // selection. | 37 // selection. |
| 35 - (void)menuItemSelected:(id)sender; | 38 - (void)menuItemSelected:(id)sender; |
| 36 | 39 |
| 37 @end // WebMenuRunner (PrivateAPI) | 40 @end // WebMenuRunner (PrivateAPI) |
| 38 | 41 |
| 39 @implementation WebMenuRunner | 42 @implementation WebMenuRunner |
| 40 | 43 |
| 41 - (id)initWithItems:(const std::vector<WebMenuItem>&)items | 44 - (id)initWithItems:(const std::vector<WebMenuItem>&)items |
| 42 fontSize:(CGFloat)fontSize | 45 fontSize:(CGFloat)fontSize |
| 43 rightAligned:(BOOL)rightAligned { | 46 rightAligned:(BOOL)rightAligned { |
| 44 static BOOL newNSMenuAPIInitialized = NO; | |
| 45 if (!newNSMenuAPIInitialized) { | |
| 46 newNSMenuAPIInitialized = YES; | |
| 47 gNewNSMenuAPI = [NSMenu instancesRespondToSelector: | |
| 48 @selector(popUpMenuPositioningItem:atLocation:inView:)] && | |
| 49 [NSMenu instancesRespondToSelector:@selector(setFont:)]; | |
| 50 } | |
| 51 | |
| 52 if ((self = [super init])) { | 47 if ((self = [super init])) { |
| 53 menu_.reset([[NSMenu alloc] initWithTitle:@""]); | 48 menu_.reset([[NSMenu alloc] initWithTitle:@""]); |
| 54 if (gNewNSMenuAPI) | |
| 55 [menu_ setFont:[NSFont menuFontOfSize:fontSize]]; | |
| 56 [menu_ setAutoenablesItems:NO]; | 49 [menu_ setAutoenablesItems:NO]; |
| 57 index_ = -1; | 50 index_ = -1; |
| 58 fontSize_ = fontSize; | 51 fontSize_ = fontSize; |
| 59 scoped_nsobject<NSDictionary> attrs; | 52 rightAligned_ = rightAligned; |
| 60 if (rightAligned) { | |
| 61 // NB: Right-aligning menu items in this manner is known to not work in | |
| 62 // Mac OS X 10.5. | |
| 63 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( | |
| 64 [[NSMutableParagraphStyle alloc] init]); | |
| 65 [paragraphStyle setAlignment:NSRightTextAlignment]; | |
| 66 attrs.reset([[NSDictionary alloc] initWithObjectsAndKeys: | |
| 67 paragraphStyle, NSParagraphStyleAttributeName, nil]); | |
| 68 } | |
| 69 for (size_t i = 0; i < items.size(); ++i) | 53 for (size_t i = 0; i < items.size(); ++i) |
| 70 [self addItem:items[i] withAttributes:attrs]; | 54 [self addItem:items[i]]; |
| 71 } | 55 } |
| 72 return self; | 56 return self; |
| 73 } | 57 } |
| 74 | 58 |
| 75 - (void)addItem:(const WebMenuItem&)item | 59 - (void)addItem:(const WebMenuItem&)item { |
| 76 withAttributes:(NSDictionary*)attrs { | |
| 77 if (item.type == WebMenuItem::SEPARATOR) { | 60 if (item.type == WebMenuItem::SEPARATOR) { |
| 78 [menu_ addItem:[NSMenuItem separatorItem]]; | 61 [menu_ addItem:[NSMenuItem separatorItem]]; |
| 79 return; | 62 return; |
| 80 } | 63 } |
| 81 | 64 |
| 82 NSString* title = base::SysUTF16ToNSString(item.label); | 65 NSString* title = base::SysUTF16ToNSString(item.label); |
| 83 NSMenuItem* menuItem = [menu_ addItemWithTitle:title | 66 NSMenuItem* menuItem = [menu_ addItemWithTitle:title |
| 84 action:@selector(menuItemSelected:) | 67 action:@selector(menuItemSelected:) |
| 85 keyEquivalent:@""]; | 68 keyEquivalent:@""]; |
| 86 [menuItem setEnabled:(item.enabled && item.type != WebMenuItem::GROUP)]; | 69 [menuItem setEnabled:(item.enabled && item.type != WebMenuItem::GROUP)]; |
| 87 [menuItem setTarget:self]; | 70 [menuItem setTarget:self]; |
| 88 if (attrs) { | 71 |
| 89 scoped_nsobject<NSAttributedString> attrTitle( | 72 // Set various alignment/language attributes. Note that many (if not most) of |
| 90 [[NSAttributedString alloc] initWithString:title | 73 // these attributes are functional only on 10.6 and above. |
| 91 attributes:attrs]); | 74 scoped_nsobject<NSMutableDictionary> attrs( |
| 92 [menuItem setAttributedTitle:attrTitle]; | 75 [[NSMutableDictionary alloc] initWithCapacity:3]); |
| 76 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( |
| 77 [[NSMutableParagraphStyle alloc] init]); |
| 78 [paragraphStyle setAlignment:rightAligned_ ? NSRightTextAlignment |
| 79 : NSLeftTextAlignment]; |
| 80 NSWritingDirection writingDirection = |
| 81 item.rtl ? NSWritingDirectionRightToLeft |
| 82 : NSWritingDirectionLeftToRight; |
| 83 [paragraphStyle setBaseWritingDirection:writingDirection]; |
| 84 [attrs setObject:paragraphStyle forKey:NSParagraphStyleAttributeName]; |
| 85 |
| 86 if (item.directional_override) { |
| 87 scoped_nsobject<NSNumber> directionValue( |
| 88 [[NSNumber alloc] initWithInteger: |
| 89 writingDirection + NSTextWritingDirectionOverride]); |
| 90 scoped_nsobject<NSArray> directionArray( |
| 91 [[NSArray alloc] initWithObjects:directionValue.get(), nil]); |
| 92 [attrs setObject:directionArray forKey:NSWritingDirectionAttributeName]; |
| 93 } | 93 } |
| 94 |
| 95 [attrs setObject:[NSFont menuFontOfSize:fontSize_] |
| 96 forKey:NSFontAttributeName]; |
| 97 |
| 98 scoped_nsobject<NSAttributedString> attrTitle( |
| 99 [[NSAttributedString alloc] initWithString:title |
| 100 attributes:attrs]); |
| 101 [menuItem setAttributedTitle:attrTitle]; |
| 102 |
| 94 [menuItem setTag:[menu_ numberOfItems] - 1]; | 103 [menuItem setTag:[menu_ numberOfItems] - 1]; |
| 95 } | 104 } |
| 96 | 105 |
| 97 // Reflects the result of the user's interaction with the popup menu. If NO, the | 106 // Reflects the result of the user's interaction with the popup menu. If NO, the |
| 98 // menu was dismissed without the user choosing an item, which can happen if the | 107 // menu was dismissed without the user choosing an item, which can happen if the |
| 99 // user clicked outside the menu region or hit the escape key. If YES, the user | 108 // user clicked outside the menu region or hit the escape key. If YES, the user |
| 100 // selected an item from the menu. | 109 // selected an item from the menu. |
| 101 - (BOOL)menuItemWasChosen { | 110 - (BOOL)menuItemWasChosen { |
| 102 return menuItemWasChosen_; | 111 return menuItemWasChosen_; |
| 103 } | 112 } |
| 104 | 113 |
| 105 - (void)menuItemSelected:(id)sender { | 114 - (void)menuItemSelected:(id)sender { |
| 106 menuItemWasChosen_ = YES; | 115 menuItemWasChosen_ = YES; |
| 107 if (gNewNSMenuAPI) | |
| 108 index_ = [sender tag]; | |
| 109 } | 116 } |
| 110 | 117 |
| 111 - (void)runMenuInView:(NSView*)view | 118 - (void)runMenuInView:(NSView*)view |
| 112 withBounds:(NSRect)bounds | 119 withBounds:(NSRect)bounds |
| 113 initialIndex:(int)index { | 120 initialIndex:(int)index { |
| 114 if (gNewNSMenuAPI) { | 121 // Set up the button cell, converting to NSView coordinates. The menu is |
| 115 // index might be out of bounds, in which case we set no selection. | 122 // positioned such that the currently selected menu item appears over the |
| 116 NSMenuItem* selectedItem = [menu_ itemWithTag:index]; | 123 // popup button, which is the expected Mac popup menu behavior. |
| 117 if (selectedItem) { | 124 NSPopUpButtonCell* button = [[NSPopUpButtonCell alloc] initTextCell:@"" |
| 118 [selectedItem setState:NSOnState]; | 125 pullsDown:NO]; |
| 119 } else { | 126 [button autorelease]; |
| 120 selectedItem = [menu_ itemWithTag:0]; | 127 [button setMenu:menu_]; |
| 121 } | 128 // We use selectItemWithTag below so if the index is out-of-bounds nothing |
| 122 NSPoint anchor = NSMakePoint(NSMinX(bounds) + kPopupXOffset, | 129 // bad happens. |
| 123 NSMaxY(bounds)); | 130 [button selectItemWithTag:index]; |
| 124 [menu_ popUpMenuPositioningItem:selectedItem | |
| 125 atLocation:anchor | |
| 126 inView:view]; | |
| 127 } else { | |
| 128 // Set up the button cell, converting to NSView coordinates. The menu is | |
| 129 // positioned such that the currently selected menu item appears over the | |
| 130 // popup button, which is the expected Mac popup menu behavior. | |
| 131 NSPopUpButtonCell* button = [[NSPopUpButtonCell alloc] initTextCell:@"" | |
| 132 pullsDown:NO]; | |
| 133 [button autorelease]; | |
| 134 [button setMenu:menu_]; | |
| 135 // We use selectItemWithTag below so if the index is out-of-bounds nothing | |
| 136 // bad happens. | |
| 137 [button selectItemWithTag:index]; | |
| 138 [button setFont:[NSFont menuFontOfSize:fontSize_]]; | |
| 139 | 131 |
| 140 // Create a dummy view to associate the popup with, since the OS will use | 132 if (rightAligned_ && |
| 141 // that view for positioning the menu. | 133 [button respondsToSelector:@selector(setUserInterfaceLayoutDirection:)]) { |
| 142 NSView* dummyView = [[[NSView alloc] initWithFrame:bounds] autorelease]; | 134 [button setUserInterfaceLayoutDirection: |
| 143 [view addSubview:dummyView]; | 135 NSUserInterfaceLayoutDirectionRightToLeft]; |
| 144 NSRect dummyBounds = [dummyView convertRect:bounds fromView:view]; | 136 } |
| 145 | 137 |
| 146 // Display the menu, and set a flag if a menu item was chosen. | 138 // Create a dummy view to associate the popup with, since the OS will use |
| 147 [button performClickWithFrame:dummyBounds inView:dummyView]; | 139 // that view for positioning the menu. |
| 140 NSView* dummyView = [[[NSView alloc] initWithFrame:bounds] autorelease]; |
| 141 [view addSubview:dummyView]; |
| 142 NSRect dummyBounds = [dummyView convertRect:bounds fromView:view]; |
| 148 | 143 |
| 149 if ([self menuItemWasChosen]) | 144 // Display the menu, and set a flag if a menu item was chosen. |
| 150 index_ = [button indexOfSelectedItem]; | 145 [button performClickWithFrame:dummyBounds inView:dummyView]; |
| 151 | 146 |
| 152 [dummyView removeFromSuperview]; | 147 if ([self menuItemWasChosen]) |
| 153 } | 148 index_ = [button indexOfSelectedItem]; |
| 149 |
| 150 [dummyView removeFromSuperview]; |
| 154 } | 151 } |
| 155 | 152 |
| 156 - (int)indexOfSelectedItem { | 153 - (int)indexOfSelectedItem { |
| 157 return index_; | 154 return index_; |
| 158 } | 155 } |
| 159 | 156 |
| 160 @end // WebMenuRunner | 157 @end // WebMenuRunner |
| 161 | 158 |
| 162 namespace webkit_glue { | 159 namespace webkit_glue { |
| 163 | 160 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 characters:@"" | 206 characters:@"" |
| 210 charactersIgnoringModifiers:escape_str | 207 charactersIgnoringModifiers:escape_str |
| 211 isARepeat:NO | 208 isARepeat:NO |
| 212 keyCode:0x1B]; | 209 keyCode:0x1B]; |
| 213 } | 210 } |
| 214 | 211 |
| 215 return event; | 212 return event; |
| 216 } | 213 } |
| 217 | 214 |
| 218 } // namespace webkit_glue | 215 } // namespace webkit_glue |
| OLD | NEW |