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/status/power_menu_button.h"
6
7#include "base/string_number_conversions.h"
8#include "base/time.h"
9#include "base/utf_string_conversions.h"
10#include "chrome/browser/chromeos/cros/cros_library.h"
11#include "grit/generated_resources.h"
12#include "grit/theme_resources.h"
13#include "ui/base/l10n/l10n_util.h"
14#include "ui/base/resource/resource_bundle.h"
15#include "ui/gfx/canvas.h"
16
17namespace chromeos {
18
19////////////////////////////////////////////////////////////////////////////////
20// PowerMenuButton
21
22// static
23const int PowerMenuButton::kNumPowerImages = 19;
24
25PowerMenuButton::PowerMenuButton(StatusAreaHost* host)
26    : StatusAreaButton(host, this),
27      battery_is_present_(false),
28      line_power_on_(false),
29      battery_fully_charged_(false),
30      battery_percentage_(0.0),
31      icon_id_(-1),
32      ALLOW_THIS_IN_INITIALIZER_LIST(power_menu_(this)) {
33  UpdateIconAndLabelInfo();
34  CrosLibrary::Get()->GetPowerLibrary()->AddObserver(this);
35}
36
37PowerMenuButton::~PowerMenuButton() {
38  CrosLibrary::Get()->GetPowerLibrary()->RemoveObserver(this);
39}
40
41////////////////////////////////////////////////////////////////////////////////
42// PowerMenuButton, ui::MenuModel implementation:
43
44int PowerMenuButton::GetItemCount() const {
45  return 2;
46}
47
48ui::MenuModel::ItemType PowerMenuButton::GetTypeAt(int index) const {
49  return ui::MenuModel::TYPE_COMMAND;
50}
51
52string16 PowerMenuButton::GetLabelAt(int index) const {
53  // The first item shows the percentage of battery left.
54  if (index == 0) {
55    return l10n_util::GetStringFUTF16(IDS_STATUSBAR_BATTERY_PERCENTAGE,
56        base::IntToString16(static_cast<int>(battery_percentage_)));
57  } else if (index == 1) {
58    // The second item shows the battery is charged if it is.
59    if (battery_fully_charged_)
60      return l10n_util::GetStringUTF16(IDS_STATUSBAR_BATTERY_IS_CHARGED);
61
62    // If battery is in an intermediate charge state, show how much time left.
63    base::TimeDelta time = line_power_on_ ? battery_time_to_full_ :
64        battery_time_to_empty_;
65    if (time.InSeconds() == 0) {
66      // If time is 0, then that means we are still calculating how much time.
67      // Depending if line power is on, we either show a message saying that we
68      // are calculating time until full or calculating remaining time.
69      int msg = line_power_on_ ?
70          IDS_STATUSBAR_BATTERY_CALCULATING_TIME_UNTIL_FULL :
71          IDS_STATUSBAR_BATTERY_CALCULATING_TIME_UNTIL_EMPTY;
72      return l10n_util::GetStringUTF16(msg);
73    } else {
74      // Depending if line power is on, we either show a message saying XX:YY
75      // until full or XX:YY remaining where XX is number of hours and YY is
76      // number of minutes.
77      int msg = line_power_on_ ? IDS_STATUSBAR_BATTERY_TIME_UNTIL_FULL :
78          IDS_STATUSBAR_BATTERY_TIME_UNTIL_EMPTY;
79      int hour = time.InHours();
80      int min = (time - base::TimeDelta::FromHours(hour)).InMinutes();
81      string16 hour_str = base::IntToString16(hour);
82      string16 min_str = base::IntToString16(min);
83      // Append a "0" before the minute if it's only a single digit.
84      if (min < 10)
85        min_str = ASCIIToUTF16("0") + min_str;
86      return l10n_util::GetStringFUTF16(msg, hour_str, min_str);
87    }
88  } else {
89    NOTREACHED();
90    return string16();
91  }
92}
93
94////////////////////////////////////////////////////////////////////////////////
95// PowerMenuButton, views::View implementation:
96void PowerMenuButton::OnLocaleChanged() {
97  UpdateIconAndLabelInfo();
98}
99
100////////////////////////////////////////////////////////////////////////////////
101// PowerMenuButton, views::ViewMenuDelegate implementation:
102
103void PowerMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
104  power_menu_.Rebuild();
105  power_menu_.RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
106}
107
108////////////////////////////////////////////////////////////////////////////////
109// PowerMenuButton, PowerLibrary::Observer implementation:
110
111void PowerMenuButton::PowerChanged(PowerLibrary* obj) {
112  UpdateIconAndLabelInfo();
113}
114
115////////////////////////////////////////////////////////////////////////////////
116// PowerMenuButton, StatusAreaButton implementation:
117
118void PowerMenuButton::UpdateIconAndLabelInfo() {
119  PowerLibrary* cros = CrosLibrary::Get()->GetPowerLibrary();
120  if (!cros)
121    return;
122
123  bool cros_loaded = CrosLibrary::Get()->EnsureLoaded();
124  if (cros_loaded) {
125    battery_is_present_ = cros->battery_is_present();
126    line_power_on_ = cros->line_power_on();
127    battery_fully_charged_ = cros->battery_fully_charged();
128    battery_percentage_ = cros->battery_percentage();
129    // If fully charged, always show 100% even if internal number is a bit less.
130    // Note: we always call cros->battery_percentage() for test predictability.
131    if (battery_fully_charged_)
132      battery_percentage_ = 100.0;
133    battery_time_to_full_ = cros->battery_time_to_full();
134    battery_time_to_empty_ = cros->battery_time_to_empty();
135  }
136
137  if (!cros_loaded) {
138    icon_id_ = IDR_STATUSBAR_BATTERY_UNKNOWN;
139  } else if (!battery_is_present_) {
140    icon_id_ = IDR_STATUSBAR_BATTERY_MISSING;
141  } else if (line_power_on_ && battery_fully_charged_) {
142    icon_id_ = IDR_STATUSBAR_BATTERY_CHARGED;
143  } else {
144    // Get the power image depending on battery percentage. Percentage is
145    // from 0 to 100, so we need to convert that to 0 to kNumPowerImages - 1.
146    // NOTE: Use an array rather than just calculating a resource number to
147    // avoid creating implicit ordering dependencies on the resource values.
148    static const int kChargingImages[kNumPowerImages] = {
149      IDR_STATUSBAR_BATTERY_CHARGING_1,
150      IDR_STATUSBAR_BATTERY_CHARGING_2,
151      IDR_STATUSBAR_BATTERY_CHARGING_3,
152      IDR_STATUSBAR_BATTERY_CHARGING_4,
153      IDR_STATUSBAR_BATTERY_CHARGING_5,
154      IDR_STATUSBAR_BATTERY_CHARGING_6,
155      IDR_STATUSBAR_BATTERY_CHARGING_7,
156      IDR_STATUSBAR_BATTERY_CHARGING_8,
157      IDR_STATUSBAR_BATTERY_CHARGING_9,
158      IDR_STATUSBAR_BATTERY_CHARGING_10,
159      IDR_STATUSBAR_BATTERY_CHARGING_11,
160      IDR_STATUSBAR_BATTERY_CHARGING_12,
161      IDR_STATUSBAR_BATTERY_CHARGING_13,
162      IDR_STATUSBAR_BATTERY_CHARGING_14,
163      IDR_STATUSBAR_BATTERY_CHARGING_15,
164      IDR_STATUSBAR_BATTERY_CHARGING_16,
165      IDR_STATUSBAR_BATTERY_CHARGING_17,
166      IDR_STATUSBAR_BATTERY_CHARGING_18,
167      IDR_STATUSBAR_BATTERY_CHARGING_19,
168    };
169    static const int kDischargingImages[kNumPowerImages] = {
170      IDR_STATUSBAR_BATTERY_DISCHARGING_1,
171      IDR_STATUSBAR_BATTERY_DISCHARGING_2,
172      IDR_STATUSBAR_BATTERY_DISCHARGING_3,
173      IDR_STATUSBAR_BATTERY_DISCHARGING_4,
174      IDR_STATUSBAR_BATTERY_DISCHARGING_5,
175      IDR_STATUSBAR_BATTERY_DISCHARGING_6,
176      IDR_STATUSBAR_BATTERY_DISCHARGING_7,
177      IDR_STATUSBAR_BATTERY_DISCHARGING_8,
178      IDR_STATUSBAR_BATTERY_DISCHARGING_9,
179      IDR_STATUSBAR_BATTERY_DISCHARGING_10,
180      IDR_STATUSBAR_BATTERY_DISCHARGING_11,
181      IDR_STATUSBAR_BATTERY_DISCHARGING_12,
182      IDR_STATUSBAR_BATTERY_DISCHARGING_13,
183      IDR_STATUSBAR_BATTERY_DISCHARGING_14,
184      IDR_STATUSBAR_BATTERY_DISCHARGING_15,
185      IDR_STATUSBAR_BATTERY_DISCHARGING_16,
186      IDR_STATUSBAR_BATTERY_DISCHARGING_17,
187      IDR_STATUSBAR_BATTERY_DISCHARGING_18,
188      IDR_STATUSBAR_BATTERY_DISCHARGING_19,
189    };
190
191    int index = static_cast<int>(battery_percentage_ / 100.0 *
192                nextafter(static_cast<float>(kNumPowerImages), 0));
193    index = std::max(std::min(index, kNumPowerImages - 1), 0);
194    icon_id_ = line_power_on_ ?
195        kChargingImages[index] : kDischargingImages[index];
196  }
197
198  SetIcon(*ResourceBundle::GetSharedInstance().GetBitmapNamed(icon_id_));
199  SetTooltipText(UTF16ToWide(GetLabelAt(0)));
200  power_menu_.Rebuild();
201  SchedulePaint();
202}
203
204}  // namespace chromeos
205