blob: 6bddf634cb2917a5f8a54aee50520bed256d0356 [file] [log] [blame]
// Copyright 2013 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/ui/cocoa/omnibox/omnibox_popup_matrix.h"
#include "base/logging.h"
#import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h"
namespace {
// NSEvent -buttonNumber for middle mouse button.
const NSInteger kMiddleButtonNumber = 2;
} // namespace
@interface OmniboxPopupMatrix()
- (void)resetTrackingArea;
- (void)highlightRowAt:(NSInteger)rowIndex;
- (void)highlightRowUnder:(NSEvent*)theEvent;
- (BOOL)selectCellForEvent:(NSEvent*)theEvent;
@end
@implementation OmniboxPopupMatrix
- (id)initWithDelegate:(OmniboxPopupMatrixDelegate*)delegate {
if ((self = [super initWithFrame:NSZeroRect])) {
delegate_ = delegate;
[self setCellClass:[OmniboxPopupCell class]];
// Cells pack with no spacing.
[self setIntercellSpacing:NSMakeSize(0.0, 0.0)];
[self setDrawsBackground:YES];
[self setBackgroundColor:[NSColor controlBackgroundColor]];
[self renewRows:0 columns:1];
[self setAllowsEmptySelection:YES];
[self setMode:NSRadioModeMatrix];
[self deselectAllCells];
[self resetTrackingArea];
}
return self;
}
- (void)setDelegate:(OmniboxPopupMatrixDelegate*)delegate {
delegate_ = delegate;
}
- (NSInteger)highlightedRow {
NSArray* cells = [self cells];
const NSUInteger count = [cells count];
for(NSUInteger i = 0; i < count; ++i) {
if ([[cells objectAtIndex:i] isHighlighted]) {
return i;
}
}
return -1;
}
- (void)updateTrackingAreas {
[self resetTrackingArea];
[super updateTrackingAreas];
}
// Callbacks from tracking area.
- (void)mouseEntered:(NSEvent*)theEvent {
[self highlightRowUnder:theEvent];
}
- (void)mouseMoved:(NSEvent*)theEvent {
[self highlightRowUnder:theEvent];
}
- (void)mouseExited:(NSEvent*)theEvent {
[self highlightRowAt:-1];
}
// The tracking area events aren't forwarded during a drag, so handle
// highlighting manually for middle-click and middle-drag.
- (void)otherMouseDown:(NSEvent*)theEvent {
if ([theEvent buttonNumber] == kMiddleButtonNumber) {
[self highlightRowUnder:theEvent];
}
[super otherMouseDown:theEvent];
}
- (void)otherMouseDragged:(NSEvent*)theEvent {
if ([theEvent buttonNumber] == kMiddleButtonNumber) {
[self highlightRowUnder:theEvent];
}
[super otherMouseDragged:theEvent];
}
- (void)otherMouseUp:(NSEvent*)theEvent {
// Only intercept middle button.
if ([theEvent buttonNumber] != kMiddleButtonNumber) {
[super otherMouseUp:theEvent];
return;
}
// -otherMouseDragged: should always have been called at this location, but
// make sure the user is getting the right feedback.
[self highlightRowUnder:theEvent];
const NSInteger highlightedRow = [self highlightedRow];
if (highlightedRow != -1) {
DCHECK(delegate_);
delegate_->OnMatrixRowMiddleClicked(self, highlightedRow);
}
}
// Track the mouse until released, keeping the cell under the mouse selected.
// If the mouse wanders off-view, revert to the originally-selected cell. If
// the mouse is released over a cell, call the delegate to open the row's URL.
- (void)mouseDown:(NSEvent*)theEvent {
NSCell* selectedCell = [self selectedCell];
// Clear any existing highlight.
[self highlightRowAt:-1];
do {
if (![self selectCellForEvent:theEvent]) {
[self selectCell:selectedCell];
}
const NSUInteger mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
theEvent = [[self window] nextEventMatchingMask:mask];
} while ([theEvent type] == NSLeftMouseDragged);
// Do not message the delegate if released outside view.
if (![self selectCellForEvent:theEvent]) {
[self selectCell:selectedCell];
} else {
const NSInteger selectedRow = [self selectedRow];
DCHECK_GE(selectedRow, 0);
DCHECK(delegate_);
delegate_->OnMatrixRowClicked(self, selectedRow);
}
}
- (void)resetTrackingArea {
if (trackingArea_.get())
[self removeTrackingArea:trackingArea_.get()];
trackingArea_.reset([[CrTrackingArea alloc]
initWithRect:[self frame]
options:NSTrackingMouseEnteredAndExited |
NSTrackingMouseMoved |
NSTrackingActiveInActiveApp |
NSTrackingInVisibleRect
owner:self
userInfo:nil]);
[self addTrackingArea:trackingArea_.get()];
}
- (void)highlightRowAt:(NSInteger)rowIndex {
// highlightCell will be nil if rowIndex is out of range, so no cell will be
// highlighted.
NSCell* highlightCell = [self cellAtRow:rowIndex column:0];
for (NSCell* cell in [self cells]) {
[cell setHighlighted:(cell == highlightCell)];
}
}
- (void)highlightRowUnder:(NSEvent*)theEvent {
NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
NSInteger row, column;
if ([self getRow:&row column:&column forPoint:point]) {
[self highlightRowAt:row];
} else {
[self highlightRowAt:-1];
}
}
// Select cell under |theEvent|, returning YES if a selection is made.
- (BOOL)selectCellForEvent:(NSEvent*)theEvent {
NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
NSInteger row, column;
if ([self getRow:&row column:&column forPoint:point]) {
DCHECK_EQ(column, 0);
DCHECK(delegate_);
delegate_->OnMatrixRowSelected(self, row);
return YES;
}
return NO;
}
@end