summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/delayedmenu_button.mm
blob: dbbde4afbb2f1c09a781f66ff9df6c378155f6af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright (c) 2009 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 "chrome/browser/cocoa/delayedmenu_button.h"

#include "base/logging.h"
#include "base/scoped_nsobject.h"
#import "chrome/browser/cocoa/clickhold_button_cell.h"

@interface DelayedMenuButton (Private)

- (void)setupCell;
- (void)attachedMenuAction:(id)sender;

@end  // @interface DelayedMenuButton (Private)

@implementation DelayedMenuButton

// Overrides:

+ (Class)cellClass {
  return [ClickHoldButtonCell class];
}

- (id)init {
  if ((self = [super init]))
    [self setupCell];
  return self;
}

- (id)initWithCoder:(NSCoder*)decoder {
  if ((self = [super initWithCoder:decoder]))
    [self setupCell];
  return self;
}

- (id)initWithFrame:(NSRect)frameRect {
  if ((self = [super initWithFrame:frameRect]))
    [self setupCell];
  return self;
}

- (void)dealloc {
  [attachedMenu_ release];
  [super dealloc];
}

- (void)awakeFromNib {
  [self setupCell];
}

- (void)setCell:(NSCell*)cell {
  [super setCell:cell];
  [self setupCell];
}

// Accessors and mutators:

@synthesize attachedMenu = attachedMenu_;

// Don't synthesize for attachedMenuEnabled_; its mutator must do other things.
- (void)setAttachedMenuEnabled:(BOOL)enabled {
  attachedMenuEnabled_ = enabled;
  [[self cell] setEnableClickHold:attachedMenuEnabled_];
}

- (BOOL)attachedMenuEnabled {
  return attachedMenuEnabled_;
}

@end  // @implementation DelayedMenuButton

@implementation DelayedMenuButton (Private)

// Set up the button's cell if we've reached a point where it's been set.
- (void)setupCell {
  ClickHoldButtonCell* cell = [self cell];
  if (cell) {
    DCHECK([cell isKindOfClass:[ClickHoldButtonCell class]]);
    [self setEnabled:NO];               // Make the controller put in a menu and
                                        // enable it explicitly. This also takes
                                        // care of |[cell setEnableClickHold:]|.
    [cell setClickHoldAction:@selector(attachedMenuAction:)];
    [cell setClickHoldTarget:self];
  }
}

// Display the menu.
- (void)attachedMenuAction:(id)sender {
  // We shouldn't get here unless the menu is enabled.
  DCHECK(attachedMenuEnabled_);

  // If we don't have a menu (in which case the person using this control is
  // being bad), just wait for a mouse up.
  if (!attachedMenu_) {
    LOG(WARNING) << "No menu available.";
    [NSApp nextEventMatchingMask:NSLeftMouseUpMask
                       untilDate:[NSDate distantFuture]
                          inMode:NSEventTrackingRunLoopMode
                         dequeue:YES];
    return;
  }

  // TODO(viettrungluu): We have some fudge factors below to make things line up
  // (approximately). I wish I knew how to get rid of them. (Note that our view
  // is flipped, and that frame should be in our coordinates.) The y/height is
  // very odd, since it doesn't seem to respond to changes the way that it
  // should. I don't understand it.
  NSRect frame = [self convertRect:[self frame]
                          fromView:[self superview]];
  frame.origin.x -= 2.0;
  frame.size.height += 10.0;

  // Make our pop-up button cell and set things up. This is, as of 10.5, the
  // official Apple-recommended hack. Later, perhaps |-[NSMenu
  // popUpMenuPositioningItem:atLocation:inView:]| may be a better option.
  // However, using a pulldown has the benefit that Cocoa automatically places
  // the menu correctly even when we're at the edge of the screen (including
  // "dragging upwards" when the button is close to the bottom of the screen).
  // A |scoped_nsobject| local variable cannot be used here because
  // Accessibility on 10.5 grabs the NSPopUpButtonCell without retaining it, and
  // uses it later. (This is fixed in 10.6.)
  if (!popUpCell_.get()) {
    popUpCell_.reset([[NSPopUpButtonCell alloc] initTextCell:@""
                                                   pullsDown:YES]);
  }
  DCHECK(popUpCell_.get());
  [popUpCell_ setMenu:attachedMenu_];
  [popUpCell_ selectItem:nil];
  [popUpCell_ attachPopUpWithFrame:frame
                            inView:self];
  [popUpCell_ performClickWithFrame:frame
                             inView:self];
}

@end  // @implementation DelayedMenuButton (Private)