| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "chrome/browser/ui/cocoa/collected_cookies_mac.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "app/l10n_util_mac.h" | |
| 10 #include "app/resource_bundle.h" | |
| 11 #import "base/mac/mac_util.h" | |
| 12 #include "base/sys_string_conversions.h" | |
| 13 #include "chrome/browser/profiles/profile.h" | |
| 14 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 15 #import "chrome/browser/ui/cocoa/vertical_gradient_view.h" | |
| 16 #include "chrome/common/notification_details.h" | |
| 17 #include "chrome/common/notification_source.h" | |
| 18 #include "grit/generated_resources.h" | |
| 19 #include "grit/theme_resources.h" | |
| 20 #include "skia/ext/skia_utils_mac.h" | |
| 21 #include "third_party/apple/ImageAndTextCell.h" | |
| 22 #include "third_party/skia/include/core/SkBitmap.h" | |
| 23 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | |
| 24 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" | |
| 25 | |
| 26 namespace { | |
| 27 // Colors for the infobar. | |
| 28 const double kBannerGradientColorTop[3] = | |
| 29 {255.0 / 255.0, 242.0 / 255.0, 183.0 / 255.0}; | |
| 30 const double kBannerGradientColorBottom[3] = | |
| 31 {250.0 / 255.0, 230.0 / 255.0, 145.0 / 255.0}; | |
| 32 const double kBannerStrokeColor = 135.0 / 255.0; | |
| 33 | |
| 34 // Minimal height for the collected cookies dialog. | |
| 35 const CGFloat kMinCollectedCookiesViewHeight = 116; | |
| 36 } // namespace | |
| 37 | |
| 38 #pragma mark Bridge between the constrained window delegate and the sheet | |
| 39 | |
| 40 // The delegate used to forward the events from the sheet to the constrained | |
| 41 // window delegate. | |
| 42 @interface CollectedCookiesSheetBridge : NSObject { | |
| 43 CollectedCookiesMac* collectedCookies_; // weak | |
| 44 } | |
| 45 - (id)initWithCollectedCookiesMac:(CollectedCookiesMac*)collectedCookies; | |
| 46 - (void)sheetDidEnd:(NSWindow*)sheet | |
| 47 returnCode:(int)returnCode | |
| 48 contextInfo:(void*)contextInfo; | |
| 49 @end | |
| 50 | |
| 51 @implementation CollectedCookiesSheetBridge | |
| 52 - (id)initWithCollectedCookiesMac:(CollectedCookiesMac*)collectedCookies { | |
| 53 if ((self = [super init])) { | |
| 54 collectedCookies_ = collectedCookies; | |
| 55 } | |
| 56 return self; | |
| 57 } | |
| 58 | |
| 59 - (void)sheetDidEnd:(NSWindow*)sheet | |
| 60 returnCode:(int)returnCode | |
| 61 contextInfo:(void*)contextInfo { | |
| 62 collectedCookies_->OnSheetDidEnd(sheet); | |
| 63 } | |
| 64 @end | |
| 65 | |
| 66 #pragma mark Constrained window delegate | |
| 67 | |
| 68 CollectedCookiesMac::CollectedCookiesMac(NSWindow* parent, | |
| 69 TabContents* tab_contents) | |
| 70 : ConstrainedWindowMacDelegateCustomSheet( | |
| 71 [[[CollectedCookiesSheetBridge alloc] | |
| 72 initWithCollectedCookiesMac:this] autorelease], | |
| 73 @selector(sheetDidEnd:returnCode:contextInfo:)), | |
| 74 tab_contents_(tab_contents) { | |
| 75 TabSpecificContentSettings* content_settings = | |
| 76 tab_contents->GetTabSpecificContentSettings(); | |
| 77 registrar_.Add(this, NotificationType::COLLECTED_COOKIES_SHOWN, | |
| 78 Source<TabSpecificContentSettings>(content_settings)); | |
| 79 | |
| 80 sheet_controller_ = [[CollectedCookiesWindowController alloc] | |
| 81 initWithTabContents:tab_contents]; | |
| 82 | |
| 83 set_sheet([sheet_controller_ window]); | |
| 84 | |
| 85 window_ = tab_contents->CreateConstrainedDialog(this); | |
| 86 } | |
| 87 | |
| 88 CollectedCookiesMac::~CollectedCookiesMac() { | |
| 89 NSWindow* window = [sheet_controller_ window]; | |
| 90 if (window_ && window && is_sheet_open()) { | |
| 91 window_ = NULL; | |
| 92 [NSApp endSheet:window]; | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 void CollectedCookiesMac::DeleteDelegate() { | |
| 97 delete this; | |
| 98 } | |
| 99 | |
| 100 void CollectedCookiesMac::Observe(NotificationType type, | |
| 101 const NotificationSource& source, | |
| 102 const NotificationDetails& details) { | |
| 103 DCHECK(type == NotificationType::COLLECTED_COOKIES_SHOWN); | |
| 104 DCHECK_EQ(Source<TabSpecificContentSettings>(source).ptr(), | |
| 105 tab_contents_->GetTabSpecificContentSettings()); | |
| 106 window_->CloseConstrainedWindow(); | |
| 107 } | |
| 108 | |
| 109 void CollectedCookiesMac::OnSheetDidEnd(NSWindow* sheet) { | |
| 110 [sheet orderOut:sheet_controller_]; | |
| 111 if (window_) | |
| 112 window_->CloseConstrainedWindow(); | |
| 113 } | |
| 114 | |
| 115 #pragma mark Window Controller | |
| 116 | |
| 117 @interface CollectedCookiesWindowController(Private) | |
| 118 -(void)showInfoBarForDomain:(const string16&)domain | |
| 119 setting:(ContentSetting)setting; | |
| 120 -(void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting; | |
| 121 -(void)animateInfoBar; | |
| 122 @end | |
| 123 | |
| 124 @implementation CollectedCookiesWindowController | |
| 125 | |
| 126 @synthesize allowedCookiesButtonsEnabled = | |
| 127 allowedCookiesButtonsEnabled_; | |
| 128 @synthesize blockedCookiesButtonsEnabled = | |
| 129 blockedCookiesButtonsEnabled_; | |
| 130 | |
| 131 @synthesize allowedTreeController = allowedTreeController_; | |
| 132 @synthesize blockedTreeController = blockedTreeController_; | |
| 133 | |
| 134 - (id)initWithTabContents:(TabContents*)tabContents { | |
| 135 DCHECK(tabContents); | |
| 136 infoBarVisible_ = NO; | |
| 137 tabContents_ = tabContents; | |
| 138 | |
| 139 NSString* nibpath = | |
| 140 [base::mac::MainAppBundle() pathForResource:@"CollectedCookies" | |
| 141 ofType:@"nib"]; | |
| 142 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { | |
| 143 [self loadTreeModelFromTabContents]; | |
| 144 | |
| 145 animation_.reset([[NSViewAnimation alloc] init]); | |
| 146 [animation_ setAnimationBlockingMode:NSAnimationNonblocking]; | |
| 147 } | |
| 148 return self; | |
| 149 } | |
| 150 | |
| 151 - (void)awakeFromNib { | |
| 152 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 153 NSImage* infoIcon = rb.GetNativeImageNamed(IDR_INFO); | |
| 154 DCHECK(infoIcon); | |
| 155 [infoBarIcon_ setImage:infoIcon]; | |
| 156 | |
| 157 // Initialize the banner gradient and stroke color. | |
| 158 NSColor* bannerStartingColor = | |
| 159 [NSColor colorWithCalibratedRed:kBannerGradientColorTop[0] | |
| 160 green:kBannerGradientColorTop[1] | |
| 161 blue:kBannerGradientColorTop[2] | |
| 162 alpha:1.0]; | |
| 163 NSColor* bannerEndingColor = | |
| 164 [NSColor colorWithCalibratedRed:kBannerGradientColorBottom[0] | |
| 165 green:kBannerGradientColorBottom[1] | |
| 166 blue:kBannerGradientColorBottom[2] | |
| 167 alpha:1.0]; | |
| 168 scoped_nsobject<NSGradient> bannerGradient( | |
| 169 [[NSGradient alloc] initWithStartingColor:bannerStartingColor | |
| 170 endingColor:bannerEndingColor]); | |
| 171 [infoBar_ setGradient:bannerGradient]; | |
| 172 | |
| 173 NSColor* bannerStrokeColor = | |
| 174 [NSColor colorWithCalibratedWhite:kBannerStrokeColor | |
| 175 alpha:1.0]; | |
| 176 [infoBar_ setStrokeColor:bannerStrokeColor]; | |
| 177 | |
| 178 // Change the label of the blocked cookies part if necessary. | |
| 179 if (tabContents_->profile()->GetHostContentSettingsMap()-> | |
| 180 BlockThirdPartyCookies()) { | |
| 181 [blockedCookiesText_ setStringValue:l10n_util::GetNSString( | |
| 182 IDS_COLLECTED_COOKIES_BLOCKED_THIRD_PARTY_BLOCKING_ENABLED)]; | |
| 183 CGFloat textDeltaY = [GTMUILocalizerAndLayoutTweaker | |
| 184 sizeToFitFixedWidthTextField:blockedCookiesText_]; | |
| 185 | |
| 186 // Shrink the upper custom view. | |
| 187 NSView* upperContentView = [[splitView_ subviews] objectAtIndex:0]; | |
| 188 NSRect frame = [upperContentView frame]; | |
| 189 [splitView_ setPosition:(frame.size.height - textDeltaY/2.0) | |
| 190 ofDividerAtIndex:0]; | |
| 191 | |
| 192 // Shrink the lower outline view. | |
| 193 frame = [lowerScrollView_ frame]; | |
| 194 frame.size.height -= textDeltaY; | |
| 195 [lowerScrollView_ setFrame:frame]; | |
| 196 | |
| 197 // Move the label down so it actually fits. | |
| 198 frame = [blockedCookiesText_ frame]; | |
| 199 frame.origin.y -= textDeltaY; | |
| 200 [blockedCookiesText_ setFrame:frame]; | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 - (void)windowWillClose:(NSNotification*)notif { | |
| 205 [allowedOutlineView_ setDelegate:nil]; | |
| 206 [blockedOutlineView_ setDelegate:nil]; | |
| 207 [animation_ stopAnimation]; | |
| 208 [self autorelease]; | |
| 209 } | |
| 210 | |
| 211 - (IBAction)closeSheet:(id)sender { | |
| 212 [NSApp endSheet:[self window]]; | |
| 213 } | |
| 214 | |
| 215 - (void)addException:(ContentSetting)setting | |
| 216 forTreeController:(NSTreeController*)controller { | |
| 217 NSArray* nodes = [controller selectedNodes]; | |
| 218 BOOL multipleDomainsChanged = NO; | |
| 219 string16 lastDomain; | |
| 220 for (NSTreeNode* treeNode in nodes) { | |
| 221 CocoaCookieTreeNode* node = [treeNode representedObject]; | |
| 222 CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]); | |
| 223 if (cookie->GetDetailedInfo().node_type != | |
| 224 CookieTreeNode::DetailedInfo::TYPE_ORIGIN) { | |
| 225 continue; | |
| 226 } | |
| 227 CookieTreeOriginNode* origin_node = | |
| 228 static_cast<CookieTreeOriginNode*>(cookie); | |
| 229 origin_node->CreateContentException( | |
| 230 tabContents_->profile()->GetHostContentSettingsMap(), | |
| 231 setting); | |
| 232 if (!lastDomain.empty()) | |
| 233 multipleDomainsChanged = YES; | |
| 234 lastDomain = origin_node->GetTitle(); | |
| 235 } | |
| 236 if (multipleDomainsChanged) | |
| 237 [self showInfoBarForMultipleDomainsAndSetting:setting]; | |
| 238 else | |
| 239 [self showInfoBarForDomain:lastDomain setting:setting]; | |
| 240 } | |
| 241 | |
| 242 - (IBAction)allowOrigin:(id)sender { | |
| 243 [self addException:CONTENT_SETTING_ALLOW | |
| 244 forTreeController:blockedTreeController_]; | |
| 245 } | |
| 246 | |
| 247 - (IBAction)allowForSessionFromOrigin:(id)sender { | |
| 248 [self addException:CONTENT_SETTING_SESSION_ONLY | |
| 249 forTreeController:blockedTreeController_]; | |
| 250 } | |
| 251 | |
| 252 - (IBAction)blockOrigin:(id)sender { | |
| 253 [self addException:CONTENT_SETTING_BLOCK | |
| 254 forTreeController:allowedTreeController_]; | |
| 255 } | |
| 256 | |
| 257 - (CGFloat) splitView:(NSSplitView *)sender | |
| 258 constrainMinCoordinate:(CGFloat)proposedMin | |
| 259 ofSubviewAt:(NSInteger)offset { | |
| 260 return proposedMin + kMinCollectedCookiesViewHeight; | |
| 261 } | |
| 262 - (CGFloat) splitView:(NSSplitView *)sender | |
| 263 constrainMaxCoordinate:(CGFloat)proposedMax | |
| 264 ofSubviewAt:(NSInteger)offset { | |
| 265 return proposedMax - kMinCollectedCookiesViewHeight; | |
| 266 } | |
| 267 - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview { | |
| 268 return YES; | |
| 269 } | |
| 270 | |
| 271 - (CocoaCookieTreeNode*)cocoaAllowedTreeModel { | |
| 272 return cocoaAllowedTreeModel_.get(); | |
| 273 } | |
| 274 - (void)setCocoaAllowedTreeModel:(CocoaCookieTreeNode*)model { | |
| 275 cocoaAllowedTreeModel_.reset([model retain]); | |
| 276 } | |
| 277 | |
| 278 - (CookiesTreeModel*)allowedTreeModel { | |
| 279 return allowedTreeModel_.get(); | |
| 280 } | |
| 281 | |
| 282 - (CocoaCookieTreeNode*)cocoaBlockedTreeModel { | |
| 283 return cocoaBlockedTreeModel_.get(); | |
| 284 } | |
| 285 - (void)setCocoaBlockedTreeModel:(CocoaCookieTreeNode*)model { | |
| 286 cocoaBlockedTreeModel_.reset([model retain]); | |
| 287 } | |
| 288 | |
| 289 - (CookiesTreeModel*)blockedTreeModel { | |
| 290 return blockedTreeModel_.get(); | |
| 291 } | |
| 292 | |
| 293 - (void)outlineView:(NSOutlineView*)outlineView | |
| 294 willDisplayCell:(id)cell | |
| 295 forTableColumn:(NSTableColumn*)tableColumn | |
| 296 item:(id)item { | |
| 297 CocoaCookieTreeNode* node = [item representedObject]; | |
| 298 int index; | |
| 299 if (outlineView == allowedOutlineView_) | |
| 300 index = allowedTreeModel_->GetIconIndex([node treeNode]); | |
| 301 else | |
| 302 index = blockedTreeModel_->GetIconIndex([node treeNode]); | |
| 303 NSImage* icon = nil; | |
| 304 if (index >= 0) | |
| 305 icon = [icons_ objectAtIndex:index]; | |
| 306 else | |
| 307 icon = [icons_ lastObject]; | |
| 308 DCHECK([cell isKindOfClass:[ImageAndTextCell class]]); | |
| 309 [static_cast<ImageAndTextCell*>(cell) setImage:icon]; | |
| 310 } | |
| 311 | |
| 312 - (void)outlineViewSelectionDidChange:(NSNotification*)notif { | |
| 313 BOOL isAllowedOutlineView; | |
| 314 if ([notif object] == allowedOutlineView_) { | |
| 315 isAllowedOutlineView = YES; | |
| 316 } else if ([notif object] == blockedOutlineView_) { | |
| 317 isAllowedOutlineView = NO; | |
| 318 } else { | |
| 319 NOTREACHED(); | |
| 320 return; | |
| 321 } | |
| 322 NSTreeController* controller = | |
| 323 isAllowedOutlineView ? allowedTreeController_ : blockedTreeController_; | |
| 324 | |
| 325 NSArray* nodes = [controller selectedNodes]; | |
| 326 for (NSTreeNode* treeNode in nodes) { | |
| 327 CocoaCookieTreeNode* node = [treeNode representedObject]; | |
| 328 CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]); | |
| 329 if (cookie->GetDetailedInfo().node_type != | |
| 330 CookieTreeNode::DetailedInfo::TYPE_ORIGIN) { | |
| 331 continue; | |
| 332 } | |
| 333 CookieTreeOriginNode* origin_node = | |
| 334 static_cast<CookieTreeOriginNode*>(cookie); | |
| 335 if (origin_node->CanCreateContentException()) { | |
| 336 if (isAllowedOutlineView) { | |
| 337 [self setAllowedCookiesButtonsEnabled:YES]; | |
| 338 } else { | |
| 339 [self setBlockedCookiesButtonsEnabled:YES]; | |
| 340 } | |
| 341 return; | |
| 342 } | |
| 343 } | |
| 344 if (isAllowedOutlineView) { | |
| 345 [self setAllowedCookiesButtonsEnabled:NO]; | |
| 346 } else { | |
| 347 [self setBlockedCookiesButtonsEnabled:NO]; | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 // Initializes the |allowedTreeModel_| and |blockedTreeModel_|, and builds | |
| 352 // the |cocoaAllowedTreeModel_| and |cocoaBlockedTreeModel_|. | |
| 353 - (void)loadTreeModelFromTabContents { | |
| 354 TabSpecificContentSettings* content_settings = | |
| 355 tabContents_->GetTabSpecificContentSettings(); | |
| 356 allowedTreeModel_.reset(content_settings->GetAllowedCookiesTreeModel()); | |
| 357 blockedTreeModel_.reset(content_settings->GetBlockedCookiesTreeModel()); | |
| 358 | |
| 359 // Convert the model's icons from Skia to Cocoa. | |
| 360 std::vector<SkBitmap> skiaIcons; | |
| 361 allowedTreeModel_->GetIcons(&skiaIcons); | |
| 362 icons_.reset([[NSMutableArray alloc] init]); | |
| 363 for (std::vector<SkBitmap>::iterator it = skiaIcons.begin(); | |
| 364 it != skiaIcons.end(); ++it) { | |
| 365 [icons_ addObject:gfx::SkBitmapToNSImage(*it)]; | |
| 366 } | |
| 367 | |
| 368 // Default icon will be the last item in the array. | |
| 369 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 370 // TODO(rsesek): Rename this resource now that it's in multiple places. | |
| 371 [icons_ addObject:rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER)]; | |
| 372 | |
| 373 // Create the Cocoa model. | |
| 374 CookieTreeNode* root = | |
| 375 static_cast<CookieTreeNode*>(allowedTreeModel_->GetRoot()); | |
| 376 scoped_nsobject<CocoaCookieTreeNode> model( | |
| 377 [[CocoaCookieTreeNode alloc] initWithNode:root]); | |
| 378 [self setCocoaAllowedTreeModel:model.get()]; // Takes ownership. | |
| 379 root = static_cast<CookieTreeNode*>(blockedTreeModel_->GetRoot()); | |
| 380 model.reset( | |
| 381 [[CocoaCookieTreeNode alloc] initWithNode:root]); | |
| 382 [self setCocoaBlockedTreeModel:model.get()]; // Takes ownership. | |
| 383 } | |
| 384 | |
| 385 -(void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting { | |
| 386 NSString* label; | |
| 387 switch (setting) { | |
| 388 case CONTENT_SETTING_BLOCK: | |
| 389 label = l10n_util::GetNSString( | |
| 390 IDS_COLLECTED_COOKIES_MULTIPLE_BLOCK_RULES_CREATED); | |
| 391 break; | |
| 392 | |
| 393 case CONTENT_SETTING_ALLOW: | |
| 394 label = l10n_util::GetNSString( | |
| 395 IDS_COLLECTED_COOKIES_MULTIPLE_ALLOW_RULES_CREATED); | |
| 396 break; | |
| 397 | |
| 398 case CONTENT_SETTING_SESSION_ONLY: | |
| 399 label = l10n_util::GetNSString( | |
| 400 IDS_COLLECTED_COOKIES_MULTIPLE_SESSION_RULES_CREATED); | |
| 401 break; | |
| 402 | |
| 403 default: | |
| 404 NOTREACHED(); | |
| 405 label = [[[NSString alloc] init] autorelease]; | |
| 406 } | |
| 407 [infoBarText_ setStringValue:label]; | |
| 408 [self animateInfoBar]; | |
| 409 } | |
| 410 | |
| 411 -(void)showInfoBarForDomain:(const string16&)domain | |
| 412 setting:(ContentSetting)setting { | |
| 413 NSString* label; | |
| 414 switch (setting) { | |
| 415 case CONTENT_SETTING_BLOCK: | |
| 416 label = l10n_util::GetNSStringF( | |
| 417 IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED, | |
| 418 domain); | |
| 419 break; | |
| 420 | |
| 421 case CONTENT_SETTING_ALLOW: | |
| 422 label = l10n_util::GetNSStringF( | |
| 423 IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED, | |
| 424 domain); | |
| 425 break; | |
| 426 | |
| 427 case CONTENT_SETTING_SESSION_ONLY: | |
| 428 label = l10n_util::GetNSStringF( | |
| 429 IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED, | |
| 430 domain); | |
| 431 break; | |
| 432 | |
| 433 default: | |
| 434 NOTREACHED(); | |
| 435 label = [[[NSString alloc] init] autorelease]; | |
| 436 } | |
| 437 [infoBarText_ setStringValue:label]; | |
| 438 [self animateInfoBar]; | |
| 439 } | |
| 440 | |
| 441 -(void)animateInfoBar { | |
| 442 if (infoBarVisible_) | |
| 443 return; | |
| 444 | |
| 445 infoBarVisible_ = YES; | |
| 446 | |
| 447 NSMutableArray* animations = [NSMutableArray arrayWithCapacity:3]; | |
| 448 | |
| 449 NSWindow* sheet = [self window]; | |
| 450 NSRect sheetFrame = [sheet frame]; | |
| 451 NSRect infoBarFrame = [infoBar_ frame]; | |
| 452 NSRect splitViewFrame = [splitView_ frame]; | |
| 453 | |
| 454 // Calculate the end position of the info bar and set it to its start | |
| 455 // position. | |
| 456 infoBarFrame.origin.y = NSHeight(sheetFrame); | |
| 457 infoBarFrame.size.width = NSWidth(sheetFrame); | |
| 458 NSRect infoBarStartFrame = infoBarFrame; | |
| 459 infoBarStartFrame.origin.y += NSHeight(infoBarFrame); | |
| 460 infoBarStartFrame.size.height = 0.0; | |
| 461 [infoBar_ setFrame:infoBarStartFrame]; | |
| 462 [[[self window] contentView] addSubview:infoBar_]; | |
| 463 | |
| 464 // Calculate the new position of the sheet. | |
| 465 sheetFrame.origin.y -= NSHeight(infoBarFrame); | |
| 466 sheetFrame.size.height += NSHeight(infoBarFrame); | |
| 467 | |
| 468 // Slide the infobar in. | |
| 469 [animations addObject: | |
| 470 [NSDictionary dictionaryWithObjectsAndKeys: | |
| 471 infoBar_, NSViewAnimationTargetKey, | |
| 472 [NSValue valueWithRect:infoBarFrame], | |
| 473 NSViewAnimationEndFrameKey, | |
| 474 [NSValue valueWithRect:infoBarStartFrame], | |
| 475 NSViewAnimationStartFrameKey, | |
| 476 nil]]; | |
| 477 // Make sure the split view ends up in the right position. | |
| 478 [animations addObject: | |
| 479 [NSDictionary dictionaryWithObjectsAndKeys: | |
| 480 splitView_, NSViewAnimationTargetKey, | |
| 481 [NSValue valueWithRect:splitViewFrame], | |
| 482 NSViewAnimationEndFrameKey, | |
| 483 nil]]; | |
| 484 | |
| 485 // Grow the sheet. | |
| 486 [animations addObject: | |
| 487 [NSDictionary dictionaryWithObjectsAndKeys: | |
| 488 sheet, NSViewAnimationTargetKey, | |
| 489 [NSValue valueWithRect:sheetFrame], | |
| 490 NSViewAnimationEndFrameKey, | |
| 491 nil]]; | |
| 492 [animation_ setViewAnimations:animations]; | |
| 493 // The default duration is 0.5s, which actually feels slow in here, so speed | |
| 494 // it up a bit. | |
| 495 [animation_ gtm_setDuration:0.2 | |
| 496 eventMask:NSLeftMouseUpMask]; | |
| 497 [animation_ startAnimation]; | |
| 498 } | |
| 499 | |
| 500 @end | |
| OLD | NEW |