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 #include "base/logging.h" |
| 6 #include "base/mac_util.h" |
| 7 #import "chrome/browser/cocoa/browser_window_controller.h" |
| 8 #include "chrome/browser/cocoa/infobar.h" |
| 9 #import "chrome/browser/cocoa/infobar_container_controller.h" |
| 10 #import "chrome/browser/cocoa/infobar_controller.h" |
| 11 #include "chrome/browser/cocoa/tab_strip_model_observer_bridge.h" |
| 12 #include "chrome/browser/tab_contents/tab_contents.h" |
| 13 #include "chrome/common/notification_service.h" |
| 14 #include "skia/ext/skia_utils_mac.h" |
| 15 |
| 16 // C++ class that receives INFOBAR_ADDED and INFOBAR_REMOVED |
| 17 // notifications and proxies them back to |controller|. |
| 18 class InfoBarNotificationObserver : public NotificationObserver { |
| 19 public: |
| 20 InfoBarNotificationObserver(InfoBarContainerController* controller) |
| 21 : controller_(controller) { |
| 22 } |
| 23 |
| 24 private: |
| 25 // NotificationObserver implementation |
| 26 void Observe(NotificationType type, |
| 27 const NotificationSource& source, |
| 28 const NotificationDetails& details) { |
| 29 switch (type.value) { |
| 30 case NotificationType::TAB_CONTENTS_INFOBAR_ADDED: |
| 31 [controller_ addInfoBar:Details<InfoBarDelegate>(details).ptr()]; |
| 32 break; |
| 33 case NotificationType::TAB_CONTENTS_INFOBAR_REMOVED: |
| 34 [controller_ |
| 35 removeInfoBarsForDelegate:Details<InfoBarDelegate>(details).ptr()]; |
| 36 break; |
| 37 default: |
| 38 NOTREACHED(); // we don't ask for anything else! |
| 39 break; |
| 40 } |
| 41 |
| 42 [controller_ positionInfoBarsAndRedraw]; |
| 43 } |
| 44 |
| 45 InfoBarContainerController* controller_; // weak, owns us. |
| 46 }; |
| 47 |
| 48 |
| 49 @interface InfoBarContainerController (PrivateMethods) |
| 50 // Returns the desired height of the container view, computed by |
| 51 // adding together the heights of all its subviews. |
| 52 - (float)desiredHeight; |
| 53 |
| 54 // Modifies this container to display infobars for the given |
| 55 // |contents|. Registers for INFOBAR_ADDED and INFOBAR_REMOVED |
| 56 // notifications for |contents|. If we are currently showing any |
| 57 // infobars, removes them first and deregisters for any |
| 58 // notifications. |contents| can be NULL, in which case no infobars |
| 59 // are shown and no notifications are registered for. |
| 60 - (void)changeTabContents:(TabContents*)contents; |
| 61 |
| 62 @end |
| 63 |
| 64 |
| 65 @implementation InfoBarContainerController |
| 66 - (id)initWithTabStripModel:(TabStripModel*)model |
| 67 browserWindowController:(BrowserWindowController*)controller { |
| 68 DCHECK(controller); |
| 69 if ((self = [super initWithNibName:@"InfoBarContainer" |
| 70 bundle:mac_util::MainAppBundle()])) { |
| 71 browserController_ = controller; |
| 72 tabObserver_.reset(new TabStripModelObserverBridge(model, self)); |
| 73 infoBarObserver_.reset(new InfoBarNotificationObserver(self)); |
| 74 |
| 75 // NSMutableArray needs an initial capacity, and we rarely ever see |
| 76 // more than two infobars at a time, so that seems like a good choice. |
| 77 infobarControllers_.reset([[NSMutableArray alloc] initWithCapacity:2]); |
| 78 } |
| 79 return self; |
| 80 } |
| 81 |
| 82 - (void)dealloc { |
| 83 DCHECK([infobarControllers_ count] == 0); |
| 84 [super dealloc]; |
| 85 } |
| 86 |
| 87 - (void)removeDelegate:(InfoBarDelegate*)delegate { |
| 88 DCHECK(currentTabContents_); |
| 89 currentTabContents_->RemoveInfoBar(delegate); |
| 90 } |
| 91 |
| 92 // TabStripModelObserverBridge notifications |
| 93 - (void)selectTabWithContents:(TabContents*)newContents |
| 94 previousContents:(TabContents*)oldContents |
| 95 atIndex:(NSInteger)index |
| 96 userGesture:(bool)wasUserGesture { |
| 97 [self changeTabContents:newContents]; |
| 98 } |
| 99 |
| 100 - (void)tabDetachedWithContents:(TabContents*)contents |
| 101 atIndex:(NSInteger)index { |
| 102 [self changeTabContents:NULL]; |
| 103 } |
| 104 |
| 105 @end |
| 106 |
| 107 @implementation InfoBarContainerController (PrivateMethods) |
| 108 |
| 109 - (float)desiredHeight { |
| 110 float height = 0; |
| 111 |
| 112 for (InfoBarController* controller in infobarControllers_.get()) { |
| 113 height += [[controller view] frame].size.height; |
| 114 } |
| 115 |
| 116 return height; |
| 117 } |
| 118 |
| 119 - (void)changeTabContents:(TabContents*)contents { |
| 120 registrar_.RemoveAll(); |
| 121 [self removeAllInfoBars]; |
| 122 |
| 123 currentTabContents_ = contents; |
| 124 if (currentTabContents_) { |
| 125 for (int i = 0; i < currentTabContents_->infobar_delegate_count(); ++i) { |
| 126 [self addInfoBar:currentTabContents_->GetInfoBarDelegateAt(i)]; |
| 127 } |
| 128 |
| 129 Source<TabContents> source(currentTabContents_); |
| 130 registrar_.Add(infoBarObserver_.get(), |
| 131 NotificationType::TAB_CONTENTS_INFOBAR_ADDED, source); |
| 132 registrar_.Add(infoBarObserver_.get(), |
| 133 NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, source); |
| 134 } |
| 135 |
| 136 [self positionInfoBarsAndRedraw]; |
| 137 } |
| 138 |
| 139 - (void)addInfoBar:(InfoBarDelegate*)delegate { |
| 140 scoped_ptr<InfoBar> infobar(delegate->CreateInfoBar()); |
| 141 InfoBarController* controller = infobar->controller(); |
| 142 [controller setContainerController:self]; |
| 143 [[self view] addSubview:[controller view]]; |
| 144 [infobarControllers_ addObject:[controller autorelease]]; |
| 145 } |
| 146 |
| 147 - (void)removeInfoBarsForDelegate:(InfoBarDelegate*)delegate { |
| 148 for (InfoBarController* controller in |
| 149 [NSArray arrayWithArray:infobarControllers_.get()]) { |
| 150 if ([controller delegate] == delegate) { |
| 151 // This code can be executed while -[InfoBarController closeInfoBar] is |
| 152 // still on the stack, so we retain and autorelease the controller to |
| 153 // prevent it from being dealloc'ed too early. |
| 154 [[controller retain] autorelease]; |
| 155 [[controller view] removeFromSuperview]; |
| 156 [infobarControllers_ removeObject:controller]; |
| 157 } |
| 158 } |
| 159 } |
| 160 |
| 161 - (void)removeAllInfoBars { |
| 162 for (InfoBarController* controller in infobarControllers_.get()) { |
| 163 [[controller view] removeFromSuperview]; |
| 164 } |
| 165 [infobarControllers_ removeAllObjects]; |
| 166 } |
| 167 |
| 168 - (void)positionInfoBarsAndRedraw { |
| 169 NSRect containerBounds = [[self view] bounds]; |
| 170 int minY = 0; |
| 171 |
| 172 // Stack the infobars at the bottom of the view, starting with the |
| 173 // last infobar and working our way to the front of the array. This |
| 174 // way we ensure that the first infobar added shows up on top, with |
| 175 // the others below. |
| 176 for (InfoBarController* controller in |
| 177 [infobarControllers_ reverseObjectEnumerator]) { |
| 178 NSView* view = [controller view]; |
| 179 NSRect frame = [view frame]; |
| 180 frame.origin.x = NSMinX(containerBounds); |
| 181 frame.size.width = NSWidth(containerBounds); |
| 182 frame.origin.y = minY; |
| 183 minY += frame.size.height; |
| 184 // TODO(rohitrao, jrg): Replace with an animator. |
| 185 [view setFrame:frame]; |
| 186 } |
| 187 |
| 188 [browserController_ infoBarResized:[self desiredHeight]]; |
| 189 } |
| 190 |
| 191 @end |
OLD | NEW |