summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2013-08-08 10:24:53 +0100
committerBen Murdoch <benm@google.com>2013-08-08 10:24:53 +0100
commitbb1529ce867d8845a77ec7cdf3e3003ef1771a40 (patch)
treef78d0de03cc8aed1a934d921636a0beb8d164378 /ui
parentc95505573d864f17cabf515e32f5b8e0831ae237 (diff)
downloadchromium_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')
-rw-r--r--ui/android/java/src/org/chromium/ui/SelectFileDialog.java11
-rw-r--r--ui/android/java/src/org/chromium/ui/WindowAndroid.java7
-rw-r--r--ui/app_list/cocoa/apps_grid_controller.mm2
-rw-r--r--ui/app_list/cocoa/apps_grid_view_item.mm10
-rw-r--r--ui/base/default_theme_provider.h9
-rw-r--r--ui/base/default_theme_provider_mac.mm14
-rw-r--r--ui/base/ime/character_composer.cc2
-rw-r--r--ui/base/l10n/time_format.cc408
-rw-r--r--ui/base/l10n/time_format.h67
-rw-r--r--ui/base/l10n/time_format_unittest.cc73
-rw-r--r--ui/base/ozone/surface_factory_ozone.cc4
-rw-r--r--ui/base/ozone/surface_factory_ozone.h5
-rw-r--r--ui/base/strings/ui_strings.grd1079
-rw-r--r--ui/base/theme_provider.h20
-rw-r--r--ui/gl/android/surface_texture_bridge.cc3
-rw-r--r--ui/gl/android/surface_texture_bridge.h6
-rw-r--r--ui/message_center/cocoa/settings_controller.h1
-rw-r--r--ui/message_center/cocoa/settings_controller.mm2
-rw-r--r--ui/message_center/fake_notifier_settings_provider.cc24
-rw-r--r--ui/message_center/fake_notifier_settings_provider.h10
-rw-r--r--ui/message_center/message_center.gyp2
-rw-r--r--ui/message_center/notifier_settings.cc11
-rw-r--r--ui/message_center/notifier_settings.h44
-rw-r--r--ui/message_center/views/message_center_button_bar.cc210
-rw-r--r--ui/message_center/views/message_center_button_bar.h68
-rw-r--r--ui/message_center/views/message_center_view.cc313
-rw-r--r--ui/message_center/views/message_center_view.h15
-rw-r--r--ui/message_center/views/notifier_settings_view.cc171
-rw-r--r--ui/message_center/views/notifier_settings_view.h20
-rw-r--r--ui/surface/accelerated_surface_win.cc2
-rw-r--r--ui/ui.gyp2
-rw-r--r--ui/ui.target.darwin-arm.mk1
-rw-r--r--ui/ui.target.darwin-mips.mk1
-rw-r--r--ui/ui.target.darwin-x86.mk1
-rw-r--r--ui/ui.target.linux-arm.mk1
-rw-r--r--ui/ui.target.linux-mips.mk1
-rw-r--r--ui/ui.target.linux-x86.mk1
-rw-r--r--ui/ui_unittests.gypi1
-rw-r--r--ui/views/ime/input_method_win.cc509
-rw-r--r--ui/views/ime/input_method_win.h117
-rw-r--r--ui/views/touchui/touch_selection_controller_impl.cc95
-rw-r--r--ui/views/views.gyp2
-rw-r--r--ui/views/widget/desktop_aura/desktop_screen_position_client.cc11
-rw-r--r--ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc48
-rw-r--r--ui/views/win/hwnd_message_handler.cc42
-rw-r--r--ui/views/win/hwnd_message_handler.h2
-rw-r--r--ui/webui/resources/js/cr/ui/context_menu_handler.js5
-rw-r--r--ui/webui/resources/js/cr/ui/focus_outline_manager.js16
-rw-r--r--ui/webui/resources/js/cr/ui/menu.js17
-rw-r--r--ui/webui/resources/js/cr/ui/menu_item.js5
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(&notifiers);
- 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(&notifiers);
+
+ 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.
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 0cd6abd132..e17d242e5f 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -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')))