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.
5 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h"
7 #include "base/logging.h"
8 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h"
12 // NSEvent -buttonNumber for middle mouse button.
13 const NSInteger kMiddleButtonNumber = 2;
17 @interface OmniboxPopupMatrix()
18 - (void)resetTrackingArea;
19 - (void)highlightRowAt:(NSInteger)rowIndex;
20 - (void)highlightRowUnder:(NSEvent*)theEvent;
21 - (BOOL)selectCellForEvent:(NSEvent*)theEvent;
24 @implementation OmniboxPopupMatrix
26 - (id)initWithDelegate:(OmniboxPopupMatrixDelegate*)delegate {
27 if ((self = [super initWithFrame:NSZeroRect])) {
29 [self setCellClass:[OmniboxPopupCell class]];
31 // Cells pack with no spacing.
32 [self setIntercellSpacing:NSMakeSize(0.0, 0.0)];
34 [self setDrawsBackground:YES];
35 [self setBackgroundColor:[NSColor controlBackgroundColor]];
36 [self renewRows:0 columns:1];
37 [self setAllowsEmptySelection:YES];
38 [self setMode:NSRadioModeMatrix];
39 [self deselectAllCells];
41 [self resetTrackingArea];
46 - (void)setDelegate:(OmniboxPopupMatrixDelegate*)delegate {
50 - (NSInteger)highlightedRow {
51 NSArray* cells = [self cells];
52 const NSUInteger count = [cells count];
53 for(NSUInteger i = 0; i < count; ++i) {
54 if ([[cells objectAtIndex:i] isHighlighted]) {
61 - (void)updateTrackingAreas {
62 [self resetTrackingArea];
63 [super updateTrackingAreas];
66 // Callbacks from tracking area.
67 - (void)mouseEntered:(NSEvent*)theEvent {
68 [self highlightRowUnder:theEvent];
71 - (void)mouseMoved:(NSEvent*)theEvent {
72 [self highlightRowUnder:theEvent];
75 - (void)mouseExited:(NSEvent*)theEvent {
76 [self highlightRowAt:-1];
79 // The tracking area events aren't forwarded during a drag, so handle
80 // highlighting manually for middle-click and middle-drag.
81 - (void)otherMouseDown:(NSEvent*)theEvent {
82 if ([theEvent buttonNumber] == kMiddleButtonNumber) {
83 [self highlightRowUnder:theEvent];
85 [super otherMouseDown:theEvent];
88 - (void)otherMouseDragged:(NSEvent*)theEvent {
89 if ([theEvent buttonNumber] == kMiddleButtonNumber) {
90 [self highlightRowUnder:theEvent];
92 [super otherMouseDragged:theEvent];
95 - (void)otherMouseUp:(NSEvent*)theEvent {
96 // Only intercept middle button.
97 if ([theEvent buttonNumber] != kMiddleButtonNumber) {
98 [super otherMouseUp:theEvent];
102 // -otherMouseDragged: should always have been called at this location, but
103 // make sure the user is getting the right feedback.
104 [self highlightRowUnder:theEvent];
106 const NSInteger highlightedRow = [self highlightedRow];
107 if (highlightedRow != -1) {
109 delegate_->OnMatrixRowMiddleClicked(self, highlightedRow);
113 // Track the mouse until released, keeping the cell under the mouse selected.
114 // If the mouse wanders off-view, revert to the originally-selected cell. If
115 // the mouse is released over a cell, call the delegate to open the row's URL.
116 - (void)mouseDown:(NSEvent*)theEvent {
117 NSCell* selectedCell = [self selectedCell];
119 // Clear any existing highlight.
120 [self highlightRowAt:-1];
123 if (![self selectCellForEvent:theEvent]) {
124 [self selectCell:selectedCell];
127 const NSUInteger mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
128 theEvent = [[self window] nextEventMatchingMask:mask];
129 } while ([theEvent type] == NSLeftMouseDragged);
131 // Do not message the delegate if released outside view.
132 if (![self selectCellForEvent:theEvent]) {
133 [self selectCell:selectedCell];
135 const NSInteger selectedRow = [self selectedRow];
137 // No row could be selected if the model failed to update.
138 if (selectedRow == -1) {
144 delegate_->OnMatrixRowClicked(self, selectedRow);
148 - (void)resetTrackingArea {
149 if (trackingArea_.get())
150 [self removeTrackingArea:trackingArea_.get()];
152 trackingArea_.reset([[CrTrackingArea alloc]
153 initWithRect:[self frame]
154 options:NSTrackingMouseEnteredAndExited |
155 NSTrackingMouseMoved |
156 NSTrackingActiveInActiveApp |
157 NSTrackingInVisibleRect
160 [self addTrackingArea:trackingArea_.get()];
163 - (void)highlightRowAt:(NSInteger)rowIndex {
164 // highlightCell will be nil if rowIndex is out of range, so no cell will be
166 NSCell* highlightCell = [self cellAtRow:rowIndex column:0];
168 for (NSCell* cell in [self cells]) {
169 [cell setHighlighted:(cell == highlightCell)];
173 - (void)highlightRowUnder:(NSEvent*)theEvent {
174 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
175 NSInteger row, column;
176 if ([self getRow:&row column:&column forPoint:point]) {
177 [self highlightRowAt:row];
179 [self highlightRowAt:-1];
183 // Select cell under |theEvent|, returning YES if a selection is made.
184 - (BOOL)selectCellForEvent:(NSEvent*)theEvent {
185 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
187 NSInteger row, column;
188 if ([self getRow:&row column:&column forPoint:point]) {
189 DCHECK_EQ(column, 0);
191 delegate_->OnMatrixRowSelected(self, row);