Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • bluca/gnome-initial-setup
  • kkuo/gnome-initial-setup
  • binli/gnome-initial-setup
  • aleasto/gnome-initial-setup
  • seb128/gnome-initial-setup
  • matthew-hagemann/gnome-initial-setup
  • mwei/gnome-initial-setup
  • Sergio.Costas/gnome-initial-setup
  • bandali/gnome-initial-setup
  • janitor-team/proposed/gnome-initial-setup
  • nteodosio/gnome-initial-setup
  • gnome-team/gnome-initial-setup
  • azzar1-guest/gnome-initial-setup
  • wjt/gnome-initial-setup
  • 3v1n0/gnome-initial-setup
  • ptomato-guest/gnome-initial-setup
16 results
Select Git revision
Show changes
Commits on Source (45)
Showing
with 483 additions and 458 deletions
......@@ -4,5 +4,3 @@
# GNOME Builder litter
/.buildconfig
/subprojects/*
!/subprojects/*.wrap
\ No newline at end of file
46.beta
-------
* Avatars are now generated using HdyAvatar. (#111)
- In addition, the buttons overlaid on the avatar widget now have tooltips.
(!237)
* The Welcome page image is now more appropriately sized and not cropped. (#167)
* The Welcome page now uses PRETTY_NAME from os-release rather than combining
the NAME + VERSION_ID fields. (!225)
* The summary page no longer expresses our collective hope that users love
GNOME, reducing the overall exuberance of the page. (!232)
Bugs fixed:
* On distributions which show the minimise button in general, Initial Setup
no longer shows it, making it harder to hide the Initial Setup window and
not know how to get it back. (!237)
* Clicking the ABRT privacy policy link, if present, is now handled. This was
a regression in 46.alpha.
* The “minimize” button is no longer shown in the header bar (on distributions
which override the defaults to show it on most windows.
* While setting up an enterprise user account, the window would previously
freeze, potentially triggering the force-quit dialog. This is now fixed.
(!216, #79)
* On distributions which reënable the existing-user mode, the window can now
be closed with Alt+F4 once again. (!224)
Translation updates:
- Czech (Daniel Rusek)
- Galician (Fran Dieguez)
- Georgian (Ekaterine Papava)
- Russian (Artur S0)
- Turkish (Sabri Ünal, Emin Tufan Çetin)
46.alpha
--------
......
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk30">
<object class="GtkHeaderBar" id="titlebar">
<property name="show-title-buttons">False</property>
<child type="title">
<object class="GtkLabel" id="title">
<attributes>
......
......@@ -64,9 +64,10 @@ typedef enum {
PROP_PARENTAL_CONTROLS_ENABLED,
PROP_FULL_NAME,
PROP_AVATAR,
PROP_HAS_DEFAULT_AVATAR,
} GisDriverProperty;
static GParamSpec *obj_props[PROP_AVATAR + 1];
static GParamSpec *obj_props[PROP_HAS_DEFAULT_AVATAR + 1];
struct _GisDriver {
AdwApplication parent_instance;
......@@ -90,7 +91,8 @@ struct _GisDriver {
gchar *username;
gchar *full_name; /* (owned) (nullable) */
GdkPaintable *avatar; /* (owned) (nullable) */
GdkTexture *avatar; /* (owned) (nullable) */
gboolean has_default_avatar;
GisDriverMode mode;
UmAccountMode account_mode;
......@@ -299,10 +301,10 @@ gis_driver_get_full_name (GisDriver *driver)
*/
void
gis_driver_set_avatar (GisDriver *driver,
GdkPaintable *avatar)
GdkTexture *avatar)
{
g_return_if_fail (GIS_IS_DRIVER (driver));
g_return_if_fail (avatar == NULL || GDK_IS_PAINTABLE (avatar));
g_return_if_fail (avatar == NULL || GDK_IS_TEXTURE (avatar));
if (g_set_object (&driver->avatar, avatar))
g_object_notify_by_pspec (G_OBJECT (driver), obj_props[PROP_AVATAR]);
......@@ -317,7 +319,7 @@ gis_driver_set_avatar (GisDriver *driver,
* Returns: (nullable) (transfer none): avatar of the main user, or %NULL if not known
* Since: 3.36
*/
GdkPaintable *
GdkTexture *
gis_driver_get_avatar (GisDriver *driver)
{
g_return_val_if_fail (GIS_IS_DRIVER (driver), NULL);
......@@ -325,6 +327,42 @@ gis_driver_get_avatar (GisDriver *driver)
return driver->avatar;
}
/**
* gis_driver_set_has_default_avatar:
* @driver: a #GisDriver
* @has_default_avatar: whether the generated user avatar should be used
*
* Set the #GisDriver:has-default-avatar property.
*
* Since: 46
*/
void
gis_driver_set_has_default_avatar (GisDriver *driver,
gboolean has_default_avatar)
{
if (driver->has_default_avatar == has_default_avatar)
return;
driver->has_default_avatar = has_default_avatar;
g_object_notify_by_pspec (G_OBJECT (driver), obj_props[PROP_HAS_DEFAULT_AVATAR]);
}
/**
* gis_driver_get_has_default_avatar:
* @driver: a #GisDriver
*
* Get the #GisDriver:has-default-avatar property.
*
* Returns: whether the generated user avatar should be used
* Since: 46
*/
gboolean
gis_driver_get_has_default_avatar (GisDriver *driver)
{
return driver->has_default_avatar;
}
void
gis_driver_set_user_permissions (GisDriver *driver,
ActUser *user,
......@@ -628,6 +666,9 @@ gis_driver_get_property (GObject *object,
case PROP_AVATAR:
g_value_set_object (value, driver->avatar);
break;
case PROP_HAS_DEFAULT_AVATAR:
g_value_set_boolean (value, driver->has_default_avatar);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -660,6 +701,9 @@ gis_driver_set_property (GObject *object,
case PROP_AVATAR:
gis_driver_set_avatar (driver, g_value_get_object (value));
break;
case PROP_HAS_DEFAULT_AVATAR:
gis_driver_set_has_default_avatar (driver, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -802,10 +846,13 @@ gis_driver_startup (GApplication *app)
G_CALLBACK (window_realize_cb),
(gpointer)app);
/* Only allow closing the window in existing user mode*/
if (driver->mode != GIS_DRIVER_MODE_EXISTING_USER) {
g_signal_connect (driver->main_window,
"close-request",
G_CALLBACK (window_close_request_cb),
NULL);
}
driver->assistant = g_object_new (GIS_TYPE_ASSISTANT, NULL);
gtk_window_set_child (GTK_WINDOW (driver->main_window),
......@@ -821,6 +868,7 @@ static void
gis_driver_init (GisDriver *driver)
{
load_vendor_conf_file (driver);
driver->has_default_avatar = FALSE;
}
static void
......@@ -910,7 +958,23 @@ gis_driver_class_init (GisDriverClass *klass)
g_param_spec_object ("avatar",
"Avatar",
"Avatar of the main user.",
GDK_TYPE_PAINTABLE,
GDK_TYPE_TEXTURE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
/**
* GisDriver:has-default-avatar:
*
* Whether the generated user avatar should be used, If this is %TRUE then,
* the default generated avatar should be used, instead of the #GdkTexture
* returned by #GisDriver:avatar property.
*
* Since: 46
*/
obj_props[PROP_HAS_DEFAULT_AVATAR] =
g_param_spec_boolean ("has-default-avatar",
"Has Default Avatar",
"Whether the generated user avatar should be used",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, G_N_ELEMENTS (obj_props), obj_props);
......
......@@ -88,8 +88,12 @@ void gis_driver_set_full_name (GisDriver *driver,
const gchar *gis_driver_get_full_name (GisDriver *driver);
void gis_driver_set_avatar (GisDriver *driver,
GdkPaintable *avatar);
GdkPaintable *gis_driver_get_avatar (GisDriver *driver);
GdkTexture *avatar);
GdkTexture *gis_driver_get_avatar (GisDriver *driver);
void gis_driver_set_has_default_avatar (GisDriver *driver,
gboolean has_default_avatar);
gboolean gis_driver_get_has_default_avatar (GisDriver *driver);
gboolean gis_driver_get_gdm_objects (GisDriver *driver,
GdmGreeter **greeter,
......
......@@ -26,16 +26,7 @@ sources += [
'gis-keyring.h'
]
geocode_glib_2_dep = dependency(
'geocode-glib-2.0',
fallback: ['geocode-glib', 'geocode_glib_dep'],
default_options: [
'enable-gtk-doc=false',
'enable-installed-tests=false',
'enable-introspection=false',
'soup2=false',
],
)
geocode_glib_2_dep = dependency('geocode-glib-2.0')
gweather_dep = dependency('gweather4')
......
<?xml version="1.0"?>
<interface>
<template class="UmPhotoDialog" parent="GtkPopover">
<property name="height-request">360</property>
<property name="width-request">480</property>
<child>
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkFlowBox" id="recent_pictures">
<property name="halign">start</property>
<property name="margin-top">20</property>
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-bottom">0</property>
<property name="selection-mode">none</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="flowbox">
<property name="margin-top">12</property>
......@@ -23,6 +11,8 @@
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="selection-mode">none</property>
<property name="max-children-per-line">5</property>
<property name="homogeneous">True</property>
</object>
</child>
</object>
......
......@@ -166,6 +166,36 @@ gis_account_page_enterprise_validate (GisAccountPageEnterprise *page)
return valid_name && valid_domain;
}
static void
on_cache_user (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GisAccountPageEnterprise *page = user_data;
GError *error = NULL;
page->act_user = act_user_manager_cache_user_finish (ACT_USER_MANAGER (source),
result,
&error);
if (error != NULL) {
show_error_dialog (page, _("Failed to cache account"), error);
g_message ("Couldn't cache account: %s", error->message);
g_error_free (error);
apply_complete (page, FALSE);
return;
}
act_user_set_account_type (page->act_user,
ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR);
g_signal_emit (page,
signals[USER_CACHED],
0,
page->act_user,
gtk_editable_get_text (GTK_EDITABLE (page->password)));
apply_complete (page, TRUE);
}
static void
on_permit_user_login (GObject *source,
GAsyncResult *result,
......@@ -190,10 +220,11 @@ on_permit_user_login (GObject *source,
g_debug ("Caching remote user: %s", login);
page->act_user = act_user_manager_cache_user (page->act_client, login, NULL);
act_user_set_account_type (page->act_user, ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR);
g_signal_emit (page, signals[USER_CACHED], 0, page->act_user, gtk_editable_get_text (GTK_EDITABLE (page->password)));
apply_complete (page, TRUE);
act_user_manager_cache_user_async (page->act_client,
login,
page->cancellable,
on_cache_user,
page);
g_free (login);
} else {
......
......@@ -27,6 +27,7 @@
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <glib/gstdio.h>
#include <string.h>
#include <act/act-user-manager.h>
......@@ -37,6 +38,15 @@
#include <json-glib/json-glib.h>
/* Key and values that are written as metadata to the exported user avatar this
* way it's possible to know how the image was initially created.
* If set to generated we can regenerated the avatar when the style changes or
* when the users full name changes. The other two values don't have a specific use yet */
#define IMAGE_SOURCE_KEY "tEXt::source"
#define IMAGE_SOURCE_VALUE_GENERATED "gnome-generated"
#define IMAGE_SOURCE_VALUE_FACE "gnome-face"
#define IMAGE_SOURCE_VALUE_CUSTOM "gnome-custom"
#define IMAGE_SIZE 512
#define VALIDATION_TIMEOUT 600
struct _GisAccountPageLocal
......@@ -44,6 +54,7 @@ struct _GisAccountPageLocal
AdwBin parent;
GtkWidget *avatar_button;
GtkWidget *remove_avatar_button;
GtkWidget *avatar_image;
GtkWidget *header;
GtkWidget *fullname_entry;
......@@ -56,9 +67,6 @@ struct _GisAccountPageLocal
gint timeout_id;
GdkPixbuf *avatar_pixbuf;
gchar *avatar_filename;
ActUserManager *act_client;
gboolean valid_name;
......@@ -84,6 +92,25 @@ validation_changed (GisAccountPageLocal *page)
g_signal_emit (page, signals[VALIDATION_CHANGED], 0);
}
static void
update_avatar_text (GisAccountPageLocal *page)
{
const gchar *name;
name = gtk_editable_get_text (GTK_EDITABLE (page->fullname_entry));
if (*name == '\0') {
GtkWidget *entry = gtk_combo_box_get_child (GTK_COMBO_BOX (page->username_combo));
name = gtk_editable_get_text (GTK_EDITABLE (entry));
}
if (*name == '\0') {
name = NULL;
}
adw_avatar_set_text (ADW_AVATAR (page->avatar_image), name);
}
static gboolean
validate (GisAccountPageLocal *page)
{
......@@ -115,8 +142,6 @@ validate (GisAccountPageLocal *page)
gtk_label_set_text (GTK_LABEL (page->username_explanation), tip);
g_free (tip);
um_photo_dialog_generate_avatar (page->photo_dialog, name);
validation_changed (page);
return G_SOURCE_REMOVE;
......@@ -160,6 +185,8 @@ fullname_changed (GtkWidget *w,
page->valid_name = FALSE;
/* username_changed() is called consequently due to changes */
update_avatar_text (page);
}
static void
......@@ -185,40 +212,34 @@ username_changed (GtkComboBoxText *combo,
if (page->timeout_id != 0)
g_source_remove (page->timeout_id);
page->timeout_id = g_timeout_add (VALIDATION_TIMEOUT, (GSourceFunc)validate, page);
update_avatar_text (page);
}
static void
on_remove_avatar_button_clicked (GisAccountPageLocal *page)
{
adw_avatar_set_custom_image (ADW_AVATAR (page->avatar_image), NULL);
gtk_widget_set_visible (GTK_WIDGET (page->remove_avatar_button), FALSE);
}
static void
avatar_callback (GdkPixbuf *pixbuf,
const gchar *filename,
avatar_callback (const gchar *filename,
gpointer user_data)
{
GisAccountPageLocal *page = user_data;
g_autoptr(GdkPixbuf) tmp = NULL;
g_autoptr(GdkPixbuf) rounded = NULL;
g_clear_object (&page->avatar_pixbuf);
g_clear_pointer (&page->avatar_filename, g_free);
g_autoptr(GdkTexture) texture = NULL;
if (pixbuf) {
page->avatar_pixbuf = g_object_ref (pixbuf);
rounded = round_image (pixbuf);
}
else if (filename) {
page->avatar_filename = g_strdup (filename);
tmp = gdk_pixbuf_new_from_file_at_size (filename, 96, 96, NULL);
if (filename) {
g_autoptr(GError) error = NULL;
texture = gdk_texture_new_from_filename (filename, &error);
if (tmp != NULL)
rounded = round_image (tmp);
if (error)
g_warning ("Failed to load user icon from path %s: %s", filename, error->message);
}
if (rounded != NULL) {
gtk_image_set_from_pixbuf (GTK_IMAGE (page->avatar_image), rounded);
}
else {
/* Fallback. */
gtk_image_set_pixel_size (GTK_IMAGE (page->avatar_image), 96);
gtk_image_set_from_icon_name (GTK_IMAGE (page->avatar_image), "avatar-default-symbolic");
}
adw_avatar_set_custom_image (ADW_AVATAR (page->avatar_image), GDK_PAINTABLE (texture));
gtk_widget_set_visible (GTK_WIDGET (page->remove_avatar_button), texture != NULL);
}
static void
......@@ -264,6 +285,7 @@ gis_account_page_local_constructed (GObject *object)
G_OBJECT_CLASS (gis_account_page_local_parent_class)->constructed (object);
page->act_client = act_user_manager_get_default ();
page->photo_dialog = um_photo_dialog_new (avatar_callback, page);
g_signal_connect (page->fullname_entry, "notify::text",
G_CALLBACK (fullname_changed), page);
......@@ -298,11 +320,6 @@ gis_account_page_local_constructed (GObject *object)
gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (page->username_combo))));
page->has_custom_username = FALSE;
gtk_image_set_pixel_size (GTK_IMAGE (page->avatar_image), 96);
gtk_image_set_from_icon_name (GTK_IMAGE (page->avatar_image), "avatar-default-symbolic");
page->photo_dialog = um_photo_dialog_new (avatar_callback, page);
um_photo_dialog_generate_avatar (page->photo_dialog, "");
gtk_menu_button_set_popover (GTK_MENU_BUTTON (page->avatar_button),
GTK_WIDGET (page->photo_dialog));
......@@ -314,48 +331,122 @@ gis_account_page_local_dispose (GObject *object)
{
GisAccountPageLocal *page = GIS_ACCOUNT_PAGE_LOCAL (object);
g_clear_object (&page->avatar_pixbuf);
g_clear_pointer (&page->avatar_filename, g_free);
g_clear_handle_id (&page->timeout_id, g_source_remove);
G_OBJECT_CLASS (gis_account_page_local_parent_class)->dispose (object);
}
/* This function was taken from AdwAvatar and modified so that it's possible to
* export a GdkTexture at a different size than the AdwAvatar is rendered
* See: https://gitlab.gnome.org/GNOME/libadwaita/-/blob/afd0fab86ff9b4332d165b985a435ea6f822d41b/src/adw-avatar.c#L751
* License: LGPL-2.1-or-later */
static GdkTexture *
draw_avatar_to_texture (AdwAvatar *avatar, int size)
{
GdkTexture *result;
GskRenderNode *node;
GtkSnapshot *snapshot;
GdkPaintable *paintable;
GtkNative *native;
GskRenderer *renderer;
int real_size;
graphene_matrix_t transform;
gboolean transform_ok;
real_size = adw_avatar_get_size (avatar);
/* This works around the issue that when the custom-image or text of the AdwAvatar changes the
* allocation gets invalidateds and therefore we can't snapshot the widget till the allocation
* is recalculated */
gtk_widget_measure (GTK_WIDGET (avatar), GTK_ORIENTATION_HORIZONTAL, real_size, NULL, NULL, NULL, NULL);
gtk_widget_allocate (GTK_WIDGET (avatar), real_size, real_size, -1, NULL);
transform_ok = gtk_widget_compute_transform (GTK_WIDGET (avatar),
gtk_widget_get_first_child (GTK_WIDGET (avatar)),
&transform);
g_assert (transform_ok);
snapshot = gtk_snapshot_new ();
gtk_snapshot_transform_matrix (snapshot, &transform);
GTK_WIDGET_GET_CLASS (avatar)->snapshot (GTK_WIDGET (avatar), snapshot);
/* Create first a GdkPaintable at the size the avatar was drawn
* then create a GdkSnapshot of it at the size requested */
paintable = gtk_snapshot_free_to_paintable (snapshot, &GRAPHENE_SIZE_INIT (real_size, real_size));
snapshot = gtk_snapshot_new ();
gdk_paintable_snapshot (paintable, snapshot, size, size);
g_object_unref (paintable);
node = gtk_snapshot_free_to_node (snapshot);
native = gtk_widget_get_native (GTK_WIDGET (avatar));
renderer = gtk_native_get_renderer (native);
result = gsk_renderer_render_texture (renderer, node, &GRAPHENE_RECT_INIT (-1, 0, size, size));
gsk_render_node_unref (node);
return result;
}
static GdkPixbuf *
texture_to_pixbuf (GdkTexture *texture)
{
g_autoptr(GdkTextureDownloader) downloader = NULL;
g_autoptr(GBytes) bytes = NULL;
gsize stride;
downloader = gdk_texture_downloader_new (texture);
gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R8G8B8A8);
bytes = gdk_texture_downloader_download_bytes (downloader, &stride);
return gdk_pixbuf_new_from_bytes (bytes,
GDK_COLORSPACE_RGB,
true,
8,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture),
stride);
}
static void
set_user_avatar (GisAccountPageLocal *page,
set_user_avatar (GisPage *page,
ActUser *user)
{
GFile *file = NULL;
GFileIOStream *io_stream = NULL;
GOutputStream *stream = NULL;
GError *error = NULL;
GdkTexture *texture = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *path = NULL;
const gchar *image_source;
int fd;
if (page->avatar_filename != NULL) {
act_user_set_icon_file (user, page->avatar_filename);
return;
}
texture = gis_driver_get_avatar (page->driver);
if (gis_driver_get_has_default_avatar (page->driver))
image_source = IMAGE_SOURCE_VALUE_GENERATED;
else
image_source = IMAGE_SOURCE_VALUE_FACE;
/* IMAGE_SOURCE_VALUE_CUSTOM isn't used here since we don't allow custom files
* to be set during initial setup */
fd = g_file_open_tmp ("usericonXXXXXX", &path, &error);
if (page->avatar_pixbuf == NULL) {
if (fd == -1) {
g_warning ("Failed to create temporary user icon: %s", error->message);
return;
}
file = g_file_new_tmp ("usericonXXXXXX", &io_stream, &error);
if (error != NULL)
goto out;
g_autoptr(GdkPixbuf) pixbuf = texture_to_pixbuf (texture);
if (!gdk_pixbuf_save (pixbuf, path, "png", &error, IMAGE_SOURCE_KEY, image_source, NULL)) {
g_warning ("Failed to save temporary user icon: %s", error->message);
}
stream = g_io_stream_get_output_stream (G_IO_STREAM (io_stream));
if (!gdk_pixbuf_save_to_stream (page->avatar_pixbuf, stream, "png", NULL, &error, NULL))
goto out;
close (fd);
act_user_set_icon_file (user, g_file_get_path (file));
act_user_set_icon_file (user, path);
out:
if (error != NULL) {
g_warning ("failed to save image: %s", error->message);
g_error_free (error);
}
g_clear_object (&io_stream);
g_clear_object (&file);
g_remove (path);
}
static gboolean
......@@ -435,7 +526,7 @@ local_create_user (GisAccountPageLocal *local,
return FALSE;
}
set_user_avatar (local, main_user);
set_user_avatar (page, main_user);
g_signal_emit (local, signals[MAIN_USER_CREATED], 0, main_user, "");
......@@ -450,6 +541,7 @@ gis_account_page_local_class_init (GisAccountPageLocalClass *klass)
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-account-page-local.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, avatar_button);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, remove_avatar_button);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, avatar_image);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, header);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, fullname_entry);
......@@ -457,6 +549,8 @@ gis_account_page_local_class_init (GisAccountPageLocalClass *klass)
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, username_explanation);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, enable_parental_controls_box);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, enable_parental_controls_check_button);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), on_remove_avatar_button_clicked);
object_class->constructed = gis_account_page_local_constructed;
object_class->dispose = gis_account_page_local_dispose;
......@@ -502,33 +596,32 @@ gis_account_page_local_create_user (GisAccountPageLocal *local,
gboolean
gis_account_page_local_apply (GisAccountPageLocal *local, GisPage *page)
{
GisDriver *driver = GIS_PAGE (page)->driver;
const gchar *username, *full_name;
gboolean parental_controls_enabled;
GdkTexture *texture = NULL;
g_object_freeze_notify (G_OBJECT (driver));
username = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (local->username_combo));
gis_driver_set_username (GIS_PAGE (page)->driver, username);
gis_driver_set_username (driver, username);
full_name = gtk_editable_get_text (GTK_EDITABLE (local->fullname_entry));
gis_driver_set_full_name (GIS_PAGE (page)->driver, full_name);
gis_driver_set_full_name (driver, full_name);
if (local->avatar_pixbuf != NULL)
{
g_autoptr(GdkTexture) texture = NULL;
texture = GDK_TEXTURE (adw_avatar_get_custom_image (ADW_AVATAR (local->avatar_image)));
texture = gdk_texture_new_for_pixbuf (local->avatar_pixbuf);
gis_driver_set_avatar (GIS_PAGE (page)->driver, GDK_PAINTABLE (texture));
}
else if (local->avatar_filename != NULL)
if (texture)
{
g_autoptr(GdkTexture) texture = NULL;
g_autoptr(GError) error = NULL;
texture = gdk_texture_new_from_filename (local->avatar_filename, &error);
if (!error)
gis_driver_set_avatar (GIS_PAGE (page)->driver, GDK_PAINTABLE (texture));
gis_driver_set_avatar (driver, texture);
gis_driver_set_has_default_avatar (driver, FALSE);
}
else
g_warning ("Error loading avatar: %s", error->message);
{
texture = draw_avatar_to_texture (ADW_AVATAR (local->avatar_image), IMAGE_SIZE);
gis_driver_set_avatar (driver, texture);
gis_driver_set_has_default_avatar (driver, TRUE);
g_object_unref (texture);
}
#ifdef HAVE_PARENTAL_CONTROLS
......@@ -536,7 +629,9 @@ gis_account_page_local_apply (GisAccountPageLocal *local, GisPage *page)
#else
parental_controls_enabled = FALSE;
#endif
gis_driver_set_parental_controls_enabled (GIS_PAGE (page)->driver, parental_controls_enabled);
gis_driver_set_parental_controls_enabled (driver, parental_controls_enabled);
g_object_thaw_notify (G_OBJECT (driver));
return FALSE;
}
......
......@@ -11,9 +11,9 @@
<property name="margin_top">24</property>
<property name="halign">center</property>
<child>
<object class="GtkImage" id="avatar_image">
<property name="pixel_size">96</property>
<property name="icon_name">avatar-default-symbolic</property>
<object class="AdwAvatar" id="avatar_image">
<property name="size">128</property>
<property name="show-initials">True</property>
</object>
</child>
<child type="overlay">
......@@ -25,10 +25,8 @@
<property name="valign">end</property>
<child>
<object class="GtkMenuButton" id="avatar_button">
<property name="tooltip-text" translatable="yes">Change Avatar</property>
<property name="icon-name">document-edit-symbolic</property>
<accessibility>
<property name="label" translatable="yes">Edit avatar</property>
</accessibility>
<style>
<class name="circular"/>
</style>
......@@ -36,6 +34,27 @@
</child>
</object>
</child>
<child type="overlay">
<object class="AdwBin">
<style>
<class name="cutout-button"/>
</style>
<property name="halign">end</property>
<property name="valign">start</property>
<child>
<object class="GtkButton" id="remove_avatar_button">
<property name="tooltip-text" translatable="yes">Remove Avatar</property>
<property name="visible">False</property>
<property name="icon-name">user-trash-symbolic</property>
<signal name="clicked" handler="on_remove_avatar_button_clicked" object="GisAccountPageLocal" swapped="yes"/>
<style>
<class name="circular"/>
<class name="destructive-image-button"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
......
......@@ -3,3 +3,8 @@
border-radius: 9999px;
padding: 2px;
}
/* This is used for remove_avatar_button */
.destructive-image-button {
color: @destructive_color;
}
......@@ -27,6 +27,7 @@
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <adwaita.h>
#include "um-photo-dialog.h"
#include "um-utils.h"
......@@ -38,12 +39,8 @@ struct _UmPhotoDialog {
GtkPopover parent;
GtkWidget *flowbox;
GtkWidget *recent_pictures;
GListStore *recent_faces;
GListStore *faces;
GFile *generated_avatar;
gboolean custom_avatar_was_chosen;
SelectAvatarCallback *callback;
gpointer data;
......@@ -69,42 +66,23 @@ face_widget_activated (GtkFlowBox *flowbox,
image = gtk_flow_box_child_get_child (child);
filename = g_object_get_data (G_OBJECT (image), "filename");
um->callback (NULL, filename, um->data);
um->custom_avatar_was_chosen = TRUE;
um->callback (filename, um->data);
gtk_popover_popdown (GTK_POPOVER (um));
}
static void
generated_avatar_activated (GtkFlowBox *flowbox,
GtkFlowBoxChild *child,
UmPhotoDialog *um)
{
face_widget_activated (flowbox, child, um);
um->custom_avatar_was_chosen = FALSE;
}
static GtkWidget *
create_face_widget (gpointer item,
gpointer user_data)
{
g_autoptr(GdkPixbuf) pixbuf = NULL;
GtkWidget *image;
g_autoptr(GdkTexture) texture = NULL;
g_autofree gchar *path = g_file_get_path (G_FILE (item));
GtkWidget *image;
pixbuf = gdk_pixbuf_new_from_file_at_size (path,
AVATAR_PIXEL_SIZE,
AVATAR_PIXEL_SIZE,
NULL);
if (pixbuf != NULL)
image = gtk_image_new_from_pixbuf (round_image (pixbuf));
else
image = gtk_image_new ();
gtk_image_set_pixel_size (GTK_IMAGE (image), AVATAR_PIXEL_SIZE);
image = adw_avatar_new (AVATAR_PIXEL_SIZE, NULL, TRUE);
gtk_widget_set_visible (image, TRUE);
texture = gdk_texture_new_from_file (G_FILE (item), NULL);
adw_avatar_set_custom_image (ADW_AVATAR (image), GDK_PAINTABLE (texture));
g_object_set_data_full (G_OBJECT (image),
"filename", g_steal_pointer (&path),
......@@ -222,16 +200,6 @@ setup_photo_popup (UmPhotoDialog *um)
g_signal_connect (um->flowbox, "child-activated",
G_CALLBACK (face_widget_activated), um);
um->recent_faces = g_list_store_new (G_TYPE_FILE);
gtk_flow_box_bind_model (GTK_FLOW_BOX (um->recent_pictures),
G_LIST_MODEL (um->recent_faces),
create_face_widget,
um,
NULL);
g_signal_connect (um->recent_pictures, "child-activated",
G_CALLBACK (generated_avatar_activated), um);
um->custom_avatar_was_chosen = FALSE;
facesdirs = get_settings_facesdirs ();
added_faces = add_faces_from_dirs (um->faces, facesdirs, TRUE);
......@@ -241,33 +209,6 @@ setup_photo_popup (UmPhotoDialog *um)
}
}
void
um_photo_dialog_generate_avatar (UmPhotoDialog *um,
const gchar *name)
{
cairo_surface_t *surface;
gchar *filename;
surface = generate_user_picture (name);
/* Save into a tmp file that later gets copied by AccountsService */
filename = g_build_filename (g_get_user_runtime_dir (), "avatar.png", NULL);
um->generated_avatar = g_file_new_for_path (filename);
cairo_surface_write_to_png (surface, g_file_get_path (um->generated_avatar));
g_free (filename);
/* Overwrite the first item */
if (g_list_model_get_item (G_LIST_MODEL (um->recent_faces), 0) != NULL)
g_list_store_remove (um->recent_faces, 0);
g_list_store_insert (um->recent_faces, 0,
um->generated_avatar);
if (!um->custom_avatar_was_chosen) {
um->callback (NULL, g_file_get_path (um->generated_avatar), um->data);
}
}
UmPhotoDialog *
um_photo_dialog_new (SelectAvatarCallback callback,
gpointer data)
......@@ -305,7 +246,6 @@ um_photo_dialog_class_init (UmPhotoDialogClass *klass)
gtk_widget_class_set_template_from_resource (wclass, "/org/gnome/initial-setup/gis-account-avatar-chooser.ui");
gtk_widget_class_bind_template_child (wclass, UmPhotoDialog, flowbox);
gtk_widget_class_bind_template_child (wclass, UmPhotoDialog, recent_pictures);
gtk_widget_class_bind_template_callback (wclass, webcam_icon_selected);
oclass->dispose = um_photo_dialog_dispose;
......
......@@ -32,15 +32,13 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (UmPhotoDialog, um_photo_dialog, UM, PHOTO_DIALOG, GtkPopover)
typedef struct _UmPhotoDialog UmPhotoDialog;
typedef void (SelectAvatarCallback) (GdkPixbuf *pixbuf,
const gchar *filename,
typedef void (SelectAvatarCallback) (const gchar *filename,
gpointer data);
UmPhotoDialog *um_photo_dialog_new (SelectAvatarCallback callback,
gpointer data);
void um_photo_dialog_free (UmPhotoDialog *dialog);
void um_photo_dialog_generate_avatar (UmPhotoDialog *dialog,
void um_photo_dialog_set_generated_avatar_text (UmPhotoDialog *dialog,
const gchar *name);
G_END_DECLS
......
......@@ -387,182 +387,3 @@ generate_username_choices (const gchar *name,
g_string_free (item3, TRUE);
g_string_free (item4, TRUE);
}
#define IMAGE_SIZE 512
#define IMAGE_BORDER_WIDTH 7.2 /* At least 1px when avatar rendered at 72px */
/* U+1F464 "bust in silhouette"
* U+FE0E Variant Selector 15 to force text style (monochrome) emoji
*/
#define PLACEHOLDER "\U0001F464\U0000FE0E"
static gchar *
extract_initials_from_name (const gchar *name)
{
GString *initials = g_string_new ("");
gchar *p;
gchar *normalized;
gunichar unichar;
if (name == NULL || name[0] == '\0') {
g_string_free (initials, TRUE);
return g_strdup (PLACEHOLDER);
}
p = g_utf8_strup (name, -1);
normalized = g_utf8_normalize (g_strstrip (p), -1, G_NORMALIZE_DEFAULT_COMPOSE);
g_clear_pointer (&p, g_free);
if (normalized == NULL) {
g_free (normalized);
g_string_free (initials, TRUE);
return g_strdup (PLACEHOLDER);
}
unichar = g_utf8_get_char (normalized);
g_string_append_unichar (initials, unichar);
p = g_utf8_strrchr (normalized, -1, ' ');
if (p != NULL) {
p = g_utf8_next_char (p);
unichar = g_utf8_get_char (p);
g_string_append_unichar (initials, unichar);
}
g_free (normalized);
return g_string_free (initials, FALSE);
}
GdkRGBA
get_color_for_name (const gchar *name)
{
// https://gitlab.gnome.org/Community/Design/HIG-app-icons/blob/master/GNOME%20HIG.gpl
static gdouble gnome_color_palette[][3] = {
{ 98, 160, 234 },
{ 53, 132, 228 },
{ 28, 113, 216 },
{ 26, 95, 180 },
{ 87, 227, 137 },
{ 51, 209, 122 },
{ 46, 194, 126 },
{ 38, 162, 105 },
{ 248, 228, 92 },
{ 246, 211, 45 },
{ 245, 194, 17 },
{ 229, 165, 10 },
{ 255, 163, 72 },
{ 255, 120, 0 },
{ 230, 97, 0 },
{ 198, 70, 0 },
{ 237, 51, 59 },
{ 224, 27, 36 },
{ 192, 28, 40 },
{ 165, 29, 45 },
{ 192, 97, 203 },
{ 163, 71, 186 },
{ 129, 61, 156 },
{ 97, 53, 131 },
{ 181, 131, 90 },
{ 152, 106, 68 },
{ 134, 94, 60 },
{ 99, 69, 44 }
};
GdkRGBA color = { 255, 255, 255, 1.0 };
guint hash;
gint number_of_colors;
gint idx;
if (name == NULL || name[0] == '\0') {
idx = 5;
} else {
hash = g_str_hash (name);
number_of_colors = G_N_ELEMENTS (gnome_color_palette);
idx = hash % number_of_colors;
}
color.red = gnome_color_palette[idx][0];
color.green = gnome_color_palette[idx][1];
color.blue = gnome_color_palette[idx][2];
return color;
}
static void
darken_color (GdkRGBA *color, gdouble fraction) {
color->red = CLAMP (color->red + color->red * fraction, 0, 255);
color->green = CLAMP (color->green + color->green * fraction, 0, 255);
color->blue = CLAMP (color->blue + color->blue * fraction, 0, 255);
}
cairo_surface_t *
generate_user_picture (const gchar *name) {
PangoFontDescription *font_desc;
g_autofree gchar *initials = extract_initials_from_name (name);
g_autofree gchar *font = g_strdup_printf ("Sans %d", (int)ceil (IMAGE_SIZE / 2.5));
PangoLayout *layout;
GdkRGBA color = get_color_for_name (name);
cairo_surface_t *surface;
gint width, height;
cairo_t *cr;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
IMAGE_SIZE,
IMAGE_SIZE);
cr = cairo_create (surface);
cairo_arc (cr, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2, 0, 2 * G_PI);
cairo_set_source_rgb (cr, color.red/255.0, color.green/255.0, color.blue/255.0);
cairo_fill (cr);
cairo_arc (cr, IMAGE_SIZE / 2, IMAGE_SIZE / 2, IMAGE_SIZE / 2 - IMAGE_BORDER_WIDTH / 2,
0, 2 * G_PI);
darken_color (&color, -0.3);
cairo_set_source_rgb (cr, color.red / 255.0, color.green / 255.0, color.blue / 255.0);
cairo_set_line_width (cr, IMAGE_BORDER_WIDTH);
cairo_stroke (cr);
/* Draw the initials on top */
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
layout = pango_cairo_create_layout (cr);
pango_layout_set_text (layout, initials, -1);
font_desc = pango_font_description_from_string (font);
pango_layout_set_font_description (layout, font_desc);
pango_font_description_free (font_desc);
pango_layout_get_size (layout, &width, &height);
cairo_translate (cr, IMAGE_SIZE/2, IMAGE_SIZE/2);
cairo_move_to (cr, - ((double)width / PANGO_SCALE)/2, - ((double)height/PANGO_SCALE)/2);
pango_cairo_show_layout (cr, layout);
cairo_destroy (cr);
return surface;
}
GdkPixbuf *
round_image (GdkPixbuf *image)
{
GdkPixbuf *dest = NULL;
cairo_surface_t *surface;
cairo_t *cr;
gint size;
size = gdk_pixbuf_get_width (image);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size, size);
cr = cairo_create (surface);
/* Clip a circle */
cairo_arc (cr, size/2, size/2, size/2, 0, 2 * G_PI);
cairo_clip (cr);
cairo_new_path (cr);
gdk_cairo_set_source_pixbuf (cr, image, 0, 0);
cairo_paint (cr);
dest = gdk_pixbuf_get_from_surface (surface, 0, 0, size, size);
cairo_surface_destroy (surface);
cairo_destroy (cr);
return dest;
}
......@@ -41,6 +41,7 @@ struct _GisParentalControlsPage
GisPage parent_instance;
GtkWidget *header;
GtkWidget *avatar;
GtkWidget *user_controls;
};
......@@ -95,24 +96,29 @@ static void
update_header (GisParentalControlsPage *page)
{
g_autofree gchar *title = NULL;
const gchar *subtitle, *icon_name;
const gchar *subtitle;
GdkPaintable *paintable;
gboolean small_screen = FALSE;
g_object_get (G_OBJECT (page), "small-screen", &small_screen, NULL);
/* Translators: The placeholder is the user’s full name. */
title = g_strdup_printf (_("Parental Controls for %s"),
gis_driver_get_full_name (GIS_PAGE (page)->driver));
subtitle = _("Set restrictions on what this user can run or install.");
paintable = gis_driver_get_avatar (GIS_PAGE (page)->driver);
icon_name = (paintable != NULL) ? NULL : "dialog-password-symbolic";
g_object_set (G_OBJECT (page->header),
"title", title,
"subtitle", subtitle,
NULL);
if (paintable != NULL)
g_object_set (G_OBJECT (page->header), "paintable", paintable, NULL);
else if (icon_name != NULL)
g_object_set (G_OBJECT (page->header), "icon-name", icon_name, NULL);
paintable = gis_driver_get_has_default_avatar (GIS_PAGE (page)->driver) ? NULL :
GDK_PAINTABLE (gis_driver_get_avatar (GIS_PAGE (page)->driver));
g_object_set (G_OBJECT (page->avatar),
"visible", !small_screen,
"text", gis_driver_get_full_name (GIS_PAGE (page)->driver),
"custom-image", paintable, NULL);
}
static void
......@@ -167,10 +173,14 @@ gis_parental_controls_page_constructed (GObject *object)
G_CALLBACK (user_details_changed_cb), page);
g_signal_connect (GIS_PAGE (page)->driver, "notify::avatar",
G_CALLBACK (user_details_changed_cb), page);
g_signal_connect (GIS_PAGE (page)->driver, "notify::has-default-avatar",
G_CALLBACK (user_details_changed_cb), page);
g_signal_connect (GIS_PAGE (page)->driver, "notify::user-locale",
G_CALLBACK (user_details_changed_cb), page);
g_signal_connect (GIS_PAGE (page)->driver, "notify::user-display-name",
G_CALLBACK (user_details_changed_cb), page);
g_signal_connect (page, "notify::small-screen",
G_CALLBACK (update_header), NULL);
update_header (page);
......@@ -219,6 +229,7 @@ gis_parental_controls_page_class_init (GisParentalControlsPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GisParentalControlsPage, header);
gtk_widget_class_bind_template_child (widget_class, GisParentalControlsPage, user_controls);
gtk_widget_class_bind_template_child (widget_class, GisParentalControlsPage, avatar);
}
static void
......
......@@ -7,12 +7,18 @@
<child>
<object class="AdwPreferencesGroup">
<property name="margin-top">24</property>
<child>
<object class="AdwAvatar" id="avatar">
<property name="margin_bottom">18</property>
<property name="size">96</property>
<property name="show-initials">True</property>
</object>
</child>
<child>
<object class="GisPageHeader" id="header">
<property name="margin-top">24</property>
<!-- title and subtitle are set in code, so are not set here -->
<property name="icon-name">dialog-password-symbolic</property>
<property name="show-icon" bind-source="GisParentalControlsPage" bind-property="small-screen" bind-flags="invert-boolean|sync-create"/>
<property name="show_icon">False</property>
</object>
</child>
</object>
......
......@@ -46,6 +46,7 @@ struct _GisPasswordPagePrivate
GtkWidget *password_explanation;
GtkWidget *confirm_explanation;
GtkWidget *header;
GtkWidget *avatar;
gboolean valid_confirm;
gboolean valid_password;
......@@ -83,13 +84,14 @@ update_header (GisPasswordPage *page)
g_autofree gchar *title = NULL;
g_autofree gchar *subtitle = NULL;
const gchar *icon_name;
GdkPaintable *paintable;
gboolean small_screen = FALSE;
g_object_get (G_OBJECT (page), "small-screen", &small_screen, NULL);
#ifndef HAVE_PARENTAL_CONTROLS
/* Don’t break UI compatibility if parental controls are disabled. */
title = g_strdup (_("Set a Password"));
subtitle = g_strdup (_("Be careful not to lose your password."));
paintable = NULL;
icon_name = "dialog-password-symbolic";
#else
if (!priv->parent_mode)
......@@ -98,8 +100,7 @@ update_header (GisPasswordPage *page)
title = g_strdup_printf (_("Set a Password for %s"),
gis_driver_get_full_name (GIS_PAGE (page)->driver));
subtitle = g_strdup (_("Be careful not to lose your password."));
paintable = gis_driver_get_avatar (GIS_PAGE (page)->driver);
icon_name = (paintable != NULL) ? NULL : "dialog-password-symbolic";
icon_name = NULL;
}
else
{
......@@ -108,21 +109,30 @@ update_header (GisPasswordPage *page)
subtitle = g_strdup_printf (_("This password will control access to the parental controls for %s."),
gis_driver_get_full_name (GIS_PAGE (page)->driver));
icon_name = "org.freedesktop.MalcontentControl-symbolic";
paintable = NULL;
}
#endif
/* Doesn’t make sense to set both. */
g_assert (icon_name == NULL || paintable == NULL);
g_object_set (G_OBJECT (priv->header),
"title", title,
"subtitle", subtitle,
"show_icon", !small_screen && icon_name != NULL,
NULL);
if (paintable != NULL)
g_object_set (G_OBJECT (priv->header), "paintable", paintable, NULL);
else if (icon_name != NULL)
if (icon_name)
{
g_object_set (G_OBJECT (priv->header), "icon-name", icon_name, NULL);
g_object_set (G_OBJECT (priv->avatar), "visible", FALSE, NULL);
}
else
{
GdkPaintable *paintable = gis_driver_get_has_default_avatar (GIS_PAGE (page)->driver) ? NULL :
GDK_PAINTABLE (gis_driver_get_avatar (GIS_PAGE (page)->driver));
g_object_set (G_OBJECT (priv->avatar),
"visible", !small_screen,
"text", gis_driver_get_full_name (GIS_PAGE (page)->driver),
"custom-image", paintable, NULL);
}
}
static void
......@@ -369,6 +379,11 @@ gis_password_page_constructed (GObject *object)
G_CALLBACK (full_name_or_avatar_changed), page);
g_signal_connect (GIS_PAGE (page)->driver, "notify::avatar",
G_CALLBACK (full_name_or_avatar_changed), page);
g_signal_connect (GIS_PAGE (page)->driver, "notify::has-default-avatar",
G_CALLBACK (full_name_or_avatar_changed), page);
g_signal_connect (page, "notify::small-screen",
G_CALLBACK (update_header), NULL);
validate (page);
update_header (page);
......@@ -448,6 +463,7 @@ gis_password_page_class_init (GisPasswordPageClass *klass)
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, password_explanation);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, confirm_explanation);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, header);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, avatar);
page_class->page_id = PAGE_ID;
page_class->locale_changed = gis_password_page_locale_changed;
......
......@@ -7,12 +7,18 @@
<child>
<object class="AdwPreferencesGroup">
<property name="margin_top">24</property>
<child>
<object class="AdwAvatar" id="avatar">
<property name="margin_bottom">18</property>
<property name="size">96</property>
<property name="show-initials">True</property>
</object>
</child>
<child>
<object class="GisPageHeader" id="header">
<property name="margin_top">24</property>
<!-- title and subtitle are set in code, so are not set here -->
<property name="icon_name">dialog-password-symbolic</property>
<property name="show_icon" bind-source="GisPasswordPage" bind-property="small-screen" bind-flags="invert-boolean|sync-create"/>
</object>
</child>
</object>
......
......@@ -36,8 +36,10 @@
#include "gis-page-header.h"
struct _GisPrivacyPagePrivate
struct _GisPrivacyPage
{
GisPage parent;
GtkWidget *location_switch;
GtkWidget *location_privacy_label;
GtkWidget *reporting_group;
......@@ -47,9 +49,8 @@ struct _GisPrivacyPagePrivate
GSettings *privacy_settings;
guint abrt_watch_id;
};
typedef struct _GisPrivacyPagePrivate GisPrivacyPagePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GisPrivacyPage, gis_privacy_page, GIS_TYPE_PAGE);
G_DEFINE_TYPE (GisPrivacyPage, gis_privacy_page, GIS_TYPE_PAGE);
#ifdef HAVE_WEBKITGTK
static void
......@@ -85,6 +86,8 @@ activate_link (GtkLabel *label,
"titlebar", headerbar,
"title", _("Privacy Policy"),
"modal", TRUE,
"default-width", 800,
"default-height", 600,
NULL);
overlay = gtk_overlay_new ();
......@@ -97,7 +100,6 @@ activate_link (GtkLabel *label,
gtk_overlay_add_overlay (GTK_OVERLAY (overlay), progress_bar);
view = webkit_web_view_new ();
gtk_widget_set_size_request (view, 600, 500);
gtk_widget_set_hexpand (view, TRUE);
gtk_widget_set_vexpand (view, TRUE);
g_signal_connect (view, "notify::estimated-load-progress",
......@@ -110,12 +112,20 @@ activate_link (GtkLabel *label,
return TRUE;
}
#else
static gboolean
activate_link (GtkLabel *label,
const gchar *uri,
GisPrivacyPage *page)
{
/* Fall back to default handler */
return FALSE;
}
#endif
static gboolean
update_os_data (GisPrivacyPage *page)
{
GisPrivacyPagePrivate *priv = gis_privacy_page_get_instance_private (page);
g_autofree char *name = g_get_os_info (G_OS_INFO_KEY_NAME);
g_autofree char *subtitle = NULL;
#ifdef HAVE_WEBKITGTK
......@@ -134,8 +144,8 @@ update_os_data (GisPrivacyPage *page)
subtitle = g_strdup_printf (_("Sends technical reports that do not contain personal information. "
"Data is collected by %1$s (<a href='%2$s'>privacy policy</a>)."),
name, privacy_policy);
gtk_label_set_markup (GTK_LABEL (priv->reporting_label), subtitle);
g_signal_connect (priv->location_privacy_label, "activate-link", G_CALLBACK (activate_link), page);
gtk_label_set_markup (GTK_LABEL (page->reporting_label), subtitle);
return TRUE;
}
#endif
......@@ -145,7 +155,7 @@ update_os_data (GisPrivacyPage *page)
*/
subtitle = g_strdup_printf (_("Sends technical reports that do not contain personal information. "
"Data is collected by %s."), name);
gtk_label_set_label (GTK_LABEL (priv->reporting_label), subtitle);
gtk_label_set_label (GTK_LABEL (page->reporting_label), subtitle);
return TRUE;
}
......@@ -156,9 +166,8 @@ abrt_appeared_cb (GDBusConnection *connection,
gpointer user_data)
{
GisPrivacyPage *page = user_data;
GisPrivacyPagePrivate *priv = gis_privacy_page_get_instance_private (page);
gtk_widget_set_visible (priv->reporting_group, TRUE);
gtk_widget_set_visible (page->reporting_group, TRUE);
}
static void
......@@ -167,39 +176,36 @@ abrt_vanished_cb (GDBusConnection *connection,
gpointer user_data)
{
GisPrivacyPage *page = user_data;
GisPrivacyPagePrivate *priv = gis_privacy_page_get_instance_private (page);
gtk_widget_set_visible (priv->reporting_group, FALSE);
gtk_widget_set_visible (page->reporting_group, FALSE);
}
static void
gis_privacy_page_constructed (GObject *object)
{
GisPrivacyPage *page = GIS_PRIVACY_PAGE (object);
GisPrivacyPagePrivate *priv = gis_privacy_page_get_instance_private (page);
G_OBJECT_CLASS (gis_privacy_page_parent_class)->constructed (object);
gis_page_set_complete (GIS_PAGE (page), TRUE);
priv->location_settings = g_settings_new ("org.gnome.system.location");
priv->privacy_settings = g_settings_new ("org.gnome.desktop.privacy");
page->location_settings = g_settings_new ("org.gnome.system.location");
page->privacy_settings = g_settings_new ("org.gnome.desktop.privacy");
gtk_switch_set_active (GTK_SWITCH (priv->location_switch), TRUE);
gtk_switch_set_active (GTK_SWITCH (priv->reporting_switch), TRUE);
gtk_switch_set_active (GTK_SWITCH (page->location_switch), TRUE);
gtk_switch_set_active (GTK_SWITCH (page->reporting_switch), TRUE);
#ifdef HAVE_WEBKITGTK
gtk_label_set_markup (GTK_LABEL (priv->location_privacy_label),
gtk_label_set_markup (GTK_LABEL (page->location_privacy_label),
_("Allows apps to determine your geographical location. Uses the Mozilla Location Service (<a href='https://location.services.mozilla.com/privacy'>privacy policy</a>)."));
g_signal_connect (priv->location_privacy_label, "activate-link", G_CALLBACK (activate_link), page);
#else
gtk_label_set_label (GTK_LABEL (priv->location_privacy_label),
gtk_label_set_label (GTK_LABEL (page->location_privacy_label),
_("Allows apps to determine your geographical location. Uses the Mozilla Location Service."));
#endif
if (update_os_data (page))
{
priv->abrt_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
page->abrt_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
"org.freedesktop.problems.daemon",
G_BUS_NAME_WATCHER_FLAGS_NONE,
abrt_appeared_cb,
......@@ -213,12 +219,11 @@ static void
gis_privacy_page_dispose (GObject *object)
{
GisPrivacyPage *page = GIS_PRIVACY_PAGE (object);
GisPrivacyPagePrivate *priv = gis_privacy_page_get_instance_private (page);
g_clear_object (&priv->location_settings);
g_clear_object (&priv->privacy_settings);
g_clear_object (&page->location_settings);
g_clear_object (&page->privacy_settings);
g_clear_handle_id (&priv->abrt_watch_id, g_bus_unwatch_name);
g_clear_handle_id (&page->abrt_watch_id, g_bus_unwatch_name);
G_OBJECT_CLASS (gis_privacy_page_parent_class)->dispose (object);
}
......@@ -228,14 +233,13 @@ gis_privacy_page_apply (GisPage *gis_page,
GCancellable *cancellable)
{
GisPrivacyPage *page = GIS_PRIVACY_PAGE (gis_page);
GisPrivacyPagePrivate *priv = gis_privacy_page_get_instance_private (page);
gboolean active;
active = gtk_widget_is_visible (priv->location_switch) && gtk_switch_get_active (GTK_SWITCH (priv->location_switch));
g_settings_set_boolean (priv->location_settings, "enabled", active);
active = gtk_widget_is_visible (page->location_switch) && gtk_switch_get_active (GTK_SWITCH (page->location_switch));
g_settings_set_boolean (page->location_settings, "enabled", active);
active = gtk_widget_is_visible (priv->reporting_switch) && gtk_switch_get_active (GTK_SWITCH (priv->reporting_switch));
g_settings_set_boolean (priv->privacy_settings, "report-technical-problems", active);
active = gtk_widget_is_visible (page->reporting_switch) && gtk_switch_get_active (GTK_SWITCH (page->reporting_switch));
g_settings_set_boolean (page->privacy_settings, "report-technical-problems", active);
return FALSE;
}
......@@ -253,11 +257,12 @@ gis_privacy_page_class_init (GisPrivacyPageClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-privacy-page.ui");
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPrivacyPage, location_switch);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPrivacyPage, location_privacy_label);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPrivacyPage, reporting_group);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPrivacyPage, reporting_label);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPrivacyPage, reporting_switch);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisPrivacyPage, location_switch);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisPrivacyPage, location_privacy_label);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisPrivacyPage, reporting_group);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisPrivacyPage, reporting_label);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisPrivacyPage, reporting_switch);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), activate_link);
page_class->page_id = PAGE_ID;
page_class->locale_changed = gis_privacy_page_locale_changed;
......
......@@ -29,26 +29,7 @@
G_BEGIN_DECLS
#define GIS_TYPE_PRIVACY_PAGE (gis_privacy_page_get_type ())
#define GIS_PRIVACY_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_PRIVACY_PAGE, GisPrivacyPage))
#define GIS_PRIVACY_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_PRIVACY_PAGE, GisPrivacyPageClass))
#define GIS_IS_PRIVACY_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_PRIVACY_PAGE))
#define GIS_IS_PRIVACY_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_PRIVACY_PAGE))
#define GIS_PRIVACY_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_PRIVACY_PAGE, GisPrivacyPageClass))
typedef struct _GisPrivacyPage GisPrivacyPage;
typedef struct _GisPrivacyPageClass GisPrivacyPageClass;
struct _GisPrivacyPage
{
GisPage parent;
};
struct _GisPrivacyPageClass
{
GisPageClass parent_class;
};
GType gis_privacy_page_get_type (void);
G_DECLARE_FINAL_TYPE (GisPrivacyPage, gis_privacy_page, GIS, PRIVACY_PAGE, GisPage);
GisPage *gis_prepare_privacy_page (GisDriver *driver);
......