diff options
22 files changed, 3244 insertions, 35 deletions
@@ -1,5 +1,82 @@ 2007-09-05 William Jon McCann <mccann@jhu.edu> + * configure.ac: + * daemon/gdm-greeter-server.c: (handle_get_display_id): + * gui/simple-greeter/Makefile.am: + * gui/simple-greeter/cutnpaste/Makefile.am: + * gui/simple-greeter/cutnpaste/fixedtip.c: (button_press_handler), + (expose_handler), (na_fixed_tip_class_init), (na_fixed_tip_init), + (na_fixed_tip_position), (na_fixed_tip_parent_size_allocated), + (na_fixed_tip_parent_screen_changed), (na_fixed_tip_new), + (na_fixed_tip_set_markup), (na_fixed_tip_set_orientation): + * gui/simple-greeter/cutnpaste/fixedtip.h: + * gui/simple-greeter/cutnpaste/na-marshal.list: + * gui/simple-greeter/cutnpaste/na-tray-manager.c: + (na_tray_manager_init), (na_tray_manager_class_init), + (na_tray_manager_finalize), (na_tray_manager_set_property), + (na_tray_manager_get_property), (na_tray_manager_new), + (na_tray_manager_plug_removed), + (na_tray_manager_make_socket_transparent), + (na_tray_manager_socket_exposed), + (na_tray_manager_socket_style_set), + (na_tray_manager_handle_dock_request), (pending_message_free), + (na_tray_manager_handle_client_message_message_data), + (na_tray_manager_handle_begin_message), + (na_tray_manager_handle_cancel_message), + (na_tray_manager_handle_client_message_opcode), + (na_tray_manager_window_filter), + (na_tray_manager_selection_clear_event), + (na_tray_manager_unmanage), + (na_tray_manager_set_orientation_property), + (na_tray_manager_manage_screen_x11), + (na_tray_manager_manage_screen), + (na_tray_manager_check_running_screen_x11), + (na_tray_manager_check_running), (na_tray_manager_get_child_title), + (na_tray_manager_set_orientation), + (na_tray_manager_get_orientation): + * gui/simple-greeter/cutnpaste/na-tray-manager.h: + * gui/simple-greeter/cutnpaste/na-tray.c: (get_tray), (tray_added), + (tray_removed), (icon_tip_buffer_free), (icon_tip_free), + (icon_tip_buffer_compare), (icon_tip_show_next_clicked), + (icon_tip_show_next_timeout), (icon_tip_show_next), (message_sent), + (message_cancelled), (update_orientation_for_messages), + (update_size_and_orientation), (na_tray_init), + (na_tray_constructor), (na_tray_dispose), (na_tray_set_property), + (na_tray_size_request), (na_tray_size_allocate), + (na_tray_class_init), (na_tray_new_for_screen), + (na_tray_set_orientation), (na_tray_get_orientation), + (idle_redraw_cb), (na_tray_force_redraw): + * gui/simple-greeter/cutnpaste/na-tray.h: + * gui/simple-greeter/cutnpaste/obox.c: (na_obox_class_init), + (na_obox_init), (na_obox_new), (get_class), (na_obox_size_request), + (na_obox_size_allocate), (na_obox_set_orientation): + * gui/simple-greeter/cutnpaste/obox.h: + * gui/simple-greeter/cutnpaste/testtray.c: (do_add), + (update_child_count), (tray_added_cb), (tray_removed_cb), + (orientation_changed_cb), (maybe_quit), + (warning_dialog_response_cb), (add_tray_cb), + (create_tray_on_screen), (main): + * gui/simple-greeter/gdm-greeter-panel.c: + (gdm_greeter_panel_set_property), (gdm_greeter_panel_get_property), + (gdm_greeter_panel_constructor), (gdm_greeter_panel_dispose), + (gdm_greeter_panel_move_resize_window), (on_screen_size_changed), + (gdm_greeter_panel_real_realize), + (gdm_greeter_panel_real_unrealize), (get_outside_region), + (get_monitor_geometry), (set_struts), (update_struts), + (update_geometry), (gdm_greeter_panel_real_size_request), + (gdm_greeter_panel_class_init), (gdm_greeter_panel_init), + (gdm_greeter_panel_finalize), (gdm_greeter_panel_new): + * gui/simple-greeter/gdm-greeter-panel.h: + * gui/simple-greeter/gdm-simple-greeter.c: (set_sensitive), + (set_message), (create_greeter), (create_panel), + (gdm_simple_greeter_constructor): + * gui/simple-greeter/gdm-simple-greeter.glade: + * gui/simple-greeter/greeter-main.c: (get_display_id): + * gui/simple-greeter/test-greeter-panel.c: (main): + Add a greeter panel bar with an embedded notification area. + +2007-09-05 William Jon McCann <mccann@jhu.edu> + * daemon/gdm-factory-slave.c: (run_greeter): * daemon/gdm-greeter-server.c: (handle_disconnect), (handle_get_display_id), (greeter_handle_child_message), diff --git a/common/Makefile.am b/common/Makefile.am index d5021944..d1b31b93 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -100,7 +100,7 @@ test_log_SOURCES = \ $(NULL) test_log_LDADD = \ - libgdmcommon.la \ + libgdmcommon.la \ $(GLIB_LIBS) \ $(NULL) diff --git a/configure.ac b/configure.ac index 94f96d65..ced3ea57 100644 --- a/configure.ac +++ b/configure.ac @@ -1306,6 +1306,7 @@ libgreeter/Makefile gui/Makefile gui/modules/Makefile gui/simple-greeter/Makefile +gui/simple-greeter/cutnpaste/Makefile gui/simple-chooser/Makefile utils/Makefile data/Makefile diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c index b3366c61..5e583ffd 100644 --- a/daemon/gdm-greeter-server.c +++ b/daemon/gdm-greeter-server.c @@ -404,7 +404,7 @@ handle_get_display_id (GdmGreeterServer *greeter_server, reply = dbus_message_new_method_return (message); dbus_message_iter_init_append (reply, &iter); - dbus_message_iter_append_basic (&iter, DBUS_TYPE_G_OBJECT_PATH, &greeter_server->priv->display_id); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_OBJECT_PATH, &greeter_server->priv->display_id); dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); diff --git a/gui/simple-greeter/Makefile.am b/gui/simple-greeter/Makefile.am index 2f12e749..9051daa3 100644 --- a/gui/simple-greeter/Makefile.am +++ b/gui/simple-greeter/Makefile.am @@ -1,8 +1,13 @@ NULL = +SUBDIRS = \ + cutnpaste \ + $(NULL) + INCLUDES = \ -I$(top_srcdir)/common \ -I$(top_srcdir)/libgreeter \ + -I$(top_srcdir)/gui/simple-greeter/cutnpaste \ -DAUTHDIR=\""$(authdir)"\" \ -DDATADIR=\""$(datadir)"\" \ -DGDMCONFDIR=\"$(gdmconfdir)\" \ @@ -19,6 +24,21 @@ INCLUDES = \ $(GREETER_CFLAGS) \ $(NULL) +noinst_PROGRAMS = \ + test-greeter-panel \ + $(NULL) + +test_greeter_panel_SOURCES = \ + test-greeter-panel.c \ + gdm-greeter-panel.h \ + gdm-greeter-panel.c \ + $(NULL) + +test_greeter_panel_LDADD = \ + $(top_builddir)/gui/simple-greeter/cutnpaste/libnotificationarea.la \ + $(GUI_LIBS) \ + $(NULL) + libexec_PROGRAMS = \ gdm-simple-greeter @@ -26,12 +46,15 @@ gdm_simple_greeter_SOURCES = \ greeter-main.c \ gdm-simple-greeter.c \ gdm-simple-greeter.h \ + gdm-greeter-panel.h \ + gdm-greeter-panel.c \ $(NULL) gdm_simple_greeter_LDADD = \ $(EXTRA_GREETER_LIBS) \ -L$(top_builddir)/libgreeter \ -lgreeter \ + $(top_builddir)/gui/simple-greeter/cutnpaste/libnotificationarea.la \ $(GLIB_LIBS) \ $(DBUS_LIBS) \ $(GOBJECT_LIBS) \ diff --git a/gui/simple-greeter/cutnpaste/Makefile.am b/gui/simple-greeter/cutnpaste/Makefile.am new file mode 100644 index 00000000..7b5fde3c --- /dev/null +++ b/gui/simple-greeter/cutnpaste/Makefile.am @@ -0,0 +1,66 @@ +NULL = + +INCLUDES = \ + -I. \ + -I.. \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DLIBDIR=\"$(libdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DLOGDIR=\"$(logdir)\" \ + -DPIXMAPDIR=\"$(pixmapdir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + $(GUI_CFLAGS) \ + $(NULL) + +noinst_LTLIBRARIES = \ + libnotificationarea.la \ + $(NULL) + +BUILT_SOURCES = \ + na-marshal.c \ + na-marshal.h \ + $(NULL) + +libnotificationarea_la_SOURCES = \ + fixedtip.h \ + fixedtip.c \ + obox.c \ + obox.h \ + na-tray.c \ + na-tray.h \ + na-tray-manager.c \ + na-tray-manager.h \ + na-marshal.c \ + na-marshal.h \ + $(NULL) + +libnotificationarea_la_LIBADD = \ + $(GUI_LIBS) \ + $(NULL) + +libnotification_area_la_LDFLAGS = \ + -export-dynamic \ + $(NULL) + +EXTRA_DIST = \ + na-marshal.list \ + $(NULL) + +na-marshal.h: na-marshal.list $(GLIB_GENMARSHAL) + $(GLIB_GENMARSHAL) $< --header --prefix=_na_marshal > $@ + +na-marshal.c: na-marshal.list $(GLIB_GENMARSHAL) + echo "#include \"na-marshal.h\"" > $@ && \ + $(GLIB_GENMARSHAL) $< --body --prefix=_na_marshal >> $@ + +noinst_PROGRAMS = testtray + +testtray_SOURCES = \ + testtray.c \ + $(NULL) + +testtray_LDADD = \ + libnotificationarea.la \ + $(GUI_LIBS) \ + $(NULL) diff --git a/gui/simple-greeter/cutnpaste/fixedtip.c b/gui/simple-greeter/cutnpaste/fixedtip.c new file mode 100644 index 00000000..83bb77ed --- /dev/null +++ b/gui/simple-greeter/cutnpaste/fixedtip.c @@ -0,0 +1,261 @@ +/* Metacity fixed tooltip routine */ + +/* + * Copyright (C) 2001 Havoc Pennington + * Copyright (C) 2003-2006 Vincent Untz + * + * 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 "fixedtip.h" + +/* Signals */ +enum +{ + CLICKED, + LAST_SIGNAL +}; + +static guint fixedtip_signals[LAST_SIGNAL] = { 0 }; + +struct _NaFixedTipPrivate +{ + GtkWidget *parent; + GtkWidget *label; + GtkOrientation orientation; +}; + +G_DEFINE_TYPE (NaFixedTip, na_fixed_tip, GTK_TYPE_WINDOW) + +static gboolean +button_press_handler (GtkWidget *fixedtip, + GdkEventButton *event, + gpointer data) +{ + if (event->button == 1 && event->type == GDK_BUTTON_PRESS) + g_signal_emit (fixedtip, fixedtip_signals[CLICKED], 0); + + return FALSE; +} + +static gboolean +expose_handler (GtkWidget *fixedtip) +{ + GtkRequisition req; + + gtk_widget_size_request (fixedtip, &req); + + gtk_paint_flat_box (fixedtip->style, fixedtip->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, fixedtip, "tooltip", + 0, 0, req.width, req.height); + + return FALSE; +} + +static void +na_fixed_tip_class_init (NaFixedTipClass *class) +{ + fixedtip_signals[CLICKED] = + g_signal_new ("clicked", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NaFixedTipClass, clicked), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (class, sizeof (NaFixedTipPrivate)); +} + +/* Did you already see this code? Yes, it's gtk_tooltips_force_window() ;-) */ +static void +na_fixed_tip_init (NaFixedTip *fixedtip) +{ + GtkWidget *label; + + fixedtip->priv = G_TYPE_INSTANCE_GET_PRIVATE (fixedtip, NA_TYPE_FIXED_TIP, + NaFixedTipPrivate); + + gtk_window_set_type_hint (GTK_WINDOW (fixedtip), + GDK_WINDOW_TYPE_HINT_TOOLTIP); + + gtk_widget_set_app_paintable (GTK_WIDGET (fixedtip), TRUE); + gtk_window_set_resizable (GTK_WINDOW (fixedtip), FALSE); + gtk_widget_set_name (GTK_WIDGET (fixedtip), "gtk-tooltips"); + gtk_container_set_border_width (GTK_CONTAINER (fixedtip), 4); + + label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (fixedtip), label); + fixedtip->priv->label = label; + + g_signal_connect (fixedtip, "expose_event", + G_CALLBACK (expose_handler), NULL); + + gtk_widget_add_events (GTK_WIDGET (fixedtip), GDK_BUTTON_PRESS_MASK); + + g_signal_connect (fixedtip, "button_press_event", + G_CALLBACK (button_press_handler), NULL); + + fixedtip->priv->orientation = GTK_ORIENTATION_HORIZONTAL; +} + +static void +na_fixed_tip_position (NaFixedTip *fixedtip) +{ + GdkScreen *screen; + GtkRequisition req; + int root_x; + int root_y; + int parent_width; + int parent_height; + int screen_width; + int screen_height; + + screen = gtk_widget_get_screen (fixedtip->priv->parent); + gtk_window_set_screen (GTK_WINDOW (fixedtip), screen); + + gtk_widget_size_request (GTK_WIDGET (fixedtip), &req); + + gdk_window_get_origin (fixedtip->priv->parent->window, &root_x, &root_y); + gdk_drawable_get_size (GDK_DRAWABLE (fixedtip->priv->parent->window), + &parent_width, &parent_height); + + screen_width = gdk_screen_get_width (screen); + screen_height = gdk_screen_get_height (screen); + + /* pad between panel and message window */ +#define PAD 5 + + if (fixedtip->priv->orientation == GTK_ORIENTATION_VERTICAL) + { + if (root_x <= screen_width / 2) + root_x += parent_width + PAD; + else + root_x -= req.width + PAD; + } + else + { + if (root_y <= screen_height / 2) + root_y += parent_height + PAD; + else + root_y -= req.height + PAD; + } + + /* Push onscreen */ + if ((root_x + req.width) > screen_width) + root_x = screen_width - req.width; + + if ((root_y + req.height) > screen_height) + root_y = screen_height - req.height; + + gtk_window_move (GTK_WINDOW (fixedtip), root_x, root_y); +} + +static void +na_fixed_tip_parent_size_allocated (GtkWidget *parent, + GtkAllocation *allocation, + NaFixedTip *fixedtip) +{ + na_fixed_tip_position (fixedtip); +} + +static void +na_fixed_tip_parent_screen_changed (GtkWidget *parent, + GdkScreen *new_screen, + NaFixedTip *fixedtip) +{ + na_fixed_tip_position (fixedtip); +} + +GtkWidget * +na_fixed_tip_new (GtkWidget *parent, + GtkOrientation orientation) +{ + NaFixedTip *fixedtip; + + g_return_val_if_fail (parent != NULL, NULL); + + fixedtip = g_object_new (NA_TYPE_FIXED_TIP, NULL); + + /* It doesn't work if we do this in na_fixed_tip_init(), so do it here */ + GTK_WINDOW (fixedtip)->type = GTK_WINDOW_POPUP; + + fixedtip->priv->parent = parent; + +#if 0 + //FIXME: would be nice to be able to get the toplevel for the tip, but this + //doesn't work + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (parent); + /* + if (toplevel && GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel)) + gtk_window_set_transient_for (GTK_WINDOW (fixedtip), GTK_WINDOW (toplevel)); + */ +#endif + + fixedtip->priv->orientation = orientation; + + //FIXME: would be nice to move the tip when the notification area moves + g_signal_connect_object (parent, "size-allocate", + G_CALLBACK (na_fixed_tip_parent_size_allocated), + fixedtip, 0); + g_signal_connect_object (parent, "screen-changed", + G_CALLBACK (na_fixed_tip_parent_screen_changed), + fixedtip, 0); + + na_fixed_tip_position (fixedtip); + + return GTK_WIDGET (fixedtip); +} + +void +na_fixed_tip_set_markup (GtkWidget *widget, + const char *markup_text) +{ + NaFixedTip *fixedtip; + + g_return_if_fail (NA_IS_FIXED_TIP (widget)); + + fixedtip = NA_FIXED_TIP (widget); + + gtk_label_set_markup (GTK_LABEL (fixedtip->priv->label), + markup_text); + + na_fixed_tip_position (fixedtip); +} + +void +na_fixed_tip_set_orientation (GtkWidget *widget, + GtkOrientation orientation) +{ + NaFixedTip *fixedtip; + + g_return_if_fail (NA_IS_FIXED_TIP (widget)); + + fixedtip = NA_FIXED_TIP (widget); + + if (orientation == fixedtip->priv->orientation) + return; + + fixedtip->priv->orientation = orientation; + + na_fixed_tip_position (fixedtip); +} diff --git a/gui/simple-greeter/cutnpaste/fixedtip.h b/gui/simple-greeter/cutnpaste/fixedtip.h new file mode 100644 index 00000000..a29e85c9 --- /dev/null +++ b/gui/simple-greeter/cutnpaste/fixedtip.h @@ -0,0 +1,68 @@ +/* Fixed tooltip routine */ + +/* + * Copyright (C) 2001 Havoc Pennington, 2002 Red Hat Inc. + * Copyright (C) 2003-2006 Vincent Untz + * + * 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. + */ + +#ifndef FIXED_TIP_H +#define FIXED_TIP_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define NA_TYPE_FIXED_TIP (na_fixed_tip_get_type ()) +#define NA_FIXED_TIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_FIXED_TIP, NaFixedTip)) +#define NA_FIXED_TIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_FIXED_TIP, NaFixedTipClass)) +#define NA_IS_FIXED_TIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_FIXED_TIP)) +#define NA_IS_FIXED_TIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_FIXED_TIP)) +#define NA_FIXED_TIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_FIXED_TIP, NaFixedTipClass)) + +typedef struct _NaFixedTip NaFixedTip; +typedef struct _NaFixedTipPrivate NaFixedTipPrivate; +typedef struct _NaFixedTipClass NaFixedTipClass; + +struct _NaFixedTip +{ + GtkWindow parent_instance; + + NaFixedTipPrivate *priv; +}; + +struct _NaFixedTipClass +{ + GtkWindowClass parent_class; + + void (* clicked) (NaFixedTip *fixedtip); +}; + +GType na_fixed_tip_get_type (void); + +GtkWidget *na_fixed_tip_new (GtkWidget *parent, + GtkOrientation orientation); + +void na_fixed_tip_set_markup (GtkWidget *widget, + const char *markup_text); + +void na_fixed_tip_set_orientation (GtkWidget *widget, + GtkOrientation orientation); + +G_END_DECLS + +#endif /* FIXED_TIP_H */ diff --git a/gui/simple-greeter/cutnpaste/na-marshal.list b/gui/simple-greeter/cutnpaste/na-marshal.list new file mode 100644 index 00000000..e3fc3993 --- /dev/null +++ b/gui/simple-greeter/cutnpaste/na-marshal.list @@ -0,0 +1,3 @@ +VOID:OBJECT,OBJECT +VOID:OBJECT,STRING,LONG,LONG +VOID:OBJECT,LONG diff --git a/gui/simple-greeter/cutnpaste/na-tray-manager.c b/gui/simple-greeter/cutnpaste/na-tray-manager.c new file mode 100644 index 00000000..59fdca36 --- /dev/null +++ b/gui/simple-greeter/cutnpaste/na-tray-manager.c @@ -0,0 +1,907 @@ +/* na-tray-manager.c + * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org> + * Copyright (C) 2003-2006 Vincent Untz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Used to be: eggtraymanager.c + */ + +#include <config.h> +#include <string.h> +#include <libintl.h> + +#include "na-tray-manager.h" + +#include <gdkconfig.h> +#include <glib/gi18n.h> +#if defined (GDK_WINDOWING_X11) +#include <gdk/gdkx.h> +#include <X11/Xatom.h> +#elif defined (GDK_WINDOWING_WIN32) +#include <gdk/gdkwin32.h> +#endif +#include <gtk/gtkinvisible.h> +#include <gtk/gtksocket.h> +#include <gtk/gtkwindow.h> + +#include "na-marshal.h" + +/* Signals */ +enum +{ + TRAY_ICON_ADDED, + TRAY_ICON_REMOVED, + MESSAGE_SENT, + MESSAGE_CANCELLED, + LOST_SELECTION, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_ORIENTATION +}; + +typedef struct +{ + long id, len; + long remaining_len; + + long timeout; + char *str; +#ifdef GDK_WINDOWING_X11 + Window window; +#endif +} PendingMessage; + +static guint manager_signals[LAST_SIGNAL]; + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define SYSTEM_TRAY_ORIENTATION_HORZ 0 +#define SYSTEM_TRAY_ORIENTATION_VERT 1 + +#ifdef GDK_WINDOWING_X11 +static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen); +#endif + +static void na_tray_manager_finalize (GObject *object); +static void na_tray_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void na_tray_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void na_tray_manager_unmanage (NaTrayManager *manager); + +G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT) + +static void +na_tray_manager_init (NaTrayManager *manager) +{ + manager->invisible = NULL; + manager->socket_table = g_hash_table_new (NULL, NULL); +} + +static void +na_tray_manager_class_init (NaTrayManagerClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *)klass; + + gobject_class->finalize = na_tray_manager_finalize; + gobject_class->set_property = na_tray_manager_set_property; + gobject_class->get_property = na_tray_manager_get_property; + + g_object_class_install_property (gobject_class, + PROP_ORIENTATION, + g_param_spec_enum ("orientation", + "orientation", + "orientation", + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + manager_signals[TRAY_ICON_ADDED] = + g_signal_new ("tray_icon_added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_SOCKET); + + manager_signals[TRAY_ICON_REMOVED] = + g_signal_new ("tray_icon_removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_SOCKET); + manager_signals[MESSAGE_SENT] = + g_signal_new ("message_sent", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NaTrayManagerClass, message_sent), + NULL, NULL, + _na_marshal_VOID__OBJECT_STRING_LONG_LONG, + G_TYPE_NONE, 4, + GTK_TYPE_SOCKET, + G_TYPE_STRING, + G_TYPE_LONG, + G_TYPE_LONG); + manager_signals[MESSAGE_CANCELLED] = + g_signal_new ("message_cancelled", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled), + NULL, NULL, + _na_marshal_VOID__OBJECT_LONG, + G_TYPE_NONE, 2, + GTK_TYPE_SOCKET, + G_TYPE_LONG); + manager_signals[LOST_SELECTION] = + g_signal_new ("lost_selection", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +#if defined (GDK_WINDOWING_X11) + /* Nothing */ +#elif defined (GDK_WINDOWING_WIN32) + g_warning ("Port NaTrayManager to Win32"); +#else + g_warning ("Port NaTrayManager to this GTK+ backend"); +#endif +} + +static void +na_tray_manager_finalize (GObject *object) +{ + NaTrayManager *manager; + + manager = NA_TRAY_MANAGER (object); + + na_tray_manager_unmanage (manager); + + g_list_free (manager->messages); + g_hash_table_destroy (manager->socket_table); + + G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object); +} + +static void +na_tray_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NaTrayManager *manager = NA_TRAY_MANAGER (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + na_tray_manager_set_orientation (manager, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +na_tray_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NaTrayManager *manager = NA_TRAY_MANAGER (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, manager->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +NaTrayManager * +na_tray_manager_new (void) +{ + NaTrayManager *manager; + + manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL); + + return manager; +} + +#ifdef GDK_WINDOWING_X11 + +static gboolean +na_tray_manager_plug_removed (GtkSocket *socket, + NaTrayManager *manager) +{ + Window *window; + + window = g_object_get_data (G_OBJECT (socket), "na-tray-child-window"); + + g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (*window)); + g_object_set_data (G_OBJECT (socket), "na-tray-child-window", + NULL); + + g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, socket); + + /* This destroys the socket. */ + return FALSE; +} + +static void +na_tray_manager_make_socket_transparent (GtkWidget *widget, + gpointer user_data) +{ + if (GTK_WIDGET_NO_WINDOW (widget)) + return; + + gdk_window_set_back_pixmap (widget->window, NULL, TRUE); +} + +static gboolean +na_tray_manager_socket_exposed (GtkWidget *widget, + GdkEventExpose *event, + gpointer user_data) +{ + gdk_window_clear_area (widget->window, + event->area.x, event->area.y, + event->area.width, event->area.height); + return FALSE; +} + +static void +na_tray_manager_socket_style_set (GtkWidget *widget, + GtkStyle *previous_style, + gpointer user_data) +{ + if (widget->window == NULL) + return; + + na_tray_manager_make_socket_transparent (widget, user_data); +} + +static void +na_tray_manager_handle_dock_request (NaTrayManager *manager, + XClientMessageEvent *xevent) +{ + GtkWidget *socket; + Window *window; + GtkRequisition req; + + if (g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->data.l[2]))) + { + /* We already got this notification earlier, ignore this one */ + return; + } + + socket = gtk_socket_new (); + + gtk_widget_set_app_paintable (socket, TRUE); + //FIXME: need to find a theme where this (and expose event) is needed + gtk_widget_set_double_buffered (socket, FALSE); + g_signal_connect (socket, "realize", + G_CALLBACK (na_tray_manager_make_socket_transparent), NULL); + g_signal_connect (socket, "expose_event", + G_CALLBACK (na_tray_manager_socket_exposed), NULL); + g_signal_connect_after (socket, "style_set", + G_CALLBACK (na_tray_manager_socket_style_set), NULL); + + /* We need to set the child window here + * so that the client can call _get functions + * in the signal handler + */ + window = g_new (Window, 1); + *window = xevent->data.l[2]; + + g_object_set_data_full (G_OBJECT (socket), + "na-tray-child-window", + window, g_free); + g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, + socket); + + /* Add the socket only if it's been attached */ + if (GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket)))) + { + g_signal_connect (socket, "plug_removed", + G_CALLBACK (na_tray_manager_plug_removed), manager); + + gtk_socket_add_id (GTK_SOCKET (socket), *window); + + g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (*window), socket); + + /* + * Make sure the icons have a meaningfull size ... + */ + req.width = req.height = 1; + gtk_widget_size_request (socket, &req); + /* + if ((req.width < 16) || (req.height < 16)) + { + gint nw = MAX (24, req.width); + gint nh = MAX (24, req.height); + g_warning (_("tray icon has requested a size of (%i x %i), resizing to (%i x %i)"), + req.width, req.height, nw, nh); + gtk_widget_set_size_request(icon, nw, nh); + } + */ + gtk_widget_show(socket); + } + else + gtk_widget_destroy (socket); +} + +static void +pending_message_free (PendingMessage *message) +{ + g_free (message->str); + g_free (message); +} + +static GdkFilterReturn +na_tray_manager_handle_client_message_message_data (GdkXEvent *xev, + GdkEvent *event, + gpointer data) +{ + XClientMessageEvent *xevent; + NaTrayManager *manager; + GList *p; + int len; + + xevent = (XClientMessageEvent *) xev; + manager = data; + + /* Try to see if we can find the pending message in the list */ + for (p = manager->messages; p; p = p->next) + { + PendingMessage *msg = p->data; + + if (xevent->window == msg->window) + { + /* Append the message */ + len = MIN (msg->remaining_len, 20); + + memcpy ((msg->str + msg->len - msg->remaining_len), + &xevent->data, len); + msg->remaining_len -= len; + + if (msg->remaining_len == 0) + { + GtkSocket *socket; + + socket = g_hash_table_lookup (manager->socket_table, + GINT_TO_POINTER (msg->window)); + + if (socket) + g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, + socket, msg->str, msg->id, msg->timeout); + + pending_message_free (msg); + manager->messages = g_list_remove_link (manager->messages, p); + g_list_free_1 (p); + } + + break; + } + } + + return GDK_FILTER_REMOVE; +} + +static void +na_tray_manager_handle_begin_message (NaTrayManager *manager, + XClientMessageEvent *xevent) +{ + GtkSocket *socket; + GList *p; + PendingMessage *msg; + long timeout; + long len; + long id; + + socket = g_hash_table_lookup (manager->socket_table, + GINT_TO_POINTER (xevent->window)); + /* we don't know about this tray icon, so ignore the message */ + if (!socket) + return; + + /* Check if the same message is already in the queue and remove it if so */ + for (p = manager->messages; p; p = p->next) + { + PendingMessage *msg = p->data; + + if (xevent->window == msg->window && + xevent->data.l[4] == msg->id) + { + /* Hmm, we found it, now remove it */ + pending_message_free (msg); + manager->messages = g_list_remove_link (manager->messages, p); + g_list_free_1 (p); + break; + } + } + + timeout = xevent->data.l[2]; + len = xevent->data.l[3]; + id = xevent->data.l[4]; + + if (len == 0) + { + g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, + socket, "", id, timeout); + } + else + { + /* Now add the new message to the queue */ + msg = g_new0 (PendingMessage, 1); + msg->window = xevent->window; + msg->timeout = timeout; + msg->len = len; + msg->id = id; + msg->remaining_len = msg->len; + msg->str = g_malloc (msg->len + 1); + msg->str[msg->len] = '\0'; + manager->messages = g_list_prepend (manager->messages, msg); + } +} + +static void +na_tray_manager_handle_cancel_message (NaTrayManager *manager, + XClientMessageEvent *xevent) +{ + GList *p; + GtkSocket *socket; + + /* Check if the message is in the queue and remove it if so */ + for (p = manager->messages; p; p = p->next) + { + PendingMessage *msg = p->data; + + if (xevent->window == msg->window && + xevent->data.l[4] == msg->id) + { + pending_message_free (msg); + manager->messages = g_list_remove_link (manager->messages, p); + g_list_free_1 (p); + break; + } + } + + socket = g_hash_table_lookup (manager->socket_table, + GINT_TO_POINTER (xevent->window)); + + if (socket) + { + g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0, + socket, xevent->data.l[2]); + } +} + +static GdkFilterReturn +na_tray_manager_handle_client_message_opcode (GdkXEvent *xev, + GdkEvent *event, + gpointer data) +{ + XClientMessageEvent *xevent; + NaTrayManager *manager; + + xevent = (XClientMessageEvent *) xev; + manager = data; + + switch (xevent->data.l[1]) + { + case SYSTEM_TRAY_REQUEST_DOCK: + /* Ignore this one since we don't know on which window this was received + * and so we can't know for which screen this is. It will be handled + * in na_tray_manager_window_filter() since we also receive it there */ + break; + + case SYSTEM_TRAY_BEGIN_MESSAGE: + na_tray_manager_handle_begin_message (manager, xevent); + return GDK_FILTER_REMOVE; + + case SYSTEM_TRAY_CANCEL_MESSAGE: + na_tray_manager_handle_cancel_message (manager, xevent); + return GDK_FILTER_REMOVE; + default: + break; + } + + return GDK_FILTER_CONTINUE; +} + +static GdkFilterReturn +na_tray_manager_window_filter (GdkXEvent *xev, + GdkEvent *event, + gpointer data) +{ + XEvent *xevent = (GdkXEvent *)xev; + NaTrayManager *manager = data; + + if (xevent->type == ClientMessage) + { + /* We handle this client message here. See comment in + * na_tray_manager_handle_client_message_opcode() for details */ + if (xevent->xclient.message_type == manager->opcode_atom && + xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) + { + na_tray_manager_handle_dock_request (manager, + (XClientMessageEvent *) xevent); + return GDK_FILTER_REMOVE; + } + } + else if (xevent->type == SelectionClear) + { + g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); + na_tray_manager_unmanage (manager); + } + + return GDK_FILTER_CONTINUE; +} + +#if 0 +//FIXME investigate why this doesn't work +static gboolean +na_tray_manager_selection_clear_event (GtkWidget *widget, + GdkEventSelection *event, + NaTrayManager *manager) +{ + g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); + na_tray_manager_unmanage (manager); + + return FALSE; +} +#endif +#endif + +static void +na_tray_manager_unmanage (NaTrayManager *manager) +{ +#ifdef GDK_WINDOWING_X11 + GdkDisplay *display; + guint32 timestamp; + GtkWidget *invisible; + + if (manager->invisible == NULL) + return; + + invisible = manager->invisible; + g_assert (GTK_IS_INVISIBLE (invisible)); + g_assert (GTK_WIDGET_REALIZED (invisible)); + g_assert (GDK_IS_WINDOW (invisible->window)); + + display = gtk_widget_get_display (invisible); + + if (gdk_selection_owner_get_for_display (display, manager->selection_atom) == + invisible->window) + { + timestamp = gdk_x11_get_server_time (invisible->window); + gdk_selection_owner_set_for_display (display, + NULL, + manager->selection_atom, + timestamp, + TRUE); + } + + //FIXME: we should also use gdk_remove_client_message_filter when it's + //available + // See bug #351254 + gdk_window_remove_filter (invisible->window, + na_tray_manager_window_filter, manager); + + manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ + gtk_widget_destroy (invisible); + g_object_unref (G_OBJECT (invisible)); +#endif +} + +static void +na_tray_manager_set_orientation_property (NaTrayManager *manager) +{ +#ifdef GDK_WINDOWING_X11 + GdkDisplay *display; + Atom orientation_atom; + gulong data[1]; + + if (!manager->invisible || !manager->invisible->window) + return; + + display = gtk_widget_get_display (manager->invisible); + orientation_atom = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_SYSTEM_TRAY_ORIENTATION"); + + data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ? + SYSTEM_TRAY_ORIENTATION_HORZ : + SYSTEM_TRAY_ORIENTATION_VERT; + + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_WINDOW_XWINDOW (manager->invisible->window), + orientation_atom, + XA_CARDINAL, 32, + PropModeReplace, + (guchar *) &data, 1); +#endif +} + +#ifdef GDK_WINDOWING_X11 + +static gboolean +na_tray_manager_manage_screen_x11 (NaTrayManager *manager, + GdkScreen *screen) +{ + GdkDisplay *display; + Screen *xscreen; + GtkWidget *invisible; + char *selection_atom_name; + guint32 timestamp; + + g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE); + g_return_val_if_fail (manager->screen == NULL, FALSE); + + /* If there's already a manager running on the screen + * we can't create another one. + */ +#if 0 + if (na_tray_manager_check_running_screen_x11 (screen)) + return FALSE; +#endif + display = gdk_screen_get_display (screen); + xscreen = GDK_SCREEN_XSCREEN (screen); + + invisible = gtk_invisible_new_for_screen (screen); + gtk_widget_realize (invisible); + + gtk_widget_add_events (invisible, + GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); + + selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", + gdk_screen_get_number (screen)); + manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE); + g_free (selection_atom_name); + + na_tray_manager_set_orientation_property (manager); + + timestamp = gdk_x11_get_server_time (invisible->window); + + /* Check if we could set the selection owner successfully */ + if (gdk_selection_owner_set_for_display (display, + invisible->window, + manager->selection_atom, + timestamp, + TRUE)) + { + XClientMessageEvent xev; + GdkAtom opcode_atom; + GdkAtom message_data_atom; + + xev.type = ClientMessage; + xev.window = RootWindowOfScreen (xscreen); + xev.message_type = gdk_x11_get_xatom_by_name_for_display (display, + "MANAGER"); + + xev.format = 32; + xev.data.l[0] = timestamp; + xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, + manager->selection_atom); + xev.data.l[2] = GDK_WINDOW_XWINDOW (invisible->window); + xev.data.l[3] = 0; /* manager specific data */ + xev.data.l[4] = 0; /* manager specific data */ + + XSendEvent (GDK_DISPLAY_XDISPLAY (display), + RootWindowOfScreen (xscreen), + False, StructureNotifyMask, (XEvent *)&xev); + + manager->invisible = invisible; + g_object_ref (G_OBJECT (manager->invisible)); + + opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE); + manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display, + opcode_atom); + + message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA", + FALSE); + + /* Add a window filter */ +#if 0 + /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */ + g_signal_connect (invisible, "selection-clear-event", + G_CALLBACK (na_tray_manager_selection_clear_event), + manager); +#endif + /* This is for SYSTEM_TRAY_REQUEST_DOCK and SelectionClear */ + gdk_window_add_filter (invisible->window, + na_tray_manager_window_filter, manager); + /* This is for SYSTEM_TRAY_BEGIN_MESSAGE and SYSTEM_TRAY_CANCEL_MESSAGE */ + gdk_display_add_client_message_filter (display, opcode_atom, + na_tray_manager_handle_client_message_opcode, + manager); + /* This is for _NET_SYSTEM_TRAY_MESSAGE_DATA */ + gdk_display_add_client_message_filter (display, message_data_atom, + na_tray_manager_handle_client_message_message_data, + manager); + return TRUE; + } + else + { + gtk_widget_destroy (invisible); + + return FALSE; + } +} + +#endif + +gboolean +na_tray_manager_manage_screen (NaTrayManager *manager, + GdkScreen *screen) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + g_return_val_if_fail (manager->screen == NULL, FALSE); + +#ifdef GDK_WINDOWING_X11 + return na_tray_manager_manage_screen_x11 (manager, screen); +#else + return FALSE; +#endif +} + +#ifdef GDK_WINDOWING_X11 + +static gboolean +na_tray_manager_check_running_screen_x11 (GdkScreen *screen) +{ + GdkDisplay *display; + Atom selection_atom; + char *selection_atom_name; + + display = gdk_screen_get_display (screen); + selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", + gdk_screen_get_number (screen)); + selection_atom = gdk_x11_get_xatom_by_name_for_display (display, + selection_atom_name); + g_free (selection_atom_name); + + if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), + selection_atom) != None) + return TRUE; + else + return FALSE; +} + +#endif + +gboolean +na_tray_manager_check_running (GdkScreen *screen) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + +#ifdef GDK_WINDOWING_X11 + return na_tray_manager_check_running_screen_x11 (screen); +#else + return FALSE; +#endif +} + +char * +na_tray_manager_get_child_title (NaTrayManager *manager, + NaTrayManagerChild *child) +{ + char *retval = NULL; +#ifdef GDK_WINDOWING_X11 + GdkDisplay *display; + Window *child_window; + Atom utf8_string, atom, type; + int result; + int format; + gulong nitems; + gulong bytes_after; + gchar *val; + + g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), NULL); + g_return_val_if_fail (GTK_IS_SOCKET (child), NULL); + + display = gdk_screen_get_display (manager->screen); + + child_window = g_object_get_data (G_OBJECT (child), + "na-tray-child-window"); + + utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"); + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"); + + gdk_error_trap_push (); + + result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + *child_window, + atom, + 0, G_MAXLONG, + False, utf8_string, + &type, &format, &nitems, + &bytes_after, (guchar **)&val); + + if (gdk_error_trap_pop () || result != Success) + return NULL; + + if (type != utf8_string || + format != 8 || + nitems == 0) + { + if (val) + XFree (val); + return NULL; + } + + if (!g_utf8_validate (val, nitems, NULL)) + { + XFree (val); + return NULL; + } + + retval = g_strndup (val, nitems); + + XFree (val); +#endif + return retval; +} + +void +na_tray_manager_set_orientation (NaTrayManager *manager, + GtkOrientation orientation) +{ + g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); + + if (manager->orientation != orientation) + { + manager->orientation = orientation; + + na_tray_manager_set_orientation_property (manager); + + g_object_notify (G_OBJECT (manager), "orientation"); + } +} + +GtkOrientation +na_tray_manager_get_orientation (NaTrayManager *manager) +{ + g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL); + + return manager->orientation; +} diff --git a/gui/simple-greeter/cutnpaste/na-tray-manager.h b/gui/simple-greeter/cutnpaste/na-tray-manager.h new file mode 100644 index 00000000..4d4e7880 --- /dev/null +++ b/gui/simple-greeter/cutnpaste/na-tray-manager.h @@ -0,0 +1,98 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* na-tray-manager.h + * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org> + * Copyright (C) 2003-2006 Vincent Untz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Used to be: eggtraymanager.h + */ + +#ifndef __NA_TRAY_MANAGER_H__ +#define __NA_TRAY_MANAGER_H__ + +#include <gtk/gtkwidget.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif + +G_BEGIN_DECLS + +#define NA_TYPE_TRAY_MANAGER (na_tray_manager_get_type ()) +#define NA_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManager)) +#define NA_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass)) +#define NA_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_MANAGER)) +#define NA_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_MANAGER)) +#define NA_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass)) + +typedef struct _NaTrayManager NaTrayManager; +typedef struct _NaTrayManagerClass NaTrayManagerClass; +typedef struct _NaTrayManagerChild NaTrayManagerChild; + +struct _NaTrayManager +{ + GObject parent_instance; + +#ifdef GDK_WINDOWING_X11 + GdkAtom selection_atom; + Atom opcode_atom; +#endif + + GtkWidget *invisible; + GdkScreen *screen; + GtkOrientation orientation; + + GList *messages; + GHashTable *socket_table; +}; + +struct _NaTrayManagerClass +{ + GObjectClass parent_class; + + void (* tray_icon_added) (NaTrayManager *manager, + NaTrayManagerChild *child); + void (* tray_icon_removed) (NaTrayManager *manager, + NaTrayManagerChild *child); + + void (* message_sent) (NaTrayManager *manager, + NaTrayManagerChild *child, + const gchar *message, + glong id, + glong timeout); + + void (* message_cancelled) (NaTrayManager *manager, + NaTrayManagerChild *child, + glong id); + + void (* lost_selection) (NaTrayManager *manager); +}; + +GType na_tray_manager_get_type (void); + +gboolean na_tray_manager_check_running (GdkScreen *screen); +NaTrayManager *na_tray_manager_new (void); +gboolean na_tray_manager_manage_screen (NaTrayManager *manager, + GdkScreen *screen); +char *na_tray_manager_get_child_title (NaTrayManager *manager, + NaTrayManagerChild *child); +void na_tray_manager_set_orientation (NaTrayManager *manager, + GtkOrientation orientation); +GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager); + +G_END_DECLS + +#endif /* __NA_TRAY_MANAGER_H__ */ diff --git a/gui/simple-greeter/cutnpaste/na-tray.c b/gui/simple-greeter/cutnpaste/na-tray.c new file mode 100644 index 00000000..9bfc19f2 --- /dev/null +++ b/gui/simple-greeter/cutnpaste/na-tray.c @@ -0,0 +1,679 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * Copyright (C) 2003-2006 Vincent Untz + * Copyright (C) 2007 Christian Persch + * + * 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 <string.h> + +#include <gtk/gtk.h> + +#include "na-tray-manager.h" +#include "fixedtip.h" +#include "obox.h" + +#include "na-tray.h" + +#define ICON_SPACING 1 +#define MIN_BOX_SIZE 3 + +typedef struct +{ + NaTrayManager *tray_manager; + GSList *all_trays; + GHashTable *icon_table; + GHashTable *tip_table; +} TraysScreen; + +struct _NaTrayPrivate +{ + GdkScreen *screen; + TraysScreen *trays_screen; + + GtkWidget *box; + GtkWidget *frame; + + guint idle_redraw_id; + + GtkOrientation orientation; +}; + +typedef struct +{ + char *text; + glong id; + glong timeout; +} IconTipBuffer; + +typedef struct +{ + NaTray *tray; /* tray containing the tray icon */ + GtkWidget *icon; /* tray icon sending the message */ + GtkWidget *fixedtip; + guint source_id; + glong id; /* id of the current message */ + GSList *buffer; /* buffered messages */ +} IconTip; + +enum +{ + PROP_0, + PROP_ORIENTATION, + PROP_SCREEN +}; + +static gboolean initialized = FALSE; +static TraysScreen *trays_screens = NULL; + +static void icon_tip_show_next (IconTip *icontip); + +G_DEFINE_TYPE (NaTray, na_tray, GTK_TYPE_BIN); + +static NaTray * +get_tray (TraysScreen *trays_screen) +{ + if (trays_screen->all_trays == NULL) + return NULL; + + return trays_screen->all_trays->data; +} + +static void +tray_added (NaTrayManager *manager, + GtkWidget *icon, + TraysScreen *trays_screen) +{ + NaTray *tray; + NaTrayPrivate *priv; + + tray = get_tray (trays_screen); + if (tray == NULL) + return; + + priv = tray->priv; + + g_assert (priv->trays_screen == trays_screen); + + g_hash_table_insert (trays_screen->icon_table, icon, tray); + + gtk_box_pack_end (GTK_BOX (priv->box), icon, FALSE, FALSE, 0); + + gtk_widget_show (icon); + na_tray_force_redraw (tray); +} + +static void +tray_removed (NaTrayManager *manager, + GtkWidget *icon, + TraysScreen *trays_screen) +{ + NaTray *tray; + + tray = g_hash_table_lookup (trays_screen->icon_table, icon); + if (tray == NULL) + return; + + g_assert (tray->priv->trays_screen == trays_screen); + + na_tray_force_redraw (tray); + + g_hash_table_remove (trays_screen->icon_table, icon); + /* this will also destroy the tip associated to this icon */ + g_hash_table_remove (trays_screen->tip_table, icon); +} + +static void +icon_tip_buffer_free (gpointer data, + gpointer userdata) +{ + IconTipBuffer *buffer; + + buffer = data; + + g_free (buffer->text); + buffer->text = NULL; + + g_free (buffer); +} + +static void +icon_tip_free (gpointer data) +{ + IconTip *icontip; + + if (data == NULL) + return; + + icontip = data; + + if (icontip->fixedtip != NULL) + gtk_widget_destroy (GTK_WIDGET (icontip->fixedtip)); + icontip->fixedtip = NULL; + + if (icontip->source_id != 0) + g_source_remove (icontip->source_id); + icontip->source_id = 0; + + if (icontip->buffer != NULL) + { + g_slist_foreach (icontip->buffer, icon_tip_buffer_free, NULL); + g_slist_free (icontip->buffer); + } + icontip->buffer = NULL; + + g_free (icontip); +} + +static int +icon_tip_buffer_compare (gconstpointer a, + gconstpointer b) +{ + const IconTipBuffer *buffer_a = a; + const IconTipBuffer *buffer_b = b; + + if (buffer_a == NULL || buffer_b == NULL) + return !(buffer_a == buffer_b); + + return buffer_a->id - buffer_b->id; +} + +static void +icon_tip_show_next_clicked (GtkWidget *widget, + gpointer data) +{ + icon_tip_show_next ((IconTip *) data); +} + +static gboolean +icon_tip_show_next_timeout (gpointer data) +{ + IconTip *icontip = (IconTip *) data; + + icon_tip_show_next (icontip); + + return FALSE; +} + +static void +icon_tip_show_next (IconTip *icontip) +{ + IconTipBuffer *buffer; + + if (icontip->buffer == NULL) + { + /* this will also destroy the tip window */ + g_hash_table_remove (icontip->tray->priv->trays_screen->tip_table, + icontip->icon); + return; + } + + if (icontip->source_id != 0) + g_source_remove (icontip->source_id); + icontip->source_id = 0; + + buffer = icontip->buffer->data; + icontip->buffer = g_slist_remove (icontip->buffer, buffer); + + if (icontip->fixedtip == NULL) + { + icontip->fixedtip = na_fixed_tip_new (icontip->icon, + na_tray_get_orientation (icontip->tray)); + + g_signal_connect (icontip->fixedtip, "clicked", + G_CALLBACK (icon_tip_show_next_clicked), icontip); + } + + na_fixed_tip_set_markup (icontip->fixedtip, buffer->text); + + if (!GTK_WIDGET_MAPPED (icontip->fixedtip)) + gtk_widget_show (icontip->fixedtip); + + icontip->id = buffer->id; + + if (buffer->timeout > 0) + icontip->source_id = g_timeout_add (buffer->timeout * 1000, + icon_tip_show_next_timeout, icontip); + + icon_tip_buffer_free (buffer, NULL); +} + +static void +message_sent (NaTrayManager *manager, + GtkWidget *icon, + const char *text, + glong id, + glong timeout, + TraysScreen *trays_screen) +{ + IconTip *icontip; + IconTipBuffer find_buffer; + IconTipBuffer *buffer; + gboolean show_now; + + icontip = g_hash_table_lookup (trays_screen->tip_table, icon); + + find_buffer.id = id; + if (icontip && + (icontip->id == id || + g_slist_find_custom (icontip->buffer, &find_buffer, + icon_tip_buffer_compare) != NULL)) + /* we already have this message, so ignore it */ + /* FIXME: in an ideal world, we'd remember all the past ids and ignore them + * too */ + return; + + show_now = FALSE; + + if (icontip == NULL) + { + NaTray *tray; + + tray = g_hash_table_lookup (trays_screen->icon_table, icon); + if (tray == NULL) + { + /* We don't know about the icon sending the message, so ignore it. + * But this should never happen since NaTrayManager shouldn't send + * us the message if there's no socket for it. */ + g_critical ("Ignoring a message sent by a tray icon " + "we don't know: \"%s\".\n", text); + return; + } + + icontip = g_new0 (IconTip, 1); + icontip->tray = tray; + icontip->icon = icon; + + g_hash_table_insert (trays_screen->tip_table, icon, icontip); + + show_now = TRUE; + } + + buffer = g_new0 (IconTipBuffer, 1); + + buffer->text = g_strdup (text); + buffer->id = id; + buffer->timeout = timeout; + + icontip->buffer = g_slist_append (icontip->buffer, buffer); + + if (show_now) + icon_tip_show_next (icontip); +} + +static void +message_cancelled (NaTrayManager *manager, + GtkWidget *icon, + glong id, + TraysScreen *trays_screen) +{ + IconTip *icontip; + IconTipBuffer find_buffer; + GSList *cancel_buffer_l; + IconTipBuffer *cancel_buffer; + + icontip = g_hash_table_lookup (trays_screen->tip_table, icon); + if (icontip == NULL) + return; + + if (icontip->id == id) + { + icon_tip_show_next (icontip); + return; + } + + find_buffer.id = id; + cancel_buffer_l = g_slist_find_custom (icontip->buffer, &find_buffer, + icon_tip_buffer_compare); + if (cancel_buffer_l == NULL) + return; + + cancel_buffer = cancel_buffer_l->data; + icon_tip_buffer_free (cancel_buffer, NULL); + + icontip->buffer = g_slist_remove_link (icontip->buffer, cancel_buffer_l); + g_slist_free_1 (cancel_buffer_l); +} + +static void +update_orientation_for_messages (gpointer key, + gpointer value, + gpointer data) +{ + NaTray *tray; + IconTip *icontip; + + if (value == NULL) + return; + + icontip = value; + tray = data; + if (icontip->tray != tray) + return; + + if (icontip->fixedtip) + na_fixed_tip_set_orientation (icontip->fixedtip, tray->priv->orientation); +} + +static void +update_size_and_orientation (NaTray *tray) +{ + NaTrayPrivate *priv = tray->priv; + + na_obox_set_orientation (NA_OBOX (priv->box), priv->orientation); + + /* This only happens when setting the property during object construction */ + if (!priv->trays_screen) + return; + + g_hash_table_foreach (priv->trays_screen->tip_table, + update_orientation_for_messages, tray); + + if (get_tray (priv->trays_screen) == tray) + na_tray_manager_set_orientation (priv->trays_screen->tray_manager, + priv->orientation); + + /* note, you want this larger if the frame has non-NONE relief by default. */ + switch (priv->orientation) + { + case GTK_ORIENTATION_VERTICAL: + /* Give box a min size so the frame doesn't look dumb */ + gtk_widget_set_size_request (priv->box, MIN_BOX_SIZE, -1); + break; + case GTK_ORIENTATION_HORIZONTAL: + gtk_widget_set_size_request (priv->box, -1, MIN_BOX_SIZE); + break; + } + + na_tray_force_redraw (tray); +} + +static void +na_tray_init (NaTray *tray) +{ + NaTrayPrivate *priv; + + priv = tray->priv = G_TYPE_INSTANCE_GET_PRIVATE (tray, NA_TYPE_TRAY, NaTrayPrivate); + + priv->screen = NULL; + priv->orientation = GTK_ORIENTATION_HORIZONTAL; + + priv->frame = gtk_alignment_new (0.5, 0.5, 1.0, 1.0); + gtk_container_add (GTK_CONTAINER (tray), priv->frame); + gtk_widget_show (priv->frame); + + priv->box = na_obox_new (); + gtk_box_set_spacing (GTK_BOX (priv->box), ICON_SPACING); + gtk_container_add (GTK_CONTAINER (priv->frame), priv->box); + gtk_widget_show (priv->box); +} + +static GObject * +na_tray_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + NaTray *tray; + NaTrayPrivate *priv; + int screen_number; + + object = G_OBJECT_CLASS (na_tray_parent_class)->constructor (type, + n_construct_properties, + construct_params); + tray = NA_TRAY (object); + priv = tray->priv; + + g_assert (priv->screen != NULL); + + if (!initialized) + { + GdkDisplay *display; + int n_screens; + + display = gdk_display_get_default (); + n_screens = gdk_display_get_n_screens (display); + trays_screens = g_new0 (TraysScreen, n_screens); + initialized = TRUE; + } + + screen_number = gdk_screen_get_number (priv->screen); + + if (trays_screens [screen_number].tray_manager == NULL) + { + NaTrayManager *tray_manager; + + tray_manager = na_tray_manager_new (); + + if (na_tray_manager_manage_screen (tray_manager, priv->screen)) + { + trays_screens [screen_number].tray_manager = tray_manager; + + g_signal_connect (tray_manager, "tray_icon_added", + G_CALLBACK (tray_added), + &trays_screens [screen_number]); + g_signal_connect (tray_manager, "tray_icon_removed", + G_CALLBACK (tray_removed), + &trays_screens [screen_number]); + g_signal_connect (tray_manager, "message_sent", + G_CALLBACK (message_sent), + &trays_screens [screen_number]); + g_signal_connect (tray_manager, "message_cancelled", + G_CALLBACK (message_cancelled), + &trays_screens [screen_number]); + + trays_screens [screen_number].icon_table = g_hash_table_new (NULL, + NULL); + trays_screens [screen_number].tip_table = g_hash_table_new_full ( + NULL, + NULL, + NULL, + icon_tip_free); + } + else + { + g_printerr ("System tray didn't get the system tray manager selection for screen %d\n", + screen_number); + g_object_unref (tray_manager); + } + } + + priv->trays_screen = &trays_screens [screen_number]; + trays_screens [screen_number].all_trays = g_slist_append (trays_screens [screen_number].all_trays, + tray); + + update_size_and_orientation (tray); + + return object; +} + +static void +na_tray_dispose (GObject *object) +{ + NaTray *tray = NA_TRAY (object); + NaTrayPrivate *priv = tray->priv; + TraysScreen *trays_screen = priv->trays_screen; + + if (trays_screen != NULL) + { + trays_screen->all_trays = g_slist_remove (trays_screen->all_trays, tray); + + if (trays_screen->all_trays == NULL) + { + /* Make sure we drop the manager selection */ + g_object_unref (trays_screen->tray_manager); + trays_screen->tray_manager = NULL; + + g_hash_table_destroy (trays_screen->icon_table); + trays_screen->icon_table = NULL; + + g_hash_table_destroy (trays_screen->tip_table); + trays_screen->tip_table = NULL; + } + else + { + NaTray *new_tray; + + new_tray = get_tray (trays_screen); + if (new_tray != NULL) + na_tray_manager_set_orientation (trays_screen->tray_manager, + na_tray_get_orientation (new_tray)); + } + } + + priv->trays_screen = NULL; + + if (priv->idle_redraw_id != 0) + { + g_source_remove (priv->idle_redraw_id); + priv->idle_redraw_id = 0; + } + + G_OBJECT_CLASS (na_tray_parent_class)->dispose (object); +} + +static void +na_tray_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NaTray *tray = NA_TRAY (object); + NaTrayPrivate *priv = tray->priv; + + switch (prop_id) + { + case PROP_ORIENTATION: + na_tray_set_orientation (tray, g_value_get_enum (value)); + break; + case PROP_SCREEN: + priv->screen = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +na_tray_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + gtk_widget_size_request (gtk_bin_get_child (GTK_BIN (widget)), requisition); +} + +static void +na_tray_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + gtk_widget_size_allocate (gtk_bin_get_child (GTK_BIN (widget)), allocation); +} + +static void +na_tray_class_init (NaTrayClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->constructor = na_tray_constructor; + gobject_class->set_property = na_tray_set_property; + gobject_class->dispose = na_tray_dispose; + + widget_class->size_request = na_tray_size_request; + widget_class->size_allocate = na_tray_size_allocate; + + g_object_class_install_property + (gobject_class, + PROP_ORIENTATION, + g_param_spec_enum ("orientation", "orientation", "orientation", + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (gobject_class, + PROP_SCREEN, + g_param_spec_object ("screen", "screen", "screen", + GDK_TYPE_SCREEN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (gobject_class, sizeof (NaTrayPrivate)); +} + +NaTray * +na_tray_new_for_screen (GdkScreen *screen, + GtkOrientation orientation) +{ + return g_object_new (NA_TYPE_TRAY, + "screen", screen, + "orientation", orientation, + NULL); +} + +void +na_tray_set_orientation (NaTray *tray, + GtkOrientation orientation) +{ + NaTrayPrivate *priv = tray->priv; + + if (orientation == priv->orientation) + return; + + priv->orientation = orientation; + + update_size_and_orientation (tray); +} + +GtkOrientation +na_tray_get_orientation (NaTray *tray) +{ + return tray->priv->orientation; +} + +static gboolean +idle_redraw_cb (NaTray *tray) +{ + NaTrayPrivate *priv = tray->priv; + + priv->idle_redraw_id = 0; + gtk_widget_hide (priv->box); + gtk_widget_show (priv->box); + + return FALSE; +} + +void +na_tray_force_redraw (NaTray *tray) +{ + NaTrayPrivate *priv = tray->priv; + + /* Force the icons to redraw their backgrounds. + * gtk_widget_queue_draw() doesn't work across process boundaries, + * so we do this instead. + */ + if (priv->idle_redraw_id == 0) + priv->idle_redraw_id = g_idle_add ((GSourceFunc) idle_redraw_cb, tray); +} diff --git a/gui/simple-greeter/cutnpaste/na-tray.h b/gui/simple-greeter/cutnpaste/na-tray.h new file mode 100644 index 00000000..db6f058a --- /dev/null +++ b/gui/simple-greeter/cutnpaste/na-tray.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* na-tray-tray.h + * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org> + * Copyright (C) 2003-2006 Vincent Untz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Used to be: eggtraytray.h + */ + +#ifndef __NA_TRAY_H__ +#define __NA_TRAY_H__ + +#include <gtk/gtkwidget.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif + +G_BEGIN_DECLS + +#define NA_TYPE_TRAY (na_tray_get_type ()) +#define NA_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY, NaTray)) +#define NA_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY, NaTrayClass)) +#define NA_IS_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY)) +#define NA_IS_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY)) +#define NA_TRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY, NaTrayClass)) + +typedef struct _NaTray NaTray; +typedef struct _NaTrayPrivate NaTrayPrivate; +typedef struct _NaTrayClass NaTrayClass; +typedef struct _NaTrayChild NaTrayChild; + +struct _NaTray +{ + GtkBin parent_instance; + + NaTrayPrivate *priv; +}; + +struct _NaTrayClass +{ + GtkBinClass parent_class; +}; + +GType na_tray_get_type (void); +NaTray *na_tray_new_for_screen (GdkScreen *screen, + GtkOrientation orientation); +void na_tray_set_orientation (NaTray *tray, + GtkOrientation orientation); +GtkOrientation na_tray_get_orientation (NaTray *tray); +void na_tray_force_redraw (NaTray *tray); + +G_END_DECLS + +#endif /* __NA_TRAY_H__ */ diff --git a/gui/simple-greeter/cutnpaste/obox.c b/gui/simple-greeter/cutnpaste/obox.c new file mode 100644 index 00000000..96e4265c --- /dev/null +++ b/gui/simple-greeter/cutnpaste/obox.c @@ -0,0 +1,130 @@ +/* OBox Copyright (C) 2002 Red Hat Inc. based on GtkHBox */ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "obox.h" + +#include <gtk/gtkhbox.h> +#include <gtk/gtkvbox.h> + +static void na_obox_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void na_obox_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + + +G_DEFINE_TYPE (NaOBox, na_obox, GTK_TYPE_BOX) + +static void +na_obox_class_init (NaOBoxClass *class) +{ + GtkWidgetClass *widget_class; + + widget_class = (GtkWidgetClass*) class; + + widget_class->size_request = na_obox_size_request; + widget_class->size_allocate = na_obox_size_allocate; +} + +static void +na_obox_init (NaOBox *obox) +{ + obox->orientation = GTK_ORIENTATION_HORIZONTAL; +} + +GtkWidget* +na_obox_new (void) +{ + NaOBox *obox; + + obox = g_object_new (NA_TYPE_OBOX, NULL); + + return GTK_WIDGET (obox); +} + +static GtkWidgetClass* +get_class (NaOBox *obox) +{ + GtkWidgetClass *klass; + + switch (obox->orientation) + { + case GTK_ORIENTATION_HORIZONTAL: + klass = GTK_WIDGET_CLASS (gtk_type_class (GTK_TYPE_HBOX)); + break; + case GTK_ORIENTATION_VERTICAL: + klass = GTK_WIDGET_CLASS (gtk_type_class (GTK_TYPE_VBOX)); + break; + default: + g_assert_not_reached (); + klass = NULL; + break; + } + + return klass; +} + +static void +na_obox_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkWidgetClass *klass; + NaOBox *obox; + + obox = NA_OBOX (widget); + + klass = get_class (obox); + + klass->size_request (widget, requisition); +} + +static void +na_obox_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkWidgetClass *klass; + NaOBox *obox; + + obox = NA_OBOX (widget); + + klass = get_class (obox); + + klass->size_allocate (widget, allocation); +} + +void +na_obox_set_orientation (NaOBox *obox, + GtkOrientation orientation) +{ + g_return_if_fail (NA_IS_OBOX (obox)); + + if (obox->orientation == orientation) + return; + + obox->orientation = orientation; + + gtk_widget_queue_resize (GTK_WIDGET (obox)); +} diff --git a/gui/simple-greeter/cutnpaste/obox.h b/gui/simple-greeter/cutnpaste/obox.h new file mode 100644 index 00000000..5995da46 --- /dev/null +++ b/gui/simple-greeter/cutnpaste/obox.h @@ -0,0 +1,73 @@ +/* OBox Copyright (C) 2002 Red Hat Inc. based on GtkHBox */ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __NA_OBOX_H__ +#define __NA_OBOX_H__ + +#include <gtk/gtkbox.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define NA_TYPE_OBOX (na_obox_get_type ()) +#define NA_OBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_OBOX, NaOBox)) +#define NA_OBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_OBOX, NaOBoxClass)) +#define NA_IS_OBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_OBOX)) +#define NA_IS_OBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_OBOX)) +#define NA_OBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_OBOX, NaOBoxClass)) + + +typedef struct _NaOBox NaOBox; +typedef struct _NaOBoxClass NaOBoxClass; + +struct _NaOBox +{ + GtkBox box; + + GtkOrientation orientation; +}; + +struct _NaOBoxClass +{ + GtkBoxClass parent_class; +}; + + +GType na_obox_get_type (void) G_GNUC_CONST; +GtkWidget* na_obox_new (void); + +void na_obox_set_orientation (NaOBox *obox, + GtkOrientation orientation); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __NA_OBOX_H__ */ diff --git a/gui/simple-greeter/cutnpaste/testtray.c b/gui/simple-greeter/cutnpaste/testtray.c new file mode 100644 index 00000000..42d37329 --- /dev/null +++ b/gui/simple-greeter/cutnpaste/testtray.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org> + * Copyright (C) 2003-2006 Vincent Untz + * Copyright (C) 2006, 2007 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <gtk/gtk.h> +#include "na-tray-manager.h" +#include "na-tray.h" + +#define NOTIFICATION_AREA_ICON "gnome-panel-notification-area" + +static guint n_windows = 0; + +typedef struct +{ + GdkScreen *screen; + guint screen_num; + GtkWidget *window; + NaTray *tray; + GtkWidget *box; + GtkLabel *count_label; +} TrayData; + +static void +do_add (GtkWidget *child, guint *n_children) +{ + *n_children += 1; +} + +static void +update_child_count (TrayData *data) +{ + guint n_children = 0; + char text[64]; + + if (!GTK_WIDGET_REALIZED (data->window)) + return; + + gtk_container_foreach (GTK_CONTAINER (data->box), (GtkCallback) do_add, &n_children); + + g_snprintf (text, sizeof (text), "%u icons", n_children); + gtk_label_set_text (data->count_label, text); +} + +static void +tray_added_cb (GtkContainer *box, GtkWidget *icon, TrayData *data) +{ + g_print ("[Screen %u tray %p] Child %p added to tray: \"%s\"\n", + data->screen_num, data->tray, icon, "XXX");//na_tray_manager_get_child_title (manager, icon)); + + update_child_count (data); +} + +static void +tray_removed_cb (GtkContainer *box, GtkWidget *icon, TrayData *data) +{ + g_print ("[Screen %u tray %p] Child %p removed from tray\n", + data->screen_num, data->tray, icon); + + update_child_count (data); +} + +static void orientation_changed_cb (GtkComboBox *combo, TrayData *data) +{ + GtkOrientation orientation = (GtkOrientation) gtk_combo_box_get_active (combo); + + g_print ("[Screen %u tray %p] Setting orientation to \"%s\"\n", + data->screen_num, data->tray, orientation == 0 ? "horizontal" : "vertical"); + + na_tray_set_orientation (data->tray, orientation); +} + +static void +maybe_quit (gpointer data, + GObject *zombie) +{ + if (--n_windows == 0) { + gtk_main_quit (); + } +} + +static TrayData *create_tray_on_screen (GdkScreen *screen, gboolean force); + +static void +warning_dialog_response_cb (GtkWidget *dialog, + gint response, + GdkScreen *screen) +{ + if (response == GTK_RESPONSE_YES) { + create_tray_on_screen (screen, TRUE); + } + + gtk_widget_destroy (dialog); +} + +static void +add_tray_cb (GtkWidget *button, TrayData *data) +{ + create_tray_on_screen (data->screen, TRUE); +} + +static TrayData * +create_tray_on_screen (GdkScreen *screen, + gboolean force) +{ + GtkWidget *window, *hbox, *vbox, *button, *combo, *label; + TrayData *data; + + n_windows++; + + if (!force && na_tray_manager_check_running (screen)) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, + "Override tray manager?"); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "There is already a tray manager running on screen %d.", + gdk_screen_get_number (screen)); + gtk_window_set_screen (GTK_WINDOW (dialog), screen); + g_signal_connect (dialog, "response", G_CALLBACK (warning_dialog_response_cb), screen); + gtk_window_present (GTK_WINDOW (dialog)); + g_object_weak_ref (G_OBJECT (dialog), (GWeakNotify) maybe_quit, NULL); + return NULL; + } + + data = g_new0 (TrayData, 1); + data->screen = screen; + data->screen_num = gdk_screen_get_number (screen); + + data->window = window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_object_weak_ref (G_OBJECT (window), (GWeakNotify) maybe_quit, NULL); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (window), vbox); + + button = gtk_button_new_with_mnemonic ("_Add another tray"); + g_signal_connect (button, "clicked", G_CALLBACK (add_tray_cb), data); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new_with_mnemonic ("_Orientation:"); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + combo = gtk_combo_box_new_text (); + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "Horizontal"); + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "Vertical"); + g_signal_connect (combo, "changed", + G_CALLBACK (orientation_changed_cb), data); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + + label = gtk_label_new (NULL); + data->count_label = GTK_LABEL (label); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + data->tray = na_tray_new_for_screen (screen, GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (data->tray), TRUE, TRUE, 0); + + data->box = GTK_BIN (GTK_BIN (data->tray)->child)->child; + g_signal_connect_after (data->box, "add", G_CALLBACK (tray_added_cb), data); + g_signal_connect_after (data->box, "remove", G_CALLBACK (tray_removed_cb), data); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); + + gtk_window_set_screen (GTK_WINDOW (window), screen); + gtk_window_set_default_size (GTK_WINDOW (window), -1, 200); + + /* gtk_window_set_resizable (GTK_WINDOW (window), FALSE); */ + + gtk_widget_show_all (window); + + update_child_count (data); + + return data; +} + +int +main (int argc, char *argv[]) +{ + GdkDisplay *display; + GdkScreen *screen; + int n_screens, i; + + gtk_init (&argc, &argv); + + gtk_window_set_default_icon_name (NOTIFICATION_AREA_ICON); + + display = gdk_display_get_default (); + n_screens = gdk_display_get_n_screens (display); + for (i = 0; i < n_screens; ++i) { + screen = gdk_display_get_screen (display, i); + + create_tray_on_screen (screen, FALSE); + } + + gtk_main (); + + return 0; +} diff --git a/gui/simple-greeter/gdm-greeter-panel.c b/gui/simple-greeter/gdm-greeter-panel.c new file mode 100644 index 00000000..a96b4095 --- /dev/null +++ b/gui/simple-greeter/gdm-greeter-panel.c @@ -0,0 +1,435 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * 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 "gdm-greeter-panel.h" + +#include "na-tray.h" + +#define GDM_GREETER_PANEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_PANEL, GdmGreeterPanelPrivate)) + +struct GdmGreeterPanelPrivate +{ + int monitor; + GdkRectangle geometry; + GtkWidget *hbox; +}; + +enum { + PROP_0, +}; + +static void gdm_greeter_panel_class_init (GdmGreeterPanelClass *klass); +static void gdm_greeter_panel_init (GdmGreeterPanel *greeter_panel); +static void gdm_greeter_panel_finalize (GObject *object); + +G_DEFINE_TYPE (GdmGreeterPanel, gdm_greeter_panel, GTK_TYPE_WINDOW) + +static void +gdm_greeter_panel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmGreeterPanel *self; + + self = GDM_GREETER_PANEL (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_greeter_panel_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmGreeterPanel *self; + + self = GDM_GREETER_PANEL (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +gdm_greeter_panel_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GdmGreeterPanel *greeter_panel; + GdmGreeterPanelClass *klass; + + klass = GDM_GREETER_PANEL_CLASS (g_type_class_peek (GDM_TYPE_GREETER_PANEL)); + + greeter_panel = GDM_GREETER_PANEL (G_OBJECT_CLASS (gdm_greeter_panel_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (greeter_panel); +} + +static void +gdm_greeter_panel_dispose (GObject *object) +{ + GdmGreeterPanel *greeter_panel; + + greeter_panel = GDM_GREETER_PANEL (object); + + g_debug ("Disposing greeter_panel"); + + G_OBJECT_CLASS (gdm_greeter_panel_parent_class)->dispose (object); +} + +/* copied from panel-toplevel.c */ +static void +gdm_greeter_panel_move_resize_window (GdmGreeterPanel *panel, + gboolean move, + gboolean resize) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (panel); + + g_assert (GTK_WIDGET_REALIZED (widget)); + + if (move && resize) { + gdk_window_move_resize (widget->window, + panel->priv->geometry.x, + panel->priv->geometry.y, + panel->priv->geometry.width, + panel->priv->geometry.height); + } else if (move) { + gdk_window_move (widget->window, + panel->priv->geometry.x, + panel->priv->geometry.y); + } else if (resize) { + gdk_window_resize (widget->window, + panel->priv->geometry.width, + panel->priv->geometry.height); + } +} + +static void +on_screen_size_changed (GdkScreen *screen, + GdmGreeterPanel *panel) +{ + gtk_widget_queue_resize (GTK_WIDGET (panel)); +} + +static void +gdm_greeter_panel_real_realize (GtkWidget *widget) +{ + if (GTK_WIDGET_CLASS (gdm_greeter_panel_parent_class)->realize) { + GTK_WIDGET_CLASS (gdm_greeter_panel_parent_class)->realize (widget); + } + + gdm_greeter_panel_move_resize_window (GDM_GREETER_PANEL (widget), TRUE, TRUE); + + g_signal_connect (gtk_window_get_screen (GTK_WINDOW (widget)), + "size_changed", + G_CALLBACK (on_screen_size_changed), + widget); +} + +static void +gdm_greeter_panel_real_unrealize (GtkWidget *widget) +{ + g_signal_handlers_disconnect_by_func (gtk_window_get_screen (GTK_WINDOW (widget)), + on_screen_size_changed, + widget); + + if (GTK_WIDGET_CLASS (gdm_greeter_panel_parent_class)->unrealize) { + GTK_WIDGET_CLASS (gdm_greeter_panel_parent_class)->unrealize (widget); + } +} + +static GdkRegion * +get_outside_region (GdmGreeterPanel *panel) +{ + int i; + GdkRegion *region; + + region = gdk_region_new (); + for (i = 0; i < panel->priv->monitor; i++) { + GdkRectangle geometry; + + gdk_screen_get_monitor_geometry (GTK_WINDOW (panel)->screen, + i, + &geometry); + gdk_region_union_with_rect (region, &geometry); + } + + return region; +} + +static void +get_monitor_geometry (GdmGreeterPanel *panel, + GdkRectangle *geometry) +{ + GdkRegion *outside_region; + GdkRegion *monitor_region; + GdkRectangle geom; + + outside_region = get_outside_region (panel); + + gdk_screen_get_monitor_geometry (GTK_WINDOW (panel)->screen, + panel->priv->monitor, + &geom); + monitor_region = gdk_region_rectangle (&geom); + gdk_region_subtract (monitor_region, outside_region); + gdk_region_destroy (outside_region); + + gdk_region_get_clipbox (monitor_region, geometry); + gdk_region_destroy (monitor_region); +} + +static void +set_struts (GdmGreeterPanel *panel, + int x, + int y, + int width, + int height) +{ + gulong data[12] = { 0, }; + int screen_height; + + /* _NET_WM_STRUT_PARTIAL: CARDINAL[12]/32 + * + * 0: left 1: right 2: top 3: bottom + * 4: left_start_y 5: left_end_y 6: right_start_y 7: right_end_y + * 8: top_start_x 9: top_end_x 10: bottom_start_x 11: bottom_end_x + * + * Note: In xinerama use struts relative to combined screen dimensions, + * not just the current monitor. + */ + + /* for bottom panel */ + screen_height = gdk_screen_get_height (gtk_window_get_screen (GTK_WINDOW (panel))); + + /* bottom */ + data[3] = screen_height - panel->priv->geometry.y - panel->priv->geometry.height + height; + /* bottom_start_x */ + data[10] = x; + /* bottom_end_x */ + data[11] = x + width; + + g_debug ("Setting strut: bottom=%lu bottom_start_x=%lu bottom_end_x=%lu", data[3], data[10], data[11]); + + gdk_error_trap_push (); + + gdk_property_change (GTK_WIDGET (panel)->window, + gdk_atom_intern ("_NET_WM_STRUT_PARTIAL", FALSE), + gdk_atom_intern ("CARDINAL", FALSE), + 32, + GDK_PROP_MODE_REPLACE, + (guchar *) &data, + 12); + + gdk_property_change (GTK_WIDGET (panel)->window, + gdk_atom_intern ("_NET_WM_STRUT", FALSE), + gdk_atom_intern ("CARDINAL", FALSE), + 32, + GDK_PROP_MODE_REPLACE, + (guchar *) &data, + 4); + + gdk_error_trap_pop (); +} + +static void +update_struts (GdmGreeterPanel *panel) +{ + GdkRectangle geometry; + + get_monitor_geometry (panel, &geometry); + + /* FIXME: assumes only one panel */ + set_struts (panel, + panel->priv->geometry.x, + panel->priv->geometry.y, + panel->priv->geometry.width, + panel->priv->geometry.height); +} + +static void +update_geometry (GdmGreeterPanel *panel, + GtkRequisition *requisition) +{ + GdkRectangle geometry; + int height; + + get_monitor_geometry (panel, &geometry); + + height = requisition->height; + panel->priv->geometry.width = geometry.width; + panel->priv->geometry.height = height * 2; + + panel->priv->geometry.x = geometry.x; + panel->priv->geometry.y = geometry.y + geometry.height - panel->priv->geometry.height; + + g_debug ("Setting geometry x:%d y:%d w:%d h:%d", + panel->priv->geometry.x, + panel->priv->geometry.y, + panel->priv->geometry.width, + panel->priv->geometry.height); + + update_struts (panel); +} + +static void +gdm_greeter_panel_real_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GdmGreeterPanel *panel; + GtkBin *bin; + GdkRectangle old_geometry; + int position_changed = FALSE; + int size_changed = FALSE; + + panel = GDM_GREETER_PANEL (widget); + bin = GTK_BIN (widget); + + if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { + gtk_widget_size_request (bin->child, requisition); + } + + old_geometry = panel->priv->geometry; + + update_geometry (panel, requisition); + + requisition->width = panel->priv->geometry.width; + requisition->height = panel->priv->geometry.height; + + if (! GTK_WIDGET_REALIZED (widget)) { + return; + } + + if (old_geometry.width != panel->priv->geometry.width || + old_geometry.height != panel->priv->geometry.height) { + size_changed = TRUE; + } + + if (old_geometry.x != panel->priv->geometry.x || + old_geometry.y != panel->priv->geometry.y) { + position_changed = TRUE; + } + + gdm_greeter_panel_move_resize_window (panel, position_changed, size_changed); +} + +static void +gdm_greeter_panel_class_init (GdmGreeterPanelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = gdm_greeter_panel_get_property; + object_class->set_property = gdm_greeter_panel_set_property; + object_class->constructor = gdm_greeter_panel_constructor; + object_class->dispose = gdm_greeter_panel_dispose; + object_class->finalize = gdm_greeter_panel_finalize; + + widget_class->realize = gdm_greeter_panel_real_realize; + widget_class->unrealize = gdm_greeter_panel_real_unrealize; + widget_class->size_request = gdm_greeter_panel_real_size_request; + + g_type_class_add_private (klass, sizeof (GdmGreeterPanelPrivate)); +} + +static void +gdm_greeter_panel_init (GdmGreeterPanel *panel) +{ + GtkWidget *label; + GtkWidget *tray; + + panel->priv = GDM_GREETER_PANEL_GET_PRIVATE (panel); + + panel->priv->geometry.x = -1; + panel->priv->geometry.y = -1; + panel->priv->geometry.width = -1; + panel->priv->geometry.height = -1; + + gtk_window_set_decorated (GTK_WINDOW (panel), FALSE); + + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (panel), TRUE); + gtk_window_set_skip_pager_hint (GTK_WINDOW (panel), TRUE); + + gtk_window_set_keep_above (GTK_WINDOW (panel), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (panel), GDK_WINDOW_TYPE_HINT_DOCK); + + panel->priv->hbox = gtk_hbox_new (FALSE, 12); + gtk_widget_show (panel->priv->hbox); + gtk_container_add (GTK_CONTAINER (panel), panel->priv->hbox); + + label = gtk_label_new (g_get_host_name ()); + gtk_box_pack_start (GTK_BOX (panel->priv->hbox), label, FALSE, FALSE, 6); + gtk_widget_show (label); + + tray = na_tray_new_for_screen (gtk_window_get_screen (GTK_WINDOW (panel)), + GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_end (GTK_BOX (panel->priv->hbox), tray, FALSE, FALSE, 6); + gtk_widget_show (tray); + +} + +static void +gdm_greeter_panel_finalize (GObject *object) +{ + GdmGreeterPanel *greeter_panel; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_GREETER_PANEL (object)); + + greeter_panel = GDM_GREETER_PANEL (object); + + g_return_if_fail (greeter_panel->priv != NULL); + + G_OBJECT_CLASS (gdm_greeter_panel_parent_class)->finalize (object); +} + +GtkWidget * +gdm_greeter_panel_new (void) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_GREETER_PANEL, + NULL); + + return GTK_WIDGET (object); +} diff --git a/gui/simple-greeter/gdm-greeter-panel.h b/gui/simple-greeter/gdm-greeter-panel.h new file mode 100644 index 00000000..21e2848e --- /dev/null +++ b/gui/simple-greeter/gdm-greeter-panel.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * 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. + * + */ + +#ifndef __GDM_GREETER_PANEL_H +#define __GDM_GREETER_PANEL_H + +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_GREETER_PANEL (gdm_greeter_panel_get_type ()) +#define GDM_GREETER_PANEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_GREETER_PANEL, GdmGreeterPanel)) +#define GDM_GREETER_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_GREETER_PANEL, GdmGreeterPanelClass)) +#define GDM_IS_GREETER_PANEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_GREETER_PANEL)) +#define GDM_IS_GREETER_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_GREETER_PANEL)) +#define GDM_GREETER_PANEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_GREETER_PANEL, GdmGreeterPanelClass)) + +typedef struct GdmGreeterPanelPrivate GdmGreeterPanelPrivate; + +typedef struct +{ + GtkWindow parent; + GdmGreeterPanelPrivate *priv; +} GdmGreeterPanel; + +typedef struct +{ + GtkWindowClass parent_class; +} GdmGreeterPanelClass; + +GType gdm_greeter_panel_get_type (void); + +GtkWidget * gdm_greeter_panel_new (void); + +G_END_DECLS + +#endif /* __GDM_GREETER_PANEL_H */ diff --git a/gui/simple-greeter/gdm-simple-greeter.c b/gui/simple-greeter/gdm-simple-greeter.c index 92e02558..66eedfb1 100644 --- a/gui/simple-greeter/gdm-simple-greeter.c +++ b/gui/simple-greeter/gdm-simple-greeter.c @@ -48,6 +48,8 @@ #include "gdm-settings-client.h" #include "gdm-settings-keys.h" +#include "gdm-greeter-panel.h" + #if HAVE_PAM #include <security/pam_appl.h> #define PW_ENTRY_SIZE PAM_MAX_RESP_SIZE @@ -71,7 +73,8 @@ enum { struct GdmSimpleGreeterPrivate { - GladeXML *xml; + GladeXML *xml; + GtkWidget *panel; }; enum { @@ -136,10 +139,10 @@ set_sensitive (GdmSimpleGreeter *greeter, { GtkWidget *box; - box = glade_xml_get_widget (greeter->priv->xml, "input-box"); + box = glade_xml_get_widget (greeter->priv->xml, "auth-input-box"); gtk_widget_set_sensitive (box, sensitive); - box = glade_xml_get_widget (greeter->priv->xml, "button-box"); + box = glade_xml_get_widget (greeter->priv->xml, "auth-button-box"); gtk_widget_set_sensitive (box, sensitive); } @@ -149,7 +152,7 @@ set_message (GdmSimpleGreeter *greeter, { GtkWidget *label; - label = glade_xml_get_widget (greeter->priv->xml, "message-label"); + label = glade_xml_get_widget (greeter->priv->xml, "auth-message-label"); gtk_label_set_text (GTK_LABEL (label), text); } @@ -423,22 +426,17 @@ create_greeter (GdmSimpleGreeter *greeter) /* FIXME: */ } - button = glade_xml_get_widget (greeter->priv->xml, "ok-button"); + button = glade_xml_get_widget (greeter->priv->xml, "auth-ok-button"); if (dialog != NULL) { gtk_widget_grab_default (button); g_signal_connect (button, "clicked", G_CALLBACK (ok_button_clicked), greeter); } - button = glade_xml_get_widget (greeter->priv->xml, "cancel-button"); + button = glade_xml_get_widget (greeter->priv->xml, "auth-cancel-button"); if (dialog != NULL) { g_signal_connect (button, "clicked", G_CALLBACK (cancel_button_clicked), greeter); } - button = glade_xml_get_widget (greeter->priv->xml, "suspend-button"); - if (dialog != NULL) { - g_signal_connect (button, "clicked", G_CALLBACK (suspend_button_clicked), greeter); - } - gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ALWAYS); gtk_window_set_deletable (GTK_WINDOW (dialog), FALSE); gtk_window_set_decorated (GTK_WINDOW (dialog), FALSE); @@ -447,6 +445,13 @@ create_greeter (GdmSimpleGreeter *greeter) set_busy (greeter); } +static void +create_panel (GdmSimpleGreeter *greeter) +{ + greeter->priv->panel = gdm_greeter_panel_new (); + gtk_widget_show (greeter->priv->panel); +} + static GObject * gdm_simple_greeter_constructor (GType type, guint n_construct_properties, @@ -461,6 +466,7 @@ gdm_simple_greeter_constructor (GType type, n_construct_properties, construct_properties)); create_greeter (greeter); + create_panel (greeter); return G_OBJECT (greeter); } diff --git a/gui/simple-greeter/gdm-simple-greeter.glade b/gui/simple-greeter/gdm-simple-greeter.glade index c3aeb6aa..5e22069d 100644 --- a/gui/simple-greeter/gdm-simple-greeter.glade +++ b/gui/simple-greeter/gdm-simple-greeter.glade @@ -21,13 +21,13 @@ <property name="urgency_hint">False</property> <child> - <widget class="GtkVBox" id="vbox1"> + <widget class="GtkVBox" id="auth-vbox"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">12</property> <child> - <widget class="GtkHBox" id="input-box"> + <widget class="GtkHBox" id="auth-input-box"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">6</property> @@ -85,7 +85,7 @@ </child> <child> - <widget class="GtkLabel" id="message-label"> + <widget class="GtkLabel" id="auth-message-label"> <property name="visible">True</property> <property name="label" translatable="yes"></property> <property name="use_underline">False</property> @@ -110,25 +110,13 @@ </child> <child> - <widget class="GtkHButtonBox" id="button-box"> + <widget class="GtkHButtonBox" id="auth-button-box"> <property name="visible">True</property> <property name="layout_style">GTK_BUTTONBOX_END</property> <property name="spacing">6</property> <child> - <widget class="GtkButton" id="suspend-button"> - <property name="visible">True</property> - <property name="can_default">True</property> - <property name="can_focus">True</property> - <property name="label">Suspend</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - </widget> - </child> - - <child> - <widget class="GtkButton" id="cancel-button"> + <widget class="GtkButton" id="auth-cancel-button"> <property name="visible">True</property> <property name="can_default">True</property> <property name="can_focus">True</property> @@ -140,7 +128,7 @@ </child> <child> - <widget class="GtkButton" id="ok-button"> + <widget class="GtkButton" id="auth-ok-button"> <property name="visible">True</property> <property name="can_default">True</property> <property name="can_focus">True</property> @@ -160,5 +148,4 @@ </widget> </child> </widget> - </glade-interface> diff --git a/gui/simple-greeter/greeter-main.c b/gui/simple-greeter/greeter-main.c index 4fa3c569..d1100c8a 100644 --- a/gui/simple-greeter/greeter-main.c +++ b/gui/simple-greeter/greeter-main.c @@ -266,15 +266,13 @@ get_display_id (void) GError *error; char *id; - g_debug ("GREETER disconnected"); - id = NULL; error = NULL; res = dbus_g_proxy_call (server_proxy, "GetDisplayId", &error, G_TYPE_INVALID, - G_TYPE_STRING, &id, + DBUS_TYPE_G_OBJECT_PATH, &id, G_TYPE_INVALID); if (! res) { g_warning ("Unable to GetDisplayId: %s", error->message); diff --git a/gui/simple-greeter/test-greeter-panel.c b/gui/simple-greeter/test-greeter-panel.c new file mode 100644 index 00000000..ba9a0595 --- /dev/null +++ b/gui/simple-greeter/test-greeter-panel.c @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * 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 <libintl.h> +#include <locale.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "gdm-greeter-panel.h" + +int +main (int argc, char *argv[]) +{ + GtkWidget *panel; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + setlocale (LC_ALL, ""); + + gtk_init (&argc, &argv); + + panel = gdm_greeter_panel_new (); + gtk_widget_show (panel); + + gtk_main (); + + return 0; +} |