Home | History | Annotate | Download | only in renderer_host
      1 // Copyright 2013 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 #include "content/browser/renderer_host/webmenurunner_mac.h"
      6 
      7 #include "base/strings/sys_string_conversions.h"
      8 
      9 @interface WebMenuRunner (PrivateAPI)
     10 
     11 // Worker function used during initialization.
     12 - (void)addItem:(const content::MenuItem&)item;
     13 
     14 // A callback for the menu controller object to call when an item is selected
     15 // from the menu. This is not called if the menu is dismissed without a
     16 // selection.
     17 - (void)menuItemSelected:(id)sender;
     18 
     19 @end  // WebMenuRunner (PrivateAPI)
     20 
     21 @implementation WebMenuRunner
     22 
     23 - (id)initWithItems:(const std::vector<content::MenuItem>&)items
     24            fontSize:(CGFloat)fontSize
     25        rightAligned:(BOOL)rightAligned {
     26   if ((self = [super init])) {
     27     menu_.reset([[NSMenu alloc] initWithTitle:@""]);
     28     [menu_ setAutoenablesItems:NO];
     29     index_ = -1;
     30     fontSize_ = fontSize;
     31     rightAligned_ = rightAligned;
     32     for (size_t i = 0; i < items.size(); ++i)
     33       [self addItem:items[i]];
     34   }
     35   return self;
     36 }
     37 
     38 - (void)addItem:(const content::MenuItem&)item {
     39   if (item.type == content::MenuItem::SEPARATOR) {
     40     [menu_ addItem:[NSMenuItem separatorItem]];
     41     return;
     42   }
     43 
     44   NSString* title = base::SysUTF16ToNSString(item.label);
     45   NSMenuItem* menuItem = [menu_ addItemWithTitle:title
     46                                           action:@selector(menuItemSelected:)
     47                                    keyEquivalent:@""];
     48   if (!item.tool_tip.empty()) {
     49     NSString* toolTip = base::SysUTF16ToNSString(item.tool_tip);
     50     [menuItem setToolTip:toolTip];
     51   }
     52   [menuItem setEnabled:(item.enabled && item.type != content::MenuItem::GROUP)];
     53   [menuItem setTarget:self];
     54 
     55   // Set various alignment/language attributes. Note that many (if not most) of
     56   // these attributes are functional only on 10.6 and above.
     57   base::scoped_nsobject<NSMutableDictionary> attrs(
     58       [[NSMutableDictionary alloc] initWithCapacity:3]);
     59   base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
     60       [[NSMutableParagraphStyle alloc] init]);
     61   [paragraphStyle setAlignment:rightAligned_ ? NSRightTextAlignment
     62                                              : NSLeftTextAlignment];
     63   NSWritingDirection writingDirection =
     64       item.rtl ? NSWritingDirectionRightToLeft
     65                : NSWritingDirectionLeftToRight;
     66   [paragraphStyle setBaseWritingDirection:writingDirection];
     67   [attrs setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
     68 
     69   if (item.has_directional_override) {
     70     base::scoped_nsobject<NSNumber> directionValue(
     71         [[NSNumber alloc] initWithInteger:
     72             writingDirection + NSTextWritingDirectionOverride]);
     73     base::scoped_nsobject<NSArray> directionArray(
     74         [[NSArray alloc] initWithObjects:directionValue.get(), nil]);
     75     [attrs setObject:directionArray forKey:NSWritingDirectionAttributeName];
     76   }
     77 
     78   [attrs setObject:[NSFont menuFontOfSize:fontSize_]
     79             forKey:NSFontAttributeName];
     80 
     81   base::scoped_nsobject<NSAttributedString> attrTitle(
     82       [[NSAttributedString alloc] initWithString:title attributes:attrs]);
     83   [menuItem setAttributedTitle:attrTitle];
     84 
     85   [menuItem setTag:[menu_ numberOfItems] - 1];
     86 }
     87 
     88 // Reflects the result of the user's interaction with the popup menu. If NO, the
     89 // menu was dismissed without the user choosing an item, which can happen if the
     90 // user clicked outside the menu region or hit the escape key. If YES, the user
     91 // selected an item from the menu.
     92 - (BOOL)menuItemWasChosen {
     93   return menuItemWasChosen_;
     94 }
     95 
     96 - (void)menuItemSelected:(id)sender {
     97   menuItemWasChosen_ = YES;
     98 }
     99 
    100 - (void)runMenuInView:(NSView*)view
    101            withBounds:(NSRect)bounds
    102          initialIndex:(int)index {
    103   // Set up the button cell, converting to NSView coordinates. The menu is
    104   // positioned such that the currently selected menu item appears over the
    105   // popup button, which is the expected Mac popup menu behavior.
    106   base::scoped_nsobject<NSPopUpButtonCell> cell(
    107       [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
    108   [cell setMenu:menu_];
    109   // We use selectItemWithTag below so if the index is out-of-bounds nothing
    110   // bad happens.
    111   [cell selectItemWithTag:index];
    112 
    113   if (rightAligned_ &&
    114       [cell respondsToSelector:@selector(setUserInterfaceLayoutDirection:)]) {
    115     [cell setUserInterfaceLayoutDirection:
    116         NSUserInterfaceLayoutDirectionRightToLeft];
    117   }
    118 
    119   // When popping up a menu near the Dock, Cocoa restricts the menu
    120   // size to not overlap the Dock, with a scroll arrow.  Below a
    121   // certain point this doesn't work.  At that point the menu is
    122   // popped up above the element, so that the current item can be
    123   // selected without mouse-tracking selecting a different item
    124   // immediately.
    125   //
    126   // Unfortunately, instead of popping up above the passed |bounds|,
    127   // it pops up above the bounds of the view passed to inView:.  Use a
    128   // dummy view to fake this out.
    129   base::scoped_nsobject<NSView> dummyView(
    130       [[NSView alloc] initWithFrame:bounds]);
    131   [view addSubview:dummyView];
    132 
    133   // Display the menu, and set a flag if a menu item was chosen.
    134   [cell attachPopUpWithFrame:[dummyView bounds] inView:dummyView];
    135   [cell performClickWithFrame:[dummyView bounds] inView:dummyView];
    136 
    137   [dummyView removeFromSuperview];
    138 
    139   if ([self menuItemWasChosen])
    140     index_ = [cell indexOfSelectedItem];
    141 }
    142 
    143 - (int)indexOfSelectedItem {
    144   return index_;
    145 }
    146 
    147 @end  // WebMenuRunner
    148