- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / infobars / infobar_container_controller.mm
1 // Copyright (c) 2011 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/infobars/infobar_container_controller.h"
6
7 #include "base/logging.h"
8 #include "base/mac/mac_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
11 #include "chrome/browser/infobars/infobar.h"
12 #include "chrome/browser/infobars/infobar_container.h"
13 #include "chrome/browser/infobars/infobar_service.h"
14 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
15 #import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
16 #import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
17 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
18 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
19 #import "chrome/browser/ui/cocoa/view_id_util.h"
20
21 @interface InfoBarContainerController ()
22 // Removes |controller| from the list of controllers in this container and
23 // removes its view from the view hierarchy.  This method is safe to call while
24 // |controller| is still on the call stack.
25 - (void)removeController:(InfoBarController*)controller;
26 @end
27
28
29 @implementation InfoBarContainerController
30
31 @synthesize shouldSuppressTopInfoBarTip = shouldSuppressTopInfoBarTip_;
32
33 - (id)initWithResizeDelegate:(id<ViewResizer>)resizeDelegate {
34   DCHECK(resizeDelegate);
35   if ((self = [super initWithNibName:nil bundle:nil])) {
36     base::scoped_nsobject<NSView> view(
37         [[NSView alloc] initWithFrame:NSZeroRect]);
38     [view setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
39     view_id_util::SetID(view, VIEW_ID_INFO_BAR_CONTAINER);
40     [self setView:view];
41
42     resizeDelegate_ = resizeDelegate;
43     containerCocoa_.reset(new InfoBarContainerCocoa(self));
44     infobarControllers_.reset([[NSMutableArray alloc] init]);
45   }
46   return self;
47 }
48
49 - (void)dealloc {
50   // Delete the container so that any remaining infobars are removed.
51   containerCocoa_.reset();
52   DCHECK_EQ([infobarControllers_ count], 0U);
53   view_id_util::UnsetID([self view]);
54   [super dealloc];
55 }
56
57 - (BrowserWindowController*)browserWindowController {
58   id controller = [[[self view] window] windowController];
59   if (![controller isKindOfClass:[BrowserWindowController class]])
60     return nil;
61   return controller;
62 }
63
64 - (CGFloat)infobarArrowX {
65   LocationBarViewMac* locationBar =
66       [[self browserWindowController] locationBarBridge];
67   return locationBar->GetPageInfoBubblePoint().x;
68 }
69
70 - (void)changeWebContents:(content::WebContents*)contents {
71   if (contents) {
72     containerCocoa_->ChangeInfoBarService(
73         InfoBarService::FromWebContents(contents));
74   } else {
75     containerCocoa_->ChangeInfoBarService(NULL);
76   }
77 }
78
79 - (void)tabDetachedWithContents:(content::WebContents*)contents {
80   if (currentWebContents_ == contents)
81     [self changeWebContents:NULL];
82 }
83
84 - (CGFloat)overlappingTipHeight {
85   return containerCocoa_->GetVerticalOverlap(NULL);
86 }
87
88 - (void)addInfoBar:(InfoBarCocoa*)infobar
89           position:(NSUInteger)position {
90   InfoBarController* controller = infobar->controller();
91   [controller setContainerController:self];
92   [infobarControllers_ insertObject:controller atIndex:position];
93
94   NSView* relativeView = nil;
95   if (position > 0)
96     relativeView = [[infobarControllers_ objectAtIndex:position - 1] view];
97   [[self view] addSubview:[controller view]
98                positioned:NSWindowAbove
99                relativeTo:relativeView];
100 }
101
102 - (void)removeInfoBar:(InfoBarCocoa*)infobar {
103   InfoBarController* controller = infobar->controller();
104   [controller infobarWillClose];
105   infobar->set_controller(nil);
106   [self removeController:controller];
107   base::MessageLoop::current()->DeleteSoon(FROM_HERE, infobar);
108 }
109
110 - (void)positionInfoBarsAndRedraw:(BOOL)isAnimating {
111   if (isAnimating_ != isAnimating) {
112     isAnimating_ = isAnimating;
113     if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)])
114       [resizeDelegate_ setAnimationInProgress:isAnimating_];
115   }
116
117   NSRect containerBounds = [[self view] bounds];
118   int minY = 0;
119
120   // Stack the infobars at the bottom of the view, starting with the
121   // last infobar and working our way to the front of the array.  This
122   // way we ensure that the first infobar added shows up on top, with
123   // the others below.
124   for (InfoBarController* controller in
125            [infobarControllers_ reverseObjectEnumerator]) {
126     NSRect frame;
127     frame.origin.x = NSMinX(containerBounds);
128     frame.origin.y = minY;
129     frame.size.width = NSWidth(containerBounds);
130     frame.size.height = [controller infobar]->total_height();
131     [[controller view] setFrame:frame];
132
133     minY += NSHeight(frame) - [controller infobar]->arrow_height();
134     [controller layoutArrow];
135   }
136
137   int totalHeight = 0;
138   int overlap = containerCocoa_->GetVerticalOverlap(&totalHeight);
139
140   if (NSHeight([[self view] frame]) != totalHeight) {
141     [resizeDelegate_ resizeView:[self view] newHeight:totalHeight];
142   } else if (oldOverlappingTipHeight_ != overlap) {
143     // If the infobar overlap changed but the height didn't change then
144     // explicitly ask for a layout.
145     [[self browserWindowController] layoutInfoBars];
146   }
147   oldOverlappingTipHeight_ = overlap;
148 }
149
150 - (void)setShouldSuppressTopInfoBarTip:(BOOL)flag {
151   if (shouldSuppressTopInfoBarTip_ == flag)
152     return;
153   shouldSuppressTopInfoBarTip_ = flag;
154   [self positionInfoBarsAndRedraw:isAnimating_];
155 }
156
157 - (void)removeController:(InfoBarController*)controller {
158   if (![infobarControllers_ containsObject:controller])
159     return;
160
161   // This code can be executed while InfoBarController is still on the stack, so
162   // we retain and autorelease the controller to prevent it from being
163   // dealloc'ed too early.
164   [[controller retain] autorelease];
165   [[controller view] removeFromSuperview];
166   [infobarControllers_ removeObject:controller];
167 }
168
169 @end