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