power_menu_button.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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 "app/l10n_util.h"
8#include "app/resource_bundle.h"
9#include "base/string_number_conversions.h"
10#include "base/time.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/chromeos/cros/cros_library.h"
13#include "gfx/canvas.h"
14#include "grit/generated_resources.h"
15#include "grit/theme_resources.h"
16
17namespace chromeos {
18
19////////////////////////////////////////////////////////////////////////////////
20// PowerMenuButton
21
22// static
23const int PowerMenuButton::kNumPowerImages = 16;
24
25PowerMenuButton::PowerMenuButton()
26    : StatusAreaButton(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, menus::MenuModel implementation:
43
44int PowerMenuButton::GetItemCount() const {
45  return 2;
46}
47
48menus::MenuModel::ItemType PowerMenuButton::GetTypeAt(int index) const {
49  return menus::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::ViewMenuDelegate implementation:
96
97void PowerMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
98  power_menu_.Rebuild();
99  power_menu_.UpdateStates();
100  power_menu_.RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
101}
102
103////////////////////////////////////////////////////////////////////////////////
104// PowerMenuButton, PowerLibrary::Observer implementation:
105
106void PowerMenuButton::PowerChanged(PowerLibrary* obj) {
107  UpdateIconAndLabelInfo();
108}
109
110////////////////////////////////////////////////////////////////////////////////
111// PowerMenuButton, StatusAreaButton implementation:
112
113void PowerMenuButton::UpdateIconAndLabelInfo() {
114  PowerLibrary* cros = CrosLibrary::Get()->GetPowerLibrary();
115  if (!cros)
116    return;
117
118  bool cros_loaded = CrosLibrary::Get()->EnsureLoaded();
119  if (cros_loaded) {
120    battery_is_present_ = cros->battery_is_present();
121    line_power_on_ = cros->line_power_on();
122    battery_fully_charged_ = cros->battery_fully_charged();
123    battery_percentage_ = cros->battery_percentage();
124    // If fully charged, always show 100% even if internal number is a bit less.
125    // Note: we always call cros->battery_percentage() for test predictability.
126    if (battery_fully_charged_)
127      battery_percentage_ = 100.0;
128    battery_time_to_full_ = cros->battery_time_to_full();
129    battery_time_to_empty_ = cros->battery_time_to_empty();
130  }
131
132  if (!cros_loaded) {
133    icon_id_ = IDR_STATUSBAR_BATTERY_UNKNOWN;
134  } else if (!battery_is_present_) {
135    icon_id_ = IDR_STATUSBAR_BATTERY_MISSING;
136  } else if (line_power_on_ && battery_fully_charged_) {
137    icon_id_ = IDR_STATUSBAR_BATTERY_CHARGED;
138  } else {
139    // Get the power image depending on battery percentage. Percentage is
140    // from 0 to 100, so we need to convert that to 0 to kNumPowerImages - 1.
141    // NOTE: Use an array rather than just calculating a resource number to
142    // avoid creating implicit ordering dependencies on the resource values.
143    static const int kChargingImages[kNumPowerImages] = {
144      IDR_STATUSBAR_BATTERY_CHARGING_1,
145      IDR_STATUSBAR_BATTERY_CHARGING_2,
146      IDR_STATUSBAR_BATTERY_CHARGING_3,
147      IDR_STATUSBAR_BATTERY_CHARGING_4,
148      IDR_STATUSBAR_BATTERY_CHARGING_5,
149      IDR_STATUSBAR_BATTERY_CHARGING_6,
150      IDR_STATUSBAR_BATTERY_CHARGING_7,
151      IDR_STATUSBAR_BATTERY_CHARGING_8,
152      IDR_STATUSBAR_BATTERY_CHARGING_9,
153      IDR_STATUSBAR_BATTERY_CHARGING_10,
154      IDR_STATUSBAR_BATTERY_CHARGING_11,
155      IDR_STATUSBAR_BATTERY_CHARGING_12,
156      IDR_STATUSBAR_BATTERY_CHARGING_13,
157      IDR_STATUSBAR_BATTERY_CHARGING_14,
158      IDR_STATUSBAR_BATTERY_CHARGING_15,
159      IDR_STATUSBAR_BATTERY_CHARGING_16,
160    };
161    static const int kDischargingImages[kNumPowerImages] = {
162      IDR_STATUSBAR_BATTERY_DISCHARGING_1,
163      IDR_STATUSBAR_BATTERY_DISCHARGING_2,
164      IDR_STATUSBAR_BATTERY_DISCHARGING_3,
165      IDR_STATUSBAR_BATTERY_DISCHARGING_4,
166      IDR_STATUSBAR_BATTERY_DISCHARGING_5,
167      IDR_STATUSBAR_BATTERY_DISCHARGING_6,
168      IDR_STATUSBAR_BATTERY_DISCHARGING_7,
169      IDR_STATUSBAR_BATTERY_DISCHARGING_8,
170      IDR_STATUSBAR_BATTERY_DISCHARGING_9,
171      IDR_STATUSBAR_BATTERY_DISCHARGING_10,
172      IDR_STATUSBAR_BATTERY_DISCHARGING_11,
173      IDR_STATUSBAR_BATTERY_DISCHARGING_12,
174      IDR_STATUSBAR_BATTERY_DISCHARGING_13,
175      IDR_STATUSBAR_BATTERY_DISCHARGING_14,
176      IDR_STATUSBAR_BATTERY_DISCHARGING_15,
177      IDR_STATUSBAR_BATTERY_DISCHARGING_16,
178    };
179
180    int index = static_cast<int>(battery_percentage_ / 100.0 *
181                nextafter(static_cast<float>(kNumPowerImages), 0));
182    index = std::max(std::min(index, kNumPowerImages - 1), 0);
183    icon_id_ = line_power_on_ ?
184        kChargingImages[index] : kDischargingImages[index];
185  }
186
187  SetIcon(*ResourceBundle::GetSharedInstance().GetBitmapNamed(icon_id_));
188  SetTooltipText(UTF16ToWide(GetLabelAt(0)));
189  SchedulePaint();
190}
191
192}  // namespace chromeos
193