From ae1c76ff830d146d41e88d6fba724c0a54bce868 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:45:20 +0200 Subject: Adding upstream version 1:43.6. Signed-off-by: Daniel Baumann --- panels/region/.indent.pro | 2 + panels/region/cc-format-chooser.c | 541 +++++++++++++ panels/region/cc-format-chooser.h | 36 + panels/region/cc-format-chooser.ui | 262 ++++++ panels/region/cc-format-preview.c | 257 ++++++ panels/region/cc-format-preview.h | 38 + panels/region/cc-format-preview.ui | 120 +++ panels/region/cc-region-panel.c | 879 +++++++++++++++++++++ panels/region/cc-region-panel.h | 30 + panels/region/cc-region-panel.ui | 132 ++++ panels/region/gnome-region-panel.desktop.in.in | 18 + panels/region/icons/meson.build | 4 + .../org.gnome.Settings-region-symbolic.svg | 4 + panels/region/meson.build | 59 ++ panels/region/region.gresource.xml | 11 + panels/region/view-layout-symbolic.svg | 1 + 16 files changed, 2394 insertions(+) create mode 100644 panels/region/.indent.pro create mode 100644 panels/region/cc-format-chooser.c create mode 100644 panels/region/cc-format-chooser.h create mode 100644 panels/region/cc-format-chooser.ui create mode 100644 panels/region/cc-format-preview.c create mode 100644 panels/region/cc-format-preview.h create mode 100644 panels/region/cc-format-preview.ui create mode 100644 panels/region/cc-region-panel.c create mode 100644 panels/region/cc-region-panel.h create mode 100644 panels/region/cc-region-panel.ui create mode 100644 panels/region/gnome-region-panel.desktop.in.in create mode 100644 panels/region/icons/meson.build create mode 100644 panels/region/icons/scalable/org.gnome.Settings-region-symbolic.svg create mode 100644 panels/region/meson.build create mode 100644 panels/region/region.gresource.xml create mode 100644 panels/region/view-layout-symbolic.svg (limited to 'panels/region') diff --git a/panels/region/.indent.pro b/panels/region/.indent.pro new file mode 100644 index 0000000..bdff074 --- /dev/null +++ b/panels/region/.indent.pro @@ -0,0 +1,2 @@ +-kr -i8 -pcs -lps -psl + diff --git a/panels/region/cc-format-chooser.c b/panels/region/cc-format-chooser.c new file mode 100644 index 0000000..aab7832 --- /dev/null +++ b/panels/region/cc-format-chooser.c @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Matthias Clasen + */ + +#define _GNU_SOURCE +#include +#include "cc-format-chooser.h" + +#include +#include +#include +#include +#include +#include + +#include "cc-common-language.h" +#include "cc-format-preview.h" +#include "cc-util.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include + +struct _CcFormatChooser { + GtkDialog parent_instance; + + GtkWidget *title_bar; + GtkWidget *title_buttons; + GtkWidget *cancel_button; + GtkWidget *back_button; + GtkWidget *done_button; + GtkWidget *empty_results_view; + GtkWidget *main_leaflet; + GtkWidget *region_filter_entry; + GtkWidget *region_list; + GtkWidget *region_list_stack; + GtkWidget *common_region_title; + GtkWidget *common_region_listbox; + GtkWidget *region_box; + GtkWidget *region_title; + GtkWidget *region_listbox; + GtkWidget *preview_box; + CcFormatPreview *format_preview; + gboolean adding; + gboolean showing_extra; + gboolean no_results; + gchar *region; + gchar *preview_region; + gchar **filter_words; +}; + +G_DEFINE_TYPE (CcFormatChooser, cc_format_chooser, GTK_TYPE_DIALOG) + +static void +update_check_button_for_list (GtkWidget *list_box, + const gchar *locale_id) +{ + GtkWidget *child; + + for (child = gtk_widget_get_first_child (list_box); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (!GTK_IS_LIST_BOX_ROW (child)) + continue; + + GtkWidget *check = g_object_get_data (G_OBJECT (child), "check"); + const gchar *region = g_object_get_data (G_OBJECT (child), "locale-id"); + if (check == NULL || region == NULL) + continue; + + if (g_strcmp0 (locale_id, region) == 0) + gtk_widget_set_opacity (check, 1.0); + else + gtk_widget_set_opacity (check, 0.0); + } +} + +static void +set_locale_id (CcFormatChooser *chooser, + const gchar *locale_id) +{ + g_free (chooser->region); + chooser->region = g_strdup (locale_id); + + update_check_button_for_list (chooser->region_listbox, locale_id); + update_check_button_for_list (chooser->common_region_listbox, locale_id); + cc_format_preview_set_region (chooser->format_preview, locale_id); +} + +static gint +sort_regions (gconstpointer a, + gconstpointer b, + gpointer data) +{ + const gchar *la; + const gchar *lb; + + if (g_object_get_data (G_OBJECT (a), "locale-id") == NULL) + return 1; + if (g_object_get_data (G_OBJECT (b), "locale-id") == NULL) + return -1; + + la = g_object_get_data (G_OBJECT (a), "locale-name"); + lb = g_object_get_data (G_OBJECT (b), "locale-name"); + + return g_strcmp0 (la, lb); +} + +static GtkWidget * +padded_label_new (const char *text) +{ + GtkWidget *widget, *label; + + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); + g_object_set (widget, "margin-top", 4, NULL); + g_object_set (widget, "margin-bottom", 4, NULL); + g_object_set (widget, "margin-start", 10, NULL); + g_object_set (widget, "margin-end", 10, NULL); + + label = gtk_label_new (text); + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); + gtk_box_append (GTK_BOX (widget), label); + + return widget; +} + +static void +format_chooser_back_button_clicked_cb (CcFormatChooser *self) +{ + g_assert (CC_IS_FORMAT_CHOOSER (self)); + + gtk_window_set_title (GTK_WINDOW (self), _("Formats")); + adw_leaflet_set_visible_child (ADW_LEAFLET (self->main_leaflet), self->region_box); + gtk_stack_set_visible_child (GTK_STACK (self->title_buttons), self->cancel_button); + gtk_widget_show (self->done_button); +} + +static void +set_preview_button_visible (GtkWidget *row, + gboolean visible) +{ + GtkWidget *button; + + button = g_object_get_data (G_OBJECT (row), "preview-button"); + g_assert (button); + + gtk_widget_set_opacity (button, visible); + gtk_widget_set_sensitive (button, visible); +} + +static void +format_chooser_leaflet_fold_changed_cb (CcFormatChooser *self) +{ + GtkWidget *child; + gboolean folded; + + g_assert (CC_IS_FORMAT_CHOOSER (self)); + + folded = adw_leaflet_get_folded (ADW_LEAFLET (self->main_leaflet)); + + for (child = gtk_widget_get_first_child (self->common_region_listbox); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (GTK_IS_LIST_BOX_ROW (child)) + set_preview_button_visible (child, folded); + } + + for (child = gtk_widget_get_first_child (self->region_listbox); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (GTK_IS_LIST_BOX_ROW (child)) + set_preview_button_visible (child, folded); + } + + if (!folded) + { + cc_format_preview_set_region (self->format_preview, self->region); + gtk_window_set_title (GTK_WINDOW (self), _("Formats")); + adw_leaflet_set_visible_child (ADW_LEAFLET (self->main_leaflet), self->region_box); + gtk_stack_set_visible_child (GTK_STACK (self->title_buttons), self->cancel_button); + gtk_widget_show (self->done_button); + } +} + +static void +preview_button_clicked_cb (CcFormatChooser *self, + GtkWidget *button) +{ + GtkWidget *row; + const gchar *region, *locale_name; + + g_assert (CC_IS_FORMAT_CHOOSER (self)); + g_assert (GTK_IS_WIDGET (button)); + + row = gtk_widget_get_ancestor (button, GTK_TYPE_LIST_BOX_ROW); + g_assert (row); + + region = g_object_get_data (G_OBJECT (row), "locale-id"); + locale_name = g_object_get_data (G_OBJECT (row), "locale-name"); + cc_format_preview_set_region (self->format_preview, region); + + adw_leaflet_set_visible_child (ADW_LEAFLET (self->main_leaflet), self->preview_box); + gtk_stack_set_visible_child (GTK_STACK (self->title_buttons), self->back_button); + gtk_widget_hide (self->done_button); + + if (locale_name) + gtk_window_set_title (GTK_WINDOW (self), locale_name); +} + +static GtkWidget * +region_widget_new (CcFormatChooser *self, + const gchar *locale_id) +{ + gchar *locale_name; + gchar *locale_current_name; + gchar *locale_untranslated_name; + GtkWidget *row, *box, *button; + GtkWidget *check; + + locale_name = gnome_get_country_from_locale (locale_id, locale_id); + if (!locale_name) + return NULL; + + locale_current_name = gnome_get_country_from_locale (locale_id, NULL); + locale_untranslated_name = gnome_get_country_from_locale (locale_id, "C"); + + row = gtk_list_box_row_new (); + box = padded_label_new (locale_name); + gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box); + + check = gtk_image_new_from_icon_name ("object-select-symbolic"); + gtk_widget_set_halign (check, GTK_ALIGN_START); + gtk_widget_set_hexpand (check, TRUE); + gtk_widget_set_opacity (check, 0.0); + gtk_box_append (GTK_BOX (box), check); + + button = gtk_button_new_from_icon_name ("view-layout-symbolic"); + g_signal_connect_object (button, "clicked", G_CALLBACK (preview_button_clicked_cb), + self, G_CONNECT_SWAPPED); + gtk_widget_show (button); + gtk_box_append (GTK_BOX (box), button); + + g_object_set_data (G_OBJECT (row), "check", check); + g_object_set_data (G_OBJECT (row), "preview-button", button); + g_object_set_data_full (G_OBJECT (row), "locale-id", g_strdup (locale_id), g_free); + g_object_set_data_full (G_OBJECT (row), "locale-name", locale_name, g_free); + g_object_set_data_full (G_OBJECT (row), "locale-current-name", locale_current_name, g_free); + g_object_set_data_full (G_OBJECT (row), "locale-untranslated-name", locale_untranslated_name, g_free); + + return row; +} + +static void +add_regions (CcFormatChooser *chooser, + gchar **locale_ids, + GHashTable *initial) +{ + g_autoptr(GList) initial_locales = NULL; + GtkWidget *widget; + GList *l; + + chooser->adding = TRUE; + initial_locales = g_hash_table_get_keys (initial); + + /* Populate Common Locales */ + for (l = initial_locales; l != NULL; l = l->next) { + if (!cc_common_language_has_font (l->data)) + continue; + + widget = region_widget_new (chooser, l->data); + if (!widget) + continue; + + gtk_list_box_append (GTK_LIST_BOX (chooser->common_region_listbox), widget); + } + + /* Populate All locales */ + while (*locale_ids) { + gchar *locale_id; + + locale_id = *locale_ids; + locale_ids ++; + + if (!cc_common_language_has_font (locale_id)) + continue; + + widget = region_widget_new (chooser, locale_id); + if (!widget) + continue; + + gtk_list_box_append (GTK_LIST_BOX (chooser->region_listbox), widget); + } + + chooser->adding = FALSE; +} + +static void +add_all_regions (CcFormatChooser *chooser) +{ + g_auto(GStrv) locale_ids = NULL; + g_autoptr(GHashTable) initial = NULL; + + locale_ids = gnome_get_all_locales (); + initial = cc_common_language_get_initial_languages (); + add_regions (chooser, locale_ids, initial); +} + +static gboolean +match_all (gchar **words, + const gchar *str) +{ + gchar **w; + + for (w = words; *w; ++w) + if (!strstr (str, *w)) + return FALSE; + + return TRUE; +} + +static gboolean +region_visible (GtkListBoxRow *row, + gpointer user_data) +{ + CcFormatChooser *chooser = user_data; + g_autofree gchar *locale_name = NULL; + g_autofree gchar *locale_current_name = NULL; + g_autofree gchar *locale_untranslated_name = NULL; + gboolean match = TRUE; + + if (!chooser->filter_words) + goto end; + + locale_name = + cc_util_normalize_casefold_and_unaccent (g_object_get_data (G_OBJECT (row), "locale-name")); + if (match_all (chooser->filter_words, locale_name)) + goto end; + + locale_current_name = + cc_util_normalize_casefold_and_unaccent (g_object_get_data (G_OBJECT (row), "locale-current-name")); + if (match_all (chooser->filter_words, locale_current_name)) + goto end; + + locale_untranslated_name = + cc_util_normalize_casefold_and_unaccent (g_object_get_data (G_OBJECT (row), "locale-untranslated-name")); + + match = match_all (chooser->filter_words, locale_untranslated_name); + + end: + if (match) + chooser->no_results = FALSE; + return match; +} + +static void +filter_changed (CcFormatChooser *chooser) +{ + g_autofree gchar *filter_contents = NULL; + gboolean visible; + + g_clear_pointer (&chooser->filter_words, g_strfreev); + + filter_contents = + cc_util_normalize_casefold_and_unaccent (gtk_editable_get_text (GTK_EDITABLE (chooser->region_filter_entry))); + + /* The popular listbox is shown only if search is empty */ + visible = filter_contents == NULL || *filter_contents == '\0'; + gtk_widget_set_visible (chooser->common_region_listbox, visible); + gtk_widget_set_visible (chooser->common_region_title, visible); + gtk_widget_set_visible (chooser->region_title, visible); + + /* Reset cached search state */ + chooser->no_results = TRUE; + + if (!filter_contents) { + gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->region_listbox)); + gtk_list_box_set_placeholder (GTK_LIST_BOX (chooser->region_listbox), NULL); + return; + } + chooser->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0); + gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->region_listbox)); + + if (chooser->no_results) + gtk_stack_set_visible_child (GTK_STACK (chooser->region_list_stack), + GTK_WIDGET (chooser->empty_results_view)); + else + gtk_stack_set_visible_child (GTK_STACK (chooser->region_list_stack), + GTK_WIDGET (chooser->region_list)); +} + +static void +row_activated (CcFormatChooser *chooser, + GtkListBoxRow *row) +{ + const gchar *new_locale_id; + + if (chooser->adding) + return; + + new_locale_id = g_object_get_data (G_OBJECT (row), "locale-id"); + if (g_strcmp0 (new_locale_id, chooser->region) == 0) { + gtk_dialog_response (GTK_DIALOG (chooser), + gtk_dialog_get_response_for_widget (GTK_DIALOG (chooser), + chooser->done_button)); + } else { + set_locale_id (chooser, new_locale_id); + } +} + +static void +activate_default (CcFormatChooser *chooser) +{ + GtkWidget *focus; + const gchar *locale_id; + + focus = gtk_window_get_focus (GTK_WINDOW (chooser)); + if (!focus) + return; + + locale_id = g_object_get_data (G_OBJECT (focus), "locale-id"); + if (g_strcmp0 (locale_id, chooser->region) == 0) + return; + + g_signal_stop_emission_by_name (chooser, "activate-default"); + gtk_widget_activate (focus); +} + +static void +cc_format_chooser_dispose (GObject *object) +{ + CcFormatChooser *chooser = CC_FORMAT_CHOOSER (object); + + g_clear_pointer (&chooser->filter_words, g_strfreev); + g_clear_pointer (&chooser->region, g_free); + + G_OBJECT_CLASS (cc_format_chooser_parent_class)->dispose (object); +} + +void +cc_format_chooser_class_init (CcFormatChooserClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = cc_format_chooser_dispose; + + g_type_ensure (CC_TYPE_FORMAT_PREVIEW); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/region/cc-format-chooser.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, title_bar); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, title_buttons); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, cancel_button); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, back_button); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, done_button); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, main_leaflet); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, region_filter_entry); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, common_region_title); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, common_region_listbox); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, region_box); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, region_title); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, region_listbox); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, region_list); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, region_list_stack); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, preview_box); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, empty_results_view); + gtk_widget_class_bind_template_child (widget_class, CcFormatChooser, format_preview); + + gtk_widget_class_bind_template_callback (widget_class, format_chooser_back_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, format_chooser_leaflet_fold_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, filter_changed); + gtk_widget_class_bind_template_callback (widget_class, row_activated); +} + +void +cc_format_chooser_init (CcFormatChooser *chooser) +{ + gtk_widget_init_template (GTK_WIDGET (chooser)); + + gtk_list_box_set_sort_func (GTK_LIST_BOX (chooser->common_region_listbox), + (GtkListBoxSortFunc)sort_regions, chooser, NULL); + gtk_list_box_set_sort_func (GTK_LIST_BOX (chooser->region_listbox), + (GtkListBoxSortFunc)sort_regions, chooser, NULL); + gtk_list_box_set_filter_func (GTK_LIST_BOX (chooser->region_listbox), + region_visible, chooser, NULL); + + add_all_regions (chooser); + gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->region_listbox)); + format_chooser_leaflet_fold_changed_cb (chooser); + + g_signal_connect_object (chooser, "activate-default", + G_CALLBACK (activate_default), chooser, G_CONNECT_SWAPPED); +} + +CcFormatChooser * +cc_format_chooser_new (void) +{ + return CC_FORMAT_CHOOSER (g_object_new (CC_TYPE_FORMAT_CHOOSER, + "use-header-bar", 1, + NULL)); +} + +void +cc_format_chooser_clear_filter (CcFormatChooser *chooser) +{ + g_return_if_fail (CC_IS_FORMAT_CHOOSER (chooser)); + gtk_editable_set_text (GTK_EDITABLE (chooser->region_filter_entry), ""); +} + +const gchar * +cc_format_chooser_get_region (CcFormatChooser *chooser) +{ + g_return_val_if_fail (CC_IS_FORMAT_CHOOSER (chooser), NULL); + return chooser->region; +} + +void +cc_format_chooser_set_region (CcFormatChooser *chooser, + const gchar *region) +{ + g_return_if_fail (CC_IS_FORMAT_CHOOSER (chooser)); + set_locale_id (chooser, region); +} diff --git a/panels/region/cc-format-chooser.h b/panels/region/cc-format-chooser.h new file mode 100644 index 0000000..0248338 --- /dev/null +++ b/panels/region/cc-format-chooser.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Red Hat, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Matthias Clasen + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define CC_TYPE_FORMAT_CHOOSER (cc_format_chooser_get_type ()) +G_DECLARE_FINAL_TYPE (CcFormatChooser, cc_format_chooser, CC, FORMAT_CHOOSER, GtkDialog) + +CcFormatChooser *cc_format_chooser_new (void); +void cc_format_chooser_clear_filter (CcFormatChooser *chooser); +const gchar *cc_format_chooser_get_region (CcFormatChooser *chooser); +void cc_format_chooser_set_region (CcFormatChooser *chooser, + const gchar *region); + +G_END_DECLS diff --git a/panels/region/cc-format-chooser.ui b/panels/region/cc-format-chooser.ui new file mode 100644 index 0000000..9a8cf92 --- /dev/null +++ b/panels/region/cc-format-chooser.ui @@ -0,0 +1,262 @@ + + + + diff --git a/panels/region/cc-format-preview.c b/panels/region/cc-format-preview.c new file mode 100644 index 0000000..61b434b --- /dev/null +++ b/panels/region/cc-format-preview.c @@ -0,0 +1,257 @@ +/* cc-format-preview.c + * + * Copyright (C) 2013 Red Hat, Inc. + * Copyright (C) 2020 System76, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Matthias Clasen + * Ian Douglas Scott + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "cc-format-preview.h" + +#include +#include +#include +#include +#include + +struct _CcFormatPreview { + GtkBox parent_instance; + + GtkWidget *date_format_label; + GtkWidget *date_time_format_label; + GtkWidget *measurement_format_label; + GtkWidget *number_format_label; + GtkWidget *paper_format_label; + GtkWidget *time_format_label; + + gchar *region; +}; + +enum +{ + PROP_0, + PROP_REGION +}; + +G_DEFINE_TYPE (CcFormatPreview, cc_format_preview, GTK_TYPE_BOX) + +static void +display_date (GtkWidget *label, GDateTime *dt, const gchar *format) +{ + g_autofree gchar *s = g_date_time_format (dt, format); + gtk_label_set_text (GTK_LABEL (label), g_strstrip (s)); +} + +static void +update_format_examples (CcFormatPreview *self) +{ + const gchar *region = self->region; + locale_t locale; + locale_t old_locale; + g_autoptr(GDateTime) dt = NULL; + g_autofree gchar *s = NULL; +#ifdef LC_MEASUREMENT + const gchar *fmt; +#endif + g_autoptr(GtkPaperSize) paper = NULL; + + if (region == NULL || region[0] == '\0') + return; + + locale = newlocale (LC_TIME_MASK, region, (locale_t) 0); + if (locale == (locale_t) 0) + g_warning ("Failed to create locale %s: %s", region, g_strerror (errno)); + else + old_locale = uselocale (locale); + + dt = g_date_time_new_now_local (); + display_date (self->date_format_label, dt, "%x"); + display_date (self->time_format_label, dt, "%X"); + display_date (self->date_time_format_label, dt, "%c"); + + if (locale != (locale_t) 0) + { + uselocale (old_locale); + freelocale (locale); + } + + locale = newlocale (LC_NUMERIC_MASK, region, (locale_t) 0); + if (locale == (locale_t) 0) + g_warning ("Failed to create locale %s: %s", region, g_strerror (errno)); + else + old_locale = uselocale (locale); + + s = g_strdup_printf ("%'.2f", 123456789.00); + gtk_label_set_text (GTK_LABEL (self->number_format_label), s); + + if (locale != (locale_t) 0) + { + uselocale (old_locale); + freelocale (locale); + } + +#if 0 + locale = newlocale (LC_MONETARY_MASK, region, (locale_t) 0); + if (locale == (locale_t) 0) + g_warning ("Failed to create locale %s: %s", region, g_strerror (errno)); + else + old_locale = uselocale (locale); + + num_info = localeconv (); + if (num_info != NULL) + gtk_label_set_text (GTK_LABEL (self->currency_format_label), num_info->currency_symbol); + + if (locale != (locale_t) 0) + { + uselocale (old_locale); + freelocale (locale); + } +#endif + +#ifdef LC_MEASUREMENT + locale = newlocale (LC_MEASUREMENT_MASK, region, (locale_t) 0); + if (locale == (locale_t) 0) + g_warning ("Failed to create locale %s: %s", region, g_strerror (errno)); + else + old_locale = uselocale (locale); + + fmt = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT); + if (fmt && *fmt == 2) + gtk_label_set_text (GTK_LABEL (self->measurement_format_label), C_("measurement format", "Imperial")); + else + gtk_label_set_text (GTK_LABEL (self->measurement_format_label), C_("measurement format", "Metric")); + + if (locale != (locale_t) 0) + { + uselocale (old_locale); + freelocale (locale); + } +#endif + +#ifdef LC_PAPER + locale = newlocale (LC_PAPER_MASK, region, (locale_t) 0); + if (locale == (locale_t) 0) + g_warning ("Failed to create locale %s: %s", region, g_strerror (errno)); + else + old_locale = uselocale (locale); + + paper = gtk_paper_size_new (gtk_paper_size_get_default ()); + gtk_label_set_text (GTK_LABEL (self->paper_format_label), gtk_paper_size_get_display_name (paper)); + + if (locale != (locale_t) 0) + { + uselocale (old_locale); + freelocale (locale); + } +#endif +} + +static void +cc_format_preview_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcFormatPreview *self; + + self = CC_FORMAT_PREVIEW (object); + + switch (prop_id) { + case PROP_REGION: + cc_format_preview_set_region (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +cc_format_preview_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcFormatPreview *self; + + self = CC_FORMAT_PREVIEW (object); + + switch (prop_id) { + case PROP_REGION: + g_value_set_string (value, self->region); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +cc_format_preview_finalize (GObject *object) +{ + CcFormatPreview *self = CC_FORMAT_PREVIEW (object); + + g_clear_pointer (&self->region, g_free); + + G_OBJECT_CLASS (cc_format_preview_parent_class)->finalize (object); +} + +void +cc_format_preview_class_init (CcFormatPreviewClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = cc_format_preview_get_property; + object_class->set_property = cc_format_preview_set_property; + object_class->finalize = cc_format_preview_finalize; + + g_object_class_install_property (object_class, + PROP_REGION, + g_param_spec_string ("region", + "region", + "region", + NULL, + G_PARAM_READWRITE)); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/region/cc-format-preview.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcFormatPreview, date_format_label); + gtk_widget_class_bind_template_child (widget_class, CcFormatPreview, date_time_format_label); + gtk_widget_class_bind_template_child (widget_class, CcFormatPreview, measurement_format_label); + gtk_widget_class_bind_template_child (widget_class, CcFormatPreview, number_format_label); + gtk_widget_class_bind_template_child (widget_class, CcFormatPreview, paper_format_label); + gtk_widget_class_bind_template_child (widget_class, CcFormatPreview, time_format_label); +} + +void +cc_format_preview_init (CcFormatPreview *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +void +cc_format_preview_set_region (CcFormatPreview *preview, + const gchar *region) +{ + g_free (preview->region); + preview->region = g_strdup (region); + update_format_examples (preview); +} diff --git a/panels/region/cc-format-preview.h b/panels/region/cc-format-preview.h new file mode 100644 index 0000000..04d79bf --- /dev/null +++ b/panels/region/cc-format-preview.h @@ -0,0 +1,38 @@ +/* cc-format-preview.c + * + * Copyright (C) 2013 Red Hat, Inc. + * Copyright (C) 2020 System76, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Matthias Clasen + * Ian Douglas Scott + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define CC_TYPE_FORMAT_PREVIEW (cc_format_preview_get_type()) +G_DECLARE_FINAL_TYPE (CcFormatPreview, cc_format_preview, CC, FORMAT_PREVIEW, GtkBox) + +void cc_format_preview_set_region (CcFormatPreview *preview, + const gchar *region); + +G_END_DECLS diff --git a/panels/region/cc-format-preview.ui b/panels/region/cc-format-preview.ui new file mode 100644 index 0000000..2773aa9 --- /dev/null +++ b/panels/region/cc-format-preview.ui @@ -0,0 +1,120 @@ + + + + + diff --git a/panels/region/cc-region-panel.c b/panels/region/cc-region-panel.c new file mode 100644 index 0000000..bba51e9 --- /dev/null +++ b/panels/region/cc-region-panel.c @@ -0,0 +1,879 @@ +/* + * Copyright (C) 2010 Intel, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Author: Sergey Udaltsov + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cc-region-panel.h" +#include "cc-region-resources.h" +#include "cc-language-chooser.h" +#include "cc-format-chooser.h" + +#include "cc-common-language.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include +#include + +#include + +#define GNOME_SYSTEM_LOCALE_DIR "org.gnome.system.locale" +#define KEY_REGION "region" + +#define DEFAULT_LOCALE "en_US.utf-8" + +typedef enum { + USER, + SYSTEM, +} CcLocaleTarget; + +struct _CcRegionPanel { + CcPanel parent_instance; + + GtkListBoxRow *formats_row; + GtkInfoBar *infobar; + GtkSizeGroup *input_size_group; + AdwActionRow *login_formats_row; + GtkWidget *login_group; + AdwActionRow *login_language_row; + GtkListBoxRow *language_row; + GtkButton *restart_button; + + gboolean login_auto_apply; + GPermission *permission; + GDBusProxy *localed; + GDBusProxy *session; + + ActUserManager *user_manager; + ActUser *user; + GSettings *locale_settings; + + gchar *language; + gchar *region; + gchar *system_language; + gchar *system_region; +}; + +CC_PANEL_REGISTER (CcRegionPanel, cc_region_panel) + +/* Auxiliary methods */ + +static GFile * +get_needs_restart_file (void) +{ + g_autofree gchar *path = NULL; + + path = g_build_filename (g_get_user_runtime_dir (), + "gnome-control-center-region-needs-restart", + NULL); + return g_file_new_for_path (path); +} + +static void +restart_now (CcRegionPanel *self) +{ + g_autoptr(GFile) file = NULL; + + file = get_needs_restart_file (); + g_file_delete (file, NULL, NULL); + + g_dbus_proxy_call (self->session, + "Logout", + g_variant_new ("(u)", 0), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); +} + +static void +set_restart_notification_visible (CcRegionPanel *self, + const gchar *locale, + gboolean visible) +{ + locale_t new_locale; + locale_t current_locale; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileOutputStream) output_stream = NULL; + g_autoptr(GError) error = NULL; + + if (locale) { + new_locale = newlocale (LC_MESSAGES_MASK, locale, (locale_t) 0); + if (new_locale != (locale_t) 0) { + current_locale = uselocale (new_locale); + uselocale (current_locale); + freelocale (new_locale); + } else { + g_warning ("Failed to create locale %s: %s", locale, g_strerror (errno)); + } + } + + gtk_info_bar_set_revealed (self->infobar, visible); + + file = get_needs_restart_file (); + + if (!visible) { + g_file_delete (file, NULL, NULL); + return; + } + + output_stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error); + if (output_stream == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + g_warning ("Unable to create %s: %s", g_file_get_path (file), error->message); + } +} + +typedef struct { + CcRegionPanel *self; + int category; + gchar *target_locale; +} MaybeNotifyData; + +static void +maybe_notify_data_free (MaybeNotifyData *data) +{ + g_free (data->target_locale); + g_free (data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MaybeNotifyData, maybe_notify_data_free) + +static void +maybe_notify_finish (GObject *source, + GAsyncResult *res, + gpointer data) +{ + g_autoptr(MaybeNotifyData) mnd = data; + CcRegionPanel *self = mnd->self; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) retval = NULL; + g_autofree gchar *current_lang_code = NULL; + g_autofree gchar *current_country_code = NULL; + g_autofree gchar *target_lang_code = NULL; + g_autofree gchar *target_country_code = NULL; + const gchar *current_locale = NULL; + + retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!retval) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to get locale: %s\n", error->message); + return; + } + + g_variant_get (retval, "(&s)", ¤t_locale); + + if (!gnome_parse_locale (current_locale, + ¤t_lang_code, + ¤t_country_code, + NULL, + NULL)) + return; + + if (!gnome_parse_locale (mnd->target_locale, + &target_lang_code, + &target_country_code, + NULL, + NULL)) + return; + + if (g_str_equal (current_lang_code, target_lang_code) == FALSE || + g_str_equal (current_country_code, target_country_code) == FALSE) + set_restart_notification_visible (self, + mnd->category == LC_MESSAGES ? mnd->target_locale : NULL, + TRUE); + else + set_restart_notification_visible (self, + mnd->category == LC_MESSAGES ? mnd->target_locale : NULL, + FALSE); +} + +static void +maybe_notify (CcRegionPanel *self, + int category, + const gchar *target_locale) +{ + MaybeNotifyData *mnd; + + mnd = g_new0 (MaybeNotifyData, 1); + mnd->self = self; + mnd->category = category; + mnd->target_locale = g_strdup (target_locale); + + g_dbus_proxy_call (self->session, + "GetLocale", + g_variant_new ("(i)", category), + G_DBUS_CALL_FLAGS_NONE, + -1, + cc_panel_get_cancellable (CC_PANEL (self)), + maybe_notify_finish, + mnd); +} + +static void +set_localed_locale (CcRegionPanel *self) +{ + g_autoptr(GVariantBuilder) b = NULL; + g_autofree gchar *lang_value = NULL; + + b = g_variant_builder_new (G_VARIANT_TYPE ("as")); + lang_value = g_strconcat ("LANG=", self->system_language, NULL); + g_variant_builder_add (b, "s", lang_value); + + if (self->system_region != NULL) { + g_autofree gchar *time_value = NULL; + g_autofree gchar *numeric_value = NULL; + g_autofree gchar *monetary_value = NULL; + g_autofree gchar *measurement_value = NULL; + g_autofree gchar *paper_value = NULL; + time_value = g_strconcat ("LC_TIME=", self->system_region, NULL); + g_variant_builder_add (b, "s", time_value); + numeric_value = g_strconcat ("LC_NUMERIC=", self->system_region, NULL); + g_variant_builder_add (b, "s", numeric_value); + monetary_value = g_strconcat ("LC_MONETARY=", self->system_region, NULL); + g_variant_builder_add (b, "s", monetary_value); + measurement_value = g_strconcat ("LC_MEASUREMENT=", self->system_region, NULL); + g_variant_builder_add (b, "s", measurement_value); + paper_value = g_strconcat ("LC_PAPER=", self->system_region, NULL); + g_variant_builder_add (b, "s", paper_value); + } + g_dbus_proxy_call (self->localed, + "SetLocale", + g_variant_new ("(asb)", b, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); +} + +static void +set_system_language (CcRegionPanel *self, + const gchar *language) +{ + if (g_strcmp0 (language, self->system_language) == 0) + return; + + g_free (self->system_language); + self->system_language = g_strdup (language); + + set_localed_locale (self); +} + +static void +update_language (CcRegionPanel *self, + CcLocaleTarget target, + const gchar *language) +{ + switch (target) { + case USER: + if (g_strcmp0 (language, self->language) == 0) + return; + act_user_set_language (self->user, language); + if (self->login_auto_apply) + set_system_language (self, language); + maybe_notify (self, LC_MESSAGES, language); + break; + + case SYSTEM: + set_system_language (self, language); + break; + } +} + +static void +set_system_region (CcRegionPanel *self, + const gchar *region) +{ + if (g_strcmp0 (region, self->system_region) == 0) + return; + + g_free (self->system_region); + self->system_region = g_strdup (region); + + set_localed_locale (self); +} + +static void +update_region (CcRegionPanel *self, + CcLocaleTarget target, + const gchar *region) +{ + switch (target) { + case USER: + if (g_strcmp0 (region, self->region) == 0) + return; + if (region == NULL || region[0] == '\0') + g_settings_reset (self->locale_settings, KEY_REGION); + else + g_settings_set_string (self->locale_settings, KEY_REGION, region); + if (self->login_auto_apply) + set_system_region (self, region); + + if (region == NULL || region[0] == '\0') { + // Formats (region) are being reset as part of changing the language, + // and that already triggers the notification check. + return; + } + + maybe_notify (self, LC_TIME, region); + break; + + case SYSTEM: + set_system_region (self, region); + break; + } +} + +static void +language_response (CcRegionPanel *self, + gint response_id, + CcLanguageChooser *chooser) +{ + const gchar *language; + + if (response_id == GTK_RESPONSE_OK) { + CcLocaleTarget target; + + language = cc_language_chooser_get_language (chooser); + target = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (chooser), "target")); + update_language (self, target, language); + + /* Keep format strings consistent with the user's language */ + update_region (self, target, NULL); + } + + gtk_window_destroy (GTK_WINDOW (chooser)); +} + +static const gchar * +get_effective_language (CcRegionPanel *self, + CcLocaleTarget target) +{ + switch (target) { + case USER: + return self->language; + case SYSTEM: + return self->system_language; + default: + g_assert_not_reached (); + } +} + +static void +show_language_chooser (CcRegionPanel *self, + CcLocaleTarget target) +{ + CcLanguageChooser *chooser; + CcShell *shell; + + shell = cc_panel_get_shell (CC_PANEL (self)); + chooser = cc_language_chooser_new (); + gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (cc_shell_get_toplevel (shell))); + cc_language_chooser_set_language (chooser, get_effective_language (self, target)); + g_object_set_data (G_OBJECT (chooser), "target", GINT_TO_POINTER (target)); + g_signal_connect_object (chooser, "response", + G_CALLBACK (language_response), self, G_CONNECT_SWAPPED); + gtk_window_present (GTK_WINDOW (chooser)); +} + +static const gchar * +get_effective_region (CcRegionPanel *self, + CcLocaleTarget target) +{ + const gchar *region = NULL; + + switch (target) { + case USER: + region = self->region; + break; + + case SYSTEM: + region = self->system_region; + break; + } + + /* Region setting might be empty - show the language because + * that's what LC_TIME and others will effectively be when the + * user logs in again. */ + if (region == NULL || region[0] == '\0') + region = get_effective_language (self, target); + + return region; +} + +static void +format_response (CcRegionPanel *self, + gint response_id, + CcFormatChooser *chooser) +{ + const gchar *region; + + if (response_id == GTK_RESPONSE_OK) { + CcLocaleTarget target; + + region = cc_format_chooser_get_region (chooser); + target = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (chooser), "target")); + + update_region (self, target, region); + } + + gtk_window_destroy (GTK_WINDOW (chooser)); +} + +static void +show_region_chooser (CcRegionPanel *self, + CcLocaleTarget target) +{ + CcFormatChooser *chooser; + CcShell *shell; + + shell = cc_panel_get_shell (CC_PANEL (self)); + chooser = cc_format_chooser_new (); + gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (cc_shell_get_toplevel (shell))); + cc_format_chooser_set_region (chooser, get_effective_region (self, target)); + g_object_set_data (G_OBJECT (chooser), "target", GINT_TO_POINTER (target)); + g_signal_connect_object (chooser, "response", + G_CALLBACK (format_response), self, G_CONNECT_SWAPPED); + gtk_window_present (GTK_WINDOW (chooser)); +} + +static gboolean +permission_acquired (GPermission *permission, GAsyncResult *res, const gchar *action) +{ + g_autoptr(GError) error = NULL; + + if (!g_permission_acquire_finish (permission, res, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to acquire permission to %s: %s\n", error->message, action); + return FALSE; + } + + return TRUE; +} + +static void +choose_language_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + CcRegionPanel *self = user_data; + if (permission_acquired (G_PERMISSION (source), res, "choose language")) + show_language_chooser (self, SYSTEM); +} + +static void +choose_region_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + CcRegionPanel *self = user_data; + if (permission_acquired (G_PERMISSION (source), res, "choose region")) + show_region_chooser (self, SYSTEM); +} + +static void +update_user_region_row (CcRegionPanel *self) +{ + const gchar *region = get_effective_region (self, USER); + g_autofree gchar *name = NULL; + + if (region) + name = gnome_get_country_from_locale (region, region); + + if (!name) + name = gnome_get_country_from_locale (DEFAULT_LOCALE, DEFAULT_LOCALE); + + adw_action_row_set_subtitle (ADW_ACTION_ROW (self->formats_row), name); +} + +static void +update_user_language_row (CcRegionPanel *self) +{ + g_autofree gchar *name = NULL; + + if (self->language) + name = gnome_get_language_from_locale (self->language, self->language); + + if (!name) + name = gnome_get_language_from_locale (DEFAULT_LOCALE, DEFAULT_LOCALE); + + adw_action_row_set_subtitle (ADW_ACTION_ROW (self->language_row), name); + + /* Formats will change too if not explicitly set. */ + update_user_region_row (self); +} + +static void +update_language_from_user (CcRegionPanel *self) +{ + const gchar *language = NULL; + + if (act_user_is_loaded (self->user)) + language = act_user_get_language (self->user); + + if (language == NULL || *language == '\0') + language = setlocale (LC_MESSAGES, NULL); + + g_free (self->language); + self->language = g_strdup (language); + update_user_language_row (self); +} + +static void +update_region_from_setting (CcRegionPanel *self) +{ + g_free (self->region); + self->region = g_settings_get_string (self->locale_settings, KEY_REGION); + update_user_region_row (self); +} + +static void +setup_language_section (CcRegionPanel *self) +{ + self->user = act_user_manager_get_user_by_id (self->user_manager, getuid ()); + g_signal_connect_object (self->user, "notify::language", + G_CALLBACK (update_language_from_user), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->user, "notify::is-loaded", + G_CALLBACK (update_language_from_user), self, G_CONNECT_SWAPPED); + + self->locale_settings = g_settings_new (GNOME_SYSTEM_LOCALE_DIR); + g_signal_connect_object (self->locale_settings, "changed::" KEY_REGION, + G_CALLBACK (update_region_from_setting), self, G_CONNECT_SWAPPED); + + update_language_from_user (self); + update_region_from_setting (self); +} + +static void +update_login_region (CcRegionPanel *self) +{ + g_autofree gchar *name = NULL; + + if (self->system_region) + name = gnome_get_country_from_locale (self->system_region, self->system_region); + + if (!name) + name = gnome_get_country_from_locale (DEFAULT_LOCALE, DEFAULT_LOCALE); + + adw_action_row_set_subtitle (ADW_ACTION_ROW (self->login_formats_row), name); +} + +static void +update_login_language (CcRegionPanel *self) +{ + g_autofree gchar *name = NULL; + + if (self->system_language) + name = gnome_get_language_from_locale (self->system_language, self->system_language); + + if (!name) + name = gnome_get_language_from_locale (DEFAULT_LOCALE, DEFAULT_LOCALE); + + adw_action_row_set_subtitle (self->login_language_row, name); + update_login_region (self); +} + +static void +set_login_button_visibility (CcRegionPanel *self) +{ + gboolean has_multiple_users; + gboolean loaded; + + g_object_get (self->user_manager, "is-loaded", &loaded, NULL); + if (!loaded) + return; + + g_object_get (self->user_manager, "has-multiple-users", &has_multiple_users, NULL); + + self->login_auto_apply = !has_multiple_users && g_permission_get_allowed (self->permission); + gtk_widget_set_visible (self->login_group, !self->login_auto_apply); + + g_signal_handlers_disconnect_by_func (self->user_manager, set_login_button_visibility, self); +} + +/* Callbacks */ + +static void +on_localed_properties_changed (GDBusProxy *localed_proxy, + GVariant *changed_properties, + const gchar **invalidated_properties, + CcRegionPanel *self) +{ + g_autoptr(GVariant) v = NULL; + + v = g_dbus_proxy_get_cached_property (localed_proxy, "Locale"); + if (v) { + g_autofree const gchar **strv = NULL; + gsize len; + gint i; + const gchar *lang, *messages, *time; + + strv = g_variant_get_strv (v, &len); + + lang = messages = time = NULL; + for (i = 0; strv[i]; i++) { + if (g_str_has_prefix (strv[i], "LANG=")) { + lang = strv[i] + strlen ("LANG="); + } else if (g_str_has_prefix (strv[i], "LC_MESSAGES=")) { + messages = strv[i] + strlen ("LC_MESSAGES="); + } else if (g_str_has_prefix (strv[i], "LC_TIME=")) { + time = strv[i] + strlen ("LC_TIME="); + } + } + if (!lang) { + lang = setlocale (LC_MESSAGES, NULL); + } + if (!messages) { + messages = lang; + } + g_free (self->system_language); + self->system_language = g_strdup (messages); + g_free (self->system_region); + self->system_region = g_strdup (time); + + update_login_language (self); + } +} + +static void +localed_proxy_ready (GObject *source, + GAsyncResult *res, + gpointer data) +{ + CcRegionPanel *self = data; + GDBusProxy *proxy; + g_autoptr(GError) error = NULL; + + proxy = g_dbus_proxy_new_finish (res, &error); + + if (!proxy) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to contact localed: %s\n", error->message); + return; + } + + self->localed = proxy; + + g_signal_connect_object (self->localed, + "g-properties-changed", + G_CALLBACK (on_localed_properties_changed), + self, + 0); + + on_localed_properties_changed (self->localed, NULL, NULL, self); +} + +static void +setup_login_permission (CcRegionPanel *self) +{ + g_autoptr(GDBusConnection) bus = NULL; + g_autoptr(GError) error = NULL; + gboolean can_acquire; + gboolean loaded; + + self->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-locale", NULL, NULL, &error); + if (self->permission == NULL) { + g_warning ("Could not get 'org.freedesktop.locale1.set-locale' permission: %s", + error->message); + return; + } + + bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); + g_dbus_proxy_new (bus, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + cc_panel_get_cancellable (CC_PANEL (self)), + (GAsyncReadyCallback) localed_proxy_ready, + self); + + g_object_get (self->user_manager, "is-loaded", &loaded, NULL); + if (loaded) + set_login_button_visibility (self); + else + g_signal_connect_object (self->user_manager, "notify::is-loaded", + G_CALLBACK (set_login_button_visibility), self, G_CONNECT_SWAPPED); + + can_acquire = self->permission && + (g_permission_get_allowed (self->permission) || + g_permission_get_can_acquire (self->permission)); + gtk_widget_set_sensitive (self->login_group, can_acquire); +} + +static void +session_proxy_ready (GObject *source, + GAsyncResult *res, + gpointer data) +{ + CcRegionPanel *self = data; + GDBusProxy *proxy; + g_autoptr(GError) error = NULL; + + proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (!proxy) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to contact gnome-session: %s\n", error->message); + return; + } + + self->session = proxy; +} + +static void +on_login_formats_row_activated_cb (GtkListBoxRow *row, + CcRegionPanel *self) +{ + if (g_permission_get_allowed (self->permission)) { + show_region_chooser (self, SYSTEM); + } else if (g_permission_get_can_acquire (self->permission)) { + g_permission_acquire_async (self->permission, + cc_panel_get_cancellable (CC_PANEL (self)), + choose_region_permission_cb, + self); + } +} + +static void +on_login_language_row_activated_cb (GtkListBoxRow *row, + CcRegionPanel *self) +{ + if (g_permission_get_allowed (self->permission)) { + show_language_chooser (self, SYSTEM); + } else if (g_permission_get_can_acquire (self->permission)) { + g_permission_acquire_async (self->permission, + cc_panel_get_cancellable (CC_PANEL (self)), + choose_language_permission_cb, + self); + } +} + +static void +on_user_formats_row_activated_cb (GtkListBoxRow *row, + CcRegionPanel *self) +{ + show_region_chooser (self, USER); +} + +static void +on_user_language_row_activated_cb (GtkListBoxRow *row, + CcRegionPanel *self) +{ + show_language_chooser (self, USER); +} + +/* CcPanel overrides */ + +static const char * +cc_region_panel_get_help_uri (CcPanel *panel) +{ + return "help:gnome-help/prefs-language"; +} + +/* GObject overrides */ + +static void +cc_region_panel_finalize (GObject *object) +{ + CcRegionPanel *self = CC_REGION_PANEL (object); + GtkWidget *chooser; + + if (self->user_manager) { + g_signal_handlers_disconnect_by_data (self->user_manager, self); + self->user_manager = NULL; + } + + if (self->user) { + g_signal_handlers_disconnect_by_data (self->user, self); + self->user = NULL; + } + + g_clear_object (&self->permission); + g_clear_object (&self->localed); + g_clear_object (&self->session); + g_clear_object (&self->locale_settings); + g_free (self->language); + g_free (self->region); + g_free (self->system_language); + g_free (self->system_region); + + chooser = g_object_get_data (G_OBJECT (self), "input-chooser"); + if (chooser) + gtk_window_destroy (GTK_WINDOW (chooser)); + + G_OBJECT_CLASS (cc_region_panel_parent_class)->finalize (object); +} + +static void +cc_region_panel_class_init (CcRegionPanelClass * klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + CcPanelClass *panel_class = CC_PANEL_CLASS (klass); + + panel_class->get_help_uri = cc_region_panel_get_help_uri; + + object_class->finalize = cc_region_panel_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/region/cc-region-panel.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, formats_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, infobar); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, login_formats_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, login_group); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, login_language_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, language_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, restart_button); + + gtk_widget_class_bind_template_callback (widget_class, on_login_formats_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, on_login_language_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, on_user_formats_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, on_user_language_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, restart_now); +} + +static void +cc_region_panel_init (CcRegionPanel *self) +{ + g_autoptr(GFile) needs_restart_file = NULL; + + g_resources_register (cc_region_get_resource ()); + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->user_manager = act_user_manager_get_default (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + cc_panel_get_cancellable (CC_PANEL (self)), + session_proxy_ready, + self); + + setup_login_permission (self); + setup_language_section (self); + + needs_restart_file = get_needs_restart_file (); + if (g_file_query_exists (needs_restart_file, NULL)) + set_restart_notification_visible (self, NULL, TRUE); +} diff --git a/panels/region/cc-region-panel.h b/panels/region/cc-region-panel.h new file mode 100644 index 0000000..c0c8dac --- /dev/null +++ b/panels/region/cc-region-panel.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010 Intel, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Author: Sergey Udaltsov + * + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define CC_TYPE_REGION_PANEL (cc_region_panel_get_type ()) +G_DECLARE_FINAL_TYPE (CcRegionPanel, cc_region_panel, CC, REGION_PANEL, CcPanel) + +G_END_DECLS diff --git a/panels/region/cc-region-panel.ui b/panels/region/cc-region-panel.ui new file mode 100644 index 0000000..b66de90 --- /dev/null +++ b/panels/region/cc-region-panel.ui @@ -0,0 +1,132 @@ + + + + diff --git a/panels/region/gnome-region-panel.desktop.in.in b/panels/region/gnome-region-panel.desktop.in.in new file mode 100644 index 0000000..0bdc8ff --- /dev/null +++ b/panels/region/gnome-region-panel.desktop.in.in @@ -0,0 +1,18 @@ +[Desktop Entry] +Name=Region & Language +Comment=Select your display language and formats +Exec=gnome-control-center region +# Translators: Do NOT translate or transliterate this text (this is an icon file name)! +Icon=org.gnome.Settings-region-symbolic +Terminal=false +Type=Application +NoDisplay=true +StartupNotify=true +Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;X-GNOME-DetailsSettings; +OnlyShowIn=GNOME;Unity; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-control-center +X-GNOME-Bugzilla-Component=region +X-GNOME-Bugzilla-Version=@VERSION@ +# Translators: Search terms to find the Region and Language panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! +Keywords=Language;Layout;Keyboard;Input; diff --git a/panels/region/icons/meson.build b/panels/region/icons/meson.build new file mode 100644 index 0000000..a2c3888 --- /dev/null +++ b/panels/region/icons/meson.build @@ -0,0 +1,4 @@ +install_data( + 'scalable/org.gnome.Settings-region-symbolic.svg', + install_dir: join_paths(control_center_icondir, 'hicolor', 'scalable', 'apps') +) diff --git a/panels/region/icons/scalable/org.gnome.Settings-region-symbolic.svg b/panels/region/icons/scalable/org.gnome.Settings-region-symbolic.svg new file mode 100644 index 0000000..11ac471 --- /dev/null +++ b/panels/region/icons/scalable/org.gnome.Settings-region-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/panels/region/meson.build b/panels/region/meson.build new file mode 100644 index 0000000..71f9a62 --- /dev/null +++ b/panels/region/meson.build @@ -0,0 +1,59 @@ +panels_list += cappletname +desktop = 'gnome-@0@-panel.desktop'.format(cappletname) + +desktop_in = configure_file( + input: desktop + '.in.in', + output: desktop + '.in', + configuration: desktop_conf +) + +i18n.merge_file( + type: 'desktop', + input: desktop_in, + output: desktop, + po_dir: po_dir, + install: true, + install_dir: control_center_desktopdir +) + +sources = files( + 'cc-region-panel.c', + 'cc-format-chooser.c', + 'cc-format-preview.c', +) + +resource_data = files( + 'cc-format-chooser.ui', + 'cc-format-preview.ui', + 'cc-region-panel.ui', + 'view-layout-symbolic.svg', +) + +sources += gnome.compile_resources( + 'cc-' + cappletname + '-resources', + cappletname + '.gresource.xml', + c_name: 'cc_' + cappletname, + dependencies: resource_data, + export: true +) + +deps = common_deps + [ + accounts_dep, + gnome_desktop_dep, + liblanguage_dep, + polkit_gobject_dep +] + +if enable_ibus + deps += ibus_dep +endif + +panels_libs += static_library( + cappletname, + sources: sources, + include_directories: top_inc, + dependencies: deps, + c_args: cflags +) + +subdir('icons') diff --git a/panels/region/region.gresource.xml b/panels/region/region.gresource.xml new file mode 100644 index 0000000..e3bff86 --- /dev/null +++ b/panels/region/region.gresource.xml @@ -0,0 +1,11 @@ + + + + cc-format-chooser.ui + cc-region-panel.ui + cc-format-preview.ui + + + view-layout-symbolic.svg + + diff --git a/panels/region/view-layout-symbolic.svg b/panels/region/view-layout-symbolic.svg new file mode 100644 index 0000000..cfc561a --- /dev/null +++ b/panels/region/view-layout-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file -- cgit v1.2.3