notification_panel.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Draws the view for the balloons.
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/notifications/notification_panel.h"
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/l10n_util.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/resource_bundle.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/notifications/balloon_collection_impl.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/notifications/balloon_view.h"
133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "cros/chromeos_wm_ipc_enums.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "gfx/canvas.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/background.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/native/native_view_host.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/scroll_view.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/root_view.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget_gtk.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#define SET_STATE(state) SetState(state, __PRETTY_FUNCTION__)
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Minimum and maximum size of balloon content.
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kBalloonMinWidth = 300;
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kBalloonMaxWidth = 300;
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kBalloonMinHeight = 24;
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kBalloonMaxHeight = 120;
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Maximum height of the notification panel.
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TODO(oshima): Get this from system's metrics.
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMaxPanelHeight = 400;
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The duration for a new notification to become stale.
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kStaleTimeoutInSeconds = 10;
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing chromeos::BalloonViewImpl;
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing chromeos::NotificationPanel;
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if !defined(NDEBUG)
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// A utility function to convert State enum to string.
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char* ToStr(const NotificationPanel::State state) {
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (state) {
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationPanel::FULL:
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return "full";
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationPanel::KEEP_SIZE:
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return "keep_size";
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationPanel::STICKY_AND_NEW:
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return "sticky_new";
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationPanel::MINIMIZED:
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return "minimized";
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationPanel::CLOSED:
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return "closed";
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return "unknown";
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochchromeos::BalloonViewImpl* GetBalloonViewOf(const Balloon* balloon) {
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<chromeos::BalloonViewImpl*>(balloon->view());
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// A WidgetGtk to preevnt recursive calls to PaintNow, which is observed
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// with gtk 2.18.6. See http://crbug.com/42235 for more details.
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass PanelWidget : public views::WidgetGtk {
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PanelWidget() : WidgetGtk(TYPE_WINDOW), painting_(false) {
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~PanelWidget() {
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Enable double buffering because the panel has both pure views control and
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // native controls (scroll bar).
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    EnableDoubleBuffer(true);
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // views::WidgetGtk overrides.
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void PaintNow(const gfx::Rect& update_rect) {
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!painting_) {
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      painting_ = true;
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      WidgetGtk::PaintNow(update_rect);
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      painting_ = false;
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // True if the painting is in progress.
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool painting_;
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(PanelWidget);
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// A WidgetGtk that covers entire ScrollView's viewport. Without this,
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// all renderer's native gtk widgets are moved one by one via
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// View::VisibleBoundsInRootChanged() notification, which makes
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// scrolling not smooth.
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass ViewportWidget : public views::WidgetGtk {
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit ViewportWidget(chromeos::NotificationPanel* panel)
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : WidgetGtk(views::WidgetGtk::TYPE_CHILD),
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_(panel) {
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void UpdateControl() {
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (last_point_.get())
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      panel_->OnMouseMotion(*last_point_.get());
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // views::WidgetGtk overrides.
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual gboolean OnMotionNotify(GtkWidget* widget, GdkEventMotion* event) {
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gboolean result = WidgetGtk::OnMotionNotify(widget, event);
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int x = 0, y = 0;
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetContainedWidgetEventCoordinates(event, &x, &y);
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The window_contents_' allocation has been moved off the top left
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // corner, so we need to adjust it.
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GtkAllocation alloc = widget->allocation;
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    x -= alloc.x;
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    y -= alloc.y;
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!last_point_.get()) {
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      last_point_.reset(new gfx::Point(x, y));
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      last_point_->set_x(x);
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      last_point_->set_y(y);
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_->OnMouseMotion(*last_point_.get());
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return result;
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual gboolean OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event) {
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gboolean result = views::WidgetGtk::OnLeaveNotify(widget, event);
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Leave notify can happen if the mouse moves into the child gdk window.
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Make sure the mouse is outside of the panel.
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Point p(event->x_root, event->y_root);
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bounds;
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetBounds(&bounds, true);
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!bounds.Contains(p)) {
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      panel_->OnMouseLeave();
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      last_point_.reset();
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return result;
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  chromeos::NotificationPanel* panel_;
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<gfx::Point> last_point_;
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(ViewportWidget);
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass BalloonSubContainer : public views::View {
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit BalloonSubContainer(int margin)
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : margin_(margin) {
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~BalloonSubContainer() {}
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // views::View overrides.
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual gfx::Size GetPreferredSize() {
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return preferred_size_;
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void Layout() {
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Layout bottom up
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int height = 0;
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::View* child = GetChildViewAt(i);
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      child->SetBounds(0, height, child->width(), child->height());
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      height += child->height() + margin_;
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SchedulePaint();
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Updates the bound so that it can show all balloons.
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void UpdateBounds() {
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int height = 0;
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int max_width = 0;
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::View* child = GetChildViewAt(i);
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      height += child->height() + margin_;
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      max_width = std::max(max_width, child->width());
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (height > 0)
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      height -= margin_;
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    preferred_size_.set_width(max_width);
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    preferred_size_.set_height(height);
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SizeToPreferredSize();
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the bounds that covers new notifications.
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect GetNewBounds() {
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect rect;
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      BalloonViewImpl* view =
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<BalloonViewImpl*>(GetChildViewAt(i));
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!view->stale()) {
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (rect.IsEmpty()) {
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          rect = view->bounds();
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        } else {
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          rect = rect.Union(view->bounds());
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return gfx::Rect(x(), y(), rect.width(), rect.height());
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns # of new notifications.
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int GetNewCount() {
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int count = 0;
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      BalloonViewImpl* view =
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<BalloonViewImpl*>(GetChildViewAt(i));
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!view->stale())
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        count++;
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return count;
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make all notifications stale.
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void MakeAllStale() {
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      BalloonViewImpl* view =
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<BalloonViewImpl*>(GetChildViewAt(i));
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view->set_stale();
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void DismissAll() {
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      BalloonViewImpl* view =
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<BalloonViewImpl*>(GetChildViewAt(i));
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view->Close(true);
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* FindBalloonView(const Notification& notification) {
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      BalloonViewImpl* view =
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<BalloonViewImpl*>(GetChildViewAt(i));
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (view->IsFor(notification)) {
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return view;
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* FindBalloonView(const gfx::Point point) {
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Point copy(point);
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ConvertPointFromWidget(this, &copy);
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = GetChildViewCount() - 1; i >= 0; --i) {
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::View* view = GetChildViewAt(i);
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (view->bounds().Contains(copy))
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return static_cast<BalloonViewImpl*>(view);
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size preferred_size_;
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int margin_;
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(BalloonSubContainer);
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace chromeos {
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass BalloonContainer : public views::View {
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
274513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  explicit BalloonContainer(int margin)
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : margin_(margin),
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        sticky_container_(new BalloonSubContainer(margin)),
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        non_sticky_container_(new BalloonSubContainer(margin)) {
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AddChildView(sticky_container_);
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AddChildView(non_sticky_container_);
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~BalloonContainer() {}
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // views::View overrides.
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void Layout() {
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int margin =
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (sticky_container_->GetChildViewCount() != 0 &&
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         non_sticky_container_->GetChildViewCount() != 0) ?
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        margin_ : 0;
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sticky_container_->SetBounds(
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        0, 0, width(), sticky_container_->height());
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    non_sticky_container_->SetBounds(
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        0, sticky_container_->bounds().bottom() + margin,
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        width(), non_sticky_container_->height());
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual gfx::Size GetPreferredSize() {
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return preferred_size_;
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the size that covers sticky and new notifications.
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size GetStickyNewSize() {
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect sticky = sticky_container_->bounds();
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect new_non_sticky = non_sticky_container_->GetNewBounds();
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (sticky.IsEmpty())
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return new_non_sticky.size();
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (new_non_sticky.IsEmpty())
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return sticky.size();
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return sticky.Union(new_non_sticky).size();
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Adds a ballon to the panel.
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Add(Balloon* balloon) {
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BalloonViewImpl* view = GetBalloonViewOf(balloon);
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetContainerFor(balloon)->AddChildView(view);
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Updates the position of the |balloon|.
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool Update(Balloon* balloon) {
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BalloonViewImpl* view = GetBalloonViewOf(balloon);
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    View* container = NULL;
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (sticky_container_->HasChildView(view)) {
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      container = sticky_container_;
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if (non_sticky_container_->HasChildView(view)) {
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      container = non_sticky_container_;
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (container) {
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      container->RemoveChildView(view);
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      container->AddChildView(view);
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return true;
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Removes a ballon from the panel.
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* Remove(Balloon* balloon) {
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BalloonViewImpl* view = GetBalloonViewOf(balloon);
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetContainerFor(balloon)->RemoveChildView(view);
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return view;
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the number of notifications added to the panel.
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int GetNotificationCount() {
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return sticky_container_->GetChildViewCount() +
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        non_sticky_container_->GetChildViewCount();
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the # of new notifications.
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int GetNewNotificationCount() {
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return sticky_container_->GetNewCount() +
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        non_sticky_container_->GetNewCount();
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the # of sticky and new notifications.
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int GetStickyNewNotificationCount() {
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return sticky_container_->GetChildViewCount() +
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        non_sticky_container_->GetNewCount();
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the # of sticky notifications.
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int GetStickyNotificationCount() {
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return sticky_container_->GetChildViewCount();
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns true if the |view| is contained in the panel.
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool HasBalloonView(View* view) {
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return sticky_container_->HasChildView(view) ||
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        non_sticky_container_->HasChildView(view);
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Updates the bounds so that all notifications are visible.
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void UpdateBounds() {
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sticky_container_->UpdateBounds();
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    non_sticky_container_->UpdateBounds();
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    preferred_size_ = sticky_container_->GetPreferredSize();
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Size non_sticky_size = non_sticky_container_->GetPreferredSize();
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int margin =
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (!preferred_size_.IsEmpty() && !non_sticky_size.IsEmpty()) ?
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        margin_ : 0;
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    preferred_size_.Enlarge(0, non_sticky_size.height() + margin);
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    preferred_size_.set_width(std::max(
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        preferred_size_.width(), non_sticky_size.width()));
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SizeToPreferredSize();
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void MakeAllStale() {
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sticky_container_->MakeAllStale();
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    non_sticky_container_->MakeAllStale();
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void DismissAllNonSticky() {
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    non_sticky_container_->DismissAll();
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* FindBalloonView(const Notification& notification) {
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BalloonViewImpl* view = sticky_container_->FindBalloonView(notification);
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return view ? view : non_sticky_container_->FindBalloonView(notification);
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* FindBalloonView(const gfx::Point& point) {
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BalloonViewImpl* view = sticky_container_->FindBalloonView(point);
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return view ? view : non_sticky_container_->FindBalloonView(point);
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonSubContainer* GetContainerFor(Balloon* balloon) const {
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BalloonViewImpl* view = GetBalloonViewOf(balloon);
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return view->sticky() ?
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        sticky_container_ : non_sticky_container_;
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int margin_;
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Sticky/non-sticky ballon containers. They're child views and
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // deleted when this container is deleted.
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonSubContainer* sticky_container_;
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonSubContainer* non_sticky_container_;
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size preferred_size_;
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(BalloonContainer);
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochNotificationPanel::NotificationPanel()
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : balloon_container_(NULL),
425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      panel_widget_(NULL),
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      container_host_(NULL),
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      state_(CLOSED),
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      task_factory_(this),
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      min_bounds_(0, 0, kBalloonMinWidth, kBalloonMinHeight),
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      stale_timeout_(1000 * kStaleTimeoutInSeconds),
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      active_(NULL),
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      scroll_to_(NULL) {
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Init();
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochNotificationPanel::~NotificationPanel() {
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Hide();
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NottificationPanel public.
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::Show() {
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!panel_widget_) {
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(oshima): Using window because Popup widget behaves weird
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // when resizing. This needs to be investigated.
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_widget_ = new PanelWidget();
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bounds = GetPreferredBounds();
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bounds = bounds.Union(min_bounds_);
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_widget_->Init(NULL, bounds);
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Set minimum bounds so that it can grow freely.
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_set_size_request(GTK_WIDGET(panel_widget_->GetNativeView()),
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                min_bounds_.width(), min_bounds_.height());
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    views::NativeViewHost* native = new views::NativeViewHost();
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scroll_view_->SetContents(native);
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_widget_->SetContentsView(scroll_view_.get());
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Add the view port after scroll_view is attached to the panel widget.
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ViewportWidget* widget = new ViewportWidget(this);
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    container_host_ = widget;
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    container_host_->Init(NULL, gfx::Rect());
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    container_host_->SetContentsView(balloon_container_.get());
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The window_contents_ is onwed by the WidgetGtk. Increase ref count
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // so that window_contents does not get deleted when detached.
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    g_object_ref(widget->window_contents());
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    native->Attach(widget->window_contents());
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UnregisterNotification();
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_controller_.reset(
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        new PanelController(this, GTK_WINDOW(panel_widget_->GetNativeView())));
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_controller_->Init(false /* don't focus when opened */,
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            gfx::Rect(0, 0, kBalloonMinWidth, 1), 0,
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            WM_IPC_PANEL_USER_RESIZE_VERTICALLY);
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Add(this, NotificationType::PANEL_STATE_CHANGED,
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   Source<PanelController>(panel_controller_.get()));
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  panel_widget_->Show();
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::Hide() {
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_container_->DismissAllNonSticky();
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (panel_widget_) {
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    container_host_->GetRootView()->RemoveChildView(balloon_container_.get());
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    views::NativeViewHost* native =
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        static_cast<views::NativeViewHost*>(scroll_view_->GetContents());
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    native->Detach();
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scroll_view_->SetContents(NULL);
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    container_host_->Hide();
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    container_host_->CloseNow();
493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    container_host_ = NULL;
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UnregisterNotification();
496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_controller_->Close();
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MessageLoop::current()->DeleteSoon(FROM_HERE, panel_controller_.release());
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We need to remove & detach the scroll view from hierarchy to
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // avoid GTK deleting child.
500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(oshima): handle this details in WidgetGtk.
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_widget_->GetRootView()->RemoveChildView(scroll_view_.get());
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_widget_->Close();
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    panel_widget_ = NULL;
504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// BalloonCollectionImpl::NotificationUI overrides.
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::Add(Balloon* balloon) {
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_container_->Add(balloon);
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (state_ == CLOSED || state_ == MINIMIZED)
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SET_STATE(STICKY_AND_NEW);
514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Show();
515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't resize the panel yet. The panel will be resized when WebKit tells
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the size in ResizeNotification.
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdatePanel(false);
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateControl();
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartStaleTimer(balloon);
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scroll_to_ = balloon;
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool NotificationPanel::Update(Balloon* balloon) {
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return balloon_container_->Update(balloon);
525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::Remove(Balloon* balloon) {
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* view = balloon_container_->Remove(balloon);
529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (view == active_)
530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    active_ = NULL;
531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (scroll_to_ == balloon)
532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scroll_to_ = NULL;
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(oshima): May be we shouldn't close
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // if the mouse pointer is still on the panel.
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (balloon_container_->GetNotificationCount() == 0)
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SET_STATE(CLOSED);
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // no change to the state
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (state_ == KEEP_SIZE) {
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Just update the content.
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdateContainerBounds();
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (state_ != CLOSED &&
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        balloon_container_->GetStickyNewNotificationCount() == 0)
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SET_STATE(MINIMIZED);
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdatePanel(true);
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateControl();
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::Show(Balloon* balloon) {
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (state_ == CLOSED || state_ == MINIMIZED)
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SET_STATE(STICKY_AND_NEW);
554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Show();
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdatePanel(true);
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartStaleTimer(balloon);
557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ScrollBalloonToVisible(balloon);
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::ResizeNotification(
561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Balloon* balloon, const gfx::Size& size) {
562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // restrict to the min & max sizes
563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size real_size(
564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::max(kBalloonMinWidth,
565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch               std::min(kBalloonMaxWidth, size.width())),
566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::max(kBalloonMinHeight,
567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch               std::min(kBalloonMaxHeight, size.height())));
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't allow balloons to shrink.  This avoids flickering
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // which sometimes rapidly reports alternating sizes.  Special
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // case for setting the minimum value.
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size old_size = balloon->content_size();
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (real_size.width() > old_size.width() ||
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      real_size.height() > old_size.height() ||
575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      real_size == min_bounds_.size()) {
576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    balloon->set_content_size(real_size);
577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetBalloonViewOf(balloon)->Layout();
578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdatePanel(true);
579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (scroll_to_ == balloon) {
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ScrollBalloonToVisible(scroll_to_);
581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      scroll_to_ = NULL;
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::SetActiveView(BalloonViewImpl* view) {
587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't change the active view if it's same notification,
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // or the notification is being closed.
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (active_ == view || (view && view->closed()))
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (active_)
592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    active_->Deactivated();
593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_ = view;
594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (active_)
595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    active_->Activated();
596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// PanelController overrides.
600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring16 NotificationPanel::GetPanelTitle() {
602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return string16(l10n_util::GetStringUTF16(IDS_NOTIFICATION_PANEL_TITLE));
603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
605c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochSkBitmap NotificationPanel::GetPanelIcon() {
606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return SkBitmap();
607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::ClosePanel() {
610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SET_STATE(CLOSED);
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdatePanel(false);
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NotificationObserver overrides.
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::Observe(NotificationType type,
618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                const NotificationSource& source,
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                const NotificationDetails& details) {
620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(type == NotificationType::PANEL_STATE_CHANGED);
621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PanelController::State* state =
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      reinterpret_cast<PanelController::State*>(details.map_key());
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (*state) {
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case PanelController::EXPANDED:
625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Geting expanded in STICKY_AND_NEW or in KEEP_SIZE state means
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // that a new notification is added, so just leave the
627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // state. Otherwise, expand to full.
628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (state_ != STICKY_AND_NEW && state_ != KEEP_SIZE)
629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SET_STATE(FULL);
630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // When the panel is to be expanded, we either show all, or
631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // show only sticky/new, depending on the state.
632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UpdatePanel(false);
633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case PanelController::MINIMIZED:
635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SET_STATE(MINIMIZED);
636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Make all notifications stale when a user minimize the panel.
637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      balloon_container_->MakeAllStale();
638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case PanelController::INITIAL:
640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED() << "Transition to Initial state should not happen";
641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// PanelController public.
646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::OnMouseLeave() {
648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetActiveView(NULL);
649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (balloon_container_->GetNotificationCount() == 0)
650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SET_STATE(CLOSED);
651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdatePanel(true);
652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
653c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::OnMouseMotion(const gfx::Point& point) {
655c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetActiveView(balloon_container_->FindBalloonView(point));
656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SET_STATE(KEEP_SIZE);
657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
659c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochNotificationPanelTester* NotificationPanel::GetTester() {
660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!tester_.get())
661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tester_.reset(new NotificationPanelTester(this));
662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tester_.get();
663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NotificationPanel private.
667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::Init() {
669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(!panel_widget_);
670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_container_.reset(new BalloonContainer(1));
671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_container_->set_parent_owned(false);
672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_container_->set_background(
673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Background::CreateSolidBackground(ResourceBundle::frame_color));
674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scroll_view_.reset(new views::ScrollView());
676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scroll_view_->set_parent_owned(false);
677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scroll_view_->set_background(
678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Background::CreateSolidBackground(SK_ColorWHITE));
679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::UnregisterNotification() {
682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (panel_controller_.get())
683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Remove(this, NotificationType::PANEL_STATE_CHANGED,
684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      Source<PanelController>(panel_controller_.get()));
685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::ScrollBalloonToVisible(Balloon* balloon) {
688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* view = GetBalloonViewOf(balloon);
689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!view->closed()) {
690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We can't use View::ScrollRectToVisible because the viewport is not
691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // ancestor of the BalloonViewImpl.
692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Use Widget's coordinate which is same as viewport's coordinates.
693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Point p(0, 0);
694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    views::View::ConvertPointToWidget(view, &p);
695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect visible_rect(p.x(), p.y(), view->width(), view->height());
696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scroll_view_->ScrollContentsRegionToBeVisible(visible_rect);
697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::UpdatePanel(bool update_container_size) {
701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (update_container_size)
702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdateContainerBounds();
703513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  switch (state_) {
704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case KEEP_SIZE: {
705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gfx::Rect min_bounds = GetPreferredBounds();
706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gfx::Rect panel_bounds;
707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      panel_widget_->GetBounds(&panel_bounds, true);
708c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (min_bounds.height() < panel_bounds.height())
709c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_widget_->SetBounds(min_bounds);
710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      else if (min_bounds.height() > panel_bounds.height()) {
711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // need scroll bar
712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        int width = balloon_container_->width() +
713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            scroll_view_->GetScrollBarWidth();
714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_bounds.set_width(width);
715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_widget_->SetBounds(panel_bounds);
716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // no change.
719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case CLOSED:
722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Hide();
723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case MINIMIZED:
725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      balloon_container_->MakeAllStale();
726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (panel_controller_.get())
727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_controller_->SetState(PanelController::MINIMIZED);
728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case FULL:
730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (panel_widget_) {
731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_widget_->SetBounds(GetPreferredBounds());
732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_controller_->SetState(PanelController::EXPANDED);
733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case STICKY_AND_NEW:
736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (panel_widget_) {
737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_widget_->SetBounds(GetStickyNewBounds());
738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        panel_controller_->SetState(PanelController::EXPANDED);
739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::UpdateContainerBounds() {
745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_container_->UpdateBounds();
746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::NativeViewHost* native =
747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<views::NativeViewHost*>(scroll_view_->GetContents());
748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update from WebKit may arrive after the panel is closed/hidden
749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // and viewport widget is detached.
750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (native) {
751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    native->SetBounds(balloon_container_->bounds());
752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scroll_view_->Layout();
753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::UpdateControl() {
757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (container_host_)
758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    static_cast<ViewportWidget*>(container_host_)->UpdateControl();
759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect NotificationPanel::GetPreferredBounds() {
762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size pref_size = balloon_container_->GetPreferredSize();
763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_height = std::min(pref_size.height(), kMaxPanelHeight);
764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_width = pref_size.width();
765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Adjust the width to avoid showing a horizontal scroll bar.
766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (new_height != pref_size.height()) {
767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    new_width += scroll_view_->GetScrollBarWidth();
768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Rect(0, 0, new_width, new_height).Union(min_bounds_);
770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect NotificationPanel::GetStickyNewBounds() {
773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size pref_size = balloon_container_->GetPreferredSize();
774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size sticky_size = balloon_container_->GetStickyNewSize();
775c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_height = std::min(sticky_size.height(), kMaxPanelHeight);
776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_width = pref_size.width();
777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Adjust the width to avoid showing a horizontal scroll bar.
778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (new_height != pref_size.height())
779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    new_width += scroll_view_->GetScrollBarWidth();
780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Rect(0, 0, new_width, new_height).Union(min_bounds_);
781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::StartStaleTimer(Balloon* balloon) {
784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* view = GetBalloonViewOf(balloon);
785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoop::current()->PostDelayedTask(
786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      FROM_HERE,
787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      task_factory_.NewRunnableMethod(
788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          &NotificationPanel::OnStale, view),
789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      stale_timeout_);
790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
792c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::OnStale(BalloonViewImpl* view) {
793c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (balloon_container_->HasBalloonView(view) && !view->stale()) {
794c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view->set_stale();
795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // don't update panel on stale
796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (state_ == KEEP_SIZE)
797c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (balloon_container_->GetStickyNewNotificationCount() > 0) {
799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SET_STATE(STICKY_AND_NEW);
800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SET_STATE(MINIMIZED);
802c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdatePanel(false);
804c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::SetState(State new_state, const char* name) {
808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if !defined(NDEBUG)
809513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DVLOG(1) << "state transition " << ToStr(state_) << " >> " << ToStr(new_state)
810513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch           << " in " << name;
811c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  state_ = new_state;
813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanel::MarkStale(const Notification& notification) {
816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BalloonViewImpl* view = balloon_container_->FindBalloonView(notification);
817c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(view);
818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  OnStale(view);
819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NotificationPanelTester public.
823c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
824c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint NotificationPanelTester::GetNotificationCount() const {
825c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return panel_->balloon_container_->GetNotificationCount();
826c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
827c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
828c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint NotificationPanelTester::GetStickyNotificationCount() const {
829c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return panel_->balloon_container_->GetStickyNotificationCount();
830c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
831c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
832c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint NotificationPanelTester::GetNewNotificationCount() const {
833c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return panel_->balloon_container_->GetNewNotificationCount();
834c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
835c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
836c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanelTester::SetStaleTimeout(int timeout) {
837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  panel_->stale_timeout_ = timeout;
838c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
839c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
840c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NotificationPanelTester::MarkStale(const Notification& notification) {
841c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  panel_->MarkStale(notification);
842c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
844c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochPanelController* NotificationPanelTester::GetPanelController() const {
845c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return panel_->panel_controller_.get();
846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
847c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
848c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBalloonViewImpl* NotificationPanelTester::GetBalloonView(
849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BalloonCollectionImpl* collection,
850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const Notification& notification) {
851201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  Balloon* balloon = collection->FindBalloon(notification);
852201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  DCHECK(balloon);
853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return GetBalloonViewOf(balloon);
854c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
855c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
856c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool NotificationPanelTester::IsVisible(const BalloonViewImpl* view) const {
857c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect rect = panel_->scroll_view_->GetVisibleRect();
858c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point origin(0, 0);
859c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View::ConvertPointToView(view, panel_->balloon_container_.get(),
860c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  &origin);
861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return rect.Contains(gfx::Rect(origin, view->bounds().size()));
862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
863c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
864c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
865c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool NotificationPanelTester::IsActive(const BalloonViewImpl* view) const {
866c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return panel_->active_ == view;
867c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
868c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
869c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace chromeos
870