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