Coverage Report

Created: 2018-09-25 14:53

/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