| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 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 <Cocoa/Cocoa.h> | |
| 6 | |
| 7 #include "base/mac/mac_util.h" | |
| 8 #include "base/sys_string_conversions.h" | |
| 9 #include "chrome/browser/renderer_host/render_view_host.h" | |
| 10 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 11 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h" | |
| 12 #import "chrome/browser/ui/cocoa/find_bar_cocoa_controller.h" | |
| 13 #import "chrome/browser/ui/cocoa/find_bar_bridge.h" | |
| 14 #import "chrome/browser/ui/cocoa/find_bar_text_field.h" | |
| 15 #import "chrome/browser/ui/cocoa/find_bar_text_field_cell.h" | |
| 16 #import "chrome/browser/ui/cocoa/find_pasteboard.h" | |
| 17 #import "chrome/browser/ui/cocoa/focus_tracker.h" | |
| 18 #import "chrome/browser/ui/cocoa/tab_strip_controller.h" | |
| 19 #include "chrome/browser/ui/find_bar/find_bar_controller.h" | |
| 20 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | |
| 21 | |
| 22 namespace { | |
| 23 const float kFindBarOpenDuration = 0.2; | |
| 24 const float kFindBarCloseDuration = 0.15; | |
| 25 } | |
| 26 | |
| 27 @interface FindBarCocoaController (PrivateMethods) | |
| 28 // Returns the appropriate frame for a hidden find bar. | |
| 29 - (NSRect)hiddenFindBarFrame; | |
| 30 | |
| 31 // Sets the frame of |findBarView_|. |duration| is ignored if |animate| is NO. | |
| 32 - (void)setFindBarFrame:(NSRect)endFrame | |
| 33 animate:(BOOL)animate | |
| 34 duration:(float)duration; | |
| 35 | |
| 36 // Optionally stops the current search, puts |text| into the find bar, and | |
| 37 // enables the buttons, but doesn't start a new search for |text|. | |
| 38 - (void)prepopulateText:(NSString*)text stopSearch:(BOOL)stopSearch; | |
| 39 @end | |
| 40 | |
| 41 @implementation FindBarCocoaController | |
| 42 | |
| 43 - (id)init { | |
| 44 if ((self = [super initWithNibName:@"FindBar" | |
| 45 bundle:base::mac::MainAppBundle()])) { | |
| 46 [[NSNotificationCenter defaultCenter] | |
| 47 addObserver:self | |
| 48 selector:@selector(findPboardUpdated:) | |
| 49 name:kFindPasteboardChangedNotification | |
| 50 object:[FindPasteboard sharedInstance]]; | |
| 51 } | |
| 52 return self; | |
| 53 } | |
| 54 | |
| 55 - (void)dealloc { | |
| 56 // All animations should be explicitly stopped by the TabContents before a tab | |
| 57 // is closed. | |
| 58 DCHECK(!currentAnimation_.get()); | |
| 59 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 60 [super dealloc]; | |
| 61 } | |
| 62 | |
| 63 - (void)setFindBarBridge:(FindBarBridge*)findBarBridge { | |
| 64 DCHECK(!findBarBridge_); // should only be called once. | |
| 65 findBarBridge_ = findBarBridge; | |
| 66 } | |
| 67 | |
| 68 - (void)awakeFromNib { | |
| 69 [findBarView_ setFrame:[self hiddenFindBarFrame]]; | |
| 70 | |
| 71 // Stopping the search requires a findbar controller, which isn't valid yet | |
| 72 // during setup. Furthermore, there is no active search yet anyway. | |
| 73 [self prepopulateText:[[FindPasteboard sharedInstance] findText] | |
| 74 stopSearch:NO]; | |
| 75 } | |
| 76 | |
| 77 - (IBAction)close:(id)sender { | |
| 78 if (findBarBridge_) | |
| 79 findBarBridge_->GetFindBarController()->EndFindSession( | |
| 80 FindBarController::kKeepSelection); | |
| 81 } | |
| 82 | |
| 83 - (IBAction)previousResult:(id)sender { | |
| 84 if (findBarBridge_) | |
| 85 findBarBridge_->GetFindBarController()->tab_contents()->StartFinding( | |
| 86 base::SysNSStringToUTF16([findText_ stringValue]), | |
| 87 false, false); | |
| 88 } | |
| 89 | |
| 90 - (IBAction)nextResult:(id)sender { | |
| 91 if (findBarBridge_) | |
| 92 findBarBridge_->GetFindBarController()->tab_contents()->StartFinding( | |
| 93 base::SysNSStringToUTF16([findText_ stringValue]), | |
| 94 true, false); | |
| 95 } | |
| 96 | |
| 97 - (void)findPboardUpdated:(NSNotification*)notification { | |
| 98 if (suppressPboardUpdateActions_) | |
| 99 return; | |
| 100 [self prepopulateText:[[FindPasteboard sharedInstance] findText] | |
| 101 stopSearch:YES]; | |
| 102 } | |
| 103 | |
| 104 - (void)positionFindBarViewAtMaxY:(CGFloat)maxY maxWidth:(CGFloat)maxWidth { | |
| 105 static const CGFloat kRightEdgeOffset = 25; | |
| 106 NSView* containerView = [self view]; | |
| 107 CGFloat containerHeight = NSHeight([containerView frame]); | |
| 108 CGFloat containerWidth = NSWidth([containerView frame]); | |
| 109 | |
| 110 // Adjust where we'll actually place the find bar. | |
| 111 CGFloat maxX = maxWidth - kRightEdgeOffset; | |
| 112 DLOG_IF(WARNING, maxX < 0) << "Window too narrow for find bar"; | |
| 113 maxY += 1; | |
| 114 | |
| 115 NSRect newFrame = NSMakeRect(maxX - containerWidth, maxY - containerHeight, | |
| 116 containerWidth, containerHeight); | |
| 117 [containerView setFrame:newFrame]; | |
| 118 } | |
| 119 | |
| 120 // NSControl delegate method. | |
| 121 - (void)controlTextDidChange:(NSNotification *)aNotification { | |
| 122 if (!findBarBridge_) | |
| 123 return; | |
| 124 | |
| 125 TabContents* tab_contents = | |
| 126 findBarBridge_->GetFindBarController()->tab_contents(); | |
| 127 if (!tab_contents) | |
| 128 return; | |
| 129 | |
| 130 NSString* findText = [findText_ stringValue]; | |
| 131 suppressPboardUpdateActions_ = YES; | |
| 132 [[FindPasteboard sharedInstance] setFindText:findText]; | |
| 133 suppressPboardUpdateActions_ = NO; | |
| 134 | |
| 135 if ([findText length] > 0) { | |
| 136 tab_contents->StartFinding(base::SysNSStringToUTF16(findText), true, false); | |
| 137 } else { | |
| 138 // The textbox is empty so we reset. | |
| 139 tab_contents->StopFinding(FindBarController::kClearSelection); | |
| 140 [self updateUIForFindResult:tab_contents->find_result() | |
| 141 withText:string16()]; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 // NSControl delegate method | |
| 146 - (BOOL)control:(NSControl*)control | |
| 147 textView:(NSTextView*)textView | |
| 148 doCommandBySelector:(SEL)command { | |
| 149 if (command == @selector(insertNewline:)) { | |
| 150 // Pressing Return | |
| 151 NSEvent* event = [NSApp currentEvent]; | |
| 152 | |
| 153 if ([event modifierFlags] & NSShiftKeyMask) | |
| 154 [previousButton_ performClick:nil]; | |
| 155 else | |
| 156 [nextButton_ performClick:nil]; | |
| 157 | |
| 158 return YES; | |
| 159 } else if (command == @selector(insertLineBreak:)) { | |
| 160 // Pressing Ctrl-Return | |
| 161 if (findBarBridge_) { | |
| 162 findBarBridge_->GetFindBarController()->EndFindSession( | |
| 163 FindBarController::kActivateSelection); | |
| 164 } | |
| 165 return YES; | |
| 166 } else if (command == @selector(pageUp:) || | |
| 167 command == @selector(pageUpAndModifySelection:) || | |
| 168 command == @selector(scrollPageUp:) || | |
| 169 command == @selector(pageDown:) || | |
| 170 command == @selector(pageDownAndModifySelection:) || | |
| 171 command == @selector(scrollPageDown:) || | |
| 172 command == @selector(scrollToBeginningOfDocument:) || | |
| 173 command == @selector(scrollToEndOfDocument:) || | |
| 174 command == @selector(moveUp:) || | |
| 175 command == @selector(moveDown:)) { | |
| 176 TabContents* contents = | |
| 177 findBarBridge_->GetFindBarController()->tab_contents(); | |
| 178 if (!contents) | |
| 179 return NO; | |
| 180 | |
| 181 // Sanity-check to make sure we got a keyboard event. | |
| 182 NSEvent* event = [NSApp currentEvent]; | |
| 183 if ([event type] != NSKeyDown && [event type] != NSKeyUp) | |
| 184 return NO; | |
| 185 | |
| 186 // Forward the event to the renderer. | |
| 187 // TODO(rohitrao): Should this call -[BaseView keyEvent:]? Is there code in | |
| 188 // that function that we want to keep or avoid? Calling | |
| 189 // |ForwardKeyboardEvent()| directly ignores edit commands, which breaks | |
| 190 // cmd-up/down if we ever decide to include |moveToBeginningOfDocument:| in | |
| 191 // the list above. | |
| 192 RenderViewHost* render_view_host = contents->render_view_host(); | |
| 193 render_view_host->ForwardKeyboardEvent(NativeWebKeyboardEvent(event)); | |
| 194 return YES; | |
| 195 } | |
| 196 | |
| 197 return NO; | |
| 198 } | |
| 199 | |
| 200 // Methods from FindBar | |
| 201 - (void)showFindBar:(BOOL)animate { | |
| 202 // Save the currently-focused view. |findBarView_| is in the view | |
| 203 // hierarchy by now. showFindBar can be called even when the | |
| 204 // findbar is already open, so do not overwrite an already saved | |
| 205 // view. | |
| 206 if (!focusTracker_.get()) | |
| 207 focusTracker_.reset( | |
| 208 [[FocusTracker alloc] initWithWindow:[findBarView_ window]]); | |
| 209 | |
| 210 // Animate the view into place. | |
| 211 NSRect frame = [findBarView_ frame]; | |
| 212 frame.origin = NSMakePoint(0, 0); | |
| 213 [self setFindBarFrame:frame animate:animate duration:kFindBarOpenDuration]; | |
| 214 } | |
| 215 | |
| 216 - (void)hideFindBar:(BOOL)animate { | |
| 217 NSRect frame = [self hiddenFindBarFrame]; | |
| 218 [self setFindBarFrame:frame animate:animate duration:kFindBarCloseDuration]; | |
| 219 } | |
| 220 | |
| 221 - (void)stopAnimation { | |
| 222 if (currentAnimation_.get()) { | |
| 223 [currentAnimation_ stopAnimation]; | |
| 224 currentAnimation_.reset(nil); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 - (void)setFocusAndSelection { | |
| 229 [[findText_ window] makeFirstResponder:findText_]; | |
| 230 | |
| 231 // Enable the buttons if the find text is non-empty. | |
| 232 BOOL buttonsEnabled = ([[findText_ stringValue] length] > 0) ? YES : NO; | |
| 233 [previousButton_ setEnabled:buttonsEnabled]; | |
| 234 [nextButton_ setEnabled:buttonsEnabled]; | |
| 235 } | |
| 236 | |
| 237 - (void)restoreSavedFocus { | |
| 238 if (!(focusTracker_.get() && | |
| 239 [focusTracker_ restoreFocusInWindow:[findBarView_ window]])) { | |
| 240 // Fall back to giving focus to the tab contents. | |
| 241 findBarBridge_->GetFindBarController()->tab_contents()->Focus(); | |
| 242 } | |
| 243 focusTracker_.reset(nil); | |
| 244 } | |
| 245 | |
| 246 - (void)setFindText:(NSString*)findText { | |
| 247 [findText_ setStringValue:findText]; | |
| 248 | |
| 249 // Make sure the text in the find bar always ends up in the find pasteboard | |
| 250 // (and, via notifications, in the other find bars too). | |
| 251 [[FindPasteboard sharedInstance] setFindText:findText]; | |
| 252 } | |
| 253 | |
| 254 - (void)clearResults:(const FindNotificationDetails&)results { | |
| 255 // Just call updateUIForFindResult, which will take care of clearing | |
| 256 // the search text and the results label. | |
| 257 [self updateUIForFindResult:results withText:string16()]; | |
| 258 } | |
| 259 | |
| 260 - (void)updateUIForFindResult:(const FindNotificationDetails&)result | |
| 261 withText:(const string16&)findText { | |
| 262 // If we don't have any results and something was passed in, then | |
| 263 // that means someone pressed Cmd-G while the Find box was | |
| 264 // closed. In that case we need to repopulate the Find box with what | |
| 265 // was passed in. | |
| 266 if ([[findText_ stringValue] length] == 0 && !findText.empty()) { | |
| 267 [findText_ setStringValue:base::SysUTF16ToNSString(findText)]; | |
| 268 [findText_ selectText:self]; | |
| 269 } | |
| 270 | |
| 271 // Make sure Find Next and Find Previous are enabled if we found any matches. | |
| 272 BOOL buttonsEnabled = result.number_of_matches() > 0 ? YES : NO; | |
| 273 [previousButton_ setEnabled:buttonsEnabled]; | |
| 274 [nextButton_ setEnabled:buttonsEnabled]; | |
| 275 | |
| 276 // Update the results label. | |
| 277 BOOL validRange = result.active_match_ordinal() != -1 && | |
| 278 result.number_of_matches() != -1; | |
| 279 NSString* searchString = [findText_ stringValue]; | |
| 280 if ([searchString length] > 0 && validRange) { | |
| 281 [[findText_ findBarTextFieldCell] | |
| 282 setActiveMatch:result.active_match_ordinal() | |
| 283 of:result.number_of_matches()]; | |
| 284 } else { | |
| 285 // If there was no text entered, we don't show anything in the results area. | |
| 286 [[findText_ findBarTextFieldCell] clearResults]; | |
| 287 } | |
| 288 | |
| 289 [findText_ resetFieldEditorFrameIfNeeded]; | |
| 290 | |
| 291 // If we found any results, reset the focus tracker, so we always | |
| 292 // restore focus to the tab contents. | |
| 293 if (result.number_of_matches() > 0) | |
| 294 focusTracker_.reset(nil); | |
| 295 } | |
| 296 | |
| 297 - (BOOL)isFindBarVisible { | |
| 298 // Find bar is visible if any part of it is on the screen. | |
| 299 return NSIntersectsRect([[self view] bounds], [findBarView_ frame]); | |
| 300 } | |
| 301 | |
| 302 - (BOOL)isFindBarAnimating { | |
| 303 return (currentAnimation_.get() != nil); | |
| 304 } | |
| 305 | |
| 306 // NSAnimation delegate methods. | |
| 307 - (void)animationDidEnd:(NSAnimation*)animation { | |
| 308 // Autorelease the animation (cannot use release because the animation object | |
| 309 // is still on the stack. | |
| 310 DCHECK(animation == currentAnimation_.get()); | |
| 311 [currentAnimation_.release() autorelease]; | |
| 312 | |
| 313 // If the find bar is not visible, make it actually hidden, so it'll no longer | |
| 314 // respond to key events. | |
| 315 [findBarView_ setHidden:![self isFindBarVisible]]; | |
| 316 } | |
| 317 | |
| 318 @end | |
| 319 | |
| 320 @implementation FindBarCocoaController (PrivateMethods) | |
| 321 | |
| 322 - (NSRect)hiddenFindBarFrame { | |
| 323 NSRect frame = [findBarView_ frame]; | |
| 324 NSRect containerBounds = [[self view] bounds]; | |
| 325 frame.origin = NSMakePoint(NSMinX(containerBounds), NSMaxY(containerBounds)); | |
| 326 return frame; | |
| 327 } | |
| 328 | |
| 329 - (void)setFindBarFrame:(NSRect)endFrame | |
| 330 animate:(BOOL)animate | |
| 331 duration:(float)duration { | |
| 332 // Save the current frame. | |
| 333 NSRect startFrame = [findBarView_ frame]; | |
| 334 | |
| 335 // Stop any existing animations. | |
| 336 [currentAnimation_ stopAnimation]; | |
| 337 | |
| 338 if (!animate) { | |
| 339 [findBarView_ setFrame:endFrame]; | |
| 340 [findBarView_ setHidden:![self isFindBarVisible]]; | |
| 341 currentAnimation_.reset(nil); | |
| 342 return; | |
| 343 } | |
| 344 | |
| 345 // If animating, ensure that the find bar is not hidden. Hidden status will be | |
| 346 // updated at the end of the animation. | |
| 347 [findBarView_ setHidden:NO]; | |
| 348 | |
| 349 // Reset the frame to what was saved above. | |
| 350 [findBarView_ setFrame:startFrame]; | |
| 351 NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: | |
| 352 findBarView_, NSViewAnimationTargetKey, | |
| 353 [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey, nil]; | |
| 354 | |
| 355 currentAnimation_.reset( | |
| 356 [[NSViewAnimation alloc] | |
| 357 initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]]); | |
| 358 [currentAnimation_ gtm_setDuration:duration | |
| 359 eventMask:NSLeftMouseUpMask]; | |
| 360 [currentAnimation_ setDelegate:self]; | |
| 361 [currentAnimation_ startAnimation]; | |
| 362 } | |
| 363 | |
| 364 - (void)prepopulateText:(NSString*)text stopSearch:(BOOL)stopSearch{ | |
| 365 [self setFindText:text]; | |
| 366 | |
| 367 // End the find session, hide the "x of y" text and disable the | |
| 368 // buttons, but do not close the find bar or raise the window here. | |
| 369 if (stopSearch && findBarBridge_) { | |
| 370 TabContents* contents = | |
| 371 findBarBridge_->GetFindBarController()->tab_contents(); | |
| 372 if (contents) { | |
| 373 contents->StopFinding(FindBarController::kClearSelection); | |
| 374 findBarBridge_->ClearResults(contents->find_result()); | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 // Has to happen after |ClearResults()| above. | |
| 379 BOOL buttonsEnabled = [text length] > 0 ? YES : NO; | |
| 380 [previousButton_ setEnabled:buttonsEnabled]; | |
| 381 [nextButton_ setEnabled:buttonsEnabled]; | |
| 382 } | |
| 383 | |
| 384 @end | |
| OLD | NEW |