Blame intl/locale/gtk/OSPreferences_gtk.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
Packit f0b94e
 *
Packit f0b94e
 * This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include <locale.h>
Packit f0b94e
#include "OSPreferences.h"
Packit f0b94e
#include "dlfcn.h"
Packit f0b94e
#include "glib.h"
Packit f0b94e
#include "gio/gio.h"
Packit f0b94e
Packit f0b94e
using namespace mozilla::intl;
Packit f0b94e
Packit f0b94e
OSPreferences::OSPreferences() {}
Packit f0b94e
Packit f0b94e
OSPreferences::~OSPreferences() {}
Packit f0b94e
Packit f0b94e
bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) {
Packit f0b94e
  MOZ_ASSERT(aLocaleList.IsEmpty());
Packit f0b94e
Packit f0b94e
  nsAutoCString defaultLang(uloc_getDefault());
Packit f0b94e
Packit f0b94e
  if (CanonicalizeLanguageTag(defaultLang)) {
Packit f0b94e
    aLocaleList.AppendElement(defaultLang);
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
  return false;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool OSPreferences::ReadRegionalPrefsLocales(nsTArray<nsCString>& aLocaleList) {
Packit f0b94e
  MOZ_ASSERT(aLocaleList.IsEmpty());
Packit f0b94e
Packit f0b94e
  // For now we're just taking the LC_TIME from POSIX environment for all
Packit f0b94e
  // regional preferences.
Packit f0b94e
  nsAutoCString localeStr(setlocale(LC_TIME, nullptr));
Packit f0b94e
Packit f0b94e
  if (CanonicalizeLanguageTag(localeStr)) {
Packit f0b94e
    aLocaleList.AppendElement(localeStr);
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return false;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/*
Packit f0b94e
 * This looks up into gtk settings for hourCycle format.
Packit f0b94e
 *
Packit f0b94e
 * This works for all GUIs that use gtk settings like Gnome, Elementary etc.
Packit f0b94e
 * Ubuntu does not use those settings so we'll want to support them separately.
Packit f0b94e
 *
Packit f0b94e
 * We're taking the current 12/24h settings irrelevant of the locale, because
Packit f0b94e
 * in the UI user selects this setting for all locales.
Packit f0b94e
 */
Packit f0b94e
typedef GVariant* (*get_value_fn_t)(GSettings*, const gchar*);
Packit f0b94e
Packit f0b94e
static get_value_fn_t FindGetValueFunction() {
Packit f0b94e
  get_value_fn_t fn = reinterpret_cast<get_value_fn_t>(
Packit f0b94e
      dlsym(RTLD_DEFAULT, "g_settings_get_user_value"));
Packit f0b94e
  return fn ? fn : &g_settings_get_value;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static int HourCycle() {
Packit f0b94e
  int rval = 0;
Packit f0b94e
Packit f0b94e
  const char* schema;
Packit f0b94e
  const char* key;
Packit f0b94e
  const char* env = getenv("XDG_CURRENT_DESKTOP");
Packit f0b94e
  if (env && strcmp(env, "Unity") == 0) {
Packit f0b94e
    schema = "com.canonical.indicator.datetime";
Packit f0b94e
    key = "time-format";
Packit f0b94e
  } else {
Packit f0b94e
    schema = "org.gnome.desktop.interface";
Packit f0b94e
    key = "clock-format";
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // This is a workaround for old GTK versions.
Packit f0b94e
  // Once we bump the minimum version to 2.40 we should replace
Packit f0b94e
  // this with g_settings_schme_source_lookup.
Packit f0b94e
  // See bug 1356718 for details.
Packit f0b94e
  const char* const* schemas = g_settings_list_schemas();
Packit f0b94e
  GSettings* settings = nullptr;
Packit f0b94e
Packit f0b94e
  for (uint32_t i = 0; schemas[i] != nullptr; i++) {
Packit f0b94e
    if (strcmp(schemas[i], schema) == 0) {
Packit f0b94e
      settings = g_settings_new(schema);
Packit f0b94e
      break;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (settings) {
Packit f0b94e
    // We really want to use g_settings_get_user_value which will
Packit f0b94e
    // only want to take it if user manually changed the value.
Packit f0b94e
    // But this requires glib 2.40, and we still support older glib versions,
Packit f0b94e
    // so we have to check whether it's available and fall back to the older
Packit f0b94e
    // g_settings_get_value if not.
Packit f0b94e
    static get_value_fn_t sGetValueFunction = FindGetValueFunction();
Packit f0b94e
    GVariant* value = sGetValueFunction(settings, key);
Packit f0b94e
    if (value) {
Packit f0b94e
      if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
Packit f0b94e
        const char* strVal = g_variant_get_string(value, nullptr);
Packit f0b94e
        if (strncmp("12", strVal, 2) == 0) {
Packit f0b94e
          rval = 12;
Packit f0b94e
        } else if (strncmp("24", strVal, 2) == 0) {
Packit f0b94e
          rval = 24;
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
      g_variant_unref(value);
Packit f0b94e
    }
Packit f0b94e
    g_object_unref(settings);
Packit f0b94e
  }
Packit f0b94e
  return rval;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/**
Packit f0b94e
 * Since Gtk does not provide a way to customize or format date/time patterns,
Packit f0b94e
 * we're reusing ICU data here, but we do modify it according to the only
Packit f0b94e
 * setting Gtk gives us - hourCycle.
Packit f0b94e
 *
Packit f0b94e
 * This means that for gtk we will return a pattern from ICU altered to
Packit f0b94e
 * represent h12/h24 hour cycle if the user modified the default value.
Packit f0b94e
 *
Packit f0b94e
 * In short, this should work like this:
Packit f0b94e
 *
Packit f0b94e
 *  * gtk defaults, pl: 24h
Packit f0b94e
 *  * gtk defaults, en: 12h
Packit f0b94e
 *
Packit f0b94e
 *  * gtk 12h, pl: 12h
Packit f0b94e
 *  * gtk 12h, en: 12h
Packit f0b94e
 *
Packit f0b94e
 *  * gtk 24h, pl: 24h
Packit f0b94e
 *  * gtk 12h, en: 12h
Packit f0b94e
 */
Packit f0b94e
bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle,
Packit f0b94e
                                        DateTimeFormatStyle aTimeStyle,
Packit f0b94e
                                        const nsACString& aLocale,
Packit f0b94e
                                        nsAString& aRetVal) {
Packit f0b94e
  nsAutoString skeleton;
Packit f0b94e
  if (!GetDateTimeSkeletonForStyle(aDateStyle, aTimeStyle, aLocale, skeleton)) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Customize the skeleton if necessary to reflect user's 12/24hr pref
Packit f0b94e
  switch (HourCycle()) {
Packit f0b94e
    case 12: {
Packit f0b94e
      // If skeleton contains 'H' or 'k', replace with 'h' or 'K' respectively,
Packit f0b94e
      // and add 'a' unless already present.
Packit f0b94e
      if (skeleton.FindChar('H') == -1 && skeleton.FindChar('k') == -1) {
Packit f0b94e
        break;  // nothing to do
Packit f0b94e
      }
Packit f0b94e
      bool foundA = false;
Packit f0b94e
      for (size_t i = 0; i < skeleton.Length(); ++i) {
Packit f0b94e
        switch (skeleton[i]) {
Packit f0b94e
          case 'a':
Packit f0b94e
            foundA = true;
Packit f0b94e
            break;
Packit f0b94e
          case 'H':
Packit f0b94e
            skeleton.SetCharAt('h', i);
Packit f0b94e
            break;
Packit f0b94e
          case 'k':
Packit f0b94e
            skeleton.SetCharAt('K', i);
Packit f0b94e
            break;
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
      if (!foundA) {
Packit f0b94e
        skeleton.Append(char16_t('a'));
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    }
Packit f0b94e
    case 24:
Packit f0b94e
      // If skeleton contains 'h' or 'K', replace with 'H' or 'k' respectively,
Packit f0b94e
      // and delete 'a' if present.
Packit f0b94e
      if (skeleton.FindChar('h') == -1 && skeleton.FindChar('K') == -1) {
Packit f0b94e
        break;  // nothing to do
Packit f0b94e
      }
Packit f0b94e
      for (int32_t i = 0; i < int32_t(skeleton.Length()); ++i) {
Packit f0b94e
        switch (skeleton[i]) {
Packit f0b94e
          case 'a':
Packit f0b94e
            skeleton.Cut(i, 1);
Packit f0b94e
            --i;
Packit f0b94e
            break;
Packit f0b94e
          case 'h':
Packit f0b94e
            skeleton.SetCharAt('H', i);
Packit f0b94e
            break;
Packit f0b94e
          case 'K':
Packit f0b94e
            skeleton.SetCharAt('k', i);
Packit f0b94e
            break;
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!GetPatternForSkeleton(skeleton, aLocale, aRetVal)) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return true;
Packit f0b94e
}