gnome-control-center/panels/applications/cc-applications-panel.c
Matthias Clasen edc4044f84 applications: Add a 'run in background' switch
This is backed by the 'background' permission that
the flatpak portal uses. It would be slightly nicer
if we had a separate 'unset' state. For now, we just
show 'unset' as 'on'.
2019-12-10 03:09:43 +00:00

1839 lines
57 KiB
C

/* cc-applications-panel.c
*
* Copyright 2018 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* 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 3 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "cc-applications-panel"
#include <config.h>
#include <glib/gi18n.h>
#include <gio/gdesktopappinfo.h>
#include "cc-applications-panel.h"
#include "cc-applications-row.h"
#include "cc-toggle-row.h"
#include "cc-info-row.h"
#include "cc-action-row.h"
#include "cc-applications-resources.h"
#include "cc-util.h"
#include "globs.h"
#include "list-box-helper.h"
#include "search.h"
#include "utils.h"
#define MASTER_SCHEMA "org.gnome.desktop.notifications"
#define APP_SCHEMA MASTER_SCHEMA ".application"
#define APP_PREFIX "/org/gnome/desktop/notifications/application/"
struct _CcApplicationsPanel
{
CcPanel parent;
GtkWidget *sidebar_box;
GtkListBox *sidebar_listbox;
GtkEntry *sidebar_search_entry;
GtkWidget *header_button;
GtkWidget *title_label;
GAppInfoMonitor *monitor;
gulong monitor_id;
GCancellable *cancellable;
gchar *current_app_id;
gchar *current_flatpak_id;
GHashTable *globs;
GHashTable *search_providers;
GDBusProxy *perm_store;
GSettings *notification_settings;
GSettings *location_settings;
GSettings *privacy_settings;
GSettings *search_settings;
GtkListBox *stack;
GtkWidget *permission_section;
GtkWidget *permission_list;
GtkWidget *camera;
GtkWidget *no_camera;
GtkWidget *location;
GtkWidget *no_location;
GtkWidget *microphone;
GtkWidget *no_microphone;
GtkWidget *builtin;
GtkWidget *builtin_dialog;
GtkWidget *builtin_label;
GtkWidget *builtin_list;
GtkWidget *integration_section;
GtkWidget *integration_list;
GtkWidget *notification;
GtkWidget *background;
GtkWidget *sound;
GtkWidget *no_sound;
GtkWidget *search;
GtkWidget *no_search;
GtkWidget *handler_section;
GtkWidget *handler_reset;
GtkWidget *handler_list;
GtkWidget *hypertext;
GtkWidget *text;
GtkWidget *images;
GtkWidget *fonts;
GtkWidget *archives;
GtkWidget *packages;
GtkWidget *audio;
GtkWidget *video;
GtkWidget *other;
GtkWidget *link;
GtkWidget *usage_section;
GtkWidget *usage_list;
GtkWidget *storage;
GtkWidget *storage_dialog;
GtkWidget *storage_list;
GtkWidget *app;
GtkWidget *data;
GtkWidget *cache;
GtkWidget *total;
GtkWidget *clear_cache_button;
guint64 app_size;
guint64 cache_size;
guint64 data_size;
};
static void select_app (CcApplicationsPanel *self,
const gchar *app_id);
G_DEFINE_TYPE (CcApplicationsPanel, cc_applications_panel, CC_TYPE_PANEL)
enum
{
PROP_0,
PROP_PARAMETERS
};
/* Callbacks */
static gboolean
privacy_link_cb (CcApplicationsPanel *self)
{
CcShell *shell = cc_panel_get_shell (CC_PANEL (self));
g_autoptr(GError) error = NULL;
if (!cc_shell_set_active_panel_from_id (shell, "location", NULL, &error))
g_warning ("Failed to switch to privacy panel: %s", error->message);
return TRUE;
}
static void
open_software_cb (GtkButton *button,
CcApplicationsPanel *self)
{
const gchar *argv[] = { "gnome-software", "--details", "appid", NULL };
if (self->current_app_id == NULL)
argv[1] = NULL;
else
argv[2] = self->current_app_id;
g_spawn_async (NULL, (char **)argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
}
/* --- portal permissions and utilities --- */
static gchar **
get_portal_permissions (CcApplicationsPanel *self,
const gchar *table,
const gchar *id,
const gchar *app_id)
{
g_autoptr(GVariant) ret = NULL;
g_autoptr(GVariantIter) iter = NULL;
const gchar *key = NULL;
GStrv val;
GStrv result = NULL;
ret = g_dbus_proxy_call_sync (self->perm_store,
"Lookup",
g_variant_new ("(ss)", table, id),
0, G_MAXINT, NULL, NULL);
if (ret == NULL)
return NULL;
g_variant_get (ret, "(a{sas}v)", &iter, NULL);
while (g_variant_iter_loop (iter, "{&s^a&s}", &key, &val))
{
if (strcmp (key, app_id) == 0 && result == NULL)
result = g_strdupv (val);
}
return result;
}
static void
set_portal_permissions (CcApplicationsPanel *self,
const gchar *table,
const gchar *id,
const gchar *app_id,
const gchar * const *permissions)
{
g_autoptr(GError) error = NULL;
g_dbus_proxy_call_sync (self->perm_store,
"SetPermission",
g_variant_new ("(sbss^as)", table, TRUE, id, app_id, permissions),
0,
G_MAXINT,
NULL,
&error);
if (error)
g_warning ("Error setting portal permissions: %s", error->message);
}
static char *
get_flatpak_id (GAppInfo *info)
{
if (G_IS_DESKTOP_APP_INFO (info))
return g_desktop_app_info_get_string (G_DESKTOP_APP_INFO (info), "X-Flatpak");
return NULL;
}
static GFile *
get_flatpak_app_dir (const gchar *app_id,
const gchar *subdir)
{
g_autofree gchar *path = NULL;
g_autoptr(GFile) appdir = NULL;
path = g_build_filename (g_get_home_dir (), ".var", "app", app_id, NULL);
appdir = g_file_new_for_path (path);
return g_file_get_child (appdir, subdir);
}
/* --- search settings --- */
static void
set_search_enabled (CcApplicationsPanel *self,
const gchar *app_id,
gboolean enabled)
{
g_autoptr(GPtrArray) new_apps = NULL;
g_autofree gchar *desktop_id = NULL;
g_auto(GStrv) apps = NULL;
gpointer key, value;
gboolean default_disabled;
gint i;
desktop_id = g_strconcat (app_id, ".desktop", NULL);
if (!g_hash_table_lookup_extended (self->search_providers, app_id, &key, &value))
{
g_warning ("Trying to configure search for a provider-less app - this shouldn't happen");
return;
}
default_disabled = GPOINTER_TO_INT (value);
new_apps = g_ptr_array_new_with_free_func (g_free);
if (default_disabled)
{
apps = g_settings_get_strv (self->search_settings, "enabled");
for (i = 0; apps[i]; i++)
{
if (strcmp (apps[i], desktop_id) != 0)
g_ptr_array_add (new_apps, g_strdup (apps[i]));
}
if (enabled)
g_ptr_array_add (new_apps, g_strdup (desktop_id));
g_ptr_array_add (new_apps, NULL);
g_settings_set_strv (self->search_settings, "enabled", (const gchar * const *)new_apps->pdata);
}
else
{
apps = g_settings_get_strv (self->search_settings, "disabled");
for (i = 0; apps[i]; i++)
{
if (strcmp (apps[i], desktop_id) != 0)
g_ptr_array_add (new_apps, g_strdup (apps[i]));
}
if (!enabled)
g_ptr_array_add (new_apps, g_strdup (desktop_id));
g_ptr_array_add (new_apps, NULL);
g_settings_set_strv (self->search_settings, "disabled", (const gchar * const *)new_apps->pdata);
}
}
static gboolean
search_contains_string_for_app (CcApplicationsPanel *self,
const gchar *app_id,
const gchar *setting)
{
g_autofree gchar *desktop_id = NULL;
g_auto(GStrv) apps = NULL;
desktop_id = g_strconcat (app_id, ".desktop", NULL);
apps = g_settings_get_strv (self->search_settings, setting);
return g_strv_contains ((const gchar * const *)apps, desktop_id);
}
static gboolean
search_enabled_for_app (CcApplicationsPanel *self,
const gchar *app_id)
{
return search_contains_string_for_app (self, app_id, "enabled");
}
static gboolean
search_disabled_for_app (CcApplicationsPanel *self,
const gchar *app_id)
{
return search_contains_string_for_app (self, app_id, "disabled");
}
static void
get_search_enabled (CcApplicationsPanel *self,
const gchar *app_id,
gboolean *set,
gboolean *enabled)
{
gpointer key, value;
*enabled = FALSE;
*set = g_hash_table_lookup_extended (self->search_providers, app_id, &key, &value);
if (!*set)
return;
if (search_enabled_for_app (self, app_id))
*enabled = TRUE;
else if (search_disabled_for_app (self, app_id))
*enabled = FALSE;
else
*enabled = !GPOINTER_TO_INT (value);
}
static void
search_cb (CcApplicationsPanel *self)
{
if (self->current_app_id)
set_search_enabled (self,
self->current_app_id,
cc_toggle_row_get_allowed (CC_TOGGLE_ROW (self->search)));
}
/* --- notification permissions (flatpaks and non-flatpak) --- */
static void
get_notification_allowed (CcApplicationsPanel *self,
const gchar *app_id,
gboolean *set,
gboolean *allowed)
{
if (self->notification_settings)
{
/* FIXME */
*set = TRUE;
*allowed = g_settings_get_boolean (self->notification_settings, "enable");
}
else
{
g_auto(GStrv) perms = get_portal_permissions (self, "notifications", "notification", app_id);
*set = perms != NULL;
/* FIXME: needs unreleased xdg-desktop-portals to write permissions on use */
*set = TRUE;
*allowed = perms == NULL || strcmp (perms[0], "no") != 0;
}
}
static void
set_notification_allowed (CcApplicationsPanel *self,
gboolean allowed)
{
if (self->notification_settings)
{
g_settings_set_boolean (self->notification_settings, "enable", allowed);
}
else
{
const gchar *perms[2] = { NULL, NULL };
perms[0] = allowed ? "yes" : "no";
set_portal_permissions (self, "notifications", "notification", self->current_flatpak_id, perms);
}
}
static void
notification_cb (CcApplicationsPanel *self)
{
if (self->current_app_id)
set_notification_allowed (self, cc_toggle_row_get_allowed (CC_TOGGLE_ROW (self->notification)));
}
static gchar *
munge_app_id (const gchar *app_id)
{
gchar *id = g_strdup (app_id);
gint i;
g_strcanon (id,
"0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"-",
'-');
for (i = 0; id[i] != '\0'; i++)
id[i] = g_ascii_tolower (id[i]);
return id;
}
static GSettings *
get_notification_settings (const gchar *app_id)
{
g_autofree gchar *munged_app_id = munge_app_id (app_id);
g_autofree gchar *path = g_strconcat (APP_PREFIX, munged_app_id, "/", NULL);
return g_settings_new_with_path (APP_SCHEMA, path);
}
/* --- background --- */
static void
get_background_allowed (CcApplicationsPanel *self,
const gchar *app_id,
gboolean *set,
gboolean *allowed)
{
g_auto(GStrv) perms = get_portal_permissions (self, "background", "background", app_id);
*set = TRUE;
*allowed = perms == NULL || strcmp (perms[0], "no") != 0;
}
static void
set_background_allowed (CcApplicationsPanel *self,
gboolean allowed)
{
const gchar *perms[2] = { NULL, NULL };
perms[0] = allowed ? "yes" : "no";
set_portal_permissions (self, "background", "background", self->current_flatpak_id, perms);
}
static void
background_cb (CcApplicationsPanel *self)
{
if (self->current_app_id)
set_background_allowed (self, cc_toggle_row_get_allowed (CC_TOGGLE_ROW (self->background)));
}
/* --- device (microphone, camera, speaker) permissions (flatpak) --- */
static void
get_device_allowed (CcApplicationsPanel *self,
const gchar *device,
const gchar *app_id,
gboolean *set,
gboolean *allowed)
{
g_auto(GStrv) perms = NULL;
perms = get_portal_permissions (self, "devices", device, app_id);
*set = perms != NULL;
*allowed = perms == NULL || strcmp (perms[0], "no") != 0;
}
static void
set_device_allowed (CcApplicationsPanel *self,
const gchar *device,
gboolean allowed)
{
const gchar *perms[2];
perms[0] = allowed ? "yes" : "no";
perms[1] = NULL;
set_portal_permissions (self, "devices", device, self->current_flatpak_id, perms);
}
static void
microphone_cb (CcApplicationsPanel *self)
{
if (self->current_flatpak_id)
set_device_allowed (self, "microphone", cc_toggle_row_get_allowed (CC_TOGGLE_ROW (self->microphone)));
}
static void
sound_cb (CcApplicationsPanel *self)
{
if (self->current_flatpak_id)
set_device_allowed (self, "speakers", cc_toggle_row_get_allowed (CC_TOGGLE_ROW (self->sound)));
}
static void
camera_cb (CcApplicationsPanel *self)
{
if (self->current_flatpak_id)
set_device_allowed (self, "camera", cc_toggle_row_get_allowed (CC_TOGGLE_ROW (self->camera)));
}
/* --- location permissions (flatpak) --- */
static void
get_location_allowed (CcApplicationsPanel *self,
const gchar *app_id,
gboolean *set,
gboolean *allowed)
{
g_auto(GStrv) perms = NULL;
perms = get_portal_permissions (self, "location", "location", app_id);
*set = perms != NULL;
*allowed = perms == NULL || strcmp (perms[0], "NONE") != 0;
}
static void
set_location_allowed (CcApplicationsPanel *self,
gboolean allowed)
{
const gchar *perms[3];
/* FIXME allow setting accuracy */
perms[0] = allowed ? "EXACT" : "NONE";
perms[1] = "0";
perms[2] = NULL;
set_portal_permissions (self, "location", "location", self->current_flatpak_id, perms);
}
static void
location_cb (CcApplicationsPanel *self)
{
if (self->current_flatpak_id)
set_location_allowed (self, cc_toggle_row_get_allowed (CC_TOGGLE_ROW (self->location)));
}
/* --- permissions section --- */
static gint
add_static_permission_row (CcApplicationsPanel *self,
const gchar *title,
const gchar *subtitle)
{
GtkWidget *row;
row = g_object_new (CC_TYPE_INFO_ROW,
"title", title,
"info", subtitle,
NULL);
gtk_container_add (GTK_CONTAINER (self->builtin_list), row);
return 1;
}
static void
permission_row_activated_cb (GtkListBox *list,
GtkListBoxRow *list_row,
CcApplicationsPanel *self)
{
GtkWidget *row = GTK_WIDGET (list_row);
if (row == self->builtin)
{
gtk_window_set_transient_for (GTK_WINDOW (self->builtin_dialog),
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));
gtk_window_present (GTK_WINDOW (self->builtin_dialog));
}
}
static gboolean
add_static_permissions (CcApplicationsPanel *self,
GAppInfo *info,
const gchar *app_id)
{
g_autoptr(GKeyFile) keyfile = NULL;
g_auto(GStrv) sockets = NULL;
g_auto(GStrv) devices = NULL;
g_auto(GStrv) shared = NULL;
g_auto(GStrv) filesystems = NULL;
g_autofree gchar *str = NULL;
gint added = 0;
g_autofree gchar *text = NULL;
keyfile = get_flatpak_metadata (app_id);
if (keyfile == NULL)
return FALSE;
sockets = g_key_file_get_string_list (keyfile, "Context", "sockets", NULL, NULL);
if (sockets && g_strv_contains ((const gchar * const*)sockets, "system-bus"))
added += add_static_permission_row (self, _("System Bus"), _("Full access"));
if (sockets && g_strv_contains ((const gchar * const*)sockets, "session-bus"))
added += add_static_permission_row (self, _("Session Bus"), _("Full access"));
devices = g_key_file_get_string_list (keyfile, "Context", "devices", NULL, NULL);
if (devices && g_strv_contains ((const gchar * const*)devices, "all"))
added += add_static_permission_row (self, _("Devices"), _("Full access to /dev"));
shared = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL);
if (shared && g_strv_contains ((const gchar * const*)shared, "network"))
added += add_static_permission_row (self, _("Network"), _("Has network access"));
filesystems = g_key_file_get_string_list (keyfile, "Context", "filesystems", NULL, NULL);
if (filesystems && (g_strv_contains ((const gchar * const *)filesystems, "home") ||
g_strv_contains ((const gchar * const *)filesystems, "home:rw")))
added += add_static_permission_row (self, _("Home"), _("Full access"));
else if (filesystems && g_strv_contains ((const gchar * const *)filesystems, "home:ro"))
added += add_static_permission_row (self, _("Home"), _("Read-only"));
if (filesystems && (g_strv_contains ((const gchar * const *)filesystems, "host") ||
g_strv_contains ((const gchar * const *)filesystems, "host:rw")))
added += add_static_permission_row (self, _("File System"), _("Full access"));
else if (filesystems && g_strv_contains ((const gchar * const *)filesystems, "host:ro"))
added += add_static_permission_row (self, _("File System"), _("Read-only"));
str = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL);
if (str && g_str_equal (str, "talk"))
added += add_static_permission_row (self, _("Settings"), _("Can change settings"));
gtk_widget_set_visible (self->builtin, added > 0);
text = g_strdup_printf (_("%s has the following permissions built-in. These cannot be altered. If you are concerned about these permissions, consider removing this application."), g_app_info_get_display_name (info));
gtk_label_set_label (GTK_LABEL (self->builtin_label), text);
return added > 0;
}
static void
remove_static_permissions (CcApplicationsPanel *self)
{
container_remove_all (GTK_CONTAINER (self->builtin_list));
}
static void
update_permission_section (CcApplicationsPanel *self,
GAppInfo *info)
{
g_autofree gchar *flatpak_id = get_flatpak_id (info);
gboolean disabled, allowed, set;
gboolean has_any = FALSE;
if (flatpak_id == NULL)
{
gtk_widget_hide (self->permission_section);
return;
}
disabled = g_settings_get_boolean (self->privacy_settings, "disable-camera");
get_device_allowed (self, "camera", flatpak_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->camera), allowed);
gtk_widget_set_visible (self->camera, set && !disabled);
gtk_widget_set_visible (self->no_camera, set && disabled);
has_any |= set;
disabled = g_settings_get_boolean (self->privacy_settings, "disable-microphone");
get_device_allowed (self, "microphone", flatpak_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->microphone), allowed);
gtk_widget_set_visible (self->microphone, set && !disabled);
gtk_widget_set_visible (self->no_microphone, set && disabled);
has_any |= set;
disabled = !g_settings_get_boolean (self->location_settings, "enabled");
get_location_allowed (self, flatpak_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->location), allowed);
gtk_widget_set_visible (self->location, set && !disabled);
gtk_widget_set_visible (self->no_location, set && disabled);
has_any |= set;
remove_static_permissions (self);
has_any |= add_static_permissions (self, info, flatpak_id);
gtk_widget_set_visible (self->permission_section, has_any);
}
/* --- gintegration section --- */
static void
update_integration_section (CcApplicationsPanel *self,
GAppInfo *info)
{
g_autofree gchar *app_id = get_app_id (info);
g_autofree gchar *flatpak_id = get_flatpak_id (info);
gboolean set, allowed, disabled;
gboolean has_any = FALSE;
disabled = g_settings_get_boolean (self->search_settings, "disable-external");
get_search_enabled (self, app_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->search), allowed);
gtk_widget_set_visible (self->search, set && !disabled);
gtk_widget_set_visible (self->no_search, set && disabled);
if (flatpak_id != NULL)
{
g_clear_object (&self->notification_settings);
get_notification_allowed (self, flatpak_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->notification), allowed);
gtk_widget_set_visible (self->notification, set);
has_any |= set;
get_background_allowed (self, flatpak_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->background), allowed);
gtk_widget_set_visible (self->background, set);
has_any |= set;
disabled = g_settings_get_boolean (self->privacy_settings, "disable-sound-output");
get_device_allowed (self, "speakers", flatpak_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->sound), allowed);
gtk_widget_set_visible (self->sound, set && !disabled);
gtk_widget_set_visible (self->no_sound, set && disabled);
}
else
{
g_set_object (&self->notification_settings, get_notification_settings (app_id));
get_notification_allowed (self, app_id, &set, &allowed);
cc_toggle_row_set_allowed (CC_TOGGLE_ROW (self->notification), allowed);
gtk_widget_set_visible (self->notification, set);
has_any |= set;
gtk_widget_hide (self->background);
gtk_widget_hide (self->sound);
gtk_widget_hide (self->no_sound);
}
gtk_widget_set_visible (self->integration_section, has_any);
}
/* --- handler section --- */
static void
unset_cb (CcActionRow *row,
CcApplicationsPanel *self)
{
const gchar *type;
GtkListBoxRow *selected;
GAppInfo *info;
selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (self->sidebar_listbox));
info = cc_applications_row_get_info (CC_APPLICATIONS_ROW (selected));
type = (const gchar *)g_object_get_data (G_OBJECT (row), "type");
g_app_info_remove_supports_type (info, type, NULL);
}
static void
update_group_row_count (GtkWidget *row,
gint delta)
{
gint count;
g_autofree gchar *text = NULL;
count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "count"));
count += delta;
g_object_set_data (G_OBJECT (row), "count", GINT_TO_POINTER (count));
text = g_strdup_printf ("%d", count);
g_object_set (row, "info", text, NULL);
}
static void
add_scheme (CcApplicationsPanel *self,
GtkWidget *after,
const gchar *type)
{
CcActionRow *row = NULL;
gint pos;
if (g_str_has_suffix (type, "http"))
{
row = cc_action_row_new ();
cc_action_row_set_title (row, _("Web Links"));
cc_action_row_set_subtitle (row, "http://, https://");
}
else if (g_str_has_suffix (type, "https"))
{
return; /* assume anything that handles https also handles http */
}
else if (g_str_has_suffix (type, "git"))
{
row = cc_action_row_new ();
cc_action_row_set_title (row, _("Git Links"));
cc_action_row_set_subtitle (row, "git://");
}
else
{
gchar *scheme = strrchr (type, '/') + 1;
g_autofree gchar *title = g_strdup_printf (_("%s Links"), scheme);
g_autofree gchar *subtitle = g_strdup_printf ("%s://", scheme);
row = cc_action_row_new ();
cc_action_row_set_title (row, title);
cc_action_row_set_subtitle (row, subtitle);
}
cc_action_row_set_action (row, _("Unset"), TRUE);
g_object_set_data_full (G_OBJECT (row), "type", g_strdup (type), g_free);
g_signal_connect_object (row,
"activated",
G_CALLBACK (unset_cb),
self, 0);
if (after)
{
pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (after)) + 1;
g_object_bind_property (after, "expanded",
row, "visible",
G_BINDING_SYNC_CREATE);
}
else
pos = -1;
gtk_list_box_insert (GTK_LIST_BOX (self->handler_list), GTK_WIDGET (row), pos);
update_group_row_count (after, 1);
}
static void
add_file_type (CcApplicationsPanel *self,
GtkWidget *after,
const gchar *type)
{
CcActionRow *row;
g_autofree gchar *desc = NULL;
gint pos;
const gchar *glob;
glob = g_hash_table_lookup (self->globs, type);
desc = g_content_type_get_description (type);
row = cc_action_row_new ();
cc_action_row_set_title (row, desc);
cc_action_row_set_subtitle (row, glob ? glob : "");
cc_action_row_set_action (row, _("Unset"), TRUE);
g_object_set_data_full (G_OBJECT (row), "type", g_strdup (type), g_free);
g_signal_connect (row, "activated", G_CALLBACK (unset_cb), self);
if (after)
{
pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (after)) + 1;
g_object_bind_property (after, "expanded",
row, "visible",
G_BINDING_SYNC_CREATE);
}
else
{
pos = -1;
}
gtk_list_box_insert (GTK_LIST_BOX (self->handler_list), GTK_WIDGET (row), pos);
update_group_row_count (after, 1);
}
static gboolean
is_hypertext_type (const gchar *type)
{
const gchar *types[] = {
"text/html",
"text/htmlh",
"text/xml",
"application/xhtml+xml",
"application/vnd.mozilla.xul+xml",
"text/mml",
NULL
};
return g_strv_contains (types, type);
}
static void
ensure_group_row (CcApplicationsPanel *self,
GtkWidget **row,
const gchar *title)
{
if (*row == NULL)
{
CcInfoRow *r = CC_INFO_ROW (g_object_new (CC_TYPE_INFO_ROW,
"title", title,
"has-expander", TRUE,
NULL));
gtk_list_box_insert (GTK_LIST_BOX (self->handler_list), GTK_WIDGET (r), -1);
*row = GTK_WIDGET (r);
}
}
static void
add_link_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->link, _("Links"));
add_scheme (self, self->link, type);
}
static void
add_hypertext_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->hypertext, _("Hypertext Files"));
add_file_type (self, self->hypertext, type);
}
static gboolean
is_text_type (const gchar *type)
{
return g_content_type_is_a (type, "text/*");
}
static void
add_text_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->text, _("Text Files"));
add_file_type (self, self->text, type);
}
static gboolean
is_image_type (const gchar *type)
{
return g_content_type_is_a (type, "image/*");
}
static void
add_image_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->images, _("Image Files"));
add_file_type (self, self->images, type);
}
static gboolean
is_font_type (const gchar *type)
{
return g_content_type_is_a (type, "font/*") ||
g_str_equal (type, "application/x-font-pcf") ||
g_str_equal (type, "application/x-font-type1");
}
static void
add_font_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->fonts, _("Font Files"));
add_file_type (self, self->fonts, type);
}
static gboolean
is_archive_type (const gchar *type)
{
const gchar *types[] = {
"application/bzip2",
"application/zip",
"application/x-xz-compressed-tar",
"application/x-xz",
"application/x-xar",
"application/x-tarz",
"application/x-tar",
"application/x-lzma-compressed-tar",
"application/x-lzma",
"application/x-lzip-compressed-tar",
"application/x-lzip",
"application/x-lha",
"application/gzip",
"application/x-cpio",
"application/x-compressed-tar",
"application/x-compress",
"application/x-bzip-compressed-tar",
"application/x-bzip",
"application/x-7z-compressed-tar",
"application/x-7z-compressed",
"application/x-zoo",
"application/x-war",
"application/x-stuffit",
"application/x-rzip-compressed-tar",
"application/x-rzip",
"application/vnd.rar",
"application/x-lzop-compressed-tar",
"application/x-lzop",
"application/x-lz4-compressed-tar",
"application/x-lz4",
"application/x-lrzip-compressed-tar",
"application/x-lrzip",
"application/x-lhz",
"application/x-java-archive",
"application/x-ear",
"application/x-cabinet",
"application/x-bzip1-compressed-tar",
"application/x-bzip1",
"application/x-arj",
"application/x-archive",
"application/x-ar",
"application/x-alz",
"application/x-ace",
"application/vnd.ms-cab-compressed",
NULL
};
return g_strv_contains (types, type);
}
static void
add_archive_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->archives, _("Archive Files"));
add_file_type (self, self->archives, type);
}
static gboolean
is_package_type (const gchar *type)
{
const gchar *types[] = {
"application/x-source-rpm",
"application/x-rpm",
"application/vnd.debian.binary-package",
NULL
};
return g_strv_contains (types, type);
}
static void
add_package_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->packages, _("Package Files"));
add_file_type (self, self->packages, type);
}
static gboolean
is_audio_type (const gchar *type)
{
return g_content_type_is_a (type, "audio/*") ||
g_str_equal (type, "application/ogg") ||
g_str_equal (type, "application/x-shorten") ||
g_str_equal (type, "application/x-matroska") ||
g_str_equal (type, "application/x-flac") ||
g_str_equal (type, "application/x-extension-mp4") ||
g_str_equal (type, "application/x-extension-m4a") ||
g_str_equal (type, "application/vnd.rn-realmedia") ||
g_str_equal (type, "application/ram") ||
g_str_equal (type, "application/vnd.ms-wpl");
}
static void
add_audio_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->audio, _("Audio Files"));
add_file_type (self, self->audio, type);
}
static gboolean
is_video_type (const gchar *type)
{
return g_content_type_is_a (type, "video/*") ||
g_str_equal (type, "application/x-smil") ||
g_str_equal (type, "application/vnd.ms-asf") ||
g_str_equal (type, "application/mxf");
}
static void
add_video_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->video, _("Video Files"));
add_file_type (self, self->video, type);
}
static void
add_other_type (CcApplicationsPanel *self,
const gchar *type)
{
ensure_group_row (self, &self->other, _("Other Files"));
add_file_type (self, self->other, type);
}
static void
add_handler_row (CcApplicationsPanel *self,
const gchar *type)
{
gtk_widget_show (self->handler_section);
if (g_content_type_is_a (type, "x-scheme-handler/*"))
add_link_type (self, type);
else if (is_hypertext_type (type))
add_hypertext_type (self, type);
else if (is_font_type (type))
add_font_type (self, type);
else if (is_package_type (type))
add_package_type (self, type);
else if (is_audio_type (type))
add_audio_type (self, type);
else if (is_video_type (type))
add_video_type (self, type);
else if (is_archive_type (type))
add_archive_type (self, type);
else if (is_text_type (type))
add_text_type (self, type);
else if (is_image_type (type))
add_image_type (self, type);
else
add_other_type (self, type);
}
static void
handler_row_activated_cb (GtkListBox *list,
GtkListBoxRow *list_row,
CcApplicationsPanel *self)
{
GtkWidget *row = GTK_WIDGET (list_row);
if (row == self->hypertext ||
row == self->text ||
row == self->images ||
row == self->fonts ||
row == self->archives ||
row == self->packages ||
row == self->audio ||
row == self->video ||
row == self->other ||
row == self->link)
{
cc_info_row_set_expanded (CC_INFO_ROW (row),
!cc_info_row_get_expanded (CC_INFO_ROW (row)));
}
}
static gboolean
app_info_recommended_for (GAppInfo *info,
const gchar *type)
{
/* this is horribly inefficient. I blame the mime system */
g_autolist(GObject) list = NULL;
GList *l;
gboolean ret = FALSE;
list = g_app_info_get_recommended_for_type (type);
for (l = list; l; l = l->next)
{
GAppInfo *ri = l->data;
if (g_app_info_equal (info, ri))
{
ret = TRUE;
break;
}
}
return ret;
}
static void
handler_reset_cb (GtkButton *button,
CcApplicationsPanel *self)
{
GtkListBoxRow *selected;
GAppInfo *info;
const gchar **types;
gint i;
selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (self->sidebar_listbox));
info = cc_applications_row_get_info (CC_APPLICATIONS_ROW (selected));
types = g_app_info_get_supported_types (info);
if (types == NULL || types[0] == NULL)
return;
g_signal_handler_block (self->monitor, self->monitor_id);
for (i = 0; types[i]; i++)
{
gchar *ctype = g_content_type_from_mime_type (types[i]);
g_app_info_add_supports_type (info, ctype, NULL);
}
g_signal_handler_unblock (self->monitor, self->monitor_id);
g_signal_emit_by_name (self->monitor, "changed");
}
static void
update_handler_sections (CcApplicationsPanel *self,
GAppInfo *info)
{
g_autoptr(GHashTable) hash = NULL;
const gchar **types;
gint i;
container_remove_all (GTK_CONTAINER (self->handler_list));
self->hypertext = NULL;
self->text = NULL;
self->images = NULL;
self->fonts = NULL;
self->archives = NULL;
self->packages = NULL;
self->audio = NULL;
self->video = NULL;
self->other = NULL;
self->link = NULL;
gtk_widget_hide (self->handler_section);
types = g_app_info_get_supported_types (info);
if (types == NULL || types[0] == NULL)
return;
hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
gtk_widget_set_sensitive (self->handler_reset, FALSE);
for (i = 0; types[i]; i++)
{
g_autofree gchar *ctype = g_content_type_from_mime_type (types[i]);
if (g_hash_table_contains (hash, ctype))
continue;
if (!app_info_recommended_for (info, ctype))
{
gtk_widget_set_sensitive (self->handler_reset, TRUE);
continue;
}
add_handler_row (self, ctype);
g_hash_table_add (hash, g_steal_pointer (&ctype));
}
}
/* --- usage section --- */
static void
storage_row_activated_cb (GtkListBox *list,
GtkListBoxRow *list_row,
CcApplicationsPanel *self)
{
GtkWidget *row = GTK_WIDGET (list_row);
if (row == self->storage)
{
gtk_window_set_transient_for (GTK_WINDOW (self->storage_dialog),
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));
gtk_window_present (GTK_WINDOW (self->storage_dialog));
}
}
static void
update_total_size (CcApplicationsPanel *self)
{
g_autofree gchar *formatted_size = NULL;
guint64 total;
total = self->app_size + self->data_size + self->cache_size;
formatted_size = g_format_size (total);
g_object_set (self->total, "info", formatted_size, NULL);
g_object_set (self->storage, "info", formatted_size, NULL);
}
static void
set_cache_size (GObject *source,
GAsyncResult *res,
gpointer data)
{
CcApplicationsPanel *self = data;
g_autofree gchar *formatted_size = NULL;
guint64 size;
g_autoptr(GError) error = NULL;
if (!file_size_finish (G_FILE (source), res, &size, &error))
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to get flatpak cache size: %s", error->message);
return;
}
self->cache_size = size;
formatted_size = g_format_size (self->cache_size);
g_object_set (self->cache, "info", formatted_size, NULL);
gtk_widget_set_sensitive (self->clear_cache_button, self->cache_size > 0);
update_total_size (self);
}
static void
update_cache_row (CcApplicationsPanel *self,
const gchar *app_id)
{
g_autoptr(GFile) dir = get_flatpak_app_dir (app_id, "cache");
g_object_set (self->cache, "info", "...", NULL);
file_size_async (dir, self->cancellable, set_cache_size, self);
}
static void
set_data_size (GObject *source,
GAsyncResult *res,
gpointer data)
{
CcApplicationsPanel *self = data;
g_autofree gchar *formatted_size = NULL;
guint64 size;
g_autoptr(GError) error = NULL;
if (!file_size_finish (G_FILE (source), res, &size, &error))
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to get flatpak data size: %s", error->message);
return;
}
self->data_size = size;
formatted_size = g_format_size (self->data_size);
g_object_set (self->data, "info", formatted_size, NULL);
update_total_size (self);
}
static void
update_data_row (CcApplicationsPanel *self,
const gchar *app_id)
{
g_autoptr(GFile) dir = get_flatpak_app_dir (app_id, "data");
g_object_set (self->data, "info", "...", NULL);
file_size_async (dir, self->cancellable, set_data_size, self);
}
static void
cache_cleared (GObject *source,
GAsyncResult *res,
gpointer data)
{
CcApplicationsPanel *self = data;
g_autoptr(GError) error = NULL;
if (!file_remove_finish (G_FILE (source), res, &error))
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to remove cache: %s", error->message);
return;
}
update_cache_row (self, self->current_app_id);
}
static void
clear_cache_cb (CcApplicationsPanel *self)
{
g_autoptr(GFile) dir = NULL;
if (self->current_app_id == NULL)
return;
dir = get_flatpak_app_dir (self->current_app_id, "cache");
file_remove_async (dir, self->cancellable, cache_cleared, self);
}
static void
update_app_row (CcApplicationsPanel *self,
const gchar *app_id)
{
g_autofree gchar *formatted_size = NULL;
self->app_size = get_flatpak_app_size (app_id);
formatted_size = g_format_size (self->app_size);
g_object_set (self->app, "info", formatted_size, NULL);
update_total_size (self);
}
static void
update_flatpak_sizes (CcApplicationsPanel *self,
const gchar *app_id)
{
gtk_widget_set_sensitive (self->clear_cache_button, FALSE);
self->app_size = self->data_size = self->cache_size = 0;
update_app_row (self, app_id);
update_cache_row (self, app_id);
update_data_row (self, app_id);
}
static void
update_usage_section (CcApplicationsPanel *self,
GAppInfo *info)
{
g_autofree gchar *flatpak_id = get_flatpak_id (info);
if (flatpak_id != NULL)
{
gtk_widget_show (self->usage_section);
update_flatpak_sizes (self, flatpak_id);
}
else
{
gtk_widget_hide (self->usage_section);
}
}
/* --- panel setup --- */
static void
update_panel (CcApplicationsPanel *self,
GtkListBoxRow *row)
{
GAppInfo *info;
if (self->perm_store == NULL)
{
g_message ("No permissions store proxy yet, come back later");
return;
}
if (row == NULL)
{
gtk_label_set_label (GTK_LABEL (self->title_label), _("Applications"));
gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "empty");
gtk_widget_hide (self->header_button);
return;
}
info = cc_applications_row_get_info (CC_APPLICATIONS_ROW (row));
gtk_label_set_label (GTK_LABEL (self->title_label), g_app_info_get_display_name (info));
gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "settings");
gtk_widget_show (self->header_button);
g_clear_pointer (&self->current_app_id, g_free);
g_clear_pointer (&self->current_flatpak_id, g_free);
update_permission_section (self, info);
update_integration_section (self, info);
update_handler_sections (self, info);
update_usage_section (self, info);
self->current_app_id = get_app_id (info);
self->current_flatpak_id = get_flatpak_id (info);
}
static void
populate_applications (CcApplicationsPanel *self)
{
g_autolist(GObject) infos = NULL;
GList *l;
container_remove_all (GTK_CONTAINER (self->sidebar_listbox));
infos = g_app_info_get_all ();
for (l = infos; l; l = l->next)
{
GAppInfo *info = l->data;
GtkWidget *row;
g_autofree gchar *id = NULL;
if (!g_app_info_should_show (info))
continue;
row = GTK_WIDGET (cc_applications_row_new (info));
gtk_list_box_insert (GTK_LIST_BOX (self->sidebar_listbox), row, -1);
id = get_app_id (info);
if (g_strcmp0 (id, self->current_app_id) == 0)
gtk_list_box_select_row (GTK_LIST_BOX (self->sidebar_listbox), GTK_LIST_BOX_ROW (row));
}
}
static gint
compare_rows (GtkListBoxRow *row1,
GtkListBoxRow *row2,
gpointer data)
{
const gchar *key1 = cc_applications_row_get_sort_key (CC_APPLICATIONS_ROW (row1));
const gchar *key2 = cc_applications_row_get_sort_key (CC_APPLICATIONS_ROW (row2));
return strcmp (key1, key2);
}
static gboolean
filter_sidebar_rows (GtkListBoxRow *row,
gpointer data)
{
CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (data);
g_autofree gchar *app_name = NULL;
g_autofree gchar *search_text = NULL;
GAppInfo *info;
/* Only filter after the second character */
if (gtk_entry_get_text_length (self->sidebar_search_entry) < 2)
return TRUE;
info = cc_applications_row_get_info (CC_APPLICATIONS_ROW (row));
app_name = cc_util_normalize_casefold_and_unaccent (g_app_info_get_name (info));
search_text = cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (self->sidebar_search_entry));
return g_strstr_len (app_name, -1, search_text) != NULL;
}
static void
apps_changed (GAppInfoMonitor *monitor,
CcApplicationsPanel *self)
{
populate_applications (self);
}
static void
row_activated_cb (GtkListBox *list,
GtkListBoxRow *row,
CcApplicationsPanel *self)
{
update_panel (self, row);
g_signal_emit_by_name (self, "sidebar-activated");
}
static void
on_perm_store_ready (GObject *source_object,
GAsyncResult *res,
gpointer data)
{
CcApplicationsPanel *self = data;
GDBusProxy *proxy;
g_autoptr(GError) error = NULL;
proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
if (proxy == NULL)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to connect to portal permission store: %s",
error->message);
return;
}
self->perm_store = proxy;
update_panel (self, gtk_list_box_get_selected_row (self->sidebar_listbox));
}
static void
select_app (CcApplicationsPanel *self,
const gchar *app_id)
{
g_autoptr(GList) children = NULL;
GList *l;
children = gtk_container_get_children (GTK_CONTAINER (self->sidebar_listbox));
for (l = children; l; l = l->next)
{
CcApplicationsRow *row = CC_APPLICATIONS_ROW (l->data);
GAppInfo *info = cc_applications_row_get_info (row);
if (g_str_has_prefix (g_app_info_get_id (info), app_id))
{
gtk_list_box_select_row (self->sidebar_listbox, GTK_LIST_BOX_ROW (row));
break;
}
}
}
static void
on_sidebar_search_entry_activated_cb (GtkSearchEntry *search_entry,
CcApplicationsPanel *self)
{
GtkListBoxRow *row;
row = gtk_list_box_get_row_at_y (self->sidebar_listbox, 0);
if (!row)
return;
/* Show the app */
gtk_list_box_select_row (self->sidebar_listbox, row);
g_signal_emit_by_name (row, "activate");
/* Cleanup the entry */
gtk_entry_set_text (self->sidebar_search_entry, "");
gtk_widget_grab_focus (GTK_WIDGET (self->sidebar_search_entry));
}
static void
on_sidebar_search_entry_search_changed_cb (GtkSearchEntry *search_entry,
CcApplicationsPanel *self)
{
gtk_list_box_invalidate_filter (self->sidebar_listbox);
}
static void
on_sidebar_search_entry_search_stopped_cb (GtkSearchEntry *search_entry,
CcApplicationsPanel *self)
{
gtk_entry_set_text (self->sidebar_search_entry, "");
}
static void
cc_applications_panel_dispose (GObject *object)
{
CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (object);
g_clear_object (&self->monitor);
g_clear_object (&self->perm_store);
g_cancellable_cancel (self->cancellable);
G_OBJECT_CLASS (cc_applications_panel_parent_class)->dispose (object);
}
static void
cc_applications_panel_finalize (GObject *object)
{
CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (object);
g_clear_object (&self->notification_settings);
g_clear_object (&self->location_settings);
g_clear_object (&self->privacy_settings);
g_clear_object (&self->search_settings);
g_clear_object (&self->cancellable);
g_free (self->current_app_id);
g_free (self->current_flatpak_id);
g_hash_table_unref (self->globs);
g_hash_table_unref (self->search_providers);
G_OBJECT_CLASS (cc_applications_panel_parent_class)->finalize (object);
}
static void
cc_applications_panel_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_PARAMETERS:
{
GVariant *parameters, *v;
const gchar *first_arg = NULL;
parameters = g_value_get_variant (value);
if (parameters == NULL)
return;
if (g_variant_n_children (parameters) > 0)
{
g_variant_get_child (parameters, 0, "v", &v);
if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
first_arg = g_variant_get_string (v, NULL);
else
g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'",
(gchar *)g_variant_get_type (v));
g_variant_unref (v);
select_app (CC_APPLICATIONS_PANEL (object), first_arg);
}
return;
}
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
cc_applications_panel_constructed (GObject *object)
{
CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (object);
CcShell *shell;
G_OBJECT_CLASS (cc_applications_panel_parent_class)->constructed (object);
shell = cc_panel_get_shell (CC_PANEL (self));
cc_shell_embed_widget_in_header (shell, self->header_button, GTK_POS_RIGHT);
}
static GtkWidget*
cc_applications_panel_get_sidebar_widget (CcPanel *panel)
{
CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (panel);
return self->sidebar_box;
}
static GtkWidget *
cc_applications_panel_get_title_widget (CcPanel *panel)
{
CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (panel);
return self->title_label;
}
static void
cc_applications_panel_class_init (CcApplicationsPanelClass *klass)
{
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = cc_applications_panel_dispose;
object_class->finalize = cc_applications_panel_finalize;
object_class->constructed = cc_applications_panel_constructed;
object_class->set_property = cc_applications_panel_set_property;
panel_class->get_sidebar_widget = cc_applications_panel_get_sidebar_widget;
panel_class->get_title_widget = cc_applications_panel_get_title_widget;
g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/applications/cc-applications-panel.ui");
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin_dialog);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin_label);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin_list);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, cache);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, camera);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, clear_cache_button);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, data);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, header_button);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_section);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_reset);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_list);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, integration_list);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, integration_section);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, location);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, microphone);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_camera);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_location);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_microphone);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_search);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_sound);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, notification);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, background);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, permission_section);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, permission_list);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, sidebar_box);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, sidebar_listbox);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, sidebar_search_entry);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, search);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, sound);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, stack);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, storage);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, storage_dialog);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, storage_list);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, title_label);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, total);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, usage_list);
gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, usage_section);
gtk_widget_class_bind_template_callback (widget_class, camera_cb);
gtk_widget_class_bind_template_callback (widget_class, location_cb);
gtk_widget_class_bind_template_callback (widget_class, microphone_cb);
gtk_widget_class_bind_template_callback (widget_class, search_cb);
gtk_widget_class_bind_template_callback (widget_class, notification_cb);
gtk_widget_class_bind_template_callback (widget_class, background_cb);
gtk_widget_class_bind_template_callback (widget_class, privacy_link_cb);
gtk_widget_class_bind_template_callback (widget_class, sound_cb);
gtk_widget_class_bind_template_callback (widget_class, permission_row_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, handler_row_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, clear_cache_cb);
gtk_widget_class_bind_template_callback (widget_class, storage_row_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, open_software_cb);
gtk_widget_class_bind_template_callback (widget_class, handler_reset_cb);
gtk_widget_class_bind_template_callback (widget_class, on_sidebar_search_entry_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, on_sidebar_search_entry_search_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_sidebar_search_entry_search_stopped_cb);
}
static void
cc_applications_panel_init (CcApplicationsPanel *self)
{
g_autoptr(GtkStyleProvider) provider = NULL;
GtkListBoxRow *row;
g_resources_register (cc_applications_get_resource ());
gtk_widget_init_template (GTK_WIDGET (self));
self->cancellable = g_cancellable_new ();
provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
gtk_css_provider_load_from_resource (GTK_CSS_PROVIDER (provider),
"/org/gnome/control-center/applications/cc-applications-panel.css");
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
provider,
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_signal_connect (self->sidebar_listbox, "row-activated",
G_CALLBACK (row_activated_cb), self);
g_signal_connect (self->header_button, "clicked", G_CALLBACK (open_software_cb), self);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->permission_list),
cc_list_box_update_header_func,
NULL, NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->integration_list),
cc_list_box_update_header_func,
NULL, NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->handler_list),
cc_list_box_update_header_func,
NULL, NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->usage_list),
cc_list_box_update_header_func,
NULL, NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->builtin_list),
cc_list_box_update_header_func,
NULL, NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->storage_list),
cc_list_box_update_header_func,
NULL, NULL);
gtk_list_box_set_sort_func (self->sidebar_listbox,
compare_rows,
NULL, NULL);
gtk_list_box_set_filter_func (self->sidebar_listbox,
filter_sidebar_rows,
self, NULL);
self->location_settings = g_settings_new ("org.gnome.system.location");
self->privacy_settings = g_settings_new ("org.gnome.desktop.privacy");
self->search_settings = g_settings_new ("org.gnome.desktop.search-providers");
populate_applications (self);
self->monitor = g_app_info_monitor_get ();
self->monitor_id = g_signal_connect (self->monitor, "changed", G_CALLBACK (apps_changed), self);
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.impl.portal.PermissionStore",
"/org/freedesktop/impl/portal/PermissionStore",
"org.freedesktop.impl.portal.PermissionStore",
self->cancellable,
on_perm_store_ready,
self);
self->globs = parse_globs ();
self->search_providers = parse_search_providers ();
/* Select the first row */
row = gtk_list_box_get_row_at_index (self->sidebar_listbox, 0);
gtk_list_box_select_row (self->sidebar_listbox, row);
g_signal_emit_by_name (row, "activate");
}