Source code

Revision control

Other Tools

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