- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / omnibox / omnibox_popup_matrix.mm
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 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h"
6
7 #include "base/logging.h"
8 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h"
9
10 namespace {
11
12 // NSEvent -buttonNumber for middle mouse button.
13 const NSInteger kMiddleButtonNumber = 2;
14
15 }  // namespace
16
17 @interface OmniboxPopupMatrix()
18 - (void)resetTrackingArea;
19 - (void)highlightRowAt:(NSInteger)rowIndex;
20 - (void)highlightRowUnder:(NSEvent*)theEvent;
21 - (BOOL)selectCellForEvent:(NSEvent*)theEvent;
22 @end
23
24 @implementation OmniboxPopupMatrix
25
26 - (id)initWithDelegate:(OmniboxPopupMatrixDelegate*)delegate {
27   if ((self = [super initWithFrame:NSZeroRect])) {
28     delegate_ = delegate;
29     [self setCellClass:[OmniboxPopupCell class]];
30
31     // Cells pack with no spacing.
32     [self setIntercellSpacing:NSMakeSize(0.0, 0.0)];
33
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];
40
41     [self resetTrackingArea];
42   }
43   return self;
44 }
45
46 - (void)setDelegate:(OmniboxPopupMatrixDelegate*)delegate {
47   delegate_ = delegate;
48 }
49
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]) {
55       return i;
56     }
57   }
58   return -1;
59 }
60
61 - (void)updateTrackingAreas {
62   [self resetTrackingArea];
63   [super updateTrackingAreas];
64 }
65
66 // Callbacks from tracking area.
67 - (void)mouseEntered:(NSEvent*)theEvent {
68   [self highlightRowUnder:theEvent];
69 }
70
71 - (void)mouseMoved:(NSEvent*)theEvent {
72   [self highlightRowUnder:theEvent];
73 }
74
75 - (void)mouseExited:(NSEvent*)theEvent {
76   [self highlightRowAt:-1];
77 }
78
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];
84   }
85   [super otherMouseDown:theEvent];
86 }
87
88 - (void)otherMouseDragged:(NSEvent*)theEvent {
89   if ([theEvent buttonNumber] == kMiddleButtonNumber) {
90     [self highlightRowUnder:theEvent];
91   }
92   [super otherMouseDragged:theEvent];
93 }
94
95 - (void)otherMouseUp:(NSEvent*)theEvent {
96   // Only intercept middle button.
97   if ([theEvent buttonNumber] != kMiddleButtonNumber) {
98     [super otherMouseUp:theEvent];
99     return;
100   }
101
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];
105
106   const NSInteger highlightedRow = [self highlightedRow];
107   if (highlightedRow != -1) {
108     DCHECK(delegate_);
109     delegate_->OnMatrixRowMiddleClicked(self, highlightedRow);
110   }
111 }
112
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];
118
119   // Clear any existing highlight.
120   [self highlightRowAt:-1];
121
122   do {
123     if (![self selectCellForEvent:theEvent]) {
124       [self selectCell:selectedCell];
125     }
126
127     const NSUInteger mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
128     theEvent = [[self window] nextEventMatchingMask:mask];
129   } while ([theEvent type] == NSLeftMouseDragged);
130
131   // Do not message the delegate if released outside view.
132   if (![self selectCellForEvent:theEvent]) {
133     [self selectCell:selectedCell];
134   } else {
135     const NSInteger selectedRow = [self selectedRow];
136
137     // No row could be selected if the model failed to update.
138     if (selectedRow == -1) {
139       NOTREACHED();
140       return;
141     }
142
143     DCHECK(delegate_);
144     delegate_->OnMatrixRowClicked(self, selectedRow);
145   }
146 }
147
148 - (void)resetTrackingArea {
149   if (trackingArea_.get())
150     [self removeTrackingArea:trackingArea_.get()];
151
152   trackingArea_.reset([[CrTrackingArea alloc]
153       initWithRect:[self frame]
154            options:NSTrackingMouseEnteredAndExited |
155                    NSTrackingMouseMoved |
156                    NSTrackingActiveInActiveApp |
157                    NSTrackingInVisibleRect
158              owner:self
159           userInfo:nil]);
160   [self addTrackingArea:trackingArea_.get()];
161 }
162
163 - (void)highlightRowAt:(NSInteger)rowIndex {
164   // highlightCell will be nil if rowIndex is out of range, so no cell will be
165   // highlighted.
166   NSCell* highlightCell = [self cellAtRow:rowIndex column:0];
167
168   for (NSCell* cell in [self cells]) {
169     [cell setHighlighted:(cell == highlightCell)];
170   }
171 }
172
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];
178   } else {
179     [self highlightRowAt:-1];
180   }
181 }
182
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];
186
187   NSInteger row, column;
188   if ([self getRow:&row column:&column forPoint:point]) {
189     DCHECK_EQ(column, 0);
190     DCHECK(delegate_);
191     delegate_->OnMatrixRowSelected(self, row);
192     return YES;
193   }
194   return NO;
195 }
196
197 @end