notification_view.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/views/notification_view.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/command_line.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/utf_string_conversions.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "grit/ui_resources.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/accessibility/accessible_view_state.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/base/layout.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/text/text_elider.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/canvas.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/size.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/message_center/message_center.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/message_center_constants.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/message_center_switches.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/message_center_util.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/notification.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/notification_types.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/message_center/views/bounded_label.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/views/message_simple_view.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/button/image_button.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/image_view.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/label.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/layout/box_layout.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/layout/fill_layout.h"
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/widget/widget.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Dimensions.
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kIconSize = message_center::kNotificationIconSize;
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kLegacyIconSize = 40;
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kIconBottomPadding = 16;
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kTextLeftPadding = kIconSize + message_center::kIconToTextPadding;
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kTextBottomPadding = 12;
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kTextRightPadding = 23;
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kItemTitleToMessagePadding = 3;
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kButtonHeight = 38;
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kButtonHorizontalPadding = 16;
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kButtonVecticalPadding = 0;
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kButtonIconTopPadding = 11;
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kButtonIconToTitlePadding = 16;
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kButtonTitleTopPadding = 0;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Line limits.
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kTitleLineLimit = 3;
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kMessageCollapsedLineLimit = 3;
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kMessageExpandedLineLimit = 7;
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Character limits: Displayed text will be subject to the line limits above,
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// but we also remove trailing characters from text to reduce processing cost.
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Character limit = pixels per line * line limit / min. pixels per character.
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const size_t kTitleCharacterLimit =
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    message_center::kNotificationWidth * kTitleLineLimit / 4;
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const size_t kMessageCharacterLimit =
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    message_center::kNotificationWidth * kMessageExpandedLineLimit / 3;
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Notification colors. The text background colors below are used only to keep
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// view::Label from modifying the text color and will not actually be drawn.
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// See view::Label's RecalculateColors() for details.
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const SkColor kRegularTextBackgroundColor = SK_ColorWHITE;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const SkColor kDimTextColor = SkColorSetRGB(102, 102, 102);
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const SkColor kDimTextBackgroundColor = SK_ColorWHITE;
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const SkColor kButtonSeparatorColor = SkColorSetRGB(234, 234, 234);
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const SkColor kHoveredButtonBackgroundColor = SkColorSetRGB(243, 243, 243);
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)views::Background* MakeBackground(
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    SkColor color = message_center::kNotificationBackgroundColor) {
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return views::Background::CreateSolidBackground(color);
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)views::Border* MakeEmptyBorder(int top, int left, int bottom, int right) {
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return views::Border::CreateEmptyBorder(top, left, bottom, right);
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)views::Border* MakeTextBorder(int padding, int top, int bottom) {
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Split the padding between the top and the bottom, then add the extra space.
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return MakeEmptyBorder(padding / 2 + top, kTextLeftPadding,
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                         (padding + 1) / 2 + bottom, kTextRightPadding);
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)views::Border* MakeSeparatorBorder(int top, int left, SkColor color) {
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return views::Border::CreateSolidSidedBorder(top, left, 0, 0, color);
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Return true if and only if the image is null or has alpha.
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool HasAlpha(gfx::ImageSkia& image, views::Widget* widget) {
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Determine which bitmap to use.
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ui::ScaleFactor factor = ui::SCALE_FACTOR_100P;
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (widget) {
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    factor = ui::GetScaleFactorForNativeView(widget->GetNativeView());
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (factor == ui::SCALE_FACTOR_NONE)
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      factor = ui::SCALE_FACTOR_100P;
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Extract that bitmap's alpha and look for a non-opaque pixel there.
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap bitmap = image.GetRepresentation(factor).sk_bitmap();
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!bitmap.isNull()) {
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    SkBitmap alpha;
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    alpha.setConfig(SkBitmap::kA1_Config, bitmap.width(), bitmap.height(), 0);
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    bitmap.extractAlpha(&alpha);
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int y = 0; y < bitmap.height(); ++y) {
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      for (int x = 0; x < bitmap.width(); ++x) {
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (alpha.getColor(x, y) != SK_ColorBLACK) {
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          return true;
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If no opaque pixel was found, return false unless the bitmap is empty.
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return bitmap.isNull();
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ItemView ////////////////////////////////////////////////////////////////////
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ItemViews are responsible for drawing each list notification item's title and
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// message next to each other within a single column.
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class ItemView : public views::View {
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ItemView(const message_center::NotificationItem& item);
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~ItemView();
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void SetVisible(bool visible) OVERRIDE;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ItemView);
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ItemView::ItemView(const message_center::NotificationItem& item) {
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        0, 0, kItemTitleToMessagePadding));
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::Label* title = new views::Label(item.title);
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  title->set_collapse_when_hidden(true);
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  title->SetElideBehavior(views::Label::ELIDE_AT_END);
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  title->SetEnabledColor(message_center::kRegularTextColor);
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  title->SetBackgroundColor(kRegularTextBackgroundColor);
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(title);
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::Label* message = new views::Label(item.message);
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message->set_collapse_when_hidden(true);
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message->SetElideBehavior(views::Label::ELIDE_AT_END);
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message->SetEnabledColor(kDimTextColor);
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message->SetBackgroundColor(kDimTextBackgroundColor);
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(message);
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PreferredSizeChanged();
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaint();
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ItemView::~ItemView() {
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ItemView::SetVisible(bool visible) {
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::View::SetVisible(visible);
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = 0; i < child_count(); ++i)
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    child_at(i)->SetVisible(visible);
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ProportionalImageView ///////////////////////////////////////////////////////
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ProportionalImageViews center their images to preserve their proportion.
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class ProportionalImageView : public views::View {
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ProportionalImageView(const gfx::ImageSkia& image);
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~ProportionalImageView();
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Overridden from views::View:
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual gfx::Size GetPreferredSize() OVERRIDE;
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual int GetHeightForWidth(int width) OVERRIDE;
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size GetImageSizeForWidth(int width);
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::ImageSkia image_;
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ProportionalImageView);
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ProportionalImageView::ProportionalImageView(const gfx::ImageSkia& image)
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : image_(image) {
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ProportionalImageView::~ProportionalImageView() {
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Size ProportionalImageView::GetPreferredSize() {
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size size = GetImageSizeForWidth(image_.width());
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Size(size.width() + GetInsets().width(),
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   size.height() + GetInsets().height());
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ProportionalImageView::GetHeightForWidth(int width) {
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetImageSizeForWidth(width).height();
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ProportionalImageView::OnPaint(gfx::Canvas* canvas) {
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::View::OnPaint(canvas);
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size draw_size(GetImageSizeForWidth(width()));
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!draw_size.IsEmpty()) {
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    gfx::Rect draw_bounds = GetContentsBounds();
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    draw_bounds.ClampToCenteredSize(draw_size);
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Size image_size(image_.size());
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_size == draw_size) {
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      canvas->DrawImageInt(image_, draw_bounds.x(), draw_bounds.y());
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Resize case
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SkPaint paint;
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      paint.setFilterBitmap(true);
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      canvas->DrawImageInt(image_, 0, 0,
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           image_size.width(), image_size.height(),
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           draw_bounds.x(), draw_bounds.y(),
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           draw_size.width(), draw_size.height(),
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           true, paint);
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Size ProportionalImageView::GetImageSizeForWidth(int width) {
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size size = visible() ? image_.size() : gfx::Size();
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (width > 0 && !size.IsEmpty()) {
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    double proportion = size.height() / (double) size.width();
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    size.SetSize(width, std::max(0.5 + width * proportion, 1.0));
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (size.height() > message_center::kNotificationMaximumImageHeight) {
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int height = message_center::kNotificationMaximumImageHeight;
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      size.SetSize(std::max(0.5 + height / proportion, 1.0), height);
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return size;
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// NotificationButton //////////////////////////////////////////////////////////
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// NotificationButtons render the action buttons of notifications.
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class NotificationButton : public views::CustomButton {
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NotificationButton(views::ButtonListener* listener);
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~NotificationButton();
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void SetIcon(const gfx::ImageSkia& icon);
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void SetTitle(const string16& title);
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Overridden from views::View:
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual gfx::Size GetPreferredSize() OVERRIDE;
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual int GetHeightForWidth(int width) OVERRIDE;
261c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void OnFocus() OVERRIDE;
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Overridden from views::CustomButton:
265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void StateChanged() OVERRIDE;
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::ImageView* icon_;
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::Label* title_;
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NotificationButton::NotificationButton(views::ButtonListener* listener)
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : views::CustomButton(listener),
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      icon_(NULL),
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      title_(NULL) {
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  set_focusable(true);
277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  set_request_focus_on_press(false);
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        kButtonHorizontalPadding,
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        kButtonVecticalPadding,
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        kButtonIconToTitlePadding));
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NotificationButton::~NotificationButton() {
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NotificationButton::SetIcon(const gfx::ImageSkia& image) {
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (icon_ != NULL)
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    delete icon_;  // This removes the icon from this view's children.
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (image.isNull()) {
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_ = NULL;
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_ = new views::ImageView();
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_->SetImageSize(gfx::Size(message_center::kNotificationButtonIconSize,
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  message_center::kNotificationButtonIconSize));
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_->SetImage(image);
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_->SetHorizontalAlignment(views::ImageView::LEADING);
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_->SetVerticalAlignment(views::ImageView::LEADING);
299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    icon_->set_border(MakeEmptyBorder(kButtonIconTopPadding, 0, 0, 0));
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    AddChildViewAt(icon_, 0);
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NotificationButton::SetTitle(const string16& title) {
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (title_ != NULL)
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    delete title_;  // This removes the title from this view's children.
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (title.empty()) {
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    title_ = NULL;
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    title_ = new views::Label(title);
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    title_->SetElideBehavior(views::Label::ELIDE_AT_END);
313c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    title_->SetEnabledColor(message_center::kRegularTextColor);
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    title_->SetBackgroundColor(kRegularTextBackgroundColor);
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    title_->set_border(MakeEmptyBorder(kButtonTitleTopPadding, 0, 0, 0));
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    AddChildView(title_);
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
318b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  SetAccessibleName(title);
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Size NotificationButton::GetPreferredSize() {
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Size(message_center::kNotificationWidth, kButtonHeight);
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int NotificationButton::GetHeightForWidth(int width) {
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return kButtonHeight;
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
329c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NotificationButton::OnFocus() {
330b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  views::CustomButton::OnFocus();
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ScrollRectToVisible(GetLocalBounds());
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NotificationButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (HasFocus() && (focusable() || IsAccessibilityFocusable())) {
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3),
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                     message_center::kFocusBorderColor);
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NotificationButton::StateChanged() {
342c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (state() == STATE_HOVERED || state() == STATE_PRESSED)
343c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    set_background(MakeBackground(kHoveredButtonBackgroundColor));
344c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  else
345c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    set_background(NULL);
346c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
347c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace message_center {
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// NotificationView ////////////////////////////////////////////////////////////
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)MessageView* NotificationView::Create(const Notification& notification,
356c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                      MessageCenter* message_center,
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      bool expanded) {
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Use MessageSimpleView for simple notifications unless rich style
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // notifications are enabled. This preserves the appearance of notifications
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // created by existing code that uses webkitNotifications.
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!IsRichNotificationEnabled() &&
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      notification.type() == NOTIFICATION_TYPE_SIMPLE)
363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return new MessageSimpleView(notification, message_center);
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (notification.type()) {
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case NOTIFICATION_TYPE_BASE_FORMAT:
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case NOTIFICATION_TYPE_IMAGE:
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case NOTIFICATION_TYPE_MULTIPLE:
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case NOTIFICATION_TYPE_SIMPLE:
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // If the caller asks for an unrecognized kind of view (entirely possible
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // if an application is running on an older version of this code that
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // doesn't have the requested kind of notification template), we'll fall
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // back to a notification instance that will provide at least basic
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // functionality.
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      LOG(WARNING) << "Unable to fulfill request for unrecognized "
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   << "notification type " << notification.type() << ". "
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   << "Falling back to simple notification type.";
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Currently all roads lead to the generic NotificationView.
383c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return new NotificationView(notification, message_center, expanded);
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NotificationView::NotificationView(const Notification& notification,
387c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                   MessageCenter* message_center,
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   bool expanded)
389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : MessageView(notification, message_center, expanded) {
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the opaque background that's above the view's shadow.
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  background_view_ = new views::View();
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  background_view_->set_background(MakeBackground());
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the top_view_, which collects into a vertical box all content
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // at the top of the notification (to the right of the icon) except for the
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // close button.
397c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  top_view_ = new views::View();
398c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  top_view_->SetLayoutManager(
399c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
400c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  top_view_->set_border(MakeEmptyBorder(
401c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      kTextTopPadding - 8, 0, kTextBottomPadding - 5, 0));
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the title view if appropriate.
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  title_view_ = NULL;
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!notification.title().empty()) {
406c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    gfx::Font font = views::Label().font().DeriveFont(2);
407c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int padding = kTitleLineHeight - font.GetHeight();
408c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    title_view_ = new BoundedLabel(
409c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ui::TruncateString(notification.title(), kTitleCharacterLimit), font);
410c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    title_view_->SetLineHeight(kTitleLineHeight);
411c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    title_view_->SetLineLimit(kTitleLineLimit);
412c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    title_view_->SetColors(message_center::kRegularTextColor,
413c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           kRegularTextBackgroundColor);
414c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    title_view_->set_border(MakeTextBorder(padding, 3, 0));
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    top_view_->AddChildView(title_view_);
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the message view if appropriate.
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message_view_ = NULL;
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!notification.message().empty()) {
421c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int padding = kMessageLineHeight - views::Label().font().GetHeight();
422c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    message_view_ = new BoundedLabel(
423c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ui::TruncateString(notification.message(), kMessageCharacterLimit));
424c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    message_view_->SetLineHeight(kMessageLineHeight);
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    message_view_->SetVisible(!is_expanded() || !notification.items().size());
426c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    message_view_->SetColors(kDimTextColor, kDimTextBackgroundColor);
427c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    message_view_->set_border(MakeTextBorder(padding, 4, 0));
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    top_view_->AddChildView(message_view_);
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the list item views (up to a maximum).
432c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int padding = kMessageLineHeight - views::Label().font().GetHeight();
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<NotificationItem> items = notification.items();
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < items.size() && i < kNotificationMaximumItems; ++i) {
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ItemView* item_view = new ItemView(items[i]);
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    item_view->SetVisible(is_expanded());
437c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    item_view->set_border(MakeTextBorder(padding, i ? 0 : 4, 0));
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    item_views_.push_back(item_view);
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    top_view_->AddChildView(item_view);
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the notification icon view.
443c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::ImageSkia icon = notification.icon().AsImageSkia();
444c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (notification.type() == NOTIFICATION_TYPE_SIMPLE &&
445c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      (icon.width() != kIconSize ||
446c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       icon.height() != kIconSize ||
447c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       HasAlpha(icon, GetWidget()))) {
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    views::ImageView* icon_view = new views::ImageView();
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    icon_view->SetImage(icon);
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_view->SetImageSize(gfx::Size(kLegacyIconSize, kLegacyIconSize));
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_view->SetHorizontalAlignment(views::ImageView::CENTER);
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_view->SetVerticalAlignment(views::ImageView::CENTER);
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_view->set_background(MakeBackground(kLegacyIconBackgroundColor));
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    icon_view_ = icon_view;
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
456c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    icon_view_ = new ProportionalImageView(icon);
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the bottom_view_, which collects into a vertical box all content
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // below the notification icon except for the expand button.
461c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bottom_view_ = new views::View();
462c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bottom_view_->SetLayoutManager(
463c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the image view if appropriate.
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  image_view_ = NULL;
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!notification.image().IsEmpty()) {
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    image_view_ = new ProportionalImageView(notification.image().AsImageSkia());
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    image_view_->SetVisible(is_expanded());
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bottom_view_->AddChildView(image_view_);
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create action buttons if appropriate.
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<ButtonInfo> buttons = notification.buttons();
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < buttons.size(); ++i) {
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    views::View* separator = new views::ImageView();
477c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    separator->set_border(MakeSeparatorBorder(1, 0, kButtonSeparatorColor));
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bottom_view_->AddChildView(separator);
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NotificationButton* button = new NotificationButton(this);
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ButtonInfo button_info = buttons[i];
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    button->SetTitle(button_info.title);
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    button->SetIcon(button_info.icon.AsImageSkia());
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    action_buttons_.push_back(button);
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bottom_view_->AddChildView(button);
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Put together the different content and control views. Layering those allows
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // for proper layout logic and it also allows the close and expand buttons to
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // overlap the content as needed to provide large enough click and touch areas
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // (<http://crbug.com/168822> and <http://crbug.com/168856>).
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(background_view_);
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(top_view_);
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(icon_view_);
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(bottom_view_);
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(close_button());
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(expand_button());
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NotificationView::~NotificationView() {
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Size NotificationView::GetPreferredSize() {
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int top_width = top_view_->GetPreferredSize().width();
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int bottom_width = bottom_view_->GetPreferredSize().width();
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int preferred_width = std::max(top_width, bottom_width) + GetInsets().width();
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Size(preferred_width, GetHeightForWidth(preferred_width));
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int NotificationView::GetHeightForWidth(int width) {
510c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Get the height assuming no line limit changes.
511c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int content_width = width - GetInsets().width();
512c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int top_height = top_view_->GetHeightForWidth(content_width);
513c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int bottom_height = bottom_view_->GetHeightForWidth(content_width);
514c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int content_height = std::max(top_height, kIconSize) + bottom_height;
515c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
516c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // <http://crbug.com/230448> Fix: Adjust the height when the message_view's
517c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // line limit would be different for the specified width than it currently is.
518c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TODO(dharcourt): Avoid BoxLayout and directly compute the correct height.
519c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (message_view_) {
520c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int used_limit = message_view_->GetLineLimit();
521c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int correct_limit = GetMessageLineLimit(width);
522c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (used_limit != correct_limit) {
523c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content_height -= GetMessageHeight(content_width, used_limit);
524c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content_height += GetMessageHeight(content_width, correct_limit);
525c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
526c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
527c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
528c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Adjust the height to make sure there is at least 16px of space below the
529c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // icon if there is any space there (<http://crbug.com/232966>).
530c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (content_height > kIconSize)
531c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content_height = std::max(content_height, kIconSize + kIconBottomPadding);
532c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
533c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return content_height + GetInsets().height();
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NotificationView::Layout() {
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Insets insets = GetInsets();
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int content_width = width() - insets.width();
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int content_right = width() - insets.right();
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
541c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Before any resizing, set or adjust the number of message lines.
542c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (message_view_)
543c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    message_view_->SetLineLimit(GetMessageLineLimit(width()));
544c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
545c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Background.
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  background_view_->SetBounds(insets.left(), insets.top(),
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              content_width, height() - insets.height());
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
549c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Top views.
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int top_height = top_view_->GetHeightForWidth(content_width);
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  top_view_->SetBounds(insets.left(), insets.top(), content_width, top_height);
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
553c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Icon.
554c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  icon_view_->SetBounds(insets.left(), insets.top(), kIconSize, kIconSize);
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
556c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Bottom views.
557c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int bottom_y = insets.top() + std::max(top_height, kIconSize);
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int bottom_height = bottom_view_->GetHeightForWidth(content_width);
5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bottom_view_->SetBounds(insets.left(), bottom_y,
5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          content_width, bottom_height);
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
562c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Close button.
5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size close_size(close_button()->GetPreferredSize());
5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  close_button()->SetBounds(content_right - close_size.width(), insets.top(),
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            close_size.width(), close_size.height());
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
567c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Expand button.
5682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size expand_size(expand_button()->GetPreferredSize());
5692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int expand_y = bottom_y - expand_size.height();
570c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  expand_button()->SetVisible(IsExpansionNeeded(width()));
5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  expand_button()->SetBounds(content_right - expand_size.width(), expand_y,
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             expand_size.width(), expand_size.height());
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
575c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NotificationView::ScrollRectToVisible(const gfx::Rect& rect) {
576c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Notification want to show the whole notification when a part of it (like
577c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // a button) gets focused.
578c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  views::View::ScrollRectToVisible(GetLocalBounds());
579c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
580c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
581a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)views::View* NotificationView::GetEventHandlerForPoint(
582a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    const gfx::Point& point) {
583a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  // Want to return this for underlying views, otherwise GetCursor is not
584a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  // called. But buttons are exceptions, they'll have their own event handlings.
585a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  std::vector<views::View*> buttons(action_buttons_);
586a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  buttons.push_back(close_button());
587a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  buttons.push_back(expand_button());
588a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
589a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  for (size_t i = 0; i < buttons.size(); ++i) {
590a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    gfx::Point point_in_child = point;
591a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    ConvertPointToTarget(this, buttons[i], &point_in_child);
592a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if (buttons[i]->HitTestPoint(point_in_child))
593a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      return buttons[i]->GetEventHandlerForPoint(point_in_child);
594a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  }
595a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
596a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return this;
597a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}
598a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
599a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)gfx::NativeCursor NotificationView::GetCursor(const ui::MouseEvent& event) {
600a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#if defined(USE_AURA)
601a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return ui::kCursorHand;
602a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#elif defined(OS_WIN)
603a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_HAND);
604a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return g_hand_cursor;
605a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#endif
606a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}
607a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
6082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NotificationView::ButtonPressed(views::Button* sender,
6092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     const ui::Event& event) {
6102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See if the button pressed was an action button.
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < action_buttons_.size(); ++i) {
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (sender == action_buttons_[i]) {
613c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      message_center()->ClickOnNotificationButton(notification_id(), i);
6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
6152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Let the superclass handled anything other than action buttons.
6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MessageView::ButtonPressed(sender, event);
6202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
621c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Adjust notification subviews for expansion.
6222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (sender == expand_button()) {
6232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (message_view_ && item_views_.size())
6242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      message_view_->SetVisible(false);
6252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (size_t i = 0; i < item_views_.size(); ++i)
6262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      item_views_[i]->SetVisible(true);
6272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_view_)
6282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      image_view_->SetVisible(true);
6292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    expand_button()->SetVisible(false);
6302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    PreferredSizeChanged();
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SchedulePaint();
6322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
635c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool NotificationView::IsExpansionNeeded(int width) {
636c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return (!is_expanded() &&
637c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              (image_view_ ||
638c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  item_views_.size() ||
639c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  IsMessageExpansionNeeded(width)));
640c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
641c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
642c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool NotificationView::IsMessageExpansionNeeded(int width) {
643c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int current = GetMessageLines(width, GetMessageLineLimit(width));
644c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int expanded = GetMessageLines(width, kMessageExpandedLineLimit);
645c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return current < expanded;
646c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
647c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
648c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)int NotificationView::GetMessageLineLimit(int width) {
649c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Expanded notifications get a larger limit, except for image notifications,
650c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // whose images must be kept flush against their icons.
651c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (is_expanded() && !image_view_)
652c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return kMessageExpandedLineLimit;
653c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
654c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If there's a title ensure title + message lines <= collapsed line limit.
655c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (title_view_) {
656c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int title_lines = title_view_->GetLinesForWidthAndLimit(width, -1);
657c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return std::max(kMessageCollapsedLineLimit - title_lines, 0);
658c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
659c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
660c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return kMessageCollapsedLineLimit;
661c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
662c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
663c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)int NotificationView::GetMessageLines(int width, int limit) {
664c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return message_view_ ?
665c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         message_view_->GetLinesForWidthAndLimit(width, limit) : 0;
666c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
6672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
668c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)int NotificationView::GetMessageHeight(int width, int limit) {
669c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return message_view_ ?
670c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         message_view_->GetSizeForWidthAndLines(width, limit).height() : 0;
6712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace message_center
674