1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian 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
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/status/clock_menu_button.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/i18n/time_formatting.h"
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/time.h"
103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/cros/cros_library.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/status/status_area_host.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/prefs/pref_service.h"
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/profiles/profile.h"
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/pref_names.h"
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h"
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
2072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas.h"
2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/font.h"
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "unicode/datefmt.h"
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace chromeos {
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Amount of slop to add into the timer to make sure we're into the next minute
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// when the timer goes off.
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kTimerSlopSeconds = 1;
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochClockMenuButton::ClockMenuButton(StatusAreaHost* host)
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    : StatusAreaButton(host, this) {
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Add as SystemAccess observer. We update the clock if timezone changes.
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  SystemAccess::GetInstance()->AddObserver(this);
34dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CrosLibrary::Get()->GetPowerLibrary()->AddObserver(this);
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Start monitoring the kUse24HourClock preference.
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (host->GetProfile()) {  // This can be NULL in the login screen.
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    registrar_.Init(host->GetProfile()->GetPrefs());
38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    registrar_.Add(prefs::kUse24HourClock, this);
39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateTextAndSetNextTimer();
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochClockMenuButton::~ClockMenuButton() {
45dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CrosLibrary::Get()->GetPowerLibrary()->RemoveObserver(this);
46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  SystemAccess::GetInstance()->RemoveObserver(this);
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ClockMenuButton::UpdateTextAndSetNextTimer() {
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateText();
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Try to set the timer to go off at the next change of the minute. We don't
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // want to have the timer go off more than necessary since that will cause
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the CPU to wake up and consume power.
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  base::Time now = base::Time::Now();
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  base::Time::Exploded exploded;
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  now.LocalExplode(&exploded);
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Often this will be called at minute boundaries, and we'll actually want
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // 60 seconds from now.
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int seconds_left = 60 - exploded.second;
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (seconds_left == 0)
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    seconds_left = 60;
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure that the timer fires on the next minute. Without this, if it is
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // called just a teeny bit early, then it will skip the next minute.
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  seconds_left += kTimerSlopSeconds;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  timer_.Start(base::TimeDelta::FromSeconds(seconds_left), this,
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch               &ClockMenuButton::UpdateTextAndSetNextTimer);
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ClockMenuButton::UpdateText() {
743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  base::Time time(base::Time::Now());
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // If the profie is present, check the use 24-hour clock preference.
76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const bool use_24hour_clock =
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      host_->GetProfile() &&
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      host_->GetProfile()->GetPrefs()->GetBoolean(prefs::kUse24HourClock);
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (use_24hour_clock) {
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    SetText(UTF16ToWide(base::TimeFormatTimeOfDayWithHourClockType(
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        time, base::k24HourClock)));
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } else {
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Remove the am/pm field if it's present.
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    scoped_ptr<icu::DateFormat> formatter(
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        icu::DateFormat::createTimeInstance(icu::DateFormat::kShort));
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    icu::UnicodeString time_string;
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    icu::FieldPosition ampm_field(icu::DateFormat::kAmPmField);
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    formatter->format(
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        static_cast<UDate>(time.ToDoubleT() * 1000), time_string, ampm_field);
90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    int ampm_length = ampm_field.getEndIndex() - ampm_field.getBeginIndex();
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (ampm_length) {
92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      int begin = ampm_field.getBeginIndex();
93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Doesn't include any spacing before the field.
94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (begin)
95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        begin--;
96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      time_string.removeBetween(begin, ampm_field.getEndIndex());
97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    string16 time_string16 =
99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        string16(time_string.getBuffer(),
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                 static_cast<size_t>(time_string.length()));
101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    SetText(UTF16ToWide(time_string16));
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
10321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  SetTooltipText(UTF16ToWide(base::TimeFormatShortDate(time)));
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SchedulePaint();
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// ClockMenuButton, NotificationObserver implementation:
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ClockMenuButton::Observe(NotificationType type,
111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              const NotificationSource& source,
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              const NotificationDetails& details) {
113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (type == NotificationType::PREF_CHANGED) {
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    std::string* pref_name = Details<std::string>(details).ptr();
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (*pref_name == prefs::kUse24HourClock) {
116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      UpdateText();
117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen////////////////////////////////////////////////////////////////////////////////
12372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// ClockMenuButton, ui::MenuModel implementation:
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint ClockMenuButton::GetItemCount() const {
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If options dialog is unavailable, don't count a separator and configure
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // menu item.
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return host_->ShouldOpenButtonOptions(this) ? 3 : 1;
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenui::MenuModel::ItemType ClockMenuButton::GetTypeAt(int index) const {
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // There's a separator between the current date and the menu item to open
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the options menu.
13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return index == 1 ? ui::MenuModel::TYPE_SEPARATOR:
13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      ui::MenuModel::TYPE_COMMAND;
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring16 ClockMenuButton::GetLabelAt(int index) const {
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index == 0)
14021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return base::TimeFormatFriendlyDate(base::Time::Now());
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return l10n_util::GetStringUTF16(IDS_STATUSBAR_CLOCK_OPEN_OPTIONS_DIALOG);
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool ClockMenuButton::IsEnabledAt(int index) const {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The 1st item is the current date, which is disabled.
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return index != 0;
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ClockMenuButton::ActivatedAt(int index) {
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host_->OpenButtonOptions(this);
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// ClockMenuButton, PowerLibrary::Observer implementation:
155dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
156dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid ClockMenuButton::SystemResumed() {
157dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  UpdateText();
158dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
159dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
160dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen///////////////////////////////////////////////////////////////////////////////
161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// ClockMenuButton, SystemAccess::Observer implementation:
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ClockMenuButton::TimezoneChanged(const icu::TimeZone& timezone) {
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateText();
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// ClockMenuButton, views::ViewMenuDelegate implementation:
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ClockMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!clock_menu_.get())
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    clock_menu_.reset(new views::Menu2(this));
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    clock_menu_->Rebuild();
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  clock_menu_->UpdateStates();
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  clock_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen////////////////////////////////////////////////////////////////////////////////
180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// ClockMenuButton, views::View implementation:
181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ClockMenuButton::OnLocaleChanged() {
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  UpdateText();
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace chromeos
187