/src/mozilla-central/intl/locale/gtk/OSPreferences_gtk.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | | * |
3 | | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include <locale.h> |
8 | | #include "OSPreferences.h" |
9 | | #include "dlfcn.h" |
10 | | #include "glib.h" |
11 | | #include "gio/gio.h" |
12 | | |
13 | | using namespace mozilla::intl; |
14 | | |
15 | | OSPreferences::OSPreferences() |
16 | 0 | { |
17 | 0 | } |
18 | | |
19 | | OSPreferences::~OSPreferences() |
20 | 0 | { |
21 | 0 | } |
22 | | |
23 | | bool |
24 | | OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) |
25 | 0 | { |
26 | 0 | MOZ_ASSERT(aLocaleList.IsEmpty()); |
27 | 0 |
|
28 | 0 | nsAutoCString defaultLang(uloc_getDefault()); |
29 | 0 |
|
30 | 0 | if (CanonicalizeLanguageTag(defaultLang)) { |
31 | 0 | aLocaleList.AppendElement(defaultLang); |
32 | 0 | return true; |
33 | 0 | } |
34 | 0 | return false; |
35 | 0 | } |
36 | | |
37 | | bool |
38 | | OSPreferences::ReadRegionalPrefsLocales(nsTArray<nsCString>& aLocaleList) |
39 | 0 | { |
40 | 0 | MOZ_ASSERT(aLocaleList.IsEmpty()); |
41 | 0 |
|
42 | 0 | // For now we're just taking the LC_TIME from POSIX environment for all |
43 | 0 | // regional preferences. |
44 | 0 | nsAutoCString localeStr(setlocale(LC_TIME, nullptr)); |
45 | 0 |
|
46 | 0 | if (CanonicalizeLanguageTag(localeStr)) { |
47 | 0 | aLocaleList.AppendElement(localeStr); |
48 | 0 | return true; |
49 | 0 | } |
50 | 0 | |
51 | 0 | return false; |
52 | 0 | } |
53 | | |
54 | | /* |
55 | | * This looks up into gtk settings for hourCycle format. |
56 | | * |
57 | | * This works for all GUIs that use gtk settings like Gnome, Elementary etc. |
58 | | * Ubuntu does not use those settings so we'll want to support them separately. |
59 | | * |
60 | | * We're taking the current 12/24h settings irrelevant of the locale, because |
61 | | * in the UI user selects this setting for all locales. |
62 | | */ |
63 | | typedef GVariant* (*get_value_fn_t)(GSettings*, const gchar*); |
64 | | |
65 | | static get_value_fn_t |
66 | | FindGetValueFunction() |
67 | 0 | { |
68 | 0 | get_value_fn_t fn = reinterpret_cast<get_value_fn_t>( |
69 | 0 | dlsym(RTLD_DEFAULT, "g_settings_get_user_value") |
70 | 0 | ); |
71 | 0 | return fn ? fn : &g_settings_get_value; |
72 | 0 | } |
73 | | |
74 | | static int |
75 | | HourCycle() |
76 | 0 | { |
77 | 0 | int rval = 0; |
78 | 0 |
|
79 | 0 | const char* schema; |
80 | 0 | const char* key; |
81 | 0 | const char* env = getenv("XDG_CURRENT_DESKTOP"); |
82 | 0 | if (env && strcmp(env, "Unity") == 0) { |
83 | 0 | schema = "com.canonical.indicator.datetime"; |
84 | 0 | key = "time-format"; |
85 | 0 | } else { |
86 | 0 | schema = "org.gnome.desktop.interface"; |
87 | 0 | key = "clock-format"; |
88 | 0 | } |
89 | 0 |
|
90 | 0 | // This is a workaround for old GTK versions. |
91 | 0 | // Once we bump the minimum version to 2.40 we should replace |
92 | 0 | // this with g_settings_schme_source_lookup. |
93 | 0 | // See bug 1356718 for details. |
94 | 0 | const char* const* schemas = g_settings_list_schemas(); |
95 | 0 | GSettings* settings = nullptr; |
96 | 0 |
|
97 | 0 | for (uint32_t i = 0; schemas[i] != nullptr; i++) { |
98 | 0 | if (strcmp(schemas[i], schema) == 0) { |
99 | 0 | settings = g_settings_new(schema); |
100 | 0 | break; |
101 | 0 | } |
102 | 0 | } |
103 | 0 |
|
104 | 0 | if (settings) { |
105 | 0 | // We really want to use g_settings_get_user_value which will |
106 | 0 | // only want to take it if user manually changed the value. |
107 | 0 | // But this requires glib 2.40, and we still support older glib versions, |
108 | 0 | // so we have to check whether it's available and fall back to the older |
109 | 0 | // g_settings_get_value if not. |
110 | 0 | static get_value_fn_t sGetValueFunction = FindGetValueFunction(); |
111 | 0 | GVariant* value = sGetValueFunction(settings, key); |
112 | 0 | if (value) { |
113 | 0 | if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) { |
114 | 0 | const char* strVal = g_variant_get_string(value, nullptr); |
115 | 0 | if (strncmp("12", strVal, 2) == 0) { |
116 | 0 | rval = 12; |
117 | 0 | } else if (strncmp("24", strVal, 2) == 0) { |
118 | 0 | rval = 24; |
119 | 0 | } |
120 | 0 | } |
121 | 0 | g_variant_unref(value); |
122 | 0 | } |
123 | 0 | g_object_unref(settings); |
124 | 0 | } |
125 | 0 | return rval; |
126 | 0 | } |
127 | | |
128 | | /** |
129 | | * Since Gtk does not provide a way to customize or format date/time patterns, |
130 | | * we're reusing ICU data here, but we do modify it according to the only |
131 | | * setting Gtk gives us - hourCycle. |
132 | | * |
133 | | * This means that for gtk we will return a pattern from ICU altered to |
134 | | * represent h12/h24 hour cycle if the user modified the default value. |
135 | | * |
136 | | * In short, this should work like this: |
137 | | * |
138 | | * * gtk defaults, pl: 24h |
139 | | * * gtk defaults, en: 12h |
140 | | * |
141 | | * * gtk 12h, pl: 12h |
142 | | * * gtk 12h, en: 12h |
143 | | * |
144 | | * * gtk 24h, pl: 24h |
145 | | * * gtk 12h, en: 12h |
146 | | */ |
147 | | bool |
148 | | OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle, |
149 | | DateTimeFormatStyle aTimeStyle, |
150 | | const nsACString& aLocale, nsAString& aRetVal) |
151 | 0 | { |
152 | 0 | nsAutoString skeleton; |
153 | 0 | if (!GetDateTimeSkeletonForStyle(aDateStyle, aTimeStyle, aLocale, skeleton)) { |
154 | 0 | return false; |
155 | 0 | } |
156 | 0 | |
157 | 0 | // Customize the skeleton if necessary to reflect user's 12/24hr pref |
158 | 0 | switch (HourCycle()) { |
159 | 0 | case 12: { |
160 | 0 | // If skeleton contains 'H' or 'k', replace with 'h' or 'K' respectively, |
161 | 0 | // and add 'a' unless already present. |
162 | 0 | if (skeleton.FindChar('H') == -1 && skeleton.FindChar('k') == -1) { |
163 | 0 | break; // nothing to do |
164 | 0 | } |
165 | 0 | bool foundA = false; |
166 | 0 | for (size_t i = 0; i < skeleton.Length(); ++i) { |
167 | 0 | switch (skeleton[i]) { |
168 | 0 | case 'a': |
169 | 0 | foundA = true; |
170 | 0 | break; |
171 | 0 | case 'H': |
172 | 0 | skeleton.SetCharAt('h', i); |
173 | 0 | break; |
174 | 0 | case 'k': |
175 | 0 | skeleton.SetCharAt('K', i); |
176 | 0 | break; |
177 | 0 | } |
178 | 0 | } |
179 | 0 | if (!foundA) { |
180 | 0 | skeleton.Append(char16_t('a')); |
181 | 0 | } |
182 | 0 | break; |
183 | 0 | } |
184 | 0 | case 24: |
185 | 0 | // If skeleton contains 'h' or 'K', replace with 'H' or 'k' respectively, |
186 | 0 | // and delete 'a' if present. |
187 | 0 | if (skeleton.FindChar('h') == -1 && skeleton.FindChar('K') == -1) { |
188 | 0 | break; // nothing to do |
189 | 0 | } |
190 | 0 | for (int32_t i = 0; i < int32_t(skeleton.Length()); ++i) { |
191 | 0 | switch (skeleton[i]) { |
192 | 0 | case 'a': |
193 | 0 | skeleton.Cut(i, 1); |
194 | 0 | --i; |
195 | 0 | break; |
196 | 0 | case 'h': |
197 | 0 | skeleton.SetCharAt('H', i); |
198 | 0 | break; |
199 | 0 | case 'K': |
200 | 0 | skeleton.SetCharAt('k', i); |
201 | 0 | break; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | break; |
205 | 0 | } |
206 | 0 | |
207 | 0 | if (!GetPatternForSkeleton(skeleton, aLocale, aRetVal)) { |
208 | 0 | return false; |
209 | 0 | } |
210 | 0 | |
211 | 0 | return true; |
212 | 0 | } |
213 | | |