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