1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/setting_level_bubble.h"
6
7#include <gdk/gdk.h>
8
9#include "base/timer.h"
10#include "chrome/browser/chromeos/login/background_view.h"
11#include "chrome/browser/chromeos/login/login_utils.h"
12#include "chrome/browser/chromeos/setting_level_bubble_view.h"
13#include "chrome/browser/profiles/profile_manager.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/browser/ui/browser_list.h"
16#include "chrome/browser/ui/browser_window.h"
17#include "chrome/browser/ui/views/bubble/bubble.h"
18#include "views/widget/root_view.h"
19
20namespace {
21
22const int kBubbleShowTimeoutSec = 2;
23const int kAnimationDurationMs = 200;
24
25// Horizontal relative position: 0 - leftmost, 0.5 - center, 1 - rightmost.
26const double kBubbleXRatio = 0.5;
27
28// Vertical gap from the bottom of the screen in pixels.
29const int kBubbleBottomGap = 30;
30
31int LimitPercent(int percent) {
32  if (percent < 0)
33    percent = 0;
34  else if (percent > 100)
35    percent = 100;
36  return percent;
37}
38
39}  // namespace
40
41namespace chromeos {
42
43// Temporary helper routine. Tries to first return the widget from the
44// most-recently-focused normal browser window, then from a login
45// background, and finally NULL if both of those fail.
46// TODO(glotov): remove this in favor of enabling Bubble class act
47// without |parent| specified. crosbug.com/4025
48static views::Widget* GetToplevelWidget() {
49  GtkWindow* window = NULL;
50
51  // We just use the default profile here -- this gets overridden as needed
52  // in Chrome OS depending on whether the user is logged in or not.
53  Browser* browser =
54      BrowserList::FindBrowserWithType(
55          ProfileManager::GetDefaultProfile(),
56          Browser::TYPE_NORMAL,
57          true);  // match_incognito
58  if (browser) {
59    window = GTK_WINDOW(browser->window()->GetNativeHandle());
60  } else {
61    // Otherwise, see if there's a background window that we can use.
62    BackgroundView* background = LoginUtils::Get()->GetBackgroundView();
63    if (background)
64      window = GTK_WINDOW(background->GetNativeWindow());
65  }
66
67  if (!window)
68    return NULL;
69
70  views::NativeWidget* native_widget =
71      views::NativeWidget::GetNativeWidgetForNativeWindow(window);
72  return native_widget->GetWidget();
73}
74
75SettingLevelBubble::SettingLevelBubble(SkBitmap* increase_icon,
76                                       SkBitmap* decrease_icon,
77                                       SkBitmap* zero_icon)
78    : previous_percent_(-1),
79      current_percent_(-1),
80      increase_icon_(increase_icon),
81      decrease_icon_(decrease_icon),
82      zero_icon_(zero_icon),
83      bubble_(NULL),
84      view_(NULL),
85      animation_(this) {
86  animation_.SetSlideDuration(kAnimationDurationMs);
87  animation_.SetTweenType(ui::Tween::LINEAR);
88}
89
90void SettingLevelBubble::ShowBubble(int percent) {
91  percent = LimitPercent(percent);
92  if (previous_percent_ == -1)
93    previous_percent_ = percent;
94  current_percent_ = percent;
95
96  SkBitmap* icon = increase_icon_;
97  if (current_percent_ == 0)
98    icon = zero_icon_;
99  else if (current_percent_ < previous_percent_)
100    icon = decrease_icon_;
101
102  if (!bubble_) {
103    views::Widget* widget = GetToplevelWidget();
104    if (widget == NULL)
105      return;
106    DCHECK(view_ == NULL);
107    view_ = new SettingLevelBubbleView;
108    view_->Init(icon, previous_percent_);
109    // Calculate position of the bubble.
110    gfx::Rect bounds = widget->GetClientAreaScreenBounds();
111    const gfx::Size view_size = view_->GetPreferredSize();
112    // Note that (x, y) is the point of the center of the bubble.
113    const int x = view_size.width() / 2 +
114        kBubbleXRatio * (bounds.width() - view_size.width());
115    const int y = bounds.height() - view_size.height() / 2 - kBubbleBottomGap;
116    bubble_ = Bubble::ShowFocusless(widget,  // parent
117                                    gfx::Rect(x, y, 0, 20),
118                                    BubbleBorder::FLOAT,
119                                    view_,  // contents
120                                    this,   // delegate
121                                    true);  // show while screen is locked
122  } else {
123    DCHECK(view_);
124    timeout_timer_.Stop();
125    view_->SetIcon(icon);
126  }
127  if (animation_.is_animating())
128    animation_.End();
129  animation_.Reset();
130  animation_.Show();
131  timeout_timer_.Start(base::TimeDelta::FromSeconds(kBubbleShowTimeoutSec),
132                       this, &SettingLevelBubble::OnTimeout);
133}
134
135void SettingLevelBubble::HideBubble() {
136  if (bubble_)
137    bubble_->Close();
138}
139
140void SettingLevelBubble::UpdateWithoutShowingBubble(int percent) {
141  percent = LimitPercent(percent);
142
143  previous_percent_ =
144      animation_.is_animating() ?
145      animation_.GetCurrentValue() :
146      current_percent_;
147  if (previous_percent_ < 0)
148    previous_percent_ = percent;
149  current_percent_ = percent;
150
151  if (animation_.is_animating())
152    animation_.End();
153  animation_.Reset();
154  animation_.Show();
155}
156
157void SettingLevelBubble::OnTimeout() {
158  HideBubble();
159}
160
161void SettingLevelBubble::BubbleClosing(Bubble* bubble, bool) {
162  DCHECK(bubble == bubble_);
163  timeout_timer_.Stop();
164  animation_.Stop();
165  bubble_ = NULL;
166  view_ = NULL;
167}
168
169void SettingLevelBubble::AnimationEnded(const ui::Animation* animation) {
170  previous_percent_ = current_percent_;
171}
172
173void SettingLevelBubble::AnimationProgressed(const ui::Animation* animation) {
174  if (view_) {
175    view_->Update(
176        ui::Tween::ValueBetween(animation->GetCurrentValue(),
177                                previous_percent_,
178                                current_percent_));
179  }
180}
181
182}  // namespace chromeos
183