diff options
Diffstat (limited to 'mate-session/gsm-inhibit-dialog.c')
-rw-r--r-- | mate-session/gsm-inhibit-dialog.c | 1164 |
1 files changed, 1164 insertions, 0 deletions
diff --git a/mate-session/gsm-inhibit-dialog.c b/mate-session/gsm-inhibit-dialog.c new file mode 100644 index 0000000..d5b4d57 --- /dev/null +++ b/mate-session/gsm-inhibit-dialog.c @@ -0,0 +1,1164 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <mateconf/mateconf-client.h> + +#include "gsm-inhibit-dialog.h" +#include "gsm-store.h" +#include "gsm-client.h" +#include "gsm-inhibitor.h" +#include "eggdesktopfile.h" +#include "gsm-util.h" + +#ifdef HAVE_XRENDER +#include <X11/extensions/Xrender.h> +#endif + +#define GSM_INHIBIT_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_INHIBIT_DIALOG, GsmInhibitDialogPrivate)) + +#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0') + +#define GTKBUILDER_FILE "gsm-inhibit-dialog.ui" + +#ifndef DEFAULT_ICON_SIZE +#define DEFAULT_ICON_SIZE 32 +#endif + +#ifndef DEFAULT_SNAPSHOT_SIZE +#define DEFAULT_SNAPSHOT_SIZE 128 +#endif + +#define DIALOG_RESPONSE_LOCK_SCREEN 1 + +struct GsmInhibitDialogPrivate +{ + GtkBuilder *xml; + int action; + gboolean is_done; + GsmStore *inhibitors; + GsmStore *clients; + GtkListStore *list_store; + gboolean have_xrender; + int xrender_event_base; + int xrender_error_base; +}; + +enum { + PROP_0, + PROP_ACTION, + PROP_INHIBITOR_STORE, + PROP_CLIENT_STORE +}; + +enum { + INHIBIT_IMAGE_COLUMN = 0, + INHIBIT_NAME_COLUMN, + INHIBIT_REASON_COLUMN, + INHIBIT_ID_COLUMN, + NUMBER_OF_COLUMNS +}; + +static void gsm_inhibit_dialog_class_init (GsmInhibitDialogClass *klass); +static void gsm_inhibit_dialog_init (GsmInhibitDialog *inhibit_dialog); +static void gsm_inhibit_dialog_finalize (GObject *object); + +G_DEFINE_TYPE (GsmInhibitDialog, gsm_inhibit_dialog, GTK_TYPE_DIALOG) + +static void +lock_screen (GsmInhibitDialog *dialog) +{ + GError *error; + error = NULL; + g_spawn_command_line_async ("mate-screensaver-command --lock", &error); + if (error != NULL) { + g_warning ("Couldn't lock screen: %s", error->message); + g_error_free (error); + } +} + +static void +on_response (GsmInhibitDialog *dialog, + gint response_id) + +{ + if (dialog->priv->is_done) { + g_signal_stop_emission_by_name (dialog, "response"); + return; + } + + switch (response_id) { + case DIALOG_RESPONSE_LOCK_SCREEN: + g_signal_stop_emission_by_name (dialog, "response"); + lock_screen (dialog); + break; + default: + dialog->priv->is_done = TRUE; + break; + } +} + +static void +gsm_inhibit_dialog_set_action (GsmInhibitDialog *dialog, + int action) +{ + dialog->priv->action = action; +} + +static gboolean +find_inhibitor (GsmInhibitDialog *dialog, + const char *id, + GtkTreeIter *iter) +{ + GtkTreeModel *model; + gboolean found_item; + + g_assert (GSM_IS_INHIBIT_DIALOG (dialog)); + + found_item = FALSE; + model = GTK_TREE_MODEL (dialog->priv->list_store); + + if (!gtk_tree_model_get_iter_first (model, iter)) { + return FALSE; + } + + do { + char *item_id; + + gtk_tree_model_get (model, + iter, + INHIBIT_ID_COLUMN, &item_id, + -1); + if (item_id != NULL + && id != NULL + && strcmp (item_id, id) == 0) { + found_item = TRUE; + } + g_free (item_id); + } while (!found_item && gtk_tree_model_iter_next (model, iter)); + + return found_item; +} + +/* copied from mate-panel panel-util.c */ +static char * +_util_icon_remove_extension (const char *icon) +{ + char *icon_no_extension; + char *p; + + icon_no_extension = g_strdup (icon); + p = strrchr (icon_no_extension, '.'); + if (p && + (strcmp (p, ".png") == 0 || + strcmp (p, ".xpm") == 0 || + strcmp (p, ".svg") == 0)) { + *p = 0; + } + + return icon_no_extension; +} + +/* copied from mate-panel panel-util.c */ +static char * +_find_icon (GtkIconTheme *icon_theme, + const char *icon_name, + gint size) +{ + GtkIconInfo *info; + char *retval; + char *icon_no_extension; + + if (icon_name == NULL || strcmp (icon_name, "") == 0) + return NULL; + + if (g_path_is_absolute (icon_name)) { + if (g_file_test (icon_name, G_FILE_TEST_EXISTS)) { + return g_strdup (icon_name); + } else { + char *basename; + + basename = g_path_get_basename (icon_name); + retval = _find_icon (icon_theme, basename, + size); + g_free (basename); + + return retval; + } + } + + /* This is needed because some .desktop files have an icon name *and* + * an extension as icon */ + icon_no_extension = _util_icon_remove_extension (icon_name); + + info = gtk_icon_theme_lookup_icon (icon_theme, icon_no_extension, + size, 0); + + g_free (icon_no_extension); + + if (info) { + retval = g_strdup (gtk_icon_info_get_filename (info)); + gtk_icon_info_free (info); + } else + retval = NULL; + + return retval; +} + +/* copied from mate-panel panel-util.c */ +static GdkPixbuf * +_load_icon (GtkIconTheme *icon_theme, + const char *icon_name, + int size, + int desired_width, + int desired_height, + char **error_msg) +{ + GdkPixbuf *retval; + char *file; + GError *error; + + g_return_val_if_fail (error_msg == NULL || *error_msg == NULL, NULL); + + file = _find_icon (icon_theme, icon_name, size); + if (!file) { + if (error_msg) + *error_msg = g_strdup_printf (_("Icon '%s' not found"), + icon_name); + + return NULL; + } + + error = NULL; + retval = gdk_pixbuf_new_from_file_at_size (file, + desired_width, + desired_height, + &error); + if (error) { + if (error_msg) + *error_msg = g_strdup (error->message); + g_error_free (error); + } + + g_free (file); + + return retval; +} + + +static GdkPixbuf * +scale_pixbuf (GdkPixbuf *pixbuf, + int max_width, + int max_height, + gboolean no_stretch_hint) +{ + int pw; + int ph; + float scale_factor_x = 1.0; + float scale_factor_y = 1.0; + float scale_factor = 1.0; + + pw = gdk_pixbuf_get_width (pixbuf); + ph = gdk_pixbuf_get_height (pixbuf); + + /* Determine which dimension requires the smallest scale. */ + scale_factor_x = (float) max_width / (float) pw; + scale_factor_y = (float) max_height / (float) ph; + + if (scale_factor_x > scale_factor_y) { + scale_factor = scale_factor_y; + } else { + scale_factor = scale_factor_x; + } + + /* always scale down, allow to disable scaling up */ + if (scale_factor < 1.0 || !no_stretch_hint) { + int scale_x = (int) (pw * scale_factor); + int scale_y = (int) (ph * scale_factor); + g_debug ("Scaling to %dx%d", scale_x, scale_y); + return gdk_pixbuf_scale_simple (pixbuf, + scale_x, + scale_y, + GDK_INTERP_BILINEAR); + } else { + return g_object_ref (pixbuf); + } +} + +#ifdef HAVE_XRENDER + +/* adapted from marco */ +static GdkColormap* +get_cmap (GdkPixmap *pixmap) +{ + GdkColormap *cmap; + + cmap = gdk_drawable_get_colormap (pixmap); + if (cmap) { + g_object_ref (G_OBJECT (cmap)); + } + + if (cmap == NULL) { + if (gdk_drawable_get_depth (pixmap) == 1) { + g_debug ("Using NULL colormap for snapshotting bitmap\n"); + cmap = NULL; + } else { + g_debug ("Using system cmap to snapshot pixmap\n"); + cmap = gdk_screen_get_system_colormap (gdk_drawable_get_screen (pixmap)); + + g_object_ref (G_OBJECT (cmap)); + } + } + + /* Be sure we aren't going to blow up due to visual mismatch */ + if (cmap && + (gdk_visual_get_depth (gdk_colormap_get_visual (cmap)) != + gdk_drawable_get_depth (pixmap))) { + cmap = NULL; + g_debug ("Switching back to NULL cmap because of depth mismatch\n"); + } + + return cmap; +} + +static GdkPixbuf * +pixbuf_get_from_pixmap (Pixmap xpixmap) +{ + GdkDrawable *drawable; + GdkPixbuf *retval; + GdkColormap *cmap; + int width; + int height; + + retval = NULL; + cmap = NULL; + + g_debug ("GsmInhibitDialog: getting foreign pixmap for %u", (guint)xpixmap); + drawable = gdk_pixmap_foreign_new (xpixmap); + if (GDK_IS_PIXMAP (drawable)) { + cmap = get_cmap (drawable); + + #if GTK_CHECK_VERSION(3, 0, 0) + width = gdk_window_get_width(drawable); + height = gdk_window_get_height(drawable); + #else + gdk_drawable_get_size(drawable, &width, &height); + #endif + + g_debug ("GsmInhibitDialog: getting pixbuf w=%d h=%d", width, height); + + retval = gdk_pixbuf_get_from_drawable (NULL, + drawable, + cmap, + 0, 0, + 0, 0, + width, height); + } + if (cmap) { + g_object_unref (G_OBJECT (cmap)); + } + if (drawable) { + g_object_unref (G_OBJECT (drawable)); + } + + return retval; +} + +static Pixmap +get_pixmap_for_window (Window window) +{ + XWindowAttributes attr; + XRenderPictureAttributes pa; + Pixmap pixmap; + XRenderPictFormat *format; + Picture src_picture; + Picture dst_picture; + gboolean has_alpha; + int x; + int y; + int width; + int height; + + XGetWindowAttributes (GDK_DISPLAY (), window, &attr); + + format = XRenderFindVisualFormat (GDK_DISPLAY (), attr.visual); + has_alpha = (format->type == PictTypeDirect && format->direct.alphaMask); + x = attr.x; + y = attr.y; + width = attr.width; + height = attr.height; + + pa.subwindow_mode = IncludeInferiors; /* Don't clip child widgets */ + + src_picture = XRenderCreatePicture (GDK_DISPLAY (), window, format, CPSubwindowMode, &pa); + + pixmap = XCreatePixmap (GDK_DISPLAY (), + window, + width, height, + attr.depth); + + dst_picture = XRenderCreatePicture (GDK_DISPLAY (), pixmap, format, 0, 0); + XRenderComposite (GDK_DISPLAY (), + has_alpha ? PictOpOver : PictOpSrc, + src_picture, + None, + dst_picture, + 0, 0, 0, 0, + 0, 0, + width, height); + + + return pixmap; +} + +#endif /* HAVE_COMPOSITE */ + +static GdkPixbuf * +get_pixbuf_for_window (guint xid, + int width, + int height) +{ + GdkPixbuf *pixbuf = NULL; +#ifdef HAVE_XRENDER + Window xwindow; + Pixmap xpixmap; + + xwindow = (Window) xid; + xpixmap = get_pixmap_for_window (xwindow); + if (xpixmap == None) { + g_debug ("GsmInhibitDialog: Unable to get window snapshot for %u", xid); + return NULL; + } else { + g_debug ("GsmInhibitDialog: Got xpixmap %u", (guint)xpixmap); + } + + pixbuf = pixbuf_get_from_pixmap (xpixmap); + + if (xpixmap != None) { + gdk_error_trap_push (); + XFreePixmap (GDK_DISPLAY (), xpixmap); + gdk_display_sync (gdk_display_get_default ()); + gdk_error_trap_pop (); + } + + if (pixbuf != NULL) { + GdkPixbuf *scaled; + g_debug ("GsmInhibitDialog: scaling pixbuf to w=%d h=%d", width, height); + scaled = scale_pixbuf (pixbuf, width, height, TRUE); + g_object_unref (pixbuf); + pixbuf = scaled; + } +#else + g_debug ("GsmInhibitDialog: no support for getting window snapshot"); +#endif + return pixbuf; +} + +static void +add_inhibitor (GsmInhibitDialog *dialog, + GsmInhibitor *inhibitor) +{ + const char *name; + const char *icon_name; + const char *app_id; + char *desktop_filename; + GdkPixbuf *pixbuf; + EggDesktopFile *desktop_file; + GError *error; + char **search_dirs; + guint xid; + char *freeme; + + /* FIXME: get info from xid */ + + desktop_file = NULL; + name = NULL; + pixbuf = NULL; + freeme = NULL; + + app_id = gsm_inhibitor_peek_app_id (inhibitor); + + if (IS_STRING_EMPTY (app_id)) { + desktop_filename = NULL; + } else if (! g_str_has_suffix (app_id, ".desktop")) { + desktop_filename = g_strdup_printf ("%s.desktop", app_id); + } else { + desktop_filename = g_strdup (app_id); + } + + xid = gsm_inhibitor_peek_toplevel_xid (inhibitor); + g_debug ("GsmInhibitDialog: inhibitor has XID %u", xid); + if (xid > 0 && dialog->priv->have_xrender) { + pixbuf = get_pixbuf_for_window (xid, DEFAULT_SNAPSHOT_SIZE, DEFAULT_SNAPSHOT_SIZE); + if (pixbuf == NULL) { + g_debug ("GsmInhibitDialog: unable to read pixbuf from %u", xid); + } + } + + if (desktop_filename != NULL) { + search_dirs = gsm_util_get_desktop_dirs (); + + if (g_path_is_absolute (desktop_filename)) { + char *basename; + + error = NULL; + desktop_file = egg_desktop_file_new (desktop_filename, + &error); + if (desktop_file == NULL) { + if (error) { + g_warning ("Unable to load desktop file '%s': %s", + desktop_filename, error->message); + g_error_free (error); + } else { + g_warning ("Unable to load desktop file '%s'", + desktop_filename); + } + + basename = g_path_get_basename (desktop_filename); + g_free (desktop_filename); + desktop_filename = basename; + } + } + + if (desktop_file == NULL) { + error = NULL; + desktop_file = egg_desktop_file_new_from_dirs (desktop_filename, + (const char **)search_dirs, + &error); + } + + /* look for a file with a vendor prefix */ + if (desktop_file == NULL) { + if (error) { + g_warning ("Unable to find desktop file '%s': %s", + desktop_filename, error->message); + g_error_free (error); + } else { + g_warning ("Unable to find desktop file '%s'", + desktop_filename); + } + g_free (desktop_filename); + desktop_filename = g_strdup_printf ("mate-%s.desktop", app_id); + error = NULL; + desktop_file = egg_desktop_file_new_from_dirs (desktop_filename, + (const char **)search_dirs, + &error); + } + g_strfreev (search_dirs); + + if (desktop_file == NULL) { + if (error) { + g_warning ("Unable to find desktop file '%s': %s", + desktop_filename, error->message); + g_error_free (error); + } else { + g_warning ("Unable to find desktop file '%s'", + desktop_filename); + } + } else { + name = egg_desktop_file_get_name (desktop_file); + icon_name = egg_desktop_file_get_icon (desktop_file); + + if (pixbuf == NULL) { + pixbuf = _load_icon (gtk_icon_theme_get_default (), + icon_name, + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + NULL); + } + } + } + + /* try client info */ + if (name == NULL) { + const char *client_id; + client_id = gsm_inhibitor_peek_client_id (inhibitor); + if (! IS_STRING_EMPTY (client_id)) { + GsmClient *client; + client = GSM_CLIENT (gsm_store_lookup (dialog->priv->clients, client_id)); + if (client != NULL) { + freeme = gsm_client_get_app_name (client); + name = freeme; + } + } + } + + if (name == NULL) { + if (! IS_STRING_EMPTY (app_id)) { + name = app_id; + } else { + name = _("Unknown"); + } + } + + if (pixbuf == NULL) { + pixbuf = _load_icon (gtk_icon_theme_get_default (), + "mate-windows", + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + NULL); + } + + gtk_list_store_insert_with_values (dialog->priv->list_store, + NULL, 0, + INHIBIT_IMAGE_COLUMN, pixbuf, + INHIBIT_NAME_COLUMN, name, + INHIBIT_REASON_COLUMN, gsm_inhibitor_peek_reason (inhibitor), + INHIBIT_ID_COLUMN, gsm_inhibitor_peek_id (inhibitor), + -1); + + g_free (desktop_filename); + g_free (freeme); + if (pixbuf != NULL) { + g_object_unref (pixbuf); + } + if (desktop_file != NULL) { + egg_desktop_file_free (desktop_file); + } +} + +static gboolean +model_has_one_entry (GtkTreeModel *model) +{ + guint n_rows; + + n_rows = gtk_tree_model_iter_n_children (model, NULL); + g_debug ("Model has %d rows", n_rows); + + return (n_rows > 0 && n_rows < 2); +} + +static void +update_dialog_text (GsmInhibitDialog *dialog) +{ + const char *description_text; + const char *header_text; + GtkWidget *widget; + + if (model_has_one_entry (GTK_TREE_MODEL (dialog->priv->list_store))) { + g_debug ("Found one entry in model"); + header_text = _("A program is still running:"); + description_text = _("Waiting for the program to finish. Interrupting the program may cause you to lose work."); + } else { + g_debug ("Found multiple entries (or none) in model"); + header_text = _("Some programs are still running:"); + description_text = _("Waiting for programs to finish. Interrupting these programs may cause you to lose work."); + } + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "header-label")); + if (widget != NULL) { + char *markup; + markup = g_strdup_printf ("<b>%s</b>", header_text); + gtk_label_set_markup (GTK_LABEL (widget), markup); + g_free (markup); + } + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "description-label")); + if (widget != NULL) { + gtk_label_set_text (GTK_LABEL (widget), description_text); + } +} + +static void +on_store_inhibitor_added (GsmStore *store, + const char *id, + GsmInhibitDialog *dialog) +{ + GsmInhibitor *inhibitor; + GtkTreeIter iter; + + g_debug ("GsmInhibitDialog: inhibitor added: %s", id); + + if (dialog->priv->is_done) { + return; + } + + inhibitor = (GsmInhibitor *)gsm_store_lookup (store, id); + + /* Add to model */ + if (! find_inhibitor (dialog, id, &iter)) { + add_inhibitor (dialog, inhibitor); + update_dialog_text (dialog); + } + +} + +static void +on_store_inhibitor_removed (GsmStore *store, + const char *id, + GsmInhibitDialog *dialog) +{ + GtkTreeIter iter; + + g_debug ("GsmInhibitDialog: inhibitor removed: %s", id); + + if (dialog->priv->is_done) { + return; + } + + /* Remove from model */ + if (find_inhibitor (dialog, id, &iter)) { + gtk_list_store_remove (dialog->priv->list_store, &iter); + update_dialog_text (dialog); + } + + /* if there are no inhibitors left then trigger response */ + if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dialog->priv->list_store), &iter)) { + gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + } +} + +static void +gsm_inhibit_dialog_set_inhibitor_store (GsmInhibitDialog *dialog, + GsmStore *store) +{ + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (dialog)); + + if (store != NULL) { + g_object_ref (store); + } + + if (dialog->priv->inhibitors != NULL) { + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_added, + dialog); + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_removed, + dialog); + + g_object_unref (dialog->priv->inhibitors); + } + + + g_debug ("GsmInhibitDialog: setting store %p", store); + + dialog->priv->inhibitors = store; + + if (dialog->priv->inhibitors != NULL) { + g_signal_connect (dialog->priv->inhibitors, + "added", + G_CALLBACK (on_store_inhibitor_added), + dialog); + g_signal_connect (dialog->priv->inhibitors, + "removed", + G_CALLBACK (on_store_inhibitor_removed), + dialog); + } +} + +static void +gsm_inhibit_dialog_set_client_store (GsmInhibitDialog *dialog, + GsmStore *store) +{ + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (dialog)); + + if (store != NULL) { + g_object_ref (store); + } + + if (dialog->priv->clients != NULL) { + g_object_unref (dialog->priv->clients); + } + + dialog->priv->clients = store; +} + +static void +gsm_inhibit_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmInhibitDialog *dialog = GSM_INHIBIT_DIALOG (object); + + switch (prop_id) { + case PROP_ACTION: + gsm_inhibit_dialog_set_action (dialog, g_value_get_int (value)); + break; + case PROP_INHIBITOR_STORE: + gsm_inhibit_dialog_set_inhibitor_store (dialog, g_value_get_object (value)); + break; + case PROP_CLIENT_STORE: + gsm_inhibit_dialog_set_client_store (dialog, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_inhibit_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmInhibitDialog *dialog = GSM_INHIBIT_DIALOG (object); + + switch (prop_id) { + case PROP_ACTION: + g_value_set_int (value, dialog->priv->action); + break; + case PROP_INHIBITOR_STORE: + g_value_set_object (value, dialog->priv->inhibitors); + break; + case PROP_CLIENT_STORE: + g_value_set_object (value, dialog->priv->clients); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +name_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + GsmInhibitDialog *dialog) +{ + char *name; + char *reason; + char *markup; + + name = NULL; + reason = NULL; + gtk_tree_model_get (model, + iter, + INHIBIT_NAME_COLUMN, &name, + INHIBIT_REASON_COLUMN, &reason, + -1); + + markup = g_strdup_printf ("<b>%s</b>\n" + "<span size=\"small\">%s</span>", + name ? name : "(null)", + reason ? reason : "(null)"); + + g_free (name); + g_free (reason); + + g_object_set (cell, "markup", markup, NULL); + g_free (markup); +} + +static gboolean +add_to_model (const char *id, + GsmInhibitor *inhibitor, + GsmInhibitDialog *dialog) +{ + add_inhibitor (dialog, inhibitor); + return FALSE; +} + +static void +populate_model (GsmInhibitDialog *dialog) +{ + gsm_store_foreach_remove (dialog->priv->inhibitors, + (GsmStoreFunc)add_to_model, + dialog); + update_dialog_text (dialog); +} + +static void +setup_dialog (GsmInhibitDialog *dialog) +{ + const char *button_text; + GtkWidget *treeview; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + switch (dialog->priv->action) { + case GSM_LOGOUT_ACTION_SWITCH_USER: + button_text = _("Switch User Anyway"); + break; + case GSM_LOGOUT_ACTION_LOGOUT: + button_text = _("Log Out Anyway"); + break; + case GSM_LOGOUT_ACTION_SLEEP: + button_text = _("Suspend Anyway"); + break; + case GSM_LOGOUT_ACTION_HIBERNATE: + button_text = _("Hibernate Anyway"); + break; + case GSM_LOGOUT_ACTION_SHUTDOWN: + button_text = _("Shut Down Anyway"); + break; + case GSM_LOGOUT_ACTION_REBOOT: + button_text = _("Reboot Anyway"); + break; + default: + g_assert_not_reached (); + break; + } + + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("Lock Screen"), + DIALOG_RESPONSE_LOCK_SCREEN); + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("Cancel"), + GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (dialog), + button_text, + GTK_RESPONSE_ACCEPT); + g_signal_connect (dialog, + "response", + G_CALLBACK (on_response), + dialog); + + dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + + treeview = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "inhibitors-treeview")); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), + GTK_TREE_MODEL (dialog->priv->list_store)); + + /* IMAGE COLUMN */ + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + gtk_tree_view_column_set_attributes (column, + renderer, + "pixbuf", INHIBIT_IMAGE_COLUMN, + NULL); + + g_object_set (renderer, "xalign", 1.0, NULL); + + /* NAME COLUMN */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + gtk_tree_view_column_set_cell_data_func (column, + renderer, + (GtkTreeCellDataFunc) name_cell_data_func, + dialog, + NULL); + + gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), + INHIBIT_REASON_COLUMN); + + populate_model (dialog); +} + +static GObject * +gsm_inhibit_dialog_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmInhibitDialog *dialog; + + dialog = GSM_INHIBIT_DIALOG (G_OBJECT_CLASS (gsm_inhibit_dialog_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + +#ifdef HAVE_XRENDER + gdk_error_trap_push (); + if (XRenderQueryExtension (GDK_DISPLAY (), &dialog->priv->xrender_event_base, &dialog->priv->xrender_error_base)) { + g_debug ("GsmInhibitDialog: Initialized XRender extension"); + dialog->priv->have_xrender = TRUE; + } else { + g_debug ("GsmInhibitDialog: Unable to initialize XRender extension"); + dialog->priv->have_xrender = FALSE; + } + gdk_display_sync (gdk_display_get_default ()); + gdk_error_trap_pop (); +#endif /* HAVE_XRENDER */ + + /* FIXME: turn this on when it is ready */ + dialog->priv->have_xrender = FALSE; + + setup_dialog (dialog); + + gtk_widget_show_all (GTK_WIDGET (dialog)); + + return G_OBJECT (dialog); +} + +static void +gsm_inhibit_dialog_dispose (GObject *object) +{ + GsmInhibitDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (object)); + + dialog = GSM_INHIBIT_DIALOG (object); + + g_debug ("GsmInhibitDialog: dispose called"); + + if (dialog->priv->list_store != NULL) { + g_object_unref (dialog->priv->list_store); + dialog->priv->list_store = NULL; + } + + if (dialog->priv->inhibitors != NULL) { + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_added, + dialog); + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_removed, + dialog); + + g_object_unref (dialog->priv->inhibitors); + dialog->priv->inhibitors = NULL; + } + + if (dialog->priv->xml != NULL) { + g_object_unref (dialog->priv->xml); + dialog->priv->xml = NULL; + } + + G_OBJECT_CLASS (gsm_inhibit_dialog_parent_class)->dispose (object); +} + +static void +gsm_inhibit_dialog_class_init (GsmInhibitDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsm_inhibit_dialog_get_property; + object_class->set_property = gsm_inhibit_dialog_set_property; + object_class->constructor = gsm_inhibit_dialog_constructor; + object_class->dispose = gsm_inhibit_dialog_dispose; + object_class->finalize = gsm_inhibit_dialog_finalize; + + g_object_class_install_property (object_class, + PROP_ACTION, + g_param_spec_int ("action", + "action", + "action", + -1, + G_MAXINT, + -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_INHIBITOR_STORE, + g_param_spec_object ("inhibitor-store", + NULL, + NULL, + GSM_TYPE_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_CLIENT_STORE, + g_param_spec_object ("client-store", + NULL, + NULL, + GSM_TYPE_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmInhibitDialogPrivate)); +} + +static void +gsm_inhibit_dialog_init (GsmInhibitDialog *dialog) +{ + GtkWidget *content_area; + GtkWidget *widget; + GError *error; + + dialog->priv = GSM_INHIBIT_DIALOG_GET_PRIVATE (dialog); + + dialog->priv->xml = gtk_builder_new (); + gtk_builder_set_translation_domain (dialog->priv->xml, GETTEXT_PACKAGE); + + error = NULL; + if (!gtk_builder_add_from_file (dialog->priv->xml, + GTKBUILDER_DIR "/" GTKBUILDER_FILE, + &error)) { + if (error) { + g_warning ("Could not load inhibitor UI file: %s", + error->message); + g_error_free (error); + } else { + g_warning ("Could not load inhibitor UI file."); + } + } + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "main-box")); + gtk_container_add (GTK_CONTAINER (content_area), widget); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_icon_name (GTK_WINDOW (dialog), "system-log-out"); + gtk_window_set_title (GTK_WINDOW (dialog), ""); + g_object_set (dialog, + "allow-shrink", FALSE, + "allow-grow", FALSE, + NULL); +} + +static void +gsm_inhibit_dialog_finalize (GObject *object) +{ + GsmInhibitDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (object)); + + dialog = GSM_INHIBIT_DIALOG (object); + + g_return_if_fail (dialog->priv != NULL); + + g_debug ("GsmInhibitDialog: finalizing"); + + G_OBJECT_CLASS (gsm_inhibit_dialog_parent_class)->finalize (object); +} + +GtkWidget * +gsm_inhibit_dialog_new (GsmStore *inhibitors, + GsmStore *clients, + int action) +{ + GObject *object; + + object = g_object_new (GSM_TYPE_INHIBIT_DIALOG, + "action", action, + "inhibitor-store", inhibitors, + "client-store", clients, + NULL); + + return GTK_WIDGET (object); +} |