1 // Copyright (c) 2012 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.
5 #include "ash/system/date/date_view.h"
8 #include "ash/system/tray/system_tray_delegate.h"
9 #include "ash/system/tray/tray_constants.h"
10 #include "ash/system/tray/tray_utils.h"
11 #include "base/i18n/time_formatting.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "grit/ash_strings.h"
15 #include "third_party/icu/source/i18n/unicode/datefmt.h"
16 #include "third_party/icu/source/i18n/unicode/dtptngen.h"
17 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/views/controls/label.h"
20 #include "ui/views/layout/box_layout.h"
21 #include "ui/views/layout/grid_layout.h"
22 #include "ui/views/widget/widget.h"
30 // Amount of slop to add into the timer to make sure we're into the next minute
31 // when the timer goes off.
32 const int kTimerSlopSeconds
= 1;
34 // Top number text color of vertical clock.
35 const SkColor kVerticalClockHourColor
= SkColorSetRGB(0xBA, 0xBA, 0xBA);
37 base::string16
FormatDate(const base::Time
& time
) {
38 icu::UnicodeString date_string
;
39 scoped_ptr
<icu::DateFormat
> formatter(
40 icu::DateFormat::createDateInstance(icu::DateFormat::kMedium
));
41 formatter
->format(static_cast<UDate
>(time
.ToDoubleT() * 1000), date_string
);
42 return base::string16(date_string
.getBuffer(),
43 static_cast<size_t>(date_string
.length()));
46 base::string16
FormatDayOfWeek(const base::Time
& time
) {
47 UErrorCode status
= U_ZERO_ERROR
;
48 scoped_ptr
<icu::DateTimePatternGenerator
> generator(
49 icu::DateTimePatternGenerator::createInstance(status
));
50 DCHECK(U_SUCCESS(status
));
51 const char kBasePattern
[] = "EEE";
52 icu::UnicodeString generated_pattern
=
53 generator
->getBestPattern(icu::UnicodeString(kBasePattern
), status
);
54 DCHECK(U_SUCCESS(status
));
55 icu::SimpleDateFormat
simple_formatter(generated_pattern
, status
);
56 DCHECK(U_SUCCESS(status
));
57 icu::UnicodeString date_string
;
58 simple_formatter
.format(
59 static_cast<UDate
>(time
.ToDoubleT() * 1000), date_string
, status
);
60 DCHECK(U_SUCCESS(status
));
61 return base::string16(
62 date_string
.getBuffer(), static_cast<size_t>(date_string
.length()));
65 views::Label
* CreateLabel() {
66 views::Label
* label
= new views::Label
;
67 label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
68 label
->SetBackgroundColor(SkColorSetARGB(0, 255, 255, 255));
74 BaseDateTimeView::~BaseDateTimeView() {
78 void BaseDateTimeView::UpdateText() {
79 base::Time now
= base::Time::Now();
80 UpdateTextInternal(now
);
85 BaseDateTimeView::BaseDateTimeView() {
86 SetTimer(base::Time::Now());
89 void BaseDateTimeView::SetTimer(const base::Time
& now
) {
90 // Try to set the timer to go off at the next change of the minute. We don't
91 // want to have the timer go off more than necessary since that will cause
92 // the CPU to wake up and consume power.
93 base::Time::Exploded exploded
;
94 now
.LocalExplode(&exploded
);
96 // Often this will be called at minute boundaries, and we'll actually want
97 // 60 seconds from now.
98 int seconds_left
= 60 - exploded
.second
;
99 if (seconds_left
== 0)
102 // Make sure that the timer fires on the next minute. Without this, if it is
103 // called just a teeny bit early, then it will skip the next minute.
104 seconds_left
+= kTimerSlopSeconds
;
108 FROM_HERE
, base::TimeDelta::FromSeconds(seconds_left
),
109 this, &BaseDateTimeView::UpdateText
);
112 void BaseDateTimeView::ChildPreferredSizeChanged(views::View
* child
) {
113 PreferredSizeChanged();
116 void BaseDateTimeView::OnLocaleChanged() {
120 DateView::DateView() : actionable_(false) {
122 new views::BoxLayout(
123 views::BoxLayout::kVertical
, 0, 0, 0));
124 date_label_
= CreateLabel();
125 date_label_
->SetEnabledColor(kHeaderTextColorNormal
);
126 UpdateTextInternal(base::Time::Now());
127 AddChildView(date_label_
);
128 set_focusable(actionable_
);
131 DateView::~DateView() {
134 void DateView::SetActionable(bool actionable
) {
135 actionable_
= actionable
;
136 set_focusable(actionable_
);
139 void DateView::UpdateTextInternal(const base::Time
& now
) {
141 base::TimeFormatFriendlyDate(now
) +
143 base::TimeFormatTimeOfDayWithHourClockType(
144 now
, base::k12HourClock
, base:: kKeepAmPm
));
145 date_label_
->SetText(
146 l10n_util::GetStringFUTF16(
147 IDS_ASH_STATUS_TRAY_DATE
, FormatDayOfWeek(now
), FormatDate(now
)));
150 bool DateView::PerformAction(const ui::Event
& event
) {
154 ash::Shell::GetInstance()->system_tray_delegate()->ShowDateSettings();
158 void DateView::OnMouseEntered(const ui::MouseEvent
& event
) {
161 date_label_
->SetEnabledColor(kHeaderTextColorHover
);
165 void DateView::OnMouseExited(const ui::MouseEvent
& event
) {
168 date_label_
->SetEnabledColor(kHeaderTextColorNormal
);
172 TimeView::TimeView(TrayDate::ClockLayout clock_layout
)
173 : hour_type_(ash::Shell::GetInstance()->system_tray_delegate()->
174 GetHourClockType()) {
176 UpdateTextInternal(base::Time::Now());
177 UpdateClockLayout(clock_layout
);
178 set_focusable(false);
181 TimeView::~TimeView() {
184 void TimeView::UpdateTimeFormat() {
186 ash::Shell::GetInstance()->system_tray_delegate()->GetHourClockType();
190 void TimeView::UpdateTextInternal(const base::Time
& now
) {
191 // Just in case |now| is null, do NOT update time; otherwise, it will
192 // crash icu code by calling into base::TimeFormatTimeOfDayWithHourClockType,
193 // see details in crbug.com/147570.
195 LOG(ERROR
) << "Received null value from base::Time |now| in argument";
199 base::string16 current_time
= base::TimeFormatTimeOfDayWithHourClockType(
200 now
, hour_type_
, base::kDropAmPm
);
201 label_
->SetText(current_time
);
202 label_
->SetTooltipText(base::TimeFormatFriendlyDate(now
));
204 // Calculate vertical clock layout labels.
205 size_t colon_pos
= current_time
.find(ASCIIToUTF16(":"));
206 base::string16 hour
= current_time
.substr(0, colon_pos
);
207 base::string16 minute
= current_time
.substr(colon_pos
+ 1);
208 label_hour_left_
->SetText(hour
.substr(0, 1));
209 label_hour_right_
->SetText(hour
.length() == 2 ?
210 hour
.substr(1,1) : ASCIIToUTF16(":"));
211 label_minute_left_
->SetText(minute
.substr(0, 1));
212 label_minute_right_
->SetText(minute
.substr(1, 1));
217 bool TimeView::PerformAction(const ui::Event
& event
) {
221 bool TimeView::OnMousePressed(const ui::MouseEvent
& event
) {
222 // Let the event fall through.
226 void TimeView::UpdateClockLayout(TrayDate::ClockLayout clock_layout
){
227 SetBorder(clock_layout
);
228 if (clock_layout
== TrayDate::HORIZONTAL_CLOCK
) {
229 RemoveChildView(label_hour_left_
.get());
230 RemoveChildView(label_hour_right_
.get());
231 RemoveChildView(label_minute_left_
.get());
232 RemoveChildView(label_minute_right_
.get());
234 new views::BoxLayout(views::BoxLayout::kHorizontal
, 0, 0, 0));
235 AddChildView(label_
.get());
237 RemoveChildView(label_
.get());
238 views::GridLayout
* layout
= new views::GridLayout(this);
239 SetLayoutManager(layout
);
240 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
241 columns
->AddPaddingColumn(0, 6);
242 columns
->AddColumn(views::GridLayout::CENTER
, views::GridLayout::CENTER
,
243 0, views::GridLayout::USE_PREF
, 0, 0);
244 columns
->AddColumn(views::GridLayout::CENTER
, views::GridLayout::CENTER
,
245 0, views::GridLayout::USE_PREF
, 0, 0);
246 layout
->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVeriticalAlignment
);
247 layout
->StartRow(0, 0);
248 layout
->AddView(label_hour_left_
.get());
249 layout
->AddView(label_hour_right_
.get());
250 layout
->StartRow(0, 0);
251 layout
->AddView(label_minute_left_
.get());
252 layout
->AddView(label_minute_right_
.get());
253 layout
->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVeriticalAlignment
);
258 void TimeView::SetBorder(TrayDate::ClockLayout clock_layout
) {
259 if (clock_layout
== TrayDate::HORIZONTAL_CLOCK
)
260 set_border(views::Border::CreateEmptyBorder(
261 0, kTrayLabelItemHorizontalPaddingBottomAlignment
,
262 0, kTrayLabelItemHorizontalPaddingBottomAlignment
));
267 void TimeView::SetupLabels() {
268 label_
.reset(CreateLabel());
269 SetupLabel(label_
.get());
270 label_hour_left_
.reset(CreateLabel());
271 SetupLabel(label_hour_left_
.get());
272 label_hour_right_
.reset(CreateLabel());
273 SetupLabel(label_hour_right_
.get());
274 label_minute_left_
.reset(CreateLabel());
275 SetupLabel(label_minute_left_
.get());
276 label_minute_right_
.reset(CreateLabel());
277 SetupLabel(label_minute_right_
.get());
278 label_hour_left_
->SetEnabledColor(kVerticalClockHourColor
);
279 label_hour_right_
->SetEnabledColor(kVerticalClockHourColor
);
282 void TimeView::SetupLabel(views::Label
* label
) {
283 label
->set_owned_by_client();
284 SetupLabelForTray(label
);
285 gfx::Font font
= label
->font();
286 label
->SetFont(font
.DeriveFont(0, font
.GetStyle() & ~gfx::Font::BOLD
));
290 } // namespace internal