diff options
author | Ben Murdoch <benm@google.com> | 2013-08-08 10:24:53 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2013-08-08 10:24:53 +0100 |
commit | bb1529ce867d8845a77ec7cdf3e3003ef1771a40 (patch) | |
tree | f78d0de03cc8aed1a934d921636a0beb8d164378 /ui | |
parent | c95505573d864f17cabf515e32f5b8e0831ae237 (diff) | |
download | chromium_org-bb1529ce867d8845a77ec7cdf3e3003ef1771a40.tar.gz |
Merge from Chromium at DEPS revision r216370
This commit was generated by merge_to_master.py.
Change-Id: I739228187a6f1df6c28c5761160e593a49891113
Diffstat (limited to 'ui')
50 files changed, 2459 insertions, 1032 deletions
diff --git a/ui/android/java/src/org/chromium/ui/SelectFileDialog.java b/ui/android/java/src/org/chromium/ui/SelectFileDialog.java index 9aa11e4631..91533b52cf 100644 --- a/ui/android/java/src/org/chromium/ui/SelectFileDialog.java +++ b/ui/android/java/src/org/chromium/ui/SelectFileDialog.java @@ -64,17 +64,16 @@ class SelectFileDialog implements WindowAndroid.IntentCallback{ Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); Intent soundRecorder = new Intent( MediaStore.Audio.Media.RECORD_SOUND_ACTION); - String lowMemoryError = window.getContext().getString(R.string.low_memory_error); // Quick check - if the |capture| parameter is set and |fileTypes| has the appropriate MIME // type, we should just launch the appropriate intent. Otherwise build up a chooser based on // the accept type and then display that to the user. if (captureCamera()) { - if (window.showIntent(camera, this, lowMemoryError)) return; + if (window.showIntent(camera, this, R.string.low_memory_error)) return; } else if (captureCamcorder()) { - if (window.showIntent(camcorder, this, lowMemoryError)) return; + if (window.showIntent(camcorder, this, R.string.low_memory_error)) return; } else if (captureMicrophone()) { - if (window.showIntent(soundRecorder, this, lowMemoryError)) return; + if (window.showIntent(soundRecorder, this, R.string.low_memory_error)) return; } Intent getContentIntent = new Intent(Intent.ACTION_GET_CONTENT); @@ -109,7 +108,9 @@ class SelectFileDialog implements WindowAndroid.IntentCallback{ chooser.putExtra(Intent.EXTRA_INTENT, getContentIntent); - if (!window.showIntent(chooser, this, lowMemoryError)) onFileNotSelected(); + if (!window.showIntent(chooser, this, R.string.low_memory_error)) { + onFileNotSelected(); + } } /** diff --git a/ui/android/java/src/org/chromium/ui/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/WindowAndroid.java index 477e5e6f29..316690b4bc 100644 --- a/ui/android/java/src/org/chromium/ui/WindowAndroid.java +++ b/ui/android/java/src/org/chromium/ui/WindowAndroid.java @@ -52,10 +52,11 @@ public class WindowAndroid { * Shows an intent and returns the results to the callback object. * @param intent The intent that needs to be showed. * @param callback The object that will receive the results for the intent. - * @param error The error string to be show if activity is paused before intent results. + * @param errorId The ID of error string to be show if activity is paused before intent + * results. * @return Whether the intent was shown. */ - public boolean showIntent(Intent intent, IntentCallback callback, String error) { + public boolean showIntent(Intent intent, IntentCallback callback, int errorId) { int requestCode = REQUEST_CODE_PREFIX + mNextRequestCode; mNextRequestCode = (mNextRequestCode + 1) % REQUEST_CODE_RANGE_SIZE; @@ -66,7 +67,7 @@ public class WindowAndroid { } mOutstandingIntents.put(requestCode, callback); - if (error != null) mIntentErrors.put(requestCode, error); + mIntentErrors.put(requestCode, mActivity.getString(errorId)); return true; } diff --git a/ui/app_list/cocoa/apps_grid_controller.mm b/ui/app_list/cocoa/apps_grid_controller.mm index c2873c4709..d32a791339 100644 --- a/ui/app_list/cocoa/apps_grid_controller.mm +++ b/ui/app_list/cocoa/apps_grid_controller.mm @@ -22,7 +22,7 @@ const int kItemsPerPage = kFixedRows * kFixedColumns; // Padding space in pixels for fixed layout. const CGFloat kGridTopPadding = 1; -const CGFloat kLeftRightPadding = 16; +const CGFloat kLeftRightPadding = 21; const CGFloat kScrollerPadding = 16; // Preferred tile size when showing in fixed layout. These should be even diff --git a/ui/app_list/cocoa/apps_grid_view_item.mm b/ui/app_list/cocoa/apps_grid_view_item.mm index 908bb87b9f..34fe975ebc 100644 --- a/ui/app_list/cocoa/apps_grid_view_item.mm +++ b/ui/app_list/cocoa/apps_grid_view_item.mm @@ -30,6 +30,10 @@ const CGFloat kIconSize = 48; const CGFloat kProgressBarHorizontalPadding = 8; const CGFloat kProgressBarVerticalPadding = 13; +// On Mac, fonts of the same enum from ResourceBundle are larger. The smallest +// enum is already used, so it needs to be reduced further to match Windows. +const int kMacFontSizeDelta = -1; + } // namespace @class AppsGridItemBackgroundView; @@ -238,8 +242,10 @@ void ItemModelObserverBridge::ItemPercentDownloadedChanged() { [paragraphStyle setAlignment:NSCenterTextAlignment]; NSDictionary* titleAttributes = @{ NSParagraphStyleAttributeName : paragraphStyle, - NSFontAttributeName : ui::ResourceBundle::GetSharedInstance().GetFont( - app_list::kItemTextFontStyle).GetNativeFont(), + NSFontAttributeName : ui::ResourceBundle::GetSharedInstance() + .GetFont(app_list::kItemTextFontStyle) + .DeriveFont(kMacFontSizeDelta) + .GetNativeFont(), NSForegroundColorAttributeName : [self isSelected] ? gfx::SkColorToSRGBNSColor(app_list::kGridTitleHoverColor) : gfx::SkColorToSRGBNSColor(app_list::kGridTitleColor) diff --git a/ui/base/default_theme_provider.h b/ui/base/default_theme_provider.h index 6487b429e0..2594dfbf66 100644 --- a/ui/base/default_theme_provider.h +++ b/ui/base/default_theme_provider.h @@ -34,11 +34,10 @@ class UI_EXPORT DefaultThemeProvider : public ThemeProvider { ui::ScaleFactor scale_factor) const OVERRIDE; #if defined(OS_MACOSX) && !defined(TOOLKIT_VIEWS) - virtual NSImage* GetNSImageNamed(int id, bool allow_default) const OVERRIDE; - virtual NSColor* GetNSImageColorNamed(int id, - bool allow_default) const OVERRIDE; - virtual NSColor* GetNSColor(int id, bool allow_default) const OVERRIDE; - virtual NSColor* GetNSColorTint(int id, bool allow_default) const OVERRIDE; + virtual NSImage* GetNSImageNamed(int id) const OVERRIDE; + virtual NSColor* GetNSImageColorNamed(int id) const OVERRIDE; + virtual NSColor* GetNSColor(int id) const OVERRIDE; + virtual NSColor* GetNSColorTint(int id) const OVERRIDE; virtual NSGradient* GetNSGradient(int id) const OVERRIDE; #elif defined(OS_POSIX) && !defined(TOOLKIT_VIEWS) && !defined(OS_ANDROID) virtual GdkPixbuf* GetRTLEnabledPixbufNamed(int id) const OVERRIDE; diff --git a/ui/base/default_theme_provider_mac.mm b/ui/base/default_theme_provider_mac.mm index 08725c22ab..cc337287e9 100644 --- a/ui/base/default_theme_provider_mac.mm +++ b/ui/base/default_theme_provider_mac.mm @@ -11,25 +11,21 @@ namespace ui { #if !defined(TOOLKIT_VIEWS) -NSImage* DefaultThemeProvider::GetNSImageNamed(int id, - bool allow_default) const { +NSImage* DefaultThemeProvider::GetNSImageNamed(int id) const { return ResourceBundle::GetSharedInstance(). GetNativeImageNamed(id).ToNSImage(); } -NSColor* DefaultThemeProvider::GetNSImageColorNamed(int id, - bool allow_default) const { - NSImage* image = GetNSImageNamed(id, allow_default); +NSColor* DefaultThemeProvider::GetNSImageColorNamed(int id) const { + NSImage* image = GetNSImageNamed(id); return [NSColor colorWithPatternImage:image]; } -NSColor* DefaultThemeProvider::GetNSColor(int id, - bool allow_default) const { +NSColor* DefaultThemeProvider::GetNSColor(int id) const { return [NSColor redColor]; } -NSColor* DefaultThemeProvider::GetNSColorTint(int id, - bool allow_default) const { +NSColor* DefaultThemeProvider::GetNSColorTint(int id) const { return [NSColor redColor]; } diff --git a/ui/base/ime/character_composer.cc b/ui/base/ime/character_composer.cc index 9ffb2886e5..0402be2ba9 100644 --- a/ui/base/ime/character_composer.cc +++ b/ui/base/ime/character_composer.cc @@ -38,8 +38,10 @@ const struct BlackListedDeadKey { uint32 output_char; // the character to be inserted if the filter is matched. bool consume; // true if the original key event will be consumed. } kBlackListedDeadKeys[] = { + { GDK_KEY_dead_acute, GDK_KEY_m, GDK_KEY_apostrophe, false }, { GDK_KEY_dead_acute, GDK_KEY_s, GDK_KEY_apostrophe, false }, { GDK_KEY_dead_acute, GDK_KEY_t, GDK_KEY_apostrophe, false }, + { GDK_KEY_dead_acute, GDK_KEY_v, GDK_KEY_apostrophe, false }, { GDK_KEY_dead_acute, GDK_KEY_dead_acute, GDK_KEY_apostrophe, true }, }; diff --git a/ui/base/l10n/time_format.cc b/ui/base/l10n/time_format.cc new file mode 100644 index 0000000000..5ba606c3df --- /dev/null +++ b/ui/base/l10n/time_format.cc @@ -0,0 +1,408 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/l10n/time_format.h" + +#include <vector> + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/stl_util.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "grit/ui_strings.h" +#include "third_party/icu/source/common/unicode/locid.h" +#include "third_party/icu/source/i18n/unicode/datefmt.h" +#include "third_party/icu/source/i18n/unicode/plurfmt.h" +#include "third_party/icu/source/i18n/unicode/plurrule.h" +#include "third_party/icu/source/i18n/unicode/smpdtfmt.h" +#include "ui/base/l10n/l10n_util.h" + +using base::Time; +using base::TimeDelta; + +namespace { + +static const char kFallbackFormatSuffixShort[] = "}"; +static const char kFallbackFormatSuffixLeft[] = " left}"; +static const char kFallbackFormatSuffixAgo[] = " ago}"; + +// Contains message IDs for various time units and pluralities. +struct MessageIDs { + // There are 4 different time units and 6 different pluralities. + int ids[4][6]; +}; + +// Message IDs for different time formats. +static const MessageIDs kTimeShortMessageIDs = { { + { + IDS_TIME_SECS_DEFAULT, IDS_TIME_SEC_SINGULAR, IDS_TIME_SECS_ZERO, + IDS_TIME_SECS_TWO, IDS_TIME_SECS_FEW, IDS_TIME_SECS_MANY + }, + { + IDS_TIME_MINS_DEFAULT, IDS_TIME_MIN_SINGULAR, IDS_TIME_MINS_ZERO, + IDS_TIME_MINS_TWO, IDS_TIME_MINS_FEW, IDS_TIME_MINS_MANY + }, + { + IDS_TIME_HOURS_DEFAULT, IDS_TIME_HOUR_SINGULAR, IDS_TIME_HOURS_ZERO, + IDS_TIME_HOURS_TWO, IDS_TIME_HOURS_FEW, IDS_TIME_HOURS_MANY + }, + { + IDS_TIME_DAYS_DEFAULT, IDS_TIME_DAY_SINGULAR, IDS_TIME_DAYS_ZERO, + IDS_TIME_DAYS_TWO, IDS_TIME_DAYS_FEW, IDS_TIME_DAYS_MANY + } +} }; + +static const MessageIDs kTimeRemainingMessageIDs = { { + { + IDS_TIME_REMAINING_SECS_DEFAULT, IDS_TIME_REMAINING_SEC_SINGULAR, + IDS_TIME_REMAINING_SECS_ZERO, IDS_TIME_REMAINING_SECS_TWO, + IDS_TIME_REMAINING_SECS_FEW, IDS_TIME_REMAINING_SECS_MANY + }, + { + IDS_TIME_REMAINING_MINS_DEFAULT, IDS_TIME_REMAINING_MIN_SINGULAR, + IDS_TIME_REMAINING_MINS_ZERO, IDS_TIME_REMAINING_MINS_TWO, + IDS_TIME_REMAINING_MINS_FEW, IDS_TIME_REMAINING_MINS_MANY + }, + { + IDS_TIME_REMAINING_HOURS_DEFAULT, IDS_TIME_REMAINING_HOUR_SINGULAR, + IDS_TIME_REMAINING_HOURS_ZERO, IDS_TIME_REMAINING_HOURS_TWO, + IDS_TIME_REMAINING_HOURS_FEW, IDS_TIME_REMAINING_HOURS_MANY + }, + { + IDS_TIME_REMAINING_DAYS_DEFAULT, IDS_TIME_REMAINING_DAY_SINGULAR, + IDS_TIME_REMAINING_DAYS_ZERO, IDS_TIME_REMAINING_DAYS_TWO, + IDS_TIME_REMAINING_DAYS_FEW, IDS_TIME_REMAINING_DAYS_MANY + } +} }; + +static const MessageIDs kTimeRemainingLongMessageIDs = { { + { + IDS_TIME_REMAINING_SECS_DEFAULT, IDS_TIME_REMAINING_SEC_SINGULAR, + IDS_TIME_REMAINING_SECS_ZERO, IDS_TIME_REMAINING_SECS_TWO, + IDS_TIME_REMAINING_SECS_FEW, IDS_TIME_REMAINING_SECS_MANY + }, + { + IDS_TIME_REMAINING_LONG_MINS_DEFAULT, IDS_TIME_REMAINING_LONG_MIN_SINGULAR, + IDS_TIME_REMAINING_LONG_MINS_ZERO, IDS_TIME_REMAINING_LONG_MINS_TWO, + IDS_TIME_REMAINING_LONG_MINS_FEW, IDS_TIME_REMAINING_LONG_MINS_MANY + }, + { + IDS_TIME_REMAINING_HOURS_DEFAULT, IDS_TIME_REMAINING_HOUR_SINGULAR, + IDS_TIME_REMAINING_HOURS_ZERO, IDS_TIME_REMAINING_HOURS_TWO, + IDS_TIME_REMAINING_HOURS_FEW, IDS_TIME_REMAINING_HOURS_MANY + }, + { + IDS_TIME_REMAINING_DAYS_DEFAULT, IDS_TIME_REMAINING_DAY_SINGULAR, + IDS_TIME_REMAINING_DAYS_ZERO, IDS_TIME_REMAINING_DAYS_TWO, + IDS_TIME_REMAINING_DAYS_FEW, IDS_TIME_REMAINING_DAYS_MANY + } +} }; + +static const MessageIDs kTimeDurationLongMessageIDs = { { + { + IDS_TIME_DURATION_LONG_SECS_DEFAULT, IDS_TIME_DURATION_LONG_SEC_SINGULAR, + IDS_TIME_DURATION_LONG_SECS_ZERO, IDS_TIME_DURATION_LONG_SECS_TWO, + IDS_TIME_DURATION_LONG_SECS_FEW, IDS_TIME_DURATION_LONG_SECS_MANY + }, + { + IDS_TIME_DURATION_LONG_MINS_DEFAULT, IDS_TIME_DURATION_LONG_MIN_SINGULAR, + IDS_TIME_DURATION_LONG_MINS_ZERO, IDS_TIME_DURATION_LONG_MINS_TWO, + IDS_TIME_DURATION_LONG_MINS_FEW, IDS_TIME_DURATION_LONG_MINS_MANY + }, + { + IDS_TIME_HOURS_DEFAULT, IDS_TIME_HOUR_SINGULAR, + IDS_TIME_HOURS_ZERO, IDS_TIME_HOURS_TWO, + IDS_TIME_HOURS_FEW, IDS_TIME_HOURS_MANY + }, + { + IDS_TIME_DAYS_DEFAULT, IDS_TIME_DAY_SINGULAR, + IDS_TIME_DAYS_ZERO, IDS_TIME_DAYS_TWO, + IDS_TIME_DAYS_FEW, IDS_TIME_DAYS_MANY + } +} }; + +static const MessageIDs kTimeElapsedMessageIDs = { { + { + IDS_TIME_ELAPSED_SECS_DEFAULT, IDS_TIME_ELAPSED_SEC_SINGULAR, + IDS_TIME_ELAPSED_SECS_ZERO, IDS_TIME_ELAPSED_SECS_TWO, + IDS_TIME_ELAPSED_SECS_FEW, IDS_TIME_ELAPSED_SECS_MANY + }, + { + IDS_TIME_ELAPSED_MINS_DEFAULT, IDS_TIME_ELAPSED_MIN_SINGULAR, + IDS_TIME_ELAPSED_MINS_ZERO, IDS_TIME_ELAPSED_MINS_TWO, + IDS_TIME_ELAPSED_MINS_FEW, IDS_TIME_ELAPSED_MINS_MANY + }, + { + IDS_TIME_ELAPSED_HOURS_DEFAULT, IDS_TIME_ELAPSED_HOUR_SINGULAR, + IDS_TIME_ELAPSED_HOURS_ZERO, IDS_TIME_ELAPSED_HOURS_TWO, + IDS_TIME_ELAPSED_HOURS_FEW, IDS_TIME_ELAPSED_HOURS_MANY + }, + { + IDS_TIME_ELAPSED_DAYS_DEFAULT, IDS_TIME_ELAPSED_DAY_SINGULAR, + IDS_TIME_ELAPSED_DAYS_ZERO, IDS_TIME_ELAPSED_DAYS_TWO, + IDS_TIME_ELAPSED_DAYS_FEW, IDS_TIME_ELAPSED_DAYS_MANY + } +} }; + +// Different format types. +enum FormatType { + FORMAT_SHORT, + FORMAT_REMAINING, + FORMAT_REMAINING_LONG, + FORMAT_DURATION_LONG, + FORMAT_ELAPSED, +}; + +class TimeFormatter { + public: + const std::vector<icu::PluralFormat*>& formatter(FormatType format_type) { + switch (format_type) { + case FORMAT_SHORT: + return short_formatter_.get(); + case FORMAT_REMAINING: + return time_left_formatter_.get(); + case FORMAT_REMAINING_LONG: + return time_left_long_formatter_.get(); + case FORMAT_DURATION_LONG: + return time_duration_long_formatter_.get(); + case FORMAT_ELAPSED: + return time_elapsed_formatter_.get(); + default: + NOTREACHED(); + return short_formatter_.get(); + } + } + private: + static const MessageIDs& GetMessageIDs(FormatType format_type) { + switch (format_type) { + case FORMAT_SHORT: + return kTimeShortMessageIDs; + case FORMAT_REMAINING: + return kTimeRemainingMessageIDs; + case FORMAT_REMAINING_LONG: + return kTimeRemainingLongMessageIDs; + case FORMAT_DURATION_LONG: + return kTimeDurationLongMessageIDs; + case FORMAT_ELAPSED: + return kTimeElapsedMessageIDs; + default: + NOTREACHED(); + return kTimeShortMessageIDs; + } + } + + static const char* GetFallbackFormatSuffix(FormatType format_type) { + switch (format_type) { + case FORMAT_SHORT: + return kFallbackFormatSuffixShort; + case FORMAT_REMAINING: + case FORMAT_REMAINING_LONG: + return kFallbackFormatSuffixLeft; + case FORMAT_ELAPSED: + return kFallbackFormatSuffixAgo; + default: + NOTREACHED(); + return kFallbackFormatSuffixShort; + } + } + + TimeFormatter() { + BuildFormats(FORMAT_SHORT, &short_formatter_); + BuildFormats(FORMAT_REMAINING, &time_left_formatter_); + BuildFormats(FORMAT_REMAINING_LONG, &time_left_long_formatter_); + BuildFormats(FORMAT_DURATION_LONG, &time_duration_long_formatter_); + BuildFormats(FORMAT_ELAPSED, &time_elapsed_formatter_); + } + ~TimeFormatter() { + } + friend struct base::DefaultLazyInstanceTraits<TimeFormatter>; + + ScopedVector<icu::PluralFormat> short_formatter_; + ScopedVector<icu::PluralFormat> time_left_formatter_; + ScopedVector<icu::PluralFormat> time_left_long_formatter_; + ScopedVector<icu::PluralFormat> time_duration_long_formatter_; + ScopedVector<icu::PluralFormat> time_elapsed_formatter_; + static void BuildFormats(FormatType format_type, + ScopedVector<icu::PluralFormat>* time_formats); + static icu::PluralFormat* createFallbackFormat( + const icu::PluralRules& rules, int index, FormatType format_type); + + DISALLOW_COPY_AND_ASSIGN(TimeFormatter); +}; + +static base::LazyInstance<TimeFormatter> g_time_formatter = + LAZY_INSTANCE_INITIALIZER; + +void TimeFormatter::BuildFormats( + FormatType format_type, ScopedVector<icu::PluralFormat>* time_formats) { + const icu::UnicodeString kKeywords[] = { + UNICODE_STRING_SIMPLE("other"), UNICODE_STRING_SIMPLE("one"), + UNICODE_STRING_SIMPLE("zero"), UNICODE_STRING_SIMPLE("two"), + UNICODE_STRING_SIMPLE("few"), UNICODE_STRING_SIMPLE("many") + }; + UErrorCode err = U_ZERO_ERROR; + scoped_ptr<icu::PluralRules> rules( + icu::PluralRules::forLocale(icu::Locale::getDefault(), err)); + if (U_FAILURE(err)) { + err = U_ZERO_ERROR; + icu::UnicodeString fallback_rules("one: n is 1", -1, US_INV); + rules.reset(icu::PluralRules::createRules(fallback_rules, err)); + DCHECK(U_SUCCESS(err)); + } + + const MessageIDs& message_ids = GetMessageIDs(format_type); + + for (int i = 0; i < 4; ++i) { + icu::UnicodeString pattern; + for (size_t j = 0; j < arraysize(kKeywords); ++j) { + int msg_id = message_ids.ids[i][j]; + std::string sub_pattern = l10n_util::GetStringUTF8(msg_id); + // NA means this keyword is not used in the current locale. + // Even if a translator translated for this keyword, we do not + // use it unless it's 'other' (j=0) or it's defined in the rules + // for the current locale. Special-casing of 'other' will be removed + // once ICU's isKeyword is fixed to return true for isKeyword('other'). + if (sub_pattern.compare("NA") != 0 && + (j == 0 || rules->isKeyword(kKeywords[j]))) { + pattern += kKeywords[j]; + pattern += UNICODE_STRING_SIMPLE("{"); + pattern += icu::UnicodeString(sub_pattern.c_str(), "UTF-8"); + pattern += UNICODE_STRING_SIMPLE("}"); + } + } + icu::PluralFormat* format = new icu::PluralFormat(*rules, pattern, err); + if (U_SUCCESS(err)) { + time_formats->push_back(format); + } else { + delete format; + time_formats->push_back(createFallbackFormat(*rules, i, format_type)); + // Reset it so that next ICU call can proceed. + err = U_ZERO_ERROR; + } + } +} + +// Create a hard-coded fallback plural format. This will never be called +// unless translators make a mistake. +icu::PluralFormat* TimeFormatter::createFallbackFormat( + const icu::PluralRules& rules, int index, FormatType format_type) { + const icu::UnicodeString kUnits[4][2] = { + { UNICODE_STRING_SIMPLE("sec"), UNICODE_STRING_SIMPLE("secs") }, + { UNICODE_STRING_SIMPLE("min"), UNICODE_STRING_SIMPLE("mins") }, + { UNICODE_STRING_SIMPLE("hour"), UNICODE_STRING_SIMPLE("hours") }, + { UNICODE_STRING_SIMPLE("day"), UNICODE_STRING_SIMPLE("days") } + }; + icu::UnicodeString suffix(GetFallbackFormatSuffix(format_type), -1, US_INV); + icu::UnicodeString pattern; + if (rules.isKeyword(UNICODE_STRING_SIMPLE("one"))) { + pattern += UNICODE_STRING_SIMPLE("one{# ") + kUnits[index][0] + suffix; + } + pattern += UNICODE_STRING_SIMPLE(" other{# ") + kUnits[index][1] + suffix; + UErrorCode err = U_ZERO_ERROR; + icu::PluralFormat* format = new icu::PluralFormat(rules, pattern, err); + DCHECK(U_SUCCESS(err)); + return format; +} + +string16 FormatTimeImpl(const TimeDelta& delta, FormatType format_type) { + if (delta.ToInternalValue() < 0) { + NOTREACHED() << "Negative duration"; + return string16(); + } + + int number; + + const std::vector<icu::PluralFormat*>& formatters = + g_time_formatter.Get().formatter(format_type); + + UErrorCode error = U_ZERO_ERROR; + icu::UnicodeString time_string; + // Less than a minute gets "X seconds left" + if (delta.ToInternalValue() < Time::kMicrosecondsPerMinute) { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerSecond); + time_string = formatters[0]->format(number, error); + + // Less than 1 hour gets "X minutes left". + } else if (delta.ToInternalValue() < Time::kMicrosecondsPerHour) { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerMinute); + time_string = formatters[1]->format(number, error); + + // Less than 1 day remaining gets "X hours left" + } else if (delta.ToInternalValue() < Time::kMicrosecondsPerDay) { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerHour); + time_string = formatters[2]->format(number, error); + + // Anything bigger gets "X days left" + } else { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerDay); + time_string = formatters[3]->format(number, error); + } + + // With the fallback added, this should never fail. + DCHECK(U_SUCCESS(error)); + int capacity = time_string.length() + 1; + DCHECK_GT(capacity, 1); + string16 result; + time_string.extract(static_cast<UChar*>(WriteInto(&result, capacity)), + capacity, error); + DCHECK(U_SUCCESS(error)); + return result; +} + +} // namespace + +namespace ui { + +// static +string16 TimeFormat::TimeElapsed(const TimeDelta& delta) { + return FormatTimeImpl(delta, FORMAT_ELAPSED); +} + +// static +string16 TimeFormat::TimeRemaining(const TimeDelta& delta) { + return FormatTimeImpl(delta, FORMAT_REMAINING); +} + +// static +string16 TimeFormat::TimeRemainingLong(const TimeDelta& delta) { + return FormatTimeImpl(delta, FORMAT_REMAINING_LONG); +} + +// static +string16 TimeFormat::TimeRemainingShort(const TimeDelta& delta) { + return FormatTimeImpl(delta, FORMAT_SHORT); +} + +// static +string16 TimeFormat::TimeDurationLong(const TimeDelta& delta) { + return FormatTimeImpl(delta, FORMAT_DURATION_LONG); +} + +// static +string16 TimeFormat::RelativeDate( + const Time& time, + const Time* optional_midnight_today) { + Time midnight_today = optional_midnight_today ? *optional_midnight_today : + Time::Now().LocalMidnight(); + TimeDelta day = TimeDelta::FromMicroseconds(Time::kMicrosecondsPerDay); + Time tomorrow = midnight_today + day; + Time yesterday = midnight_today - day; + if (time >= tomorrow) + return string16(); + else if (time >= midnight_today) + return l10n_util::GetStringUTF16(IDS_PAST_TIME_TODAY); + else if (time >= yesterday) + return l10n_util::GetStringUTF16(IDS_PAST_TIME_YESTERDAY); + return string16(); +} + +} // namespace ui diff --git a/ui/base/l10n/time_format.h b/ui/base/l10n/time_format.h new file mode 100644 index 0000000000..075b6989ce --- /dev/null +++ b/ui/base/l10n/time_format.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_L10N_TIME_FORMAT_H_ +#define UI_BASE_L10N_TIME_FORMAT_H_ + +#include "base/basictypes.h" +#include "base/strings/string16.h" +#include "ui/base/ui_export.h" + +namespace base { +class Time; +class TimeDelta; +} + +namespace ui { + +// Methods to format time values as strings. +class UI_EXPORT TimeFormat { + public: + // TimeElapsed, TimeRemaining and TimeRemainingShort functions: + // These functions return a localized string of approximate time duration. The + // conditions are simpler than PastTime since these functions are used for + // in-progress operations and users have different expectations of units. + + // Returns times in elapsed-format: "3 mins ago", "2 days ago". + static string16 TimeElapsed(const base::TimeDelta& delta); + + // Returns times in remaining-format: "3 mins left", "2 days left". + static string16 TimeRemaining(const base::TimeDelta& delta); + + // Returns times in remaining-long-format: "3 minutes left", "2 days left". + // Currently, this only affects the minutes in long format, the rest + // of the time units are formatted the same as TimeRemaining does. + static string16 TimeRemainingLong(const base::TimeDelta& delta); + + // Returns times in short-format: "3 mins", "2 days". + static string16 TimeRemainingShort(const base::TimeDelta& delta); + + // Return times in long-format: "2 hours", "25 minutes". + static string16 TimeDurationLong(const base::TimeDelta& delta); + + // For displaying a relative time in the past. This method returns either + // "Today", "Yesterday", or an empty string if it's older than that. Returns + // the empty string for days in the future. + // + // TODO(brettw): This should be able to handle days in the future like + // "Tomorrow". + // TODO(tc): This should be able to do things like "Last week". This + // requires handling singluar/plural for all languages. + // + // The second parameter is optional, it is midnight of "Now" for relative day + // computations: Time::Now().LocalMidnight() + // If NULL, the current day's midnight will be retrieved, which can be + // slow. If many items are being processed, it is best to get the current + // time once at the beginning and pass it for each computation. + static string16 RelativeDate(const base::Time& time, + const base::Time* optional_midnight_today); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TimeFormat); +}; + +} // namespace ui + +#endif // UI_BASE_L10N_TIME_FORMAT_H_ diff --git a/ui/base/l10n/time_format_unittest.cc b/ui/base/l10n/time_format_unittest.cc new file mode 100644 index 0000000000..eaee957136 --- /dev/null +++ b/ui/base/l10n/time_format_unittest.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/l10n/time_format.h" + +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/resource/resource_bundle.h" + +namespace ui { +namespace { + +using base::TimeDelta; + +void TestTimeFormats(const TimeDelta& delta, const char* expected_ascii) { + string16 expected = ASCIIToUTF16(expected_ascii); + string16 expected_left = expected + ASCIIToUTF16(" left"); + string16 expected_ago = expected + ASCIIToUTF16(" ago"); + EXPECT_EQ(expected, TimeFormat::TimeRemainingShort(delta)); + EXPECT_EQ(expected_left, TimeFormat::TimeRemaining(delta)); + EXPECT_EQ(expected_ago, TimeFormat::TimeElapsed(delta)); +} + +TEST(TimeFormat, FormatTime) { + const TimeDelta one_day = TimeDelta::FromDays(1); + const TimeDelta three_days = TimeDelta::FromDays(3); + const TimeDelta one_hour = TimeDelta::FromHours(1); + const TimeDelta four_hours = TimeDelta::FromHours(4); + const TimeDelta one_min = TimeDelta::FromMinutes(1); + const TimeDelta three_mins = TimeDelta::FromMinutes(3); + const TimeDelta one_sec = TimeDelta::FromSeconds(1); + const TimeDelta five_secs = TimeDelta::FromSeconds(5); + const TimeDelta twohundred_millisecs = TimeDelta::FromMilliseconds(200); + + // TODO(jungshik) : These test only pass when the OS locale is 'en'. + // We need to add SetUp() and TearDown() to set the locale to 'en'. + TestTimeFormats(twohundred_millisecs, "0 secs"); + TestTimeFormats(one_sec - twohundred_millisecs, "0 secs"); + TestTimeFormats(one_sec + twohundred_millisecs, "1 sec"); + TestTimeFormats(five_secs + twohundred_millisecs, "5 secs"); + TestTimeFormats(one_min + five_secs, "1 min"); + TestTimeFormats(three_mins + twohundred_millisecs, "3 mins"); + TestTimeFormats(one_hour + five_secs, "1 hour"); + TestTimeFormats(four_hours + five_secs, "4 hours"); + TestTimeFormats(one_day + five_secs, "1 day"); + TestTimeFormats(three_days, "3 days"); + TestTimeFormats(three_days + four_hours, "3 days"); +} + +// crbug.com/159388: This test fails when daylight savings time ends. +TEST(TimeFormat, RelativeDate) { + base::Time now = base::Time::Now(); + string16 today_str = TimeFormat::RelativeDate(now, NULL); + EXPECT_EQ(ASCIIToUTF16("Today"), today_str); + + base::Time yesterday = now - TimeDelta::FromDays(1); + string16 yesterday_str = TimeFormat::RelativeDate(yesterday, NULL); + EXPECT_EQ(ASCIIToUTF16("Yesterday"), yesterday_str); + + base::Time two_days_ago = now - TimeDelta::FromDays(2); + string16 two_days_ago_str = TimeFormat::RelativeDate(two_days_ago, NULL); + EXPECT_TRUE(two_days_ago_str.empty()); + + base::Time a_week_ago = now - TimeDelta::FromDays(7); + string16 a_week_ago_str = TimeFormat::RelativeDate(a_week_ago, NULL); + EXPECT_TRUE(a_week_ago_str.empty()); +} + +} // namespace +} // namespace ui diff --git a/ui/base/ozone/surface_factory_ozone.cc b/ui/base/ozone/surface_factory_ozone.cc index a617efe902..f082e7dc41 100644 --- a/ui/base/ozone/surface_factory_ozone.cc +++ b/ui/base/ozone/surface_factory_ozone.cc @@ -29,10 +29,6 @@ class SurfaceFactoryOzoneStub : public SurfaceFactoryOzone { const gfx::Rect& bounds) OVERRIDE { return false; } - virtual bool AcceleratedWidgetCanBeResized( - gfx::AcceleratedWidget w) OVERRIDE { - return false; - } virtual gfx::VSyncProvider* GetVSyncProvider( gfx::AcceleratedWidget w) OVERRIDE { return NULL; diff --git a/ui/base/ozone/surface_factory_ozone.h b/ui/base/ozone/surface_factory_ozone.h index d98882d54a..50a9b4bdf5 100644 --- a/ui/base/ozone/surface_factory_ozone.h +++ b/ui/base/ozone/surface_factory_ozone.h @@ -60,11 +60,6 @@ class SurfaceFactoryOzone { gfx::AcceleratedWidget w, const gfx::Rect& bounds) = 0; - // TODO(rjkroege): keeping the old API for downstream compat asked by - // rjkroege; he promised to remove as soon as his implementation gets fixed, - // but we should take this function API as deprecated for now. - virtual bool AcceleratedWidgetCanBeResized(gfx::AcceleratedWidget w) = 0; - // Returns a gfx::VsyncProvider for the provided AcceleratedWidget. Note // that this may be called after we have entered the sandbox so if there are // operations (e.g. opening a file descriptor providing vsync events) that diff --git a/ui/base/strings/ui_strings.grd b/ui/base/strings/ui_strings.grd index 21d7001ba4..1a243e585d 100644 --- a/ui/base/strings/ui_strings.grd +++ b/ui/base/strings/ui_strings.grd @@ -206,6 +206,1072 @@ need to be translated for each locale.--> <release seq="1" allow_pseudo="false"> <messages fallback_to_english="true"> + <!-- time format --> + <message name="IDS_TIME_SECS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> secs + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_SEC_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> sec + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_SEC_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_SECS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> secs + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_SECS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_SECS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> secs + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_SECS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_SECS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> secs + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_SECS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_SECS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> secs + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_SECS_MANY" + desc=""> + NA + </message> + </if> + + <message name="IDS_TIME_MINS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> mins + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_MIN_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> min + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_MIN_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_MINS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> mins + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_MINS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_MINS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> mins + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_MINS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_MINS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> mins + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_MINS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_MINS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> mins + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_MINS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_HOURS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> hours + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_HOUR_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> hour + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_HOUR_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_HOURS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> hours + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_HOURS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_HOURS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> hours + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_HOURS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_HOURS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> hours + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_HOURS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_HOURS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> hours + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_HOURS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_DAYS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> days + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_DAY_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> day + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_DAY_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_DAYS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> days + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_DAYS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_DAYS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> days + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_DAYS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_DAYS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> days + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_DAYS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_DAYS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> days + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_DAYS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_REMAINING_SECS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> secs left + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_REMAINING_SEC_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> sec left + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_REMAINING_SEC_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_REMAINING_SECS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> secs left + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_REMAINING_SECS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_SECS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> secs left + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_SECS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_SECS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> secs left + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_SECS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_REMAINING_SECS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> secs left + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_REMAINING_SECS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_REMAINING_MINS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> mins left + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_REMAINING_MIN_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> min left + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_REMAINING_MIN_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_REMAINING_MINS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> mins left + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_REMAINING_MINS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_MINS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> mins left + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_MINS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_MINS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> mins left + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_MINS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_REMAINING_MINS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> mins left + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_REMAINING_MINS_MANY" + desc=""> + NA + </message> + </if> + + <message name="IDS_TIME_REMAINING_LONG_MINS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> minutes left + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_REMAINING_LONG_MIN_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> minute left + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_REMAINING_LONG_MIN_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_REMAINING_LONG_MINS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> minutes left + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_REMAINING_LONG_MINS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_LONG_MINS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> minutes left + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_LONG_MINS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_LONG_MINS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> minutes left + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_LONG_MINS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_REMAINING_LONG_MINS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> minutes left + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_REMAINING_LONG_MINS_MANY" + desc=""> + NA + </message> + </if> + + <message name="IDS_TIME_REMAINING_HOURS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> hours left + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_REMAINING_HOUR_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> hour left + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_REMAINING_HOUR_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_REMAINING_HOURS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> hours left + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_REMAINING_HOURS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_HOURS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> hours left + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_HOURS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_HOURS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> hours left + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_HOURS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_REMAINING_HOURS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> hours left + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_REMAINING_HOURS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_REMAINING_DAYS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> days left + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_REMAINING_DAY_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> day left + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_REMAINING_DAY_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_REMAINING_DAYS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> days left + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_REMAINING_DAYS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_DAYS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> days left + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_DAYS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_REMAINING_DAYS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> days left + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_REMAINING_DAYS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_REMAINING_DAYS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> days left + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_REMAINING_DAYS_MANY" + desc=""> + NA + </message> + </if> + + <message name="IDS_TIME_DURATION_LONG_SECS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> seconds + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_DURATION_LONG_SEC_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> second + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_SEC_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_DURATION_LONG_SECS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> seconds + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_SECS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_DURATION_LONG_SECS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> seconds + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_SECS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_DURATION_LONG_SECS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> seconds + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_SECS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_DURATION_LONG_SECS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> seconds + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_SECS_MANY" + desc=""> + NA + </message> + </if> + + <message name="IDS_TIME_DURATION_LONG_MINS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> minutes + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_DURATION_LONG_MIN_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> minute + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_MIN_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_DURATION_LONG_MINS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> minutes + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_MINS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_DURATION_LONG_MINS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> minutes + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_MINS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_DURATION_LONG_MINS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> minutes + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_MINS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_DURATION_LONG_MINS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> minutes + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_DURATION_LONG_MINS_MANY" + desc=""> + NA + </message> + </if> + + <message name="IDS_TIME_ELAPSED_SECS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> secs ago + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_ELAPSED_SEC_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> sec ago + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_ELAPSED_SEC_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_ELAPSED_SECS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> secs ago + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_ELAPSED_SECS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_SECS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> secs ago + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_SECS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_SECS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> secs ago + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_SECS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_ELAPSED_SECS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> secs ago + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_ELAPSED_SECS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_ELAPSED_MINS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> mins ago + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_ELAPSED_MIN_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> min ago + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_ELAPSED_MIN_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_ELAPSED_MINS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> mins ago + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_ELAPSED_MINS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_MINS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> mins ago + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_MINS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_MINS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> mins ago + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_MINS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_ELAPSED_MINS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> mins ago + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_ELAPSED_MINS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_ELAPSED_HOURS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> hours ago + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_ELAPSED_HOUR_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> hour ago + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_ELAPSED_HOUR_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_ELAPSED_HOURS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> hours ago + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_ELAPSED_HOURS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_HOURS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> hours ago + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_HOURS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_HOURS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> hours ago + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_HOURS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_ELAPSED_HOURS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> hours ago + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_ELAPSED_HOURS_MANY" + desc=""> + NA + </message> + </if> + + + <message name="IDS_TIME_ELAPSED_DAYS_DEFAULT" + desc="This is necessary for every language. This is the default for all the numbers NOT covered by special cases (singular, dual/two, few, many) some languages need. For CJK, Vietnamese, Turkish and Kannada, this is the only string necessary. For languages with singular-plural distinction, this is the generic plural. For Lithuanian, NUMBER_DEFAULT is 11 .. 19."> + <ph name="NUMBER_DEFAULT"><ex>37</ex>#</ph> days ago + </message> + + <if expr="lang not in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message name="IDS_TIME_ELAPSED_DAY_SINGULAR" + desc="NUMBER_ONE is one or one-like numbers : 1 (many European and most Indian languages), 1 and 0 (French, Brazilian Portuguese and Hindi), 1,21,31, .. (Russian, Ukrainian, Croatian, Serbian, Latvian, Lithuanian), or 1, 101, 201, .. (Slovenian). Do NOT translate this for CJK, Vietnamese, Turkish and Kannada"> + <ph name="NUMBER_ONE"><ex>1</ex>#</ph> day ago + </message> + </if> + <if expr="lang in ['zh-CN', 'zh-TW', 'ko', 'ja', 'vi', 'tr', 'kn']"> + <message translateable="false" name="IDS_TIME_ELAPSED_DAY_SINGULAR" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ar', 'ro', 'lv']"> + <message name="IDS_TIME_ELAPSED_DAYS_ZERO" + desc="NUMBER_ZERO is 0 (Arabic, Latvian) or 0, 2..19, 101..119, ... (Romanian). For other languages, do NOT translate."> + <ph name="NUMBER_ZERO"><ex>0</ex>#</ph> days ago + </message> + </if> + <if expr="lang not in ['ar', 'ro', 'lv']"> + <message translateable="false" name="IDS_TIME_ELAPSED_DAYS_ZERO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ga', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_DAYS_TWO" + desc="NUMBER_TWO is two or two-like/dual numbers : 2 (Arabic and Irish) or 2, 102, 202 ... (Slovenian). For other languages, do NOT translated."> + <ph name="NUMBER_TWO"><ex>2</ex>#</ph> days ago + </message> + </if> + <if expr="lang not in ['ga', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_DAYS_TWO" + desc=""> + NA + </message> + </if> + + <if expr="lang in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message name="IDS_TIME_ELAPSED_DAYS_FEW" + desc="NUMBER_FEW is few or few-like numbers in Arabic, Russian, Polish, Croatian, Serbian, Ukrainian, Czech, Slovak, Slovenian, Latvian. For other languages, do NOT translate."> + <ph name="NUMBER_FEW"><ex>3</ex>#</ph> days ago + </message> + </if> + <if expr="lang not in ['ru', 'lt', 'hr', 'uk', 'cs', 'sk', 'pl', 'sl', 'ar']"> + <message translateable="false" name="IDS_TIME_ELAPSED_DAYS_FEW" + desc=""> + NA + </message> + </if> + + <if expr="lang == 'ar'"> + <message name="IDS_TIME_ELAPSED_DAYS_MANY" + desc="NUMBER_MANY is 11 through 99 in Arabic. For all other languages, do NOT translate."> + <ph name="NUMBER_MANY"><ex>23</ex>#</ph> days ago + </message> + </if> + <if expr="lang != 'ar'"> + <message translateable="false" name="IDS_TIME_ELAPSED_DAYS_MANY" + desc=""> + NA + </message> + </if> + + <message name="IDS_PAST_TIME_TODAY" desc="Relative day today"> + Today + </message> + <message name="IDS_PAST_TIME_YESTERDAY" desc="Relative day yesterday"> + Yesterday + </message> + <!-- Menus --> <message name="IDS_APP_MENU_EMPTY_SUBMENU" desc="Used when a submenu has no entries"> (empty) @@ -464,9 +1530,16 @@ need to be translated for each locale.--> <message name="IDS_MESSAGE_CENTER_SETTINGS_GO_BACK_BUTTON_TOOLTIP" desc="The tooltip on back button that returns from settings to the notification list."> Go back to notifications </message> - <message name="IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION" desc="The label to describe the settings dialog."> - Allow notifications from the following: - </message> + <if expr="is_macosx"> + <message name="IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION" desc="The label to describe the settings dialog."> + Allow notifications from the following: + </message> + </if> + <if expr="not is_macosx"> + <message name="IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION" desc="The label to describe the settings dialog."> + Manage notifications per profile: + </message> + </if> <message name="IDS_MESSAGE_CENTER_SETTINGS" desc="The menu entry or button for visiting the appropriate settings page."> Settings... </message> diff --git a/ui/base/theme_provider.h b/ui/base/theme_provider.h index 007094f88c..22f70a0025 100644 --- a/ui/base/theme_provider.h +++ b/ui/base/theme_provider.h @@ -78,29 +78,17 @@ class UI_EXPORT ThemeProvider { #if defined(OS_MACOSX) && !defined(TOOLKIT_VIEWS) // Gets the NSImage with the specified |id|. - // - // The bitmap is not assumed to exist. If a theme does not provide an image, - // if |allow_default| is true, then the default image will be returned, else - // this function will return nil. - virtual NSImage* GetNSImageNamed(int id, bool allow_default) const = 0; + virtual NSImage* GetNSImageNamed(int id) const = 0; // Gets the NSImage that GetNSImageNamed (above) would return, but returns it // as a pattern color. - virtual NSColor* GetNSImageColorNamed(int id, bool allow_default) const = 0; + virtual NSColor* GetNSImageColorNamed(int id) const = 0; // Gets the NSColor with the specified |id|. - // - // The color is not assumed to exist. If a theme does not provide an color, if - // |allow_default| is true, then the default color will be returned, else this - // function will return nil. - virtual NSColor* GetNSColor(int id, bool allow_default) const = 0; + virtual NSColor* GetNSColor(int id) const = 0; // Gets the NSColor for tinting with the specified |id|. - // - // The tint is not assumed to exist. If a theme does not provide a tint with - // that id, if |allow_default| is true, then the default tint will be - // returned, else this function will return nil. - virtual NSColor* GetNSColorTint(int id, bool allow_default) const = 0; + virtual NSColor* GetNSColorTint(int id) const = 0; // Gets the NSGradient with the specified |id|. virtual NSGradient* GetNSGradient(int id) const = 0; diff --git a/ui/gl/android/surface_texture_bridge.cc b/ui/gl/android/surface_texture_bridge.cc index 19ce2a708d..c4206a983e 100644 --- a/ui/gl/android/surface_texture_bridge.cc +++ b/ui/gl/android/surface_texture_bridge.cc @@ -43,8 +43,7 @@ bool GlContextMethodsAvailable() { namespace gfx { -SurfaceTextureBridge::SurfaceTextureBridge(int texture_id) - : texture_id_(texture_id) { +SurfaceTextureBridge::SurfaceTextureBridge(int texture_id) { JNIEnv* env = AttachCurrentThread(); CHECK(env); RegisterNativesIfNeeded(env); diff --git a/ui/gl/android/surface_texture_bridge.h b/ui/gl/android/surface_texture_bridge.h index f3f3bcf00e..fc7fb5416a 100644 --- a/ui/gl/android/surface_texture_bridge.h +++ b/ui/gl/android/surface_texture_bridge.h @@ -53,10 +53,6 @@ class GL_EXPORT SurfaceTextureBridge // by calling ANativeWindow_release(). ANativeWindow* CreateSurface(); - int texture_id() const { - return texture_id_; - } - const base::android::JavaRef<jobject>& j_surface_texture() const { return j_surface_texture_; } @@ -65,8 +61,6 @@ class GL_EXPORT SurfaceTextureBridge friend class base::RefCountedThreadSafe<SurfaceTextureBridge>; ~SurfaceTextureBridge(); - const int texture_id_; - // Java SurfaceTexture instance. base::android::ScopedJavaGlobalRef<jobject> j_surface_texture_; diff --git a/ui/message_center/cocoa/settings_controller.h b/ui/message_center/cocoa/settings_controller.h index d81b99b6bc..380ae1403c 100644 --- a/ui/message_center/cocoa/settings_controller.h +++ b/ui/message_center/cocoa/settings_controller.h @@ -26,6 +26,7 @@ class NotifierSettingsObserverMac : public NotifierSettingsObserver { // Overridden from NotifierSettingsObserver: virtual void UpdateIconImage(const NotifierId& notifier_id, const gfx::Image& icon) OVERRIDE; + virtual void NotifierGroupChanged() OVERRIDE; private: MCSettingsController* settings_controller_; // weak, owns this diff --git a/ui/message_center/cocoa/settings_controller.mm b/ui/message_center/cocoa/settings_controller.mm index 45c66ea71a..f34f9f0172 100644 --- a/ui/message_center/cocoa/settings_controller.mm +++ b/ui/message_center/cocoa/settings_controller.mm @@ -110,6 +110,8 @@ void NotifierSettingsObserverMac::UpdateIconImage(const NotifierId& notifier_id, [settings_controller_ setIcon:icon.AsNSImage() forNotifierId:notifier_id]; } +void NotifierSettingsObserverMac::NotifierGroupChanged() {} + } // namespace message_center @implementation MCSettingsController diff --git a/ui/message_center/fake_notifier_settings_provider.cc b/ui/message_center/fake_notifier_settings_provider.cc index 0695d085cb..66b193b0b8 100644 --- a/ui/message_center/fake_notifier_settings_provider.cc +++ b/ui/message_center/fake_notifier_settings_provider.cc @@ -4,14 +4,36 @@ #include "ui/message_center/fake_notifier_settings_provider.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/gfx/image/image.h" + namespace message_center { FakeNotifierSettingsProvider::FakeNotifierSettingsProvider( const std::vector<Notifier*>& notifiers) - : notifiers_(notifiers), closed_called_count_(0) {} + : notifiers_(notifiers), + notifier_group_(gfx::Image(), + UTF8ToUTF16("Fake name"), + UTF8ToUTF16("fake@email.com"), + true), + closed_called_count_(0) {} FakeNotifierSettingsProvider::~FakeNotifierSettingsProvider() {} +size_t FakeNotifierSettingsProvider::GetNotifierGroupCount() const { return 1; } + +const message_center::NotifierGroup& +FakeNotifierSettingsProvider::GetNotifierGroupAt(size_t index) const { + return notifier_group_; +} + +void FakeNotifierSettingsProvider::SwitchToNotifierGroup(size_t index) {} + +const message_center::NotifierGroup& +FakeNotifierSettingsProvider::GetActiveNotifierGroup() const { + return notifier_group_; +} + void FakeNotifierSettingsProvider::GetNotifierList( std::vector<Notifier*>* notifiers) { notifiers->clear(); diff --git a/ui/message_center/fake_notifier_settings_provider.h b/ui/message_center/fake_notifier_settings_provider.h index 47a3549838..f2c5cd7b8e 100644 --- a/ui/message_center/fake_notifier_settings_provider.h +++ b/ui/message_center/fake_notifier_settings_provider.h @@ -14,7 +14,14 @@ namespace message_center { class FakeNotifierSettingsProvider : public NotifierSettingsProvider { public: FakeNotifierSettingsProvider(const std::vector<Notifier*>& notifiers); - ~FakeNotifierSettingsProvider(); + virtual ~FakeNotifierSettingsProvider(); + + virtual size_t GetNotifierGroupCount() const OVERRIDE; + virtual const message_center::NotifierGroup& GetNotifierGroupAt( + size_t index) const OVERRIDE; + virtual void SwitchToNotifierGroup(size_t index) OVERRIDE; + virtual const message_center::NotifierGroup& GetActiveNotifierGroup() const + OVERRIDE; virtual void GetNotifierList(std::vector<Notifier*>* notifiers) OVERRIDE; @@ -31,6 +38,7 @@ class FakeNotifierSettingsProvider : public NotifierSettingsProvider { private: std::vector<Notifier*> notifiers_; std::map<const Notifier*, bool> enabled_; + const NotifierGroup notifier_group_; int closed_called_count_; }; diff --git a/ui/message_center/message_center.gyp b/ui/message_center/message_center.gyp index eac6938cba..dc1f1ab6fe 100644 --- a/ui/message_center/message_center.gyp +++ b/ui/message_center/message_center.gyp @@ -70,6 +70,8 @@ 'views/message_bubble_base.h', 'views/message_center_bubble.cc', 'views/message_center_bubble.h', + 'views/message_center_button_bar.cc', + 'views/message_center_button_bar.h', 'views/message_center_view.cc', 'views/message_center_view.h', 'views/message_popup_collection.cc', diff --git a/ui/message_center/notifier_settings.cc b/ui/message_center/notifier_settings.cc index 85c6097bad..4355b9459f 100644 --- a/ui/message_center/notifier_settings.cc +++ b/ui/message_center/notifier_settings.cc @@ -50,8 +50,15 @@ Notifier::Notifier(const NotifierId& notifier_id, Notifier::~Notifier() { } -std::string ToString( - NotifierId::SystemComponentNotifierType type) { +NotifierGroup::NotifierGroup(const gfx::Image& icon, + const string16& name, + const string16& login_info, + size_t index) + : icon(icon), name(name), login_info(login_info), index(index) {} + +NotifierGroup::~NotifierGroup() {} + +std::string ToString(NotifierId::SystemComponentNotifierType type) { switch (type) { case NotifierId::SCREENSHOT: return "screenshot"; diff --git a/ui/message_center/notifier_settings.h b/ui/message_center/notifier_settings.h index c494048e56..05fc7fef33 100644 --- a/ui/message_center/notifier_settings.h +++ b/ui/message_center/notifier_settings.h @@ -84,6 +84,30 @@ struct MESSAGE_CENTER_EXPORT Notifier { DISALLOW_COPY_AND_ASSIGN(Notifier); }; +struct MESSAGE_CENTER_EXPORT NotifierGroup { + NotifierGroup(const gfx::Image& icon, + const string16& name, + const string16& login_info, + size_t index); + ~NotifierGroup(); + + // Icon of a notifier group. + const gfx::Image icon; + + // Display name of a notifier group. + const string16 name; + + // More display information about the notifier group. + string16 login_info; + + // Unique identifier for the notifier group so that they can be selected in + // the UI. + const size_t index; + + private: + DISALLOW_COPY_AND_ASSIGN(NotifierGroup); +}; + MESSAGE_CENTER_EXPORT std::string ToString( NotifierId::SystemComponentNotifierType type); MESSAGE_CENTER_EXPORT NotifierId::SystemComponentNotifierType @@ -96,16 +120,36 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsObserver { // Called when an icon in the controller has been updated. virtual void UpdateIconImage(const NotifierId& notifier_id, const gfx::Image& icon) = 0; + + // Called when any change happens to the set of notifier groups. + virtual void NotifierGroupChanged() = 0; }; // A class used by NotifierSettingsView to integrate with a setting system // for the clients of this module. class MESSAGE_CENTER_EXPORT NotifierSettingsProvider { public: + virtual ~NotifierSettingsProvider() {}; + // Sets the delegate. virtual void AddObserver(NotifierSettingsObserver* observer) = 0; virtual void RemoveObserver(NotifierSettingsObserver* observer) = 0; + // Returns the number of notifier groups available. + virtual size_t GetNotifierGroupCount() const = 0; + + // Requests the model for a particular notifier group. + virtual const message_center::NotifierGroup& GetNotifierGroupAt( + size_t index) const = 0; + + // Informs the settings provider that further requests to GetNotifierList + // should return notifiers for the specified notifier group. + virtual void SwitchToNotifierGroup(size_t index) = 0; + + // Requests the currently active notifier group. + virtual const message_center::NotifierGroup& GetActiveNotifierGroup() + const = 0; + // Collects the current notifier list and fills to |notifiers|. Caller takes // the ownership of the elements of |notifiers|. virtual void GetNotifierList(std::vector<Notifier*>* notifiers) = 0; diff --git a/ui/message_center/views/message_center_button_bar.cc b/ui/message_center/views/message_center_button_bar.cc new file mode 100644 index 0000000000..b436bf30d9 --- /dev/null +++ b/ui/message_center/views/message_center_button_bar.cc @@ -0,0 +1,210 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/views/message_center_button_bar.h" + +#include "grit/ui_resources.h" +#include "grit/ui_strings.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/text_constants.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/message_center_style.h" +#include "ui/message_center/notifier_settings.h" +#include "ui/message_center/views/message_center_view.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/button/menu_button.h" +#include "ui/views/controls/button/menu_button_listener.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/menu/menu_runner.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/grid_layout.h" + +namespace message_center { + +namespace { +const int kButtonSize = 40; +const int kChevronMargin = 4; +const int kFooterLeftMargin = 17; +const int kFooterRightMargin = 14; +} // namespace + +// NotificationCenterButton //////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +class NotificationCenterButton : public views::ToggleImageButton { + public: + NotificationCenterButton(views::ButtonListener* listener, + int normal_id, + int hover_id, + int pressed_id, + int text_id); + + protected: + // Overridden from views::View: + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationCenterButton); +}; + +NotificationCenterButton::NotificationCenterButton( + views::ButtonListener* listener, + int normal_id, + int hover_id, + int pressed_id, + int text_id) + : views::ToggleImageButton(listener) { + ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); + SetImage(STATE_NORMAL, resource_bundle.GetImageSkiaNamed(normal_id)); + SetImage(STATE_HOVERED, resource_bundle.GetImageSkiaNamed(hover_id)); + SetImage(STATE_PRESSED, resource_bundle.GetImageSkiaNamed(pressed_id)); + SetImageAlignment(views::ImageButton::ALIGN_CENTER, + views::ImageButton::ALIGN_MIDDLE); + SetTooltipText(resource_bundle.GetLocalizedString(text_id)); + set_focusable(true); + set_request_focus_on_press(false); +} + +gfx::Size NotificationCenterButton::GetPreferredSize() { + return gfx::Size(kButtonSize, kButtonSize); +} + +void NotificationCenterButton::OnPaintFocusBorder(gfx::Canvas* canvas) { + if (HasFocus() && (focusable() || IsAccessibilityFocusable())) { + canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3), + kFocusBorderColor); + } +} + +// MessageCenterButtonBar ///////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +MessageCenterButtonBar::MessageCenterButtonBar( + MessageCenterView* message_center_view, + MessageCenter* message_center, + NotifierSettingsProvider* notifier_settings_provider, + bool settings_initially_visible) + : message_center_view_(message_center_view), + message_center_(message_center), + close_all_button_(NULL), + quiet_mode_button_(NULL) { + if (get_use_acceleration_when_possible()) + SetPaintToLayer(true); + set_background( + views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); + + views::Label* notification_label = new views::Label( + l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_FOOTER_TITLE)); + notification_label->SetAutoColorReadabilityEnabled(false); + notification_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + notification_label->SetEnabledColor(kRegularTextColor); + AddChildView(notification_label); + + views::View* button_container = new views::View; + button_container->SetLayoutManager( + new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); + quiet_mode_button_ = new NotificationCenterButton( + this, + IDR_NOTIFICATION_DO_NOT_DISTURB, + IDR_NOTIFICATION_DO_NOT_DISTURB_HOVER, + IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED, + IDS_MESSAGE_CENTER_QUIET_MODE_BUTTON_TOOLTIP); + ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); + quiet_mode_button_->SetToggledImage( + views::Button::STATE_NORMAL, + resource_bundle.GetImageSkiaNamed( + IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED)); + quiet_mode_button_->SetToggledImage( + views::Button::STATE_HOVERED, + resource_bundle.GetImageSkiaNamed( + IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED)); + quiet_mode_button_->SetToggledImage( + views::Button::STATE_PRESSED, + resource_bundle.GetImageSkiaNamed( + IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED)); + quiet_mode_button_->SetToggled(message_center->IsQuietMode()); + button_container->AddChildView(quiet_mode_button_); + + close_all_button_ = + new NotificationCenterButton(this, + IDR_NOTIFICATION_CLEAR_ALL, + IDR_NOTIFICATION_CLEAR_ALL_HOVER, + IDR_NOTIFICATION_CLEAR_ALL_PRESSED, + IDS_MESSAGE_CENTER_CLEAR_ALL); + button_container->AddChildView(close_all_button_); + settings_button_ = + new NotificationCenterButton(this, + IDR_NOTIFICATION_SETTINGS, + IDR_NOTIFICATION_SETTINGS_HOVER, + IDR_NOTIFICATION_SETTINGS_PRESSED, + IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL); + button_container->AddChildView(settings_button_); + + gfx::ImageSkia* settings_image = + ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_NOTIFICATION_SETTINGS); + int image_margin = std::max(0, (kButtonSize - settings_image->width()) / 2); + views::GridLayout* layout = new views::GridLayout(this); + SetLayoutManager(layout); + layout->SetInsets( + 0, kFooterLeftMargin, 0, std::max(0, kFooterRightMargin - image_margin)); + views::ColumnSet* column = layout->AddColumnSet(0); + column->AddColumn(views::GridLayout::FILL, + views::GridLayout::FILL, + 1.0f, + views::GridLayout::USE_PREF, + 0, + 0); + column->AddColumn(views::GridLayout::LEADING, + views::GridLayout::FILL, + 0, + views::GridLayout::USE_PREF, + 0, + 0); + layout->StartRow(0, 0); + layout->AddView(notification_label); + layout->AddView(button_container); +} + +MessageCenterButtonBar::~MessageCenterButtonBar() {} + +void MessageCenterButtonBar::SetAllButtonsEnabled(bool enabled) { + if (close_all_button_) + close_all_button_->SetEnabled(enabled); + settings_button_->SetEnabled(enabled); + quiet_mode_button_->SetEnabled(enabled); +} + +void MessageCenterButtonBar::SetCloseAllButtonVisible(bool visible) { + if (close_all_button_) + close_all_button_->SetVisible(visible); +} + +void MessageCenterButtonBar::ChildVisibilityChanged(views::View* child) { + InvalidateLayout(); +} + +void MessageCenterButtonBar::ButtonPressed(views::Button* sender, + const ui::Event& event) { + if (sender == close_all_button_) { + message_center_view()->ClearAllNotifications(); + } else if (sender == settings_button_) { + MessageCenterView* center_view = message_center_view(); + center_view->SetSettingsVisible(!center_view->settings_visible()); + } else if (sender == quiet_mode_button_) { + if (message_center()->IsQuietMode()) + message_center()->SetQuietMode(false); + else + message_center()->EnterQuietModeWithExpire(base::TimeDelta::FromDays(1)); + quiet_mode_button_->SetToggled(message_center()->IsQuietMode()); + } else { + NOTREACHED(); + } +} + +} // namespace message_center diff --git a/ui/message_center/views/message_center_button_bar.h b/ui/message_center/views/message_center_button_bar.h new file mode 100644 index 0000000000..72b30c9317 --- /dev/null +++ b/ui/message_center/views/message_center_button_bar.h @@ -0,0 +1,68 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_BUTTON_BAR_H_ +#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_BUTTON_BAR_H_ + +#include "ui/views/controls/button/button.h" +#include "ui/views/view.h" + +namespace views { +class Label; +} + +namespace message_center { + +class ButtonBarSettingsLabel; +class MessageCenter; +class MessageCenterTray; +class MessageCenterView; +class NotificationCenterButton; +class NotifierSettingsProvider; + +// MessageCenterButtonBar is the class that shows the content outside the main +// notification area - the label (or NotifierGroup switcher) and the buttons. +class MessageCenterButtonBar : public views::View, + public views::ButtonListener { + public: + MessageCenterButtonBar(MessageCenterView* message_center_view, + MessageCenter* message_center, + NotifierSettingsProvider* notifier_settings_provider, + bool settings_initially_visible); + virtual ~MessageCenterButtonBar(); + + // Enables or disables all of the buttons in the center. This is used to + // prevent user clicks during the close-all animation. + virtual void SetAllButtonsEnabled(bool enabled); + + // Sometimes we shouldn't see the close-all button. + void SetCloseAllButtonVisible(bool visible); + + private: + // Overridden from views::View: + virtual void ChildVisibilityChanged(views::View* child) OVERRIDE; + + // Overridden from views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const ui::Event& event) OVERRIDE; + + MessageCenterView* message_center_view() const { + return message_center_view_; + } + MessageCenter* message_center() const { return message_center_; } + + MessageCenterView* message_center_view_; // Weak reference. + MessageCenter* message_center_; // Weak reference. + + // Sub-views of the button bar. + views::Button* close_all_button_; + NotificationCenterButton* settings_button_; + NotificationCenterButton* quiet_mode_button_; + + DISALLOW_COPY_AND_ASSIGN(MessageCenterButtonBar); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_BUTTON_BAR_H_ diff --git a/ui/message_center/views/message_center_view.cc b/ui/message_center/views/message_center_view.cc index ee7ac74f4a..9f0d2fdf31 100644 --- a/ui/message_center/views/message_center_view.cc +++ b/ui/message_center/views/message_center_view.cc @@ -10,22 +10,17 @@ #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" -#include "grit/ui_resources.h" #include "grit/ui_strings.h" #include "ui/base/animation/multi_animation.h" #include "ui/base/animation/slide_animation.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/insets.h" -#include "ui/gfx/point.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" -#include "ui/gfx/text_constants.h" #include "ui/message_center/message_center.h" #include "ui/message_center/message_center_style.h" -#include "ui/message_center/message_center_tray.h" -#include "ui/message_center/message_center_util.h" +#include "ui/message_center/views/message_center_button_bar.h" #include "ui/message_center/views/message_view.h" #include "ui/message_center/views/notification_view.h" #include "ui/message_center/views/notifier_settings_view.h" @@ -34,238 +29,30 @@ #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/controls/button/button.h" -#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/scrollbar/overlay_scroll_bar.h" #include "ui/views/layout/box_layout.h" -#include "ui/views/layout/grid_layout.h" -#include "ui/views/painter.h" #include "ui/views/widget/widget.h" namespace message_center { namespace { -const int kMinScrollViewHeight = 100; -const int kFooterLeftMargin = 17; -const int kFooterRightMargin = 14; -const int kButtonSize = 40; -const SkColor kNoNotificationsTextColor = SkColorSetRGB(0xb4, 0xb4, 0xb4); const SkColor kBorderDarkColor = SkColorSetRGB(0xaa, 0xaa, 0xaa); -const SkColor kTransparentColor = SkColorSetARGB(0, 0, 0, 0); const SkColor kButtonTextHighlightColor = SkColorSetRGB(0x2a, 0x2a, 0x2a); const SkColor kButtonTextHoverColor = SkColorSetRGB(0x2a, 0x2a, 0x2a); +const SkColor kNoNotificationsTextColor = SkColorSetRGB(0xb4, 0xb4, 0xb4); +const SkColor kTransparentColor = SkColorSetARGB(0, 0, 0, 0); const int kAnimateClearingNextNotificationDelayMS = 40; +const int kButtonBarBorderThickness = 1; +const int kMinScrollViewHeight = 100; -static const int kDefaultFrameRateHz = 60; static const int kDefaultAnimationDurationMs = 120; +static const int kDefaultFrameRateHz = 60; } // namespace -// NotificationCenterButton //////////////////////////////////////////////////// - -class NotificationCenterButton : public views::ToggleImageButton { - public: - NotificationCenterButton(views::ButtonListener* listener, - int normal_id, - int hover_id, - int pressed_id, - int text_id); - - protected: - // Overridden from views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(NotificationCenterButton); -}; - -NotificationCenterButton::NotificationCenterButton( - views::ButtonListener* listener, - int normal_id, - int hover_id, - int pressed_id, - int text_id) - : views::ToggleImageButton(listener) { - ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); - SetImage(STATE_NORMAL, resource_bundle.GetImageSkiaNamed(normal_id)); - SetImage(STATE_HOVERED, resource_bundle.GetImageSkiaNamed(hover_id)); - SetImage(STATE_PRESSED, resource_bundle.GetImageSkiaNamed(pressed_id)); - SetImageAlignment(views::ImageButton::ALIGN_CENTER, - views::ImageButton::ALIGN_MIDDLE); - SetTooltipText(resource_bundle.GetLocalizedString(text_id)); - set_focusable(true); - set_request_focus_on_press(false); -} - -gfx::Size NotificationCenterButton::GetPreferredSize() { - return gfx::Size(kButtonSize, kButtonSize); -} - -void NotificationCenterButton::OnPaintFocusBorder(gfx::Canvas* canvas) { - if (HasFocus() && (focusable() || IsAccessibilityFocusable())) { - canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3), - kFocusBorderColor); - } -} - -// MessageCenterButtonBar ////////////////////////////////////////////////// - -class MessageCenterButtonBar : public views::View, - public views::ButtonListener { - public: - MessageCenterButtonBar(MessageCenterView* message_center_view, - MessageCenter* message_center); - virtual ~MessageCenterButtonBar(); - - virtual void SetAllButtonsEnabled(bool enabled); - void SetCloseAllVisible(bool visible); - - private: - // Overridden from views::View: - virtual void ChildVisibilityChanged(views::View* child) OVERRIDE; - - // Overridden from views::ButtonListener: - virtual void ButtonPressed(views::Button* sender, const ui::Event& event) - OVERRIDE; - - MessageCenterView* message_center_view() const { - return message_center_view_; - } - MessageCenter* message_center() const { return message_center_; } - MessageCenterTray* tray() const { return tray_; } - views::Button* close_all_button() const { return close_all_button_; } - void set_close_all_button(views::Button* button) { - close_all_button_ = button; - } - - MessageCenterView* message_center_view_; // Weak reference. - MessageCenter* message_center_; // Weak reference. - MessageCenterTray* tray_; // Weak reference. - views::Button* close_all_button_; - NotificationCenterButton* settings_button_; - NotificationCenterButton* quiet_mode_button_; - - DISALLOW_COPY_AND_ASSIGN(MessageCenterButtonBar); -}; - -MessageCenterButtonBar::MessageCenterButtonBar( - MessageCenterView* message_center_view, - MessageCenter* message_center) - : message_center_view_(message_center_view), - message_center_(message_center), - close_all_button_(NULL) { - if (get_use_acceleration_when_possible()) - SetPaintToLayer(true); - set_background(views::Background::CreateSolidBackground( - kMessageCenterBackgroundColor)); - - views::Label* notification_label = new views::Label(l10n_util::GetStringUTF16( - IDS_MESSAGE_CENTER_FOOTER_TITLE)); - notification_label->SetAutoColorReadabilityEnabled(false); - notification_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); - notification_label->SetEnabledColor(kRegularTextColor); - AddChildView(notification_label); - - views::View* button_container = new views::View; - button_container->SetLayoutManager( - new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); - quiet_mode_button_ = new NotificationCenterButton( - this, - IDR_NOTIFICATION_DO_NOT_DISTURB, - IDR_NOTIFICATION_DO_NOT_DISTURB_HOVER, - IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED, - IDS_MESSAGE_CENTER_QUIET_MODE_BUTTON_TOOLTIP); - ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); - quiet_mode_button_->SetToggledImage( - views::Button::STATE_NORMAL, - resource_bundle.GetImageSkiaNamed( - IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED)); - quiet_mode_button_->SetToggledImage( - views::Button::STATE_HOVERED, - resource_bundle.GetImageSkiaNamed( - IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED)); - quiet_mode_button_->SetToggledImage( - views::Button::STATE_PRESSED, - resource_bundle.GetImageSkiaNamed( - IDR_NOTIFICATION_DO_NOT_DISTURB_PRESSED)); - quiet_mode_button_->SetToggled(message_center->IsQuietMode()); - button_container->AddChildView(quiet_mode_button_); - - NotificationCenterButton* close_all_button = new NotificationCenterButton( - this, - IDR_NOTIFICATION_CLEAR_ALL, - IDR_NOTIFICATION_CLEAR_ALL_HOVER, - IDR_NOTIFICATION_CLEAR_ALL_PRESSED, - IDS_MESSAGE_CENTER_CLEAR_ALL); - button_container->AddChildView(close_all_button); - set_close_all_button(close_all_button); - settings_button_ = new NotificationCenterButton( - this, - IDR_NOTIFICATION_SETTINGS, - IDR_NOTIFICATION_SETTINGS_HOVER, - IDR_NOTIFICATION_SETTINGS_PRESSED, - IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL); - button_container->AddChildView(settings_button_); - - gfx::ImageSkia* settings_image = - ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - IDR_NOTIFICATION_SETTINGS); - int image_margin = std::max(0, (kButtonSize - settings_image->width()) / 2); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); - layout->SetInsets( - 0, kFooterLeftMargin, 0, std::max(0, kFooterRightMargin - image_margin)); - views::ColumnSet* column = layout->AddColumnSet(0); - column->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, - 1.0f, views::GridLayout::USE_PREF, 0, 0); - column->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, - 0, views::GridLayout::USE_PREF, 0, 0); - layout->StartRow(0, 0); - layout->AddView(notification_label); - layout->AddView(button_container); -} - -MessageCenterButtonBar::~MessageCenterButtonBar() {} - -void MessageCenterButtonBar::SetAllButtonsEnabled(bool enabled) { - if (close_all_button_) - close_all_button_->SetEnabled(enabled); - settings_button_->SetEnabled(enabled); - quiet_mode_button_->SetEnabled(enabled); -} - -void MessageCenterButtonBar::SetCloseAllVisible(bool visible) { - if (close_all_button_) - close_all_button_->SetVisible(visible); -} - -// Overridden from views::View: -void MessageCenterButtonBar::ChildVisibilityChanged(views::View* child) { - InvalidateLayout(); -} - -// Overridden from views::ButtonListener: -void MessageCenterButtonBar::ButtonPressed(views::Button* sender, - const ui::Event& event) { - if (sender == close_all_button()) { - message_center_view()->ClearAllNotifications(); - } else if (sender == settings_button_) { - MessageCenterView* center_view = static_cast<MessageCenterView*>(parent()); - center_view->SetSettingsVisible(!center_view->settings_visible()); - } else if (sender == quiet_mode_button_) { - if (message_center()->IsQuietMode()) - message_center()->SetQuietMode(false); - else - message_center()->EnterQuietModeWithExpire(base::TimeDelta::FromDays(1)); - quiet_mode_button_->SetToggled(message_center()->IsQuietMode()); - } else { - NOTREACHED(); - } -} - // BoundedScrollView /////////////////////////////////////////////////////////// // A custom scroll view whose height has a minimum and maximum value and whose @@ -806,17 +593,33 @@ MessageCenterView::MessageCenterView(MessageCenter* message_center, tray_(tray), top_down_(top_down), settings_visible_(initially_settings_visible), + source_view_(NULL), + source_height_(0), + target_view_(NULL), + target_height_(0), is_closing_(false) { message_center_->AddObserver(this); set_notify_enter_exit_on_child(true); set_background(views::Background::CreateSolidBackground( kMessageCenterBackgroundColor)); - button_bar_ = new MessageCenterButtonBar(this, message_center); + NotifierSettingsProvider* notifier_settings_provider = + message_center_->GetNotifierSettingsProvider(); + button_bar_ = new MessageCenterButtonBar(this, + message_center, + notifier_settings_provider, + initially_settings_visible); const int button_height = button_bar_->GetPreferredSize().height(); - scroller_ = new BoundedScrollView(kMinScrollViewHeight, - max_height - button_height); + button_bar_->set_border(views::Border::CreateSolidSidedBorder( + top_down_ ? 0 : kButtonBarBorderThickness, + 0, + top_down_ ? kButtonBarBorderThickness : 0, + 0, + kFooterDelimiterColor)); + + scroller_ = + new BoundedScrollView(kMinScrollViewHeight, max_height - button_height); if (get_use_acceleration_when_possible()) { scroller_->SetPaintToLayer(true); @@ -832,8 +635,7 @@ MessageCenterView::MessageCenterView(MessageCenter* message_center, message_list_view_->AddChildView(no_notifications_message_view_); scroller_->SetContents(message_list_view_); - settings_view_ = new NotifierSettingsView( - message_center_->GetNotifierSettingsProvider()); + settings_view_ = new NotifierSettingsView(notifier_settings_provider); if (initially_settings_visible) scroller_->SetVisible(false); @@ -915,13 +717,6 @@ void MessageCenterView::SetSettingsVisible(bool visible) { settings_transition_animation_->Start(); } -void MessageCenterView::SetIsClosing(bool is_closing) { - is_closing_ = is_closing; - if (is_closing) - message_center_->RemoveObserver(this); - else - message_center_->AddObserver(this); -} void MessageCenterView::ClearAllNotifications() { if (is_closing_) @@ -942,11 +737,26 @@ size_t MessageCenterView::NumMessageViewsForTest() const { return message_list_view_->child_count(); } +void MessageCenterView::OnSettingsChanged() { + scroller_->InvalidateLayout(); + PreferredSizeChanged(); + Layout(); +} + +void MessageCenterView::SetIsClosing(bool is_closing) { + is_closing_ = is_closing; + if (is_closing) + message_center_->RemoveObserver(this); + else + message_center_->AddObserver(this); +} + void MessageCenterView::Layout() { if (is_closing_) return; - int button_height = button_bar_->GetHeightForWidth(width()); + int button_height = button_bar_->GetHeightForWidth(width()) + + button_bar_->GetInsets().height(); // Skip unnecessary re-layout of contents during the resize animation. if (settings_transition_animation_ && settings_transition_animation_->is_animating() && @@ -966,27 +776,6 @@ void MessageCenterView::Layout() { width(), height() - button_height); - bool is_scrollable = false; - if (scroller_->visible()) - is_scrollable = scroller_->height() < message_list_view_->height(); - else - is_scrollable = settings_view_->IsScrollable(); - - if (is_scrollable && !button_bar_->border()) { - // Draw separator line on the top of the button bar if it is on the bottom - // or draw it at the bottom if the bar is on the top. - button_bar_->set_border(views::Border::CreateSolidSidedBorder( - top_down_ ? 0 : 1, - 0, - top_down_ ? 1 : 0, - 0, - kFooterDelimiterColor)); - button_bar_->SchedulePaint(); - } else if (!is_scrollable && button_bar_->border()) { - button_bar_->set_border(NULL); - button_bar_->SchedulePaint(); - } - button_bar_->SetBounds(0, top_down_ ? 0 : height() - button_height, width(), @@ -1030,7 +819,8 @@ int MessageCenterView::GetHeightForWidth(int width) { content_height += scroller_->GetHeightForWidth(width); else content_height += settings_view_->GetHeightForWidth(width); - return button_bar_->GetHeightForWidth(width) + content_height; + return button_bar_->GetHeightForWidth(width) + + button_bar_->GetInsets().height() + content_height; } bool MessageCenterView::OnMouseWheel(const ui::MouseWheelEvent& event) { @@ -1178,15 +968,12 @@ void MessageCenterView::AddNotificationAt(const Notification& notification, } void MessageCenterView::NotificationsChanged() { - if (!message_views_.empty()) { - no_notifications_message_view_->SetVisible(false); - button_bar_->SetCloseAllVisible(true); - scroller_->set_focusable(true); - } else { - no_notifications_message_view_->SetVisible(true); - button_bar_->SetCloseAllVisible(false); - scroller_->set_focusable(false); - } + bool no_message_views = message_views_.empty(); + + no_notifications_message_view_->SetVisible(no_message_views); + button_bar_->SetCloseAllButtonVisible(!no_message_views); + scroller_->set_focusable(!no_message_views); + scroller_->InvalidateLayout(); PreferredSizeChanged(); Layout(); diff --git a/ui/message_center/views/message_center_view.h b/ui/message_center/views/message_center_view.h index 506a3a4533..d53910c273 100644 --- a/ui/message_center/views/message_center_view.h +++ b/ui/message_center/views/message_center_view.h @@ -54,6 +54,7 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, size_t NumMessageViewsForTest() const; void SetSettingsVisible(bool visible); + void OnSettingsChanged(); bool settings_visible() const { return settings_visible_; } void SetIsClosing(bool is_closing); @@ -86,7 +87,9 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, MessageCenter* message_center_; // Weak reference. MessageCenterTray* tray_; // Weak reference. - std::vector<MessageView*> message_views_; + std::vector<MessageView*> message_views_; // Weak references. + + // Child views. views::ScrollView* scroller_; MessageListView* message_list_view_; NotifierSettingsView* settings_view_; @@ -96,11 +99,17 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, // Data for transition animation between settings view and message list. bool settings_visible_; + + // Animation managing transition between message center and settings (and vice + // versa). + scoped_ptr<ui::MultiAnimation> settings_transition_animation_; + + // Helper data to keep track of the transition between settings and + // message center views. views::View* source_view_; - views::View* target_view_; int source_height_; + views::View* target_view_; int target_height_; - scoped_ptr<ui::MultiAnimation> settings_transition_animation_; // True when the widget is closing so that further operations should be // ignored. diff --git a/ui/message_center/views/notifier_settings_view.cc b/ui/message_center/views/notifier_settings_view.cc index 5673070fbe..b97bc841b5 100644 --- a/ui/message_center/views/notifier_settings_view.cc +++ b/ui/message_center/views/notifier_settings_view.cc @@ -8,14 +8,18 @@ #include <string> #include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" #include "grit/ui_strings.h" +#include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/keycodes/keyboard_codes.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/size.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/views/message_center_view.h" @@ -23,8 +27,10 @@ #include "ui/views/border.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" +#include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/scrollbar/overlay_scroll_bar.h" #include "ui/views/layout/box_layout.h" @@ -114,6 +120,78 @@ bool EntryView::OnKeyReleased(const ui::KeyEvent& event) { } // namespace +// NotifierGroupMenuModel ////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +class NotifierGroupMenuModel : public ui::SimpleMenuModel, + public ui::SimpleMenuModel::Delegate { + public: + NotifierGroupMenuModel(NotifierSettingsProvider* notifier_settings_provider); + virtual ~NotifierGroupMenuModel(); + + // Overridden from ui::SimpleMenuModel::Delegate: + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) OVERRIDE; + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; + + private: + NotifierSettingsProvider* notifier_settings_provider_; +}; + +NotifierGroupMenuModel::NotifierGroupMenuModel( + NotifierSettingsProvider* notifier_settings_provider) + : ui::SimpleMenuModel(this), + notifier_settings_provider_(notifier_settings_provider) { + if (!notifier_settings_provider_) + return; + + size_t num_menu_items = notifier_settings_provider_->GetNotifierGroupCount(); + for (size_t i = 0; i < num_menu_items; ++i) { + const NotifierGroup& group = + notifier_settings_provider_->GetNotifierGroupAt(i); + + AddItem(i, group.login_info.empty() ? group.name : group.login_info); + + gfx::ImageSkia resized_icon = gfx::ImageSkiaOperations::CreateResizedImage( + *group.icon.ToImageSkia(), + skia::ImageOperations::RESIZE_BETTER, + gfx::Size(kSettingsIconSize, kSettingsIconSize)); + + SetIcon(i, gfx::Image(resized_icon)); + } +} + +NotifierGroupMenuModel::~NotifierGroupMenuModel() {} + +bool NotifierGroupMenuModel::IsCommandIdChecked(int command_id) const { + return false; +} + +bool NotifierGroupMenuModel::IsCommandIdEnabled(int command_id) const { + return true; +} + +bool NotifierGroupMenuModel::GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) { + return false; +} + +void NotifierGroupMenuModel::ExecuteCommand(int command_id, int event_flags) { + if (!notifier_settings_provider_) + return; + + size_t notifier_group_index = static_cast<size_t>(command_id); + size_t num_notifier_groups = + notifier_settings_provider_->GetNotifierGroupCount(); + if (notifier_group_index >= num_notifier_groups) + return; + + notifier_settings_provider_->SwitchToNotifierGroup(notifier_group_index); +} + // We do not use views::Checkbox class directly because it doesn't support // showing 'icon'. class NotifierSettingsView::NotifierButton : public views::CustomButton, @@ -240,30 +318,11 @@ NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider) scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false)); AddChildView(scroller_); - views::View* contents_view = new views::View(); - contents_view->SetLayoutManager(new views::BoxLayout( - views::BoxLayout::kVertical, 0, 0, 0)); - - views::Label* top_label = new views::Label(l10n_util::GetStringUTF16( - IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION)); - top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); - top_label->SetMultiLine(true); - top_label->SizeToFit(kMinimumWindowWidth - kMarginWidth * 2); - contents_view->AddChildView(new EntryView(top_label)); - std::vector<Notifier*> notifiers; if (provider_) provider_->GetNotifierList(¬ifiers); - for (size_t i = 0; i < notifiers.size(); ++i) { - NotifierButton* button = new NotifierButton(notifiers[i], this); - EntryView* entry = new EntryView(button); - entry->set_focusable(true); - contents_view->AddChildView(entry); - buttons_.insert(button); - } - scroller_->SetContents(contents_view); - contents_view->SetBoundsRect(gfx::Rect(contents_view->GetPreferredSize())); + UpdateContentsView(notifiers); } NotifierSettingsView::~NotifierSettingsView() { @@ -287,6 +346,57 @@ void NotifierSettingsView::UpdateIconImage(const NotifierId& notifier_id, } } +void NotifierSettingsView::NotifierGroupChanged() { + std::vector<Notifier*> notifiers; + if (provider_) + provider_->GetNotifierList(¬ifiers); + + UpdateContentsView(notifiers); +} + +void NotifierSettingsView::UpdateContentsView( + const std::vector<Notifier*>& notifiers) { + buttons_.clear(); + + views::View* contents_view = new views::View(); + contents_view->SetLayoutManager( + new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); + + views::View* contents_title_view = new views::View(); + contents_title_view->SetLayoutManager( + new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 5)); + views::Label* top_label = new views::Label(l10n_util::GetStringUTF16( + IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION)); + top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + top_label->SetMultiLine(true); + contents_title_view->AddChildView(top_label); + + string16 notifier_group_text; + if (provider_) { + const NotifierGroup& active_group = provider_->GetActiveNotifierGroup(); + notifier_group_text = active_group.login_info.empty() + ? active_group.name + : active_group.login_info; + } + + views::View* notifier_group_selector = + new views::MenuButton(NULL, notifier_group_text, this, true); + contents_title_view->AddChildView(notifier_group_selector); + contents_view->AddChildView(new EntryView(contents_title_view)); + + for (size_t i = 0; i < notifiers.size(); ++i) { + NotifierButton* button = new NotifierButton(notifiers[i], this); + EntryView* entry = new EntryView(button); + entry->set_focusable(true); + contents_view->AddChildView(entry); + buttons_.insert(button); + } + scroller_->SetContents(contents_view); + + contents_view->SetBoundsRect(gfx::Rect(contents_view->GetPreferredSize())); + InvalidateLayout(); +} + void NotifierSettingsView::Layout() { int title_height = title_entry_->GetHeightForWidth(width()); title_entry_->SetBounds(0, 0, width(), title_height); @@ -343,11 +453,30 @@ void NotifierSettingsView::ButtonPressed(views::Button* sender, std::set<NotifierButton*>::iterator iter = buttons_.find( static_cast<NotifierButton*>(sender)); - DCHECK(iter != buttons_.end()); + + if (iter == buttons_.end()) + return; (*iter)->SetChecked(!(*iter)->checked()); if (provider_) provider_->SetNotifierEnabled((*iter)->notifier(), (*iter)->checked()); } +void NotifierSettingsView::OnMenuButtonClicked(views::View* source, + const gfx::Point& point) { + notifier_group_menu_model_.reset(new NotifierGroupMenuModel(provider_)); + notifier_group_menu_runner_.reset( + new views::MenuRunner(notifier_group_menu_model_.get())); + if (views::MenuRunner::MENU_DELETED == + notifier_group_menu_runner_->RunMenuAt(GetWidget(), + NULL, + source->GetBoundsInScreen(), + views::MenuItemView::BUBBLE_ABOVE, + ui::MENU_SOURCE_MOUSE, + views::MenuRunner::CONTEXT_MENU)) + return; + MessageCenterView* center_view = static_cast<MessageCenterView*>(parent()); + center_view->OnSettingsChanged(); +} + } // namespace message_center diff --git a/ui/message_center/views/notifier_settings_view.h b/ui/message_center/views/notifier_settings_view.h index 303eff4cd8..6fc5b586ee 100644 --- a/ui/message_center/views/notifier_settings_view.h +++ b/ui/message_center/views/notifier_settings_view.h @@ -5,20 +5,30 @@ #ifndef UI_MESSAGE_CENTER_VIEWS_NOTIFIER_SETTINGS_VIEW_H_ #define UI_MESSAGE_CENTER_VIEWS_NOTIFIER_SETTINGS_VIEW_H_ +#include <set> + +#include "base/memory/scoped_ptr.h" #include "ui/message_center/message_center_export.h" #include "ui/message_center/notifier_settings.h" #include "ui/message_center/views/message_bubble_base.h" #include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/button/menu_button_listener.h" #include "ui/views/view.h" +namespace views { +class MenuRunner; +} + namespace message_center { +class NotifierGroupMenuModel; // A class to show the list of notifier extensions / URL patterns and allow // users to customize the settings. class MESSAGE_CENTER_EXPORT NotifierSettingsView : public NotifierSettingsObserver, public views::View, - public views::ButtonListener { + public views::ButtonListener, + public views::MenuButtonListener { public: explicit NotifierSettingsView(NotifierSettingsProvider* provider); virtual ~NotifierSettingsView(); @@ -28,6 +38,7 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView // Overridden from NotifierSettingsDelegate: virtual void UpdateIconImage(const NotifierId& notifier_id, const gfx::Image& icon) OVERRIDE; + virtual void NotifierGroupChanged() OVERRIDE; void set_provider(NotifierSettingsProvider* new_provider) { provider_ = new_provider; @@ -36,6 +47,9 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView private: class NotifierButton; + // Given a new list of notifiers, updates the view to reflect it. + void UpdateContentsView(const std::vector<Notifier*>& notifiers); + // Overridden from views::View: virtual void Layout() OVERRIDE; virtual gfx::Size GetMinimumSize() OVERRIDE; @@ -46,12 +60,16 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView // Overridden from views::ButtonListener: virtual void ButtonPressed(views::Button* sender, const ui::Event& event) OVERRIDE; + virtual void OnMenuButtonClicked(views::View* source, + const gfx::Point& point) OVERRIDE; views::ImageButton* title_arrow_; views::View* title_entry_; views::ScrollView* scroller_; NotifierSettingsProvider* provider_; std::set<NotifierButton*> buttons_; + scoped_ptr<NotifierGroupMenuModel> notifier_group_menu_model_; + scoped_ptr<views::MenuRunner> notifier_group_menu_runner_; DISALLOW_COPY_AND_ASSIGN(NotifierSettingsView); }; diff --git a/ui/surface/accelerated_surface_win.cc b/ui/surface/accelerated_surface_win.cc index cc144ef9de..3b86db01ad 100644 --- a/ui/surface/accelerated_surface_win.cc +++ b/ui/surface/accelerated_surface_win.cc @@ -728,7 +728,6 @@ void AcceleratedPresenter::DoPresentAndAcknowledge( return; } -#if !defined(USE_AURA) // If the window is a different size than the swap chain that is being // presented then drop the frame. gfx::Size window_size = GetWindowSize(); @@ -751,7 +750,6 @@ void AcceleratedPresenter::DoPresentAndAcknowledge( "windowheight", window_size.height()); return; } -#endif // Round up size so the swap chain is not continuously resized with the // surface, which could lead to memory fragmentation. @@ -259,6 +259,8 @@ 'base/l10n/l10n_util_posix.cc', 'base/l10n/l10n_util_win.cc', 'base/l10n/l10n_util_win.h', + 'base/l10n/time_format.cc', + 'base/l10n/time_format.h', 'base/layout.cc', 'base/layout.h', 'base/layout_mac.mm', diff --git a/ui/ui.target.darwin-arm.mk b/ui/ui.target.darwin-arm.mk index 602d86c926..f3e940a8da 100644 --- a/ui/ui.target.darwin-arm.mk +++ b/ui/ui.target.darwin-arm.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := \ ui/base/l10n/l10n_util.cc \ ui/base/l10n/l10n_util_android.cc \ ui/base/l10n/l10n_util_posix.cc \ + ui/base/l10n/time_format.cc \ ui/base/layout.cc \ ui/base/models/button_menu_item_model.cc \ ui/base/models/combobox_model.cc \ diff --git a/ui/ui.target.darwin-mips.mk b/ui/ui.target.darwin-mips.mk index 5b22a3c541..7a521ee723 100644 --- a/ui/ui.target.darwin-mips.mk +++ b/ui/ui.target.darwin-mips.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := \ ui/base/l10n/l10n_util.cc \ ui/base/l10n/l10n_util_android.cc \ ui/base/l10n/l10n_util_posix.cc \ + ui/base/l10n/time_format.cc \ ui/base/layout.cc \ ui/base/models/button_menu_item_model.cc \ ui/base/models/combobox_model.cc \ diff --git a/ui/ui.target.darwin-x86.mk b/ui/ui.target.darwin-x86.mk index f2a0f18d45..9c9323535d 100644 --- a/ui/ui.target.darwin-x86.mk +++ b/ui/ui.target.darwin-x86.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := \ ui/base/l10n/l10n_util.cc \ ui/base/l10n/l10n_util_android.cc \ ui/base/l10n/l10n_util_posix.cc \ + ui/base/l10n/time_format.cc \ ui/base/layout.cc \ ui/base/models/button_menu_item_model.cc \ ui/base/models/combobox_model.cc \ diff --git a/ui/ui.target.linux-arm.mk b/ui/ui.target.linux-arm.mk index 602d86c926..f3e940a8da 100644 --- a/ui/ui.target.linux-arm.mk +++ b/ui/ui.target.linux-arm.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := \ ui/base/l10n/l10n_util.cc \ ui/base/l10n/l10n_util_android.cc \ ui/base/l10n/l10n_util_posix.cc \ + ui/base/l10n/time_format.cc \ ui/base/layout.cc \ ui/base/models/button_menu_item_model.cc \ ui/base/models/combobox_model.cc \ diff --git a/ui/ui.target.linux-mips.mk b/ui/ui.target.linux-mips.mk index 5b22a3c541..7a521ee723 100644 --- a/ui/ui.target.linux-mips.mk +++ b/ui/ui.target.linux-mips.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := \ ui/base/l10n/l10n_util.cc \ ui/base/l10n/l10n_util_android.cc \ ui/base/l10n/l10n_util_posix.cc \ + ui/base/l10n/time_format.cc \ ui/base/layout.cc \ ui/base/models/button_menu_item_model.cc \ ui/base/models/combobox_model.cc \ diff --git a/ui/ui.target.linux-x86.mk b/ui/ui.target.linux-x86.mk index f2a0f18d45..9c9323535d 100644 --- a/ui/ui.target.linux-x86.mk +++ b/ui/ui.target.linux-x86.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := \ ui/base/l10n/l10n_util.cc \ ui/base/l10n/l10n_util_android.cc \ ui/base/l10n/l10n_util_posix.cc \ + ui/base/l10n/time_format.cc \ ui/base/layout.cc \ ui/base/models/button_menu_item_model.cc \ ui/base/models/combobox_model.cc \ diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi index d5db0d5df6..dc46a5575c 100644 --- a/ui/ui_unittests.gypi +++ b/ui/ui_unittests.gypi @@ -99,6 +99,7 @@ 'base/l10n/l10n_util_mac_unittest.mm', 'base/l10n/l10n_util_unittest.cc', 'base/l10n/l10n_util_win_unittest.cc', + 'base/l10n/time_format_unittest.cc', 'base/models/tree_node_iterator_unittest.cc', 'base/range/range_mac_unittest.mm', 'base/range/range_unittest.cc', diff --git a/ui/views/ime/input_method_win.cc b/ui/views/ime/input_method_win.cc deleted file mode 100644 index 5ec7c72423..0000000000 --- a/ui/views/ime/input_method_win.cc +++ /dev/null @@ -1,509 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/ime/input_method_win.h" - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/strings/string_util.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_constants.h" -#include "ui/base/events/event_utils.h" -#include "ui/base/ime/composition_text.h" -#include "ui/base/ime/input_method.h" -#include "ui/base/ime/text_input_client.h" -#include "ui/base/keycodes/keyboard_codes.h" -#include "ui/base/win/hwnd_util.h" - -// Extra number of chars before and after selection (or composition) range which -// is returned to IME for improving conversion accuracy. -static const size_t kExtraNumberOfChars = 20; - -namespace views { - -InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate, - HWND hwnd, - ui::InputMethod* host) - : hwnd_(hwnd), - active_(false), - is_candidate_popup_open_(false), - direction_(base::i18n::UNKNOWN_DIRECTION), - pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION), - host_(host) { - SetDelegate(delegate); -} - -InputMethodWin::~InputMethodWin() { - if (widget()) - imm32_manager_.DisableIME(hwnd_); -} - -void InputMethodWin::Init(Widget* widget) { - InputMethodBase::Init(widget); - - // Gets the initial input locale and text direction information. - OnInputLocaleChanged(); -} - -void InputMethodWin::OnFocus() { - UpdateIMEState(); -} - -void InputMethodWin::OnBlur() { - ConfirmCompositionText(); -} - -bool InputMethodWin::OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) { - LRESULT original_result = 0; - BOOL handled = FALSE; - switch (event.message) { - case WM_IME_SETCONTEXT: - original_result = OnImeSetContext( - event.message, event.wParam, event.lParam, &handled); - break; - case WM_IME_STARTCOMPOSITION: - original_result = OnImeStartComposition( - event.message, event.wParam, event.lParam, &handled); - break; - case WM_IME_COMPOSITION: - original_result = OnImeComposition( - event.message, event.wParam, event.lParam, &handled); - break; - case WM_IME_ENDCOMPOSITION: - original_result = OnImeEndComposition( - event.message, event.wParam, event.lParam, &handled); - break; - case WM_IME_REQUEST: - original_result = OnImeRequest( - event.message, event.wParam, event.lParam, &handled); - break; - case WM_IME_NOTIFY: - original_result = OnImeNotify( - event.message, event.wParam, event.lParam, &handled); - break; - case WM_CHAR: - case WM_SYSCHAR: - original_result = OnChar( - event.message, event.wParam, event.lParam, &handled); - break; - case WM_DEADCHAR: - case WM_SYSDEADCHAR: - original_result = OnDeadChar( - event.message, event.wParam, event.lParam, &handled); - break; - default: - NOTREACHED() << "Unknown IME message:" << event.message; - break; - } - if (result) - *result = original_result; - return !!handled; -} - -void InputMethodWin::DispatchKeyEvent(const ui::KeyEvent& key) { - // Handles ctrl-shift key to change text direction and layout alignment. - if (ui::IMM32Manager::IsRTLKeyboardLayoutInstalled() && - !IsTextInputTypeNone()) { - ui::KeyboardCode code = key.key_code(); - if (key.type() == ui::ET_KEY_PRESSED) { - if (code == ui::VKEY_SHIFT) { - base::i18n::TextDirection dir; - if (ui::IMM32Manager::IsCtrlShiftPressed(&dir)) - pending_requested_direction_ = dir; - } else if (code != ui::VKEY_CONTROL) { - pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; - } - } else if (key.type() == ui::ET_KEY_RELEASED && - (code == ui::VKEY_SHIFT || code == ui::VKEY_CONTROL) && - pending_requested_direction_ != base::i18n::UNKNOWN_DIRECTION) { - GetTextInputClient()->ChangeTextDirectionAndLayoutAlignment( - pending_requested_direction_); - pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; - } - } - - DispatchKeyEventPostIME(key); -} - -void InputMethodWin::OnTextInputTypeChanged(View* view) { - if (IsViewFocused(view)) { - imm32_manager_.CancelIME(hwnd_); - UpdateIMEState(); - } - InputMethodBase::OnTextInputTypeChanged(view); -} - -void InputMethodWin::OnCaretBoundsChanged(View* view) { - gfx::Rect rect; - if (!IsViewFocused(view) || !GetCaretBoundsInWidget(&rect)) - return; - imm32_manager_.UpdateCaretRect(hwnd_, rect); -} - -void InputMethodWin::CancelComposition(View* view) { - if (IsViewFocused(view)) - imm32_manager_.CancelIME(hwnd_); -} - -void InputMethodWin::OnInputLocaleChanged() { - active_ = imm32_manager_.SetInputLanguage(); - locale_ = imm32_manager_.GetInputLanguageName(); - direction_ = imm32_manager_.GetTextDirection(); - OnInputMethodChanged(); -} - -std::string InputMethodWin::GetInputLocale() { - return locale_; -} - -base::i18n::TextDirection InputMethodWin::GetInputTextDirection() { - return direction_; -} - -bool InputMethodWin::IsActive() { - return active_; -} - -ui::TextInputClient* InputMethodWin::GetTextInputClient() const { - if (InputMethodBase::GetTextInputClient()) - return InputMethodBase::GetTextInputClient(); - - return host_ ? host_->GetTextInputClient() : NULL; -} - -bool InputMethodWin::IsCandidatePopupOpen() const { - return is_candidate_popup_open_; -} - -void InputMethodWin::OnWillChangeFocus(View* focused_before, View* focused) { - ConfirmCompositionText(); -} - -void InputMethodWin::OnDidChangeFocus(View* focused_before, View* focused) { - UpdateIMEState(); -} - -LRESULT InputMethodWin::OnImeSetContext( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - active_ = (wparam == TRUE); - if (active_) - imm32_manager_.CreateImeWindow(hwnd_); - - OnInputMethodChanged(); - return imm32_manager_.SetImeWindowStyle(hwnd_, message, wparam, lparam, - handled); -} - -LRESULT InputMethodWin::OnImeStartComposition( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - // We have to prevent WTL from calling ::DefWindowProc() because the function - // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to - // over-write the position of IME windows. - *handled = TRUE; - - if (IsTextInputTypeNone()) - return 0; - - // Reset the composition status and create IME windows. - imm32_manager_.CreateImeWindow(hwnd_); - imm32_manager_.ResetComposition(hwnd_); - return 0; -} - -LRESULT InputMethodWin::OnImeComposition( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - // We have to prevent WTL from calling ::DefWindowProc() because we do not - // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. - *handled = TRUE; - - if (IsTextInputTypeNone()) - return 0; - - // At first, update the position of the IME window. - imm32_manager_.UpdateImeWindow(hwnd_); - - // Retrieve the result string and its attributes of the ongoing composition - // and send it to a renderer process. - ui::CompositionText composition; - if (imm32_manager_.GetResult(hwnd_, lparam, &composition.text)) { - GetTextInputClient()->InsertText(composition.text); - imm32_manager_.ResetComposition(hwnd_); - // Fall though and try reading the composition string. - // Japanese IMEs send a message containing both GCS_RESULTSTR and - // GCS_COMPSTR, which means an ongoing composition has been finished - // by the start of another composition. - } - // Retrieve the composition string and its attributes of the ongoing - // composition and send it to a renderer process. - if (imm32_manager_.GetComposition(hwnd_, lparam, &composition)) - GetTextInputClient()->SetCompositionText(composition); - - return 0; -} - -LRESULT InputMethodWin::OnImeEndComposition( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - // Let WTL call ::DefWindowProc() and release its resources. - *handled = FALSE; - - if (IsTextInputTypeNone()) - return 0; - - if (GetTextInputClient()->HasCompositionText()) - GetTextInputClient()->ClearCompositionText(); - - imm32_manager_.ResetComposition(hwnd_); - imm32_manager_.DestroyImeWindow(hwnd_); - return 0; -} - -LRESULT InputMethodWin::OnImeRequest( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - *handled = FALSE; - - // Should not receive WM_IME_REQUEST message, if IME is disabled. - const ui::TextInputType type = GetTextInputType(); - if (type == ui::TEXT_INPUT_TYPE_NONE || - type == ui::TEXT_INPUT_TYPE_PASSWORD) { - return 0; - } - - switch (wparam) { - case IMR_RECONVERTSTRING: - *handled = TRUE; - return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam)); - case IMR_DOCUMENTFEED: - *handled = TRUE; - return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam)); - case IMR_QUERYCHARPOSITION: - *handled = TRUE; - return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam)); - default: - return 0; - } -} - -LRESULT InputMethodWin::OnImeNotify( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - *handled = FALSE; - - // Update |is_candidate_popup_open_|, whether a candidate window is open. - switch (wparam) { - case IMN_OPENCANDIDATE: - is_candidate_popup_open_ = true; - break; - case IMN_CLOSECANDIDATE: - is_candidate_popup_open_ = false; - break; - } - - return 0; -} - -LRESULT InputMethodWin::OnChar( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - *handled = TRUE; - - // We need to send character events to the focused text input client event if - // its text input type is ui::TEXT_INPUT_TYPE_NONE. - if (GetTextInputClient()) { - GetTextInputClient()->InsertChar(static_cast<char16>(wparam), - ui::GetModifiersFromKeyState()); - } - - // Explicitly show the system menu at a good location on [Alt]+[Space]. - // Note: Setting |handled| to FALSE for DefWindowProc triggering of the system - // menu causes unsdesirable titlebar artifacts in the classic theme. - if (message == WM_SYSCHAR && wparam == VK_SPACE) - ui::ShowSystemMenu(hwnd_); - - return 0; -} - -LRESULT InputMethodWin::OnDeadChar( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { - *handled = TRUE; - - if (IsTextInputTypeNone()) - return 0; - - if (!GetTextInputClient()) - return 0; - - // Shows the dead character as a composition text, so that the user can know - // what dead key was pressed. - ui::CompositionText composition; - composition.text.assign(1, static_cast<char16>(wparam)); - composition.selection = ui::Range(0, 1); - composition.underlines.push_back( - ui::CompositionUnderline(0, 1, SK_ColorBLACK, false)); - GetTextInputClient()->SetCompositionText(composition); - return 0; -} - -LRESULT InputMethodWin::OnDocumentFeed(RECONVERTSTRING* reconv) { - ui::TextInputClient* client = GetTextInputClient(); - if (!client) - return 0; - - ui::Range text_range; - if (!client->GetTextRange(&text_range) || text_range.is_empty()) - return 0; - - bool result = false; - ui::Range target_range; - if (client->HasCompositionText()) - result = client->GetCompositionTextRange(&target_range); - - if (!result || target_range.is_empty()) { - if (!client->GetSelectionRange(&target_range) || - !target_range.IsValid()) { - return 0; - } - } - - if (!text_range.Contains(target_range)) - return 0; - - if (target_range.GetMin() - text_range.start() > kExtraNumberOfChars) - text_range.set_start(target_range.GetMin() - kExtraNumberOfChars); - - if (text_range.end() - target_range.GetMax() > kExtraNumberOfChars) - text_range.set_end(target_range.GetMax() + kExtraNumberOfChars); - - size_t len = text_range.length(); - size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); - - if (!reconv) - return need_size; - - if (reconv->dwSize < need_size) - return 0; - - string16 text; - if (!GetTextInputClient()->GetTextFromRange(text_range, &text)) - return 0; - DCHECK_EQ(text_range.length(), text.length()); - - reconv->dwVersion = 0; - reconv->dwStrLen = len; - reconv->dwStrOffset = sizeof(RECONVERTSTRING); - reconv->dwCompStrLen = - client->HasCompositionText() ? target_range.length() : 0; - reconv->dwCompStrOffset = - (target_range.GetMin() - text_range.start()) * sizeof(WCHAR); - reconv->dwTargetStrLen = target_range.length(); - reconv->dwTargetStrOffset = reconv->dwCompStrOffset; - - memcpy((char*)reconv + sizeof(RECONVERTSTRING), - text.c_str(), len * sizeof(WCHAR)); - - // According to Microsft API document, IMR_RECONVERTSTRING and - // IMR_DOCUMENTFEED should return reconv, but some applications return - // need_size. - return reinterpret_cast<LRESULT>(reconv); -} - -LRESULT InputMethodWin::OnReconvertString(RECONVERTSTRING* reconv) { - ui::TextInputClient* client = GetTextInputClient(); - if (!client) - return 0; - - // If there is a composition string already, we don't allow reconversion. - if (client->HasCompositionText()) - return 0; - - ui::Range text_range; - if (!client->GetTextRange(&text_range) || text_range.is_empty()) - return 0; - - ui::Range selection_range; - if (!client->GetSelectionRange(&selection_range) || - selection_range.is_empty()) { - return 0; - } - - DCHECK(text_range.Contains(selection_range)); - - size_t len = selection_range.length(); - size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); - - if (!reconv) - return need_size; - - if (reconv->dwSize < need_size) - return 0; - - // TODO(penghuang): Return some extra context to help improve IME's - // reconversion accuracy. - string16 text; - if (!GetTextInputClient()->GetTextFromRange(selection_range, &text)) - return 0; - DCHECK_EQ(selection_range.length(), text.length()); - - reconv->dwVersion = 0; - reconv->dwStrLen = len; - reconv->dwStrOffset = sizeof(RECONVERTSTRING); - reconv->dwCompStrLen = len; - reconv->dwCompStrOffset = 0; - reconv->dwTargetStrLen = len; - reconv->dwTargetStrOffset = 0; - - memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING), - text.c_str(), len * sizeof(WCHAR)); - - // According to Microsft API document, IMR_RECONVERTSTRING and - // IMR_DOCUMENTFEED should return reconv, but some applications return - // need_size. - return reinterpret_cast<LRESULT>(reconv); -} - -LRESULT InputMethodWin::OnQueryCharPosition(IMECHARPOSITION *char_positon) { - if (!char_positon) - return 0; - - if (char_positon->dwSize < sizeof(IMECHARPOSITION)) - return 0; - - ui::TextInputClient* client = GetTextInputClient(); - if (!client) - return 0; - - gfx::Rect rect; - if (!client->GetCompositionCharacterBounds(char_positon->dwCharPos, &rect)) - return 0; - - char_positon->pt.x = rect.x(); - char_positon->pt.y = rect.y(); - char_positon->cLineHeight = rect.height(); - return 1; // returns non-zero value when succeeded. -} - -void InputMethodWin::ConfirmCompositionText() { - if (!IsTextInputTypeNone()) { - imm32_manager_.CleanupComposition(hwnd_); - // Though above line should confirm the client's composition text by sending - // a result text to us, in case the input method and the client are in - // inconsistent states, we check the client's composition state again. - if (GetTextInputClient()->HasCompositionText()) - GetTextInputClient()->ConfirmCompositionText(); - } -} - -void InputMethodWin::UpdateIMEState() { - // Use switch here in case we are going to add more text input types. - // We disable input method in password field. - switch (GetTextInputType()) { - case ui::TEXT_INPUT_TYPE_NONE: - case ui::TEXT_INPUT_TYPE_PASSWORD: - imm32_manager_.DisableIME(hwnd_); - break; - default: - imm32_manager_.EnableIME(hwnd_); - break; - } -} - -} // namespace views diff --git a/ui/views/ime/input_method_win.h b/ui/views/ime/input_method_win.h deleted file mode 100644 index 5668e8f30a..0000000000 --- a/ui/views/ime/input_method_win.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_IME_INPUT_METHOD_WIN_H_ -#define UI_VIEWS_IME_INPUT_METHOD_WIN_H_ - -#include <windows.h> - -#include <string> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "ui/base/ime/win/imm32_manager.h" -#include "ui/views/ime/input_method_base.h" -#include "ui/views/view.h" -#include "ui/views/widget/widget.h" - -namespace ui { -class InputMethod; -} // namespace ui - -namespace views { - -// An InputMethod implementation based on Windows IMM32 API. -class InputMethodWin : public InputMethodBase { - public: - InputMethodWin(internal::InputMethodDelegate* delegate, - HWND hwnd, - ui::InputMethod* host); - virtual ~InputMethodWin(); - - // Overridden from InputMethod: - virtual void Init(Widget* widget) OVERRIDE; - virtual void OnFocus() OVERRIDE; - virtual void OnBlur() OVERRIDE; - virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) OVERRIDE; - virtual void DispatchKeyEvent(const ui::KeyEvent& key) OVERRIDE; - virtual void OnTextInputTypeChanged(View* view) OVERRIDE; - virtual void OnCaretBoundsChanged(View* view) OVERRIDE; - virtual void CancelComposition(View* view) OVERRIDE; - virtual void OnInputLocaleChanged() OVERRIDE; - virtual std::string GetInputLocale() OVERRIDE; - virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE; - virtual bool IsActive() OVERRIDE; - virtual bool IsCandidatePopupOpen() const OVERRIDE; - - // Overridden from InputMethodBase. - virtual ui::TextInputClient* GetTextInputClient() const OVERRIDE; - - private: - LRESULT OnImeSetContext( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - LRESULT OnImeStartComposition( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - LRESULT OnImeComposition( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - LRESULT OnImeEndComposition( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - LRESULT OnImeRequest( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - LRESULT OnImeNotify( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - // For both WM_CHAR and WM_SYSCHAR - LRESULT OnChar( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - // For both WM_DEADCHAR and WM_SYSDEADCHAR - LRESULT OnDeadChar( - UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled); - - LRESULT OnDocumentFeed(RECONVERTSTRING *reconv); - LRESULT OnReconvertString(RECONVERTSTRING *reconv); - LRESULT OnQueryCharPosition(IMECHARPOSITION *char_positon); - - // Overridden from InputMethodBase. - virtual void OnWillChangeFocus(View* focused_before, View* focused) OVERRIDE; - virtual void OnDidChangeFocus(View* focused_before, View* focused) OVERRIDE; - - // Asks the client to confirm current composition text. - void ConfirmCompositionText(); - - // Enables or disables the IME according to the current text input type. - void UpdateIMEState(); - - // The HWND this InputMethod is bound to. - HWND hwnd_; - - // Indicates if the current input locale has an IME. - bool active_; - - // True if we know for sure that a candidate window is open. - bool is_candidate_popup_open_; - - // Name of the current input locale. - std::string locale_; - - // The current input text direction. - base::i18n::TextDirection direction_; - - // The new text direction and layout alignment requested by the user by - // pressing ctrl-shift. It'll be sent to the text input client when the key - // is released. - base::i18n::TextDirection pending_requested_direction_; - - // Windows IMM32 wrapper. - // (See "ui/base/ime/win/ime_input.h" for its details.) - ui::IMM32Manager imm32_manager_; - - ui::InputMethod* const host_; - - DISALLOW_COPY_AND_ASSIGN(InputMethodWin); -}; - -} // namespace views - -#endif // UI_VIEWS_IME_INPUT_METHOD_WIN_H_ diff --git a/ui/views/touchui/touch_selection_controller_impl.cc b/ui/views/touchui/touch_selection_controller_impl.cc index b309483634..8b6b35116c 100644 --- a/ui/views/touchui/touch_selection_controller_impl.cc +++ b/ui/views/touchui/touch_selection_controller_impl.cc @@ -25,14 +25,39 @@ const int kSelectionHandleLineWidth = 1; const SkColor kSelectionHandleLineColor = SkColorSetRGB(0x42, 0x81, 0xf4); +// When a handle is dragged, the drag position reported to the client view is +// offset vertically to represent the cursor position. This constant specifies +// the offset in pixels above the "O" (see pic below). This is required because +// say if this is zero, that means the drag position we report is the point +// right above the "O" or the bottom most point of the cursor "|". In that case, +// a vertical movement of even one pixel will make the handle jump to the line +// below it. So when the user just starts dragging, the handle will jump to the +// next line if the user makes any vertical movement. It is correct but +// looks/feels weird. So we have this non-zero offset to prevent this jumping. +// +// Editing handle widget showing the difference between the position of the +// ET_GESTURE_SCROLL_UPDATE event and the drag position reported to the client: +// _____ +// | |<-|---- Drag position reported to client +// _ | O | +// Vertical Padding __| | <-|---- ET_GESTURE_SCROLL_UPDATE position +// |_ |_____|<--- Editing handle widget +// +// | | +// T +// Horizontal Padding +// +const int kSelectionHandleVerticalDragOffset = 5; + // Padding around the selection handle defining the area that will be included -// in the touch target to make dragging the handle easier. -const int kSelectionHandlePadding = 10; +// in the touch target to make dragging the handle easier (see pic above). +const int kSelectionHandleHorizPadding = 10; +const int kSelectionHandleVertPadding = 20; // The minimum selection size to trigger selection controller. // TODO(varunjain): Figure out if this is really required and get rid of it if // it isnt. -const int kMinSelectionSize = 2; +const int kMinSelectionSize = 1; const int kContextMenuTimoutMs = 200; @@ -107,7 +132,9 @@ class TouchSelectionControllerImpl::EditingHandleView public: explicit EditingHandleView(TouchSelectionControllerImpl* controller, gfx::NativeView context) - : controller_(controller) { + : controller_(controller), + drag_offset_(0), + draw_invisible_(false) { widget_.reset(CreateTouchSelectionPopupWidget(context, this)); widget_->SetContentsView(this); widget_->SetAlwaysOnTop(true); @@ -132,9 +159,9 @@ class TouchSelectionControllerImpl::EditingHandleView virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE { gfx::Size image_size = GetHandleImageSize(); mask->addRect(SkIntToScalar(0), SkIntToScalar(cursor_height()), - SkIntToScalar(image_size.width()) + 2 * kSelectionHandlePadding, + SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding, SkIntToScalar(cursor_height() + image_size.height() + - kSelectionHandlePadding)); + kSelectionHandleVertPadding)); } virtual void DeleteDelegate() OVERRIDE { @@ -143,9 +170,11 @@ class TouchSelectionControllerImpl::EditingHandleView // Overridden from views::View: virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + if (draw_invisible_) + return; gfx::Size image_size = GetHandleImageSize(); int cursor_pos_x = image_size.width() / 2 - kSelectionHandleLineWidth + - kSelectionHandlePadding; + kSelectionHandleHorizPadding; // Draw the cursor line. canvas->FillRect( @@ -155,7 +184,7 @@ class TouchSelectionControllerImpl::EditingHandleView // Draw the handle image. canvas->DrawImageInt(*GetHandleImage()->ToImageSkia(), - kSelectionHandlePadding, cursor_height()); + kSelectionHandleHorizPadding, cursor_height()); } virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { @@ -164,10 +193,15 @@ class TouchSelectionControllerImpl::EditingHandleView case ui::ET_GESTURE_SCROLL_BEGIN: widget_->SetCapture(this); controller_->SetDraggingHandle(this); + drag_offset_ = event->y() - cursor_height() - + kSelectionHandleVerticalDragOffset; break; - case ui::ET_GESTURE_SCROLL_UPDATE: - controller_->SelectionHandleDragged(event->location()); + case ui::ET_GESTURE_SCROLL_UPDATE: { + gfx::Point drag_pos(event->location().x(), + event->location().y() - drag_offset_); + controller_->SelectionHandleDragged(drag_pos); break; + } case ui::ET_GESTURE_SCROLL_END: case ui::ET_SCROLL_FLING_START: widget_->ReleaseCapture(); @@ -191,8 +225,8 @@ class TouchSelectionControllerImpl::EditingHandleView virtual gfx::Size GetPreferredSize() OVERRIDE { gfx::Size image_size = GetHandleImageSize(); - return gfx::Size(image_size.width() + 2 * kSelectionHandlePadding, - image_size.height() + cursor_height() + kSelectionHandlePadding); + return gfx::Size(image_size.width() + 2 * kSelectionHandleHorizPadding, + image_size.height() + cursor_height() + kSelectionHandleVertPadding); } bool IsWidgetVisible() const { @@ -203,10 +237,10 @@ class TouchSelectionControllerImpl::EditingHandleView gfx::Size image_size = GetHandleImageSize(); selection_rect_ = rect; gfx::Rect widget_bounds( - rect.x() - image_size.width() / 2 - kSelectionHandlePadding, + rect.x() - image_size.width() / 2 - kSelectionHandleHorizPadding, rect.y(), - image_size.width() + 2 * kSelectionHandlePadding, - rect.height() + image_size.height() + kSelectionHandlePadding); + image_size.width() + 2 * kSelectionHandleHorizPadding, + rect.height() + image_size.height() + kSelectionHandleVertPadding); widget_->SetBounds(widget_bounds); } @@ -214,10 +248,24 @@ class TouchSelectionControllerImpl::EditingHandleView return widget_->GetClientAreaBoundsInScreen().origin(); } + void SetDrawInvisible(bool draw_invisible) { + if (draw_invisible_ == draw_invisible) + return; + draw_invisible_ = draw_invisible; + SchedulePaint(); + } + private: scoped_ptr<Widget> widget_; TouchSelectionControllerImpl* controller_; gfx::Rect selection_rect_; + int drag_offset_; + + // If set to true, the handle will not draw anything, hence providing an empty + // widget. We need this because we may want to stop showing the handle while + // it is being dragged. Since it is being dragged, we cannot destroy the + // handle. + bool draw_invisible_; DISALLOW_COPY_AND_ASSIGN(EditingHandleView); }; @@ -273,6 +321,13 @@ void TouchSelectionControllerImpl::SelectionChanged() { // the start. dragging_handle_->SetSelectionRectInScreen(screen_rect_2); + // Temporary fix for selection handle going outside a window. On a webpage, + // the page should scroll if the selection handle is dragged outside the + // window. That does not happen currently. So we just hide the handle for + // now. + // TODO(varunjain): Fix this: crbug.com/269003 + dragging_handle_->SetDrawInvisible(!client_view_->GetBounds().Contains(r2)); + if (dragging_handle_ != cursor_handle_.get()) { // The non-dragging-handle might have recently become visible. EditingHandleView* non_dragging_handle = @@ -321,13 +376,11 @@ void TouchSelectionControllerImpl::SelectionHandleDragged( HideContextMenu(); DCHECK(dragging_handle_); + gfx::Point drag_pos_in_client = drag_pos; + ConvertPointToClientView(dragging_handle_, &drag_pos_in_client); - gfx::Size image_size = GetHandleImageSize(); - gfx::Point offset_drag_pos(drag_pos.x(), - drag_pos.y() - image_size.height() - kSelectionHandlePadding - 1); - ConvertPointToClientView(dragging_handle_, &offset_drag_pos); if (dragging_handle_ == cursor_handle_.get()) { - client_view_->MoveCaretTo(offset_drag_pos); + client_view_->MoveCaretTo(drag_pos_in_client); return; } @@ -344,7 +397,7 @@ void TouchSelectionControllerImpl::SelectionHandleDragged( // Instruct client_view to select the region between p1 and p2. The position // of |fixed_handle| is the start and that of |dragging_handle| is the end // of selection. - client_view_->SelectRect(p2, offset_drag_pos); + client_view_->SelectRect(p2, drag_pos_in_client); } void TouchSelectionControllerImpl::ConvertPointToClientView( diff --git a/ui/views/views.gyp b/ui/views/views.gyp index ea0285b9b2..ff3ab0aaa6 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -297,8 +297,6 @@ 'ime/input_method_bridge.h', 'ime/input_method_delegate.h', 'ime/input_method.h', - 'ime/input_method_win.cc', - 'ime/input_method_win.h', 'ime/mock_input_method.cc', 'ime/mock_input_method.h', 'layout/box_layout.cc', diff --git a/ui/views/widget/desktop_aura/desktop_screen_position_client.cc b/ui/views/widget/desktop_aura/desktop_screen_position_client.cc index 12be4ccc97..2ca556c397 100644 --- a/ui/views/widget/desktop_aura/desktop_screen_position_client.cc +++ b/ui/views/widget/desktop_aura/desktop_screen_position_client.cc @@ -68,19 +68,16 @@ void DesktopScreenPositionClient::SetBounds( const gfx::Rect& bounds, const gfx::Display& display) { // TODO: Use the 3rd parameter, |display|. - gfx::Point origin = bounds.origin(); aura::RootWindow* root = window->GetRootWindow(); - aura::Window::ConvertPointToTarget(window->parent(), root, &origin); - - if (window->type() == aura::client::WINDOW_TYPE_CONTROL) { - window->SetBounds(gfx::Rect(origin, bounds.size())); - return; - } if (PositionWindowInScreenCoordinates(window)) { // The caller expects windows we consider "embedded" to be placed in the // screen coordinate system. So we need to offset the root window's // position (which is in screen coordinates) from these bounds. + + gfx::Point origin = bounds.origin(); + aura::Window::ConvertPointToTarget(window->parent(), root, &origin); + gfx::Point host_origin = GetOrigin(root); origin.Offset(-host_origin.x(), -host_origin.y()); window->SetBounds(gfx::Rect(origin, bounds.size())); diff --git a/ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc b/ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc index 196f6e4f25..bb1cd8b08a 100644 --- a/ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc +++ b/ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc @@ -34,4 +34,52 @@ TEST_F(DesktopScreenPositionClientTest, PositionDialog) { EXPECT_EQ("11,12", dialog->GetWindowBoundsInScreen().origin().ToString()); } +// Verifies that setting the bounds of a control parented to something other +// than the root window is positioned correctly. +TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) { + Widget widget1; + Widget widget2; + Widget widget3; + gfx::Point origin = gfx::Point(16, 16); + + // Use a custom frame type. By default we will choose a native frame when + // aero glass is enabled, and this complicates the logic surrounding origin + // computation, making it difficult to compute the expected origin location. + widget1.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM); + widget2.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM); + widget3.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM); + + // Create 3 windows. A root window, an arbitrary window parented to the root + // but NOT positioned at (0,0) relative to the root, and then a third window + // parented to the second, also not positioned at (0,0). + Widget::InitParams params1 = + CreateParams(Widget::InitParams::TYPE_WINDOW); + params1.bounds = gfx::Rect(origin, gfx::Size(700, 600)); + params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params1.native_widget = new DesktopNativeWidgetAura(&widget1); + widget1.Init(params1); + + Widget::InitParams params2 = + CreateParams(Widget::InitParams::TYPE_WINDOW); + params2.bounds = gfx::Rect(origin, gfx::Size(600, 500)); + params2.parent = widget1.GetNativeView(); + params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params2.child = true; + widget2.Init(params2); + + Widget::InitParams params3 = + CreateParams(Widget::InitParams::TYPE_CONTROL); + params3.parent = widget2.GetNativeView(); + params3.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params3.child = true; + params3.bounds = gfx::Rect(origin, gfx::Size(500, 400)); + widget3.Init(params3); + + // The origin of the 3rd window should be the sum of all parent origins. + gfx::Point expected_origin(origin.x() * 3, origin.y() * 3); + gfx::Rect expected_bounds(expected_origin, gfx::Size(500, 400)); + gfx::Rect actual_bounds(widget3.GetWindowBoundsInScreen()); + EXPECT_EQ(expected_bounds.ToString(), actual_bounds.ToString()); +} + } // namespace views diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index f27ad8c927..1a62709273 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -1030,17 +1030,16 @@ void HWNDMessageHandler::ClientAreaSizeChanged() { } } -gfx::Insets HWNDMessageHandler::GetClientAreaInsets() const { - gfx::Insets insets; - if (delegate_->GetClientAreaInsets(&insets)) - return insets; - DCHECK(insets.empty()); +bool HWNDMessageHandler::GetClientAreaInsets(gfx::Insets* insets) const { + if (delegate_->GetClientAreaInsets(insets)) + return true; + DCHECK(insets->empty()); - // Returning an empty Insets object causes the default handling in - // NativeWidgetWin::OnNCCalcSize() to be invoked. + // Returning false causes the default handling in OnNCCalcSize() to + // be invoked. if (!delegate_->IsWidgetWindow() || (!delegate_->IsUsingCustomFrame() && !remove_standard_frame_)) { - return insets; + return false; } if (IsMaximized()) { @@ -1049,8 +1048,9 @@ gfx::Insets HWNDMessageHandler::GetClientAreaInsets() const { int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); if (remove_standard_frame_) border_thickness -= 1; - return gfx::Insets(border_thickness, border_thickness, border_thickness, - border_thickness); + *insets = gfx::Insets( + border_thickness, border_thickness, border_thickness, border_thickness); + return true; } // Returning empty insets for a window with the standard frame removed seems @@ -1067,8 +1067,17 @@ gfx::Insets HWNDMessageHandler::GetClientAreaInsets() const { // means that the client area is reported 1px larger than it really is, so // user code has to compensate by making its content shorter if it wants // everything to appear inside the window. - if (remove_standard_frame_) - return gfx::Insets(0, 0, IsMaximized() ? 0 : kClientAreaBottomInsetHack, 0); + if (remove_standard_frame_) { + *insets = + gfx::Insets(0, 0, IsMaximized() ? 0 : kClientAreaBottomInsetHack, 0); + return true; + } + +#if defined(USE_AURA) + // The -1 hack below breaks rendering in Aura. + // See http://crbug.com/172099 http://crbug.com/267131 + *insets = gfx::Insets(); +#else // This is weird, but highly essential. If we don't offset the bottom edge // of the client rect, the window client area and window area will match, // and when returning to glass rendering mode from non-glass, the client @@ -1080,7 +1089,9 @@ gfx::Insets HWNDMessageHandler::GetClientAreaInsets() const { // rect when using the opaque frame. // Note: this is only required for non-fullscreen windows. Note that // fullscreen windows are in restored state, not maximized. - return gfx::Insets(0, 0, fullscreen_handler_->fullscreen() ? 0 : 1, 0); + *insets = gfx::Insets(0, 0, fullscreen_handler_->fullscreen() ? 0 : 1, 0); +#endif + return true; } void HWNDMessageHandler::ResetWindowRegion(bool force) { @@ -1643,8 +1654,9 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { } } - gfx::Insets insets = GetClientAreaInsets(); - if (insets.empty() && !fullscreen_handler_->fullscreen() && + gfx::Insets insets; + bool got_insets = GetClientAreaInsets(&insets); + if (!got_insets && !fullscreen_handler_->fullscreen() && !(mode && remove_standard_frame_)) { SetMsgHandled(FALSE); return 0; diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h index d9e20d752d..c1c724cd1d 100644 --- a/ui/views/win/hwnd_message_handler.h +++ b/ui/views/win/hwnd_message_handler.h @@ -196,7 +196,7 @@ class VIEWS_EXPORT HWNDMessageHandler : // Returns the insets of the client area relative to the non-client area of // the window. - gfx::Insets GetClientAreaInsets() const; + bool GetClientAreaInsets(gfx::Insets* insets) const; // Resets the window region for the current widget bounds if necessary. // If |force| is true, the window region is reset to NULL even for native diff --git a/ui/webui/resources/js/cr/ui/context_menu_handler.js b/ui/webui/resources/js/cr/ui/context_menu_handler.js index 30ab13c695..05264d6ee3 100644 --- a/ui/webui/resources/js/cr/ui/context_menu_handler.js +++ b/ui/webui/resources/js/cr/ui/context_menu_handler.js @@ -35,8 +35,11 @@ cr.define('cr.ui', function() { * @param {!cr.ui.Menu} menu The menu to show. */ showMenu: function(e, menu) { - this.menu_ = menu; menu.updateCommands(e.currentTarget); + if (!menu.hasVisibleItems()) + return; + + this.menu_ = menu; menu.classList.remove('hide-delayed'); menu.hidden = false; menu.contextElement = e.currentTarget; diff --git a/ui/webui/resources/js/cr/ui/focus_outline_manager.js b/ui/webui/resources/js/cr/ui/focus_outline_manager.js index e5b247695f..fd106c2982 100644 --- a/ui/webui/resources/js/cr/ui/focus_outline_manager.js +++ b/ui/webui/resources/js/cr/ui/focus_outline_manager.js @@ -32,11 +32,16 @@ cr.define('cr.ui', function() { var self = this; doc.addEventListener('keydown', function(e) { if (e.keyCode == 9) // Tab - self.visible = true; + self.focusByKeyboard_ = true; }, true); doc.addEventListener('mousedown', function(e) { - self.visible = false; + self.focusByKeyboard_ = false; + }, true); + + doc.addEventListener('focus', function(event) { + // Update visibility only when focus is actually changed. + self.visible = self.focusByKeyboard_; }, true); } @@ -48,6 +53,13 @@ cr.define('cr.ui', function() { FocusOutlineManager.prototype = { /** + * Whether focus change is triggered by TAB key. + * @type {boolean} + * @private + */ + focusByKeyboard_: false, + + /** * Whether the focus outline should be visible. * @type {boolean} */ diff --git a/ui/webui/resources/js/cr/ui/menu.js b/ui/webui/resources/js/cr/ui/menu.js index 3047123e5e..4956b3824d 100644 --- a/ui/webui/resources/js/cr/ui/menu.js +++ b/ui/webui/resources/js/cr/ui/menu.js @@ -144,8 +144,10 @@ cr.define('cr.ui', function() { this.selectedIndex = 0; } - if (this.selectedItem) + if (this.selectedItem) { this.selectedItem.focus(); + this.setAttribute('aria-activedescendant', this.selectedItem.id); + } }, /** @@ -156,6 +158,19 @@ cr.define('cr.ui', function() { }, /** + * Returns if the menu has any visible item. + * @return {boolean} True if the menu has visible item. Otherwise, false. + */ + hasVisibleItems: function() { + var menuItems = this.menuItems; // Cache. + for (var i = 0, menuItem; menuItem = menuItems[i]; i++) { + if (!menuItem.hidden) + return true; + } + return false; + }, + + /** * This is the function that handles keyboard navigation. This is usually * called by the element responsible for managing the menu. * @param {Event} e The keydown event object. diff --git a/ui/webui/resources/js/cr/ui/menu_item.js b/ui/webui/resources/js/cr/ui/menu_item.js index d1ab2ea35f..89054e2ccb 100644 --- a/ui/webui/resources/js/cr/ui/menu_item.js +++ b/ui/webui/resources/js/cr/ui/menu_item.js @@ -40,7 +40,10 @@ cr.define('cr.ui', function() { // the appearance of this element. this.classList.add('custom-appearance'); - this.setAttribute('role', 'menuitem'); + // Enable Text to Speech on the menu. Additionaly, ID has to be set, since + // it is used in element's aria-activedescendant attribute. + if (!this.isSeparator()) + this.setAttribute('role', 'menuitem'); var iconUrl; if ((iconUrl = this.getAttribute('icon'))) |