| OLD | NEW | 
|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h" | 5 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h" | 
| 6 | 6 | 
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" | 
| 8 #include "app/resource_bundle.h" | 8 #include "app/resource_bundle.h" | 
| 9 #include "base/mac_util.h" | 9 #include "base/mac_util.h" | 
| 10 #include "base/sys_string_conversions.h" | 10 #include "base/sys_string_conversions.h" | 
| 11 #include "chrome/browser/download/download_item.h" | 11 #include "chrome/browser/download/download_item.h" | 
| 12 #include "chrome/browser/download/download_manager.h" | 12 #include "chrome/browser/download/download_manager.h" | 
| 13 #include "chrome/browser/profiles/profile.h" | 13 #include "chrome/browser/profiles/profile.h" | 
| 14 #include "chrome/browser/themes/browser_theme_provider.h" | 14 #include "chrome/browser/themes/browser_theme_provider.h" | 
| 15 #include "chrome/browser/ui/browser.h" | 15 #include "chrome/browser/ui/browser.h" | 
| 16 #import "chrome/browser/ui/cocoa/animatable_view.h" | 16 #import "chrome/browser/ui/cocoa/animatable_view.h" | 
| 17 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h" | 17 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h" | 
| 18 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 18 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 
| 19 #include "chrome/browser/ui/cocoa/download/download_item_controller.h" | 19 #include "chrome/browser/ui/cocoa/download/download_item_controller.h" | 
| 20 #include "chrome/browser/ui/cocoa/download/download_shelf_mac.h" | 20 #include "chrome/browser/ui/cocoa/download/download_shelf_mac.h" | 
| 21 #import "chrome/browser/ui/cocoa/download/download_shelf_view.h" | 21 #import "chrome/browser/ui/cocoa/download/download_shelf_view.h" | 
| 22 #import "chrome/browser/ui/cocoa/hyperlink_button_cell.h" | 22 #import "chrome/browser/ui/cocoa/hyperlink_button_cell.h" | 
| 23 #include "grit/generated_resources.h" | 23 #include "grit/generated_resources.h" | 
| 24 #include "grit/theme_resources.h" | 24 #include "grit/theme_resources.h" | 
| 25 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | 25 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | 
| 26 | 26 | 
|  | 27 // Download shelf autoclose behavior: | 
|  | 28 // | 
|  | 29 // The download shelf autocloses if all of this is true: | 
|  | 30 // 1) An item on the shelf has just been opened. | 
|  | 31 // 2) All remaining items on the shelf have been opened in the past. | 
|  | 32 // 3) The mouse leaves the shelf and remains off the shelf for 5 seconds. | 
|  | 33 // | 
|  | 34 // If the mouse re-enters the shelf within the 5 second grace period, the | 
|  | 35 // autoclose is canceled.  An autoclose can only be scheduled in response to a | 
|  | 36 // shelf item being opened or removed.  If an item is opened and then the | 
|  | 37 // resulting autoclose is canceled, subsequent mouse exited events will NOT | 
|  | 38 // trigger an autoclose. | 
|  | 39 // | 
|  | 40 // If the shelf is manually closed while a download is still in progress, that | 
|  | 41 // download is marked as "opened" for these purposes.  If the shelf is later | 
|  | 42 // reopened, these previously-in-progress download will not block autoclose, | 
|  | 43 // even if that download was never actually clicked on and opened. | 
|  | 44 | 
| 27 namespace { | 45 namespace { | 
| 28 | 46 | 
| 29 // Max number of download views we'll contain. Any time a view is added and | 47 // Max number of download views we'll contain. Any time a view is added and | 
| 30 // we already have this many download views, one is removed. | 48 // we already have this many download views, one is removed. | 
| 31 const size_t kMaxDownloadItemCount = 16; | 49 const size_t kMaxDownloadItemCount = 16; | 
| 32 | 50 | 
| 33 // Horizontal padding between two download items. | 51 // Horizontal padding between two download items. | 
| 34 const int kDownloadItemPadding = 0; | 52 const int kDownloadItemPadding = 0; | 
| 35 | 53 | 
| 36 // Duration for the open-new-leftmost-item animation, in seconds. | 54 // Duration for the open-new-leftmost-item animation, in seconds. | 
| 37 const NSTimeInterval kDownloadItemOpenDuration = 0.8; | 55 const NSTimeInterval kDownloadItemOpenDuration = 0.8; | 
| 38 | 56 | 
| 39 // Duration for download shelf closing animation, in seconds. | 57 // Duration for download shelf closing animation, in seconds. | 
| 40 const NSTimeInterval kDownloadShelfCloseDuration = 0.12; | 58 const NSTimeInterval kDownloadShelfCloseDuration = 0.12; | 
| 41 | 59 | 
|  | 60 // Amount of time between when the mouse is moved off the shelf and the shelf is | 
|  | 61 // autoclosed, in seconds. | 
|  | 62 const NSTimeInterval kAutoCloseDelaySeconds = 5; | 
|  | 63 | 
| 42 }  // namespace | 64 }  // namespace | 
| 43 | 65 | 
| 44 @interface DownloadShelfController(Private) | 66 @interface DownloadShelfController(Private) | 
| 45 - (void)showDownloadShelf:(BOOL)enable; | 67 - (void)showDownloadShelf:(BOOL)enable; | 
| 46 - (void)layoutItems:(BOOL)skipFirst; | 68 - (void)layoutItems:(BOOL)skipFirst; | 
| 47 - (void)closed; | 69 - (void)closed; | 
|  | 70 - (BOOL)canAutoClose; | 
| 48 | 71 | 
| 49 - (void)updateTheme; | 72 - (void)updateTheme; | 
| 50 - (void)themeDidChangeNotification:(NSNotification*)notification; | 73 - (void)themeDidChangeNotification:(NSNotification*)notification; | 
| 51 - (void)viewFrameDidChange:(NSNotification*)notification; | 74 - (void)viewFrameDidChange:(NSNotification*)notification; | 
|  | 75 | 
|  | 76 - (void)installTrackingArea; | 
|  | 77 - (void)cancelAutoCloseAndRemoveTrackingArea; | 
| 52 @end | 78 @end | 
| 53 | 79 | 
| 54 | 80 | 
| 55 @implementation DownloadShelfController | 81 @implementation DownloadShelfController | 
| 56 | 82 | 
| 57 - (id)initWithBrowser:(Browser*)browser | 83 - (id)initWithBrowser:(Browser*)browser | 
| 58        resizeDelegate:(id<ViewResizer>)resizeDelegate { | 84        resizeDelegate:(id<ViewResizer>)resizeDelegate { | 
| 59   if ((self = [super initWithNibName:@"DownloadShelf" | 85   if ((self = [super initWithNibName:@"DownloadShelf" | 
| 60                               bundle:mac_util::MainAppBundle()])) { | 86                               bundle:mac_util::MainAppBundle()])) { | 
| 61     resizeDelegate_ = resizeDelegate; | 87     resizeDelegate_ = resizeDelegate; | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 93                       object:[self view]]; | 119                       object:[self view]]; | 
| 94 | 120 | 
| 95   ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | 121   ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | 
| 96   NSImage* favicon = rb.GetNativeImageNamed(IDR_DOWNLOADS_FAVICON); | 122   NSImage* favicon = rb.GetNativeImageNamed(IDR_DOWNLOADS_FAVICON); | 
| 97   DCHECK(favicon); | 123   DCHECK(favicon); | 
| 98   [image_ setImage:favicon]; | 124   [image_ setImage:favicon]; | 
| 99 } | 125 } | 
| 100 | 126 | 
| 101 - (void)dealloc { | 127 - (void)dealloc { | 
| 102   [[NSNotificationCenter defaultCenter] removeObserver:self]; | 128   [[NSNotificationCenter defaultCenter] removeObserver:self]; | 
|  | 129   [self cancelAutoCloseAndRemoveTrackingArea]; | 
| 103 | 130 | 
| 104   // The controllers will unregister themselves as observers when they are | 131   // The controllers will unregister themselves as observers when they are | 
| 105   // deallocated. No need to do that here. | 132   // deallocated. No need to do that here. | 
| 106   [super dealloc]; | 133   [super dealloc]; | 
| 107 } | 134 } | 
| 108 | 135 | 
| 109 // Called after the current theme has changed. | 136 // Called after the current theme has changed. | 
| 110 - (void)themeDidChangeNotification:(NSNotification*)notification { | 137 - (void)themeDidChangeNotification:(NSNotification*)notification { | 
| 111   [self updateTheme]; | 138   [self updateTheme]; | 
| 112 } | 139 } | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 166 | 193 | 
| 167   [downloadItemControllers_ removeObject:download]; | 194   [downloadItemControllers_ removeObject:download]; | 
| 168 | 195 | 
| 169   [self layoutItems]; | 196   [self layoutItems]; | 
| 170 | 197 | 
| 171   // Check to see if we have any downloads remaining and if not, hide the shelf. | 198   // Check to see if we have any downloads remaining and if not, hide the shelf. | 
| 172   if (![downloadItemControllers_ count]) | 199   if (![downloadItemControllers_ count]) | 
| 173     [self showDownloadShelf:NO]; | 200     [self showDownloadShelf:NO]; | 
| 174 } | 201 } | 
| 175 | 202 | 
|  | 203 - (void)downloadWasOpened:(DownloadItemController*)item_controller { | 
|  | 204   // This should only be called on the main thead. | 
|  | 205   DCHECK([NSThread isMainThread]); | 
|  | 206 | 
|  | 207   if ([self canAutoClose]) | 
|  | 208     [self installTrackingArea]; | 
|  | 209 } | 
|  | 210 | 
| 176 // We need to explicitly release our download controllers here since they need | 211 // We need to explicitly release our download controllers here since they need | 
| 177 // to remove themselves as observers before the remaining shutdown happens. | 212 // to remove themselves as observers before the remaining shutdown happens. | 
| 178 - (void)exiting { | 213 - (void)exiting { | 
| 179   [[self animatableView] stopAnimation]; | 214   [[self animatableView] stopAnimation]; | 
|  | 215   [self cancelAutoCloseAndRemoveTrackingArea]; | 
| 180   downloadItemControllers_.reset(); | 216   downloadItemControllers_.reset(); | 
| 181 } | 217 } | 
| 182 | 218 | 
| 183 // Show or hide the bar based on the value of |enable|. Handles animating the | 219 // Show or hide the bar based on the value of |enable|. Handles animating the | 
| 184 // resize of the content view. | 220 // resize of the content view. | 
| 185 - (void)showDownloadShelf:(BOOL)enable { | 221 - (void)showDownloadShelf:(BOOL)enable { | 
| 186   if ([self isVisible] == enable) | 222   if ([self isVisible] == enable) | 
| 187     return; | 223     return; | 
| 188 | 224 | 
| 189   if ([[self view] window]) | 225   if ([[self view] window]) | 
| (...skipping 19 matching lines...) Expand all  Loading... | 
| 209 | 245 | 
| 210 - (BOOL)isVisible { | 246 - (BOOL)isVisible { | 
| 211   return barIsVisible_; | 247   return barIsVisible_; | 
| 212 } | 248 } | 
| 213 | 249 | 
| 214 - (void)show:(id)sender { | 250 - (void)show:(id)sender { | 
| 215   [self showDownloadShelf:YES]; | 251   [self showDownloadShelf:YES]; | 
| 216 } | 252 } | 
| 217 | 253 | 
| 218 - (void)hide:(id)sender { | 254 - (void)hide:(id)sender { | 
|  | 255   [self cancelAutoCloseAndRemoveTrackingArea]; | 
|  | 256 | 
| 219   // If |sender| isn't nil, then we're being closed from the UI by the user and | 257   // If |sender| isn't nil, then we're being closed from the UI by the user and | 
| 220   // we need to tell our shelf implementation to close. Otherwise, we're being | 258   // we need to tell our shelf implementation to close. Otherwise, we're being | 
| 221   // closed programmatically by our shelf implementation. | 259   // closed programmatically by our shelf implementation. | 
| 222   if (sender) | 260   if (sender) | 
| 223     bridge_->Close(); | 261     bridge_->Close(); | 
| 224   else | 262   else | 
| 225     [self showDownloadShelf:NO]; | 263     [self showDownloadShelf:NO]; | 
| 226 } | 264 } | 
| 227 | 265 | 
| 228 - (void)animationDidEnd:(NSAnimation*)animation { | 266 - (void)animationDidEnd:(NSAnimation*)animation { | 
| (...skipping 19 matching lines...) Expand all  Loading... | 
| 248     skipFirst = NO; | 286     skipFirst = NO; | 
| 249   } | 287   } | 
| 250 } | 288 } | 
| 251 | 289 | 
| 252 - (void)layoutItems { | 290 - (void)layoutItems { | 
| 253   [self layoutItems:NO]; | 291   [self layoutItems:NO]; | 
| 254 } | 292 } | 
| 255 | 293 | 
| 256 - (void)addDownloadItem:(BaseDownloadItemModel*)model { | 294 - (void)addDownloadItem:(BaseDownloadItemModel*)model { | 
| 257   DCHECK([NSThread isMainThread]); | 295   DCHECK([NSThread isMainThread]); | 
|  | 296   [self cancelAutoCloseAndRemoveTrackingArea]; | 
|  | 297 | 
| 258   // Insert new item at the left. | 298   // Insert new item at the left. | 
| 259   scoped_nsobject<DownloadItemController> controller( | 299   scoped_nsobject<DownloadItemController> controller( | 
| 260       [[DownloadItemController alloc] initWithModel:model shelf:self]); | 300       [[DownloadItemController alloc] initWithModel:model shelf:self]); | 
| 261 | 301 | 
| 262   // Adding at index 0 in NSMutableArrays is O(1). | 302   // Adding at index 0 in NSMutableArrays is O(1). | 
| 263   [downloadItemControllers_ insertObject:controller.get() atIndex:0]; | 303   [downloadItemControllers_ insertObject:controller.get() atIndex:0]; | 
| 264 | 304 | 
| 265   [itemContainerView_ addSubview:[controller view]]; | 305   [itemContainerView_ addSubview:[controller view]]; | 
| 266 | 306 | 
| 267   // The controller is in charge of removing itself as an observer in its | 307   // The controller is in charge of removing itself as an observer in its | 
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 305   // laying out the items, so that the longer animation duration we set up above | 345   // laying out the items, so that the longer animation duration we set up above | 
| 306   // is not overwritten. | 346   // is not overwritten. | 
| 307   [self layoutItems:YES]; | 347   [self layoutItems:YES]; | 
| 308 } | 348 } | 
| 309 | 349 | 
| 310 - (void)closed { | 350 - (void)closed { | 
| 311   NSUInteger i = 0; | 351   NSUInteger i = 0; | 
| 312   while (i < [downloadItemControllers_ count]) { | 352   while (i < [downloadItemControllers_ count]) { | 
| 313     DownloadItemController* itemController = | 353     DownloadItemController* itemController = | 
| 314         [downloadItemControllers_ objectAtIndex:i]; | 354         [downloadItemControllers_ objectAtIndex:i]; | 
|  | 355     DownloadItem* download = [itemController download]; | 
| 315     bool isTransferDone = | 356     bool isTransferDone = | 
| 316         [itemController download]->state() == DownloadItem::COMPLETE || | 357         download->state() == DownloadItem::COMPLETE || | 
| 317         [itemController download]->state() == DownloadItem::CANCELLED; | 358         download->state() == DownloadItem::CANCELLED; | 
| 318     if (isTransferDone && | 359     if (isTransferDone && | 
| 319         [itemController download]->safety_state() != DownloadItem::DANGEROUS) { | 360         download->safety_state() != DownloadItem::DANGEROUS) { | 
| 320       [self remove:itemController]; | 361       [self remove:itemController]; | 
| 321     } else { | 362     } else { | 
|  | 363       // Treat the item as opened when we close. This way if we get shown again | 
|  | 364       // the user need not open this item for the shelf to auto-close. | 
|  | 365       download->set_opened(true); | 
| 322       ++i; | 366       ++i; | 
| 323     } | 367     } | 
| 324   } | 368   } | 
| 325 } | 369 } | 
| 326 | 370 | 
|  | 371 - (void)mouseEntered:(NSEvent*)event { | 
|  | 372   // If the mouse re-enters the download shelf, cancel the auto-close.  Further | 
|  | 373   // mouse exits should not trigger autoclose, so also remove the tracking area. | 
|  | 374   [self cancelAutoCloseAndRemoveTrackingArea]; | 
|  | 375 } | 
|  | 376 | 
|  | 377 - (void)mouseExited:(NSEvent*)event { | 
|  | 378   // Cancel any previous hide requests, just to be safe. | 
|  | 379   [NSObject cancelPreviousPerformRequestsWithTarget:self | 
|  | 380                                            selector:@selector(hide:) | 
|  | 381                                              object:self]; | 
|  | 382 | 
|  | 383   // Schedule an autoclose after a delay.  If the mouse is moved back into the | 
|  | 384   // view, or if an item is added to the shelf, the timer will be canceled. | 
|  | 385   [self performSelector:@selector(hide:) | 
|  | 386              withObject:self | 
|  | 387              afterDelay:kAutoCloseDelaySeconds]; | 
|  | 388 } | 
|  | 389 | 
|  | 390 - (BOOL)canAutoClose { | 
|  | 391   for (NSUInteger i = 0; i < [downloadItemControllers_ count]; ++i) { | 
|  | 392     DownloadItemController* itemController = | 
|  | 393         [downloadItemControllers_ objectAtIndex:i]; | 
|  | 394     if (![itemController download]->opened()) | 
|  | 395       return NO; | 
|  | 396   } | 
|  | 397   return YES; | 
|  | 398 } | 
|  | 399 | 
|  | 400 - (void)installTrackingArea { | 
|  | 401   // Install the tracking area to listen for mouseExited messages and trigger | 
|  | 402   // the shelf autoclose. | 
|  | 403   if (trackingArea_.get()) | 
|  | 404     return; | 
|  | 405 | 
|  | 406   trackingArea_.reset([[NSTrackingArea alloc] | 
|  | 407                         initWithRect:[[self view] bounds] | 
|  | 408                              options:NSTrackingMouseEnteredAndExited | | 
|  | 409                                      NSTrackingActiveAlways | 
|  | 410                                owner:self | 
|  | 411                             userInfo:nil]); | 
|  | 412   [[self view] addTrackingArea:trackingArea_]; | 
|  | 413 } | 
|  | 414 | 
|  | 415 - (void)cancelAutoCloseAndRemoveTrackingArea { | 
|  | 416   [NSObject cancelPreviousPerformRequestsWithTarget:self | 
|  | 417                                            selector:@selector(hide:) | 
|  | 418                                              object:self]; | 
|  | 419 | 
|  | 420   if (trackingArea_.get()) { | 
|  | 421     [[self view] removeTrackingArea:trackingArea_]; | 
|  | 422     trackingArea_.reset(nil); | 
|  | 423   } | 
|  | 424 } | 
|  | 425 | 
| 327 @end | 426 @end | 
| OLD | NEW | 
|---|