172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 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
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/notifications/balloon_view_gtk.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <gtk/gtk.h>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string>
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector>
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_host.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_process_manager.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/notifications/balloon.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/notifications/desktop_notification_service.h"
18201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/notifications/notification.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/notifications/notification_options_menu_model.h"
2021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/themes/theme_service.h"
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_list.h"
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_window.h"
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/custom_button.h"
25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/gtk/gtk_theme_service.h"
2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h"
2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/info_bubble_gtk.h"
2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/menu_gtk.h"
2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/notifications/balloon_view_host_gtk.h"
3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/rounded_window.h"
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h"
32dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_view_host.h"
33dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_widget_host_view.h"
34dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_details.h"
35dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_service.h"
36dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_source.h"
37dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_type.h"
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h"
403f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/slide_animation.h"
4172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
4272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h"
4372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas.h"
4472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/insets.h"
4572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/native_widget_types.h"
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Margin, in pixels, between the notification frame and the contents
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// of the notification.
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kTopMargin = 0;
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kBottomMargin = 1;
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kLeftMargin = 1;
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kRightMargin = 1;
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How many pixels of overlap there is between the shelf top and the
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// balloon bottom.
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kShelfBorderTopOverlap = 0;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Properties of the origin label.
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kLeftLabelMargin = 8;
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TODO(johnnyg): Add a shadow for the frame.
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kLeftShadowWidth = 0;
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kRightShadowWidth = 0;
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kTopShadowWidth = 0;
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kBottomShadowWidth = 0;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Space in pixels between text and icon on the buttons.
703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickconst int kButtonSpacing = 4;
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Number of characters to show in the origin label before ellipsis.
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kOriginLabelCharacters = 18;
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The shelf height for the system default font size.  It is scaled
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// with changes in the default font size.
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kDefaultShelfHeight = 21;
783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickconst int kShelfVerticalMargin = 4;
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The amount that the bubble collections class offsets from the side of the
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// screen.
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kScreenBorder = 5;
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Colors specified in various ways for different parts of the UI.
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// These match the windows colors in balloon_view.cc
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char* kLabelColor = "#7D7D7D";
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst double kShelfBackgroundColorR = 245.0 / 255.0;
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst double kShelfBackgroundColorG = 245.0 / 255.0;
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst double kShelfBackgroundColorB = 245.0 / 255.0;
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst double kDividerLineColorR = 180.0 / 255.0;
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst double kDividerLineColorG = 180.0 / 255.0;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst double kDividerLineColorB = 180.0 / 255.0;
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Makes the website label relatively smaller to the base text size.
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char* kLabelMarkup = "<span size=\"small\" color=\"%s\">%s</span>";
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBalloonViewImpl::BalloonViewImpl(BalloonCollection* collection)
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : balloon_(NULL),
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      frame_container_(NULL),
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      html_container_(NULL),
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      method_factory_(this),
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      close_button_(NULL),
10572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      animation_(NULL),
10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      menu_showing_(false),
10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      pending_close_(false) {
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBalloonViewImpl::~BalloonViewImpl() {
111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (frame_container_) {
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    GtkWidget* widget = frame_container_;
113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    frame_container_ = NULL;
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    gtk_widget_hide(widget);
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BalloonViewImpl::Close(bool by_user) {
11972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Delay a system-initiated close if the menu is showing.
12072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!by_user && menu_showing_) {
12172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    pending_close_ = true;
12272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else {
12372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    MessageLoop::current()->PostTask(
12472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        FROM_HERE,
12572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        method_factory_.NewRunnableMethod(
12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen            &BalloonViewImpl::DelayedClose, by_user));
12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Size BalloonViewImpl::GetSize() const {
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // BalloonView has no size if it hasn't been shown yet (which is when
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // balloon_ is set).
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!balloon_)
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return gfx::Size();
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Although this may not be the instantaneous size of the balloon if
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // called in the middle of an animation, it is the effective size that
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // will result from the animation.
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Size(GetDesiredTotalWidth(), GetDesiredTotalHeight());
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
14221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenBalloonHost* BalloonViewImpl::GetHost() const {
14321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  return html_contents_.get();
14421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
14521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BalloonViewImpl::DelayedClose(bool by_user) {
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  html_contents_->Shutdown();
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (frame_container_) {
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // It's possible that |frame_container_| was destroyed before the
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // BalloonViewImpl if our related browser window was closed first.
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_hide(frame_container_);
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_->OnClose(by_user);
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BalloonViewImpl::RepositionToBalloon() {
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!frame_container_) {
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // No need to create a slide animation when this balloon is fading out.
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(balloon_);
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create an amination from the current position to the desired one.
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int start_x;
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int start_y;
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int start_w;
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int start_h;
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_get_position(GTK_WINDOW(frame_container_), &start_x, &start_y);
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_get_size(GTK_WINDOW(frame_container_), &start_w, &start_h);
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int end_x = balloon_->GetPosition().x();
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int end_y = balloon_->GetPosition().y();
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int end_w = GetDesiredTotalWidth();
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int end_h = GetDesiredTotalHeight();
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  anim_frame_start_ = gfx::Rect(start_x, start_y, start_w, start_h);
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  anim_frame_end_ = gfx::Rect(end_x, end_y, end_w, end_h);
1793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  animation_.reset(new ui::SlideAnimation(this));
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  animation_->Show();
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1833f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid BalloonViewImpl::AnimationProgressed(const ui::Animation* animation) {
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_EQ(animation, animation_.get());
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Linear interpolation from start to end position.
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double end = animation->GetCurrentValue();
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double start = 1.0 - end;
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect frame_position(
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<int>(start * anim_frame_start_.x() +
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       end * anim_frame_end_.x()),
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<int>(start * anim_frame_start_.y() +
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       end * anim_frame_end_.y()),
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<int>(start * anim_frame_start_.width() +
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       end * anim_frame_end_.width()),
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<int>(start * anim_frame_start_.height() +
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       end * anim_frame_end_.height()));
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_resize(GTK_WINDOW(frame_container_),
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    frame_position.width(), frame_position.height());
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_move(GTK_WINDOW(frame_container_),
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  frame_position.x(), frame_position.y());
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect contents_rect = GetContentsRectangle();
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  html_contents_->UpdateActualSize(contents_rect.size());
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BalloonViewImpl::Show(Balloon* balloon) {
209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  theme_service_ = GtkThemeService::GetFrom(balloon->profile());
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const std::string source_label_text = l10n_util::GetStringFUTF8(
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      IDS_NOTIFICATION_BALLOON_SOURCE_LABEL,
2133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      balloon->notification().display_source());
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const std::string options_text =
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      l10n_util::GetStringUTF8(IDS_NOTIFICATION_OPTIONS_MENU_LABEL);
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const std::string dismiss_text =
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      l10n_util::GetStringUTF8(IDS_NOTIFICATION_BALLOON_DISMISS_LABEL);
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_ = balloon;
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  frame_container_ = gtk_window_new(GTK_WINDOW_POPUP);
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Construct the options menu.
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  options_menu_model_.reset(new NotificationOptionsMenuModel(balloon_));
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  options_menu_.reset(new MenuGtk(this, options_menu_model_.get()));
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a BalloonViewHost to host the HTML contents of this balloon.
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  html_contents_.reset(new BalloonViewHost(balloon));
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  html_contents_->Init();
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::NativeView contents = html_contents_->native_view();
23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  g_signal_connect_after(contents, "expose-event",
23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                         G_CALLBACK(OnContentsExposeThunk), this);
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Divide the frame vertically into the shelf and the content area.
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* vbox = gtk_vbox_new(0, 0);
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(frame_container_), vbox);
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shelf_ = gtk_hbox_new(0, 0);
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(vbox), shelf_);
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_alignment_set_padding(
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      GTK_ALIGNMENT(alignment),
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kTopMargin, kBottomMargin, kLeftMargin, kRightMargin);
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show_all(alignment);
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(alignment), contents);
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(vbox), alignment);
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a toolbar and add it to the shelf.
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  hbox_ = gtk_hbox_new(FALSE, 0);
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_size_request(GTK_WIDGET(hbox_), -1, GetShelfHeight());
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(shelf_), hbox_);
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show_all(vbox);
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(frame_container_, "expose-event",
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnExposeThunk), this);
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(frame_container_, "destroy",
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDestroyThunk), this);
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a label for the source of the notification and add it to the
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // toolbar.
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* source_label_ = gtk_label_new(NULL);
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  char* markup = g_markup_printf_escaped(kLabelMarkup,
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         kLabelColor,
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         source_label_text.c_str());
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_label_set_markup(GTK_LABEL(source_label_), markup);
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_free(markup);
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_label_set_max_width_chars(GTK_LABEL(source_label_),
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                kOriginLabelCharacters);
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_label_set_ellipsize(GTK_LABEL(source_label_), PANGO_ELLIPSIZE_END);
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* label_alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_alignment_set_padding(GTK_ALIGNMENT(label_alignment),
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            kShelfVerticalMargin, kShelfVerticalMargin,
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            kLeftLabelMargin, 0);
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(label_alignment), source_label_);
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_pack_start(GTK_BOX(hbox_), label_alignment, FALSE, FALSE, 0);
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
2783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a button to dismiss the balloon and add it to the toolbar.
2803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  close_button_.reset(new CustomDrawButton(IDR_TAB_CLOSE,
2813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                           IDR_TAB_CLOSE_P,
2823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                           IDR_TAB_CLOSE_H,
2833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                           IDR_TAB_CLOSE));
2843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  close_button_->SetBackground(SK_ColorBLACK,
2853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                               rb.GetBitmapNamed(IDR_TAB_CLOSE),
2863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                               rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK));
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_tooltip_text(close_button_->widget(), dismiss_text.c_str());
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(close_button_->widget(), "clicked",
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnCloseButtonThunk), this);
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GTK_WIDGET_UNSET_FLAGS(close_button_->widget(), GTK_CAN_FOCUS);
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* close_alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_alignment_set_padding(GTK_ALIGNMENT(close_alignment),
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            kShelfVerticalMargin, kShelfVerticalMargin,
2943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                            0, kButtonSpacing);
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(close_alignment), close_button_->widget());
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_pack_end(GTK_BOX(hbox_), close_alignment, FALSE, FALSE, 0);
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a button for showing the options menu, and add it to the toolbar.
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  options_menu_button_.reset(new CustomDrawButton(IDR_BALLOON_WRENCH,
3003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                                  IDR_BALLOON_WRENCH_P,
3013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                                  IDR_BALLOON_WRENCH_H,
3023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                                  0));
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_tooltip_text(options_menu_button_->widget(),
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              options_text.c_str());
30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  g_signal_connect(options_menu_button_->widget(), "button-press-event",
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnOptionsMenuButtonThunk), this);
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GTK_WIDGET_UNSET_FLAGS(options_menu_button_->widget(), GTK_CAN_FOCUS);
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* options_alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_alignment_set_padding(GTK_ALIGNMENT(options_alignment),
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            kShelfVerticalMargin, kShelfVerticalMargin,
3113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                            0, kButtonSpacing);
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(options_alignment),
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    options_menu_button_->widget());
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_pack_end(GTK_BOX(hbox_), options_alignment, FALSE, FALSE, 0);
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              NotificationService::AllSources());
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We don't do InitThemesFor() because it just forces a redraw.
320513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  gtk_util::ActAsRoundedWindow(frame_container_, gtk_util::kGdkBlack, 3,
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               gtk_util::ROUNDED_ALL,
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               gtk_util::BORDER_ALL);
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Realize the frame container so we can do size calculations.
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_realize(frame_container_);
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update to make sure we have everything sized properly and then move our
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // window offscreen for its initial animation.
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  html_contents_->UpdateActualSize(balloon_->content_size());
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int window_width;
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_get_size(GTK_WINDOW(frame_container_), &window_width, NULL);
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int pos_x = gdk_screen_width() - window_width - kScreenBorder;
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int pos_y = gdk_screen_height();
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_move(GTK_WINDOW(frame_container_), pos_x, pos_y);
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  balloon_->SetPosition(gfx::Point(pos_x, pos_y), false);
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show_all(frame_container_);
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(this,
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon));
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BalloonViewImpl::Update() {
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(html_contents_.get()) << "BalloonView::Update called before Show";
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (html_contents_->render_view_host())
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    html_contents_->render_view_host()->NavigateToURL(
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        balloon_->notification().content_url());
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Point BalloonViewImpl::GetContentsOffset() const {
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Point(kLeftShadowWidth + kLeftMargin,
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    GetShelfHeight() + kTopShadowWidth + kTopMargin);
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BalloonViewImpl::GetShelfHeight() const {
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(johnnyg): add scaling here.
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return kDefaultShelfHeight;
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BalloonViewImpl::GetDesiredTotalWidth() const {
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return balloon_->content_size().width() +
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kLeftMargin + kRightMargin + kLeftShadowWidth + kRightShadowWidth;
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BalloonViewImpl::GetDesiredTotalHeight() const {
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return balloon_->content_size().height() +
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kTopMargin + kBottomMargin + kTopShadowWidth + kBottomShadowWidth +
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      GetShelfHeight();
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect BalloonViewImpl::GetContentsRectangle() const {
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!frame_container_)
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return gfx::Rect();
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size content_size = balloon_->content_size();
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point offset = GetContentsOffset();
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int x = 0, y = 0;
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_get_position(GTK_WINDOW(frame_container_), &x, &y);
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Rect(x + offset.x(), y + offset.y(),
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   content_size.width(), content_size.height());
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BalloonViewImpl::Observe(NotificationType type,
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              const NotificationSource& source,
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              const NotificationDetails& details) {
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (type == NotificationType::NOTIFY_BALLOON_DISCONNECTED) {
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If the renderer process attached to this balloon is disconnected
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // (e.g., because of a crash), we want to close the balloon.
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    notification_registrar_.Remove(this,
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NotificationType::NOTIFY_BALLOON_DISCONNECTED,
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        Source<Balloon>(balloon_));
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Close(false);
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (type == NotificationType::BROWSER_THEME_CHANGED) {
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Since all the buttons change their own properties, and our expose does
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // all the real differences, we'll need a redraw.
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_queue_draw(frame_container_);
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BalloonViewImpl::OnCloseButton(GtkWidget* widget) {
4033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  Close(true);
4043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
4053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
40672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// We draw black dots on the bottom left and right corners to fill in the
40772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// border. Otherwise, the border has a gap because the sharp corners of the
40872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// HTML view cut off the roundedness of the notification window.
40972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsengboolean BalloonViewImpl::OnContentsExpose(GtkWidget* sender,
41072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                           GdkEventExpose* event) {
41172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(sender->window));
41272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gdk_cairo_rectangle(cr, &event->area);
41372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_clip(cr);
41472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
41572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // According to a discussion on a mailing list I found, these degenerate
41672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // paths are the officially supported way to draw points in Cairo.
41772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_set_source_rgb(cr, 0, 0, 0);
41872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
41972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_set_line_width(cr, 1.0);
42072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_move_to(cr, 0.5, sender->allocation.height - 0.5);
42172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_close_path(cr);
42272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_move_to(cr, sender->allocation.width - 0.5,
42372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                    sender->allocation.height - 0.5);
42472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_close_path(cr);
42572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_stroke(cr);
42672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  cairo_destroy(cr);
42772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
42872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return FALSE;
42972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
43072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean BalloonViewImpl::OnExpose(GtkWidget* sender, GdkEventExpose* event) {
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(sender->window));
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_cairo_rectangle(cr, &event->area);
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_clip(cr);
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size content_size = balloon_->content_size();
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point offset = GetContentsOffset();
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Draw a background color behind the shelf.
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_source_rgb(cr, kShelfBackgroundColorR,
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       kShelfBackgroundColorG, kShelfBackgroundColorB);
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_rectangle(cr, kLeftMargin, kTopMargin + 0.5,
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  content_size.width() - 0.5, GetShelfHeight());
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_fill(cr);
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Now draw a one pixel line between content and shelf.
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_move_to(cr, offset.x(), offset.y() - 1);
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_line_to(cr, offset.x() + content_size.width(), offset.y() - 1);
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_line_width(cr, 0.5);
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_source_rgb(cr, kDividerLineColorR,
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       kDividerLineColorG, kDividerLineColorB);
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_stroke(cr);
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_destroy(cr);
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FALSE;
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
45972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid BalloonViewImpl::OnOptionsMenuButton(GtkWidget* widget,
46072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                          GdkEventButton* event) {
46172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  menu_showing_ = true;
46272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  options_menu_->PopupForWidget(widget, event->button, event->time);
46372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
46472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
46572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Called when the menu stops showing.
46672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid BalloonViewImpl::StoppedShowing() {
46772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  menu_showing_ = false;
468dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (pending_close_) {
469dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    MessageLoop::current()->PostTask(
470dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        FROM_HERE,
471dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        method_factory_.NewRunnableMethod(
472dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen            &BalloonViewImpl::DelayedClose, false));
473dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean BalloonViewImpl::OnDestroy(GtkWidget* widget) {
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  frame_container_ = NULL;
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Close(false);
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FALSE;  // Propagate.
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
481