--- a/patches/libwnck-01-trusted-extensions.diff Fri Nov 30 11:06:05 2007 +0000
+++ b/patches/libwnck-01-trusted-extensions.diff Fri Nov 30 15:38:48 2007 +0000
@@ -1,6 +1,6 @@
-diff -Nrup libwnck-2.19.4/config.h.in ../libwnck-2.19.4/config.h.in
---- libwnck-2.19.4/config.h.in 2007-06-18 22:16:20.000000000 +0200
-+++ ../libwnck-2.19.4/config.h.in 2007-06-27 15:08:01.815453000 +0200
+diff -urN libwnck.orig/config.h.in libwnck.new/config.h.in
+--- libwnck.orig/config.h.in 2007-11-30 14:02:04.813100000 +0000
++++ libwnck.new/config.h.in 2007-11-30 14:02:34.596882000 +0000
@@ -32,6 +32,7 @@
/* Define if we have libstartup-notification */
@@ -9,10 +9,10 @@
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
-diff -Nrup libwnck-2.19.4/configure.in ../libwnck-2.19.4/configure.in
---- libwnck-2.19.4/configure.in 2007-06-18 22:16:04.000000000 +0200
-+++ ../libwnck-2.19.4/configure.in 2007-06-27 15:08:01.815751000 +0200
-@@ -119,6 +119,15 @@ if test $pango_omitted_x_deps = yes ; th
+diff -urN libwnck.orig/configure.in libwnck.new/configure.in
+--- libwnck.orig/configure.in 2007-11-30 14:02:04.797926000 +0000
++++ libwnck.new/configure.in 2007-11-30 14:02:34.597545000 +0000
+@@ -119,6 +119,15 @@
fi
fi
@@ -28,10 +28,10 @@
## Check for XRes
AC_CHECK_LIB(XRes, XResQueryExtension,
X_LIBS="$X_LIBS -lXRes -lXext" ; AC_DEFINE_UNQUOTED(HAVE_XRES, 1, [define if you have the XRes library]), ,
-diff -Nrup libwnck-2.19.4/libwnck/Makefile.am ../libwnck-2.19.4/libwnck/Makefile.am
---- libwnck-2.19.4/libwnck/Makefile.am 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.4/libwnck/Makefile.am 2007-06-27 15:08:01.816028000 +0200
-@@ -28,6 +28,11 @@ libwnckinclude_HEADERS= \
+diff -urN libwnck.orig/libwnck/Makefile.am libwnck.new/libwnck/Makefile.am
+--- libwnck.orig/libwnck/Makefile.am 2007-11-30 14:02:04.860772000 +0000
++++ libwnck.new/libwnck/Makefile.am 2007-11-30 14:02:34.598283000 +0000
+@@ -28,6 +28,11 @@
wnck_built_headers = $(wnck_built_installed_headers) wnck-marshal.h
wnck_built_cfiles = wnck-enum-types.c wnck-marshal.c
@@ -43,7 +43,7 @@
wnck_accessibility_files = \
pager-accessible.c \
pager-accessible.h \
-@@ -55,6 +60,11 @@ libwnck_1_la_SOURCES= \
+@@ -55,6 +60,11 @@
workspace.c \
xutils.c \
xutils.h \
@@ -55,10 +55,10 @@
$(wnck_accessibility_files)
$(libwnck_1_la_OBJECTS): inlinepixbufs.h
-diff -Nrup libwnck-2.19.5/libwnck/pager.c ../libwnck-2.19.5/libwnck/pager.c
---- libwnck-2.19.5/libwnck/pager.c 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.5/libwnck/pager.c 2007-06-27 15:10:10.536137000 +0200
-@@ -28,6 +28,7 @@
+diff -urN libwnck.orig/libwnck/pager.c libwnck.new/libwnck/pager.c
+--- libwnck.orig/libwnck/pager.c 2007-11-30 14:02:04.876199000 +0000
++++ libwnck.new/libwnck/pager.c 2007-11-30 14:02:34.600462000 +0000
+@@ -29,6 +29,7 @@
#include <math.h>
#include <glib/gi18n-lib.h>
@@ -66,7 +66,7 @@
#include "pager.h"
#include "workspace.h"
#include "window.h"
-@@ -55,6 +56,11 @@
+@@ -60,6 +61,11 @@
* layout.
*/
@@ -78,7 +78,7 @@
#define N_SCREEN_CONNECTIONS 11
struct _WnckPagerPrivate
-@@ -78,6 +84,9 @@ struct _WnckPagerPrivate
+@@ -83,6 +89,9 @@
WnckWindow *drag_window;
GdkPixbuf *bg_cache;
@@ -88,7 +88,7 @@
int layout_manager_token;
-@@ -309,8 +318,8 @@ wnck_pager_realize (GtkWidget *widget)
+@@ -341,8 +350,8 @@
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
@@ -99,7 +99,7 @@
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-@@ -320,6 +329,10 @@ wnck_pager_realize (GtkWidget *widget)
+@@ -352,6 +361,10 @@
widget->style = gtk_style_attach (widget->style, widget->window);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
@@ -110,7 +110,7 @@
/* connect to the screen of this pager. In theory, this will already have
* been done in wnck_pager_size_request() */
if (pager->priv->screen == NULL)
-@@ -1069,7 +1069,13 @@
+@@ -1056,7 +1069,13 @@
if (!wnck_workspace_is_virtual (space))
{
@@ -124,7 +124,7 @@
cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
cairo_fill (cr);
}
-@@ -1061,6 +1080,52 @@ wnck_pager_draw_workspace (WnckPager
+@@ -1233,6 +1252,52 @@
cairo_stroke (cr);
cairo_destroy (cr);
}
@@ -177,7 +177,7 @@
}
static gboolean
-@@ -1595,6 +1660,14 @@ wnck_pager_motion (GtkWidget *wid
+@@ -1743,6 +1808,14 @@
gdk_window_get_pointer (widget->window, &x, &y, NULL);
@@ -192,7 +192,7 @@
if (!pager->priv->dragging &&
pager->priv->drag_window != NULL &&
gtk_drag_check_threshold (widget,
-@@ -1735,6 +1808,12 @@ wnck_pager_new (WnckScreen *screen)
+@@ -1936,6 +2009,12 @@
WnckPager *pager;
pager = g_object_new (WNCK_TYPE_PAGER, NULL);
@@ -205,10 +205,2628 @@
return GTK_WIDGET (pager);
}
-diff -Nrup libwnck-2.19.4/libwnck/private.h ../libwnck-2.19.4/libwnck/private.h
---- libwnck-2.19.4/libwnck/private.h 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.4/libwnck/private.h 2007-06-27 15:08:01.818386000 +0200
-@@ -34,6 +34,9 @@
+diff -urN libwnck.orig/libwnck/pager.c.orig libwnck.new/libwnck/pager.c.orig
+--- libwnck.orig/libwnck/pager.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/pager.c.orig 2007-11-30 14:02:08.626530000 +0000
+@@ -0,0 +1,2614 @@
++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */
++/* vim: set sw=2 et: */
++/* pager object */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2003 Kim Woelders
++ * Copyright (C) 2003 Red Hat, Inc.
++ * Copyright (C) 2003, 2005-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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 <math.h>
++#include <glib/gi18n-lib.h>
++
++#include "pager.h"
++#include "workspace.h"
++#include "window.h"
++#include "window-action-menu.h"
++#include "xutils.h"
++#include "pager-accessible-factory.h"
++#include "workspace-accessible-factory.h"
++#include "private.h"
++
++/**
++ * SECTION:pager
++ * @short_description: a pager widget, showing the content of workspaces.
++ * @see_also: #WnckScreen
++ * @stability: Unstable
++ *
++ * A #WnckPager shows a miniature view of the workspaces, representing managed
++ * windows by small rectangles, and allows the user to initiate various window
++ * manager actions by manipulating these representations. The #WnckPager offers
++ * ways to move windows between workspaces and to change the current workspace.
++ *
++ * Alternatively, a #WnckPager can be configured to only show the names of the
++ * workspace instead of their contents.
++ *
++ * The #WnckPager is also responsible for setting the layout of the workspaces.
++ * Since only one application can be responsible for setting the layout on a
++ * screen, the #WnckPager automatically tries to obtain the manager selection
++ * for the screen and only sets the layout if it owns the manager selection.
++ * See wnck_pager_set_orientation() and wnck_pager_set_n_rows() to change the
++ * layout.
++ */
++
++#define N_SCREEN_CONNECTIONS 11
++
++struct _WnckPagerPrivate
++{
++ WnckScreen *screen;
++
++ int n_rows; /* really columns for vertical orientation */
++ WnckPagerDisplayMode display_mode;
++ gboolean show_all_workspaces;
++ GtkShadowType shadow_type;
++
++ GtkOrientation orientation;
++ int workspace_size;
++ guint screen_connections[N_SCREEN_CONNECTIONS];
++ int prelight; /* workspace mouse is hovering over */
++ gboolean prelight_dnd; /* is dnd happening? */
++
++ guint dragging :1;
++ int drag_start_x;
++ int drag_start_y;
++ WnckWindow *drag_window;
++
++ GdkPixbuf *bg_cache;
++
++ int layout_manager_token;
++
++ guint dnd_activate; /* GSource that triggers switching to this workspace during dnd */
++ guint dnd_time; /* time of last event during dnd (for delayed workspace activation) */
++};
++
++G_DEFINE_TYPE (WnckPager, wnck_pager, GTK_TYPE_WIDGET);
++#define WNCK_PAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WNCK_TYPE_PAGER, WnckPagerPrivate))
++
++enum
++{
++ dummy, /* remove this when you add more signals */
++ LAST_SIGNAL
++};
++
++
++#define POINT_IN_RECT(xcoord, ycoord, rect) \
++ ((xcoord) >= (rect).x && \
++ (xcoord) < ((rect).x + (rect).width) && \
++ (ycoord) >= (rect).y && \
++ (ycoord) < ((rect).y + (rect).height))
++
++static void wnck_pager_init (WnckPager *pager);
++static void wnck_pager_class_init (WnckPagerClass *klass);
++static void wnck_pager_finalize (GObject *object);
++
++static void wnck_pager_realize (GtkWidget *widget);
++static void wnck_pager_unrealize (GtkWidget *widget);
++static void wnck_pager_size_request (GtkWidget *widget,
++ GtkRequisition *requisition);
++static void wnck_pager_size_allocate (GtkWidget *widget,
++ GtkAllocation *allocation);
++static gboolean wnck_pager_expose_event (GtkWidget *widget,
++ GdkEventExpose *event);
++static gboolean wnck_pager_button_press (GtkWidget *widget,
++ GdkEventButton *event);
++static gboolean wnck_pager_drag_motion (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ guint time);
++static void wnck_pager_drag_motion_leave (GtkWidget *widget,
++ GdkDragContext *context,
++ guint time);
++static gboolean wnck_pager_drag_drop (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ guint time);
++static void wnck_pager_drag_data_received (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ GtkSelectionData *selection_data,
++ guint info,
++ guint time_);
++static void wnck_pager_drag_data_get (GtkWidget *widget,
++ GdkDragContext *context,
++ GtkSelectionData *selection_data,
++ guint info,
++ guint time);
++static void wnck_pager_drag_end (GtkWidget *widget,
++ GdkDragContext *context);
++static gboolean wnck_pager_motion (GtkWidget *widget,
++ GdkEventMotion *event);
++static gboolean wnck_pager_leave_notify (GtkWidget *widget,
++ GdkEventCrossing *event);
++static gboolean wnck_pager_button_release (GtkWidget *widget,
++ GdkEventButton *event);
++static gboolean wnck_pager_focus (GtkWidget *widget,
++ GtkDirectionType direction);
++static gboolean wnck_pager_query_tooltip (GtkWidget *widget,
++ gint x,
++ gint y,
++ gboolean keyboard_tip,
++ GtkTooltip *tooltip);
++static void workspace_name_changed_callback (WnckWorkspace *workspace,
++ gpointer data);
++
++static gboolean wnck_pager_window_state_is_relevant (int state);
++static gint wnck_pager_window_get_workspace (WnckWindow *window,
++ gboolean is_state_relevant);
++static void wnck_pager_queue_draw_workspace (WnckPager *pager,
++ gint i);
++static void wnck_pager_queue_draw_window (WnckPager *pager,
++ WnckWindow *window);
++
++static void wnck_pager_connect_screen (WnckPager *pager);
++static void wnck_pager_connect_window (WnckPager *pager,
++ WnckWindow *window);
++static void wnck_pager_disconnect_screen (WnckPager *pager);
++
++static gboolean wnck_pager_set_layout_hint (WnckPager *pager);
++
++static void wnck_pager_clear_drag (WnckPager *pager);
++static void wnck_pager_check_prelight (WnckPager *pager,
++ gint x,
++ gint y,
++ gboolean dnd);
++
++static GdkPixbuf* wnck_pager_get_background (WnckPager *pager,
++ int width,
++ int height);
++
++static AtkObject* wnck_pager_get_accessible (GtkWidget *widget);
++
++
++static void
++wnck_pager_init (WnckPager *pager)
++{
++ int i;
++ static const GtkTargetEntry targets[] = {
++ { "application/x-wnck-window-id", 0, 0}
++ };
++
++ pager->priv = WNCK_PAGER_GET_PRIVATE (pager);
++
++ pager->priv->n_rows = 1;
++ pager->priv->display_mode = WNCK_PAGER_DISPLAY_CONTENT;
++ pager->priv->show_all_workspaces = TRUE;
++ pager->priv->shadow_type = GTK_SHADOW_NONE;
++
++ pager->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
++ pager->priv->workspace_size = 48;
++
++ for (i = 0; i < N_SCREEN_CONNECTIONS; i++)
++ pager->priv->screen_connections[i] = 0;
++
++ pager->priv->prelight = -1;
++ pager->priv->prelight_dnd = FALSE;
++
++ pager->priv->dragging = FALSE;
++ pager->priv->drag_start_x = 0;
++ pager->priv->drag_start_y = 0;
++ pager->priv->drag_window = NULL;
++
++ pager->priv->bg_cache = NULL;
++
++ pager->priv->layout_manager_token = WNCK_NO_MANAGER_TOKEN;
++
++ pager->priv->dnd_activate = 0;
++ pager->priv->dnd_time = 0;
++
++ g_object_set (pager, "has-tooltip", TRUE, NULL);
++
++ gtk_drag_dest_set (GTK_WIDGET (pager), 0, targets, G_N_ELEMENTS (targets), GDK_ACTION_MOVE);
++ GTK_WIDGET_SET_FLAGS (GTK_WIDGET (pager), GTK_CAN_FOCUS);
++}
++
++static void
++wnck_pager_class_init (WnckPagerClass *klass)
++{
++ GObjectClass *object_class = G_OBJECT_CLASS (klass);
++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
++
++ g_type_class_add_private (klass, sizeof (WnckPagerPrivate));
++
++ object_class->finalize = wnck_pager_finalize;
++
++ widget_class->realize = wnck_pager_realize;
++ widget_class->unrealize = wnck_pager_unrealize;
++ widget_class->size_request = wnck_pager_size_request;
++ widget_class->size_allocate = wnck_pager_size_allocate;
++ widget_class->expose_event = wnck_pager_expose_event;
++ widget_class->button_press_event = wnck_pager_button_press;
++ widget_class->button_release_event = wnck_pager_button_release;
++ widget_class->motion_notify_event = wnck_pager_motion;
++ widget_class->leave_notify_event = wnck_pager_leave_notify;
++ widget_class->focus = wnck_pager_focus;
++ widget_class->get_accessible = wnck_pager_get_accessible;
++ widget_class->drag_leave = wnck_pager_drag_motion_leave;
++ widget_class->drag_motion = wnck_pager_drag_motion;
++ widget_class->drag_drop = wnck_pager_drag_drop;
++ widget_class->drag_data_received = wnck_pager_drag_data_received;
++ widget_class->drag_data_get = wnck_pager_drag_data_get;
++ widget_class->drag_end = wnck_pager_drag_end;
++ widget_class->query_tooltip = wnck_pager_query_tooltip;
++}
++
++static void
++wnck_pager_finalize (GObject *object)
++{
++ WnckPager *pager;
++
++ pager = WNCK_PAGER (object);
++
++ if (pager->priv->bg_cache)
++ {
++ g_object_unref (G_OBJECT (pager->priv->bg_cache));
++ pager->priv->bg_cache = NULL;
++ }
++
++ if (pager->priv->dnd_activate != 0)
++ {
++ g_source_remove (pager->priv->dnd_activate);
++ pager->priv->dnd_activate = 0;
++ }
++
++ G_OBJECT_CLASS (wnck_pager_parent_class)->finalize (object);
++}
++
++static void
++_wnck_pager_set_screen (WnckPager *pager)
++{
++ GdkScreen *gdkscreen;
++
++ gdkscreen = gtk_widget_get_screen (GTK_WIDGET (pager));
++ pager->priv->screen = wnck_screen_get (gdk_screen_get_number (gdkscreen));
++
++ if (!wnck_pager_set_layout_hint (pager))
++ {
++ _WnckLayoutOrientation orientation;
++
++ /* we couldn't set the layout on the screen. This means someone else owns
++ * it. Let's at least show the correct layout. */
++ _wnck_screen_get_workspace_layout (pager->priv->screen,
++ &orientation,
++ &pager->priv->n_rows,
++ NULL, NULL);
++
++ /* test in this order to default to horizontal in case there was in issue
++ * when fetching the layout */
++ if (orientation == WNCK_LAYOUT_ORIENTATION_VERTICAL)
++ pager->priv->orientation = GTK_ORIENTATION_VERTICAL;
++ else
++ pager->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
++
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++ }
++
++ wnck_pager_connect_screen (pager);
++}
++
++static void
++wnck_pager_realize (GtkWidget *widget)
++{
++
++ GdkWindowAttr attributes;
++ gint attributes_mask;
++ WnckPager *pager;
++
++ pager = WNCK_PAGER (widget);
++
++ /* do not call the parent class realize since we're doing things a bit
++ * differently here */
++ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
++
++ attributes.window_type = GDK_WINDOW_CHILD;
++ attributes.x = widget->allocation.x;
++ attributes.y = widget->allocation.y;
++ attributes.width = widget->allocation.width;
++ attributes.height = widget->allocation.height;
++ attributes.wclass = GDK_INPUT_OUTPUT;
++ attributes.visual = gtk_widget_get_visual (widget);
++ attributes.colormap = gtk_widget_get_colormap (widget);
++ attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK |
++ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
++ GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK |
++ GDK_POINTER_MOTION_HINT_MASK;
++
++ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
++
++ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
++ gdk_window_set_user_data (widget->window, widget);
++
++ widget->style = gtk_style_attach (widget->style, widget->window);
++ gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
++
++ /* connect to the screen of this pager. In theory, this will already have
++ * been done in wnck_pager_size_request() */
++ if (pager->priv->screen == NULL)
++ _wnck_pager_set_screen (pager);
++ g_assert (pager->priv->screen != NULL);
++}
++
++static void
++wnck_pager_unrealize (GtkWidget *widget)
++{
++ WnckPager *pager;
++
++ pager = WNCK_PAGER (widget);
++
++ wnck_pager_clear_drag (pager);
++ pager->priv->prelight = -1;
++ pager->priv->prelight_dnd = FALSE;
++
++ wnck_screen_release_workspace_layout (pager->priv->screen,
++ pager->priv->layout_manager_token);
++ pager->priv->layout_manager_token = WNCK_NO_MANAGER_TOKEN;
++
++ wnck_pager_disconnect_screen (pager);
++ pager->priv->screen = NULL;
++
++ GTK_WIDGET_CLASS (wnck_pager_parent_class)->unrealize (widget);
++}
++
++static void
++wnck_pager_size_request (GtkWidget *widget,
++ GtkRequisition *requisition)
++{
++ WnckPager *pager;
++ int n_spaces;
++ int spaces_per_row;
++ double screen_aspect;
++ int other_dimension_size;
++ int size;
++ int n_rows;
++ int focus_width;
++ WnckWorkspace *space;
++
++ pager = WNCK_PAGER (widget);
++
++ /* if we're not realized, we don't know about our screen yet */
++ if (pager->priv->screen == NULL)
++ _wnck_pager_set_screen (pager);
++ g_assert (pager->priv->screen != NULL);
++
++ n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
++
++ g_assert (pager->priv->n_rows > 0);
++ spaces_per_row = (n_spaces + pager->priv->n_rows - 1) / pager->priv->n_rows;
++ space = wnck_screen_get_workspace (pager->priv->screen, 0);
++
++ if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL)
++ {
++ if (space) {
++ screen_aspect =
++ (double) wnck_workspace_get_height (space) /
++ (double) wnck_workspace_get_width (space);
++ } else {
++ screen_aspect =
++ (double) wnck_screen_get_height (pager->priv->screen) /
++ (double) wnck_screen_get_width (pager->priv->screen);
++ }
++
++ /* TODO: Handle WNCK_PAGER_DISPLAY_NAME for this case */
++
++ if (pager->priv->show_all_workspaces)
++ {
++ size = pager->priv->workspace_size;
++ n_rows = pager->priv->n_rows;
++ }
++ else
++ {
++ size = pager->priv->workspace_size;
++ n_rows = 1;
++ spaces_per_row = 1;
++ }
++
++ other_dimension_size = screen_aspect * size;
++
++ requisition->width = size * n_rows + (n_rows - 1);
++ requisition->height = other_dimension_size * spaces_per_row + (spaces_per_row - 1);
++ }
++ else
++ {
++ if (space) {
++ screen_aspect =
++ (double) wnck_workspace_get_width (space) /
++ (double) wnck_workspace_get_height (space);
++ } else {
++ screen_aspect =
++ (double) wnck_screen_get_width (pager->priv->screen) /
++ (double) wnck_screen_get_height (pager->priv->screen);
++ }
++
++ if (pager->priv->show_all_workspaces)
++ {
++ size = pager->priv->workspace_size;
++ n_rows = pager->priv->n_rows;
++ }
++ else
++ {
++ size = pager->priv->workspace_size;
++ n_rows = 1;
++ spaces_per_row = 1;
++ }
++
++ if (pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT)
++ {
++ other_dimension_size = screen_aspect * size;
++ }
++ else
++ {
++ int n_spaces, i, w;
++ WnckScreen *screen;
++ PangoLayout *layout;
++
++ n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
++ layout = gtk_widget_create_pango_layout (widget, NULL);
++ screen = pager->priv->screen;
++ other_dimension_size = 1;
++
++ for (i = 0; i < n_spaces; i++)
++ {
++ pango_layout_set_text (layout,
++ wnck_workspace_get_name (wnck_screen_get_workspace (screen, i)),
++ -1);
++ pango_layout_get_pixel_size (layout, &w, NULL);
++ other_dimension_size = MAX (other_dimension_size, w);
++ }
++
++ g_object_unref (layout);
++
++ other_dimension_size += 2;
++ }
++
++ requisition->width = other_dimension_size * spaces_per_row + (spaces_per_row - 1);
++ requisition->height = size * n_rows + (n_rows - 1);
++ }
++
++ if (pager->priv->shadow_type != GTK_SHADOW_NONE)
++ {
++ requisition->width += 2 * widget->style->xthickness;
++ requisition->height += 2 * widget->style->ythickness;
++ }
++
++ gtk_widget_style_get (widget,
++ "focus-line-width", &focus_width,
++ NULL);
++
++ requisition->width += 2 * focus_width;
++ requisition->height += 2 * focus_width;
++}
++
++static void
++wnck_pager_size_allocate (GtkWidget *widget,
++ GtkAllocation *allocation)
++{
++ WnckPager *pager;
++ int workspace_size;
++ int focus_width;
++ int width;
++ int height;
++
++ pager = WNCK_PAGER (widget);
++
++ gtk_widget_style_get (GTK_WIDGET (pager),
++ "focus-line-width", &focus_width,
++ NULL);
++
++ width = allocation->width - 2 * focus_width;
++ height = allocation->height - 2* focus_width;
++
++ if (pager->priv->shadow_type != GTK_SHADOW_NONE)
++ {
++ width -= 2 * widget->style->xthickness;
++ height -= 2 * widget->style->ythickness;
++ }
++
++ g_assert (pager->priv->n_rows > 0);
++
++ if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL)
++ {
++ if (pager->priv->show_all_workspaces)
++ workspace_size = (width - (pager->priv->n_rows - 1)) / pager->priv->n_rows;
++ else
++ workspace_size = width;
++ }
++ else
++ {
++ if (pager->priv->show_all_workspaces)
++ workspace_size = (height - (pager->priv->n_rows - 1))/ pager->priv->n_rows;
++ else
++ workspace_size = height;
++ }
++
++ if (workspace_size != pager->priv->workspace_size)
++ {
++ pager->priv->workspace_size = workspace_size;
++ gtk_widget_queue_resize (GTK_WIDGET (widget));
++ return;
++ }
++
++ GTK_WIDGET_CLASS (wnck_pager_parent_class)->size_allocate (widget,
++ allocation);
++}
++
++static void
++get_workspace_rect (WnckPager *pager,
++ int space,
++ GdkRectangle *rect)
++{
++ int hsize, vsize;
++ int n_spaces;
++ int spaces_per_row;
++ GtkWidget *widget;
++ int col, row;
++ int focus_width;
++
++ gtk_widget_style_get (GTK_WIDGET (pager),
++ "focus-line-width", &focus_width,
++ NULL);
++
++ widget = GTK_WIDGET (pager);
++
++ if (!pager->priv->show_all_workspaces)
++ {
++ WnckWorkspace *active_space;
++
++ active_space = wnck_screen_get_active_workspace (pager->priv->screen);
++
++ if (active_space && space == wnck_workspace_get_number (active_space))
++ {
++ rect->x = focus_width;
++ rect->y = focus_width;
++ rect->width = widget->allocation.width - 2 * focus_width;
++ rect->height = widget->allocation.height - 2 * focus_width;
++
++ if (pager->priv->shadow_type != GTK_SHADOW_NONE)
++ {
++ rect->x += widget->style->xthickness;
++ rect->y += widget->style->ythickness;
++ rect->width -= 2 * widget->style->xthickness;
++ rect->height -= 2 * widget->style->ythickness;
++ }
++ }
++ else
++ {
++ rect->x = 0;
++ rect->y = 0;
++ rect->width = 0;
++ rect->height = 0;
++ }
++
++ return;
++ }
++
++ hsize = widget->allocation.width - 2 * focus_width;
++ vsize = widget->allocation.height - 2 * focus_width;
++
++ if (pager->priv->shadow_type != GTK_SHADOW_NONE)
++ {
++ hsize -= 2 * widget->style->xthickness;
++ vsize -= 2 * widget->style->ythickness;
++ }
++
++ n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
++
++ g_assert (pager->priv->n_rows > 0);
++ spaces_per_row = (n_spaces + pager->priv->n_rows - 1) / pager->priv->n_rows;
++
++ if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL)
++ {
++ rect->width = (hsize - (pager->priv->n_rows - 1)) / pager->priv->n_rows;
++ rect->height = (vsize - (spaces_per_row - 1)) / spaces_per_row;
++
++ col = space / spaces_per_row;
++ row = space % spaces_per_row;
++
++ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
++ col = pager->priv->n_rows - col - 1;
++
++ rect->x = (rect->width + 1) * col;
++ rect->y = (rect->height + 1) * row;
++
++ if (col == pager->priv->n_rows - 1)
++ rect->width = hsize - rect->x;
++
++ if (row == spaces_per_row - 1)
++ rect->height = vsize - rect->y;
++ }
++ else
++ {
++ rect->width = (hsize - (spaces_per_row - 1)) / spaces_per_row;
++ rect->height = (vsize - (pager->priv->n_rows - 1)) / pager->priv->n_rows;
++
++ col = space % spaces_per_row;
++ row = space / spaces_per_row;
++
++ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
++ col = spaces_per_row - col - 1;
++
++ rect->x = (rect->width + 1) * col;
++ rect->y = (rect->height + 1) * row;
++
++ if (col == spaces_per_row - 1)
++ rect->width = hsize - rect->x;
++
++ if (row == pager->priv->n_rows - 1)
++ rect->height = vsize - rect->y;
++ }
++
++ rect->x += focus_width;
++ rect->y += focus_width;
++
++ if (pager->priv->shadow_type != GTK_SHADOW_NONE)
++ {
++ rect->x += widget->style->xthickness;
++ rect->y += widget->style->ythickness;
++ }
++}
++
++static gboolean
++wnck_pager_window_state_is_relevant (int state)
++{
++ return (state & (WNCK_WINDOW_STATE_HIDDEN | WNCK_WINDOW_STATE_SKIP_PAGER)) ? FALSE : TRUE;
++}
++
++static gint
++wnck_pager_window_get_workspace (WnckWindow *window,
++ gboolean is_state_relevant)
++{
++ gint state;
++ WnckWorkspace *workspace;
++
++ state = wnck_window_get_state (window);
++ if (is_state_relevant && !wnck_pager_window_state_is_relevant (state))
++ return -1;
++ workspace = wnck_window_get_workspace (window);
++ if (workspace == NULL && wnck_window_is_pinned (window))
++ workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window));
++
++ return workspace ? wnck_workspace_get_number (workspace) : -1;
++}
++
++static GList*
++get_windows_for_workspace_in_bottom_to_top (WnckScreen *screen,
++ WnckWorkspace *workspace)
++{
++ GList *result;
++ GList *windows;
++ GList *tmp;
++ int workspace_num;
++
++ result = NULL;
++ workspace_num = wnck_workspace_get_number (workspace);
++
++ windows = wnck_screen_get_windows_stacked (screen);
++ for (tmp = windows; tmp != NULL; tmp = tmp->next)
++ {
++ WnckWindow *win = WNCK_WINDOW (tmp->data);
++ if (wnck_pager_window_get_workspace (win, TRUE) == workspace_num)
++ result = g_list_prepend (result, win);
++ }
++
++ result = g_list_reverse (result);
++
++ return result;
++}
++
++static void
++get_window_rect (WnckWindow *window,
++ const GdkRectangle *workspace_rect,
++ GdkRectangle *rect)
++{
++ double width_ratio, height_ratio;
++ int x, y, width, height;
++ WnckWorkspace *workspace;
++ GdkRectangle unclipped_win_rect;
++
++ workspace = wnck_window_get_workspace (window);
++ if (workspace == NULL)
++ workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window));
++
++ /* scale window down by same ratio we scaled workspace down */
++ width_ratio = (double) workspace_rect->width / (double) wnck_workspace_get_width (workspace);
++ height_ratio = (double) workspace_rect->height / (double) wnck_workspace_get_height (workspace);
++
++ wnck_window_get_geometry (window, &x, &y, &width, &height);
++
++ x += wnck_workspace_get_viewport_x (workspace);
++ y += wnck_workspace_get_viewport_y (workspace);
++ x = x * width_ratio + 0.5;
++ y = y * height_ratio + 0.5;
++ width = width * width_ratio + 0.5;
++ height = height * height_ratio + 0.5;
++
++ x += workspace_rect->x;
++ y += workspace_rect->y;
++
++ if (width < 3)
++ width = 3;
++ if (height < 3)
++ height = 3;
++
++ unclipped_win_rect.x = x;
++ unclipped_win_rect.y = y;
++ unclipped_win_rect.width = width;
++ unclipped_win_rect.height = height;
++
++ gdk_rectangle_intersect ((GdkRectangle *) workspace_rect, &unclipped_win_rect, rect);
++}
++
++static void
++draw_window (GdkDrawable *drawable,
++ GtkWidget *widget,
++ WnckWindow *win,
++ const GdkRectangle *winrect,
++ GtkStateType state,
++ gboolean translucent)
++{
++ cairo_t *cr;
++ GdkPixbuf *icon;
++ int icon_x, icon_y, icon_w, icon_h;
++ gboolean is_active;
++ GdkColor *color;
++ gdouble translucency;
++
++ is_active = wnck_window_is_active (win);
++ translucency = translucent ? 0.4 : 1.0;
++
++ cr = gdk_cairo_create (drawable);
++ cairo_rectangle (cr, winrect->x, winrect->y, winrect->width, winrect->height);
++ cairo_clip (cr);
++
++ if (is_active)
++ color = &widget->style->light[state];
++ else
++ color = &widget->style->bg[state];
++ cairo_set_source_rgba (cr,
++ color->red / 65535.,
++ color->green / 65535.,
++ color->blue / 65535.,
++ translucency);
++ cairo_rectangle (cr,
++ winrect->x + 1, winrect->y + 1,
++ MAX (0, winrect->width - 2), MAX (0, winrect->height - 2));
++ cairo_fill (cr);
++
++ icon = wnck_window_get_icon (win);
++
++ icon_w = icon_h = 0;
++
++ if (icon)
++ {
++ icon_w = gdk_pixbuf_get_width (icon);
++ icon_h = gdk_pixbuf_get_height (icon);
++
++ /* If the icon is too big, fall back to mini icon.
++ * We don't arbitrarily scale the icon, because it's
++ * just too slow on my Athlon 850.
++ */
++ if (icon_w > (winrect->width - 2) ||
++ icon_h > (winrect->height - 2))
++ {
++ icon = wnck_window_get_mini_icon (win);
++ if (icon)
++ {
++ icon_w = gdk_pixbuf_get_width (icon);
++ icon_h = gdk_pixbuf_get_height (icon);
++
++ /* Give up. */
++ if (icon_w > (winrect->width - 2) ||
++ icon_h > (winrect->height - 2))
++ icon = NULL;
++ }
++ }
++ }
++
++ if (icon)
++ {
++ icon_x = winrect->x + (winrect->width - icon_w) / 2;
++ icon_y = winrect->y + (winrect->height - icon_h) / 2;
++
++ cairo_save (cr);
++ gdk_cairo_set_source_pixbuf (cr, icon, icon_x, icon_y);
++ cairo_rectangle (cr, icon_x, icon_y, icon_w, icon_h);
++ cairo_clip (cr);
++ cairo_paint_with_alpha (cr, translucency);
++ cairo_restore (cr);
++ }
++
++ if (is_active)
++ color = &widget->style->fg[state];
++ else
++ color = &widget->style->fg[state];
++ cairo_set_source_rgba (cr,
++ color->red / 65535.,
++ color->green / 65535.,
++ color->blue / 65535.,
++ translucency);
++ cairo_set_line_width (cr, 1.0);
++ cairo_rectangle (cr,
++ winrect->x + 0.5, winrect->y + 0.5,
++ MAX (0, winrect->width - 1), MAX (0, winrect->height - 1));
++ cairo_stroke (cr);
++
++ cairo_destroy (cr);
++}
++
++static WnckWindow *
++window_at_point (WnckPager *pager,
++ WnckWorkspace *space,
++ GdkRectangle *space_rect,
++ int x,
++ int y)
++{
++ WnckWindow *window;
++ GList *windows;
++ GList *tmp;
++
++ window = NULL;
++
++ windows = get_windows_for_workspace_in_bottom_to_top (pager->priv->screen,
++ space);
++
++ /* clicks on top windows first */
++ windows = g_list_reverse (windows);
++
++ for (tmp = windows; tmp != NULL; tmp = tmp->next)
++ {
++ WnckWindow *win = WNCK_WINDOW (tmp->data);
++ GdkRectangle winrect;
++
++ get_window_rect (win, space_rect, &winrect);
++
++ if (POINT_IN_RECT (x, y, winrect))
++ {
++ /* wnck_window_activate (win); */
++ window = win;
++ break;
++ }
++ }
++
++ g_list_free (windows);
++
++ return window;
++}
++
++static int
++workspace_at_point (WnckPager *pager,
++ int x,
++ int y,
++ int *viewport_x,
++ int *viewport_y)
++{
++ GtkWidget *widget;
++ int i;
++ int n_spaces;
++ int focus_width;
++ int xthickness;
++ int ythickness;
++
++ widget = GTK_WIDGET (pager);
++
++ gtk_widget_style_get (GTK_WIDGET (pager),
++ "focus-line-width", &focus_width,
++ NULL);
++
++ if (pager->priv->shadow_type != GTK_SHADOW_NONE)
++ {
++ xthickness = focus_width + widget->style->xthickness;
++ ythickness = focus_width + widget->style->ythickness;
++ }
++ else
++ {
++ xthickness = focus_width;
++ ythickness = focus_width;
++ }
++
++ n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
++
++ i = 0;
++ while (i < n_spaces)
++ {
++ GdkRectangle rect;
++ GtkWidget *widget;
++
++ get_workspace_rect (pager, i, &rect);
++
++ /* If workspace is on the edge, pretend points on the frame belong to the
++ * workspace.
++ * Else, pretend the right/bottom line separating two workspaces belong
++ * to the workspace.
++ */
++ widget = GTK_WIDGET (pager);
++
++ if (rect.x == xthickness)
++ {
++ rect.x = 0;
++ rect.width += xthickness;
++ }
++ if (rect.y == ythickness)
++ {
++ rect.y = 0;
++ rect.height += ythickness;
++ }
++ if (rect.y + rect.height == widget->allocation.height - ythickness)
++ {
++ rect.height += ythickness;
++ }
++ else
++ {
++ rect.height += 1;
++ }
++ if (rect.x + rect.width == widget->allocation.width - xthickness)
++ {
++ rect.width += xthickness;
++ }
++ else
++ {
++ rect.width += 1;
++ }
++
++ if (POINT_IN_RECT (x, y, rect))
++ {
++ double width_ratio, height_ratio;
++ WnckWorkspace *space;
++
++ space = wnck_screen_get_workspace (pager->priv->screen, i);
++ g_assert (space != NULL);
++
++ /* Scale x, y mouse coords to corresponding screenwide viewport coords */
++
++ width_ratio = (double) wnck_workspace_get_width (space) / (double) rect.width;
++ height_ratio = (double) wnck_workspace_get_height (space) / (double) rect.height;
++
++ if (viewport_x)
++ *viewport_x = width_ratio * (x - rect.x);
++ if (viewport_y)
++ *viewport_y = height_ratio * (y - rect.y);
++
++ return i;
++ }
++
++ ++i;
++ }
++
++ return -1;
++}
++
++
++static void
++wnck_pager_draw_workspace (WnckPager *pager,
++ int workspace,
++ GdkRectangle *rect,
++ GdkPixbuf *bg_pixbuf)
++{
++ GList *windows;
++ GList *tmp;
++ gboolean is_current;
++ WnckWorkspace *space;
++ GtkWidget *widget;
++ GtkStateType state;
++
++ space = wnck_screen_get_workspace (pager->priv->screen, workspace);
++ if (!space)
++ return;
++
++ widget = GTK_WIDGET (pager);
++ is_current = (space == wnck_screen_get_active_workspace (pager->priv->screen));
++
++ if (is_current)
++ state = GTK_STATE_SELECTED;
++ else if (workspace == pager->priv->prelight)
++ state = GTK_STATE_PRELIGHT;
++ else
++ state = GTK_STATE_NORMAL;
++
++ /* FIXME in names mode, should probably draw things like a button.
++ */
++
++ if (bg_pixbuf)
++ {
++ gdk_draw_pixbuf (widget->window,
++ widget->style->dark_gc[state],
++ bg_pixbuf,
++ 0, 0,
++ rect->x, rect->y,
++ -1, -1,
++ GDK_RGB_DITHER_MAX,
++ 0, 0);
++ }
++ else
++ {
++ cairo_t *cr;
++
++ cr = gdk_cairo_create (widget->window);
++
++ if (!wnck_workspace_is_virtual (space))
++ {
++ gdk_cairo_set_source_color (cr, &widget->style->dark[state]);
++ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
++ cairo_fill (cr);
++ }
++ else
++ {
++ //FIXME prelight for dnd in the viewport?
++ int workspace_width, workspace_height;
++ int screen_width, screen_height;
++ double width_ratio, height_ratio;
++ double vx, vy, vw, vh; /* viewport */
++
++ workspace_width = wnck_workspace_get_width (space);
++ workspace_height = wnck_workspace_get_height (space);
++ screen_width = wnck_screen_get_width (pager->priv->screen);
++ screen_height = wnck_screen_get_height (pager->priv->screen);
++
++ if ((workspace_width % screen_width == 0) &&
++ (workspace_height % screen_height == 0))
++ {
++ int i, j;
++ int active_i, active_j;
++ int horiz_views;
++ int verti_views;
++
++ horiz_views = workspace_width / screen_width;
++ verti_views = workspace_height / screen_height;
++
++ /* do not forget thin lines to delimit "workspaces" */
++ width_ratio = (rect->width - (horiz_views - 1)) /
++ (double) workspace_width;
++ height_ratio = (rect->height - (verti_views - 1)) /
++ (double) workspace_height;
++
++ if (is_current)
++ {
++ active_i =
++ wnck_workspace_get_viewport_x (space) / screen_width;
++ active_j =
++ wnck_workspace_get_viewport_y (space) / screen_height;
++ }
++ else
++ {
++ active_i = -1;
++ active_j = -1;
++ }
++
++ for (i = 0; i < horiz_views; i++)
++ {
++ /* "+ i" is for the thin lines */
++ vx = rect->x + (width_ratio * screen_width) * i + i;
++
++ if (i == horiz_views - 1)
++ vw = rect->width + rect->x - vx;
++ else
++ vw = width_ratio * screen_width;
++
++ vh = height_ratio * screen_height;
++
++ for (j = 0; j < verti_views; j++)
++ {
++ /* "+ j" is for the thin lines */
++ vy = rect->y + (height_ratio * screen_height) * j + j;
++
++ if (j == verti_views - 1)
++ vh = rect->height + rect->y - vy;
++
++ if (active_i == i && active_j == j)
++ gdk_cairo_set_source_color (cr,
++ &widget->style->dark[GTK_STATE_SELECTED]);
++ else
++ gdk_cairo_set_source_color (cr,
++ &widget->style->dark[GTK_STATE_NORMAL]);
++ cairo_rectangle (cr, vx, vy, vw, vh);
++ cairo_fill (cr);
++ }
++ }
++ }
++ else
++ {
++ width_ratio = rect->width / (double) workspace_width;
++ height_ratio = rect->height / (double) workspace_height;
++
++ /* first draw non-active part of the viewport */
++ gdk_cairo_set_source_color (cr, &widget->style->dark[GTK_STATE_NORMAL]);
++ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
++ cairo_fill (cr);
++
++ if (is_current)
++ {
++ /* draw the active part of the viewport */
++ vx = rect->x +
++ width_ratio * wnck_workspace_get_viewport_x (space);
++ vy = rect->y +
++ height_ratio * wnck_workspace_get_viewport_y (space);
++ vw = width_ratio * screen_width;
++ vh = height_ratio * screen_height;
++
++ gdk_cairo_set_source_color (cr, &widget->style->dark[GTK_STATE_SELECTED]);
++ cairo_rectangle (cr, vx, vy, vw, vh);
++ cairo_fill (cr);
++ }
++ }
++ }
++
++ cairo_destroy (cr);
++ }
++
++ if (pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT)
++ {
++ windows = get_windows_for_workspace_in_bottom_to_top (pager->priv->screen,
++ wnck_screen_get_workspace (pager->priv->screen,
++ workspace));
++
++ tmp = windows;
++ while (tmp != NULL)
++ {
++ WnckWindow *win = tmp->data;
++ GdkRectangle winrect;
++
++ get_window_rect (win, rect, &winrect);
++
++ draw_window (widget->window,
++ widget,
++ win,
++ &winrect,
++ state,
++ win == pager->priv->drag_window && pager->priv->dragging ? TRUE : FALSE);
++
++ tmp = tmp->next;
++ }
++
++ g_list_free (windows);
++ }
++ else
++ {
++ /* Workspace name mode */
++ const char *workspace_name;
++ PangoLayout *layout;
++ int w, h;
++
++ workspace_name = wnck_workspace_get_name (wnck_screen_get_workspace (pager->priv->screen,
++ workspace));
++ layout = gtk_widget_create_pango_layout (widget,
++ workspace_name);
++
++ pango_layout_get_pixel_size (layout, &w, &h);
++
++ gdk_draw_layout (widget->window,
++ is_current ?
++ widget->style->fg_gc[GTK_STATE_SELECTED] :
++ widget->style->fg_gc[GTK_STATE_NORMAL],
++ rect->x + (rect->width - w) / 2,
++ rect->y + (rect->height - h) / 2,
++ layout);
++
++ g_object_unref (layout);
++ }
++
++ if (workspace == pager->priv->prelight && pager->priv->prelight_dnd)
++ {
++ /* stolen directly from gtk source so it matches nicely */
++ cairo_t *cr;
++ gtk_paint_shadow (widget->style, widget->window,
++ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
++ NULL, widget, "dnd",
++ rect->x, rect->y, rect->width, rect->height);
++
++ cr = gdk_cairo_create (widget->window);
++ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
++ cairo_set_line_width (cr, 1.0);
++ cairo_rectangle (cr,
++ rect->x + 0.5, rect->y + 0.5,
++ MAX (0, rect->width - 1), MAX (0, rect->height - 1));
++ cairo_stroke (cr);
++ cairo_destroy (cr);
++ }
++}
++
++static gboolean
++wnck_pager_expose_event (GtkWidget *widget,
++ GdkEventExpose *event)
++{
++ WnckPager *pager;
++ int i;
++ int n_spaces;
++ WnckWorkspace *active_space;
++ GdkPixbuf *bg_pixbuf;
++ gboolean first;
++ int focus_width;
++
++ pager = WNCK_PAGER (widget);
++
++ n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
++ active_space = wnck_screen_get_active_workspace (pager->priv->screen);
++ bg_pixbuf = NULL;
++ first = TRUE;
++
++ gtk_widget_style_get (widget,
++ "focus-line-width", &focus_width,
++ NULL);
++
++ if (GTK_WIDGET_HAS_FOCUS (widget))
++ gtk_paint_focus (widget->style,
++ widget->window,
++ GTK_WIDGET_STATE (widget),
++ NULL,
++ widget,
++ "pager",
++ 0, 0,
++ widget->allocation.width,
++ widget->allocation.height);
++
++ if (pager->priv->shadow_type != GTK_SHADOW_NONE)
++ {
++ gtk_paint_shadow (widget->style,
++ widget->window,
++ GTK_WIDGET_STATE (widget),
++ pager->priv->shadow_type,
++ NULL,
++ widget,
++ "pager",
++ focus_width,
++ focus_width,
++ widget->allocation.width - 2 * focus_width,
++ widget->allocation.height - 2 * focus_width);
++ }
++
++ i = 0;
++ while (i < n_spaces)
++ {
++ GdkRectangle rect, intersect;
++
++ if (pager->priv->show_all_workspaces ||
++ (active_space && i == wnck_workspace_get_number (active_space)))
++ {
++ get_workspace_rect (pager, i, &rect);
++
++ /* We only want to do this once, even if w/h change,
++ * for efficiency. width/height will only change by
++ * one pixel at most.
++ */
++ if (first &&
++ pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT)
++ {
++ bg_pixbuf = wnck_pager_get_background (pager,
++ rect.width,
++ rect.height);
++ first = FALSE;
++ }
++
++ if (gdk_rectangle_intersect (&event->area, &rect, &intersect))
++ wnck_pager_draw_workspace (pager, i, &rect, bg_pixbuf);
++ }
++
++ ++i;
++ }
++
++ return FALSE;
++}
++
++static gboolean
++wnck_pager_button_press (GtkWidget *widget,
++ GdkEventButton *event)
++{
++ WnckPager *pager;
++ int space_number;
++ WnckWorkspace *space = NULL;
++ GdkRectangle workspace_rect;
++
++ if (event->button != 1)
++ return FALSE;
++
++ pager = WNCK_PAGER (widget);
++
++ space_number = workspace_at_point (pager, event->x, event->y, NULL, NULL);
++
++ if (space_number != -1)
++ {
++ get_workspace_rect (pager, space_number, &workspace_rect);
++ space = wnck_screen_get_workspace (pager->priv->screen, space_number);
++ }
++
++ if (space)
++ {
++ /* always save the start coordinates so we can know if we need to change
++ * workspace when the button is released (ie, start and end coordinates
++ * should be in the same workspace) */
++ pager->priv->drag_start_x = event->x;
++ pager->priv->drag_start_y = event->y;
++ }
++
++ if (space && (pager->priv->display_mode != WNCK_PAGER_DISPLAY_NAME))
++ {
++ pager->priv->drag_window = window_at_point (pager, space,
++ &workspace_rect,
++ event->x, event->y);
++ }
++
++ return TRUE;
++}
++
++static gboolean
++wnck_pager_drag_motion_timeout (gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ WnckWorkspace *active_workspace, *dnd_workspace;
++
++ pager->priv->dnd_activate = 0;
++ active_workspace = wnck_screen_get_active_workspace (pager->priv->screen);
++ dnd_workspace = wnck_screen_get_workspace (pager->priv->screen,
++ pager->priv->prelight);
++
++ if (dnd_workspace &&
++ (pager->priv->prelight != wnck_workspace_get_number (active_workspace)))
++ wnck_workspace_activate (dnd_workspace, pager->priv->dnd_time);
++
++ return FALSE;
++}
++
++static void
++wnck_pager_queue_draw_workspace (WnckPager *pager,
++ gint i)
++{
++ GdkRectangle rect;
++
++ if (i < 0)
++ return;
++
++ get_workspace_rect (pager, i, &rect);
++ gtk_widget_queue_draw_area (GTK_WIDGET (pager),
++ rect.x, rect.y,
++ rect.width, rect.height);
++}
++
++static void
++wnck_pager_queue_draw_window (WnckPager *pager,
++ WnckWindow *window)
++{
++ gint workspace;
++
++ workspace = wnck_pager_window_get_workspace (window, TRUE);
++ if (workspace == -1)
++ return;
++
++ wnck_pager_queue_draw_workspace (pager, workspace);
++}
++
++static void
++wnck_pager_check_prelight (WnckPager *pager,
++ gint x,
++ gint y,
++ gboolean prelight_dnd)
++{
++ gint id;
++
++ if (x < 0 || y < 0)
++ id = -1;
++ else
++ id = workspace_at_point (pager, x, y, NULL, NULL);
++
++ if (id != pager->priv->prelight)
++ {
++ wnck_pager_queue_draw_workspace (pager, pager->priv->prelight);
++ wnck_pager_queue_draw_workspace (pager, id);
++ pager->priv->prelight = id;
++ pager->priv->prelight_dnd = prelight_dnd;
++ }
++ else if (prelight_dnd != pager->priv->prelight_dnd)
++ {
++ wnck_pager_queue_draw_workspace (pager, pager->priv->prelight);
++ pager->priv->prelight_dnd = prelight_dnd;
++ }
++}
++
++static gboolean
++wnck_pager_drag_motion (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ guint time)
++{
++ WnckPager *pager;
++ gint previous_workspace;
++
++ pager = WNCK_PAGER (widget);
++
++ previous_workspace = pager->priv->prelight;
++ wnck_pager_check_prelight (pager, x, y, TRUE);
++
++ if (gtk_drag_dest_find_target (widget, context, NULL))
++ {
++ gdk_drag_status (context, context->suggested_action, time);
++ }
++ else
++ {
++ gdk_drag_status (context, 0, time);
++
++ if (pager->priv->prelight != previous_workspace &&
++ pager->priv->dnd_activate != 0)
++ {
++ /* remove timeout, the window we hover over changed */
++ g_source_remove (pager->priv->dnd_activate);
++ pager->priv->dnd_activate = 0;
++ pager->priv->dnd_time = 0;
++ }
++
++ if (pager->priv->dnd_activate == 0 && pager->priv->prelight > -1)
++ {
++ pager->priv->dnd_activate = g_timeout_add (WNCK_ACTIVATE_TIMEOUT,
++ wnck_pager_drag_motion_timeout,
++ pager);
++ pager->priv->dnd_time = time;
++ }
++ }
++
++ return (pager->priv->prelight != -1);
++}
++
++static gboolean
++wnck_pager_drag_drop (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ guint time)
++{
++ WnckPager *pager = WNCK_PAGER (widget);
++ GdkAtom target;
++
++ target = gtk_drag_dest_find_target (widget, context, NULL);
++
++ if (target != GDK_NONE)
++ gtk_drag_get_data (widget, context, target, time);
++ else
++ gtk_drag_finish (context, FALSE, FALSE, time);
++
++ wnck_pager_clear_drag (pager);
++ wnck_pager_check_prelight (pager, x, y, FALSE);
++
++ return TRUE;
++}
++
++static void
++wnck_pager_drag_data_received (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ GtkSelectionData *selection_data,
++ guint info,
++ guint time)
++{
++ WnckPager *pager = WNCK_PAGER (widget);
++ WnckWorkspace *space;
++ GList *tmp;
++ gint i;
++ gulong xid;
++
++ if ((selection_data->length != sizeof (gulong)) ||
++ (selection_data->format != 8))
++ {
++ gtk_drag_finish (context, FALSE, FALSE, time);
++ return;
++ }
++
++ i = workspace_at_point (pager, x, y, NULL, NULL);
++ space = wnck_screen_get_workspace (pager->priv->screen, i);
++ if (!space)
++ {
++ gtk_drag_finish (context, FALSE, FALSE, time);
++ return;
++ }
++
++ xid = *((gulong *)selection_data->data);
++
++ for (tmp = wnck_screen_get_windows_stacked (pager->priv->screen); tmp != NULL; tmp = tmp->next)
++ {
++ if (wnck_window_get_xid (tmp->data) == xid)
++ {
++ WnckWindow *win = tmp->data;
++ wnck_window_move_to_workspace (win, space);
++ if (space == wnck_screen_get_active_workspace (pager->priv->screen))
++ wnck_window_activate (win, time);
++ gtk_drag_finish (context, TRUE, FALSE, time);
++ return;
++ }
++ }
++
++ gtk_drag_finish (context, FALSE, FALSE, time);
++}
++
++static void
++wnck_pager_drag_data_get (GtkWidget *widget,
++ GdkDragContext *context,
++ GtkSelectionData *selection_data,
++ guint info,
++ guint time)
++{
++ WnckPager *pager = WNCK_PAGER (widget);
++ gulong xid;
++
++ if (pager->priv->drag_window == NULL)
++ return;
++
++ xid = wnck_window_get_xid (pager->priv->drag_window);
++ gtk_selection_data_set (selection_data,
++ selection_data->target,
++ 8, (guchar *)&xid, sizeof (gulong));
++}
++
++static void
++wnck_pager_drag_end (GtkWidget *widget,
++ GdkDragContext *context)
++{
++ WnckPager *pager = WNCK_PAGER (widget);
++
++ wnck_pager_clear_drag (pager);
++}
++
++static void
++wnck_pager_drag_motion_leave (GtkWidget *widget,
++ GdkDragContext *context,
++ guint time)
++{
++ WnckPager *pager = WNCK_PAGER (widget);
++
++ if (pager->priv->dnd_activate != 0)
++ {
++ g_source_remove (pager->priv->dnd_activate);
++ pager->priv->dnd_activate = 0;
++ }
++ pager->priv->dnd_time = 0;
++ wnck_pager_check_prelight (pager, -1, -1, FALSE);
++}
++
++static void
++wnck_drag_clean_up (WnckWindow *window,
++ GdkDragContext *context,
++ gboolean clean_up_for_context_destroy,
++ gboolean clean_up_for_window_destroy);
++
++static void
++wnck_drag_context_destroyed (gpointer windowp,
++ GObject *context)
++{
++ wnck_drag_clean_up (windowp, (GdkDragContext *) context, TRUE, FALSE);
++}
++
++static void
++wnck_update_drag_icon (WnckWindow *window,
++ GdkDragContext *context)
++{
++ gint org_w, org_h, dnd_w, dnd_h;
++ WnckWorkspace *workspace;
++ GdkRectangle rect;
++ GdkPixmap *pixmap;
++ GtkWidget *widget;
++
++ widget = g_object_get_data (G_OBJECT (context), "wnck-drag-source-widget");
++ if (!widget)
++ return;
++
++ if (!gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
++ GTK_ICON_SIZE_DND, &dnd_w, &dnd_h))
++ dnd_w = dnd_h = 32;
++ /* windows are huge, so let's make this huge */
++ dnd_w *= 3;
++
++ workspace = wnck_window_get_workspace (window);
++ if (workspace == NULL)
++ workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window));
++ if (workspace == NULL)
++ return;
++
++ wnck_window_get_geometry (window, NULL, NULL, &org_w, &org_h);
++
++ rect.x = rect.y = 0;
++ rect.width = 0.5 + ((double) (dnd_w * org_w) / (double) wnck_workspace_get_width (workspace));
++ rect.width = MIN (org_w, rect.width);
++ rect.height = 0.5 + ((double) (rect.width * org_h) / (double) org_w);
++
++ /* we need at least three pixels to draw the smallest window */
++ rect.width = MAX (rect.width, 3);
++ rect.height = MAX (rect.height, 3);
++
++ pixmap = gdk_pixmap_new (GTK_WIDGET (widget)->window,
++ rect.width, rect.height, -1);
++ draw_window (GDK_DRAWABLE (pixmap), widget, window,
++ &rect, GTK_STATE_NORMAL, FALSE);
++
++ gtk_drag_set_icon_pixmap (context,
++ gdk_drawable_get_colormap (GDK_DRAWABLE (pixmap)),
++ pixmap, NULL,
++ -2, -2);
++
++ g_object_unref (pixmap);
++}
++
++static void
++wnck_drag_window_destroyed (gpointer contextp,
++ GObject *window)
++{
++ wnck_drag_clean_up ((WnckWindow *) window, GDK_DRAG_CONTEXT (contextp),
++ FALSE, TRUE);
++}
++
++static void
++wnck_drag_source_destroyed (gpointer contextp,
++ GObject *drag_source)
++{
++ g_object_steal_data (G_OBJECT (contextp), "wnck-drag-source-widget");
++}
++
++/* CAREFUL: This function is a bit brittle, because the pointers given may be
++ * finalized already */
++static void
++wnck_drag_clean_up (WnckWindow *window,
++ GdkDragContext *context,
++ gboolean clean_up_for_context_destroy,
++ gboolean clean_up_for_window_destroy)
++{
++ if (clean_up_for_context_destroy)
++ {
++ GtkWidget *drag_source;
++
++ drag_source = g_object_get_data (G_OBJECT (context),
++ "wnck-drag-source-widget");
++ if (drag_source)
++ g_object_weak_unref (G_OBJECT (drag_source),
++ wnck_drag_source_destroyed, context);
++
++ g_object_weak_unref (G_OBJECT (window),
++ wnck_drag_window_destroyed, context);
++ if (g_signal_handlers_disconnect_by_func (window,
++ wnck_update_drag_icon, context) != 2)
++ g_assert_not_reached ();
++ }
++
++ if (clean_up_for_window_destroy)
++ {
++ g_object_steal_data (G_OBJECT (context), "wnck-drag-source-widget");
++ g_object_weak_unref (G_OBJECT (context),
++ wnck_drag_context_destroyed, window);
++ }
++}
++
++/**
++ * wnck_window_set_as_drag_icon:
++ * @window: #WnckWindow to use as drag icon
++ * @context: #GdkDragContext to set the icon on
++ * @drag_source: #GtkWidget that started the drag, or one of its parent. This
++ * widget needs to stay alive as long as possible during the drag.
++ *
++ * Sets the given @window as the drag icon for @context.
++ **/
++void
++_wnck_window_set_as_drag_icon (WnckWindow *window,
++ GdkDragContext *context,
++ GtkWidget *drag_source)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++ g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
++
++ g_object_weak_ref (G_OBJECT (window), wnck_drag_window_destroyed, context);
++ g_signal_connect (window, "geometry_changed",
++ G_CALLBACK (wnck_update_drag_icon), context);
++ g_signal_connect (window, "icon_changed",
++ G_CALLBACK (wnck_update_drag_icon), context);
++
++ g_object_set_data (G_OBJECT (context), "wnck-drag-source-widget", drag_source);
++ g_object_weak_ref (G_OBJECT (drag_source), wnck_drag_source_destroyed, context);
++
++ g_object_weak_ref (G_OBJECT (context), wnck_drag_context_destroyed, window);
++
++ wnck_update_drag_icon (window, context);
++}
++
++static gboolean
++wnck_pager_motion (GtkWidget *widget,
++ GdkEventMotion *event)
++{
++ WnckPager *pager;
++ int x, y;
++
++ pager = WNCK_PAGER (widget);
++
++ gdk_window_get_pointer (widget->window, &x, &y, NULL);
++
++ if (!pager->priv->dragging &&
++ pager->priv->drag_window != NULL &&
++ gtk_drag_check_threshold (widget,
++ pager->priv->drag_start_x,
++ pager->priv->drag_start_y,
++ x, y))
++ {
++ GdkDragContext *context;
++ context = gtk_drag_begin (widget,
++ gtk_drag_dest_get_target_list (widget),
++ GDK_ACTION_MOVE,
++ 1, (GdkEvent *)event);
++ pager->priv->dragging = TRUE;
++ pager->priv->prelight_dnd = TRUE;
++ _wnck_window_set_as_drag_icon (pager->priv->drag_window,
++ context,
++ GTK_WIDGET (pager));
++ }
++
++ wnck_pager_check_prelight (pager, x, y, pager->priv->prelight_dnd);
++
++ return TRUE;
++}
++
++static gboolean
++wnck_pager_leave_notify (GtkWidget *widget,
++ GdkEventCrossing *event)
++{
++ WnckPager *pager;
++
++ pager = WNCK_PAGER (widget);
++
++ wnck_pager_check_prelight (pager, -1, -1, FALSE);
++
++ return FALSE;
++}
++
++static gboolean
++wnck_pager_button_release (GtkWidget *widget,
++ GdkEventButton *event)
++{
++ WnckWorkspace *space;
++ WnckPager *pager;
++ int i;
++ int j;
++ int viewport_x;
++ int viewport_y;
++
++ if (event->button != 1)
++ return FALSE;
++
++ pager = WNCK_PAGER (widget);
++
++ if (!pager->priv->dragging)
++ {
++ i = workspace_at_point (pager,
++ event->x, event->y,
++ &viewport_x, &viewport_y);
++ j = workspace_at_point (pager,
++ pager->priv->drag_start_x,
++ pager->priv->drag_start_y,
++ NULL, NULL);
++
++ if (i == j && i >= 0 &&
++ (space = wnck_screen_get_workspace (pager->priv->screen, i)))
++ {
++ int screen_width, screen_height;
++
++ /* Don't switch the desktop if we're already there */
++ if (space != wnck_screen_get_active_workspace (pager->priv->screen))
++ wnck_workspace_activate (space, event->time);
++
++ /* EWMH only lets us move the viewport for the active workspace,
++ * but we just go ahead and hackily assume that the activate
++ * just above takes effect prior to moving the viewport
++ */
++
++ /* Transform the pointer location to viewport origin, assuming
++ * that we want the nearest "regular" viewport containing the
++ * pointer.
++ */
++ screen_width = wnck_screen_get_width (pager->priv->screen);
++ screen_height = wnck_screen_get_height (pager->priv->screen);
++ viewport_x = (viewport_x / screen_width) * screen_width;
++ viewport_y = (viewport_y / screen_height) * screen_height;
++
++ if (wnck_workspace_get_viewport_x (space) != viewport_x ||
++ wnck_workspace_get_viewport_y (space) != viewport_y)
++ wnck_screen_move_viewport (pager->priv->screen, viewport_x, viewport_y);
++ }
++
++ wnck_pager_clear_drag (pager);
++ }
++
++ return FALSE;
++}
++
++static gboolean
++wnck_pager_focus (GtkWidget *widget,
++ GtkDirectionType direction)
++{
++ WnckPager *pager;
++
++ pager = WNCK_PAGER (widget);
++
++ return GTK_WIDGET_CLASS (wnck_pager_parent_class)->focus (widget, direction);
++}
++
++/**
++ * wnck_pager_set_screen:
++ * @pager: a #WnckPager.
++ * @screen: a #WnckScreen.
++ *
++ * Does nothing.
++ *
++ * Since: 2.2
++ * Deprecated:2.20:
++ */
++void
++wnck_pager_set_screen (WnckPager *pager,
++ WnckScreen *screen)
++{
++}
++
++static gboolean
++wnck_pager_query_tooltip (GtkWidget *widget,
++ gint x,
++ gint y,
++ gboolean keyboard_tip,
++ GtkTooltip *tooltip)
++{
++ int i;
++ WnckPager *pager;
++ WnckScreen *screen;
++ WnckWorkspace *space;
++ char *name;
++
++ pager = WNCK_PAGER (widget);
++ screen = pager->priv->screen;
++
++ i = workspace_at_point (pager, x, y, NULL, NULL);
++ space = wnck_screen_get_workspace (screen, i);
++ if (!space)
++ return GTK_WIDGET_CLASS (wnck_pager_parent_class)->query_tooltip (widget,
++ x, y,
++ keyboard_tip,
++ tooltip);
++
++ if (wnck_screen_get_active_workspace (screen) == space)
++ {
++ WnckWindow *window;
++ GdkRectangle workspace_rect;
++
++ get_workspace_rect (pager, i, &workspace_rect);
++
++ window = window_at_point (pager, space, &workspace_rect, x, y);
++
++ if (window)
++ name = g_strdup_printf (_("Click to start dragging \"%s\""),
++ wnck_window_get_name (window));
++ else
++ name = g_strdup_printf (_("Current workspace: \"%s\""),
++ wnck_workspace_get_name (space));
++ }
++ else
++ {
++ name = g_strdup_printf (_("Click to switch to \"%s\""),
++ wnck_workspace_get_name (space));
++ }
++
++ gtk_tooltip_set_text (tooltip, name);
++
++ g_free (name);
++
++ return TRUE;
++}
++
++/**
++ * wnck_pager_new:
++ * @screen: deprecated argument, can be %NULL.
++ *
++ * Creates a new #WnckPager. The #WnckPager will show the #WnckWorkspace of the
++ * #WnckScreen it is on.
++ *
++ * Return value: a newly created #WnckPager.
++ */
++/* TODO: when we break API again, remove the screen from here */
++GtkWidget*
++wnck_pager_new (WnckScreen *screen)
++{
++ WnckPager *pager;
++
++ pager = g_object_new (WNCK_TYPE_PAGER, NULL);
++
++ return GTK_WIDGET (pager);
++}
++
++static gboolean
++wnck_pager_set_layout_hint (WnckPager *pager)
++{
++ int layout_rows;
++ int layout_cols;
++
++ /* if we're not realized, we don't know about our screen yet */
++ if (pager->priv->screen == NULL)
++ _wnck_pager_set_screen (pager);
++ g_assert (pager->priv->screen != NULL);
++
++ /* The visual representation of the pager doesn't
++ * correspond to the layout of the workspaces
++ * here. i.e. the user will not pay any attention
++ * to the n_rows setting on this pager.
++ */
++ if (!pager->priv->show_all_workspaces)
++ return FALSE;
++
++ if (pager->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
++ {
++ layout_rows = pager->priv->n_rows;
++ layout_cols = 0;
++ }
++ else
++ {
++ layout_rows = 0;
++ layout_cols = pager->priv->n_rows;
++ }
++
++ pager->priv->layout_manager_token =
++ wnck_screen_try_set_workspace_layout (pager->priv->screen,
++ pager->priv->layout_manager_token,
++ layout_rows,
++ layout_cols);
++
++ return (pager->priv->layout_manager_token != WNCK_NO_MANAGER_TOKEN);
++}
++
++/**
++ * wnck_pager_set_orientation:
++ * @pager: a #WnckPager.
++ * @orientation: orientation to use for the layout of #WnckWorkspace on the
++ * #WnckScreen @pager is watching.
++ *
++ * Tries to change the orientation of the layout of #WnckWorkspace on the
++ * #WnckScreen @pager is watching. Since no more than one application should
++ * set this property of a #WnckScreen at a time, setting the layout is not
++ * guaranteed to work.
++ *
++ * If @orientation is %GTK_ORIENTATION_HORIZONTAL, the #WnckWorkspace will be
++ * laid out in rows, with the first #WnckWorkspace in the top left corner.
++ *
++ * If @orientation is %GTK_ORIENTATION_VERTICAL, the #WnckWorkspace will be
++ * laid out in columns, with the first #WnckWorkspace in the top left corner.
++ *
++ * For example, if the layout contains one row, but the orientation of the
++ * layout is vertical, the #WnckPager will display a column of #WnckWorkspace.
++ *
++ * Return value: %TRUE if the layout of #WnckWorkspace has been successfully
++ * changed or did not need to be changed, %FALSE otherwise.
++ */
++gboolean
++wnck_pager_set_orientation (WnckPager *pager,
++ GtkOrientation orientation)
++{
++ GtkOrientation old_orientation;
++ gboolean old_orientation_is_valid;
++
++ g_return_val_if_fail (WNCK_IS_PAGER (pager), FALSE);
++
++ if (pager->priv->orientation == orientation)
++ return TRUE;
++
++ old_orientation = pager->priv->orientation;
++ old_orientation_is_valid = pager->priv->screen != NULL;
++
++ pager->priv->orientation = orientation;
++
++ if (wnck_pager_set_layout_hint (pager))
++ {
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++ return TRUE;
++ }
++ else
++ {
++ if (old_orientation_is_valid)
++ pager->priv->orientation = old_orientation;
++ return FALSE;
++ }
++}
++
++/**
++ * wnck_pager_set_n_rows:
++ * @pager: a #WnckPager.
++ * @n_rows: the number of rows to use for the layout of #WnckWorkspace on the
++ * #WnckScreen @pager is watching.
++ *
++ * Tries to change the number of rows in the layout of #WnckWorkspace on the
++ * #WnckScreen @pager is watching. Since no more than one application should
++ * set this property of a #WnckScreen at a time, setting the layout is not
++ * guaranteed to work.
++ *
++ * Return value: %TRUE if the layout of #WnckWorkspace has been successfully
++ * changed or did not need to be changed, %FALSE otherwise.
++ */
++gboolean
++wnck_pager_set_n_rows (WnckPager *pager,
++ int n_rows)
++{
++ int old_n_rows;
++ gboolean old_n_rows_is_valid;
++
++ g_return_val_if_fail (WNCK_IS_PAGER (pager), FALSE);
++ g_return_val_if_fail (n_rows > 0, FALSE);
++
++ if (pager->priv->n_rows == n_rows)
++ return TRUE;
++
++ old_n_rows = pager->priv->n_rows;
++ old_n_rows_is_valid = pager->priv->screen != NULL;
++
++ pager->priv->n_rows = n_rows;
++
++ if (wnck_pager_set_layout_hint (pager))
++ {
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++ return TRUE;
++ }
++ else
++ {
++ if (old_n_rows_is_valid)
++ pager->priv->n_rows = old_n_rows;
++ return FALSE;
++ }
++}
++
++/**
++ * wnck_pager_set_display_mode:
++ * @pager: a #WnckPager.
++ * @mode: a display mode.
++ *
++ * Sets the display mode for @pager to @mode.
++ */
++void
++wnck_pager_set_display_mode (WnckPager *pager,
++ WnckPagerDisplayMode mode)
++{
++ g_return_if_fail (WNCK_IS_PAGER (pager));
++
++ if (pager->priv->display_mode == mode)
++ return;
++
++ g_object_set (pager, "has-tooltip", mode != WNCK_PAGER_DISPLAY_NAME, NULL);
++
++ pager->priv->display_mode = mode;
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++}
++
++/**
++ * wnck_pager_set_show_all:
++ * @pager: a #WnckPager.
++ * @show_all_workspaces: whether to display all #WnckWorkspace in @pager.
++ *
++ * Sets @pager to display all #WnckWorkspace or not, according to
++ * @show_all_workspaces.
++ */
++void
++wnck_pager_set_show_all (WnckPager *pager,
++ gboolean show_all_workspaces)
++{
++ g_return_if_fail (WNCK_IS_PAGER (pager));
++
++ show_all_workspaces = (show_all_workspaces != 0);
++
++ if (pager->priv->show_all_workspaces == show_all_workspaces)
++ return;
++
++ pager->priv->show_all_workspaces = show_all_workspaces;
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++}
++
++/**
++ * wnck_pager_set_shadow_type:
++ * @pager: a #WnckPager.
++ * @shadow_type: a shadow type.
++ *
++ * Sets the shadow type for @pager to @shadow_type. The main use of this
++ * function is proper integration of #WnckPager in panels with non-system
++ * backgrounds.
++ *
++ * Since: 2.2
++ */
++void
++wnck_pager_set_shadow_type (WnckPager * pager,
++ GtkShadowType shadow_type)
++{
++ g_return_if_fail (WNCK_IS_PAGER (pager));
++
++ if (pager->priv->shadow_type == shadow_type)
++ return;
++
++ pager->priv->shadow_type = shadow_type;
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++}
++
++static void
++active_window_changed_callback (WnckScreen *screen,
++ WnckWindow *previous_window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ gtk_widget_queue_draw (GTK_WIDGET (pager));
++}
++
++static void
++active_workspace_changed_callback (WnckScreen *screen,
++ WnckWorkspace *previous_workspace,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ gtk_widget_queue_draw (GTK_WIDGET (pager));
++}
++
++static void
++window_stacking_changed_callback (WnckScreen *screen,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ gtk_widget_queue_draw (GTK_WIDGET (pager));
++}
++
++static void
++window_opened_callback (WnckScreen *screen,
++ WnckWindow *window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++
++ wnck_pager_connect_window (pager, window);
++ wnck_pager_queue_draw_window (pager, window);
++}
++
++static void
++window_closed_callback (WnckScreen *screen,
++ WnckWindow *window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++
++ if (pager->priv->drag_window == window)
++ wnck_pager_clear_drag (pager);
++
++ wnck_pager_queue_draw_window (pager, window);
++}
++
++static void
++workspace_created_callback (WnckScreen *screen,
++ WnckWorkspace *space,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ g_signal_connect (space, "name_changed",
++ G_CALLBACK (workspace_name_changed_callback), pager);
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++}
++
++static void
++workspace_destroyed_callback (WnckScreen *screen,
++ WnckWorkspace *space,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ g_signal_handlers_disconnect_by_func (space, G_CALLBACK (workspace_name_changed_callback), pager);
++ gtk_widget_queue_resize (GTK_WIDGET (pager));
++}
++
++static void
++application_opened_callback (WnckScreen *screen,
++ WnckApplication *app,
++ gpointer data)
++{
++ /* WnckPager *pager = WNCK_PAGER (data); */
++}
++
++static void
++application_closed_callback (WnckScreen *screen,
++ WnckApplication *app,
++ gpointer data)
++{
++ /* WnckPager *pager = WNCK_PAGER (data); */
++}
++
++static void
++window_name_changed_callback (WnckWindow *window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ wnck_pager_queue_draw_window (pager, window);
++}
++
++static void
++window_state_changed_callback (WnckWindow *window,
++ WnckWindowState changed,
++ WnckWindowState new,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++
++ /* if the changed state changes the visibility in the pager, we need to
++ * redraw the whole workspace. wnck_pager_queue_draw_window() might not be
++ * enough */
++ if (!wnck_pager_window_state_is_relevant (changed))
++ wnck_pager_queue_draw_workspace (pager,
++ wnck_pager_window_get_workspace (window,
++ FALSE));
++ else
++ wnck_pager_queue_draw_window (pager, window);
++}
++
++static void
++window_workspace_changed_callback (WnckWindow *window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ gtk_widget_queue_draw (GTK_WIDGET (pager));
++}
++
++static void
++window_icon_changed_callback (WnckWindow *window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++ wnck_pager_queue_draw_window (pager, window);
++}
++
++static void
++window_geometry_changed_callback (WnckWindow *window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++
++ wnck_pager_queue_draw_window (pager, window);
++}
++
++static void
++background_changed_callback (WnckWindow *window,
++ gpointer data)
++{
++ WnckPager *pager = WNCK_PAGER (data);
++
++ if (pager->priv->bg_cache)
++ {
++ g_object_unref (G_OBJECT (pager->priv->bg_cache));
++ pager->priv->bg_cache = NULL;
++ }
++
++ gtk_widget_queue_draw (GTK_WIDGET (pager));
++}
++
++static void
++workspace_name_changed_callback (WnckWorkspace *space,
++ gpointer data)
++{
++ gtk_widget_queue_resize (GTK_WIDGET (data));
++}
++
++static void
++viewports_changed_callback (WnckWorkspace *space,
++ gpointer data)
++{
++ gtk_widget_queue_resize (GTK_WIDGET (data));
++}
++
++static void
++wnck_pager_connect_screen (WnckPager *pager)
++{
++ int i;
++ guint *c;
++ GList *tmp;
++ WnckScreen *screen;
++
++ g_return_if_fail (pager->priv->screen != NULL);
++
++ screen = pager->priv->screen;
++
++ for (tmp = wnck_screen_get_windows (screen); tmp; tmp = tmp->next)
++ {
++ wnck_pager_connect_window (pager, WNCK_WINDOW (tmp->data));
++ }
++
++ i = 0;
++ c = pager->priv->screen_connections;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "active_window_changed",
++ G_CALLBACK (active_window_changed_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "active_workspace_changed",
++ G_CALLBACK (active_workspace_changed_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "window_stacking_changed",
++ G_CALLBACK (window_stacking_changed_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "window_opened",
++ G_CALLBACK (window_opened_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "window_closed",
++ G_CALLBACK (window_closed_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "workspace_created",
++ G_CALLBACK (workspace_created_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "workspace_destroyed",
++ G_CALLBACK (workspace_destroyed_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "application_opened",
++ G_CALLBACK (application_opened_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "application_closed",
++ G_CALLBACK (application_closed_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "background_changed",
++ G_CALLBACK (background_changed_callback),
++ pager);
++ ++i;
++
++ c[i] = g_signal_connect (G_OBJECT (screen), "viewports_changed",
++ G_CALLBACK (viewports_changed_callback),
++ pager);
++ ++i;
++
++ g_assert (i == N_SCREEN_CONNECTIONS);
++
++ /* connect to name_changed on each workspace */
++ for (i = 0; i < wnck_screen_get_workspace_count (pager->priv->screen); i++)
++ {
++ WnckWorkspace *space;
++ space = wnck_screen_get_workspace (pager->priv->screen, i);
++ g_signal_connect (space, "name_changed",
++ G_CALLBACK (workspace_name_changed_callback), pager);
++ }
++}
++
++static void
++wnck_pager_connect_window (WnckPager *pager,
++ WnckWindow *window)
++{
++ g_signal_connect_object (G_OBJECT (window), "name_changed",
++ G_CALLBACK (window_name_changed_callback),
++ pager, 0);
++ g_signal_connect_object (G_OBJECT (window), "state_changed",
++ G_CALLBACK (window_state_changed_callback),
++ pager, 0);
++ g_signal_connect_object (G_OBJECT (window), "workspace_changed",
++ G_CALLBACK (window_workspace_changed_callback),
++ pager, 0);
++ g_signal_connect_object (G_OBJECT (window), "icon_changed",
++ G_CALLBACK (window_icon_changed_callback),
++ pager, 0);
++ g_signal_connect_object (G_OBJECT (window), "geometry_changed",
++ G_CALLBACK (window_geometry_changed_callback),
++ pager, 0);
++}
++
++static void
++wnck_pager_disconnect_screen (WnckPager *pager)
++{
++ int i;
++
++ if (pager->priv->screen == NULL)
++ return;
++
++ i = 0;
++ while (i < N_SCREEN_CONNECTIONS)
++ {
++ if (pager->priv->screen_connections[i] != 0)
++ g_signal_handler_disconnect (G_OBJECT (pager->priv->screen),
++ pager->priv->screen_connections[i]);
++
++ pager->priv->screen_connections[i] = 0;
++
++ ++i;
++ }
++
++ for (i = 0; i < wnck_screen_get_workspace_count (pager->priv->screen); i++)
++ {
++ WnckWorkspace *space;
++ space = wnck_screen_get_workspace (pager->priv->screen, i);
++ g_signal_handlers_disconnect_by_func (space, G_CALLBACK (workspace_name_changed_callback), pager);
++ }
++}
++
++static void
++wnck_pager_clear_drag (WnckPager *pager)
++{
++ if (pager->priv->dragging)
++ wnck_pager_queue_draw_window (pager, pager->priv->drag_window);
++
++ pager->priv->dragging = FALSE;
++ pager->priv->drag_window = NULL;
++ pager->priv->drag_start_x = -1;
++ pager->priv->drag_start_y = -1;
++}
++
++static GdkPixbuf*
++wnck_pager_get_background (WnckPager *pager,
++ int width,
++ int height)
++{
++ Pixmap p;
++ GdkPixbuf *pix = NULL;
++
++ /* We have to be careful not to keep alternating between
++ * width/height values, otherwise this would get really slow.
++ */
++ if (pager->priv->bg_cache &&
++ gdk_pixbuf_get_width (pager->priv->bg_cache) == width &&
++ gdk_pixbuf_get_height (pager->priv->bg_cache) == height)
++ return pager->priv->bg_cache;
++
++ if (pager->priv->bg_cache)
++ {
++ g_object_unref (G_OBJECT (pager->priv->bg_cache));
++ pager->priv->bg_cache = NULL;
++ }
++
++ if (pager->priv->screen == NULL)
++ return NULL;
++
++ /* FIXME this just globally disables the thumbnailing feature */
++ return NULL;
++
++#define MIN_BG_SIZE 10
++
++ if (width < MIN_BG_SIZE || height < MIN_BG_SIZE)
++ return NULL;
++
++ p = wnck_screen_get_background_pixmap (pager->priv->screen);
++
++ if (p != None)
++ {
++ _wnck_error_trap_push ();
++ pix = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
++ p,
++ 0, 0, 0, 0,
++ -1, -1);
++ _wnck_error_trap_pop ();
++ }
++
++ if (pix)
++ {
++ pager->priv->bg_cache = gdk_pixbuf_scale_simple (pix,
++ width,
++ height,
++ GDK_INTERP_BILINEAR);
++
++ g_object_unref (G_OBJECT (pix));
++ }
++
++ return pager->priv->bg_cache;
++}
++
++/*
++ *This will return aobj_pager whose parent is wnck's atk object -Gail Container
++ */
++static AtkObject *
++wnck_pager_get_accessible (GtkWidget *widget)
++{
++ static gboolean first_time = TRUE;
++
++ if (first_time)
++ {
++ AtkObjectFactory *factory;
++ AtkRegistry *registry;
++ GType derived_type;
++ GType derived_atk_type;
++
++ /*
++ * Figure out whether accessibility is enabled by looking at the
++ * type of the accessible object which would be created for
++ * the parent type WnckPager.
++ */
++ derived_type = g_type_parent (WNCK_TYPE_PAGER);
++
++ registry = atk_get_default_registry ();
++ factory = atk_registry_get_factory (registry,
++ derived_type);
++ derived_atk_type = atk_object_factory_get_accessible_type (factory);
++
++ if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
++ {
++ /*
++ * Specify what factory to use to create accessible
++ * objects
++ */
++ atk_registry_set_factory_type (registry,
++ WNCK_TYPE_PAGER,
++ WNCK_TYPE_PAGER_ACCESSIBLE_FACTORY);
++
++ atk_registry_set_factory_type (registry,
++ WNCK_TYPE_WORKSPACE,
++ WNCK_TYPE_WORKSPACE_ACCESSIBLE_FACTORY);
++ }
++ first_time = FALSE;
++ }
++ return GTK_WIDGET_CLASS (wnck_pager_parent_class)->get_accessible (widget);
++}
++
++int
++_wnck_pager_get_n_workspaces (WnckPager *pager)
++{
++ return wnck_screen_get_workspace_count (pager->priv->screen);
++}
++
++const char*
++_wnck_pager_get_workspace_name (WnckPager *pager,
++ int i)
++{
++ WnckWorkspace *space;
++
++ space = wnck_screen_get_workspace (pager->priv->screen, i);
++ if (space)
++ return wnck_workspace_get_name (space);
++ else
++ return NULL;
++}
++
++WnckWorkspace*
++_wnck_pager_get_active_workspace (WnckPager *pager)
++{
++ return wnck_screen_get_active_workspace (pager->priv->screen);
++}
++
++WnckWorkspace*
++_wnck_pager_get_workspace (WnckPager *pager,
++ int i)
++{
++ return wnck_screen_get_workspace (pager->priv->screen, i);
++}
++
++void
++_wnck_pager_activate_workspace (WnckWorkspace *wspace,
++ guint32 timestamp)
++{
++ wnck_workspace_activate (wspace, timestamp);
++}
++
++void
++_wnck_pager_get_workspace_rect (WnckPager *pager,
++ int i,
++ GdkRectangle *rect)
++{
++ get_workspace_rect (pager, i, rect);
++}
+diff -urN libwnck.orig/libwnck/private.h libwnck.new/libwnck/private.h
+--- libwnck.orig/libwnck/private.h 2007-11-30 14:02:04.876325000 +0000
++++ libwnck.new/libwnck/private.h 2007-11-30 14:02:34.601732000 +0000
+@@ -35,6 +35,9 @@
#ifdef HAVE_STARTUP_NOTIFICATION
#include <libsn/sn.h>
#endif
@@ -218,7 +2836,7 @@
G_BEGIN_DECLS
-@@ -87,9 +90,23 @@ void _wnck_application_destr
+@@ -102,9 +105,23 @@
void _wnck_workspace_update_name (WnckWorkspace *workspace,
const char *name);
@@ -242,10 +2860,164 @@
gboolean _wnck_workspace_set_geometry (WnckWorkspace *space, int w, int h);
gboolean _wnck_workspace_set_viewport (WnckWorkspace *space, int x, int y);
-diff -Nrup libwnck-2.19.4/libwnck/screen.c ../libwnck-2.19.4/libwnck/screen.c
---- libwnck-2.19.4/libwnck/screen.c 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.4/libwnck/screen.c 2007-06-27 15:10:48.888928000 +0200
-@@ -32,11 +32,17 @@
+diff -urN libwnck.orig/libwnck/private.h.orig libwnck.new/libwnck/private.h.orig
+--- libwnck.orig/libwnck/private.h.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/private.h.orig 2007-11-30 14:02:08.626655000 +0000
+@@ -0,0 +1,150 @@
++/* Private stuff */
++/* vim: set sw=2 et: */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2006-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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.
++ */
++
++#ifndef WNCK_PRIVATE_H
++#define WNCK_PRIVATE_H
++
++#include <config.h>
++#include "screen.h"
++#include "window.h"
++#include "workspace.h"
++#include "application.h"
++#include "xutils.h"
++#include "pager.h"
++#include "util.h"
++#ifdef HAVE_STARTUP_NOTIFICATION
++#include <libsn/sn.h>
++#endif
++
++G_BEGIN_DECLS
++
++#define WNCK_ACTIVATE_TIMEOUT 1000
++
++WnckClientType _wnck_get_client_type (void);
++
++void _wnck_application_process_property_notify (WnckApplication *app,
++ XEvent *xevent);
++void _wnck_window_process_property_notify (WnckWindow *window,
++ XEvent *xevent);
++
++void _wnck_screen_process_property_notify (WnckScreen *screen,
++ XEvent *xevent);
++void _wnck_window_process_configure_notify (WnckWindow *window,
++ XEvent *xevent);
++WnckWindow* _wnck_window_create (Window xwindow,
++ WnckScreen *screen,
++ gint sort_order);
++void _wnck_window_destroy (WnckWindow *window);
++
++char* _wnck_window_get_name_for_display (WnckWindow *window,
++ gboolean use_icon_name,
++ gboolean use_state_decorations);
++const char* _wnck_window_get_startup_id (WnckWindow *window);
++
++const char* _wnck_window_get_resource_class (WnckWindow *window);
++const char* _wnck_window_get_resource_name (WnckWindow *window);
++
++time_t _wnck_window_get_needs_attention_time (WnckWindow *window);
++time_t _wnck_window_or_transient_get_needs_attention_time (WnckWindow *window);
++
++WnckWorkspace* _wnck_workspace_create (int number,
++ WnckScreen *screen);
++void _wnck_workspace_destroy (WnckWorkspace *space);
++
++void _wnck_window_set_application (WnckWindow *window,
++ WnckApplication *app);
++
++void _wnck_window_set_class_group (WnckWindow *window,
++ WnckClassGroup *class_group);
++
++/* this one is in pager.c since it needs code from there to draw the icon */
++void
++_wnck_window_set_as_drag_icon (WnckWindow *window,
++ GdkDragContext *context,
++ GtkWidget *drag_source);
++
++void _wnck_application_add_window (WnckApplication *app,
++ WnckWindow *window);
++void _wnck_application_remove_window (WnckApplication *app,
++ WnckWindow *window);
++
++WnckApplication* _wnck_application_create (Window xwindow,
++ WnckScreen *screen);
++void _wnck_application_destroy (WnckApplication *app);
++
++
++WnckClassGroup* _wnck_class_group_create (const char *res_class);
++void _wnck_class_group_destroy (WnckClassGroup *class_group);
++void _wnck_class_group_add_window (WnckClassGroup *class_group,
++ WnckWindow *window);
++void _wnck_class_group_remove_window (WnckClassGroup *class_group,
++ WnckWindow *window);
++
++void _wnck_workspace_update_name (WnckWorkspace *workspace,
++ const char *name);
++void _wnck_screen_change_workspace_name (WnckScreen *screen,
++ int number,
++ const char *name);
++
++gboolean _wnck_workspace_set_geometry (WnckWorkspace *space, int w, int h);
++gboolean _wnck_workspace_set_viewport (WnckWorkspace *space, int x, int y);
++
++void _wnck_init (void);
++
++#define DEFAULT_ICON_WIDTH 32
++#define DEFAULT_ICON_HEIGHT 32
++#define DEFAULT_MINI_ICON_WIDTH 16
++#define DEFAULT_MINI_ICON_HEIGHT 16
++
++#define WNCK_SCREEN_XSCREEN(screen) (_wnck_screen_get_xscreen (screen))
++
++Screen *_wnck_screen_get_xscreen (WnckScreen *screen);
++GdkScreen *_wnck_screen_get_gdk_screen (WnckScreen *screen);
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++SnDisplay* _wnck_screen_get_sn_display (WnckScreen *screen);
++#endif
++
++WnckScreen* _wnck_screen_get_existing (int number);
++
++void _wnck_pager_activate_workspace (WnckWorkspace *wspace,
++ guint32 timestamp);
++int _wnck_pager_get_n_workspaces (WnckPager *pager);
++const char* _wnck_pager_get_workspace_name (WnckPager *pager,
++ int i);
++WnckWorkspace* _wnck_pager_get_active_workspace (WnckPager *pager);
++WnckWorkspace* _wnck_pager_get_workspace (WnckPager *pager,
++ int i);
++void _wnck_pager_get_workspace_rect (WnckPager *pager,
++ int i,
++ GdkRectangle *rect);
++
++void _make_gtk_label_bold (GtkLabel *label);
++void _make_gtk_label_normal (GtkLabel *label);
++
++void _wnck_stock_icons_init (void);
++
++
++G_END_DECLS
++
++#endif /* WNCK_PRIVATE_H */
+diff -urN libwnck.orig/libwnck/screen.c libwnck.new/libwnck/screen.c
+--- libwnck.orig/libwnck/screen.c 2007-11-30 14:02:04.924754000 +0000
++++ libwnck.new/libwnck/screen.c 2007-11-30 14:04:07.189107000 +0000
+@@ -33,11 +33,17 @@
#include "class-group.h"
#include "xutils.h"
#include "private.h"
@@ -263,7 +3035,7 @@
/**
* SECTION:screen
* @short_description: an object representing a screen.
-@@ -121,6 +127,10 @@ struct _WnckScreenPrivate
+@@ -122,6 +128,10 @@
guint need_update_active_window : 1;
guint need_update_workspace_layout : 1;
guint need_update_workspace_names : 1;
@@ -274,7 +3046,7 @@
guint need_update_bg_pixmap : 1;
guint need_update_showing_desktop : 1;
guint need_update_wm : 1;
-@@ -158,6 +168,10 @@ static void update_active_workspace (W
+@@ -160,6 +170,10 @@
static void update_active_window (WnckScreen *screen);
static void update_workspace_layout (WnckScreen *screen);
static void update_workspace_names (WnckScreen *screen);
@@ -285,7 +3057,7 @@
static void update_showing_desktop (WnckScreen *screen);
static void queue_update (WnckScreen *screen);
-@@ -548,6 +562,10 @@ wnck_screen_construct (WnckScreen *scree
+@@ -602,6 +616,10 @@
screen->priv->need_update_active_window = TRUE;
screen->priv->need_update_workspace_layout = TRUE;
screen->priv->need_update_workspace_names = TRUE;
@@ -296,7 +3068,7 @@
screen->priv->need_update_bg_pixmap = TRUE;
screen->priv->need_update_showing_desktop = TRUE;
screen->priv->need_update_wm = TRUE;
-@@ -1044,6 +1062,20 @@ _wnck_screen_process_property_notify (Wn
+@@ -1124,6 +1142,20 @@
screen->priv->need_update_workspace_names = TRUE;
queue_update (screen);
}
@@ -317,7 +3089,7 @@
else if (xevent->xproperty.atom ==
_wnck_atom_get ("_XROOTPMAP_ID"))
{
-@@ -2135,6 +2167,85 @@ update_workspace_names (WnckScreen *scre
+@@ -2223,6 +2255,85 @@
g_list_free (copy);
}
@@ -403,7 +3175,7 @@
static void
update_bg_pixmap (WnckScreen *screen)
{
-@@ -2220,6 +2331,10 @@ do_update_now (WnckScreen *screen)
+@@ -2308,6 +2419,10 @@
{
screen->priv->need_update_viewport_settings = TRUE;
screen->priv->need_update_workspace_names = TRUE;
@@ -414,7 +3186,7 @@
}
/* First get our big-picture state in order */
-@@ -2232,6 +2347,15 @@ do_update_now (WnckScreen *screen)
+@@ -2320,6 +2435,15 @@
update_active_window (screen);
update_workspace_layout (screen);
update_workspace_names (screen);
@@ -430,7 +3202,7 @@
update_showing_desktop (screen);
update_wm (screen);
-@@ -2730,3 +2854,81 @@ _wnck_screen_change_workspace_name (Wnck
+@@ -2818,3 +2942,85 @@
g_free (names);
}
@@ -475,6 +3247,10 @@
+ }
+
+ tmp_space = wnck_screen_get_workspace (screen, number);
++ /* need to refresh the role cache if an app is calling change label after setting
++ * directly the _NET_DESKTOP_ROLES as this wouldn't have been updated in libwnck yet */
++ screen->priv->need_update_workspace_roles = TRUE;
++ update_workspace_roles (screen);
+ workspace_range = _wnck_workspace_get_range (tmp_space);
+
+ if (!libtsol_blinrange (mlabel, workspace_range)) {
@@ -512,10 +3288,2834 @@
+ g_free (labels);
+}
+#endif /* HAVE_XTSOL */
-diff -Nrup libwnck-2.19.5/libwnck/tasklist.c ../libwnck-2.19.5/libwnck/tasklist.c
---- libwnck-2.19.5/libwnck/tasklist.c 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.5/libwnck/tasklist.c 2007-06-27 15:15:26.887099000 +0200
-@@ -35,6 +39,10 @@
+diff -urN libwnck.orig/libwnck/screen.c.orig libwnck.new/libwnck/screen.c.orig
+--- libwnck.orig/libwnck/screen.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/screen.c.orig 2007-11-30 14:02:08.626925000 +0000
+@@ -0,0 +1,2820 @@
++/* screen object */
++/* vim: set sw=2 et: */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2003 Kim Woelders
++ * Copyright (C) 2003 Red Hat, Inc.
++ * Copyright (C) 2006-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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 <glib/gi18n-lib.h>
++#include "screen.h"
++#include "window.h"
++#include "workspace.h"
++#include "application.h"
++#include "class-group.h"
++#include "xutils.h"
++#include "private.h"
++#include <gdk/gdk.h>
++#include <gdk/gdkx.h>
++#include <string.h>
++#include <stdlib.h>
++
++/**
++ * SECTION:screen
++ * @short_description: an object representing a screen.
++ * @see_also: #WnckWindow, #WnckWorkspace
++ * @stability: Unstable
++ *
++ * The #WnckScreen represents a physical screen. A screen may consist of
++ * multiple monitors which are merged to form a large screen area. The
++ * #WnckScreen is at the bottom of the libwnck stack of objects: #WnckWorkspace
++ * objects exist a #WnckScreen and #WnckWindow objects are displayed on a
++ * #WnckWorkspace.
++ *
++ * The #WnckScreen corresponds to the notion of
++ * <classname>GdkScreen</classname> in GDK.
++ *
++ * The #WnckScreen objects are always owned by libwnck and must not be
++ * referenced or unreferenced.
++ */
++
++#define _NET_WM_ORIENTATION_HORZ 0
++#define _NET_WM_ORIENTATION_VERT 1
++
++#define _NET_WM_TOPLEFT 0
++#define _NET_WM_TOPRIGHT 1
++#define _NET_WM_BOTTOMRIGHT 2
++#define _NET_WM_BOTTOMLEFT 3
++
++static WnckScreen** screens = NULL;
++
++struct _WnckScreenPrivate
++{
++ int number;
++ Window xroot;
++ Screen *xscreen;
++
++ /* in map order */
++ GList *mapped_windows;
++ /* in stacking order */
++ GList *stacked_windows;
++ /* in 0-to-N order */
++ GList *workspaces;
++
++ /* previously_active_window is used in tandem with active_window to
++ * determine return status of wnck_window_is_most_recently_actived().
++ * These are usually shared for all screens, although this is not guaranteed
++ * to be true.
++ */
++ WnckWindow *active_window;
++ WnckWindow *previously_active_window;
++
++ WnckWorkspace *active_workspace;
++
++ /* Provides the sorting order number for the next window, to make
++ * sure windows remain sorted in the order they appear.
++ */
++ gint window_order;
++
++ Pixmap bg_pixmap;
++
++ char *wm_name;
++
++ guint update_handler;
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ SnDisplay *sn_display;
++#endif
++
++ guint showing_desktop : 1;
++
++ guint vertical_workspaces : 1;
++ _WnckLayoutCorner starting_corner;
++ gint rows_of_workspaces;
++ gint columns_of_workspaces;
++
++ /* if you add flags, be sure to set them
++ * when we create the screen so we get an initial update
++ */
++ guint need_update_stack_list : 1;
++ guint need_update_workspace_list : 1;
++ guint need_update_viewport_settings : 1;
++ guint need_update_active_workspace : 1;
++ guint need_update_active_window : 1;
++ guint need_update_workspace_layout : 1;
++ guint need_update_workspace_names : 1;
++ guint need_update_bg_pixmap : 1;
++ guint need_update_showing_desktop : 1;
++ guint need_update_wm : 1;
++};
++
++G_DEFINE_TYPE (WnckScreen, wnck_screen, G_TYPE_OBJECT);
++#define WNCK_SCREEN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WNCK_TYPE_SCREEN, WnckScreenPrivate))
++
++enum {
++ ACTIVE_WINDOW_CHANGED,
++ ACTIVE_WORKSPACE_CHANGED,
++ WINDOW_STACKING_CHANGED,
++ WINDOW_OPENED,
++ WINDOW_CLOSED,
++ WORKSPACE_CREATED,
++ WORKSPACE_DESTROYED,
++ APPLICATION_OPENED,
++ APPLICATION_CLOSED,
++ CLASS_GROUP_OPENED,
++ CLASS_GROUP_CLOSED,
++ BACKGROUND_CHANGED,
++ SHOWING_DESKTOP_CHANGED,
++ VIEWPORTS_CHANGED,
++ WM_CHANGED,
++ LAST_SIGNAL
++};
++
++static void wnck_screen_init (WnckScreen *screen);
++static void wnck_screen_class_init (WnckScreenClass *klass);
++static void wnck_screen_finalize (GObject *object);
++
++static void update_client_list (WnckScreen *screen);
++static void update_workspace_list (WnckScreen *screen);
++static void update_viewport_settings (WnckScreen *screen);
++static void update_active_workspace (WnckScreen *screen);
++static void update_active_window (WnckScreen *screen);
++static void update_workspace_layout (WnckScreen *screen);
++static void update_workspace_names (WnckScreen *screen);
++static void update_showing_desktop (WnckScreen *screen);
++
++static void queue_update (WnckScreen *screen);
++static void unqueue_update (WnckScreen *screen);
++static void do_update_now (WnckScreen *screen);
++
++static void emit_active_window_changed (WnckScreen *screen);
++static void emit_active_workspace_changed (WnckScreen *screen,
++ WnckWorkspace *previous_space);
++static void emit_window_stacking_changed (WnckScreen *screen);
++static void emit_window_opened (WnckScreen *screen,
++ WnckWindow *window);
++static void emit_window_closed (WnckScreen *screen,
++ WnckWindow *window);
++static void emit_workspace_created (WnckScreen *screen,
++ WnckWorkspace *space);
++static void emit_workspace_destroyed (WnckScreen *screen,
++ WnckWorkspace *space);
++static void emit_application_opened (WnckScreen *screen,
++ WnckApplication *app);
++static void emit_application_closed (WnckScreen *screen,
++ WnckApplication *app);
++static void emit_class_group_opened (WnckScreen *screen,
++ WnckClassGroup *class_group);
++static void emit_class_group_closed (WnckScreen *screen,
++ WnckClassGroup *class_group);
++static void emit_background_changed (WnckScreen *screen);
++static void emit_showing_desktop_changed (WnckScreen *screen);
++static void emit_viewports_changed (WnckScreen *screen);
++static void emit_wm_changed (WnckScreen *screen);
++
++static guint signals[LAST_SIGNAL] = { 0 };
++
++static void
++wnck_screen_init (WnckScreen *screen)
++{
++ screen->priv = WNCK_SCREEN_GET_PRIVATE (screen);
++
++ screen->priv->number = -1;
++ screen->priv->xroot = None;
++ screen->priv->xscreen = NULL;
++
++ screen->priv->mapped_windows = NULL;
++ screen->priv->stacked_windows = NULL;
++ screen->priv->workspaces = NULL;
++
++ screen->priv->active_window = NULL;
++ screen->priv->previously_active_window = NULL;
++
++ screen->priv->active_workspace = NULL;
++
++ screen->priv->window_order = 0;
++
++ screen->priv->bg_pixmap = None;
++
++ screen->priv->wm_name = NULL;
++
++ screen->priv->update_handler = 0;
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ screen->priv->sn_display = NULL;
++#endif
++
++ screen->priv->showing_desktop = FALSE;
++
++ screen->priv->vertical_workspaces = FALSE;
++ screen->priv->starting_corner = WNCK_LAYOUT_CORNER_TOPLEFT;
++ screen->priv->rows_of_workspaces = 1;
++ screen->priv->columns_of_workspaces = -1;
++
++ screen->priv->need_update_stack_list = FALSE;
++ screen->priv->need_update_workspace_list = FALSE;
++ screen->priv->need_update_viewport_settings = FALSE;
++ screen->priv->need_update_active_workspace = FALSE;
++ screen->priv->need_update_active_window = FALSE;
++ screen->priv->need_update_workspace_layout = FALSE;
++ screen->priv->need_update_workspace_names = FALSE;
++ screen->priv->need_update_bg_pixmap = FALSE;
++ screen->priv->need_update_showing_desktop = FALSE;
++ screen->priv->need_update_wm = FALSE;
++}
++
++static void
++wnck_screen_class_init (WnckScreenClass *klass)
++{
++ GObjectClass *object_class = G_OBJECT_CLASS (klass);
++
++ _wnck_init ();
++
++ g_type_class_add_private (klass, sizeof (WnckScreenPrivate));
++
++ object_class->finalize = wnck_screen_finalize;
++
++ /**
++ * WnckScreen::active-window-changed:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @previously_active_window: the previously active #WnckWindow before this
++ * change.
++ *
++ * Emitted when the active window on @screen has changed.
++ */
++ signals[ACTIVE_WINDOW_CHANGED] =
++ g_signal_new ("active_window_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, active_window_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_WINDOW);
++
++ /**
++ * WnckScreen::active-workspace-changed:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @previously_active_space: the previously active #WnckWorkspace before this
++ * change.
++ *
++ * Emitted when the active workspace on @screen has changed.
++ */
++ signals[ACTIVE_WORKSPACE_CHANGED] =
++ g_signal_new ("active_workspace_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, active_workspace_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_WORKSPACE);
++
++ /**
++ * WnckScreen::window-stacking-changed:
++ * @screen: the #WnckScreen which emitted the signal.
++ *
++ * Emitted when the stacking order of #WnckWindow on @screen has changed.
++ */
++ signals[WINDOW_STACKING_CHANGED] =
++ g_signal_new ("window_stacking_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, window_stacking_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++
++ /**
++ * WnckScreen::window-opened:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @window: the opened #WnckWindow.
++ *
++ * Emitted when a new #WnckWindow is opened on @screen.
++ */
++ signals[WINDOW_OPENED] =
++ g_signal_new ("window_opened",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, window_opened),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_WINDOW);
++
++ /**
++ * WnckScreen::window-closed:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @window: the closed #WnckWindow.
++ *
++ * Emitted when a #WnckWindow is closed on @screen.
++ */
++ signals[WINDOW_CLOSED] =
++ g_signal_new ("window_closed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, window_closed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_WINDOW);
++
++ /**
++ * WnckScreen::workspace-created:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @space: the workspace that has been created.
++ *
++ * Emitted when a #WnckWorkspace is created on @screen.
++ */
++ signals[WORKSPACE_CREATED] =
++ g_signal_new ("workspace_created",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, workspace_created),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_WORKSPACE);
++
++ /**
++ * WnckScreen::workspace-destroyed:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @space: the workspace that has been destroyed.
++ *
++ * Emitted when a #WnckWorkspace is destroyed on @screen.
++ */
++ signals[WORKSPACE_DESTROYED] =
++ g_signal_new ("workspace_destroyed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, workspace_destroyed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_WORKSPACE);
++
++ /**
++ * WnckScreen::application-opened:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @app: the opened #WnckApplication.
++ *
++ * Emitted when a new #WnckApplication is opened on @screen.
++ */
++ signals[APPLICATION_OPENED] =
++ g_signal_new ("application_opened",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, application_opened),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_APPLICATION);
++
++ /**
++ * WnckScreen::application-closed:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @app: the closed #WnckApplication.
++ *
++ * Emitted when a #WnckApplication is closed on @screen.
++ */
++ signals[APPLICATION_CLOSED] =
++ g_signal_new ("application_closed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, application_closed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_APPLICATION);
++
++ /**
++ * WnckScreen::class-group-opened:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @class_group: the opened #WnckClassGroup.
++ *
++ * Emitted when a new #WnckClassGroup is opened on @screen.
++ *
++ * Since: 2.20
++ */
++ signals[CLASS_GROUP_OPENED] =
++ g_signal_new ("class_group_opened",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, class_group_opened),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_CLASS_GROUP);
++
++ /**
++ * WnckScreen::class-group-closed:
++ * @screen: the #WnckScreen which emitted the signal.
++ * @class_group: the closed #WnckClassGroup.
++ *
++ * Emitted when a #WnckClassGroup is closed on @screen.
++ *
++ * Since: 2.20
++ */
++ signals[CLASS_GROUP_CLOSED] =
++ g_signal_new ("class_group_closed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, class_group_closed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__OBJECT,
++ G_TYPE_NONE, 1, WNCK_TYPE_CLASS_GROUP);
++
++ /**
++ * WnckScreen::background-changed:
++ * @screen: the #WnckScreen which emitted the signal.
++ *
++ * Emitted when the background on the root window of @screen has changed.
++ */
++ signals[BACKGROUND_CHANGED] =
++ g_signal_new ("background_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, background_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++
++ /**
++ * WnckScreen::showing-desktop-changed:
++ * @screen: the #WnckScreen which emitted the signal.
++ *
++ * Emitted when "showing the desktop" mode of @screen is toggled.
++ *
++ * Since: 2.20
++ */
++ signals[SHOWING_DESKTOP_CHANGED] =
++ g_signal_new ("showing_desktop_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, showing_desktop_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++
++ /**
++ * WnckScreen::viewports-changed:
++ * @screen: the #WnckScreen which emitted the signal.
++ *
++ * Emitted when a viewport position has changed in a #WnckWorkspace of
++ * @screen or when a #WnckWorkspace of @screen gets or loses its viewport.
++ *
++ * Since: 2.20
++ */
++ signals[VIEWPORTS_CHANGED] =
++ g_signal_new ("viewports_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, viewports_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++
++ /**
++ * WnckScreen::window-manager-changed:
++ * @screen: the #WnckScreen which emitted the signal.
++ *
++ * Emitted when the window manager on @screen has changed.
++ *
++ * Since: 2.20
++ */
++ signals[WM_CHANGED] =
++ g_signal_new ("window_manager_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckScreenClass, window_manager_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++}
++
++static void
++wnck_screen_finalize (GObject *object)
++{
++ WnckScreen *screen;
++ GList *tmp;
++ gpointer weak_pointer;
++
++ screen = WNCK_SCREEN (object);
++
++ unqueue_update (screen);
++
++ for (tmp = screen->priv->stacked_windows; tmp; tmp = tmp->next)
++ {
++ screen->priv->mapped_windows = g_list_remove (screen->priv->mapped_windows,
++ tmp->data);
++ _wnck_window_destroy (WNCK_WINDOW (tmp->data));
++ }
++
++ for (tmp = screen->priv->mapped_windows; tmp; tmp = tmp->next)
++ _wnck_window_destroy (WNCK_WINDOW (tmp->data));
++
++ for (tmp = screen->priv->workspaces; tmp; tmp = tmp->next)
++ g_object_unref (tmp->data);
++
++ g_list_free (screen->priv->mapped_windows);
++ screen->priv->mapped_windows = NULL;
++ g_list_free (screen->priv->stacked_windows);
++ screen->priv->stacked_windows = NULL;
++
++ g_list_free (screen->priv->workspaces);
++ screen->priv->workspaces = NULL;
++
++ weak_pointer = &screen->priv->active_window;
++ if (screen->priv->active_window != NULL)
++ g_object_remove_weak_pointer (G_OBJECT (screen->priv->active_window),
++ weak_pointer);
++ screen->priv->active_window = NULL;
++
++ weak_pointer = &screen->priv->previously_active_window;
++ if (screen->priv->previously_active_window != NULL)
++ g_object_remove_weak_pointer (G_OBJECT (screen->priv->previously_active_window),
++ weak_pointer);
++ screen->priv->previously_active_window = NULL;
++
++ g_free (screen->priv->wm_name);
++ screen->priv->wm_name = NULL;
++
++ screens[screen->priv->number] = NULL;
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ sn_display_unref (screen->priv->sn_display);
++ screen->priv->sn_display = NULL;
++#endif
++
++ G_OBJECT_CLASS (wnck_screen_parent_class)->finalize (object);
++}
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++static void
++sn_error_trap_push (SnDisplay *display,
++ Display *xdisplay)
++{
++ gdk_error_trap_push ();
++}
++
++static void
++sn_error_trap_pop (SnDisplay *display,
++ Display *xdisplay)
++{
++ gdk_error_trap_pop ();
++}
++#endif /* HAVE_STARTUP_NOTIFICATION */
++
++static void
++wnck_screen_construct (WnckScreen *screen,
++ int number)
++{
++ /* Create the initial state of the screen. */
++ screen->priv->xroot = RootWindow (gdk_display, number);
++ screen->priv->xscreen = ScreenOfDisplay (gdk_display, number);
++ screen->priv->number = number;
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ screen->priv->sn_display = sn_display_new (gdk_display,
++ sn_error_trap_push,
++ sn_error_trap_pop);
++#endif
++
++ screen->priv->bg_pixmap = None;
++
++ _wnck_select_input (screen->priv->xroot,
++ PropertyChangeMask);
++
++ screen->priv->need_update_workspace_list = TRUE;
++ screen->priv->need_update_stack_list = TRUE;
++ screen->priv->need_update_viewport_settings = TRUE;
++ screen->priv->need_update_active_workspace = TRUE;
++ screen->priv->need_update_active_window = TRUE;
++ screen->priv->need_update_workspace_layout = TRUE;
++ screen->priv->need_update_workspace_names = TRUE;
++ screen->priv->need_update_bg_pixmap = TRUE;
++ screen->priv->need_update_showing_desktop = TRUE;
++ screen->priv->need_update_wm = TRUE;
++
++ queue_update (screen);
++}
++
++/**
++ * wnck_screen_get:
++ * @index: screen number, starting from 0.
++ *
++ * Returns the #WnckScreen for a given screen on the default display.
++ *
++ * Return value: the #WnckScreen for screen @index, or %NULL if no such screen
++ * exists. The returned #WnckScreen is owned by libwnck and must not be
++ * referenced or unreferenced.
++ **/
++WnckScreen*
++wnck_screen_get (int index)
++{
++ g_return_val_if_fail (gdk_display != NULL, NULL);
++
++ if (index >= ScreenCount (gdk_display))
++ return NULL;
++
++ if (screens == NULL)
++ {
++ screens = g_new0 (WnckScreen*, ScreenCount (gdk_display));
++ _wnck_event_filter_init ();
++ }
++
++ if (screens[index] == NULL)
++ {
++ screens[index] = g_object_new (WNCK_TYPE_SCREEN, NULL);
++
++ wnck_screen_construct (screens[index], index);
++ }
++
++ return screens[index];
++}
++
++WnckScreen*
++_wnck_screen_get_existing (int number)
++{
++ g_return_val_if_fail (gdk_display != NULL, NULL);
++ g_return_val_if_fail (number < ScreenCount (gdk_display), NULL);
++
++ if (screens != NULL)
++ return screens[number];
++ else
++ return NULL;
++}
++
++/**
++ * wnck_screen_get_default:
++ *
++ * Returns the default #WnckScreen on the default display.
++ *
++ * Return value: the default #WnckScreen. The returned #WnckScreen is
++ * owned by libwnck and must not be referenced or unreferenced.
++ **/
++WnckScreen*
++wnck_screen_get_default (void)
++{
++ int default_screen;
++
++ default_screen = DefaultScreen (gdk_display);
++
++ return wnck_screen_get (default_screen);
++}
++
++/**
++ * wnck_screen_get_for_root:
++ * @root_window_id: an X window ID.
++ *
++ * Returns the #WnckScreen for the root window at @root_window_id, or
++ * %NULL if no #WnckScreen exists for this root window.
++ *
++ * This function does not work if wnck_screen_get() was not called for the
++ * sought #WnckScreen before, and returns %NULL.
++ *
++ * Return value: the #WnckScreen for the root window at @root_window_id, or
++ * %NULL. The returned #WnckScreen is owned by libwnck and must not be
++ * referenced or unreferenced.
++ **/
++WnckScreen*
++wnck_screen_get_for_root (gulong root_window_id)
++{
++ int i;
++
++ if (screens == NULL)
++ return NULL;
++
++ i = 0;
++ while (i < ScreenCount (gdk_display))
++ {
++ if (screens[i] != NULL && screens[i]->priv->xroot == root_window_id)
++ return screens[i];
++
++ ++i;
++ }
++
++ return NULL;
++}
++
++/**
++ * wnck_screen_get_number:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the index of @screen on the display to which it belongs. The first
++ * #WnckScreen has an index of 0.
++ *
++ * Return value: the index of @space on @screen, or -1 on errors.
++ *
++ * Since: 2.20
++ **/
++int
++wnck_screen_get_number (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), -1);
++
++ return screen->priv->number;
++}
++
++/**
++ * wnck_screen_get_workspaces:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the list of #WnckWorkspace on @screen. The list is ordered: the
++ * first element in the list is the first #WnckWorkspace, etc..
++ *
++ * Return value: the list of #WnckWorkspace on @screen. The list should not be
++ * modified nor freed, as it is owned by @screen.
++ *
++ * Since: 2.20
++ **/
++GList*
++wnck_screen_get_workspaces (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->workspaces;
++}
++
++/**
++ * wnck_screen_get_workspace:
++ * @screen: a #WnckScreen.
++ * @workspace: a workspace index, starting from 0.
++ *
++ * Returns the #WnckWorkspace numbered @workspace on @screen.
++ *
++ * Return value: the #WnckWorkspace numbered @workspace on @screen, or
++ * %NULL if no such workspace exists. The returned #WnckWorkspace is owned by
++ * libwnck and must not be referenced or unreferenced.
++ **/
++WnckWorkspace*
++wnck_screen_get_workspace (WnckScreen *screen,
++ int workspace)
++{
++ GList *list;
++
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ /* We trust this function with property-provided numbers, it
++ * must reliably return NULL on bad data
++ */
++ list = g_list_nth (screen->priv->workspaces, workspace);
++
++ if (list == NULL)
++ return NULL;
++
++ return WNCK_WORKSPACE (list->data);
++}
++
++/**
++ * wnck_screen_get_workspace_index:
++ * @screen: a #WnckScreen.
++ * @space: a #WnckWorkspace.
++ *
++ * Returns the index of @space on @screen. The first #WnckWorkspace has an
++ * index of 0. See also wnck_workspace_get_number().
++ *
++ * Return value: the index of @space on @screen, or -1 on errors.
++ *
++ * Since: 2.14
++ * Deprecated:2.20: Use wnck_workspace_get_number() instead.
++ **/
++int
++wnck_screen_get_workspace_index (WnckScreen *screen,
++ WnckWorkspace *space)
++{
++ GList *tmp;
++ int i;
++
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), -1);
++
++ i = 0;
++ tmp = screen->priv->workspaces;
++ while (tmp != NULL)
++ {
++ if (tmp->data == space)
++ return i;
++
++ ++i;
++
++ tmp = tmp->next;
++ }
++ return -1; /* compiler warnings */
++}
++
++/**
++ * wnck_screen_get_active_workspace:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the active #WnckWorkspace on @screen. May return %NULL sometimes,
++ * if libwnck is in a weird state due to the asynchronous nature of the
++ * interaction with the window manager.
++ *
++ * Return value: the active #WnckWorkspace on @screen, or %NULL. The returned
++ * #WnckWorkspace is owned by libwnck and must not be referenced or
++ * unreferenced.
++ **/
++WnckWorkspace*
++wnck_screen_get_active_workspace (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->active_workspace;
++}
++
++/**
++ * wnck_screen_get_workspace_neighbor:
++ * @screen: a #WnckScreen.
++ * @space: a #WnckWorkspace.
++ * @direction: direction in which to search the neighbor.
++ *
++ * Returns the neighbor #WnckWorkspace of @space in the @direction direction on
++ * @screen.
++ *
++ * Return value: the neighbor #WnckWorkspace of @space in the @direction
++ * direction on @screen, or %NULL if no such neighbor #WnckWorkspace exists.
++ * The returned #WnckWorkspace is owned by libwnck and must not be referenced
++ * or unreferenced.
++ *
++ * Since: 2.14
++ * Deprecated:2.20: Use wnck_workspace_get_neighbor() instead.
++ **/
++WnckWorkspace*
++wnck_screen_get_workspace_neighbor (WnckScreen *screen,
++ WnckWorkspace *space,
++ WnckMotionDirection direction)
++{
++ WnckWorkspaceLayout layout;
++ int i, space_index;
++
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ space_index = wnck_screen_get_workspace_index (screen, space);
++
++ wnck_screen_calc_workspace_layout (screen, -1,
++ space_index, &layout);
++
++ switch (direction)
++ {
++ case WNCK_MOTION_LEFT:
++ layout.current_col -= 1;
++ break;
++ case WNCK_MOTION_RIGHT:
++ layout.current_col += 1;
++ break;
++ case WNCK_MOTION_UP:
++ layout.current_row -= 1;
++ break;
++ case WNCK_MOTION_DOWN:
++ layout.current_row += 1;
++ break;
++ }
++
++ if (layout.current_col < 0)
++ layout.current_col = 0;
++ if (layout.current_col >= layout.cols)
++ layout.current_col = layout.cols - 1;
++ if (layout.current_row < 0)
++ layout.current_row = 0;
++ if (layout.current_row >= layout.rows)
++ layout.current_row = layout.rows - 1;
++
++ i = layout.grid[layout.current_row * layout.cols + layout.current_col];
++
++ if (i < 0)
++ i = space_index;
++
++ wnck_screen_free_workspace_layout (&layout);
++ return wnck_screen_get_workspace (screen, i);
++}
++
++/**
++ * wnck_screen_get_active_window:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the active #WnckWindow on @screen. May return %NULL sometimes, since
++ * not all window managers guarantee that a window is always active.
++ *
++ * Return value: the active #WnckWindow on @screen, or %NULL. The returned
++ * #WnckWindow is owned by libwnck and must not be referenced or unreferenced.
++ **/
++WnckWindow*
++wnck_screen_get_active_window (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->active_window;
++}
++
++/**
++ * wnck_screen_get_previously_active_window:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the previously active #WnckWindow on @screen. May return %NULL
++ * sometimes, since not all window managers guarantee that a window is always
++ * active.
++ *
++ * Return value: the previously active #WnckWindow on @screen, or %NULL. The
++ * returned #WnckWindow is owned by libwnck and must not be referenced or
++ * unreferenced.
++ *
++ * Since: 2.8
++ **/
++WnckWindow*
++wnck_screen_get_previously_active_window (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->previously_active_window;
++}
++
++/**
++ * wnck_screen_get_windows:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the list of #WnckWindow on @screen. The list is not in a defined
++ * order, but should be "stable" (windows should not be reordered in it).
++ * However, the stability of the list is established by the window manager, so
++ * don't blame libwnck if it breaks down.
++ *
++ * Return value: the list of #WnckWindow on @screen, or %NULL if there is no
++ * window on @screen. The list should not be modified nor freed, as it is owned
++ * by @screen.
++ **/
++GList*
++wnck_screen_get_windows (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->mapped_windows;
++}
++
++/**
++ * wnck_screen_get_windows_stacked:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the list of #WnckWindow on @screen in bottom-to-top order.
++ *
++ * Return value: the list of #WnckWindow in stacking order on @screen, or %NULL
++ * if there is no window on @screen. The list should not be modified nor freed,
++ * as it is owned by @screen.
++ **/
++GList*
++wnck_screen_get_windows_stacked (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->stacked_windows;
++}
++
++/**
++ * _wnck_screen_get_gdk_screen:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the <classname>GdkScreen</classname referring to the same screen as
++ * @screen.
++ *
++ * Return value: the <classname>GdkScreen</classname referring to the same
++ * screen as @screen.
++ **/
++GdkScreen *
++_wnck_screen_get_gdk_screen (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return gdk_display_get_screen (gdk_display_get_default (),
++ screen->priv->number);
++}
++
++/**
++ * wnck_screen_force_update:
++ * @screen: a #WnckScreen.
++ *
++ * Synchronously and immediately updates the list of #WnckWindow on @screen.
++ * This bypasses the standard update mechanism, where the list of #WnckWindow
++ * is updated in the idle loop.
++ *
++ * This is usually a bad idea for both performance and correctness reasons (to
++ * get things right, you need to write model-view code that tracks changes, not
++ * get a static list of open windows). However, this function can be useful for
++ * small applications that just do something and then exit.
++ **/
++void
++wnck_screen_force_update (WnckScreen *screen)
++{
++ g_return_if_fail (WNCK_IS_SCREEN (screen));
++
++ do_update_now (screen);
++}
++
++/**
++ * wnck_screen_get_workspace_count:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the number of #WnckWorkspace on @screen.
++ *
++ * Return value: the number of #WnckWorkspace on @screen.
++ **/
++int
++wnck_screen_get_workspace_count (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), 0);
++
++ return g_list_length (screen->priv->workspaces);
++}
++
++/**
++ * wnck_screen_change_workspace_count:
++ * @screen: a #WnckScreen.
++ * @count: the number of #WnckWorkspace to request.
++ *
++ * Asks the window manager to change the number of #WnckWorkspace on @screen.
++ *
++ * Since: 2.2
++ **/
++void
++wnck_screen_change_workspace_count (WnckScreen *screen,
++ int count)
++{
++ XEvent xev;
++
++ g_return_if_fail (WNCK_IS_SCREEN (screen));
++ g_return_if_fail (count >= 1);
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.window = screen->priv->xroot;
++ xev.xclient.send_event = True;
++ xev.xclient.display = DisplayOfScreen (screen->priv->xscreen);
++ xev.xclient.message_type = _wnck_atom_get ("_NET_NUMBER_OF_DESKTOPS");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = count;
++
++ _wnck_error_trap_push ();
++ XSendEvent (DisplayOfScreen (screen->priv->xscreen),
++ screen->priv->xroot,
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_screen_process_property_notify (WnckScreen *screen,
++ XEvent *xevent)
++{
++ /* most frequently-changed properties first */
++ if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_ACTIVE_WINDOW"))
++ {
++ screen->priv->need_update_active_window = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_CURRENT_DESKTOP"))
++ {
++ screen->priv->need_update_active_workspace = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_CLIENT_LIST_STACKING") ||
++ xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_CLIENT_LIST"))
++ {
++ screen->priv->need_update_stack_list = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_DESKTOP_VIEWPORT"))
++ {
++ screen->priv->need_update_viewport_settings = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_DESKTOP_GEOMETRY"))
++ {
++ screen->priv->need_update_viewport_settings = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_NUMBER_OF_DESKTOPS"))
++ {
++ screen->priv->need_update_workspace_list = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_DESKTOP_LAYOUT"))
++ {
++ screen->priv->need_update_workspace_layout = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_DESKTOP_NAMES"))
++ {
++ screen->priv->need_update_workspace_names = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_XROOTPMAP_ID"))
++ {
++ screen->priv->need_update_bg_pixmap = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_SHOWING_DESKTOP"))
++ {
++ screen->priv->need_update_showing_desktop = TRUE;
++ queue_update (screen);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_SUPPORTING_WM_CHECK"))
++ {
++ screen->priv->need_update_wm = TRUE;
++ queue_update (screen);
++ }
++}
++
++/**
++ * wnck_screen_calc_workspace_layout:
++ * @screen: a #WnckScreen.
++ * @num_workspaces: the number of #WnckWorkspace on @screen, or -1 to let
++ * wnck_screen_calc_workspace_layout() find this number.
++ * @space_index: the index of a #Workspace.
++ * @layout: return location for the layout of #WnckWorkspace with additional
++ * information.
++ *
++ * Calculates the layout of #WnckWorkspace, with additional information like
++ * the row and column of the #WnckWorkspace with index @space_index.
++ *
++ * Since: 2.12
++ * Deprecated:2.20:
++ */
++/* TODO: when we make this private, remove num_workspaces since we can get it
++ * from screen! */
++void
++wnck_screen_calc_workspace_layout (WnckScreen *screen,
++ int num_workspaces,
++ int space_index,
++ WnckWorkspaceLayout *layout)
++{
++ int rows, cols;
++ int grid_area;
++ int *grid;
++ int i, r, c;
++ int current_row, current_col;
++
++ g_return_if_fail (WNCK_IS_SCREEN (screen));
++ g_return_if_fail (layout != NULL);
++
++ if (num_workspaces < 0)
++ num_workspaces = wnck_screen_get_workspace_count (screen);
++
++ rows = screen->priv->rows_of_workspaces;
++ cols = screen->priv->columns_of_workspaces;
++
++ if (rows <= 0 && cols <= 0)
++ cols = num_workspaces;
++
++ if (rows <= 0)
++ rows = num_workspaces / cols + ((num_workspaces % cols) > 0 ? 1 : 0);
++ if (cols <= 0)
++ cols = num_workspaces / rows + ((num_workspaces % rows) > 0 ? 1 : 0);
++
++ /* paranoia */
++ if (rows < 1)
++ rows = 1;
++ if (cols < 1)
++ cols = 1;
++
++ g_assert (rows != 0 && cols != 0);
++
++ grid_area = rows * cols;
++
++ grid = g_new (int, grid_area);
++
++ current_row = -1;
++ current_col = -1;
++ i = 0;
++
++ switch (screen->priv->starting_corner)
++ {
++ case WNCK_LAYOUT_CORNER_TOPLEFT:
++ if (screen->priv->vertical_workspaces)
++ {
++ c = 0;
++ while (c < cols)
++ {
++ r = 0;
++ while (r < rows)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ ++r;
++ }
++ ++c;
++ }
++ }
++ else
++ {
++ r = 0;
++ while (r < rows)
++ {
++ c = 0;
++ while (c < cols)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ ++c;
++ }
++ ++r;
++ }
++ }
++ break;
++ case WNCK_LAYOUT_CORNER_TOPRIGHT:
++ if (screen->priv->vertical_workspaces)
++ {
++ c = cols - 1;
++ while (c >= 0)
++ {
++ r = 0;
++ while (r < rows)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ ++r;
++ }
++ --c;
++ }
++ }
++ else
++ {
++ r = 0;
++ while (r < rows)
++ {
++ c = cols - 1;
++ while (c >= 0)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ --c;
++ }
++ ++r;
++ }
++ }
++ break;
++ case WNCK_LAYOUT_CORNER_BOTTOMLEFT:
++ if (screen->priv->vertical_workspaces)
++ {
++ c = 0;
++ while (c < cols)
++ {
++ r = rows - 1;
++ while (r >= 0)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ --r;
++ }
++ ++c;
++ }
++ }
++ else
++ {
++ r = rows - 1;
++ while (r >= 0)
++ {
++ c = 0;
++ while (c < cols)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ ++c;
++ }
++ --r;
++ }
++ }
++ break;
++ case WNCK_LAYOUT_CORNER_BOTTOMRIGHT:
++ if (screen->priv->vertical_workspaces)
++ {
++ c = cols - 1;
++ while (c >= 0)
++ {
++ r = rows - 1;
++ while (r >= 0)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ --r;
++ }
++ --c;
++ }
++ }
++ else
++ {
++ r = rows - 1;
++ while (r >= 0)
++ {
++ c = cols - 1;
++ while (c >= 0)
++ {
++ grid[r*cols+c] = i;
++ ++i;
++ --c;
++ }
++ --r;
++ }
++ }
++ break;
++ }
++
++ current_row = 0;
++ current_col = 0;
++ r = 0;
++ while (r < rows)
++ {
++ c = 0;
++ while (c < cols)
++ {
++ if (grid[r*cols+c] == space_index)
++ {
++ current_row = r;
++ current_col = c;
++ }
++ else if (grid[r*cols+c] >= num_workspaces)
++ {
++ /* flag nonexistent spaces with -1 */
++ grid[r*cols+c] = -1;
++ }
++ ++c;
++ }
++ ++r;
++ }
++ layout->rows = rows;
++ layout->cols = cols;
++ layout->grid = grid;
++ layout->grid_area = grid_area;
++ layout->current_row = current_row;
++ layout->current_col = current_col;
++}
++
++/**
++ * wnck_screen_free_workspace_layout:
++ * @layout: a #WnckWorkspaceLayout.
++ *
++ * Frees the content of @layout. This does not free @layout itself, so you
++ * might want to free @layout yourself after calling this.
++ *
++ * Since: 2.12
++ * Deprecated:2.20:
++ */
++void
++wnck_screen_free_workspace_layout (WnckWorkspaceLayout *layout)
++{
++ g_return_if_fail (layout != NULL);
++
++ g_free (layout->grid);
++}
++
++static void
++set_active_window (WnckScreen *screen,
++ WnckWindow *window)
++{
++ gpointer weak_pointer;
++
++ weak_pointer = &screen->priv->active_window;
++
++ /* we need the weak pointer since the active window might be shared between
++ * two screens, and so the value for one screen might become invalid when
++ * the window is destroyed on another screen */
++ if (screen->priv->active_window != NULL)
++ g_object_remove_weak_pointer (G_OBJECT (screen->priv->active_window),
++ weak_pointer);
++
++ screen->priv->active_window = window;
++ if (screen->priv->active_window != NULL)
++ g_object_add_weak_pointer (G_OBJECT (screen->priv->active_window),
++ weak_pointer);
++}
++
++static void
++set_previously_active_window (WnckScreen *screen,
++ WnckWindow *window)
++{
++ gpointer weak_pointer;
++
++ weak_pointer = &screen->priv->previously_active_window;
++
++ /* we need the weak pointer since the active window might be shared between
++ * two screens, and so the value for one screen might become invalid when
++ * the window is destroyed on another screen */
++ if (screen->priv->previously_active_window != NULL)
++ g_object_remove_weak_pointer (G_OBJECT (screen->priv->previously_active_window),
++ weak_pointer);
++
++ screen->priv->previously_active_window = window;
++ if (screen->priv->previously_active_window != NULL)
++ g_object_add_weak_pointer (G_OBJECT (screen->priv->previously_active_window),
++ weak_pointer);
++}
++
++static gboolean
++lists_equal (GList *a,
++ GList *b)
++{
++ GList *a_iter;
++ GList *b_iter;
++
++ a_iter = a;
++ b_iter = b;
++
++ while (a_iter && b_iter)
++ {
++ if (a_iter->data != b_iter->data)
++ return FALSE;
++
++ a_iter = a_iter->next;
++ b_iter = b_iter->next;
++ }
++
++ if (a_iter || b_iter)
++ return FALSE;
++
++ return TRUE;
++}
++
++static int
++wincmp (const void *a,
++ const void *b)
++{
++ const Window *aw = a;
++ const Window *bw = b;
++
++ if (*aw < *bw)
++ return -1;
++ else if (*aw > *bw)
++ return 1;
++ else
++ return 0;
++}
++
++static gboolean
++arrays_contain_same_windows (Window *a,
++ int a_len,
++ Window *b,
++ int b_len)
++{
++ Window *a_tmp;
++ Window *b_tmp;
++ gboolean result;
++
++ if (a_len != b_len)
++ return FALSE;
++
++ if (a_len == 0 ||
++ b_len == 0)
++ return FALSE; /* one was nonzero */
++
++ a_tmp = g_new (Window, a_len);
++ b_tmp = g_new (Window, b_len);
++
++ memcpy (a_tmp, a, a_len * sizeof (Window));
++ memcpy (b_tmp, b, b_len * sizeof (Window));
++
++ qsort (a_tmp, a_len, sizeof (Window), wincmp);
++ qsort (b_tmp, b_len, sizeof (Window), wincmp);
++
++ result = memcmp (a_tmp, b_tmp, sizeof (Window) * a_len) == 0;
++
++ g_free (a_tmp);
++ g_free (b_tmp);
++
++ return result;
++}
++
++static void
++update_client_list (WnckScreen *screen)
++{
++ /* stacking order */
++ Window *stack;
++ int stack_length;
++ /* mapping order */
++ Window *mapping;
++ int mapping_length;
++ GList *new_stack_list;
++ GList *new_list;
++ GList *created;
++ GList *closed;
++ GList *created_apps, *closed_apps;
++ GList *created_class_groups, *closed_class_groups;
++ GList *tmp;
++ int i;
++ GHashTable *new_hash;
++ static int reentrancy_guard = 0;
++ gboolean active_changed;
++ gboolean stack_changed;
++ gboolean list_changed;
++
++ g_return_if_fail (reentrancy_guard == 0);
++
++ if (!screen->priv->need_update_stack_list)
++ return;
++
++ ++reentrancy_guard;
++
++ screen->priv->need_update_stack_list = FALSE;
++
++ stack = NULL;
++ stack_length = 0;
++ _wnck_get_window_list (screen->priv->xroot,
++ _wnck_atom_get ("_NET_CLIENT_LIST_STACKING"),
++ &stack,
++ &stack_length);
++
++ mapping = NULL;
++ mapping_length = 0;
++ _wnck_get_window_list (screen->priv->xroot,
++ _wnck_atom_get ("_NET_CLIENT_LIST"),
++ &mapping,
++ &mapping_length);
++
++ if (!arrays_contain_same_windows (stack, stack_length,
++ mapping, mapping_length))
++ {
++ /* Don't update until we're in a consistent state */
++ g_free (stack);
++ g_free (mapping);
++ --reentrancy_guard;
++ return;
++ }
++
++ created = NULL;
++ closed = NULL;
++ created_apps = NULL;
++ closed_apps = NULL;
++ created_class_groups = NULL;
++ closed_class_groups = NULL;
++
++ new_hash = g_hash_table_new (NULL, NULL);
++
++ new_list = NULL;
++ i = 0;
++ while (i < mapping_length)
++ {
++ WnckWindow *window;
++
++ window = wnck_window_get (mapping[i]);
++
++ if (window == NULL)
++ {
++ Window leader;
++ WnckApplication *app;
++ const char *res_class;
++ WnckClassGroup *class_group;
++
++ window = _wnck_window_create (mapping[i],
++ screen,
++ screen->priv->window_order++);
++
++ created = g_list_prepend (created, window);
++
++ /* Application */
++
++ leader = wnck_window_get_group_leader (window);
++
++ app = wnck_application_get (leader);
++ if (app == NULL)
++ {
++ app = _wnck_application_create (leader, screen);
++ created_apps = g_list_prepend (created_apps, app);
++ }
++
++ _wnck_application_add_window (app, window);
++
++ /* Class group */
++
++ res_class = _wnck_window_get_resource_class (window);
++
++ class_group = wnck_class_group_get (res_class);
++ if (class_group == NULL)
++ {
++ class_group = _wnck_class_group_create (res_class);
++ created_class_groups = g_list_prepend (created_class_groups, class_group);
++ }
++
++ _wnck_class_group_add_window (class_group, window);
++ }
++
++ new_list = g_list_prepend (new_list, window);
++
++ g_hash_table_insert (new_hash, window, window);
++
++ ++i;
++ }
++
++ /* put list back in order */
++ new_list = g_list_reverse (new_list);
++
++ /* Now we need to find windows in the old list that aren't
++ * in this new list
++ */
++ tmp = screen->priv->mapped_windows;
++ while (tmp != NULL)
++ {
++ WnckWindow *window = tmp->data;
++
++ if (g_hash_table_lookup (new_hash, window) == NULL)
++ {
++ WnckApplication *app;
++ WnckClassGroup *class_group;
++
++ closed = g_list_prepend (closed, window);
++
++ /* Remove from the app */
++
++ app = wnck_window_get_application (window);
++ _wnck_application_remove_window (app, window);
++
++ if (wnck_application_get_windows (app) == NULL)
++ closed_apps = g_list_prepend (closed_apps, app);
++
++ /* Remove from the class group */
++
++ class_group = wnck_window_get_class_group (window);
++ _wnck_class_group_remove_window (class_group, window);
++
++ if (wnck_class_group_get_windows (class_group) == NULL)
++ closed_class_groups = g_list_prepend (closed_class_groups, class_group);
++ }
++
++ tmp = tmp->next;
++ }
++
++ g_hash_table_destroy (new_hash);
++
++ /* Now get the mapping in list form */
++ new_stack_list = NULL;
++ i = 0;
++ while (i < stack_length)
++ {
++ WnckWindow *window;
++
++ window = wnck_window_get (stack[i]);
++
++ g_assert (window != NULL);
++
++ new_stack_list = g_list_prepend (new_stack_list, window);
++
++ ++i;
++ }
++
++ g_free (stack);
++ g_free (mapping);
++
++ /* put list back in order */
++ new_stack_list = g_list_reverse (new_stack_list);
++
++ /* Now new_stack_list becomes screen->priv->stack_windows, new_list
++ * becomes screen->priv->mapped_windows, and we emit the opened/closed
++ * signals as appropriate
++ */
++
++ stack_changed = !lists_equal (screen->priv->stacked_windows, new_stack_list);
++ list_changed = !lists_equal (screen->priv->mapped_windows, new_list);
++
++ if (!(stack_changed || list_changed))
++ {
++ g_assert (created == NULL);
++ g_assert (closed == NULL);
++ g_assert (created_apps == NULL);
++ g_assert (closed_apps == NULL);
++ g_assert (created_class_groups == NULL);
++ g_assert (closed_class_groups == NULL);
++ g_list_free (new_stack_list);
++ g_list_free (new_list);
++ --reentrancy_guard;
++ return;
++ }
++
++ g_list_free (screen->priv->mapped_windows);
++ g_list_free (screen->priv->stacked_windows);
++ screen->priv->mapped_windows = new_list;
++ screen->priv->stacked_windows = new_stack_list;
++
++ /* Here we could get reentrancy if someone ran the main loop in
++ * signal callbacks; though that would be a bit pathological, so we
++ * don't handle it, but we do warn about it using reentrancy_guard
++ */
++
++ /* Sequence is: class_group_opened, application_opened, window_opened,
++ * window_closed, application_closed, class_group_closed. We have to do all
++ * window list changes BEFORE doing any other signals, so that any observers
++ * have valid state for the window structure before they take further action
++ */
++ for (tmp = created_class_groups; tmp; tmp = tmp->next)
++ emit_class_group_opened (screen, WNCK_CLASS_GROUP (tmp->data));
++
++ for (tmp = created_apps; tmp; tmp = tmp->next)
++ emit_application_opened (screen, WNCK_APPLICATION (tmp->data));
++
++ for (tmp = created; tmp; tmp = tmp->next)
++ emit_window_opened (screen, WNCK_WINDOW (tmp->data));
++
++ active_changed = FALSE;
++ for (tmp = closed; tmp; tmp = tmp->next)
++ {
++ WnckWindow *window;
++
++ window = WNCK_WINDOW (tmp->data);
++
++ if (window == screen->priv->previously_active_window)
++ {
++ set_previously_active_window (screen, NULL);
++ }
++
++ if (window == screen->priv->active_window)
++ {
++ set_previously_active_window (screen, screen->priv->active_window);
++ set_active_window (screen, NULL);
++ active_changed = TRUE;
++ }
++
++ emit_window_closed (screen, window);
++ }
++
++ for (tmp = closed_apps; tmp; tmp = tmp->next)
++ emit_application_closed (screen, WNCK_APPLICATION (tmp->data));
++
++ for (tmp = closed_class_groups; tmp; tmp = tmp->next)
++ emit_class_group_closed (screen, WNCK_CLASS_GROUP (tmp->data));
++
++ if (stack_changed)
++ emit_window_stacking_changed (screen);
++
++ if (active_changed)
++ emit_active_window_changed (screen);
++
++ /* Now free the closed windows */
++ for (tmp = closed; tmp; tmp = tmp->next)
++ _wnck_window_destroy (WNCK_WINDOW (tmp->data));
++
++ /* Free the closed apps */
++ for (tmp = closed_apps; tmp; tmp = tmp->next)
++ _wnck_application_destroy (WNCK_APPLICATION (tmp->data));
++
++ /* Free the closed class groups */
++ for (tmp = closed_class_groups; tmp; tmp = tmp->next)
++ _wnck_class_group_destroy (WNCK_CLASS_GROUP (tmp->data));
++
++ g_list_free (closed);
++ g_list_free (created);
++ g_list_free (closed_apps);
++ g_list_free (created_apps);
++ g_list_free (closed_class_groups);
++ g_list_free (created_class_groups);
++
++ --reentrancy_guard;
++
++ /* Maybe the active window is now valid if it wasn't */
++ if (screen->priv->active_window == NULL)
++ {
++ screen->priv->need_update_active_window = TRUE;
++ queue_update (screen);
++ }
++}
++
++static void
++update_workspace_list (WnckScreen *screen)
++{
++ int n_spaces;
++ int old_n_spaces;
++ GList *tmp;
++ GList *deleted;
++ GList *created;
++ static int reentrancy_guard = 0;
++
++ g_return_if_fail (reentrancy_guard == 0);
++
++ if (!screen->priv->need_update_workspace_list)
++ return;
++
++ screen->priv->need_update_workspace_list = FALSE;
++
++ ++reentrancy_guard;
++
++ n_spaces = 0;
++ if (!_wnck_get_cardinal (screen->priv->xroot,
++ _wnck_atom_get ("_NET_NUMBER_OF_DESKTOPS"),
++ &n_spaces))
++ n_spaces = 1;
++
++ if (n_spaces <= 0)
++ {
++ g_warning ("Someone set a weird number of desktops in _NET_NUMBER_OF_DESKTOPS, assuming the value is 1\n");
++ n_spaces = 1;
++ }
++
++ old_n_spaces = g_list_length (screen->priv->workspaces);
++
++ deleted = NULL;
++ created = NULL;
++
++ if (old_n_spaces == n_spaces)
++ {
++ --reentrancy_guard;
++ return; /* nothing changed */
++ }
++ else if (old_n_spaces > n_spaces)
++ {
++ /* Need to delete some workspaces */
++ deleted = g_list_nth (screen->priv->workspaces, n_spaces);
++ if (deleted->prev)
++ deleted->prev->next = NULL;
++ deleted->prev = NULL;
++
++ if (deleted == screen->priv->workspaces)
++ screen->priv->workspaces = NULL;
++ }
++ else
++ {
++ int i;
++
++ g_assert (old_n_spaces < n_spaces);
++
++ /* Need to create some workspaces */
++ i = 0;
++ while (i < (n_spaces - old_n_spaces))
++ {
++ WnckWorkspace *space;
++
++ space = _wnck_workspace_create (old_n_spaces + i, screen);
++
++ screen->priv->workspaces = g_list_append (screen->priv->workspaces,
++ space);
++
++ created = g_list_prepend (created, space);
++
++ ++i;
++ }
++
++ created = g_list_reverse (created);
++ }
++
++ /* Here we allow reentrancy, going into the main
++ * loop could confuse us
++ */
++ tmp = deleted;
++ while (tmp != NULL)
++ {
++ WnckWorkspace *space = WNCK_WORKSPACE (tmp->data);
++
++ if (space == screen->priv->active_workspace)
++ {
++ screen->priv->active_workspace = NULL;
++ emit_active_workspace_changed (screen, space);
++ }
++
++ emit_workspace_destroyed (screen, space);
++
++ tmp = tmp->next;
++ }
++
++ tmp = created;
++ while (tmp != NULL)
++ {
++ emit_workspace_created (screen, WNCK_WORKSPACE (tmp->data));
++
++ tmp = tmp->next;
++ }
++ g_list_free (created);
++
++ tmp = deleted;
++ while (tmp != NULL)
++ {
++ g_object_unref (tmp->data);
++
++ tmp = tmp->next;
++ }
++ g_list_free (deleted);
++
++ /* Active workspace property may now be interpretable,
++ * if it was a number larger than the active count previously
++ */
++ if (screen->priv->active_workspace == NULL)
++ {
++ screen->priv->need_update_active_workspace = TRUE;
++ queue_update (screen);
++ }
++
++ --reentrancy_guard;
++}
++
++static void
++update_viewport_settings (WnckScreen *screen)
++{
++ int i, n_spaces;
++ WnckWorkspace *space;
++ gulong *p_coord;
++ int n_coord;
++ gboolean do_update;
++ int space_width, space_height;
++ gboolean got_viewport_prop;
++
++ if (!screen->priv->need_update_viewport_settings)
++ return;
++
++ screen->priv->need_update_viewport_settings = FALSE;
++
++ do_update = FALSE;
++
++ n_spaces = wnck_screen_get_workspace_count (screen);
++
++ /* If no property, use the screen's size */
++ space_width = wnck_screen_get_width (screen);
++ space_height = wnck_screen_get_height (screen);
++
++ p_coord = NULL;
++ n_coord = 0;
++ if (_wnck_get_cardinal_list (screen->priv->xroot,
++ _wnck_atom_get ("_NET_DESKTOP_GEOMETRY"),
++ &p_coord, &n_coord) &&
++ p_coord != NULL)
++ {
++ if (n_coord == 2)
++ {
++ space_width = p_coord[0];
++ space_height = p_coord[1];
++
++ if (space_width < wnck_screen_get_width (screen))
++ space_width = wnck_screen_get_width (screen);
++
++ if (space_height < wnck_screen_get_height (screen))
++ space_height = wnck_screen_get_height (screen);
++ }
++
++ g_free (p_coord);
++ }
++
++ for (i = 0; i < n_spaces; i++)
++ {
++ space = wnck_screen_get_workspace (screen, i);
++ g_assert (space != NULL);
++
++ if (_wnck_workspace_set_geometry (space, space_width, space_height))
++ do_update = TRUE;
++ }
++
++ got_viewport_prop = FALSE;
++
++ p_coord = NULL;
++ n_coord = 0;
++ if (_wnck_get_cardinal_list (screen->priv->xroot,
++ _wnck_atom_get ("_NET_DESKTOP_VIEWPORT"),
++ &p_coord, &n_coord) &&
++ p_coord != NULL)
++ {
++ if (n_coord == 2 * n_spaces)
++ {
++ int screen_width, screen_height;
++
++ got_viewport_prop = TRUE;
++
++ screen_width = wnck_screen_get_width (screen);
++ screen_height = wnck_screen_get_height (screen);
++
++ for (i = 0; i < n_spaces; i++)
++ {
++ int x = 2 * i;
++ int y = 2 * i + 1;
++
++ space = wnck_screen_get_workspace (screen, i);
++ g_assert (space != NULL);
++
++ if (p_coord[x] < 0)
++ p_coord[x] = 0;
++ else if (p_coord[x] > space_width - screen_width)
++ p_coord[x] = space_width - screen_width;
++
++ if (p_coord[y] < 0)
++ p_coord[y] = 0;
++ else if (p_coord[y] > space_height - screen_height)
++ p_coord[y] = space_height - screen_height;
++
++ if (_wnck_workspace_set_viewport (space,
++ p_coord[x], p_coord[y]))
++ do_update = TRUE;
++ }
++ }
++
++ g_free (p_coord);
++ }
++
++ if (!got_viewport_prop)
++ {
++ for (i = 0; i < n_spaces; i++)
++ {
++ space = wnck_screen_get_workspace (screen, i);
++ g_assert (space != NULL);
++
++ if (_wnck_workspace_set_viewport (space, 0, 0))
++ do_update = TRUE;
++ }
++ }
++
++ if (do_update)
++ emit_viewports_changed (screen);
++}
++
++static void
++update_active_workspace (WnckScreen *screen)
++{
++ int number;
++ WnckWorkspace *previous_space;
++ WnckWorkspace *space;
++
++ if (!screen->priv->need_update_active_workspace)
++ return;
++
++ screen->priv->need_update_active_workspace = FALSE;
++
++ number = 0;
++ if (!_wnck_get_cardinal (screen->priv->xroot,
++ _wnck_atom_get ("_NET_CURRENT_DESKTOP"),
++ &number))
++ number = -1;
++
++ space = wnck_screen_get_workspace (screen, number);
++
++ if (space == screen->priv->active_workspace)
++ return;
++
++ previous_space = screen->priv->active_workspace;
++ screen->priv->active_workspace = space;
++
++ emit_active_workspace_changed (screen, previous_space);
++}
++
++static void
++update_active_window (WnckScreen *screen)
++{
++ WnckWindow *window;
++ Window xwindow;
++
++ if (!screen->priv->need_update_active_window)
++ return;
++
++ screen->priv->need_update_active_window = FALSE;
++
++ xwindow = None;
++ _wnck_get_window (screen->priv->xroot,
++ _wnck_atom_get ("_NET_ACTIVE_WINDOW"),
++ &xwindow);
++
++ window = wnck_window_get (xwindow);
++
++ if (window == screen->priv->active_window)
++ return;
++
++ set_previously_active_window (screen, screen->priv->active_window);
++ set_active_window (screen, window);
++
++ emit_active_window_changed (screen);
++}
++
++static void
++update_workspace_layout (WnckScreen *screen)
++{
++ gulong *list;
++ int n_items;
++
++ if (!screen->priv->need_update_workspace_layout)
++ return;
++
++ screen->priv->need_update_workspace_layout = FALSE;
++
++ list = NULL;
++ n_items = 0;
++ if (_wnck_get_cardinal_list (screen->priv->xroot,
++ _wnck_atom_get ("_NET_DESKTOP_LAYOUT"),
++ &list,
++ &n_items))
++ {
++ if (n_items == 3 || n_items == 4)
++ {
++ int cols, rows;
++
++ switch (list[0])
++ {
++ case _NET_WM_ORIENTATION_HORZ:
++ screen->priv->vertical_workspaces = FALSE;
++ break;
++ case _NET_WM_ORIENTATION_VERT:
++ screen->priv->vertical_workspaces = TRUE;
++ break;
++ default:
++ g_warning ("Someone set a weird orientation in _NET_DESKTOP_LAYOUT\n");
++ break;
++ }
++
++ cols = list[1];
++ rows = list[2];
++
++ if (rows <= 0 && cols <= 0)
++ {
++ g_warning ("Columns = %d rows = %d in _NET_DESKTOP_LAYOUT makes no sense\n", rows, cols);
++ }
++ else
++ {
++ int num_workspaces;
++
++ num_workspaces = wnck_screen_get_workspace_count (screen);
++
++ if (rows > 0)
++ screen->priv->rows_of_workspaces = rows;
++ else
++ screen->priv->rows_of_workspaces =
++ num_workspaces / cols
++ + ((num_workspaces % cols) > 0 ? 1 : 0);
++
++ if (cols > 0)
++ screen->priv->columns_of_workspaces = cols;
++ else
++ screen->priv->columns_of_workspaces =
++ num_workspaces / rows
++ + ((num_workspaces % rows) > 0 ? 1 : 0);
++ }
++ if (n_items == 4)
++ {
++ switch (list[3])
++ {
++ case _NET_WM_TOPLEFT:
++ screen->priv->starting_corner = WNCK_LAYOUT_CORNER_TOPLEFT;
++ break;
++ case _NET_WM_TOPRIGHT:
++ screen->priv->starting_corner = WNCK_LAYOUT_CORNER_TOPRIGHT;
++ break;
++ case _NET_WM_BOTTOMRIGHT:
++ screen->priv->starting_corner = WNCK_LAYOUT_CORNER_BOTTOMRIGHT;
++ break;
++ case _NET_WM_BOTTOMLEFT:
++ screen->priv->starting_corner = WNCK_LAYOUT_CORNER_BOTTOMLEFT;
++ break;
++ default:
++ g_warning ("Someone set a weird starting corner in _NET_DESKTOP_LAYOUT\n");
++ break;
++ }
++ }
++ else
++ screen->priv->starting_corner = WNCK_LAYOUT_CORNER_TOPLEFT;
++ }
++ else
++ {
++ g_warning ("Someone set _NET_DESKTOP_LAYOUT to %d integers instead of 4 (3 is accepted for backwards compat)\n", n_items);
++ }
++ g_free (list);
++ }
++}
++
++static void
++update_workspace_names (WnckScreen *screen)
++{
++ char **names;
++ int i;
++ GList *tmp;
++ GList *copy;
++
++ if (!screen->priv->need_update_workspace_names)
++ return;
++
++ screen->priv->need_update_workspace_names = FALSE;
++
++ names = _wnck_get_utf8_list (screen->priv->xroot,
++ _wnck_atom_get ("_NET_DESKTOP_NAMES"));
++
++ copy = g_list_copy (screen->priv->workspaces);
++
++ i = 0;
++ tmp = copy;
++ while (tmp != NULL)
++ {
++ if (names && names[i])
++ {
++ _wnck_workspace_update_name (tmp->data, names[i]);
++ ++i;
++ }
++ else
++ _wnck_workspace_update_name (tmp->data, NULL);
++
++ tmp = tmp->next;
++ }
++
++ g_strfreev (names);
++
++ g_list_free (copy);
++}
++
++static void
++update_bg_pixmap (WnckScreen *screen)
++{
++ Pixmap p;
++
++ if (!screen->priv->need_update_bg_pixmap)
++ return;
++
++ screen->priv->need_update_bg_pixmap = FALSE;
++
++ p = None;
++ _wnck_get_pixmap (screen->priv->xroot,
++ _wnck_atom_get ("_XROOTPMAP_ID"),
++ &p);
++ /* may have failed, so p may still be None */
++
++ screen->priv->bg_pixmap = p;
++
++ emit_background_changed (screen);
++}
++
++static void
++update_showing_desktop (WnckScreen *screen)
++{
++ int showing_desktop;
++
++ if (!screen->priv->need_update_showing_desktop)
++ return;
++
++ screen->priv->need_update_showing_desktop = FALSE;
++
++ showing_desktop = FALSE;
++ _wnck_get_cardinal (screen->priv->xroot,
++ _wnck_atom_get ("_NET_SHOWING_DESKTOP"),
++ &showing_desktop);
++
++ screen->priv->showing_desktop = showing_desktop != 0;
++
++ emit_showing_desktop_changed (screen);
++}
++
++static void
++update_wm (WnckScreen *screen)
++{
++ Window wm_window;
++
++ if (!screen->priv->need_update_wm)
++ return;
++
++ screen->priv->need_update_wm = FALSE;
++
++ wm_window = None;
++ _wnck_get_window (screen->priv->xroot,
++ _wnck_atom_get ("_NET_SUPPORTING_WM_CHECK"),
++ &wm_window);
++
++ g_free (screen->priv->wm_name);
++
++ if (wm_window != None)
++ screen->priv->wm_name = _wnck_get_utf8_property (wm_window,
++ _wnck_atom_get ("_NET_WM_NAME"));
++ else
++ screen->priv->wm_name = NULL;
++
++ emit_wm_changed (screen);
++}
++
++static void
++do_update_now (WnckScreen *screen)
++{
++ if (screen->priv->update_handler)
++ {
++ g_source_remove (screen->priv->update_handler);
++ screen->priv->update_handler = 0;
++ }
++
++ /* if number of workspaces changes, we have to
++ * update the per-workspace information as well
++ * in case the WM changed the per-workspace info
++ * first and number of spaces second.
++ */
++ if (screen->priv->need_update_workspace_list)
++ {
++ screen->priv->need_update_viewport_settings = TRUE;
++ screen->priv->need_update_workspace_names = TRUE;
++ }
++
++ /* First get our big-picture state in order */
++ update_workspace_list (screen);
++ update_client_list (screen);
++
++ /* Then note any smaller-scale changes */
++ update_active_workspace (screen);
++ update_viewport_settings (screen);
++ update_active_window (screen);
++ update_workspace_layout (screen);
++ update_workspace_names (screen);
++ update_showing_desktop (screen);
++ update_wm (screen);
++
++ update_bg_pixmap (screen);
++}
++
++static gboolean
++update_idle (gpointer data)
++{
++ WnckScreen *screen;
++
++ screen = data;
++
++ screen->priv->update_handler = 0;
++
++ do_update_now (screen);
++
++ return FALSE;
++}
++
++static void
++queue_update (WnckScreen *screen)
++{
++ if (screen->priv->update_handler != 0)
++ return;
++
++ screen->priv->update_handler = g_idle_add (update_idle, screen);
++}
++
++static void
++unqueue_update (WnckScreen *screen)
++{
++ if (screen->priv->update_handler != 0)
++ {
++ g_source_remove (screen->priv->update_handler);
++ screen->priv->update_handler = 0;
++ }
++}
++
++static void
++emit_active_window_changed (WnckScreen *screen)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[ACTIVE_WINDOW_CHANGED],
++ 0, screen->priv->previously_active_window);
++}
++
++static void
++emit_active_workspace_changed (WnckScreen *screen,
++ WnckWorkspace *previous_space)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[ACTIVE_WORKSPACE_CHANGED],
++ 0, previous_space);
++}
++
++static void
++emit_window_stacking_changed (WnckScreen *screen)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[WINDOW_STACKING_CHANGED],
++ 0);
++}
++
++static void
++emit_window_opened (WnckScreen *screen,
++ WnckWindow *window)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[WINDOW_OPENED],
++ 0, window);
++}
++
++static void
++emit_window_closed (WnckScreen *screen,
++ WnckWindow *window)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[WINDOW_CLOSED],
++ 0, window);
++}
++
++static void
++emit_workspace_created (WnckScreen *screen,
++ WnckWorkspace *space)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[WORKSPACE_CREATED],
++ 0, space);
++}
++
++static void
++emit_workspace_destroyed (WnckScreen *screen,
++ WnckWorkspace *space)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[WORKSPACE_DESTROYED],
++ 0, space);
++}
++
++static void
++emit_application_opened (WnckScreen *screen,
++ WnckApplication *app)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[APPLICATION_OPENED],
++ 0, app);
++}
++
++static void
++emit_application_closed (WnckScreen *screen,
++ WnckApplication *app)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[APPLICATION_CLOSED],
++ 0, app);
++}
++
++static void
++emit_class_group_opened (WnckScreen *screen,
++ WnckClassGroup *class_group)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[CLASS_GROUP_OPENED],
++ 0, class_group);
++}
++
++static void
++emit_class_group_closed (WnckScreen *screen,
++ WnckClassGroup *class_group)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[CLASS_GROUP_CLOSED],
++ 0, class_group);
++}
++
++static void
++emit_background_changed (WnckScreen *screen)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[BACKGROUND_CHANGED],
++ 0);
++}
++
++static void
++emit_showing_desktop_changed (WnckScreen *screen)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[SHOWING_DESKTOP_CHANGED],
++ 0);
++}
++
++static void
++emit_viewports_changed (WnckScreen *screen)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[VIEWPORTS_CHANGED],
++ 0);
++}
++
++static void
++emit_wm_changed (WnckScreen *screen)
++{
++ g_signal_emit (G_OBJECT (screen),
++ signals[WM_CHANGED],
++ 0);
++}
++
++/**
++ * wnck_screen_get_window_manager_name:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the name of the window manager.
++ *
++ * Return value: the name of the window manager, or %NULL if the window manager
++ * does not comply with the <ulink
++ * url="http://standards.freedesktop.org/wm-spec/wm-spec-latest.html">EWMH</ulink>
++ * specification.
++ *
++ * Since: 2.20
++ */
++const char *
++wnck_screen_get_window_manager_name (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->wm_name;
++}
++
++/**
++ * wnck_screen_net_wm_supports:
++ * @screen: a #WnckScreen.
++ * @atom: a property atom.
++ *
++ * Returns whether the window manager for @screen supports a certain hint from
++ * the <ulink
++ * url="http://standards.freedesktop.org/wm-spec/wm-spec-latest.html">Extended
++ * Window Manager Hints specification</ulink> (EWMH).
++ *
++ * When using this function, keep in mind that the window manager can change
++ * over time; so you should not use this function in a way that impacts
++ * persistent application state. A common bug is that your application can
++ * start up before the window manager does when the user logs in, and before
++ * the window manager starts wnck_screen_net_wm_supports() will return %FALSE
++ * for every property.
++ *
++ * See also gdk_x11_screen_supports_net_wm_hint() in GDK.
++ *
++ * Return value: %TRUE if the window manager for @screen supports the @atom
++ * hint, %FALSE otherwise.
++ */
++gboolean
++wnck_screen_net_wm_supports (WnckScreen *screen,
++ const char *atom)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), FALSE);
++
++ return gdk_x11_screen_supports_net_wm_hint (_wnck_screen_get_gdk_screen (screen),
++ gdk_atom_intern (atom, FALSE));
++}
++
++/**
++ * wnck_screen_get_background_pixmap:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the X window ID of the background pixmap of @screen.
++ *
++ * Returns: the X window ID of the background pixmap of @screen.
++ */
++gulong
++wnck_screen_get_background_pixmap (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), None);
++
++ return screen->priv->bg_pixmap;
++}
++
++/**
++ * wnck_screen_get_width:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the width of @screen.
++ *
++ * Returns: the width of @screen.
++ */
++int
++wnck_screen_get_width (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), 0);
++
++ return WidthOfScreen (screen->priv->xscreen);
++}
++
++/**
++ * wnck_screen_get_height:
++ * @screen: a #WnckScreen.
++ *
++ * Returns the height of @screen.
++ *
++ * Returns: the height of @screen.
++ */
++int
++wnck_screen_get_height (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), 0);
++
++ return HeightOfScreen (screen->priv->xscreen);
++}
++
++Screen *
++_wnck_screen_get_xscreen (WnckScreen *screen)
++{
++ return screen->priv->xscreen;
++}
++
++/**
++ * wnck_screen_get_workspace_layout:
++ * @screen: a #WnckScreen.
++ * @orientation: return location for the orientation used in the #WnckWorkspace
++ * layout. See wnck_pager_set_orientation() for more information.
++ * @rows: return location for the number of rows in the #WnckWorkspace layout.
++ * @columns: return location for the number of columns in the #WnckWorkspace
++ * layout.
++ * @starting_corner: return location for the starting corner in the
++ * #WnckWorkspace layout (i.e. the corner containing the first #WnckWorkspace).
++ *
++ * Returns the layout of #WnckWorkspace on @screen.
++ */
++/* TODO: when we are sure about this API, add this function,
++ * WnckLayoutOrientation, WnckLayoutCorner and a "layout-changed" signal. But
++ * to make it really better, use a WnckScreenLayout struct. We might also want
++ * to wait for deprecation of WnckWorkspaceLayout. */
++void
++_wnck_screen_get_workspace_layout (WnckScreen *screen,
++ _WnckLayoutOrientation *orientation,
++ int *rows,
++ int *columns,
++ _WnckLayoutCorner *starting_corner)
++{
++ g_return_if_fail (WNCK_IS_SCREEN (screen));
++
++ if (orientation)
++ *orientation = screen->priv->vertical_workspaces ?
++ WNCK_LAYOUT_ORIENTATION_VERTICAL :
++ WNCK_LAYOUT_ORIENTATION_HORIZONTAL;
++
++ if (rows)
++ *rows = screen->priv->rows_of_workspaces;
++
++ if (columns)
++ *columns = screen->priv->columns_of_workspaces;
++
++ if (starting_corner)
++ *starting_corner = screen->priv->starting_corner;
++}
++
++/**
++ * wnck_screen_try_set_workspace_layout:
++ * @screen: a #WnckScreen.
++ * @current_token: a token. Use 0 if you do not called
++ * wnck_screen_try_set_workspace_layout() before, or if you did not keep the
++ * old token.
++ * @rows: the number of rows to use for the #WnckWorkspace layout.
++ * @columns: the number of columns to use for the #WnckWorkspace layout.
++ *
++ * Tries to modify the layout of #WnckWorkspace on @screen. To do this, tries
++ * to acquire ownership of the layout. If the current process is the owner of
++ * the layout, @current_token is used to determine if the caller is the owner
++ * (there might be more than one part of the same process trying to set the
++ * layout). Since no more than one application should set this property of
++ * @screen at a time, setting the layout is not guaranteed to work.
++ *
++ * If @rows is 0, the actual number of rows will be determined based on
++ * @columns and the number of #WnckWorkspace. If @columns is 0, the actual
++ * number of columns will be determined based on @rows and the number of
++ * #WnckWorkspace. @rows and @columns must not be 0 at the same time.
++ *
++ * You have to release the ownership of the layout with
++ * wnck_screen_release_workspace_layout() when you do not need it anymore.
++ *
++ * Return value: a token to use for future calls to
++ * wnck_screen_try_set_workspace_layout() and to
++ * wnck_screen_release_workspace_layout(), or 0 if the layout could not be set.
++ */
++int
++wnck_screen_try_set_workspace_layout (WnckScreen *screen,
++ int current_token,
++ int rows,
++ int columns)
++{
++ int retval;
++
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen),
++ WNCK_NO_MANAGER_TOKEN);
++ g_return_val_if_fail (rows != 0 || columns != 0,
++ WNCK_NO_MANAGER_TOKEN);
++
++ retval = _wnck_try_desktop_layout_manager (screen->priv->xscreen, current_token);
++
++ if (retval != WNCK_NO_MANAGER_TOKEN)
++ {
++ _wnck_set_desktop_layout (screen->priv->xscreen, rows, columns);
++ }
++
++ return retval;
++}
++
++/**
++ * wnck_screen_release_workspace_layout:
++ * @screen: a #WnckScreen.
++ * @current_token: the token obtained through
++ * wnck_screen_try_set_workspace_layout().
++ *
++ * Releases the ownership of the layout of #WnckWorkspace on @screen.
++ * @current_token is used to verify that the caller is the owner of the layout.
++ * If the verification fails, nothing happens.
++ */
++void
++wnck_screen_release_workspace_layout (WnckScreen *screen,
++ int current_token)
++{
++ g_return_if_fail (WNCK_IS_SCREEN (screen));
++
++ _wnck_release_desktop_layout_manager (screen->priv->xscreen,
++ current_token);
++
++}
++
++/**
++ * wnck_screen_get_showing_desktop:
++ * @screen: a #WnckScreen.
++ *
++ * Returns whether @screen is in the "showing the desktop" mode. This mode is
++ * changed when a #WnckScreen::showing-desktop-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is fullscreen, %FALSE otherwise.
++ *
++ * Since: 2.2
++ **/
++gboolean
++wnck_screen_get_showing_desktop (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), FALSE);
++
++ return screen->priv->showing_desktop;
++}
++
++/**
++ * wnck_screen_toggle_showing_desktop:
++ * @screen: a #WnckScreen.
++ * @show: whether to activate the "showing the desktop" mode on @screen.
++ *
++ * Asks the window manager to set the "showing the desktop" mode on @screen
++ * according to @show.
++ *
++ * Since: 2.2
++ **/
++void
++wnck_screen_toggle_showing_desktop (WnckScreen *screen,
++ gboolean show)
++{
++ g_return_if_fail (WNCK_IS_SCREEN (screen));
++
++ _wnck_toggle_showing_desktop (screen->priv->xscreen,
++ show);
++}
++
++
++/**
++ * wnck_screen_move_viewport:
++ * @screen: a #WnckScreen.
++ * @x: X offset in pixels of viewport.
++ * @y: Y offset in pixels of viewport.
++ *
++ * Asks the window manager to move the viewport of the current #WnckWorkspace
++ * on @screen.
++ *
++ * Since: 2.4
++ */
++void
++wnck_screen_move_viewport (WnckScreen *screen,
++ int x,
++ int y)
++{
++ g_return_if_fail (WNCK_IS_SCREEN (screen));
++ g_return_if_fail (x >= 0);
++ g_return_if_fail (y >= 0);
++
++ _wnck_change_viewport (WNCK_SCREEN_XSCREEN (screen), x, y);
++}
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++SnDisplay*
++_wnck_screen_get_sn_display (WnckScreen *screen)
++{
++ g_return_val_if_fail (WNCK_IS_SCREEN (screen), NULL);
++
++ return screen->priv->sn_display;
++}
++#endif /* HAVE_STARTUP_NOTIFICATION */
++
++void
++_wnck_screen_change_workspace_name (WnckScreen *screen,
++ int number,
++ const char *name)
++{
++ int n_spaces;
++ char **names;
++ int i;
++
++ n_spaces = wnck_screen_get_workspace_count (screen);
++
++ names = g_new0 (char*, n_spaces + 1);
++
++ i = 0;
++ while (i < n_spaces)
++ {
++ if (i == number)
++ names[i] = (char*) name;
++ else
++ {
++ WnckWorkspace *workspace;
++ workspace = wnck_screen_get_workspace (screen, i);
++ if (workspace)
++ names[i] = (char*) wnck_workspace_get_name (workspace);
++ else
++ names[i] = (char*) ""; /* maybe this should be a g_warning() */
++ }
++
++ ++i;
++ }
++
++ _wnck_set_utf8_list (screen->priv->xroot,
++ _wnck_atom_get ("_NET_DESKTOP_NAMES"),
++ names);
++
++ g_free (names);
++}
+diff -urN libwnck.orig/libwnck/tasklist.c libwnck.new/libwnck/tasklist.c
+--- libwnck.orig/libwnck/tasklist.c 2007-11-30 14:02:04.930253000 +0000
++++ libwnck.new/libwnck/tasklist.c 2007-11-30 14:02:34.606774000 +0000
+@@ -36,6 +36,10 @@
#include "workspace.h"
#include "xutils.h"
#include "private.h"
@@ -526,7 +6126,7 @@
/**
* SECTION:tasklist
-@@ -199,6 +203,10 @@ struct _WnckTasklistPrivate
+@@ -200,6 +204,10 @@
GHashTable *class_group_hash;
GHashTable *win_hash;
@@ -537,7 +6137,7 @@
gint max_button_width;
gint max_button_height;
-@@ -1921,6 +2106,9 @@ wnck_tasklist_new (WnckScreen *screen)
+@@ -2116,6 +2124,9 @@
WnckTasklist *tasklist;
tasklist = g_object_new (WNCK_TYPE_TASKLIST, NULL);
@@ -547,7 +6147,7 @@
return GTK_WIDGET (tasklist);
}
-@@ -2730,6 +2773,10 @@ wnck_task_popup_menu (WnckTask *task,
+@@ -2953,6 +2964,10 @@
{
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_widget_show (image);
@@ -558,7 +6158,7 @@
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
image);
g_object_unref (pixbuf);
-@@ -3342,6 +3295,14 @@
+@@ -3341,6 +3356,14 @@
text = wnck_task_get_text (task, TRUE, TRUE);
if (text != NULL)
{
@@ -573,7 +6173,7 @@
gtk_label_set_text (GTK_LABEL (task->label), text);
if (wnck_task_get_needs_attention (task))
{
-@@ -3353,6 +3393,7 @@
+@@ -3352,6 +3375,7 @@
_make_gtk_label_normal ((GTK_LABEL (task->label)));
wnck_task_stop_glow (task);
}
@@ -581,7 +6181,7 @@
g_free (text);
}
-@@ -3780,7 +3724,21 @@
+@@ -3770,7 +3794,21 @@
g_free (text);
text = wnck_task_get_text (task, FALSE, FALSE);
@@ -603,7 +6203,7 @@
g_free (text);
/* Set up signals */
-@@ -4075,6 +4075,48 @@
+@@ -4135,6 +4173,48 @@
#endif
}
@@ -652,7 +6252,7 @@
static WnckTask *
wnck_task_new_from_window (WnckTasklist *tasklist,
WnckWindow *window)
-@@ -4174,6 +4907,14 @@
+@@ -4150,6 +4230,14 @@
wnck_task_create_widgets (task, tasklist->priv->relief);
@@ -667,9 +6267,4379 @@
remove_startup_sequences_for_window (tasklist, window);
return task;
-diff -Nrup libwnck-2.19.4/libwnck/trusted-tooltips.c ../libwnck-2.19.4/libwnck/trusted-tooltips.c
---- libwnck-2.19.4/libwnck/trusted-tooltips.c 1970-01-01 01:00:00.000000000 +0100
-+++ ../libwnck-2.19.4/libwnck/trusted-tooltips.c 2007-06-27 15:08:01.838168000 +0200
+diff -urN libwnck.orig/libwnck/tasklist.c.orig libwnck.new/libwnck/tasklist.c.orig
+--- libwnck.orig/libwnck/tasklist.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/tasklist.c.orig 2007-11-30 14:02:08.627637000 +0000
+@@ -0,0 +1,4366 @@
++/* tasklist object */
++/* vim: set sw=2 et: */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2003 Kim Woelders
++ * Copyright (C) 2003 Red Hat, Inc.
++ * Copyright (C) 2003, 2005-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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 <math.h>
++#include <string.h>
++#include <stdio.h>
++#include <glib/gi18n-lib.h>
++#include "tasklist.h"
++#include "window.h"
++#include "class-group.h"
++#include "window-action-menu.h"
++#include "workspace.h"
++#include "xutils.h"
++#include "private.h"
++
++/**
++ * SECTION:tasklist
++ * @short_description: a tasklist widget, showing the list of windows as a list
++ * of buttons.
++ * @see_also: #WnckScreen, #WnckSelector
++ * @stability: Unstable
++ *
++ * The #WnckTasklist represents client windows on a screen as a list of buttons
++ * labelled with the window titles and icons. Pressing a button can activate or
++ * minimize the represented window, and other typical actions are available
++ * through a popup menu. Windows needing attention can also be distinguished
++ * by a fade effect on the buttons representing them, to help attract the
++ * user's attention.
++ *
++ * The behavior of the #WnckTasklist can be customized in various ways, like
++ * grouping multiple windows of the same application in one button (see
++ * wnck_tasklist_set_grouping() and wnck_tasklist_set_grouping_limit()), or
++ * showing windows from all workspaces (see
++ * wnck_tasklist_set_include_all_workspaces()). The fade effect for windows
++ * needing attention can be controlled by various style properties like
++ * #WnckTasklist:fade-max-loops and #WnckTasklist:fade-opacity.
++ *
++ * The tasklist also acts as iconification destination. If there are multiple
++ * #WnckTasklist or other applications setting the iconification destination
++ * for windows, the iconification destinations might not be consistent among
++ * windows and it is not possible to determine which #WnckTasklist (or which
++ * other application) owns this propriety.
++ */
++
++/* TODO:
++ *
++ * Add total focused time to the grouping score function
++ * Fine tune the grouping scoring function
++ * Fix "changes" to icon for groups/applications
++ * Maybe fine tune size_allocate() some more...
++ * Better vertical layout handling
++ * prefs
++ * support for right-click menu merging w/ bonobo for the applet
++ *
++ */
++
++
++#define WNCK_TYPE_TASK (wnck_task_get_type ())
++#define WNCK_TASK(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WNCK_TYPE_TASK, WnckTask))
++#define WNCK_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WNCK_TYPE_TASK, WnckTaskClass))
++#define WNCK_IS_TASK(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WNCK_TYPE_TASK))
++#define WNCK_IS_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WNCK_TYPE_TASK))
++#define WNCK_TASK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WNCK_TYPE_TASK, WnckTaskClass))
++
++typedef struct _WnckTask WnckTask;
++typedef struct _WnckTaskClass WnckTaskClass;
++
++#define DEFAULT_GROUPING_LIMIT 80
++
++#define MINI_ICON_SIZE DEFAULT_MINI_ICON_WIDTH
++#define TASKLIST_BUTTON_PADDING 4
++#define TASKLIST_TEXT_MAX_WIDTH 25 /* maximum width in characters */
++
++#define N_SCREEN_CONNECTIONS 5
++
++#define POINT_IN_RECT(xcoord, ycoord, rect) \
++ ((xcoord) >= (rect).x && \
++ (xcoord) < ((rect).x + (rect).width) && \
++ (ycoord) >= (rect).y && \
++ (ycoord) < ((rect).y + (rect).height))
++
++typedef enum
++{
++ WNCK_TASK_CLASS_GROUP,
++ WNCK_TASK_WINDOW,
++ WNCK_TASK_STARTUP_SEQUENCE
++} WnckTaskType;
++
++struct _WnckTask
++{
++ GObject parent_instance;
++
++ WnckTasklist *tasklist;
++
++ GtkWidget *button;
++ GtkWidget *image;
++ GtkWidget *label;
++
++ WnckTaskType type;
++
++ WnckClassGroup *class_group;
++ WnckWindow *window;
++#ifdef HAVE_STARTUP_NOTIFICATION
++ SnStartupSequence *startup_sequence;
++#endif
++
++ gdouble grouping_score;
++
++ GList *windows; /* List of the WnckTask for the window,
++ if this is a class group */
++ guint state_changed_tag;
++ guint icon_changed_tag;
++ guint name_changed_tag;
++ guint class_name_changed_tag;
++ guint class_icon_changed_tag;
++
++ /* task menu */
++ GtkWidget *menu;
++ /* ops menu */
++ GtkWidget *action_menu;
++
++ guint really_toggling : 1; /* Set when tasklist really wants
++ * to change the togglebutton state
++ */
++ guint was_active : 1; /* used to fixup activation behavior */
++
++ guint button_activate;
++
++ guint32 dnd_timestamp;
++
++ GdkPixmap *screenshot;
++ GdkPixmap *screenshot_faded;
++
++ time_t start_needs_attention;
++ gdouble glow_start_time;
++
++ guint button_glow;
++
++ guint row;
++ guint col;
++};
++
++struct _WnckTaskClass
++{
++ GObjectClass parent_class;
++};
++
++typedef struct _skipped_window
++{
++ WnckWindow *window;
++ gulong tag;
++} skipped_window;
++
++struct _WnckTasklistPrivate
++{
++ WnckScreen *screen;
++
++ WnckTask *active_task; /* NULL if active window not in tasklist */
++ WnckTask *active_class_group; /* NULL if active window not in tasklist */
++
++ gboolean include_all_workspaces;
++
++ /* Calculated by update_lists */
++ GList *class_groups;
++ GList *windows;
++ GList *windows_without_class_group;
++
++ /* Not handled by update_lists */
++ GList *startup_sequences;
++
++ /* windows with _NET_WM_STATE_SKIP_TASKBAR set; connected to
++ * "state_changed" signal, but excluded from tasklist.
++ */
++ GList *skipped_windows;
++
++ GHashTable *class_group_hash;
++ GHashTable *win_hash;
++
++ gint max_button_width;
++ gint max_button_height;
++
++ gboolean switch_workspace_on_unminimize;
++
++ WnckTasklistGroupingType grouping;
++ gint grouping_limit;
++
++ guint activate_timeout_id;
++ guint screen_connections [N_SCREEN_CONNECTIONS];
++
++ guint idle_callback_tag;
++
++ int *size_hints;
++ int size_hints_len;
++
++ WnckLoadIconFunction icon_loader;
++ void *icon_loader_data;
++ GDestroyNotify free_icon_loader_data;
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ SnMonitorContext *sn_context;
++ guint startup_sequence_timeout;
++#endif
++
++ gint monitor_num;
++ GdkRectangle monitor_geometry;
++ GtkReliefStyle relief;
++
++ GdkPixmap *background;
++};
++
++static GType wnck_task_get_type (void);
++
++G_DEFINE_TYPE (WnckTask, wnck_task, G_TYPE_OBJECT);
++G_DEFINE_TYPE (WnckTasklist, wnck_tasklist, GTK_TYPE_CONTAINER);
++#define WNCK_TASKLIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WNCK_TYPE_TASKLIST, WnckTasklistPrivate))
++
++static void wnck_task_init (WnckTask *task);
++static void wnck_task_class_init (WnckTaskClass *klass);
++static void wnck_task_finalize (GObject *object);
++
++static void wnck_task_stop_glow (WnckTask *task);
++
++static WnckTask *wnck_task_new_from_window (WnckTasklist *tasklist,
++ WnckWindow *window);
++static WnckTask *wnck_task_new_from_class_group (WnckTasklist *tasklist,
++ WnckClassGroup *class_group);
++#ifdef HAVE_STARTUP_NOTIFICATION
++static WnckTask *wnck_task_new_from_startup_sequence (WnckTasklist *tasklist,
++ SnStartupSequence *sequence);
++#endif
++static gboolean wnck_task_get_needs_attention (WnckTask *task);
++
++
++static char *wnck_task_get_text (WnckTask *task,
++ gboolean icon_text,
++ gboolean include_state);
++static GdkPixbuf *wnck_task_get_icon (WnckTask *task);
++static gint wnck_task_compare_alphabetically (gconstpointer a,
++ gconstpointer b);
++static gint wnck_task_compare (gconstpointer a,
++ gconstpointer b);
++static void wnck_task_update_visible_state (WnckTask *task);
++static void wnck_task_state_changed (WnckWindow *window,
++ WnckWindowState changed_mask,
++ WnckWindowState new_state,
++ gpointer data);
++
++static void wnck_task_drag_begin (GtkWidget *widget,
++ GdkDragContext *context,
++ WnckTask *task);
++static void wnck_task_drag_data_get (GtkWidget *widget,
++ GdkDragContext *context,
++ GtkSelectionData *selection_data,
++ guint info,
++ guint time,
++ WnckTask *task);
++
++static void wnck_tasklist_init (WnckTasklist *tasklist);
++static void wnck_tasklist_class_init (WnckTasklistClass *klass);
++static GObject *wnck_tasklist_constructor (GType type,
++ guint n_construct_properties,
++ GObjectConstructParam *construct_properties);
++static void wnck_tasklist_finalize (GObject *object);
++
++static void wnck_tasklist_size_request (GtkWidget *widget,
++ GtkRequisition *requisition);
++static void wnck_tasklist_size_allocate (GtkWidget *widget,
++ GtkAllocation *allocation);
++static void wnck_tasklist_realize (GtkWidget *widget);
++static void wnck_tasklist_unrealize (GtkWidget *widget);
++static gint wnck_tasklist_expose (GtkWidget *widget,
++ GdkEventExpose *event);
++static void wnck_tasklist_forall (GtkContainer *container,
++ gboolean include_internals,
++ GtkCallback callback,
++ gpointer callback_data);
++static void wnck_tasklist_remove (GtkContainer *container,
++ GtkWidget *widget);
++static gboolean wnck_tasklist_scroll_cb (WnckTasklist *tasklist,
++ GdkEventScroll *event,
++ gpointer user_data);
++static void wnck_tasklist_free_tasks (WnckTasklist *tasklist);
++static void wnck_tasklist_update_lists (WnckTasklist *tasklist);
++static int wnck_tasklist_layout (GtkAllocation *allocation,
++ int max_width,
++ int max_height,
++ int n_buttons,
++ int *n_cols_out,
++ int *n_rows_out);
++
++static void wnck_tasklist_active_window_changed (WnckScreen *screen,
++ WnckWindow *previous_window,
++ WnckTasklist *tasklist);
++static void wnck_tasklist_active_workspace_changed (WnckScreen *screen,
++ WnckWorkspace *previous_workspace,
++ WnckTasklist *tasklist);
++static void wnck_tasklist_window_added (WnckScreen *screen,
++ WnckWindow *win,
++ WnckTasklist *tasklist);
++static void wnck_tasklist_window_removed (WnckScreen *screen,
++ WnckWindow *win,
++ WnckTasklist *tasklist);
++static void wnck_tasklist_viewports_changed (WnckScreen *screen,
++ WnckTasklist *tasklist);
++static void wnck_tasklist_connect_window (WnckTasklist *tasklist,
++ WnckWindow *window);
++static void wnck_tasklist_disconnect_window (WnckTasklist *tasklist,
++ WnckWindow *window);
++
++static void wnck_tasklist_change_active_task (WnckTasklist *tasklist,
++ WnckTask *active_task);
++static gboolean wnck_tasklist_change_active_timeout (gpointer data);
++static void wnck_tasklist_activate_task_window (WnckTask *task,
++ guint32 timestamp);
++
++static void wnck_tasklist_update_icon_geometries (WnckTasklist *tasklist,
++ GList *visible_tasks);
++static void wnck_tasklist_connect_screen (WnckTasklist *tasklist);
++static void wnck_tasklist_disconnect_screen (WnckTasklist *tasklist);
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++static void wnck_tasklist_sn_event (SnMonitorEvent *event,
++ void *user_data);
++static void wnck_tasklist_check_end_sequence (WnckTasklist *tasklist,
++ WnckWindow *window);
++#endif
++
++
++/*
++ * Keep track of all tasklist instances so we can decide
++ * whether to show windows from all monitors in the
++ * tasklist
++ */
++static GSList *tasklist_instances;
++
++static GType
++wnck_task_get_type (void) G_GNUC_CONST;
++
++static void
++cleanup_screenshots (WnckTask *task)
++{
++ if (task->screenshot != NULL)
++ {
++ g_object_unref (task->screenshot);
++ task->screenshot = NULL;
++ }
++ if (task->screenshot_faded != NULL)
++ {
++ g_object_unref (task->screenshot_faded);
++ task->screenshot_faded = NULL;
++ }
++}
++
++static void
++wnck_task_init (WnckTask *task)
++{
++ task->tasklist = NULL;
++
++ task->button = NULL;
++ task->image = NULL;
++ task->label = NULL;
++
++ task->type = WNCK_TASK_WINDOW;
++
++ task->class_group = NULL;
++ task->window = NULL;
++#ifdef HAVE_STARTUP_NOTIFICATION
++ task->startup_sequence = NULL;
++#endif
++
++ task->grouping_score = 0;
++
++ task->windows = NULL;
++
++ task->state_changed_tag = 0;
++ task->icon_changed_tag = 0;
++ task->name_changed_tag = 0;
++ task->class_name_changed_tag = 0;
++ task->class_icon_changed_tag = 0;
++
++ task->menu = NULL;
++ task->action_menu = NULL;
++
++ task->really_toggling = FALSE;
++
++ task->was_active = FALSE;
++
++ task->button_activate = 0;
++
++ task->dnd_timestamp = 0;
++
++ task->screenshot = NULL;
++ task->screenshot_faded = NULL;
++
++ task->start_needs_attention = 0;
++ task->glow_start_time = 0.0;
++
++ task->button_glow = 0;
++
++ task->row = 0;
++ task->col = 0;
++}
++
++static void
++wnck_task_class_init (WnckTaskClass *klass)
++{
++ GObjectClass *object_class = G_OBJECT_CLASS (klass);
++
++ object_class->finalize = wnck_task_finalize;
++
++ gtk_rc_parse_string ("\n"
++ " style \"tasklist-button-style\"\n"
++ " {\n"
++ " GtkWidget::focus-line-width=0\n"
++ " GtkWidget::focus-padding=0\n"
++ " }\n"
++ "\n"
++ " widget \"*.tasklist-button\" style \"tasklist-button-style\"\n"
++ "\n");
++}
++
++static gboolean
++wnck_task_button_glow (WnckTask *task)
++{
++ GTimeVal tv;
++ gdouble glow_factor, now;
++ gfloat fade_opacity, loop_time;
++ gint fade_max_loops;
++ gboolean stopped;
++ cairo_t *cr;
++
++ if (task->screenshot == NULL)
++ return TRUE;
++
++ g_get_current_time (&tv);
++ now = (tv.tv_sec * (1.0 * G_USEC_PER_SEC) +
++ tv.tv_usec) / G_USEC_PER_SEC;
++
++ if (task->glow_start_time <= G_MINDOUBLE)
++ task->glow_start_time = now;
++
++ gtk_widget_style_get (GTK_WIDGET (task->tasklist), "fade-opacity", &fade_opacity,
++ "fade-loop-time", &loop_time,
++ "fade-max-loops", &fade_max_loops,
++ NULL);
++
++ if (task->button_glow == 0)
++ {
++ /* we're in "has stopped glowing" mode */
++ glow_factor = fade_opacity * 0.5;
++ stopped = TRUE;
++ }
++ else
++ {
++ glow_factor = fade_opacity * (0.5 -
++ 0.5 * cos ((now - task->glow_start_time) *
++ M_PI * 2.0 / loop_time));
++
++ if (now - task->start_needs_attention > loop_time * 1.0 * fade_max_loops)
++ stopped = ABS (glow_factor - fade_opacity * 0.5) < 0.05;
++ else
++ stopped = FALSE;
++ }
++
++ gdk_window_begin_paint_rect (task->button->window,
++ &task->button->allocation);
++
++ cr = gdk_cairo_create (task->button->window);
++ gdk_cairo_rectangle (cr, &task->button->allocation);
++ cairo_translate (cr, task->button->allocation.x, task->button->allocation.y);
++ cairo_clip (cr);
++
++ cairo_save (cr);
++
++ gdk_cairo_set_source_pixmap (cr, task->screenshot, 0., 0.);
++ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
++ cairo_paint (cr);
++
++ cairo_restore (cr);
++
++ gdk_cairo_set_source_pixmap (cr, task->screenshot_faded, 0., 0.);
++ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
++ cairo_paint_with_alpha (cr, glow_factor);
++
++ cairo_destroy (cr);
++
++ gdk_window_end_paint (task->button->window);
++
++ if (stopped)
++ wnck_task_stop_glow (task);
++
++ return !stopped;
++}
++
++static void
++wnck_task_clear_glow_start_timeout_id (WnckTask *task)
++{
++ task->button_glow = 0;
++}
++
++static void
++wnck_task_queue_glow (WnckTask *task)
++{
++ if (task->button_glow == 0)
++ {
++ task->glow_start_time = 0.0;
++
++ /* The animation doesn't speed up or slow down based on the
++ * timeout value, but instead will just appear smoother or
++ * choppier.
++ */
++ task->button_glow =
++ g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
++ 50,
++ (GSourceFunc) wnck_task_button_glow, task,
++ (GDestroyNotify) wnck_task_clear_glow_start_timeout_id);
++ }
++}
++
++static void
++wnck_task_stop_glow (WnckTask *task)
++{
++ if (task->button_glow != 0)
++ g_source_remove (task->button_glow);
++}
++
++static void
++wnck_task_finalize (GObject *object)
++{
++ WnckTask *task;
++
++ task = WNCK_TASK (object);
++
++ if (task->tasklist->priv->active_task == task)
++ wnck_tasklist_change_active_task (task->tasklist, NULL);
++
++ if (task->button)
++ {
++ g_object_remove_weak_pointer (G_OBJECT (task->button),
++ (void**) &task->button);
++ gtk_widget_destroy (task->button);
++ task->button = NULL;
++ task->image = NULL;
++ task->label = NULL;
++ }
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ if (task->startup_sequence)
++ {
++ sn_startup_sequence_unref (task->startup_sequence);
++ task->startup_sequence = NULL;
++ }
++#endif
++
++ g_list_free (task->windows);
++ task->windows = NULL;
++
++ if (task->state_changed_tag != 0)
++ {
++ g_signal_handler_disconnect (task->window,
++ task->state_changed_tag);
++ task->state_changed_tag = 0;
++ }
++
++ if (task->icon_changed_tag != 0)
++ {
++ g_signal_handler_disconnect (task->window,
++ task->icon_changed_tag);
++ task->icon_changed_tag = 0;
++ }
++
++ if (task->name_changed_tag != 0)
++ {
++ g_signal_handler_disconnect (task->window,
++ task->name_changed_tag);
++ task->name_changed_tag = 0;
++ }
++
++ if (task->class_name_changed_tag != 0)
++ {
++ g_signal_handler_disconnect (task->class_group,
++ task->class_name_changed_tag);
++ task->class_name_changed_tag = 0;
++ }
++
++ if (task->class_icon_changed_tag != 0)
++ {
++ g_signal_handler_disconnect (task->class_group,
++ task->class_icon_changed_tag);
++ task->class_icon_changed_tag = 0;
++ }
++
++ if (task->class_group)
++ {
++ g_object_unref (task->class_group);
++ task->class_group = NULL;
++ }
++
++ if (task->window)
++ {
++ g_object_unref (task->window);
++ task->window = NULL;
++ }
++
++ if (task->menu)
++ {
++ gtk_widget_destroy (task->menu);
++ task->menu = NULL;
++ }
++
++ if (task->action_menu)
++ {
++ g_object_remove_weak_pointer (G_OBJECT (task->action_menu),
++ (void**) &task->action_menu);
++ gtk_widget_destroy (task->action_menu);
++ task->action_menu = NULL;
++ }
++
++ if (task->button_activate != 0)
++ {
++ g_source_remove (task->button_activate);
++ task->button_activate = 0;
++ }
++
++ wnck_task_stop_glow (task);
++
++ cleanup_screenshots (task);
++
++ G_OBJECT_CLASS (wnck_task_parent_class)->finalize (object);
++}
++
++static void
++wnck_tasklist_init (WnckTasklist *tasklist)
++{
++ int i;
++ GtkWidget *widget;
++ AtkObject *atk_obj;
++
++ widget = GTK_WIDGET (tasklist);
++
++ GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
++
++ tasklist->priv = WNCK_TASKLIST_GET_PRIVATE (tasklist);
++
++ tasklist->priv->screen = NULL;
++
++ tasklist->priv->active_task = NULL;
++ tasklist->priv->active_class_group = NULL;
++
++ tasklist->priv->include_all_workspaces = FALSE;
++
++ tasklist->priv->class_groups = NULL;
++ tasklist->priv->windows = NULL;
++ tasklist->priv->windows_without_class_group = NULL;
++
++ tasklist->priv->startup_sequences = NULL;
++
++ tasklist->priv->skipped_windows = NULL;
++
++ tasklist->priv->class_group_hash = g_hash_table_new (NULL, NULL);
++ tasklist->priv->win_hash = g_hash_table_new (NULL, NULL);
++
++ tasklist->priv->max_button_width = 0;
++ tasklist->priv->max_button_height = 0;
++
++ tasklist->priv->switch_workspace_on_unminimize = FALSE;
++
++ tasklist->priv->grouping = WNCK_TASKLIST_AUTO_GROUP;
++ tasklist->priv->grouping_limit = DEFAULT_GROUPING_LIMIT;
++
++ tasklist->priv->activate_timeout_id = 0;
++ for (i = 0; i < N_SCREEN_CONNECTIONS; i++)
++ tasklist->priv->screen_connections[i] = 0;
++
++ tasklist->priv->idle_callback_tag = 0;
++
++ tasklist->priv->size_hints = NULL;
++ tasklist->priv->size_hints_len = 0;
++
++ tasklist->priv->icon_loader = NULL;
++ tasklist->priv->icon_loader_data = NULL;
++ tasklist->priv->free_icon_loader_data = NULL;
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ tasklist->priv->sn_context = NULL;
++ tasklist->priv->startup_sequence_timeout = 0;
++#endif
++
++ tasklist->priv->monitor_num = -1;
++ tasklist->priv->monitor_geometry.width = -1; /* invalid value */
++ tasklist->priv->relief = GTK_RELIEF_NORMAL;
++
++ tasklist->priv->background = NULL;
++
++ atk_obj = gtk_widget_get_accessible (widget);
++ atk_object_set_name (atk_obj, _("Window List"));
++ atk_object_set_description (atk_obj, _("Tool to switch between visible windows"));
++}
++
++static void
++wnck_tasklist_class_init (WnckTasklistClass *klass)
++{
++ GObjectClass *object_class = G_OBJECT_CLASS (klass);
++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
++ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
++
++ g_type_class_add_private (klass, sizeof (WnckTasklistPrivate));
++
++ object_class->constructor = wnck_tasklist_constructor;
++ object_class->finalize = wnck_tasklist_finalize;
++
++ widget_class->size_request = wnck_tasklist_size_request;
++ widget_class->size_allocate = wnck_tasklist_size_allocate;
++ widget_class->realize = wnck_tasklist_realize;
++ widget_class->unrealize = wnck_tasklist_unrealize;
++ widget_class->expose_event = wnck_tasklist_expose;
++
++ container_class->forall = wnck_tasklist_forall;
++ container_class->remove = wnck_tasklist_remove;
++
++ /**
++ * WnckTasklist:fade-loop-time:
++ *
++ * When a window needs attention, a fade effect is drawn on the button
++ * representing the window. This property controls the time one loop of this
++ * fade effect takes, in seconds.
++ *
++ * Since: 2.16
++ */
++ gtk_widget_class_install_style_property (widget_class,
++ g_param_spec_float ("fade-loop-time",
++ "Loop time",
++ "The time one loop takes when fading, in seconds. Default: 3.0",
++ 0.2, 10.0, 3.0,
++ G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
++
++ /**
++ * WnckTasklist:fade-max-loops:
++ *
++ * When a window needs attention, a fade effect is drawn on the button
++ * representing the window. This property controls the number of loops for
++ * this fade effect. 0 means the button will only fade to the final color.
++ *
++ * Since: 2.20
++ */
++ gtk_widget_class_install_style_property (widget_class,
++ g_param_spec_int ("fade-max-loops",
++ "Maximum number of loops",
++ "The number of fading loops. 0 means the button will only fade to the final color. Default: 5",
++ 0, 50, 5,
++ G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
++
++ /**
++ * WnckTasklist:fade-overlay-rect:
++ *
++ * When a window needs attention, a fade effect is drawn on the button
++ * representing the window. Set this property to %TRUE to enable a
++ * compatibility mode for pixbuf engine themes that cannot react to color
++ * changes. If enabled, a rectangle with the correct color will be drawn on
++ * top of the button.
++ *
++ * Since: 2.16
++ */
++ gtk_widget_class_install_style_property (widget_class,
++ g_param_spec_boolean ("fade-overlay-rect",
++ "Overlay a rectangle, instead of modifying the background.",
++ "Compatibility mode for pixbuf engine themes that cannot react to color changes. If enabled, a rectangle with the correct color will be drawn on top of the button. Default: TRUE",
++ TRUE,
++ G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
++
++ /**
++ * WnckTasklist:fade-opacity:
++ *
++ * When a window needs attention, a fade effect is drawn on the button
++ * representing the window. This property controls the final opacity that
++ * will be reached by the fade effect.
++ *
++ * Since: 2.16
++ */
++ gtk_widget_class_install_style_property (widget_class,
++ g_param_spec_float ("fade-opacity",
++ "Final opacity",
++ "The final opacity that will be reached. Default: 0.8",
++ 0.0, 1.0, 0.8,
++ G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
++}
++
++static GObject *
++wnck_tasklist_constructor (GType type,
++ guint n_construct_properties,
++ GObjectConstructParam *construct_properties)
++{
++ GObject *obj;
++
++ obj = G_OBJECT_CLASS (wnck_tasklist_parent_class)->constructor (
++ type,
++ n_construct_properties,
++ construct_properties);
++
++ g_signal_connect (obj, "scroll-event",
++ G_CALLBACK (wnck_tasklist_scroll_cb), NULL);
++
++ return obj;
++}
++
++static void
++wnck_tasklist_free_skipped_windows (WnckTasklist *tasklist)
++{
++ GList *l;
++
++ l = tasklist->priv->skipped_windows;
++
++ while (l != NULL)
++ {
++ skipped_window *skipped = (skipped_window*) l->data;
++ g_signal_handler_disconnect (skipped->window, skipped->tag);
++ g_object_unref (skipped->window);
++ g_free (skipped);
++ l = l->next;
++ }
++
++ g_list_free (tasklist->priv->skipped_windows);
++}
++
++static void
++wnck_tasklist_finalize (GObject *object)
++{
++ WnckTasklist *tasklist;
++
++ tasklist = WNCK_TASKLIST (object);
++
++ /* Tasks should have gone away due to removing their
++ * buttons in container destruction
++ */
++ g_assert (tasklist->priv->class_groups == NULL);
++ g_assert (tasklist->priv->windows == NULL);
++ g_assert (tasklist->priv->windows_without_class_group == NULL);
++ g_assert (tasklist->priv->startup_sequences == NULL);
++ /* wnck_tasklist_free_tasks (tasklist); */
++
++ if (tasklist->priv->skipped_windows)
++ {
++ wnck_tasklist_free_skipped_windows (tasklist);
++ tasklist->priv->skipped_windows = NULL;
++ }
++
++ g_hash_table_destroy (tasklist->priv->class_group_hash);
++ tasklist->priv->class_group_hash = NULL;
++
++ g_hash_table_destroy (tasklist->priv->win_hash);
++ tasklist->priv->win_hash = NULL;
++
++ if (tasklist->priv->activate_timeout_id != 0)
++ {
++ g_source_remove (tasklist->priv->activate_timeout_id);
++ tasklist->priv->activate_timeout_id = 0;
++ }
++
++ if (tasklist->priv->idle_callback_tag != 0)
++ {
++ g_source_remove (tasklist->priv->idle_callback_tag);
++ tasklist->priv->idle_callback_tag = 0;
++ }
++
++ g_free (tasklist->priv->size_hints);
++ tasklist->priv->size_hints = NULL;
++ tasklist->priv->size_hints_len = 0;
++
++ if (tasklist->priv->free_icon_loader_data != NULL)
++ (* tasklist->priv->free_icon_loader_data) (tasklist->priv->icon_loader_data);
++ tasklist->priv->free_icon_loader_data = NULL;
++ tasklist->priv->icon_loader_data = NULL;
++
++ if (tasklist->priv->background)
++ {
++ g_object_unref (tasklist->priv->background);
++ tasklist->priv->background = NULL;
++ }
++
++ G_OBJECT_CLASS (wnck_tasklist_parent_class)->finalize (object);
++}
++
++/**
++ * wnck_tasklist_set_grouping:
++ * @tasklist: a #WnckTasklist.
++ * @grouping: a grouping policy.
++ *
++ * Sets the grouping policy for @tasklist to @grouping.
++ */
++void
++wnck_tasklist_set_grouping (WnckTasklist *tasklist,
++ WnckTasklistGroupingType grouping)
++{
++ g_return_if_fail (WNCK_IS_TASKLIST (tasklist));
++
++ if (tasklist->priv->grouping == grouping)
++ return;
++
++ tasklist->priv->grouping = grouping;
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++static void
++wnck_tasklist_set_relief_callback (WnckWindow *win,
++ WnckTask *task,
++ WnckTasklist *tasklist)
++{
++ gtk_button_set_relief (GTK_BUTTON (task->button), tasklist->priv->relief);
++}
++
++/**
++ * wnck_tasklist_set_button_relief:
++ * @tasklist: a #WnckTasklist.
++ * @relief: a relief type.
++ *
++ * Sets the relief type of the buttons in @tasklist to @relief. The main use of
++ * this function is proper integration of #WnckTasklist in panels with
++ * non-system backgrounds.
++ *
++ * Since: 2.12
++ */
++void
++wnck_tasklist_set_button_relief (WnckTasklist *tasklist, GtkReliefStyle relief)
++{
++ GList *walk;
++
++ g_return_if_fail (WNCK_IS_TASKLIST (tasklist));
++
++ if (relief == tasklist->priv->relief)
++ return;
++
++ tasklist->priv->relief = relief;
++
++ g_hash_table_foreach (tasklist->priv->win_hash,
++ (GHFunc) wnck_tasklist_set_relief_callback,
++ tasklist);
++ for (walk = tasklist->priv->class_groups; walk; walk = g_list_next (walk))
++ gtk_button_set_relief (GTK_BUTTON (WNCK_TASK (walk->data)->button), relief);
++}
++
++/**
++ * wnck_tasklist_set_switch_workspace_on_unminimize:
++ * @tasklist: a #WnckTasklist.
++ * @switch_workspace_on_unminimize: whether to activate the #WnckWorkspace a
++ * #WnckWindow is on when unminimizing it.
++ *
++ * Sets @tasklist to activate or not the #WnckWorkspace a #WnckWindow is on
++ * when unminimizing it, according to @switch_workspace_on_unminimize.
++ *
++ * FIXME: does it still work?
++ */
++void
++wnck_tasklist_set_switch_workspace_on_unminimize (WnckTasklist *tasklist,
++ gboolean switch_workspace_on_unminimize)
++{
++ g_return_if_fail (WNCK_IS_TASKLIST (tasklist));
++
++ tasklist->priv->switch_workspace_on_unminimize = switch_workspace_on_unminimize;
++}
++
++/**
++ * wnck_tasklist_set_include_all_workspaces:
++ * @tasklist: a #WnckTasklist.
++ * @include_all_workspaces: whether to display #WnckWindow from all
++ * #WnckWorkspace in @tasklist.
++ *
++ * Sets @tasklist to display #WnckWindow from all #WnckWorkspace or not,
++ * according to @include_all_workspaces.
++ *
++ * Note that if the active #WnckWorkspace has a viewport and if
++ * @include_all_workspaces is %FALSE, then only the #WnckWindow visible in the
++ * viewport are displayed in @tasklist. The rationale for this is that the
++ * viewport is generally used to implement workspace-like behavior. A
++ * side-effect of this is that, when using multiple #WnckWorkspace with
++ * viewport, it is not possible to show all #WnckWindow from a #WnckWorkspace
++ * (even those that are not visible in the viewport) in @tasklist without
++ * showing all #WnckWindow from all #WnckWorkspace.
++ */
++void
++wnck_tasklist_set_include_all_workspaces (WnckTasklist *tasklist,
++ gboolean include_all_workspaces)
++{
++ g_return_if_fail (WNCK_IS_TASKLIST (tasklist));
++
++ include_all_workspaces = (include_all_workspaces != 0);
++
++ if (tasklist->priv->include_all_workspaces == include_all_workspaces)
++ return;
++
++ tasklist->priv->include_all_workspaces = include_all_workspaces;
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++/**
++ * wnck_tasklist_set_grouping_limit:
++ * @tasklist: a #WnckTasklist.
++ * @limit: a size in pixels.
++ *
++ * Sets the maximum size of buttons in @tasklist before @tasklist tries to
++ * group #WnckWindow in the same #WnckApplication in only one button. This
++ * limit is valid only when the grouping policy of @tasklist is
++ * %WNCK_TASKLIST_AUTO_GROUP.
++ */
++void
++wnck_tasklist_set_grouping_limit (WnckTasklist *tasklist,
++ gint limit)
++{
++ g_return_if_fail (WNCK_IS_TASKLIST (tasklist));
++
++ if (tasklist->priv->grouping_limit == limit)
++ return;
++
++ tasklist->priv->grouping_limit = limit;
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++/**
++ * wnck_tasklist_set_minimum_width:
++ * @tasklist: a #WnckTasklist.
++ * @size: a minimum width in pixels.
++ *
++ * Does nothing.
++ *
++ * Deprecated:2.20:
++ */
++void
++wnck_tasklist_set_minimum_width (WnckTasklist *tasklist, gint size)
++{
++}
++
++/**
++ * wnck_tasklist_get_minimum_width:
++ * @tasklist: a #WnckTasklist.
++ *
++ * Returns -1.
++ *
++ * Return value: -1.
++ *
++ * Deprecated:2.20:
++ */
++gint
++wnck_tasklist_get_minimum_width (WnckTasklist *tasklist)
++{
++ return -1;
++}
++
++/**
++ * wnck_tasklist_set_minimum_height:
++ * @tasklist: a #WnckTasklist.
++ * @size: a minimum height in pixels.
++ *
++ * Does nothing.
++ *
++ * Deprecated:2.20:
++ */
++void
++wnck_tasklist_set_minimum_height (WnckTasklist *tasklist, gint size)
++{
++}
++
++/**
++ * wnck_tasklist_get_minimum_height:
++ * @tasklist: a #WnckTasklist.
++ *
++ * Returns -1.
++ *
++ * Return value: -1.
++ *
++ * Deprecated:2.20:
++ */
++gint
++wnck_tasklist_get_minimum_height (WnckTasklist *tasklist)
++{
++ return -1;
++}
++
++/**
++ * wnck_tasklist_set_icon_loader:
++ * @tasklist: a #WnckTasklist
++ * @load_icon_func: icon loader function
++ * @data: data for icon loader function
++ * @free_data_func: function to free the data
++ *
++ * Sets a function to be used for loading icons.
++ *
++ * Since: 2.2
++ **/
++void
++wnck_tasklist_set_icon_loader (WnckTasklist *tasklist,
++ WnckLoadIconFunction load_icon_func,
++ void *data,
++ GDestroyNotify free_data_func)
++{
++ g_return_if_fail (WNCK_IS_TASKLIST (tasklist));
++
++ if (tasklist->priv->free_icon_loader_data != NULL)
++ (* tasklist->priv->free_icon_loader_data) (tasklist->priv->icon_loader_data);
++
++ tasklist->priv->icon_loader = load_icon_func;
++ tasklist->priv->icon_loader_data = data;
++ tasklist->priv->free_icon_loader_data = free_data_func;
++}
++
++/* returns the maximal possible button width (i.e. if you
++ * don't want to stretch the buttons to fill the alloctions
++ * the width can be smaller) */
++static int
++wnck_tasklist_layout (GtkAllocation *allocation,
++ int max_width,
++ int max_height,
++ int n_buttons,
++ int *n_cols_out,
++ int *n_rows_out)
++{
++ int n_cols, n_rows;
++
++ /* How many rows fit in the allocation */
++ n_rows = allocation->height / max_height;
++
++ /* Don't have more rows than buttons */
++ n_rows = MIN (n_rows, n_buttons);
++
++ /* At least one row */
++ n_rows = MAX (n_rows, 1);
++
++ /* We want to use as many rows as possible to limit the width */
++ n_cols = (n_buttons + n_rows - 1) / n_rows;
++
++ /* At least one column */
++ n_cols = MAX (n_cols, 1);
++
++ *n_cols_out = n_cols;
++ *n_rows_out = n_rows;
++
++ return allocation->width / n_cols;
++}
++
++static void
++wnck_tasklist_score_groups (WnckTasklist *tasklist,
++ GList *ungrouped_class_groups)
++{
++ WnckTask *class_group_task;
++ WnckTask *win_task;
++ GList *l, *w;
++ const char *first_name = NULL;
++ int n_windows;
++ int n_same_title;
++ double same_window_ratio;
++
++ l = ungrouped_class_groups;
++ while (l != NULL)
++ {
++ class_group_task = WNCK_TASK (l->data);
++
++ n_windows = g_list_length (class_group_task->windows);
++
++ n_same_title = 0;
++ w = class_group_task->windows;
++ while (w != NULL)
++ {
++ win_task = WNCK_TASK (w->data);
++
++ if (first_name == NULL)
++ {
++ if (wnck_window_has_icon_name (win_task->window))
++ first_name = wnck_window_get_icon_name (win_task->window);
++ else
++ first_name = wnck_window_get_name (win_task->window);
++ n_same_title++;
++ }
++ else
++ {
++ const char *name;
++
++ if (wnck_window_has_icon_name (win_task->window))
++ name = wnck_window_get_icon_name (win_task->window);
++ else
++ name = wnck_window_get_name (win_task->window);
++
++ if (strcmp (name, first_name) == 0)
++ n_same_title++;
++ }
++
++ w = w->next;
++ }
++ same_window_ratio = (double)n_same_title/(double)n_windows;
++
++ /* FIXME: This is fairly bogus and should be researched more.
++ * XP groups by least used, so we probably want to add
++ * total focused time to this expression.
++ */
++ class_group_task->grouping_score = -same_window_ratio * 5 + n_windows;
++
++ l = l->next;
++ }
++}
++
++static GList *
++wnck_task_get_highest_scored (GList *ungrouped_class_groups,
++ WnckTask **class_group_task_out)
++{
++ WnckTask *class_group_task;
++ WnckTask *best_task = NULL;
++ double max_score = -1000000000.0; /* Large negative score */
++ GList *l;
++
++ l = ungrouped_class_groups;
++ while (l != NULL)
++ {
++ class_group_task = WNCK_TASK (l->data);
++
++ if (class_group_task->grouping_score >= max_score)
++ {
++ max_score = class_group_task->grouping_score;
++ best_task = class_group_task;
++ }
++
++ l = l->next;
++ }
++
++ *class_group_task_out = best_task;
++
++ return g_list_remove (ungrouped_class_groups, best_task);
++}
++
++static int
++wnck_tasklist_get_button_size (GtkWidget *widget)
++{
++ PangoContext *context;
++ PangoFontMetrics *metrics;
++ gint char_width;
++ gint text_width;
++ gint width;
++
++ gtk_widget_ensure_style (widget);
++
++ context = gtk_widget_get_pango_context (widget);
++ metrics = pango_context_get_metrics (context, widget->style->font_desc,
++ pango_context_get_language (context));
++ char_width = pango_font_metrics_get_approximate_char_width (metrics);
++ pango_font_metrics_unref (metrics);
++ text_width = PANGO_PIXELS (TASKLIST_TEXT_MAX_WIDTH * char_width);
++
++ width = text_width + 2 * TASKLIST_BUTTON_PADDING
++ + MINI_ICON_SIZE + 2 * TASKLIST_BUTTON_PADDING;
++
++ return width;
++}
++
++static void
++wnck_tasklist_size_request (GtkWidget *widget,
++ GtkRequisition *requisition)
++{
++ WnckTasklist *tasklist;
++ GtkRequisition child_req;
++ GtkAllocation fake_allocation;
++ int max_height = 1;
++ int max_width = 1;
++ /* int u_width, u_height; */
++ GList *l;
++ GArray *array;
++ GList *ungrouped_class_groups;
++ int n_windows;
++ int n_startup_sequences;
++ int n_rows;
++ int n_cols, last_n_cols;
++ int n_grouped_buttons;
++ gboolean score_set;
++ int val;
++ WnckTask *class_group_task;
++ int lowest_range;
++ int grouping_limit;
++
++ tasklist = WNCK_TASKLIST (widget);
++
++ /* Calculate max needed height and width of the buttons */
++#define GET_MAX_WIDTH_HEIGHT_FROM_BUTTONS(list) \
++ l = list; \
++ while (l != NULL) \
++ { \
++ WnckTask *task = WNCK_TASK (l->data); \
++ \
++ gtk_widget_size_request (task->button, &child_req); \
++ \
++ max_height = MAX (child_req.height, \
++ max_height); \
++ max_width = MAX (child_req.width, \
++ max_width); \
++ \
++ l = l->next; \
++ }
++
++ GET_MAX_WIDTH_HEIGHT_FROM_BUTTONS (tasklist->priv->windows)
++ GET_MAX_WIDTH_HEIGHT_FROM_BUTTONS (tasklist->priv->class_groups)
++ GET_MAX_WIDTH_HEIGHT_FROM_BUTTONS (tasklist->priv->startup_sequences)
++
++ /* Note that the fact that we nearly don't care about the width/height
++ * requested by the buttons makes it possible to hide/show the label/image
++ * in wnck_task_size_allocated(). If we really cared about those, this
++ * wouldn't work since our call to gtk_widget_size_request() does not take
++ * into account the hidden widgets.
++ */
++ tasklist->priv->max_button_width = wnck_tasklist_get_button_size (widget);
++ tasklist->priv->max_button_height = max_height;
++
++ fake_allocation.width = GTK_WIDGET (tasklist)->allocation.width;
++ fake_allocation.height = GTK_WIDGET (tasklist)->allocation.height;
++
++ array = g_array_new (FALSE, FALSE, sizeof (int));
++
++ /* Calculate size_hints list */
++
++ n_windows = g_list_length (tasklist->priv->windows);
++ n_startup_sequences = g_list_length (tasklist->priv->startup_sequences);
++ n_grouped_buttons = 0;
++ ungrouped_class_groups = g_list_copy (tasklist->priv->class_groups);
++ score_set = FALSE;
++
++ grouping_limit = MIN (tasklist->priv->grouping_limit,
++ tasklist->priv->max_button_width);
++
++ /* Try ungrouped mode */
++ wnck_tasklist_layout (&fake_allocation,
++ tasklist->priv->max_button_width,
++ tasklist->priv->max_button_height,
++ n_windows + n_startup_sequences,
++ &n_cols, &n_rows);
++
++ last_n_cols = G_MAXINT;
++ lowest_range = G_MAXINT;
++ if (tasklist->priv->grouping != WNCK_TASKLIST_ALWAYS_GROUP)
++ {
++ val = n_cols * tasklist->priv->max_button_width;
++ g_array_insert_val (array, array->len, val);
++ val = n_cols * grouping_limit;
++ g_array_insert_val (array, array->len, val);
++
++ last_n_cols = n_cols;
++ lowest_range = val;
++ }
++
++ while (ungrouped_class_groups != NULL &&
++ tasklist->priv->grouping != WNCK_TASKLIST_NEVER_GROUP)
++ {
++ if (!score_set)
++ {
++ wnck_tasklist_score_groups (tasklist, ungrouped_class_groups);
++ score_set = TRUE;
++ }
++
++ ungrouped_class_groups = wnck_task_get_highest_scored (ungrouped_class_groups, &class_group_task);
++
++ n_grouped_buttons += g_list_length (class_group_task->windows) - 1;
++
++ wnck_tasklist_layout (&fake_allocation,
++ tasklist->priv->max_button_width,
++ tasklist->priv->max_button_height,
++ n_startup_sequences + n_windows - n_grouped_buttons,
++ &n_cols, &n_rows);
++ if (n_cols != last_n_cols &&
++ (tasklist->priv->grouping == WNCK_TASKLIST_AUTO_GROUP ||
++ ungrouped_class_groups == NULL))
++ {
++ val = n_cols * tasklist->priv->max_button_width;
++ if (val >= lowest_range)
++ { /* Overlaps old range */
++ g_assert (array->len > 0);
++ lowest_range = n_cols * grouping_limit;
++ g_array_index(array, int, array->len-1) = lowest_range;
++ }
++ else
++ {
++ /* Full new range */
++ g_array_insert_val (array, array->len, val);
++ val = n_cols * grouping_limit;
++ g_array_insert_val (array, array->len, val);
++ lowest_range = val;
++ }
++
++ last_n_cols = n_cols;
++ }
++ }
++
++ g_list_free (ungrouped_class_groups);
++
++ /* Always let you go down to a zero size: */
++ if (array->len > 0)
++ g_array_index(array, int, array->len-1) = 0;
++ else
++ {
++ val = 0;
++ g_array_insert_val (array, 0, val);
++ g_array_insert_val (array, 0, val);
++ }
++
++ if (tasklist->priv->size_hints)
++ g_free (tasklist->priv->size_hints);
++
++ tasklist->priv->size_hints_len = array->len;
++
++ tasklist->priv->size_hints = (int *)g_array_free (array, FALSE);
++
++ requisition->width = tasklist->priv->size_hints[0];
++ requisition->height = fake_allocation.height;
++}
++
++/**
++ * wnck_tasklist_get_size_hint_list:
++ * @tasklist: a #WnckTasklist.
++ * @n_elements: return location for the number of elements in the array
++ * returned by this function. This number should always be pair.
++ *
++ * Since a #WnckTasklist does not have a fixed size (#WnckWindow can be grouped
++ * when needed, for example), the standard size request mechanism in GTK+ is
++ * not enough to announce what sizes can be used by @tasklist. The size hints
++ * mechanism is a solution for this. See panel_applet_set_size_hints() for more
++ * information.
++ *
++ * Return value: a list of size hints that can be used to allocate an
++ * appropriate size for @tasklist.
++ */
++const int *
++wnck_tasklist_get_size_hint_list (WnckTasklist *tasklist,
++ int *n_elements)
++{
++ g_return_val_if_fail (WNCK_IS_TASKLIST (tasklist), NULL);
++ g_return_val_if_fail (n_elements != NULL, NULL);
++
++ *n_elements = tasklist->priv->size_hints_len;
++ return tasklist->priv->size_hints;
++}
++
++static void
++wnck_task_size_allocated (GtkWidget *widget,
++ GtkAllocation *allocation,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++ int min_image_width;
++
++ min_image_width = MINI_ICON_SIZE +
++ 2 * widget->style->xthickness +
++ 2 * TASKLIST_BUTTON_PADDING;
++
++ if ((allocation->width < min_image_width + 2 * TASKLIST_BUTTON_PADDING) &&
++ (allocation->width >= min_image_width)) {
++ gtk_widget_show (task->image);
++ gtk_widget_hide (task->label);
++ } else if (allocation->width < min_image_width) {
++ gtk_widget_hide (task->image);
++ gtk_widget_show (task->label);
++ } else {
++ gtk_widget_show (task->image);
++ gtk_widget_show (task->label);
++ }
++}
++
++static void
++wnck_tasklist_size_allocate (GtkWidget *widget,
++ GtkAllocation *allocation)
++{
++ GtkAllocation child_allocation;
++ WnckTasklist *tasklist;
++ WnckTask *class_group_task;
++ int n_windows;
++ int n_startup_sequences;
++ GList *l;
++ int button_width;
++ int total_width;
++ int n_rows;
++ int n_cols;
++ int n_grouped_buttons;
++ int i;
++ gboolean score_set;
++ GList *ungrouped_class_groups;
++ WnckTask *win_task;
++ GList *visible_tasks = NULL;
++ GList *windows_sorted = NULL;
++ int grouping_limit;
++
++ tasklist = WNCK_TASKLIST (widget);
++
++ n_windows = g_list_length (tasklist->priv->windows);
++ n_startup_sequences = g_list_length (tasklist->priv->startup_sequences);
++ n_grouped_buttons = 0;
++ ungrouped_class_groups = g_list_copy (tasklist->priv->class_groups);
++ score_set = FALSE;
++
++ grouping_limit = MIN (tasklist->priv->grouping_limit,
++ tasklist->priv->max_button_width);
++
++ /* Try ungrouped mode */
++ button_width = wnck_tasklist_layout (allocation,
++ tasklist->priv->max_button_width,
++ tasklist->priv->max_button_height,
++ n_startup_sequences + n_windows,
++ &n_cols, &n_rows);
++ while (ungrouped_class_groups != NULL &&
++ ((tasklist->priv->grouping == WNCK_TASKLIST_ALWAYS_GROUP) ||
++ ((tasklist->priv->grouping == WNCK_TASKLIST_AUTO_GROUP) &&
++ (button_width < grouping_limit))))
++ {
++ if (!score_set)
++ {
++ wnck_tasklist_score_groups (tasklist, ungrouped_class_groups);
++ score_set = TRUE;
++ }
++
++ ungrouped_class_groups = wnck_task_get_highest_scored (ungrouped_class_groups, &class_group_task);
++
++ n_grouped_buttons += g_list_length (class_group_task->windows) - 1;
++
++ if (g_list_length (class_group_task->windows) > 1)
++ {
++ visible_tasks = g_list_prepend (visible_tasks, class_group_task);
++
++ /* Sort */
++ class_group_task->windows = g_list_sort (class_group_task->windows,
++ wnck_task_compare_alphabetically);
++
++ /* Hide all this group's windows */
++ l = class_group_task->windows;
++ while (l != NULL)
++ {
++ win_task = WNCK_TASK (l->data);
++
++ gtk_widget_set_child_visible (GTK_WIDGET (win_task->button), FALSE);
++
++ cleanup_screenshots (win_task);
++
++ l = l->next;
++ }
++ }
++ else
++ {
++ visible_tasks = g_list_prepend (visible_tasks, class_group_task->windows->data);
++ gtk_widget_set_child_visible (GTK_WIDGET (class_group_task->button), FALSE);
++
++ cleanup_screenshots (class_group_task);
++ }
++
++ button_width = wnck_tasklist_layout (allocation,
++ tasklist->priv->max_button_width,
++ tasklist->priv->max_button_height,
++ n_startup_sequences + n_windows - n_grouped_buttons,
++ &n_cols, &n_rows);
++ }
++
++ /* Add all ungrouped windows to visible_tasks, and hide their class groups */
++ l = ungrouped_class_groups;
++ while (l != NULL)
++ {
++ class_group_task = WNCK_TASK (l->data);
++
++ visible_tasks = g_list_concat (visible_tasks, g_list_copy (class_group_task->windows));
++ gtk_widget_set_child_visible (GTK_WIDGET (class_group_task->button), FALSE);
++
++ cleanup_screenshots (class_group_task);
++
++ l = l->next;
++ }
++
++ /* Add all windows that are ungrouped because they don't belong to any class
++ * group */
++ l = tasklist->priv->windows_without_class_group;
++ while (l != NULL)
++ {
++ WnckTask *task;
++
++ task = WNCK_TASK (l->data);
++ visible_tasks = g_list_append (visible_tasks, task);
++
++ l = l->next;
++ }
++
++ /* Add all startup sequences */
++ visible_tasks = g_list_concat (visible_tasks, g_list_copy (tasklist->priv->startup_sequences));
++
++ /* Sort */
++ visible_tasks = g_list_sort (visible_tasks, wnck_task_compare);
++
++ /* Allocate children */
++ l = visible_tasks;
++ i = 0;
++ total_width = tasklist->priv->max_button_width * n_cols;
++ total_width = MIN (total_width, allocation->width);
++ /* FIXME: this is obviously wrong, but if we don't this, some space that the
++ * panel allocated to us won't have the panel popup menu, but the tasklist
++ * popup menu */
++ total_width = allocation->width;
++ while (l != NULL)
++ {
++ WnckTask *task = WNCK_TASK (l->data);
++ int row = i % n_rows;
++ int col = i / n_rows;
++
++ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
++ col = n_cols - col - 1;
++
++ child_allocation.x = total_width*col / n_cols;
++ child_allocation.y = allocation->height*row / n_rows;
++ child_allocation.width = total_width*(col + 1) / n_cols - child_allocation.x;
++ child_allocation.height = allocation->height*(row + 1) / n_rows - child_allocation.y;
++ child_allocation.x += allocation->x;
++ child_allocation.y += allocation->y;
++
++ gtk_widget_size_allocate (task->button, &child_allocation);
++ gtk_widget_set_child_visible (GTK_WIDGET (task->button), TRUE);
++
++ if (task->type != WNCK_TASK_STARTUP_SEQUENCE)
++ {
++ GList *ll;
++
++ /* Build sorted windows list */
++ if (g_list_length (task->windows) > 1)
++ windows_sorted = g_list_concat (windows_sorted,
++ g_list_copy (task->windows));
++ else
++ windows_sorted = g_list_append (windows_sorted, task);
++ task->row = row;
++ task->col = col;
++ for (ll = task->windows; ll; ll = ll->next)
++ {
++ WNCK_TASK (ll->data)->row = row;
++ WNCK_TASK (ll->data)->col = col;
++ }
++ }
++ i++;
++ l = l->next;
++ }
++
++ /* Update icon geometries. */
++ wnck_tasklist_update_icon_geometries (tasklist, visible_tasks);
++
++ g_list_free (visible_tasks);
++ g_list_free (tasklist->priv->windows);
++ g_list_free (ungrouped_class_groups);
++ tasklist->priv->windows = windows_sorted;
++
++ GTK_WIDGET_CLASS (wnck_tasklist_parent_class)->size_allocate (widget,
++ allocation);
++}
++
++static void
++wnck_tasklist_realize (GtkWidget *widget)
++{
++ WnckTasklist *tasklist;
++ GdkScreen *gdkscreen;
++
++ tasklist = WNCK_TASKLIST (widget);
++
++ gdkscreen = gtk_widget_get_screen (widget);
++ tasklist->priv->screen = wnck_screen_get (gdk_screen_get_number (gdkscreen));
++ g_assert (tasklist->priv->screen != NULL);
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ tasklist->priv->sn_context =
++ sn_monitor_context_new (_wnck_screen_get_sn_display (tasklist->priv->screen),
++ wnck_screen_get_number (tasklist->priv->screen),
++ wnck_tasklist_sn_event,
++ tasklist,
++ NULL);
++#endif
++
++ (* GTK_WIDGET_CLASS (wnck_tasklist_parent_class)->realize) (widget);
++
++ tasklist_instances = g_slist_append (tasklist_instances, tasklist);
++ g_slist_foreach (tasklist_instances,
++ (GFunc) wnck_tasklist_update_lists,
++ NULL);
++
++ wnck_tasklist_update_lists (tasklist);
++
++ wnck_tasklist_connect_screen (tasklist);
++}
++
++static void
++wnck_tasklist_unrealize (GtkWidget *widget)
++{
++ WnckTasklist *tasklist;
++
++ tasklist = WNCK_TASKLIST (widget);
++
++ wnck_tasklist_disconnect_screen (tasklist);
++ tasklist->priv->screen = NULL;
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ sn_monitor_context_unref (tasklist->priv->sn_context);
++ tasklist->priv->sn_context = NULL;
++#endif
++
++ (* GTK_WIDGET_CLASS (wnck_tasklist_parent_class)->unrealize) (widget);
++
++ tasklist_instances = g_slist_remove (tasklist_instances, tasklist);
++ g_slist_foreach (tasklist_instances,
++ (GFunc) wnck_tasklist_update_lists,
++ NULL);
++}
++
++static gint
++wnck_tasklist_expose (GtkWidget *widget,
++ GdkEventExpose *event)
++{
++ WnckTasklist *tasklist;
++ GdkGC *gc;
++
++ g_return_val_if_fail (WNCK_IS_TASKLIST (widget), FALSE);
++ g_return_val_if_fail (event != NULL, FALSE);
++
++ if (GTK_WIDGET_DRAWABLE (widget))
++ {
++ tasklist = WNCK_TASKLIST (widget);
++ /* get a screenshot of the background */
++
++ if (tasklist->priv->background != NULL)
++ g_object_unref (tasklist->priv->background);
++
++ tasklist->priv->background = gdk_pixmap_new (widget->window,
++ widget->allocation.width,
++ widget->allocation.height,
++ -1);
++ gc = gdk_gc_new (tasklist->priv->background);
++ gdk_draw_drawable (tasklist->priv->background, gc, widget->window,
++ widget->allocation.x, widget->allocation.y, 0, 0,
++ widget->allocation.width, widget->allocation.height);
++
++ g_object_unref (gc);
++ }
++
++ return (* GTK_WIDGET_CLASS (wnck_tasklist_parent_class)->expose_event) (widget, event);
++}
++
++static void
++wnck_tasklist_forall (GtkContainer *container,
++ gboolean include_internals,
++ GtkCallback callback,
++ gpointer callback_data)
++{
++ WnckTasklist *tasklist;
++ GList *tmp;
++
++ tasklist = WNCK_TASKLIST (container);
++
++ tmp = tasklist->priv->windows;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ tmp = tmp->next;
++
++ (* callback) (task->button, callback_data);
++ }
++
++ tmp = tasklist->priv->class_groups;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ tmp = tmp->next;
++
++ (* callback) (task->button, callback_data);
++ }
++
++ tmp = tasklist->priv->startup_sequences;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ tmp = tmp->next;
++
++ (* callback) (task->button, callback_data);
++ }
++}
++
++static void
++wnck_tasklist_remove (GtkContainer *container,
++ GtkWidget *widget)
++{
++ WnckTasklist *tasklist;
++ GList *tmp;
++
++ g_return_if_fail (WNCK_IS_TASKLIST (container));
++ g_return_if_fail (widget != NULL);
++
++ tasklist = WNCK_TASKLIST (container);
++
++ /* it's safer to handle windows_without_class_group before windows */
++ tmp = tasklist->priv->windows_without_class_group;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ tmp = tmp->next;
++
++ if (task->button == widget)
++ {
++ tasklist->priv->windows_without_class_group =
++ g_list_remove (tasklist->priv->windows_without_class_group,
++ task);
++ g_object_unref (task);
++ break;
++ }
++ }
++
++ tmp = tasklist->priv->windows;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ tmp = tmp->next;
++
++ if (task->button == widget)
++ {
++ g_hash_table_remove (tasklist->priv->win_hash,
++ task->window);
++ tasklist->priv->windows =
++ g_list_remove (tasklist->priv->windows,
++ task);
++
++ gtk_widget_unparent (widget);
++ g_object_unref (task);
++ break;
++ }
++ }
++
++ tmp = tasklist->priv->class_groups;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ tmp = tmp->next;
++
++ if (task->button == widget)
++ {
++ g_hash_table_remove (tasklist->priv->class_group_hash,
++ task->class_group);
++ tasklist->priv->class_groups =
++ g_list_remove (tasklist->priv->class_groups,
++ task);
++
++ gtk_widget_unparent (widget);
++ g_object_unref (task);
++ break;
++ }
++ }
++
++ tmp = tasklist->priv->startup_sequences;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ tmp = tmp->next;
++
++ if (task->button == widget)
++ {
++ tasklist->priv->startup_sequences =
++ g_list_remove (tasklist->priv->startup_sequences,
++ task);
++
++ gtk_widget_unparent (widget);
++ g_object_unref (task);
++ break;
++ }
++ }
++
++ gtk_widget_queue_resize (GTK_WIDGET (container));
++}
++
++static void
++wnck_tasklist_connect_screen (WnckTasklist *tasklist)
++{
++ GList *windows;
++ guint *c;
++ int i;
++ WnckScreen *screen;
++
++ g_return_if_fail (tasklist->priv->screen != NULL);
++
++ screen = tasklist->priv->screen;
++
++ i = 0;
++ c = tasklist->priv->screen_connections;
++
++ c [i++] = g_signal_connect_object (G_OBJECT (screen), "active_window_changed",
++ G_CALLBACK (wnck_tasklist_active_window_changed),
++ tasklist, 0);
++ c [i++] = g_signal_connect_object (G_OBJECT (screen), "active_workspace_changed",
++ G_CALLBACK (wnck_tasklist_active_workspace_changed),
++ tasklist, 0);
++ c [i++] = g_signal_connect_object (G_OBJECT (screen), "window_opened",
++ G_CALLBACK (wnck_tasklist_window_added),
++ tasklist, 0);
++ c [i++] = g_signal_connect_object (G_OBJECT (screen), "window_closed",
++ G_CALLBACK (wnck_tasklist_window_removed),
++ tasklist, 0);
++ c [i++] = g_signal_connect_object (G_OBJECT (screen), "viewports_changed",
++ G_CALLBACK (wnck_tasklist_viewports_changed),
++ tasklist, 0);
++
++
++ g_assert (i == N_SCREEN_CONNECTIONS);
++
++ windows = wnck_screen_get_windows (screen);
++ while (windows != NULL)
++ {
++ wnck_tasklist_connect_window (tasklist, windows->data);
++ windows = windows->next;
++ }
++}
++
++static void
++wnck_tasklist_disconnect_screen (WnckTasklist *tasklist)
++{
++ GList *windows;
++ int i;
++
++ windows = wnck_screen_get_windows (tasklist->priv->screen);
++ while (windows != NULL)
++ {
++ wnck_tasklist_disconnect_window (tasklist, windows->data);
++ windows = windows->next;
++ }
++
++ i = 0;
++ while (i < N_SCREEN_CONNECTIONS)
++ {
++ if (tasklist->priv->screen_connections [i] != 0)
++ g_signal_handler_disconnect (G_OBJECT (tasklist->priv->screen),
++ tasklist->priv->screen_connections [i]);
++
++ tasklist->priv->screen_connections [i] = 0;
++
++ ++i;
++ }
++
++ g_assert (i == N_SCREEN_CONNECTIONS);
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++ if (tasklist->priv->startup_sequence_timeout != 0)
++ {
++ g_source_remove (tasklist->priv->startup_sequence_timeout);
++ tasklist->priv->startup_sequence_timeout = 0;
++ }
++#endif
++}
++
++/**
++ * wnck_tasklist_set_screen:
++ * @tasklist: a #WnckTasklist.
++ * @screen: a #WnckScreen.
++ *
++ * Does nothing.
++ *
++ * Since: 2.2
++ * Deprecated:2.20:
++ */
++void
++wnck_tasklist_set_screen (WnckTasklist *tasklist,
++ WnckScreen *screen)
++{
++}
++
++static gboolean
++wnck_tasklist_scroll_cb (WnckTasklist *tasklist,
++ GdkEventScroll *event,
++ gpointer user_data)
++{
++ /* use the fact that tasklist->priv->windows is sorted
++ * see wnck_tasklist_size_allocate() */
++ GtkTextDirection ltr;
++ GList *window;
++ gint row = 0;
++ gint col = 0;
++
++ window = g_list_find (tasklist->priv->windows,
++ tasklist->priv->active_task);
++ if (window)
++ {
++ row = WNCK_TASK (window->data)->row;
++ col = WNCK_TASK (window->data)->col;
++ }
++ else
++ if (tasklist->priv->activate_timeout_id)
++ /* There is no active_task yet, but there will be one after the timeout.
++ * It occurs if we change the active task too fast. */
++ return TRUE;
++
++ ltr = (gtk_widget_get_direction (GTK_WIDGET (tasklist)) != GTK_TEXT_DIR_RTL);
++
++ switch (event->direction)
++ {
++ case GDK_SCROLL_UP:
++ if (!window)
++ window = g_list_last (tasklist->priv->windows);
++ else
++ window = window->prev;
++ break;
++
++ case GDK_SCROLL_DOWN:
++ if (!window)
++ window = tasklist->priv->windows;
++ else
++ window = window->next;
++ break;
++
++#define TASKLIST_GET_MOST_LEFT(ltr, window, tasklist) \
++ do \
++ { \
++ if (ltr) \
++ window = tasklist->priv->windows; \
++ else \
++ window = g_list_last (tasklist->priv->windows); \
++ } while (0)
++
++#define TASKLIST_GET_MOST_RIGHT(ltr, window, tasklist) \
++ do \
++ { \
++ if (ltr) \
++ window = g_list_last (tasklist->priv->windows); \
++ else \
++ window = tasklist->priv->windows; \
++ } while (0)
++
++ case GDK_SCROLL_LEFT:
++ if (!window)
++ TASKLIST_GET_MOST_RIGHT (ltr, window, tasklist);
++ else
++ {
++ /* Search the first window on the previous colomn at same row */
++ if (ltr)
++ {
++ while (window && (WNCK_TASK(window->data)->row != row ||
++ WNCK_TASK(window->data)->col != col-1))
++ window = window->prev;
++ }
++ else
++ {
++ while (window && (WNCK_TASK(window->data)->row != row ||
++ WNCK_TASK(window->data)->col != col-1))
++ window = window->next;
++ }
++ /* If no window found, select the top/bottom left one */
++ if (!window)
++ TASKLIST_GET_MOST_LEFT (ltr, window, tasklist);
++ }
++ break;
++
++ case GDK_SCROLL_RIGHT:
++ if (!window)
++ TASKLIST_GET_MOST_LEFT (ltr, window, tasklist);
++ else
++ {
++ /* Search the first window on the next colomn at same row */
++ if (ltr)
++ {
++ while (window && (WNCK_TASK(window->data)->row != row ||
++ WNCK_TASK(window->data)->col != col+1))
++ window = window->next;
++ }
++ else
++ {
++ while (window && (WNCK_TASK(window->data)->row != row ||
++ WNCK_TASK(window->data)->col != col+1))
++ window = window->prev;
++ }
++ /* If no window found, select the top/bottom right one */
++ if (!window)
++ TASKLIST_GET_MOST_RIGHT (ltr, window, tasklist);
++ }
++ break;
++
++#undef TASKLIST_GET_MOST_LEFT
++#undef TASKLIST_GET_MOST_RIGHT
++
++ default:
++ g_assert_not_reached ();
++ }
++
++ if (window)
++ wnck_tasklist_activate_task_window (window->data, event->time);
++
++ return TRUE;
++}
++
++/**
++ * wnck_tasklist_new:
++ * @screen: deprecated argument, can be %NULL.
++ *
++ * Creates a new #WnckTasklist. The #WnckTasklist will list #WnckWindow of the
++ * #WnckScreen it is on.
++ *
++ * Return value: a newly created #WnckTasklist.
++ */
++/* TODO: when we break API again, remove the screen from here */
++GtkWidget*
++wnck_tasklist_new (WnckScreen *screen)
++{
++ WnckTasklist *tasklist;
++
++ tasklist = g_object_new (WNCK_TYPE_TASKLIST, NULL);
++
++ return GTK_WIDGET (tasklist);
++}
++
++static void
++wnck_tasklist_free_tasks (WnckTasklist *tasklist)
++{
++ GList *l;
++
++ tasklist->priv->active_task = NULL;
++ tasklist->priv->active_class_group = NULL;
++
++ if (tasklist->priv->windows)
++ {
++ l = tasklist->priv->windows;
++ while (l != NULL)
++ {
++ WnckTask *task = WNCK_TASK (l->data);
++ l = l->next;
++ /* if we just unref the task it means we lose our ref to the
++ * task before we unparent the button, which breaks stuff.
++ */
++ gtk_widget_destroy (task->button);
++ }
++ }
++ g_assert (tasklist->priv->windows == NULL);
++ g_assert (tasklist->priv->windows_without_class_group == NULL);
++ g_assert (g_hash_table_size (tasklist->priv->win_hash) == 0);
++
++ if (tasklist->priv->class_groups)
++ {
++ l = tasklist->priv->class_groups;
++ while (l != NULL)
++ {
++ WnckTask *task = WNCK_TASK (l->data);
++ l = l->next;
++ /* if we just unref the task it means we lose our ref to the
++ * task before we unparent the button, which breaks stuff.
++ */
++ gtk_widget_destroy (task->button);
++ }
++ }
++
++ g_assert (tasklist->priv->class_groups == NULL);
++ g_assert (g_hash_table_size (tasklist->priv->class_group_hash) == 0);
++
++ if (tasklist->priv->skipped_windows)
++ {
++ wnck_tasklist_free_skipped_windows (tasklist);
++ tasklist->priv->skipped_windows = NULL;
++ }
++}
++
++
++/*
++ * This function determines if a window should be included in the tasklist.
++ */
++static gboolean
++tasklist_include_window_impl (WnckTasklist *tasklist,
++ WnckWindow *win,
++ gboolean check_for_skipped_list)
++{
++ WnckWorkspace *active_workspace;
++ int x, y, w, h;
++
++ if (!check_for_skipped_list &&
++ wnck_window_get_state (win) & WNCK_WINDOW_STATE_SKIP_TASKLIST)
++ return FALSE;
++
++ if (tasklist->priv->monitor_num != -1)
++ {
++ wnck_window_get_geometry (win, &x, &y, &w, &h);
++ /* Don't include the window if its center point is not on the same monitor */
++ if (gdk_screen_get_monitor_at_point (_wnck_screen_get_gdk_screen (tasklist->priv->screen),
++ x + w / 2, y + h / 2) != tasklist->priv->monitor_num)
++ return FALSE;
++ }
++
++ /* Remainder of checks aren't relevant for checking if the window should
++ * be in the skipped list.
++ */
++ if (check_for_skipped_list)
++ return TRUE;
++
++ if (tasklist->priv->include_all_workspaces)
++ return TRUE;
++
++ if (wnck_window_is_pinned (win))
++ return TRUE;
++
++ active_workspace = wnck_screen_get_active_workspace (tasklist->priv->screen);
++ if (active_workspace == NULL)
++ return TRUE;
++
++ if (wnck_window_or_transient_needs_attention (win))
++ return TRUE;
++
++ if (active_workspace != wnck_window_get_workspace (win))
++ return FALSE;
++
++ if (!wnck_workspace_is_virtual (active_workspace))
++ return TRUE;
++
++ return wnck_window_is_in_viewport (win, active_workspace);
++}
++
++static gboolean
++tasklist_include_in_skipped_list (WnckTasklist *tasklist, WnckWindow *win)
++{
++ return tasklist_include_window_impl (tasklist,
++ win,
++ TRUE /* check_for_skipped_list */);
++}
++
++static gboolean
++wnck_tasklist_include_window (WnckTasklist *tasklist, WnckWindow *win)
++{
++ return tasklist_include_window_impl (tasklist,
++ win,
++ FALSE /* check_for_skipped_list */);
++}
++
++static void
++wnck_tasklist_update_lists (WnckTasklist *tasklist)
++{
++ GList *windows;
++ WnckWindow *win;
++ WnckClassGroup *class_group;
++ GList *l;
++ WnckTask *win_task;
++ WnckTask *class_group_task;
++
++ wnck_tasklist_free_tasks (tasklist);
++
++ /* wnck_tasklist_update_lists() will be called on realize */
++ if (!GTK_WIDGET_REALIZED (tasklist))
++ return;
++
++ if (GTK_WIDGET (tasklist)->window != NULL)
++ {
++ /*
++ * only show windows from this monitor if there is more than one tasklist running
++ */
++ if (tasklist_instances == NULL || tasklist_instances->next == NULL)
++ {
++ tasklist->priv->monitor_num = -1;
++ }
++ else
++ {
++ int monitor_num;
++
++ monitor_num = gdk_screen_get_monitor_at_window (_wnck_screen_get_gdk_screen (tasklist->priv->screen),
++ GTK_WIDGET (tasklist)->window);
++
++ if (monitor_num != tasklist->priv->monitor_num)
++ {
++ tasklist->priv->monitor_num = monitor_num;
++ gdk_screen_get_monitor_geometry (_wnck_screen_get_gdk_screen (tasklist->priv->screen),
++ tasklist->priv->monitor_num,
++ &tasklist->priv->monitor_geometry);
++ }
++ }
++ }
++
++ l = windows = wnck_screen_get_windows (tasklist->priv->screen);
++ while (l != NULL)
++ {
++ win = WNCK_WINDOW (l->data);
++
++ if (wnck_tasklist_include_window (tasklist, win))
++ {
++ win_task = wnck_task_new_from_window (tasklist, win);
++ tasklist->priv->windows = g_list_prepend (tasklist->priv->windows, win_task);
++ g_hash_table_insert (tasklist->priv->win_hash, win, win_task);
++
++ gtk_widget_set_parent (win_task->button, GTK_WIDGET (tasklist));
++ gtk_widget_show (win_task->button);
++
++ /* Class group */
++
++ class_group = wnck_window_get_class_group (win);
++ /* don't group windows if they do not belong to any class */
++ if (strcmp (wnck_class_group_get_res_class (class_group), "") != 0)
++ {
++ class_group_task =
++ g_hash_table_lookup (tasklist->priv->class_group_hash,
++ class_group);
++
++ if (class_group_task == NULL)
++ {
++ class_group_task =
++ wnck_task_new_from_class_group (tasklist,
++ class_group);
++ gtk_widget_set_parent (class_group_task->button,
++ GTK_WIDGET (tasklist));
++ gtk_widget_show (class_group_task->button);
++
++ tasklist->priv->class_groups =
++ g_list_prepend (tasklist->priv->class_groups,
++ class_group_task);
++ g_hash_table_insert (tasklist->priv->class_group_hash,
++ class_group, class_group_task);
++ }
++
++ class_group_task->windows =
++ g_list_prepend (class_group_task->windows,
++ win_task);
++ }
++ else
++ {
++ g_object_ref (win_task);
++ tasklist->priv->windows_without_class_group =
++ g_list_prepend (tasklist->priv->windows_without_class_group,
++ win_task);
++ }
++ }
++ else if (tasklist_include_in_skipped_list (tasklist, win))
++ {
++ skipped_window *skipped = g_new0 (skipped_window, 1);
++ skipped->window = g_object_ref (win);
++ skipped->tag = g_signal_connect (G_OBJECT (win),
++ "state_changed",
++ G_CALLBACK (wnck_task_state_changed),
++ tasklist);
++ tasklist->priv->skipped_windows =
++ g_list_prepend (tasklist->priv->skipped_windows,
++ (gpointer) skipped);
++ }
++
++ l = l->next;
++ }
++
++ /* Sort the class group list */
++ l = tasklist->priv->class_groups;
++ while (l)
++ {
++ class_group_task = WNCK_TASK (l->data);
++
++ class_group_task->windows = g_list_sort (class_group_task->windows, wnck_task_compare);
++
++ /* so the number of windows in the task gets reset on the
++ * task label
++ */
++ wnck_task_update_visible_state (class_group_task);
++
++ l = l->next;
++ }
++
++ /* since we cleared active_window we need to reset it */
++ wnck_tasklist_active_window_changed (tasklist->priv->screen, NULL, tasklist);
++
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++static void
++wnck_tasklist_change_active_task (WnckTasklist *tasklist, WnckTask *active_task)
++{
++ if (active_task &&
++ active_task == tasklist->priv->active_task)
++ return;
++
++ g_assert (active_task == NULL ||
++ active_task->type != WNCK_TASK_STARTUP_SEQUENCE);
++
++ if (tasklist->priv->active_task)
++ {
++ tasklist->priv->active_task->really_toggling = TRUE;
++ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_task->button),
++ FALSE);
++ tasklist->priv->active_task->really_toggling = FALSE;
++ }
++
++ tasklist->priv->active_task = active_task;
++
++ if (tasklist->priv->active_task)
++ {
++ tasklist->priv->active_task->really_toggling = TRUE;
++ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_task->button),
++ TRUE);
++ tasklist->priv->active_task->really_toggling = FALSE;
++ }
++
++ if (active_task)
++ {
++ active_task = g_hash_table_lookup (tasklist->priv->class_group_hash,
++ active_task->class_group);
++
++ if (active_task &&
++ active_task == tasklist->priv->active_class_group)
++ return;
++
++ if (tasklist->priv->active_class_group)
++ {
++ tasklist->priv->active_class_group->really_toggling = TRUE;
++ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_class_group->button),
++ FALSE);
++ tasklist->priv->active_class_group->really_toggling = FALSE;
++ }
++
++ tasklist->priv->active_class_group = active_task;
++
++ if (tasklist->priv->active_class_group)
++ {
++ tasklist->priv->active_class_group->really_toggling = TRUE;
++ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_class_group->button),
++ TRUE);
++ tasklist->priv->active_class_group->really_toggling = FALSE;
++ }
++ }
++}
++
++static void
++wnck_tasklist_update_icon_geometries (WnckTasklist *tasklist,
++ GList *visible_tasks)
++{
++ gint x, y, width, height;
++ GList *l1;
++
++ for (l1 = visible_tasks; l1; l1 = l1->next) {
++ WnckTask *task = WNCK_TASK (l1->data);
++
++ if (!GTK_WIDGET_REALIZED (task->button))
++ continue;
++
++ gdk_window_get_origin (GTK_BUTTON (task->button)->event_window,
++ &x, &y);
++ width = task->button->allocation.width;
++ height = task->button->allocation.height;
++
++ if (task->window)
++ wnck_window_set_icon_geometry (task->window,
++ x, y, width, height);
++ else {
++ GList *l2;
++
++ for (l2 = task->windows; l2; l2 = l2->next) {
++ WnckTask *win_task = WNCK_TASK (l2->data);
++
++ g_assert (win_task->window);
++
++ wnck_window_set_icon_geometry (win_task->window,
++ x, y, width, height);
++ }
++ }
++ }
++}
++
++static void
++wnck_tasklist_active_window_changed (WnckScreen *screen,
++ WnckWindow *previous_window,
++ WnckTasklist *tasklist)
++{
++ WnckWindow *active_window;
++ WnckWindow *initial_window;
++ WnckTask *active_task = NULL;
++
++ /* FIXME: check for group modal window */
++ initial_window = active_window = wnck_screen_get_active_window (screen);
++ active_task = g_hash_table_lookup (tasklist->priv->win_hash, active_window);
++ while (active_window && !active_task)
++ {
++ active_window = wnck_window_get_transient (active_window);
++ active_task = g_hash_table_lookup (tasklist->priv->win_hash,
++ active_window);
++ /* Check for transient cycles */
++ if (active_window == initial_window)
++ break;
++ }
++
++ wnck_tasklist_change_active_task (tasklist, active_task);
++}
++
++static void
++wnck_tasklist_active_workspace_changed (WnckScreen *screen,
++ WnckWorkspace *previous_workspace,
++ WnckTasklist *tasklist)
++{
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++static void
++wnck_tasklist_window_changed_workspace (WnckWindow *window,
++ WnckTasklist *tasklist)
++{
++ WnckWorkspace *active_ws;
++ WnckWorkspace *window_ws;
++ gboolean need_update;
++ GList *l;
++
++ active_ws = wnck_screen_get_active_workspace (tasklist->priv->screen);
++ window_ws = wnck_window_get_workspace (window);
++
++ if (!window_ws)
++ return;
++
++ need_update = FALSE;
++
++ if (active_ws == window_ws)
++ need_update = TRUE;
++
++ l = tasklist->priv->windows;
++ while (!need_update && l != NULL)
++ {
++ WnckTask *task = l->data;
++
++ if (task->type == WNCK_TASK_WINDOW &&
++ task->window == window)
++ need_update = TRUE;
++
++ l = l->next;
++ }
++
++ if (need_update)
++ {
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++ }
++}
++
++static gboolean
++do_wnck_tasklist_update_lists (gpointer data)
++{
++ WnckTasklist *tasklist = WNCK_TASKLIST (data);
++
++ tasklist->priv->idle_callback_tag = 0;
++
++ wnck_tasklist_update_lists (tasklist);
++
++ return FALSE;
++}
++
++static void
++wnck_tasklist_window_changed_geometry (WnckWindow *window,
++ WnckTasklist *tasklist)
++{
++ WnckTask *win_task;
++ gboolean show;
++ gboolean monitor_changed;
++ int x, y, w, h;
++
++ if (tasklist->priv->idle_callback_tag != 0)
++ return;
++
++ /*
++ * If the (parent of the) tasklist itself skips
++ * the tasklist, we need an extra check whether
++ * the tasklist itself possibly changed monitor.
++ */
++ monitor_changed = FALSE;
++ if (tasklist->priv->monitor_num != -1 &&
++ (wnck_window_get_state (window) & WNCK_WINDOW_STATE_SKIP_TASKLIST) &&
++ GTK_WIDGET (tasklist)->window != NULL)
++ {
++ /* Do the extra check only if there is a suspect of a monitor change (= this window is off monitor) */
++ wnck_window_get_geometry (window, &x, &y, &w, &h);
++ if (!POINT_IN_RECT (x + w / 2, y + h / 2, tasklist->priv->monitor_geometry))
++ {
++ monitor_changed = (gdk_screen_get_monitor_at_window (_wnck_screen_get_gdk_screen (tasklist->priv->screen),
++ GTK_WIDGET (tasklist)->window) != tasklist->priv->monitor_num);
++ }
++ }
++
++ /*
++ * We want to re-generate the task list if
++ * the window is shown but shouldn't be or
++ * the window isn't shown but should be or
++ * the tasklist itself changed monitor.
++ */
++ win_task = g_hash_table_lookup (tasklist->priv->win_hash, window);
++ show = wnck_tasklist_include_window (tasklist, window);
++ if (((win_task == NULL && !show) || (win_task != NULL && show)) &&
++ !monitor_changed)
++ return;
++
++ /* Don't keep any stale references */
++ gtk_widget_queue_draw (GTK_WIDGET (tasklist));
++
++ tasklist->priv->idle_callback_tag = g_idle_add (do_wnck_tasklist_update_lists, tasklist);
++}
++
++static void
++wnck_tasklist_connect_window (WnckTasklist *tasklist,
++ WnckWindow *window)
++{
++ g_signal_connect_object (window, "workspace_changed",
++ G_CALLBACK (wnck_tasklist_window_changed_workspace),
++ tasklist, 0);
++ g_signal_connect_object (window, "geometry_changed",
++ G_CALLBACK (wnck_tasklist_window_changed_geometry),
++ tasklist, 0);
++}
++
++static void
++wnck_tasklist_disconnect_window (WnckTasklist *tasklist,
++ WnckWindow *window)
++{
++ g_signal_handlers_disconnect_by_func (window,
++ wnck_tasklist_window_changed_workspace,
++ tasklist);
++ g_signal_handlers_disconnect_by_func (window,
++ wnck_tasklist_window_changed_geometry,
++ tasklist);
++}
++
++static void
++wnck_tasklist_window_added (WnckScreen *screen,
++ WnckWindow *win,
++ WnckTasklist *tasklist)
++{
++#ifdef HAVE_STARTUP_NOTIFICATION
++ wnck_tasklist_check_end_sequence (tasklist, win);
++#endif
++
++ wnck_tasklist_connect_window (tasklist, win);
++
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++static void
++wnck_tasklist_window_removed (WnckScreen *screen,
++ WnckWindow *win,
++ WnckTasklist *tasklist)
++{
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++static void
++wnck_tasklist_viewports_changed (WnckScreen *screen,
++ WnckTasklist *tasklist)
++{
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++}
++
++static void
++wnck_task_position_menu (GtkMenu *menu,
++ gint *x,
++ gint *y,
++ gboolean *push_in,
++ gpointer user_data)
++{
++ GtkWidget *widget = GTK_WIDGET (user_data);
++ GtkRequisition requisition;
++ gint menu_xpos;
++ gint menu_ypos;
++ gint pointer_x;
++ gint pointer_y;
++
++ gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
++
++ gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
++
++ menu_xpos += widget->allocation.x;
++ menu_ypos += widget->allocation.y;
++
++ if (menu_ypos > gdk_screen_height () / 2)
++ menu_ypos -= requisition.height;
++ else
++ menu_ypos += widget->allocation.height;
++
++ gtk_widget_get_pointer (widget, &pointer_x, &pointer_y);
++ if (requisition.width < pointer_x)
++ menu_xpos += MIN (pointer_x, widget->allocation.width - requisition.width);
++
++ *x = menu_xpos;
++ *y = menu_ypos;
++ *push_in = TRUE;
++}
++
++static gboolean
++wnck_tasklist_change_active_timeout (gpointer data)
++{
++ WnckTasklist *tasklist = WNCK_TASKLIST (data);
++
++ tasklist->priv->activate_timeout_id = 0;
++
++ wnck_tasklist_active_window_changed (tasklist->priv->screen, NULL, tasklist);
++
++ return FALSE;
++}
++
++static void
++wnck_task_menu_activated (GtkMenuItem *menu_item,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++
++ /* This is an "activate" callback function so gtk_get_current_event_time()
++ * will suffice.
++ */
++ wnck_tasklist_activate_task_window (task, gtk_get_current_event_time ());
++}
++
++static void
++wnck_tasklist_activate_next_in_class_group (WnckTask *task,
++ guint32 timestamp)
++{
++ WnckTask *activate_task;
++ gboolean activate_next;
++ GList *l;
++
++ activate_task = NULL;
++ activate_next = FALSE;
++
++ l = task->windows;
++ while (l)
++ {
++ WnckTask *task;
++
++ task = WNCK_TASK (l->data);
++
++ if (wnck_window_is_most_recently_activated (task->window))
++ activate_next = TRUE;
++ else if (activate_next)
++ {
++ activate_task = task;
++ break;
++ }
++
++ l = l->next;
++ }
++
++ /* no task in this group is active, or only the last one => activate
++ * the first task */
++ if (!activate_task && task->windows)
++ activate_task = WNCK_TASK (task->windows->data);
++
++ if (activate_task)
++ {
++ task->was_active = FALSE;
++ wnck_tasklist_activate_task_window (activate_task, timestamp);
++ }
++}
++
++static void
++wnck_tasklist_activate_task_window (WnckTask *task,
++ guint32 timestamp)
++{
++ WnckTasklist *tasklist;
++ WnckWindowState state;
++ WnckWorkspace *active_ws;
++ WnckWorkspace *window_ws;
++
++ tasklist = task->tasklist;
++
++ if (task->window == NULL)
++ return;
++
++ state = wnck_window_get_state (task->window);
++
++ active_ws = wnck_screen_get_active_workspace (tasklist->priv->screen);
++ window_ws = wnck_window_get_workspace (task->window);
++
++ if (state & WNCK_WINDOW_STATE_MINIMIZED)
++ {
++ if (window_ws &&
++ active_ws != window_ws &&
++ !tasklist->priv->switch_workspace_on_unminimize)
++ wnck_workspace_activate (window_ws, timestamp);
++
++ wnck_window_activate_transient (task->window, timestamp);
++ }
++ else
++ {
++ if ((task->was_active ||
++ wnck_window_transient_is_most_recently_activated (task->window)) &&
++ (!window_ws || active_ws == window_ws))
++ {
++ task->was_active = FALSE;
++ wnck_window_minimize (task->window);
++ return;
++ }
++ else
++ {
++ /* FIXME: THIS IS SICK AND WRONG AND BUGGY. See the end of
++ * http://mail.gnome.org/archives/wm-spec-list/2005-July/msg00032.html
++ * There should only be *one* activate call.
++ */
++ if (window_ws)
++ wnck_workspace_activate (window_ws, timestamp);
++
++ wnck_window_activate_transient (task->window, timestamp);
++ }
++ }
++
++
++ if (tasklist->priv->activate_timeout_id)
++ g_source_remove (tasklist->priv->activate_timeout_id);
++
++ tasklist->priv->activate_timeout_id =
++ g_timeout_add (500, &wnck_tasklist_change_active_timeout, tasklist);
++
++ wnck_tasklist_change_active_task (tasklist, task);
++}
++
++static void
++wnck_task_close_all (GtkMenuItem *menu_item,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++ GList *l;
++
++ l = task->windows;
++ while (l)
++ {
++ WnckTask *child = WNCK_TASK (l->data);
++ wnck_window_close (child->window, gtk_get_current_event_time ());
++ l = l->next;
++ }
++}
++
++static void
++wnck_task_unminimize_all (GtkMenuItem *menu_item,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++ GList *l;
++
++ l = task->windows;
++ while (l)
++ {
++ WnckTask *child = WNCK_TASK (l->data);
++ /* This is inside an activate callback, so gtk_get_current_event_time()
++ * will work.
++ */
++ wnck_window_unminimize (child->window, gtk_get_current_event_time ());
++ l = l->next;
++ }
++}
++
++static void
++wnck_task_minimize_all (GtkMenuItem *menu_item,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++ GList *l;
++
++ l = task->windows;
++ while (l)
++ {
++ WnckTask *child = WNCK_TASK (l->data);
++ wnck_window_minimize (child->window);
++ l = l->next;
++ }
++}
++
++static void
++wnck_task_unmaximize_all (GtkMenuItem *menu_item,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++ GList *l;
++
++ l = task->windows;
++ while (l)
++ {
++ WnckTask *child = WNCK_TASK (l->data);
++ wnck_window_unmaximize (child->window);
++ l = l->next;
++ }
++}
++
++static void
++wnck_task_maximize_all (GtkMenuItem *menu_item,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++ GList *l;
++
++ l = task->windows;
++ while (l)
++ {
++ WnckTask *child = WNCK_TASK (l->data);
++ wnck_window_maximize (child->window);
++ l = l->next;
++ }
++}
++
++static void
++wnck_task_popup_menu (WnckTask *task,
++ gboolean action_submenu)
++{
++ GtkWidget *menu;
++ WnckTask *win_task;
++ char *text;
++ GdkPixbuf *pixbuf;
++ GtkWidget *menu_item;
++ GtkWidget *image;
++ GList *l, *list;
++
++ g_return_if_fail (task->type == WNCK_TASK_CLASS_GROUP);
++
++ if (task->class_group == NULL)
++ return;
++
++ if (task->menu == NULL)
++ {
++ task->menu = gtk_menu_new ();
++ g_object_ref_sink (task->menu);
++ }
++
++ menu = task->menu;
++
++ /* Remove old menu content */
++ list = gtk_container_get_children (GTK_CONTAINER (menu));
++ l = list;
++ while (l)
++ {
++ GtkWidget *child = GTK_WIDGET (l->data);
++ gtk_container_remove (GTK_CONTAINER (menu), child);
++ l = l->next;
++ }
++ g_list_free (list);
++
++ l = task->windows;
++ while (l)
++ {
++ win_task = WNCK_TASK (l->data);
++
++ text = wnck_task_get_text (win_task, TRUE, TRUE);
++ menu_item = gtk_image_menu_item_new_with_label (text);
++ if (wnck_task_get_needs_attention (win_task))
++ _make_gtk_label_bold (GTK_LABEL (GTK_BIN (menu_item)->child));
++ g_free (text);
++
++ text = wnck_task_get_text (win_task, FALSE, FALSE);
++ gtk_widget_set_tooltip_text (menu_item, text);
++ g_free (text);
++
++ pixbuf = wnck_task_get_icon (win_task);
++ if (pixbuf)
++ {
++ image = gtk_image_new_from_pixbuf (pixbuf);
++ gtk_widget_show (image);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
++ image);
++ g_object_unref (pixbuf);
++ }
++
++ gtk_widget_show (menu_item);
++
++ if (action_submenu)
++ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item),
++ wnck_create_window_action_menu (win_task->window));
++ else
++ {
++ static const GtkTargetEntry targets[] = {
++ { "application/x-wnck-window-id", 0, 0 }
++ };
++
++ g_signal_connect_object (G_OBJECT (menu_item), "activate",
++ G_CALLBACK (wnck_task_menu_activated),
++ G_OBJECT (win_task),
++ 0);
++
++
++ gtk_drag_source_set (menu_item, GDK_BUTTON1_MASK,
++ targets, 1, GDK_ACTION_MOVE);
++ g_signal_connect_object (G_OBJECT(menu_item), "drag_begin",
++ G_CALLBACK (wnck_task_drag_begin),
++ G_OBJECT (win_task),
++ 0);
++ g_signal_connect_object (G_OBJECT(menu_item), "drag_data_get",
++ G_CALLBACK (wnck_task_drag_data_get),
++ G_OBJECT (win_task),
++ 0);
++ }
++
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
++
++ l = l->next;
++ }
++
++ /* In case of Right click, show Minimize All, Unminimize All, Close All*/
++ if (action_submenu)
++ {
++ GtkWidget *separator;
++ GtkWidget *image;
++
++ separator = gtk_separator_menu_item_new ();
++ gtk_widget_show (separator);
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
++
++ menu_item = gtk_image_menu_item_new_with_mnemonic (_("Mi_nimize All"));
++ image = gtk_image_new_from_stock (WNCK_STOCK_MINIMIZE, GTK_ICON_SIZE_MENU);
++ gtk_widget_show (image);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), image);
++ gtk_widget_show (menu_item);
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
++ g_signal_connect_object (G_OBJECT (menu_item), "activate",
++ G_CALLBACK (wnck_task_minimize_all),
++ G_OBJECT (task),
++ 0);
++
++ menu_item = gtk_image_menu_item_new_with_mnemonic (_("Un_minimize All"));
++ gtk_widget_show (menu_item);
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
++ g_signal_connect_object (G_OBJECT (menu_item), "activate",
++ G_CALLBACK (wnck_task_unminimize_all),
++ G_OBJECT (task),
++ 0);
++
++ menu_item = gtk_image_menu_item_new_with_mnemonic (_("Ma_ximize All"));
++ image = gtk_image_new_from_stock (WNCK_STOCK_MAXIMIZE, GTK_ICON_SIZE_MENU);
++ gtk_widget_show (image);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), image);
++ gtk_widget_show (menu_item);
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
++ g_signal_connect_object (G_OBJECT (menu_item), "activate",
++ G_CALLBACK (wnck_task_maximize_all),
++ G_OBJECT (task),
++ 0);
++
++ menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Unmaximize All"));
++ gtk_widget_show (menu_item);
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
++ g_signal_connect_object (G_OBJECT (menu_item), "activate",
++ G_CALLBACK (wnck_task_unmaximize_all),
++ G_OBJECT (task),
++ 0);
++
++ separator = gtk_separator_menu_item_new ();
++ gtk_widget_show (separator);
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
++
++ menu_item = gtk_image_menu_item_new_with_mnemonic(_("_Close All"));
++ image = gtk_image_new_from_stock (WNCK_STOCK_DELETE, GTK_ICON_SIZE_MENU);
++ gtk_widget_show (image);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), image);
++ gtk_widget_show (menu_item);
++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
++ g_signal_connect_object (G_OBJECT (menu_item), "activate",
++ G_CALLBACK (wnck_task_close_all),
++ G_OBJECT (task),
++ 0);
++ }
++
++ gtk_menu_set_screen (GTK_MENU (menu),
++ _wnck_screen_get_gdk_screen (task->tasklist->priv->screen));
++
++ gtk_widget_show (menu);
++ gtk_menu_popup (GTK_MENU (menu),
++ NULL, NULL,
++ wnck_task_position_menu, task->button,
++ 1, gtk_get_current_event_time ());
++}
++
++static void
++wnck_task_button_toggled (GtkButton *button,
++ WnckTask *task)
++{
++ /* Did we really want to change the state of the togglebutton? */
++ if (task->really_toggling)
++ return;
++
++ /* Undo the toggle */
++ task->really_toggling = TRUE;
++ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
++ !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
++ task->really_toggling = FALSE;
++
++ switch (task->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ wnck_task_popup_menu (task, FALSE);
++ break;
++ case WNCK_TASK_WINDOW:
++ if (task->window == NULL)
++ return;
++
++ /* This should only be called by clicking on the task button, so
++ * gtk_get_current_event_time() should be fine here...
++ */
++ wnck_tasklist_activate_task_window (task, gtk_get_current_event_time ());
++ break;
++ case WNCK_TASK_STARTUP_SEQUENCE:
++ break;
++ }
++}
++
++static char *
++wnck_task_get_text (WnckTask *task,
++ gboolean icon_text,
++ gboolean include_state)
++{
++ const char *name;
++
++ switch (task->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ name = wnck_class_group_get_name (task->class_group);
++ if (name[0] != 0)
++ return g_strdup_printf ("%s (%d)",
++ name,
++ g_list_length (task->windows));
++ else
++ return g_strdup_printf ("(%d)",
++ g_list_length (task->windows));
++
++ case WNCK_TASK_WINDOW:
++ return _wnck_window_get_name_for_display (task->window,
++ icon_text, include_state);
++ break;
++
++ case WNCK_TASK_STARTUP_SEQUENCE:
++#ifdef HAVE_STARTUP_NOTIFICATION
++ name = sn_startup_sequence_get_description (task->startup_sequence);
++ if (name == NULL)
++ name = sn_startup_sequence_get_name (task->startup_sequence);
++ if (name == NULL)
++ name = sn_startup_sequence_get_binary_name (task->startup_sequence);
++
++ return g_strdup (name);
++#else
++ return NULL;
++#endif
++ break;
++ }
++
++ return NULL;
++}
++
++static void
++wnck_dimm_icon (GdkPixbuf *pixbuf)
++{
++ int x, y, pixel_stride, row_stride;
++ guchar *row, *pixels;
++ int w, h;
++
++ g_assert (pixbuf != NULL);
++
++ w = gdk_pixbuf_get_width (pixbuf);
++ h = gdk_pixbuf_get_height (pixbuf);
++
++ g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
++
++ pixel_stride = 4;
++
++ row = gdk_pixbuf_get_pixels (pixbuf);
++ row_stride = gdk_pixbuf_get_rowstride (pixbuf);
++
++ for (y = 0; y < h; y++)
++ {
++ pixels = row;
++
++ for (x = 0; x < w; x++)
++ {
++ pixels[3] /= 2;
++
++ pixels += pixel_stride;
++ }
++
++ row += row_stride;
++ }
++}
++
++static GdkPixbuf *
++wnck_task_scale_icon (GdkPixbuf *orig, gboolean minimized)
++{
++ int w, h;
++ GdkPixbuf *pixbuf;
++
++ if (!orig)
++ return NULL;
++
++ w = gdk_pixbuf_get_width (orig);
++ h = gdk_pixbuf_get_height (orig);
++
++ if (h != MINI_ICON_SIZE ||
++ !gdk_pixbuf_get_has_alpha (orig))
++ {
++ double scale;
++
++ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
++ TRUE,
++ 8,
++ MINI_ICON_SIZE * w / (double) h,
++ MINI_ICON_SIZE);
++
++ scale = MINI_ICON_SIZE / (double) gdk_pixbuf_get_height (orig);
++
++ gdk_pixbuf_scale (orig,
++ pixbuf,
++ 0, 0,
++ gdk_pixbuf_get_width (pixbuf),
++ gdk_pixbuf_get_height (pixbuf),
++ 0, 0,
++ scale, scale,
++ GDK_INTERP_HYPER);
++ }
++ else
++ pixbuf = orig;
++
++ if (minimized)
++ {
++ if (orig == pixbuf)
++ pixbuf = gdk_pixbuf_copy (orig);
++
++ wnck_dimm_icon (pixbuf);
++ }
++
++ if (orig == pixbuf)
++ g_object_ref (pixbuf);
++
++ return pixbuf;
++}
++
++
++static GdkPixbuf *
++wnck_task_get_icon (WnckTask *task)
++{
++ WnckWindowState state;
++ GdkPixbuf *pixbuf;
++
++ pixbuf = NULL;
++
++ switch (task->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ pixbuf = wnck_task_scale_icon (wnck_class_group_get_mini_icon (task->class_group),
++ FALSE);
++ break;
++
++ case WNCK_TASK_WINDOW:
++ state = wnck_window_get_state (task->window);
++
++ pixbuf = wnck_task_scale_icon (wnck_window_get_mini_icon (task->window),
++ state & WNCK_WINDOW_STATE_MINIMIZED);
++ break;
++ case WNCK_TASK_STARTUP_SEQUENCE:
++#ifdef HAVE_STARTUP_NOTIFICATION
++ if (task->tasklist->priv->icon_loader != NULL)
++ {
++ const char *icon;
++
++ icon = sn_startup_sequence_get_icon_name (task->startup_sequence);
++ if (icon != NULL)
++ {
++ GdkPixbuf *loaded;
++
++ loaded = (* task->tasklist->priv->icon_loader) (icon,
++ MINI_ICON_SIZE,
++ 0,
++ task->tasklist->priv->icon_loader_data);
++
++ if (loaded != NULL)
++ {
++ pixbuf = wnck_task_scale_icon (loaded, FALSE);
++ g_object_unref (G_OBJECT (loaded));
++ }
++ }
++ }
++
++ if (pixbuf == NULL)
++ {
++ _wnck_get_fallback_icons (NULL, 0, 0,
++ &pixbuf, MINI_ICON_SIZE, MINI_ICON_SIZE);
++ }
++#endif
++ break;
++ }
++
++ return pixbuf;
++}
++
++static gboolean
++wnck_task_get_needs_attention (WnckTask *task)
++{
++ GList *l;
++ WnckTask *win_task;
++ gboolean needs_attention;
++
++ needs_attention = FALSE;
++
++ switch (task->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ task->start_needs_attention = 0;
++ l = task->windows;
++ while (l)
++ {
++ win_task = WNCK_TASK (l->data);
++
++ if (wnck_window_or_transient_needs_attention (win_task->window))
++ {
++ needs_attention = TRUE;
++ task->start_needs_attention = MAX (task->start_needs_attention, _wnck_window_or_transient_get_needs_attention_time (win_task->window));
++ break;
++ }
++
++ l = l->next;
++ }
++ break;
++
++ case WNCK_TASK_WINDOW:
++ needs_attention =
++ wnck_window_or_transient_needs_attention (task->window);
++ task->start_needs_attention = _wnck_window_or_transient_get_needs_attention_time (task->window);
++ break;
++
++ case WNCK_TASK_STARTUP_SEQUENCE:
++ break;
++ }
++
++ return needs_attention != FALSE;
++}
++
++static void
++wnck_task_update_visible_state (WnckTask *task)
++{
++ GdkPixbuf *pixbuf;
++ char *text;
++
++ pixbuf = wnck_task_get_icon (task);
++ gtk_image_set_from_pixbuf (GTK_IMAGE (task->image),
++ pixbuf);
++ if (pixbuf)
++ g_object_unref (pixbuf);
++
++ text = wnck_task_get_text (task, TRUE, TRUE);
++ if (text != NULL)
++ {
++ gtk_label_set_text (GTK_LABEL (task->label), text);
++ if (wnck_task_get_needs_attention (task))
++ {
++ _make_gtk_label_bold ((GTK_LABEL (task->label)));
++ wnck_task_queue_glow (task);
++ }
++ else
++ {
++ _make_gtk_label_normal ((GTK_LABEL (task->label)));
++ wnck_task_stop_glow (task);
++ }
++ g_free (text);
++ }
++
++ text = wnck_task_get_text (task, FALSE, FALSE);
++ /* if text is NULL, this unsets the tooltip, which is probably what we'd want
++ * to do */
++ gtk_widget_set_tooltip_text (task->button, text);
++ g_free (text);
++
++ gtk_widget_queue_resize (GTK_WIDGET (task->tasklist));
++}
++
++static void
++wnck_task_state_changed (WnckWindow *window,
++ WnckWindowState changed_mask,
++ WnckWindowState new_state,
++ gpointer data)
++{
++ WnckTasklist *tasklist = WNCK_TASKLIST (data);
++
++ if (changed_mask & WNCK_WINDOW_STATE_SKIP_TASKLIST)
++ {
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++ return;
++ }
++
++ if ((changed_mask & WNCK_WINDOW_STATE_DEMANDS_ATTENTION) ||
++ (changed_mask & WNCK_WINDOW_STATE_URGENT))
++ {
++ WnckWorkspace *active_workspace =
++ wnck_screen_get_active_workspace (tasklist->priv->screen);
++
++ if (active_workspace &&
++ active_workspace != wnck_window_get_workspace (window))
++ {
++ wnck_tasklist_update_lists (tasklist);
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++ }
++ }
++
++ if ((changed_mask & WNCK_WINDOW_STATE_MINIMIZED) ||
++ (changed_mask & WNCK_WINDOW_STATE_DEMANDS_ATTENTION) ||
++ (changed_mask & WNCK_WINDOW_STATE_URGENT))
++ {
++ WnckTask *win_task = NULL;
++
++ /* FIXME: Handle group modal dialogs */
++ for (; window && !win_task; window = wnck_window_get_transient (window))
++ win_task = g_hash_table_lookup (tasklist->priv->win_hash, window);
++
++ if (win_task)
++ {
++ WnckTask *class_group_task;
++
++ wnck_task_update_visible_state (win_task);
++
++ class_group_task =
++ g_hash_table_lookup (tasklist->priv->class_group_hash,
++ win_task->class_group);
++
++ if (class_group_task)
++ wnck_task_update_visible_state (class_group_task);
++ }
++ }
++
++}
++
++static void
++wnck_task_icon_changed (WnckWindow *window,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++
++ if (task)
++ wnck_task_update_visible_state (task);
++}
++
++static void
++wnck_task_name_changed (WnckWindow *window,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++
++ if (task)
++ wnck_task_update_visible_state (task);
++}
++
++static void
++wnck_task_class_name_changed (WnckClassGroup *class_group,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++
++ if (task)
++ wnck_task_update_visible_state (task);
++}
++
++static void
++wnck_task_class_icon_changed (WnckClassGroup *class_group,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++
++ if (task)
++ wnck_task_update_visible_state (task);
++}
++
++static gboolean
++wnck_task_motion_timeout (gpointer data)
++{
++ WnckWorkspace *ws;
++ WnckTask *task = WNCK_TASK (data);
++
++ task->button_activate = 0;
++
++ /* FIXME: THIS IS SICK AND WRONG AND BUGGY. See the end of
++ * http://mail.gnome.org/archives/wm-spec-list/2005-July/msg00032.html
++ * There should only be *one* activate call.
++ */
++ ws = wnck_window_get_workspace (task->window);
++ if (ws && ws != wnck_screen_get_active_workspace (wnck_screen_get_default ()))
++ {
++ wnck_workspace_activate (ws, task->dnd_timestamp);
++ }
++ wnck_window_activate_transient (task->window, task->dnd_timestamp);
++
++ task->dnd_timestamp = 0;
++
++ return FALSE;
++}
++
++static void
++wnck_task_drag_leave (GtkWidget *widget,
++ GdkDragContext *context,
++ guint time,
++ WnckTask *task)
++{
++ if (task->button_activate != 0)
++ {
++ g_source_remove (task->button_activate);
++ task->button_activate = 0;
++ }
++
++ gtk_drag_unhighlight (widget);
++}
++
++static gboolean
++wnck_task_drag_motion (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ guint time,
++ WnckTask *task)
++{
++ if (gtk_drag_dest_find_target (widget, context, NULL))
++ {
++ gtk_drag_highlight (widget);
++ gdk_drag_status (context, context->suggested_action, time);
++ }
++ else
++ {
++ task->dnd_timestamp = time;
++
++ if (task->button_activate == 0 && task->type == WNCK_TASK_WINDOW)
++ task->button_activate = g_timeout_add (WNCK_ACTIVATE_TIMEOUT,
++ wnck_task_motion_timeout,
++ task);
++ gdk_drag_status (context, 0, time);
++ }
++ return TRUE;
++}
++
++static void
++wnck_task_drag_begin (GtkWidget *widget,
++ GdkDragContext *context,
++ WnckTask *task)
++{
++ _wnck_window_set_as_drag_icon (task->window, context,
++ GTK_WIDGET (task->tasklist));
++}
++
++static void
++wnck_task_drag_data_get (GtkWidget *widget,
++ GdkDragContext *context,
++ GtkSelectionData *selection_data,
++ guint info,
++ guint time,
++ WnckTask *task)
++{
++ gulong xid;
++
++ xid = wnck_window_get_xid (task->window);
++ gtk_selection_data_set (selection_data,
++ selection_data->target,
++ 8, (guchar *)&xid, sizeof (gulong));
++}
++
++static void
++wnck_task_drag_data_received (GtkWidget *widget,
++ GdkDragContext *context,
++ gint x,
++ gint y,
++ GtkSelectionData *data,
++ guint info,
++ guint time,
++ WnckTask *target_task)
++{
++ WnckTasklist *tasklist;
++ GList *l, *windows;
++ WnckWindow *window;
++ gulong *xid;
++ guint new_order, old_order, order;
++ WnckWindow *found_window;
++
++ if ((data->length != sizeof (gulong)) || (data->format != 8))
++ {
++ gtk_drag_finish (context, FALSE, FALSE, time);
++ return;
++ }
++
++ tasklist = target_task->tasklist;
++ xid = (gulong *)data->data;
++ found_window = NULL;
++ windows = wnck_screen_get_windows (tasklist->priv->screen);
++
++ for (l = windows; l; l = l->next)
++ {
++ window = WNCK_WINDOW (l->data);
++ if (wnck_window_get_xid (window) == *xid)
++ {
++ old_order = wnck_window_get_sort_order (window);
++ new_order = wnck_window_get_sort_order (target_task->window);
++ if (old_order < new_order)
++ new_order++;
++ found_window = window;
++ break;
++ }
++ }
++
++ if (found_window)
++ {
++ for (l = windows; l; l = l->next)
++ {
++ window = WNCK_WINDOW (l->data);
++ order = wnck_window_get_sort_order (window);
++ if (order >= new_order)
++ wnck_window_set_sort_order (window, order + 1);
++ }
++ wnck_window_set_sort_order (found_window, new_order);
++
++ if (!tasklist->priv->include_all_workspaces)
++ {
++ WnckWorkspace *active_space;
++ active_space = wnck_screen_get_active_workspace (tasklist->priv->screen);
++ wnck_window_move_to_workspace (found_window, active_space);
++ }
++
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++ }
++
++ gtk_drag_finish (context, TRUE, FALSE, time);
++}
++
++static gboolean
++wnck_task_button_press_event (GtkWidget *widget,
++ GdkEventButton *event,
++ gpointer data)
++{
++ WnckTask *task = WNCK_TASK (data);
++
++ switch (task->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ if (event->button == 2)
++ wnck_tasklist_activate_next_in_class_group (task, event->time);
++ else
++ wnck_task_popup_menu (task,
++ event->button == 3);
++ return TRUE;
++
++ case WNCK_TASK_WINDOW:
++ if (event->button == 1)
++ {
++ /* is_most_recently_activated == is_active for click &
++ * sloppy focus methods. We use the former here because
++ * 'mouse' focus provides a special case. In that case, no
++ * window will be active, but if a window was the most
++ * recently active one (i.e. user moves mouse straight from
++ * window to tasklist), then we should still minimize it.
++ */
++ if (wnck_window_is_most_recently_activated (task->window))
++ task->was_active = TRUE;
++ else
++ task->was_active = FALSE;
++
++ return FALSE;
++ }
++ else if (event->button == 3)
++ {
++ if (task->action_menu)
++ gtk_widget_destroy (task->action_menu);
++
++ g_assert (task->action_menu == NULL);
++
++ task->action_menu = wnck_create_window_action_menu (task->window);
++
++ g_object_add_weak_pointer (G_OBJECT (task->action_menu),
++ (void**) &task->action_menu);
++
++ gtk_menu_set_screen (GTK_MENU (task->action_menu),
++ _wnck_screen_get_gdk_screen (task->tasklist->priv->screen));
++
++ gtk_widget_show (task->action_menu);
++ gtk_menu_popup (GTK_MENU (task->action_menu),
++ NULL, NULL,
++ wnck_task_position_menu, task->button,
++ event->button,
++ gtk_get_current_event_time ());
++
++ g_signal_connect (task->action_menu, "selection-done",
++ G_CALLBACK (gtk_widget_destroy), NULL);
++
++ return TRUE;
++ }
++ break;
++ case WNCK_TASK_STARTUP_SEQUENCE:
++ break;
++ }
++
++ return FALSE;
++}
++
++static gboolean
++wnck_task_expose (GtkWidget *widget,
++ GdkEventExpose *event,
++ gpointer data);
++
++static void
++wnck_task_create_widgets (WnckTask *task, GtkReliefStyle relief)
++{
++ GtkWidget *hbox;
++ GdkPixbuf *pixbuf;
++ char *text;
++ static GQuark disable_sound_quark = 0;
++ static const GtkTargetEntry targets[] = {
++ { "application/x-wnck-window-id", 0, 0 }
++ };
++
++ if (!disable_sound_quark)
++ disable_sound_quark = g_quark_from_static_string ("gnome_disable_sound_events");
++
++ if (task->type == WNCK_TASK_STARTUP_SEQUENCE)
++ task->button = gtk_button_new ();
++ else
++ task->button = gtk_toggle_button_new ();
++
++ gtk_button_set_relief (GTK_BUTTON (task->button), relief);
++
++ task->button_activate = 0;
++ g_object_set_qdata (G_OBJECT (task->button),
++ disable_sound_quark, GINT_TO_POINTER (TRUE));
++ g_object_add_weak_pointer (G_OBJECT (task->button),
++ (void**) &task->button);
++
++ gtk_widget_set_name (task->button,
++ "tasklist-button");
++
++ if (task->type == WNCK_TASK_WINDOW)
++ {
++ gtk_drag_source_set (GTK_WIDGET (task->button),
++ GDK_BUTTON1_MASK,
++ targets, 1,
++ GDK_ACTION_MOVE);
++ gtk_drag_dest_set (GTK_WIDGET (task->button), GTK_DEST_DEFAULT_DROP,
++ targets, 1, GDK_ACTION_MOVE);
++ }
++ else
++ gtk_drag_dest_set (GTK_WIDGET (task->button), 0,
++ NULL, 0, GDK_ACTION_DEFAULT);
++
++ hbox = gtk_hbox_new (FALSE, 0);
++
++ pixbuf = wnck_task_get_icon (task);
++ if (pixbuf)
++ {
++ task->image = gtk_image_new_from_pixbuf (pixbuf);
++ g_object_unref (pixbuf);
++ }
++ else
++ task->image = gtk_image_new ();
++
++ gtk_widget_show (task->image);
++
++ text = wnck_task_get_text (task, TRUE, TRUE);
++ task->label = gtk_label_new (text);
++ gtk_misc_set_alignment (GTK_MISC (task->label), 0.0, 0.5);
++ gtk_label_set_ellipsize (GTK_LABEL (task->label),
++ PANGO_ELLIPSIZE_END);
++
++ if (wnck_task_get_needs_attention (task))
++ {
++ _make_gtk_label_bold ((GTK_LABEL (task->label)));
++ wnck_task_queue_glow (task);
++ }
++
++ gtk_widget_show (task->label);
++
++ gtk_box_pack_start (GTK_BOX (hbox), task->image, FALSE, FALSE,
++ TASKLIST_BUTTON_PADDING);
++ gtk_box_pack_start (GTK_BOX (hbox), task->label, TRUE, TRUE,
++ TASKLIST_BUTTON_PADDING);
++
++ gtk_container_add (GTK_CONTAINER (task->button), hbox);
++ gtk_widget_show (hbox);
++ g_free (text);
++
++ text = wnck_task_get_text (task, FALSE, FALSE);
++ gtk_widget_set_tooltip_text (task->button, text);
++ g_free (text);
++
++ /* Set up signals */
++ if (GTK_IS_TOGGLE_BUTTON (task->button))
++ g_signal_connect_object (G_OBJECT (task->button), "toggled",
++ G_CALLBACK (wnck_task_button_toggled),
++ G_OBJECT (task),
++ 0);
++
++ g_signal_connect_object (G_OBJECT (task->button), "size_allocate",
++ G_CALLBACK (wnck_task_size_allocated),
++ G_OBJECT (task),
++ 0);
++
++ g_signal_connect_object (G_OBJECT (task->button), "button_press_event",
++ G_CALLBACK (wnck_task_button_press_event),
++ G_OBJECT (task),
++ 0);
++
++ g_signal_connect_object (G_OBJECT(task->button), "drag_motion",
++ G_CALLBACK (wnck_task_drag_motion),
++ G_OBJECT (task),
++ 0);
++
++ if (task->type == WNCK_TASK_WINDOW)
++ {
++ g_signal_connect_object (G_OBJECT (task->button), "drag_data_get",
++ G_CALLBACK (wnck_task_drag_data_get),
++ G_OBJECT (task),
++ 0);
++
++ g_signal_connect_object (G_OBJECT (task->button), "drag_data_received",
++ G_CALLBACK (wnck_task_drag_data_received),
++ G_OBJECT (task),
++ 0);
++
++ }
++
++ g_signal_connect_object (G_OBJECT(task->button), "drag_leave",
++ G_CALLBACK (wnck_task_drag_leave),
++ G_OBJECT (task),
++ 0);
++
++ if (task->type == WNCK_TASK_WINDOW) {
++ g_signal_connect_object (G_OBJECT(task->button), "drag_data_get",
++ G_CALLBACK (wnck_task_drag_data_get),
++ G_OBJECT (task),
++ 0);
++
++ g_signal_connect_object (G_OBJECT(task->button), "drag_begin",
++ G_CALLBACK (wnck_task_drag_begin),
++ G_OBJECT (task),
++ 0);
++ }
++
++ switch (task->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ task->class_name_changed_tag = g_signal_connect (G_OBJECT (task->class_group), "name_changed",
++ G_CALLBACK (wnck_task_class_name_changed), task);
++ task->class_icon_changed_tag = g_signal_connect (G_OBJECT (task->class_group), "icon_changed",
++ G_CALLBACK (wnck_task_class_icon_changed), task);
++ break;
++
++ case WNCK_TASK_WINDOW:
++ task->state_changed_tag = g_signal_connect (G_OBJECT (task->window), "state_changed",
++ G_CALLBACK (wnck_task_state_changed), task->tasklist);
++ task->icon_changed_tag = g_signal_connect (G_OBJECT (task->window), "icon_changed",
++ G_CALLBACK (wnck_task_icon_changed), task);
++ task->name_changed_tag = g_signal_connect (G_OBJECT (task->window), "name_changed",
++ G_CALLBACK (wnck_task_name_changed), task);
++ break;
++
++ case WNCK_TASK_STARTUP_SEQUENCE:
++ break;
++
++ default:
++ g_assert_not_reached ();
++ }
++
++ g_signal_connect_object (task->button, "expose_event",
++ G_CALLBACK (wnck_task_expose),
++ G_OBJECT (task),
++ G_CONNECT_AFTER);
++}
++
++static void
++fake_expose_widget (GtkWidget *widget,
++ GdkPixmap *pixmap,
++ gint x,
++ gint y)
++{
++ GdkWindow *tmp_window;
++ GdkEventExpose event;
++
++ event.type = GDK_EXPOSE;
++ event.window = pixmap;
++ event.send_event = FALSE;
++ event.region = NULL;
++ event.count = 0;
++
++ tmp_window = widget->window;
++ widget->window = pixmap;
++ widget->allocation.x += x;
++ widget->allocation.y += y;
++
++ event.area = widget->allocation;
++
++ gtk_widget_send_expose (widget, (GdkEvent *) &event);
++
++ widget->window = tmp_window;
++ widget->allocation.x -= x;
++ widget->allocation.y -= y;
++}
++
++static GdkPixmap *
++take_screenshot (WnckTask *task)
++{
++ WnckTasklist *tasklist;
++ GtkWidget *tasklist_widget;
++ GdkPixmap *pixmap;
++ gint width, height;
++ gboolean overlay_rect;
++
++ width = task->button->allocation.width;
++ height = task->button->allocation.height;
++
++ pixmap = gdk_pixmap_new (task->button->window, width, height, -1);
++
++ tasklist = WNCK_TASKLIST (task->tasklist);
++ tasklist_widget = GTK_WIDGET (task->tasklist);
++
++ /* first draw the button */
++ gtk_widget_style_get (tasklist_widget, "fade-overlay-rect", &overlay_rect, NULL);
++ if (overlay_rect)
++ {
++ /* Draw a rectangle with bg[SELECTED] */
++ gdk_draw_rectangle (pixmap, task->button->style->bg_gc[GTK_STATE_SELECTED],
++ TRUE, 0, 0, width + 1, height + 1);
++ }
++ else
++ {
++ GtkStyle *style;
++ GtkStyle *attached_style;
++
++ /* copy the style to change its colors around. */
++ style = gtk_style_copy (task->button->style);
++ style->bg[task->button->state] = style->bg[GTK_STATE_SELECTED];
++ /* Now attach it to the window */
++ attached_style = gtk_style_attach (style, pixmap);
++ g_object_ref (attached_style);
++
++ /* copy the background */
++ gdk_draw_drawable (pixmap, attached_style->bg_gc[GTK_STATE_NORMAL],
++ tasklist->priv->background,
++ task->button->allocation.x, task->button->allocation.y,
++ 0, 0, width, height);
++
++ /* draw the button with our modified style instead of the real one. */
++ gtk_paint_box (attached_style, (GdkWindow*) pixmap, task->button->state,
++ GTK_SHADOW_OUT, NULL, task->button, "button",
++ 0, 0, width, height);
++
++ g_object_unref (style);
++ gtk_style_detach (attached_style);
++ g_object_unref (attached_style);
++ }
++
++ /* then the image and label */
++ fake_expose_widget (task->image, pixmap,
++ -task->button->allocation.x, -task->button->allocation.y);
++ fake_expose_widget (task->label, pixmap,
++ -task->button->allocation.x, -task->button->allocation.y);
++
++ return pixmap;
++}
++
++static GdkPixmap *
++copy_pixmap (GtkWidget *widget)
++{
++ GdkPixmap *pixmap;
++
++ pixmap = gdk_pixmap_new (widget->window,
++ widget->allocation.width,
++ widget->allocation.height, -1);
++
++ gdk_draw_drawable (pixmap,
++ widget->style->bg_gc[GTK_STATE_NORMAL],
++ widget->window,
++ widget->allocation.x, widget->allocation.y,
++ 0, 0,
++ widget->allocation.width, widget->allocation.height);
++
++ return pixmap;
++}
++
++static gboolean
++wnck_task_expose (GtkWidget *widget,
++ GdkEventExpose *event,
++ gpointer data)
++{
++ GtkStyle *style;
++ GdkGC *lgc, *dgc;
++ int x, y;
++ WnckTask *task;
++
++ task = WNCK_TASK (data);
++
++ cleanup_screenshots (task);
++
++ switch (task->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ style = widget->style;
++ lgc = style->light_gc[GTK_STATE_NORMAL];
++ dgc = style->dark_gc[GTK_STATE_NORMAL];
++
++ x = widget->allocation.x + widget->allocation.width -
++ (GTK_CONTAINER (widget)->border_width + style->ythickness + 12);
++ y = widget->allocation.y + widget->allocation.height / 2 - 5;
++
++ gtk_paint_tab (widget->style, widget->window,
++ task->tasklist->priv->active_class_group == task ?
++ GTK_STATE_ACTIVE : GTK_STATE_NORMAL,
++ GTK_SHADOW_NONE, NULL, widget, NULL, x, y, 10, 10);
++
++ /* Fall through to get screenshot
++ */
++ case WNCK_TASK_WINDOW:
++ if ((event->area.x <= widget->allocation.x) &&
++ (event->area.y <= widget->allocation.y) &&
++ (event->area.width >= widget->allocation.width) &&
++ (event->area.height >= widget->allocation.height))
++ {
++ if (task->start_needs_attention)
++ {
++ task->screenshot = copy_pixmap (widget);
++ task->screenshot_faded = take_screenshot (task);
++
++ wnck_task_button_glow (task);
++ }
++ }
++
++ case WNCK_TASK_STARTUP_SEQUENCE:
++ break;
++ }
++
++ return FALSE;
++}
++
++static gint
++wnck_task_compare_alphabetically (gconstpointer a,
++ gconstpointer b)
++{
++ char *text1;
++ char *text2;
++ gint result;
++
++ text1 = wnck_task_get_text (WNCK_TASK (a), TRUE, FALSE);
++ text2 = wnck_task_get_text (WNCK_TASK (b), TRUE, FALSE);
++
++ result= g_utf8_collate (text1, text2);
++
++ g_free (text1);
++ g_free (text2);
++
++ return result;
++}
++
++static gint
++compare_class_group_tasks (WnckTask *task1, WnckTask *task2)
++{
++ const char *name1, *name2;
++
++ name1 = wnck_class_group_get_name (task1->class_group);
++ name2 = wnck_class_group_get_name (task2->class_group);
++
++ return g_utf8_collate (name1, name2);
++}
++
++static gint
++wnck_task_compare (gconstpointer a,
++ gconstpointer b)
++{
++ WnckTask *task1 = WNCK_TASK (a);
++ WnckTask *task2 = WNCK_TASK (b);
++ gint pos1, pos2;
++
++ pos1 = pos2 = 0; /* silence the compiler */
++
++ switch (task1->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ if (task2->type == WNCK_TASK_CLASS_GROUP)
++ return compare_class_group_tasks (task1, task2);
++ else
++ return -1; /* Sort groups before everything else */
++
++ case WNCK_TASK_WINDOW:
++ pos1 = wnck_window_get_sort_order (task1->window);
++ break;
++ case WNCK_TASK_STARTUP_SEQUENCE:
++ pos1 = G_MAXINT; /* startup sequences are sorted at the end. */
++ break; /* Changing this will break scrolling. */
++ }
++
++ switch (task2->type)
++ {
++ case WNCK_TASK_CLASS_GROUP:
++ if (task1->type == WNCK_TASK_CLASS_GROUP)
++ return compare_class_group_tasks (task1, task2);
++ else
++ return 1; /* Sort groups before everything else */
++
++ case WNCK_TASK_WINDOW:
++ pos2 = wnck_window_get_sort_order (task2->window);
++ break;
++ case WNCK_TASK_STARTUP_SEQUENCE:
++ pos2 = G_MAXINT;
++ break;
++ }
++
++ if (pos1 < pos2)
++ return -1;
++ else if (pos1 > pos2)
++ return 1;
++ else
++ return 0; /* should only happen if there's multiple processes being
++ * started, and then who cares about sort order... */
++}
++
++static void
++remove_startup_sequences_for_window (WnckTasklist *tasklist,
++ WnckWindow *window)
++{
++#ifdef HAVE_STARTUP_NOTIFICATION
++ const char *win_id;
++ GList *tmp;
++
++ win_id = _wnck_window_get_startup_id (window);
++ if (win_id == NULL)
++ return;
++
++ tmp = tasklist->priv->startup_sequences;
++ while (tmp != NULL)
++ {
++ WnckTask *task = tmp->data;
++ GList *next = tmp->next;
++ const char *task_id;
++
++ g_assert (task->type == WNCK_TASK_STARTUP_SEQUENCE);
++
++ task_id = sn_startup_sequence_get_id (task->startup_sequence);
++
++ if (task_id && strcmp (task_id, win_id) == 0)
++ gtk_widget_destroy (task->button);
++
++ tmp = next;
++ }
++#else
++ ; /* nothing */
++#endif
++}
++
++static WnckTask *
++wnck_task_new_from_window (WnckTasklist *tasklist,
++ WnckWindow *window)
++{
++ WnckTask *task;
++
++ task = g_object_new (WNCK_TYPE_TASK, NULL);
++
++ task->type = WNCK_TASK_WINDOW;
++ task->window = g_object_ref (window);
++ task->class_group = g_object_ref (wnck_window_get_class_group (window));
++ task->tasklist = tasklist;
++
++ wnck_task_create_widgets (task, tasklist->priv->relief);
++
++ remove_startup_sequences_for_window (tasklist, window);
++
++ return task;
++}
++
++static WnckTask *
++wnck_task_new_from_class_group (WnckTasklist *tasklist,
++ WnckClassGroup *class_group)
++{
++ WnckTask *task;
++
++ task = g_object_new (WNCK_TYPE_TASK, NULL);
++
++ task->type = WNCK_TASK_CLASS_GROUP;
++ task->window = NULL;
++ task->class_group = g_object_ref (class_group);
++ task->tasklist = tasklist;
++
++ wnck_task_create_widgets (task, tasklist->priv->relief);
++
++ return task;
++}
++
++#ifdef HAVE_STARTUP_NOTIFICATION
++static WnckTask*
++wnck_task_new_from_startup_sequence (WnckTasklist *tasklist,
++ SnStartupSequence *sequence)
++{
++ WnckTask *task;
++
++ task = g_object_new (WNCK_TYPE_TASK, NULL);
++
++ task->type = WNCK_TASK_STARTUP_SEQUENCE;
++ task->window = NULL;
++ task->class_group = NULL;
++ task->startup_sequence = sequence;
++ sn_startup_sequence_ref (task->startup_sequence);
++ task->tasklist = tasklist;
++
++ wnck_task_create_widgets (task, tasklist->priv->relief);
++
++ return task;
++}
++
++/* This should be fairly long, as it should never be required unless
++ * apps or .desktop files are buggy, and it's confusing if
++ * OpenOffice or whatever seems to stop launching - people
++ * might decide they need to launch it again.
++ */
++#define STARTUP_TIMEOUT 15000
++
++static gboolean
++sequence_timeout_callback (void *user_data)
++{
++ WnckTasklist *tasklist = user_data;
++ GList *tmp;
++ GTimeVal now;
++ long tv_sec, tv_usec;
++ double elapsed;
++
++ g_get_current_time (&now);
++
++ restart:
++ tmp = tasklist->priv->startup_sequences;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++
++ sn_startup_sequence_get_last_active_time (task->startup_sequence,
++ &tv_sec, &tv_usec);
++
++ elapsed =
++ ((((double)now.tv_sec - tv_sec) * G_USEC_PER_SEC +
++ (now.tv_usec - tv_usec))) / 1000.0;
++
++ if (elapsed > STARTUP_TIMEOUT)
++ {
++ g_assert (task->button != NULL);
++ /* removes task from list as a side effect */
++ gtk_widget_destroy (task->button);
++
++ goto restart; /* don't iterate over changed list, just restart;
++ * not efficient but who cares here.
++ */
++ }
++
++ tmp = tmp->next;
++ }
++
++ if (tasklist->priv->startup_sequences == NULL)
++ {
++ tasklist->priv->startup_sequence_timeout = 0;
++ return FALSE;
++ }
++ else
++ return TRUE;
++}
++
++static void
++wnck_tasklist_sn_event (SnMonitorEvent *event,
++ void *user_data)
++{
++ WnckTasklist *tasklist;
++
++ tasklist = WNCK_TASKLIST (user_data);
++
++ switch (sn_monitor_event_get_type (event))
++ {
++ case SN_MONITOR_EVENT_INITIATED:
++ {
++ WnckTask *task;
++
++ task = wnck_task_new_from_startup_sequence (tasklist,
++ sn_monitor_event_get_startup_sequence (event));
++
++ gtk_widget_set_parent (task->button, GTK_WIDGET (tasklist));
++ gtk_widget_show (task->button);
++
++ tasklist->priv->startup_sequences =
++ g_list_prepend (tasklist->priv->startup_sequences,
++ task);
++
++ if (tasklist->priv->startup_sequence_timeout == 0)
++ {
++ tasklist->priv->startup_sequence_timeout =
++ g_timeout_add_seconds (1, sequence_timeout_callback,
++ tasklist);
++ }
++
++ gtk_widget_queue_resize (GTK_WIDGET (tasklist));
++ }
++ break;
++
++ case SN_MONITOR_EVENT_COMPLETED:
++ {
++ GList *tmp;
++ tmp = tasklist->priv->startup_sequences;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++
++ if (task->startup_sequence ==
++ sn_monitor_event_get_startup_sequence (event))
++ {
++ g_assert (task->button != NULL);
++ /* removes task from list as a side effect */
++ gtk_widget_destroy (task->button);
++ break;
++ }
++
++ tmp = tmp->next;
++ }
++ }
++ break;
++
++ case SN_MONITOR_EVENT_CHANGED:
++ break;
++
++ case SN_MONITOR_EVENT_CANCELED:
++ break;
++ }
++
++ if (tasklist->priv->startup_sequences == NULL &&
++ tasklist->priv->startup_sequence_timeout != 0)
++ {
++ g_source_remove (tasklist->priv->startup_sequence_timeout);
++ tasklist->priv->startup_sequence_timeout = 0;
++ }
++}
++
++static void
++wnck_tasklist_check_end_sequence (WnckTasklist *tasklist,
++ WnckWindow *window)
++{
++ const char *res_class;
++ const char *res_name;
++ GList *tmp;
++
++ if (tasklist->priv->startup_sequences == NULL)
++ return;
++
++ res_class = _wnck_window_get_resource_class (window);
++ res_name = _wnck_window_get_resource_name (window);
++
++ if (res_class == NULL && res_name == NULL)
++ return;
++
++ tmp = tasklist->priv->startup_sequences;
++ while (tmp != NULL)
++ {
++ WnckTask *task = WNCK_TASK (tmp->data);
++ const char *wmclass;
++
++ wmclass = sn_startup_sequence_get_wmclass (task->startup_sequence);
++
++ if (wmclass != NULL &&
++ ((res_class && strcmp (res_class, wmclass) == 0) ||
++ (res_name && strcmp (res_name, wmclass) == 0)))
++ {
++ sn_startup_sequence_complete (task->startup_sequence);
++
++ g_assert (task->button != NULL);
++ /* removes task from list as a side effect */
++ gtk_widget_destroy (task->button);
++
++ /* only match one */
++ return;
++ }
++
++ tmp = tmp->next;
++ }
++}
++
++#endif /* HAVE_STARTUP_NOTIFICATION */
+diff -urN libwnck.orig/libwnck/trusted-tooltips.c libwnck.new/libwnck/trusted-tooltips.c
+--- libwnck.orig/libwnck/trusted-tooltips.c 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/trusted-tooltips.c 2007-11-30 14:02:34.607973000 +0000
@@ -0,0 +1,916 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
@@ -1587,9 +11557,9 @@
+}
+#endif /* HAVE_XTSOL */
+#define __TRUSTED_TOOLTIPS_C__
-diff -Nrup libwnck-2.19.4/libwnck/trusted-tooltips.h ../libwnck-2.19.4/libwnck/trusted-tooltips.h
---- libwnck-2.19.4/libwnck/trusted-tooltips.h 1970-01-01 01:00:00.000000000 +0100
-+++ ../libwnck-2.19.4/libwnck/trusted-tooltips.h 2007-06-27 15:08:01.838448000 +0200
+diff -urN libwnck.orig/libwnck/trusted-tooltips.h libwnck.new/libwnck/trusted-tooltips.h
+--- libwnck.orig/libwnck/trusted-tooltips.h 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/trusted-tooltips.h 2007-11-30 14:02:34.615836000 +0000
@@ -0,0 +1,120 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
@@ -1711,9 +11681,9 @@
+G_END_DECLS
+
+#endif /* __TRUSTED_TOOLTIPS_H__ */
-diff -Nrup libwnck-2.19.4/libwnck/tsol-pics.h ../libwnck-2.19.4/libwnck/tsol-pics.h
---- libwnck-2.19.4/libwnck/tsol-pics.h 1970-01-01 01:00:00.000000000 +0100
-+++ ../libwnck-2.19.4/libwnck/tsol-pics.h 2007-06-27 15:08:01.838626000 +0200
+diff -urN libwnck.orig/libwnck/tsol-pics.h libwnck.new/libwnck/tsol-pics.h
+--- libwnck.orig/libwnck/tsol-pics.h 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/tsol-pics.h 2007-11-30 14:02:34.616257000 +0000
@@ -0,0 +1,43 @@
+/* GdkPixbuf RGBA C-Source image dump */
+
@@ -1758,10 +11728,10 @@
+ "^^^\377^^^\377^^^\377^^^\365^^^\336^^^\303^^^\246^^^\207^^^[^^^3^^^\26"
+ "^^^\5^^^\2"};
+
-diff -Nrup libwnck-2.19.5/libwnck/window.c ../libwnck-2.19.5/libwnck/window.c
---- libwnck-2.19.5/libwnck/window.c 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.5/libwnck/window.c 2007-06-27 15:16:59.872286000 +0200
-@@ -44,6 +44,13 @@
+diff -urN libwnck.orig/libwnck/window.c libwnck.new/libwnck/window.c
+--- libwnck.orig/libwnck/window.c 2007-11-30 14:02:04.934138000 +0000
++++ libwnck.new/libwnck/window.c 2007-11-30 14:02:34.618857000 +0000
+@@ -47,6 +47,13 @@
* referenced or unreferenced.
*/
@@ -1775,7 +11745,7 @@
#define FALLBACK_NAME _("Untitled window")
#define ALL_WORKSPACES (0xFFFFFFFF)
-@@ -79,6 +86,12 @@ struct _WnckWindowPrivate
+@@ -83,6 +90,12 @@
char *icon_name;
char *session_id;
char *session_id_utf8;
@@ -1788,7 +11758,7 @@
int pid;
int workspace;
gint sort_order;
-@@ -96,6 +109,9 @@ struct _WnckWindowPrivate
+@@ -100,6 +113,9 @@
int y;
int width;
int height;
@@ -1798,7 +11768,7 @@
int left_frame;
int right_frame;
-@@ -154,6 +170,9 @@ struct _WnckWindowPrivate
+@@ -161,6 +177,9 @@
guint need_emit_name_changed : 1;
guint need_emit_icon_changed : 1;
@@ -1808,7 +11778,7 @@
};
G_DEFINE_TYPE (WnckWindow, wnck_window, G_TYPE_OBJECT);
-@@ -241,6 +241,9 @@
+@@ -223,6 +242,9 @@
window->priv->group_leader = None;
window->priv->transient_for = None;
window->priv->icon_geometry.width = -1; /* invalid cached value */
@@ -1818,7 +11788,7 @@
window->priv->name = NULL;
window->priv->icon_name = NULL;
window->priv->session_id = NULL;
-@@ -464,6 +467,12 @@
+@@ -446,6 +468,12 @@
_wnck_icon_cache_free (window->priv->icon_cache);
window->priv->icon_cache = NULL;
@@ -1831,7 +11801,7 @@
g_free (window->priv->startup_id);
window->priv->startup_id = NULL;
g_free (window->priv->res_class);
-@@ -462,11 +489,40 @@ _wnck_window_create (Window xwindow
+@@ -559,11 +587,40 @@
window->priv->need_update_frame_extents = TRUE;
window->priv->need_emit_name_changed = FALSE;
window->priv->need_emit_icon_changed = FALSE;
@@ -1872,7 +11842,7 @@
void
_wnck_window_destroy (WnckWindow *window)
{
-@@ -758,6 +767,92 @@
+@@ -711,6 +768,92 @@
return g_strdup (name);
}
@@ -1965,7 +11935,7 @@
/**
* wnck_window_get_application:
-@@ -1560,9 +1703,44 @@ void
+@@ -1777,9 +1920,44 @@
wnck_window_move_to_workspace (WnckWindow *window,
WnckWorkspace *space)
{
@@ -2010,7 +11980,7 @@
_wnck_change_workspace (WNCK_SCREEN_XSCREEN (window->priv->screen),
window->priv->xwindow,
wnck_workspace_get_number (space));
-@@ -2159,9 +2337,38 @@ gboolean
+@@ -2382,9 +2560,38 @@
wnck_window_is_on_workspace (WnckWindow *window,
WnckWorkspace *workspace)
{
@@ -2049,7 +12019,7 @@
return wnck_window_is_pinned (window) ||
wnck_window_get_workspace (window) == workspace;
}
-@@ -2188,8 +2395,12 @@ wnck_window_is_in_viewport (WnckWindow
+@@ -2411,8 +2618,12 @@
g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
g_return_val_if_fail (WNCK_IS_WORKSPACE (workspace), FALSE);
@@ -2062,7 +12032,7 @@
if (wnck_window_get_workspace (window) != workspace)
return FALSE;
-@@ -2549,6 +2760,91 @@ update_icon_name (WnckWindow *window)
+@@ -2787,6 +2998,91 @@
window->priv->icon_name = new_name;
}
@@ -2154,7 +12124,7 @@
static void
update_workspace (WnckWindow *window)
{
-@@ -2899,6 +3195,14 @@ force_update_now (WnckWindow *window)
+@@ -3144,6 +3440,14 @@
if (window->priv->need_emit_name_changed)
emit_name_changed (window);
@@ -2169,10 +12139,3274 @@
old_state = COMPRESS_STATE (window);
old_actions = window->priv->actions;
-diff -Nrup libwnck-2.19.4/libwnck/window.h ../libwnck-2.19.4/libwnck/window.h
---- libwnck-2.19.4/libwnck/window.h 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.4/libwnck/window.h 2007-06-27 15:08:01.842594000 +0200
-@@ -30,6 +30,7 @@
+diff -urN libwnck.orig/libwnck/window.c.orig libwnck.new/libwnck/window.c.orig
+--- libwnck.orig/libwnck/window.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/window.c.orig 2007-11-30 14:02:08.628079000 +0000
+@@ -0,0 +1,3260 @@
++/* window object */
++/* vim: set sw=2 et: */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2003 Kim Woelders
++ * Copyright (C) 2003 Red Hat, Inc.
++ * Copyright (C) 2006-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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 <glib/gi18n-lib.h>
++#include <string.h>
++#include <time.h>
++
++#include "window.h"
++#include "class-group.h"
++#include "util.h"
++#include "xutils.h"
++#include "private.h"
++#include "wnck-enum-types.h"
++#include "wnck-marshal.h"
++
++/**
++ * SECTION:window
++ * @short_description: an object representing a window.
++ * @see_also: #WnckWorkspace, #WnckApplication, #WnckClassGroup
++ * @stability: Unstable
++ *
++ * The #WnckWindow objects are always owned by libwnck and must not be
++ * referenced or unreferenced.
++ */
++
++#define FALLBACK_NAME _("Untitled window")
++#define ALL_WORKSPACES (0xFFFFFFFF)
++
++static GHashTable *window_hash = NULL;
++
++/* Keep 0-7 in sync with the numbers in the WindowState enum. Yeah I'm
++ * a loser.
++ */
++#define COMPRESS_STATE(window) \
++ ( ((window)->priv->is_minimized << 0) | \
++ ((window)->priv->is_maximized_horz << 1) | \
++ ((window)->priv->is_maximized_vert << 2) | \
++ ((window)->priv->is_shaded << 3) | \
++ ((window)->priv->skip_pager << 4) | \
++ ((window)->priv->skip_taskbar << 5) | \
++ ((window)->priv->is_sticky << 6) | \
++ ((window)->priv->is_hidden << 7) | \
++ ((window)->priv->is_fullscreen << 8) | \
++ ((window)->priv->demands_attention << 9) | \
++ ((window)->priv->is_urgent << 10)| \
++ ((window)->priv->is_above << 11)| \
++ ((window)->priv->is_below << 12))
++
++struct _WnckWindowPrivate
++{
++ Window xwindow;
++ WnckScreen *screen;
++ WnckApplication *app;
++ WnckClassGroup *class_group;
++ Window group_leader;
++ Window transient_for;
++ GdkRectangle icon_geometry;
++ char *name;
++ char *icon_name;
++ char *session_id;
++ char *session_id_utf8;
++ int pid;
++ int workspace;
++ gint sort_order;
++
++ WnckWindowType wintype;
++
++ GdkPixbuf *icon;
++ GdkPixbuf *mini_icon;
++
++ WnckIconCache *icon_cache;
++
++ WnckWindowActions actions;
++
++ int x;
++ int y;
++ int width;
++ int height;
++
++ int left_frame;
++ int right_frame;
++ int top_frame;
++ int bottom_frame;
++
++ char *startup_id;
++
++ char *res_class;
++ char *res_name;
++
++ /* true if transient_for points to root window,
++ * not another app window
++ */
++ guint transient_for_root : 1;
++
++ /* window state */
++ guint is_minimized : 1;
++ guint is_maximized_horz : 1;
++ guint is_maximized_vert : 1;
++ guint is_shaded : 1;
++ guint is_above : 1;
++ guint is_below : 1;
++ guint skip_pager : 1;
++ guint skip_taskbar : 1;
++ guint is_sticky : 1;
++ guint is_hidden : 1;
++ guint is_fullscreen : 1;
++ guint demands_attention : 1;
++ guint is_urgent : 1;
++
++ time_t needs_attention_time;
++
++ /* _NET_WM_STATE_HIDDEN doesn't map directly into an
++ * externally-visible state (it determines the WM_STATE
++ * interpretation)
++ */
++ guint net_wm_state_hidden : 1;
++ guint wm_state_iconic : 1;
++
++ /* idle handler for updates */
++ guint update_handler;
++
++ /* if you add flags, be sure to set them
++ * when we create the window so we get an initial update
++ */
++ guint need_update_name : 1;
++ guint need_update_state : 1;
++ guint need_update_wm_state : 1;
++ guint need_update_icon_name : 1;
++ guint need_update_workspace : 1;
++ guint need_update_actions : 1;
++ guint need_update_wintype : 1;
++ guint need_update_transient_for : 1;
++ guint need_update_startup_id : 1;
++ guint need_update_wmclass : 1;
++ guint need_update_wmhints : 1;
++ guint need_update_frame_extents : 1;
++
++ guint need_emit_name_changed : 1;
++ guint need_emit_icon_changed : 1;
++};
++
++G_DEFINE_TYPE (WnckWindow, wnck_window, G_TYPE_OBJECT);
++#define WNCK_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WNCK_TYPE_WINDOW, WnckWindowPrivate))
++
++enum {
++ NAME_CHANGED,
++ STATE_CHANGED,
++ WORKSPACE_CHANGED,
++ ICON_CHANGED,
++ ACTIONS_CHANGED,
++ GEOMETRY_CHANGED,
++ LAST_SIGNAL
++};
++
++static void wnck_window_init (WnckWindow *window);
++static void wnck_window_class_init (WnckWindowClass *klass);
++static void wnck_window_finalize (GObject *object);
++
++static void emit_name_changed (WnckWindow *window);
++static void emit_state_changed (WnckWindow *window,
++ WnckWindowState changed_mask,
++ WnckWindowState new_state);
++static void emit_workspace_changed (WnckWindow *window);
++static void emit_icon_changed (WnckWindow *window);
++static void emit_actions_changed (WnckWindow *window,
++ WnckWindowActions changed_mask,
++ WnckWindowActions new_actions);
++static void emit_geometry_changed (WnckWindow *window);
++
++static void update_name (WnckWindow *window);
++static void update_state (WnckWindow *window);
++static void update_wm_state (WnckWindow *window);
++static void update_icon_name (WnckWindow *window);
++static void update_workspace (WnckWindow *window);
++static void update_actions (WnckWindow *window);
++static void update_wintype (WnckWindow *window);
++static void update_transient_for (WnckWindow *window);
++static void update_startup_id (WnckWindow *window);
++static void update_wmclass (WnckWindow *window);
++static void update_frame_extents (WnckWindow *window);
++static void unqueue_update (WnckWindow *window);
++static void queue_update (WnckWindow *window);
++static void force_update_now (WnckWindow *window);
++
++static WnckWindow* find_last_transient_for (GList *windows,
++ Window xwindow);
++
++static guint signals[LAST_SIGNAL] = { 0 };
++
++static void
++wnck_window_init (WnckWindow *window)
++{
++ window->priv = WNCK_WINDOW_GET_PRIVATE (window);
++
++ window->priv->xwindow = None;
++ window->priv->name = NULL;
++ window->priv->app = NULL;
++ window->priv->class_group = NULL;
++ window->priv->group_leader = None;
++ window->priv->transient_for = None;
++ window->priv->icon_geometry.width = -1; /* invalid cached value */
++ window->priv->name = NULL;
++ window->priv->icon_name = NULL;
++ window->priv->session_id = NULL;
++ window->priv->session_id_utf8 = NULL;
++ window->priv->pid = 0;
++ window->priv->workspace = -1;
++ window->priv->sort_order = G_MAXINT;
++
++ /* FIXME: should we have an invalid window type for this? */
++ window->priv->wintype = 0;
++
++ window->priv->icon = NULL;
++ window->priv->mini_icon = NULL;
++
++ window->priv->icon_cache = _wnck_icon_cache_new ();
++
++ window->priv->actions = 0;
++
++ window->priv->x = 0;
++ window->priv->y = 0;
++ window->priv->width = 0;
++ window->priv->height = 0;
++
++ window->priv->left_frame = 0;
++ window->priv->right_frame = 0;
++ window->priv->top_frame = 0;
++ window->priv->bottom_frame = 0;
++
++ window->priv->startup_id = NULL;
++
++ window->priv->res_class = NULL;
++ window->priv->res_name = NULL;
++
++ window->priv->transient_for_root = FALSE;
++
++ window->priv->is_minimized = FALSE;
++ window->priv->is_maximized_horz = FALSE;
++ window->priv->is_maximized_vert = FALSE;
++ window->priv->is_shaded = FALSE;
++ window->priv->is_above = FALSE;
++ window->priv->is_below = FALSE;
++ window->priv->skip_pager = FALSE;
++ window->priv->skip_taskbar = FALSE;
++ window->priv->is_sticky = FALSE;
++ window->priv->is_hidden = FALSE;
++ window->priv->is_fullscreen = FALSE;
++ window->priv->demands_attention = FALSE;
++ window->priv->is_urgent = FALSE;
++
++ window->priv->needs_attention_time = 0;
++
++ window->priv->net_wm_state_hidden = FALSE;
++ window->priv->wm_state_iconic = FALSE;
++
++ window->priv->update_handler = 0;
++
++ window->priv->need_update_name = FALSE;
++ window->priv->need_update_state = FALSE;
++ window->priv->need_update_wm_state = FALSE;
++ window->priv->need_update_icon_name = FALSE;
++ window->priv->need_update_workspace = FALSE;
++ window->priv->need_update_actions = FALSE;
++ window->priv->need_update_wintype = FALSE;
++ window->priv->need_update_transient_for = FALSE;
++ window->priv->need_update_startup_id = FALSE;
++ window->priv->need_update_wmclass = FALSE;
++ window->priv->need_update_wmhints = FALSE;
++ window->priv->need_update_frame_extents = FALSE;
++
++ window->priv->need_emit_name_changed = FALSE;
++ window->priv->need_emit_icon_changed = FALSE;
++}
++
++static void
++wnck_window_class_init (WnckWindowClass *klass)
++{
++ GObjectClass *object_class = G_OBJECT_CLASS (klass);
++
++ g_type_class_add_private (klass, sizeof (WnckWindowPrivate));
++
++ object_class->finalize = wnck_window_finalize;
++
++ /**
++ * WnckWindow::name-changed:
++ * @window: the #WnckWindow which emitted the signal.
++ *
++ * Emitted when the name of @window changes.
++ */
++ signals[NAME_CHANGED] =
++ g_signal_new ("name_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckWindowClass, name_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++
++ /**
++ * WnckWindow::state-changed:
++ * @window: the #WnckWindow which emitted the signal.
++ * @changed_mask: the bitmask containing bits set for all states of @window
++ * that have changed.
++ * @new_state: the new state of @window.
++ *
++ * Emitted when the state of @window changes. This can happen when @window is
++ * (un)minimized, (un)maximized, (un)sticked, (un)shaded, (un)made above,
++ * (un)made below, (un)set fullscreen, when it needs attention, etc. See
++ * #WnckWindowState for the complete list of states that might have changed.
++ */
++ signals[STATE_CHANGED] =
++ g_signal_new ("state_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckWindowClass, state_changed),
++ NULL, NULL,
++ _wnck_marshal_VOID__FLAGS_FLAGS,
++ G_TYPE_NONE, 2,
++ WNCK_TYPE_WINDOW_STATE, WNCK_TYPE_WINDOW_STATE);
++
++ /**
++ * WnckWindow::workspace-changed:
++ * @window: the #WnckWindow which emitted the signal.
++ *
++ * Emitted when the current workspace of @window changes, or if @window has
++ * been pinned or unpinned.
++ */
++ signals[WORKSPACE_CHANGED] =
++ g_signal_new ("workspace_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckWindowClass, workspace_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++
++ /**
++ * WnckWindow::icon-changed:
++ * @window: the #WnckWindow which emitted the signal.
++ *
++ * Emitted when the icon of @window changes.
++ */
++ signals[ICON_CHANGED] =
++ g_signal_new ("icon_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckWindowClass, icon_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++
++ /**
++ * WnckWindow::actions-changed:
++ * @window: the #WnckWindow which emitted the signal.
++ * @changed_mask: the bitmask containing bits set for all actions
++ * availabilities for @window that have changed.
++ * @new_state: the new actions availabilities for @window.
++ *
++ * Emitted when the actions availabilities for @window change.
++ */
++ signals[ACTIONS_CHANGED] =
++ g_signal_new ("actions_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckWindowClass, actions_changed),
++ NULL, NULL,
++ _wnck_marshal_VOID__FLAGS_FLAGS,
++ G_TYPE_NONE, 2,
++ WNCK_TYPE_WINDOW_ACTIONS,
++ WNCK_TYPE_WINDOW_ACTIONS);
++
++ /**
++ * WnckWindow::geometry-changed:
++ * @window: the #WnckWindow which emitted the signal.
++ *
++ * Emitted when the geometry of @window changes.
++ */
++ signals[GEOMETRY_CHANGED] =
++ g_signal_new ("geometry_changed",
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ G_STRUCT_OFFSET (WnckWindowClass, geometry_changed),
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
++}
++
++static void
++wnck_window_finalize (GObject *object)
++{
++ WnckWindow *window;
++
++ window = WNCK_WINDOW (object);
++
++ unqueue_update (window);
++
++ if (window->priv->app)
++ g_object_unref (G_OBJECT (window->priv->app));
++ window->priv->app = NULL;
++
++ if (window->priv->class_group)
++ g_object_unref (G_OBJECT (window->priv->class_group));
++ window->priv->class_group = NULL;
++
++ g_free (window->priv->name);
++ window->priv->name = NULL;
++ g_free (window->priv->icon_name);
++ window->priv->icon_name = NULL;
++ g_free (window->priv->session_id);
++ window->priv->session_id = NULL;
++ g_free (window->priv->session_id_utf8);
++ window->priv->session_id_utf8 = NULL;
++
++ if (window->priv->icon)
++ g_object_unref (G_OBJECT (window->priv->icon));
++ window->priv->icon = NULL;
++
++ if (window->priv->mini_icon)
++ g_object_unref (G_OBJECT (window->priv->mini_icon));
++ window->priv->mini_icon = NULL;
++
++ _wnck_icon_cache_free (window->priv->icon_cache);
++ window->priv->icon_cache = NULL;
++
++ g_free (window->priv->startup_id);
++ window->priv->startup_id = NULL;
++ g_free (window->priv->res_class);
++ window->priv->res_class = NULL;
++ g_free (window->priv->res_name);
++ window->priv->res_name = NULL;
++
++ G_OBJECT_CLASS (wnck_window_parent_class)->finalize (object);
++}
++
++/**
++ * wnck_window_get:
++ * @xwindow: an X window ID.
++ *
++ * Returns a preexisting #WnckWindow for the X window @xwindow. This will not
++ * create a #WnckWindow if none exists. The function is robust against bogus
++ * window IDs.
++ *
++ * Return value: the #WnckWindow for @xwindow. The returned #WnckWindow is
++ * owned by libwnck and must not be referenced or unreferenced.
++ **/
++WnckWindow*
++wnck_window_get (gulong xwindow)
++{
++ if (window_hash == NULL)
++ return NULL;
++ else
++ return g_hash_table_lookup (window_hash, &xwindow);
++}
++
++/**
++ * wnck_window_get_screen:
++ * @window: a #WnckWindow.
++ *
++ * Returns the #WnckScreen @window is on.
++ *
++ * Return value: the #WnckScreen @window is on. The returned #WnckScreen is
++ * owned by libwnck and must not be referenced or unreferenced.
++ **/
++WnckScreen*
++wnck_window_get_screen (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ return window->priv->screen;
++}
++
++WnckWindow*
++_wnck_window_create (Window xwindow,
++ WnckScreen *screen,
++ gint sort_order)
++{
++ WnckWindow *window;
++
++ if (window_hash == NULL)
++ window_hash = g_hash_table_new (_wnck_xid_hash, _wnck_xid_equal);
++
++ g_return_val_if_fail (g_hash_table_lookup (window_hash, &xwindow) == NULL,
++ NULL);
++
++ window = g_object_new (WNCK_TYPE_WINDOW, NULL);
++ window->priv->xwindow = xwindow;
++ window->priv->screen = screen;
++
++ g_hash_table_insert (window_hash, &window->priv->xwindow, window);
++
++ /* Hash now owns one ref, caller gets none */
++
++ /* Note that xwindow may correspond to a WnckApplication's xwindow,
++ * that's why we select the union of the mask we want for Application
++ * and the one we want for window
++ */
++ _wnck_select_input (window->priv->xwindow,
++ WNCK_APP_WINDOW_EVENT_MASK);
++
++ /* Default the group leader to the window itself; it is set in
++ * update_wmhints() if a different group leader is specified.
++ */
++ window->priv->group_leader = window->priv->xwindow;
++
++ window->priv->session_id =
++ _wnck_get_session_id (window->priv->xwindow);
++
++ window->priv->pid =
++ _wnck_get_pid (window->priv->xwindow);
++
++ window->priv->x = 0;
++ window->priv->y = 0;
++ window->priv->width = 0;
++ window->priv->height = 0;
++ _wnck_get_window_geometry (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ xwindow,
++ &window->priv->x,
++ &window->priv->y,
++ &window->priv->width,
++ &window->priv->height);
++
++ window->priv->sort_order = sort_order;
++
++ window->priv->need_update_name = TRUE;
++ window->priv->need_update_state = TRUE;
++ window->priv->need_update_icon_name = TRUE;
++ window->priv->need_update_wm_state = TRUE;
++ window->priv->need_update_workspace = TRUE;
++ window->priv->need_update_actions = TRUE;
++ window->priv->need_update_wintype = TRUE;
++ window->priv->need_update_transient_for = TRUE;
++ window->priv->need_update_startup_id = TRUE;
++ window->priv->need_update_wmclass = TRUE;
++ window->priv->need_update_wmhints = TRUE;
++ window->priv->need_update_frame_extents = TRUE;
++ window->priv->need_emit_name_changed = FALSE;
++ window->priv->need_emit_icon_changed = FALSE;
++ force_update_now (window);
++
++ return window;
++}
++
++void
++_wnck_window_destroy (WnckWindow *window)
++{
++ g_return_if_fail (wnck_window_get (window->priv->xwindow) == window);
++
++ g_hash_table_remove (window_hash, &window->priv->xwindow);
++
++ g_return_if_fail (wnck_window_get (window->priv->xwindow) == NULL);
++
++ window->priv->xwindow = None;
++
++ /* remove hash's ref on the window */
++ g_object_unref (G_OBJECT (window));
++}
++
++/**
++ * wnck_window_has_name:
++ * @window: a #WnckWindow.
++ *
++ * Checks whether or not @window has a name. wnck_window_get_name()
++ * will always return some value, even if @window has no name set;
++ * wnck_window_has_name() can be used to tell if that name is
++ * real or not.
++ *
++ * For icons titles, use wnck_window_has_icon_name() instead.
++ *
++ * Return value: %TRUE if wnck_window_get_name() returns @window<!-- -->'s
++ * name, %FALSE if it returns a fallback name.
++ *
++ * Since: 2.16
++ **/
++gboolean
++wnck_window_has_name (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->name != NULL;
++}
++
++/**
++ * wnck_window_get_name:
++ * @window: a #WnckWindow.
++ *
++ * Returns the name of @window, as it should be displayed in a pager
++ * or tasklist. Always returns some value, even if @window has no name
++ * set; use wnck_window_has_name() if you need to know whether the returned
++ * name is "real" or not.
++ *
++ * For icons titles, use wnck_window_get_icon_name() instead.
++ *
++ * Return value: the name of @window, or a fallback name if no name is
++ * available.
++ **/
++const char*
++wnck_window_get_name (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ if (window->priv->name)
++ return window->priv->name;
++ else
++ return FALLBACK_NAME;
++}
++
++/**
++ * wnck_window_has_icon_name:
++ * @window: a #WnckWindow
++ *
++ * Checks whether or not @window has an icon name.
++ * wnck_window_get_icon_name() will always return some value, even if
++ * @window has no icon name set; wnck_window_has_icon_name() can
++ * be used to tell if that icon name is real or not.
++ *
++ * (Note that if wnck_window_has_icon_name() returns %FALSE, but
++ * wnck_window_has_name() returns %TRUE, then the name returned by
++ * wnck_window_get_icon_name() is @window<!-- -->'s name. Only when both
++ * methods return %FALSE does wnck_window_get_icon_name() return a
++ * generic fallback name.)
++ *
++ * Return value: %TRUE if wnck_window_get_icon_name() returns
++ * @window<!-- -->'s icon name, %FALSE if it returns a fallback name.
++ *
++ * Since: 2.16
++ **/
++gboolean
++wnck_window_has_icon_name (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->icon_name != NULL;
++}
++
++/**
++ * wnck_window_get_icon_name:
++ * @window: a #WnckWindow
++ *
++ * Returns the icon name of @window, as it should be displayed for an icon
++ * (minimized state). Always returns some value, even if @window has no icon
++ * name set; use wnck_window_has_icon_name() if you need to know whether the
++ * returned icon name is "real" or not.
++ *
++ * Contrast with wnck_window_get_name(), which returns @window<!-- -->'s
++ * title, not its icon title.
++ *
++ * Return value: the icon name of @window, or a fallback icon name if no icon
++ * name is available.
++ **/
++const char*
++wnck_window_get_icon_name (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ if (window->priv->icon_name)
++ return window->priv->icon_name;
++ else if (window->priv->name)
++ return window->priv->name;
++ else
++ return FALLBACK_NAME;
++}
++
++char *
++_wnck_window_get_name_for_display (WnckWindow *window,
++ gboolean use_icon_name,
++ gboolean use_state_decorations)
++{
++ const char *name;
++
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ if (use_icon_name && wnck_window_has_icon_name (window))
++ name = wnck_window_get_icon_name (window);
++ else
++ name = wnck_window_get_name (window);
++
++ if (use_state_decorations)
++ {
++ if (window->priv->is_shaded)
++ return g_strdup_printf ("=%s=", name);
++ else if (window->priv->is_minimized)
++ return g_strdup_printf ("[%s]", name);
++ else
++ return g_strdup (name);
++ }
++ else
++ return g_strdup (name);
++}
++
++
++/**
++ * wnck_window_get_application:
++ * @window: a #WnckWindow.
++ *
++ * Returns the #WnckApplication to which @window belongs.
++ *
++ * Return value: the #WnckApplication to which @window belongs. The returned
++ * #WnckApplication is owned by libwnck and must not be referenced or
++ * unreferenced.
++ **/
++WnckApplication*
++wnck_window_get_application (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ return window->priv->app;
++}
++
++/**
++ * wnck_window_get_transient:
++ * @window: a #WnckWindow.
++ *
++ * Returns the #WnckWindow for which @window is transient.
++ *
++ * Return value: the #WnckWindow for which @window is transient, or %NULL if
++ * @window is not transient for any #WnckWindow.
++ *
++ * Since: 2.12
++ **/
++WnckWindow*
++wnck_window_get_transient (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ return wnck_window_get (window->priv->transient_for);
++}
++
++/**
++ * wnck_window_get_group_leader:
++ * @window: a #WnckWindow.
++ *
++ * Returns the group leader of the group of windows to which @window belongs.
++ *
++ * Return value: the group leader of the group of windows to which @window
++ * belongs, or the X window ID of @window if @window does not belong to any
++ * group.
++ **/
++gulong
++wnck_window_get_group_leader (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), None);
++
++ return window->priv->group_leader;
++}
++
++/**
++ * wnck_window_get_xid:
++ * @window: a #WnckWindow.
++ *
++ * Returns the X window ID of @window.
++ *
++ * Return value: the X window ID of @window.
++ **/
++gulong
++wnck_window_get_xid (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), None);
++
++ return window->priv->xwindow;
++}
++
++/**
++ * wnck_window_get_class_group:
++ * @window: a #WnckWindow.
++ *
++ * Returns the #WnckClassGroup to which @window belongs.
++ *
++ * Return value: the #WnckClassGroup to which @window belongs. The returned
++ * #WnckClassGroup is owned by libwnck and must not be referenced or
++ * unreferenced.
++ *
++ * Since: 2.2
++ **/
++WnckClassGroup *
++wnck_window_get_class_group (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ return window->priv->class_group;
++}
++
++/**
++ * wnck_window_get_session_id:
++ * @window: a #WnckWindow.
++ *
++ * Returns the session ID for @window in Latin-1 encoding.
++ * NOTE: this is invalid UTF-8. You can't display this
++ * string in a GTK+ widget without converting to UTF-8.
++ * See wnck_window_get_session_id_utf8().
++ *
++ * Return value: the session ID for @window in Latin-1, or %NULL if @window has
++ * no session ID.
++ **/
++const char*
++wnck_window_get_session_id (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ return window->priv->session_id;
++}
++
++/**
++ * wnck_window_get_session_id_utf8:
++ * @window: a #WnckWindow.
++ *
++ * Returns the session ID for @window in UTF-8 encoding.
++ * The session ID should be in Latin-1 encoding, so the conversion should work,
++ * but a broken client could set a session ID that might not be convertable to
++ * UTF-8.
++ *
++ * Return value: the session ID for @window in UTF-8, or %NULL if @window has
++ * no session ID.
++ **/
++const char*
++wnck_window_get_session_id_utf8 (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ if (window->priv->session_id_utf8 == NULL &&
++ window->priv->session_id != NULL)
++ {
++ GString *str;
++ char *p;
++
++ str = g_string_new ("");
++
++ p = window->priv->session_id;
++ while (*p)
++ {
++ g_string_append_unichar (str, g_utf8_get_char (p));
++ p = g_utf8_next_char (p);
++ }
++
++ window->priv->session_id_utf8 = g_string_free (str, FALSE);
++ }
++
++ return window->priv->session_id_utf8;
++}
++
++/**
++ * wnck_window_get_pid:
++ * @window: a #WnckWindow.
++ *
++ * Returns the process ID of @window.
++ *
++ * Return value: the process ID of @window, or 0 if none is available.
++ **/
++int
++wnck_window_get_pid (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), 0);
++
++ return window->priv->pid;
++}
++
++/**
++ * wnck_window_get_sort_order:
++ * @window: a #WnckWindow.
++ *
++ * Returns the sort order of @window, used for ordering of @window in
++ * #WnckSelector and #WnckTasklist. The sort order is an internal state in
++ * libwnck. The initial value is defined when the window is created.
++ *
++ * Return value: the sort order of @window, or G_MAXINT if none is available.
++ *
++ * Since: 2.10
++ **/
++gint
++wnck_window_get_sort_order (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), G_MAXINT);
++
++ return window->priv->sort_order;
++}
++
++/**
++ * wnck_window_set_sort_order:
++ * @window: a #WnckWindow.
++ * @order: new sort order for @window.
++ *
++ * Sets the sort order of @window. The sort order is used for ordering of
++ * @window in #WnckSelector and #WnckTasklist.
++ *
++ * Since: 2.20
++ **/
++void wnck_window_set_sort_order (WnckWindow *window,
++ gint order)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ window->priv->sort_order = order;
++ return;
++}
++
++/**
++ * wnck_window_get_window_type:
++ * @window: a #WnckWindow.
++ *
++ * Returns the semantic type of @window.
++ *
++ * Return value: the semantic type of @window.
++ **/
++WnckWindowType
++wnck_window_get_window_type (WnckWindow *window)
++{
++ /* FIXME: should we have an invalid window type for this? */
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), 0);
++
++ return window->priv->wintype;
++}
++
++/**
++ * wnck_window_set_window_type:
++ * @window: a #WnckWindow.
++ * @wintype: a semantic type.
++ *
++ * Sets the semantic type of @window to @wintype.
++ *
++ * Since: 2.12
++ **/
++void
++wnck_window_set_window_type (WnckWindow *window, WnckWindowType wintype)
++{
++ Atom atom;
++
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ switch (wintype) {
++ case WNCK_WINDOW_NORMAL:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_NORMAL");
++ break;
++ case WNCK_WINDOW_DESKTOP:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_DESKTOP");
++ break;
++ case WNCK_WINDOW_DOCK:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_DOCK");
++ break;
++ case WNCK_WINDOW_DIALOG:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_DIALOG");
++ break;
++ case WNCK_WINDOW_TOOLBAR:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_TOOLBAR");
++ break;
++ case WNCK_WINDOW_MENU:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_MENU");
++ break;
++ case WNCK_WINDOW_UTILITY:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_UTILITY");
++ break;
++ case WNCK_WINDOW_SPLASHSCREEN:
++ atom = _wnck_atom_get ("_NET_WM_WINDOW_TYPE_SPLASHSCREEN");
++ break;
++ default:
++ return;
++ }
++ _wnck_error_trap_push ();
++
++ XChangeProperty (gdk_display,
++ window->priv->xwindow,
++ _wnck_atom_get ("_NET_WM_WINDOW_TYPE"),
++ XA_ATOM, 32, PropModeReplace,
++ (guchar *)&atom, 1);
++
++ _wnck_error_trap_pop ();
++}
++
++/**
++ * wnck_window_is_minimized:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is minimized. Minimization state may change anytime
++ * a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is minimized, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_minimized (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_minimized;
++}
++
++/**
++ * wnck_window_needs_attention:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window needs attention. This state may change anytime
++ * a #WnckWindow::state-changed signal gets emitted.
++ *
++ * This state depends on flags such as the demands_attention and is_urgent
++ * hints.
++ *
++ * Return value: %TRUE if @window needs attention, %FALSE otherwise.
++ *
++ * Since: 2.12
++ **/
++gboolean
++wnck_window_needs_attention (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->demands_attention || window->priv->is_urgent;
++}
++
++time_t
++_wnck_window_get_needs_attention_time (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), 0);
++
++ return window->priv->needs_attention_time;
++}
++
++/* Return whether the transient of @window needs attention */
++static WnckWindow *
++transient_needs_attention (WnckWindow *window)
++{
++ GList *windows;
++ WnckWindow *transient;
++
++ if (!WNCK_IS_WINDOW (window))
++ return NULL;
++
++ windows = wnck_screen_get_windows_stacked (window->priv->screen);
++
++ transient = window;
++ while ((transient = find_last_transient_for (windows, transient->priv->xwindow)))
++ {
++ /* catch transient cycles */
++ if (transient == window)
++ return NULL;
++
++ if (wnck_window_needs_attention (transient))
++ return transient;
++ }
++
++ return FALSE;
++}
++
++time_t
++_wnck_window_or_transient_get_needs_attention_time (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), 0);
++
++ if (_wnck_window_get_needs_attention_time (window) == 0)
++ {
++ WnckWindow *transient;
++
++ transient = transient_needs_attention (window);
++ if (transient)
++ return _wnck_window_get_needs_attention_time (transient);
++ else
++ return 0;
++ }
++ else
++ return _wnck_window_get_needs_attention_time (window);
++}
++
++/**
++ * wnck_window_or_transient_needs_attention:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window or one of its transients needs attention. This state
++ * may change anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window or one of its transients needs attention,
++ * %FALSE otherwise.
++ *
++ * Since: 2.12
++ **/
++gboolean
++wnck_window_or_transient_needs_attention (WnckWindow *window)
++{
++ return wnck_window_needs_attention (window) ||
++ transient_needs_attention (window) != NULL;
++}
++
++/**
++ * wnck_window_is_maximized_horizontally:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is maximized horizontally. Horizontal maximization
++ * state may change anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is maximized horizontally, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_maximized_horizontally (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_maximized_horz;
++}
++
++/**
++ * wnck_window_is_maximized_vertically:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is maximized vertically. vertiVal maximization
++ * state may change anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is maximized vertically, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_maximized_vertically (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_maximized_vert;
++}
++
++const char*
++_wnck_window_get_startup_id (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ if (window->priv->startup_id == NULL &&
++ window->priv->group_leader != None)
++ {
++ WnckApplication *app;
++
++ /* Fall back to group leader property */
++
++ app = wnck_application_get (window->priv->group_leader);
++
++ if (app != NULL)
++ return wnck_application_get_startup_id (app);
++ else
++ return NULL;
++ }
++
++ return window->priv->startup_id;
++}
++
++const char*
++_wnck_window_get_resource_class (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ return window->priv->res_class;
++}
++
++const char*
++_wnck_window_get_resource_name (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ return window->priv->res_name;
++}
++
++/**
++ * wnck_window_is_maximized:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is maximized. Maximization state may change
++ * anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * As for GDK, "maximized" means both vertically and horizontally. If @window
++ * is maximized in only one direction, then @window is not considered
++ * maximized.
++ *
++ * Return value: %TRUE if @window is maximized in both directions, %FALSE
++ * otherwise.
++ **/
++gboolean
++wnck_window_is_maximized (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return
++ window->priv->is_maximized_horz &&
++ window->priv->is_maximized_vert;
++}
++
++/**
++ * wnck_window_is_shaded:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is shaded. Shade state may change anytime
++ * a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is shaded, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_shaded (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_shaded;
++}
++
++/**
++ * wnck_window_is_above:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is above other windows. This state may change
++ * anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * See wnck_window_make_above() for more details on this state.
++ *
++ * Return value: %TRUE if @window is above other windows, %FALSE otherwise.
++ *
++ * Since: 2.14
++ **/
++gboolean
++wnck_window_is_above (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_above;
++}
++
++/**
++ * wnck_window_is_below:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is below other windows. This state may change
++ * anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * See wnck_window_make_below() for more details on this state.
++ *
++ * Return value: %TRUE if @window is below other windows, %FALSE otherwise.
++ *
++ * Since: 2.20
++ **/
++gboolean
++wnck_window_is_below (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_below;
++}
++
++/**
++ * wnck_window_is_skip_pager:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is included on pagers. This state may change
++ * anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is included on pagers, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_skip_pager (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->skip_pager;
++}
++
++/**
++ * wnck_window_set_skip_pager:
++ * @window: a #WnckWindow.
++ * @skip: whether @window should be included on pagers.
++ *
++ * Asks the window manager to make @window included or not included on pagers.
++ **/
++void
++wnck_window_set_skip_pager (WnckWindow *window,
++ gboolean skip)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ skip,
++ _wnck_atom_get ("_NET_WM_STATE_SKIP_PAGER"),
++ 0);
++}
++
++/**
++ * wnck_window_is_skip_tasklist:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is included on tasklists. This state may change
++ * anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is included on tasklists, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_skip_tasklist (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->skip_taskbar;
++}
++
++/**
++ * wnck_window_is_fullscreen:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is fullscreen. Fullscreen state may change
++ * anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Return value: %TRUE if @window is fullscreen, %FALSE otherwise.
++ *
++ * Since: 2.8
++ **/
++gboolean
++wnck_window_is_fullscreen (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_fullscreen;
++}
++
++/**
++ * wnck_window_set_skip_tasklist:
++ * @window: a #WnckWindow.
++ * @skip: whether @window should be included on tasklists.
++ *
++ * Asks the window manager to make @window included or not included on
++ * tasklists.
++ **/
++void
++wnck_window_set_skip_tasklist (WnckWindow *window,
++ gboolean skip)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ skip,
++ _wnck_atom_get ("_NET_WM_STATE_SKIP_TASKBAR"),
++ 0);
++}
++
++/**
++ * wnck_window_set_fullscreen:
++ * @window: a #WnckWindow.
++ * @fullscreen: whether to make @window fullscreen.
++ *
++ * Asks the window manager to set the fullscreen state of @window according to
++ * @fullscreen.
++ *
++ * Since: 2.8
++ **/
++void
++wnck_window_set_fullscreen (WnckWindow *window,
++ gboolean fullscreen)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ fullscreen,
++ _wnck_atom_get ("_NET_WM_STATE_FULLSCREEN"),
++ 0);
++}
++
++/**
++ * wnck_window_is_sticky:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is sticky. Sticky state may change
++ * anytime a #WnckWindow::state-changed signal gets emitted.
++ *
++ * Sticky here means "stuck to the glass", i.e. does not scroll with the
++ * viewport. In GDK/GTK+ (e.g. gdk_window_stick()/gtk_window_stick()), sticky
++ * means "stuck to the glass" and <emphasis>also</emphasis> that the window is
++ * on all workspaces. But here it only means the viewport aspect of it.
++ *
++ * Return value: %TRUE if @window is "stuck to the glass", %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_sticky (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->is_sticky;
++}
++
++/**
++ * wnck_window_close:
++ * @window: a #WnckWindow.
++ * @timestamp: the X server timestamp of the user interaction event that caused
++ * this call to occur.
++ *
++ * Closes @window.
++ *
++ * This function existed before 2.6, but the @timestamp argument was missing
++ * in earlier versions.
++ *
++ * Since: 2.6
++ **/
++void
++wnck_window_close (WnckWindow *window,
++ guint32 timestamp)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_close (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow, timestamp);
++}
++
++/**
++ * wnck_window_minimize:
++ * @window: a #WnckWindow.
++ *
++ * Minimizes @window.
++ **/
++void
++wnck_window_minimize (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_iconify (window->priv->xwindow);
++}
++
++/**
++ * wnck_window_unminimize:
++ * @window: a #WnckWindow.
++ * @timestamp: the X server timestamp of the user interaction event that caused
++ * this call to occur.
++ *
++ * Unminimizes @window by activating it or one of its transients. See
++ * wnck_window_activate_transient() for details on how the activation is done.
++ **/
++void
++wnck_window_unminimize (WnckWindow *window,
++ guint32 timestamp)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ wnck_window_activate_transient (window, timestamp);
++}
++
++/**
++ * wnck_window_maximize:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to maximize @window.
++ **/
++void
++wnck_window_maximize (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ TRUE,
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_VERT"),
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_HORZ"));
++}
++
++/**
++ * wnck_window_unmaximize:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to unmaximize @window.
++ **/
++void
++wnck_window_unmaximize (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ FALSE,
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_VERT"),
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_HORZ"));
++}
++
++/**
++ * wnck_window_maximize_horizontally:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to maximize horizontally @window.
++ **/
++void
++wnck_window_maximize_horizontally (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ TRUE,
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_HORZ"),
++ 0);
++}
++
++/**
++ * wnck_window_unmaximize_horizontally:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to unmaximize horizontally @window.
++ **/
++void
++wnck_window_unmaximize_horizontally (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ FALSE,
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_HORZ"),
++ 0);
++}
++
++/**
++ * wnck_window_maximize_vertically:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to maximize vertically @window.
++ **/
++void
++wnck_window_maximize_vertically (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ TRUE,
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_VERT"),
++ 0);
++}
++
++/**
++ * wnck_window_unmaximize_vertically:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to unmaximize vertically @window.
++ **/
++void
++wnck_window_unmaximize_vertically (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ FALSE,
++ _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_VERT"),
++ 0);
++}
++
++/**
++ * wnck_window_shade:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to shade @window.
++ **/
++void
++wnck_window_shade (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ TRUE,
++ _wnck_atom_get ("_NET_WM_STATE_SHADED"),
++ 0);
++}
++
++/**
++ * wnck_window_unshade:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to unshade @window.
++ **/
++void
++wnck_window_unshade (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ FALSE,
++ _wnck_atom_get ("_NET_WM_STATE_SHADED"),
++ 0);
++}
++
++/**
++ * wnck_window_make_above:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to put @window on top of most windows (@window will
++ * not be on top of focused fullscreen windows, of other windows with this
++ * setting and of dock windows).
++ *
++ * Since: 2.14
++ **/
++void
++wnck_window_make_above (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ TRUE,
++ _wnck_atom_get ("_NET_WM_STATE_ABOVE"),
++ 0);
++}
++
++/**
++ * wnck_window_unmake_above:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to not put @window on top of most windows, and to
++ * put it again in the stack with other windows.
++ *
++ * Since: 2.14
++ **/
++void
++wnck_window_unmake_above (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ FALSE,
++ _wnck_atom_get ("_NET_WM_STATE_ABOVE"),
++ 0);
++}
++
++/**
++ * wnck_window_make_below:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to put @window below most windows.
++ *
++ * Since: 2.20
++ **/
++void
++wnck_window_make_below (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ TRUE,
++ _wnck_atom_get ("_NET_WM_STATE_BELOW"),
++ 0);
++}
++
++/**
++ * wnck_window_unmake_below:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to not put @window below most windows, and to
++ * put it again in the stack with other windows.
++ *
++ * Since: 2.20
++ **/
++void
++wnck_window_unmake_below (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ FALSE,
++ _wnck_atom_get ("_NET_WM_STATE_BELOW"),
++ 0);
++}
++
++/**
++ * wnck_window_stick:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to keep the @window<!-- -->'s position fixed on the
++ * screen, even when the workspace or viewport scrolls.
++ **/
++void
++wnck_window_stick (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ TRUE,
++ _wnck_atom_get ("_NET_WM_STATE_STICKY"),
++ 0);
++}
++
++/**
++ * wnck_window_unstick:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to not have @window<!-- -->'s position fixed on the
++ * screen when the workspace or viewport scrolls.
++ **/
++void
++wnck_window_unstick (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_state (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ FALSE,
++ _wnck_atom_get ("_NET_WM_STATE_STICKY"),
++ 0);
++}
++
++/**
++ * wnck_window_keyboard_move:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to start moving @window via the keyboard.
++ **/
++void
++wnck_window_keyboard_move (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_keyboard_move (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow);
++}
++
++/**
++ * wnck_window_keyboard_size:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to start resizing @window via the keyboard.
++ **/
++void
++wnck_window_keyboard_size (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_keyboard_size (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow);
++}
++
++/**
++ * wnck_window_get_workspace:
++ * @window: a #WnckWindow.
++ *
++ * Returns the current workspace @window is on. If the window is pinned (on all
++ * workspaces), or not on any workspaces, %NULL may be returned.
++ *
++ * Return value: the single current workspace @window is on, or %NULL. The
++ * returned #WnckWorkspace is owned by libwnck and must not be referenced or
++ * unreferenced.
++ **/
++WnckWorkspace*
++wnck_window_get_workspace (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ if (window->priv->workspace == ALL_WORKSPACES)
++ return NULL;
++ else
++ return wnck_screen_get_workspace (window->priv->screen, window->priv->workspace);
++}
++
++/**
++ * wnck_window_move_to_workspace:
++ * @window: a #WnckWindow.
++ * @space: a #WnckWorkspace.
++ *
++ * Asks the window manager to move @window to @space.
++ *
++ * FIXME: what happens if @window is pinned?
++ **/
++void
++wnck_window_move_to_workspace (WnckWindow *window,
++ WnckWorkspace *space)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++ g_return_if_fail (WNCK_IS_WORKSPACE (space));
++
++ _wnck_change_workspace (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ wnck_workspace_get_number (space));
++}
++
++/**
++ * wnck_window_is_pinned:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is on all workspace. Pinned state may change
++ * anytime a #WnckWindow::workspace-changed signal gets emitted, but not when
++ * a #WnckWindow::state-changed gets emitted.
++ *
++ * Return value: %TRUE if @window is on all workspaces, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_pinned (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window->priv->workspace == ALL_WORKSPACES;
++}
++
++/**
++ * wnck_window_pin:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to put @window on all workspaces.
++ **/
++void
++wnck_window_pin (WnckWindow *window)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_change_workspace (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ ALL_WORKSPACES);
++}
++
++/**
++ * wnck_window_unpin:
++ * @window: a #WnckWindow.
++ *
++ * Asks the window manager to put @window only in the currently active
++ * workspace, if @window was previously pinned. If @window was not pinned,
++ * does not change @window<!-- -->'s workspace. If the active workspace
++ * is not known for some reason (it should not happen much), sets
++ * @window<!-- -->'s workspace to the first workspace.
++ **/
++void
++wnck_window_unpin (WnckWindow *window)
++{
++ WnckWorkspace *active;
++
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ if (window->priv->workspace != ALL_WORKSPACES)
++ return;
++
++ active = wnck_screen_get_active_workspace (window->priv->screen);
++
++ _wnck_change_workspace (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ active ? wnck_workspace_get_number (active) : 0);
++}
++
++/**
++ * wnck_window_activate:
++ * @window: a #WnckWindow.
++ * @timestamp: the X server timestamp of the user interaction event that caused
++ * this call to occur.
++ *
++ * Asks the window manager to make @window the active window. The
++ * window manager may choose to raise @window along with focusing it, and may
++ * decide to refuse the request (to not steal the focus if there is a more
++ * recent user activity, for example).
++ *
++ * This function existed before 2.10, but the @timestamp argument was missing
++ * in earlier versions.
++ *
++ * Since: 2.10
++ **/
++void
++wnck_window_activate (WnckWindow *window,
++ guint32 timestamp)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ _wnck_activate (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ timestamp);
++}
++
++/**
++ * wnck_window_is_active:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is the active window on its #WnckScreen.
++ *
++ * Return value: %TRUE if @window is the active window on its #WnckScreen,
++ * %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_active (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return window == wnck_screen_get_active_window (window->priv->screen);
++}
++
++/**
++ * wnck_window_is_most_recently_activated:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether @window is the most recently activated window on its
++ * #WnckScreen.
++ *
++ * The most recently activated window is identical to the active
++ * window for click and sloppy focus methods (since a window is always
++ * active in those cases) but differs slightly for mouse focus since
++ * there often is no active window.
++ *
++ * Return value: %TRUE if @window was the most recently activated window on its
++ * #WnckScreen, %FALSE otherwise.
++ *
++ * Since: 2.8
++ **/
++gboolean
++wnck_window_is_most_recently_activated (WnckWindow *window)
++{
++ WnckWindow * current;
++ WnckWindow * previous;
++ WnckWindow * most_recently_activated_window;
++
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ current = wnck_screen_get_active_window (window->priv->screen);
++ previous = wnck_screen_get_previously_active_window (window->priv->screen);
++
++ if (current)
++ most_recently_activated_window = current;
++ else
++ most_recently_activated_window = previous;
++
++ return (window == most_recently_activated_window);
++}
++
++static WnckWindow*
++find_last_transient_for (GList *windows,
++ Window xwindow)
++{
++ GList *tmp;
++ WnckWindow *retval;
++
++ /* find _last_ transient for xwindow in the list */
++
++ retval = NULL;
++
++ tmp = windows;
++ while (tmp != NULL)
++ {
++ WnckWindow *w = tmp->data;
++
++ if (w->priv->transient_for == xwindow &&
++ w->priv->wintype != WNCK_WINDOW_UTILITY)
++ retval = w;
++
++ tmp = tmp->next;
++ }
++
++ return retval;
++}
++
++/**
++ * wnck_window_activate_transient:
++ * @window: a #WnckWindow.
++ * @timestamp: the X server timestamp of the user interaction event that caused
++ * this call to occur.
++ *
++ * If @window has transients, activates the most likely transient
++ * instead of the window itself. Otherwise activates @window.
++ *
++ * FIXME the ideal behavior of this function is probably to activate
++ * the most recently active window among @window and its transients.
++ * This is probably best implemented on the window manager side.
++ *
++ * This function existed before 2.10, but the @timestamp argument was missing
++ * in earlier versions.
++ *
++ * Since: 2.10
++ **/
++void
++wnck_window_activate_transient (WnckWindow *window,
++ guint32 timestamp)
++{
++ GList *windows;
++ WnckWindow *transient;
++ WnckWindow *next;
++
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ windows = wnck_screen_get_windows_stacked (window->priv->screen);
++
++ transient = NULL;
++ next = find_last_transient_for (windows, window->priv->xwindow);
++
++ while (next != NULL)
++ {
++ if (next == window)
++ {
++ /* catch transient cycles */
++ transient = NULL;
++ break;
++ }
++
++ transient = next;
++
++ next = find_last_transient_for (windows, transient->priv->xwindow);
++ }
++
++ if (transient != NULL)
++ wnck_window_activate (transient, timestamp);
++ else
++ wnck_window_activate (window, timestamp);
++}
++
++/**
++ * wnck_window_transient_is_most_recently_activated:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether one of the transients of @window is the most
++ * recently activated window. See
++ * wnck_window_is_most_recently_activated() for a more complete
++ * description of what is meant by most recently activated. This
++ * function is needed because clicking on a #WnckTasklist once will
++ * activate a transient instead of @window itself
++ * (wnck_window_activate_transient), and clicking again should
++ * minimize @window and its transients. (Not doing this can be
++ * especially annoying in the case of modal dialogs that don't appear
++ * in the #WnckTaslist).
++ *
++ * Return value: %TRUE if one of the transients of @window is the most recently
++ * activated window, %FALSE otherwise.
++ *
++ * Since: 2.12
++ **/
++gboolean
++wnck_window_transient_is_most_recently_activated (WnckWindow *window)
++{
++ GList *windows;
++ WnckWindow *transient;
++
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ windows = wnck_screen_get_windows_stacked (window->priv->screen);
++
++ transient = window;
++ while ((transient = find_last_transient_for (windows, transient->priv->xwindow)))
++ {
++ /* catch transient cycles */
++ if (transient == window)
++ return FALSE;
++
++ if (wnck_window_is_most_recently_activated (transient))
++ return TRUE;
++ }
++
++ return FALSE;
++}
++
++static void
++get_icons (WnckWindow *window)
++{
++ GdkPixbuf *icon;
++ GdkPixbuf *mini_icon;
++
++ icon = NULL;
++ mini_icon = NULL;
++
++ if (_wnck_read_icons (window->priv->xwindow,
++ window->priv->icon_cache,
++ &icon,
++ DEFAULT_ICON_WIDTH, DEFAULT_ICON_HEIGHT,
++ &mini_icon,
++ DEFAULT_MINI_ICON_WIDTH,
++ DEFAULT_MINI_ICON_HEIGHT))
++ {
++ window->priv->need_emit_icon_changed = TRUE;
++
++ if (window->priv->icon)
++ g_object_unref (G_OBJECT (window->priv->icon));
++
++ if (window->priv->mini_icon)
++ g_object_unref (G_OBJECT (window->priv->mini_icon));
++
++ window->priv->icon = icon;
++ window->priv->mini_icon = mini_icon;
++ }
++
++ g_assert ((window->priv->icon && window->priv->mini_icon) ||
++ !(window->priv->icon || window->priv->mini_icon));
++}
++
++/**
++ * wnck_window_get_icon:
++ * @window: a #WnckWindow.
++ *
++ * Returns the icon to be used for @window. If no icon was found, a fallback
++ * icon is used. wnck_window_get_icon_is_fallback() can be used to tell if the
++ * icon is the fallback icon.
++ *
++ * Return value: the icon for @window. The caller should reference the
++ * returned <classname>GdkPixbuf</classname> if it needs to keep the icon
++ * around.
++ **/
++GdkPixbuf*
++wnck_window_get_icon (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ get_icons (window);
++ if (window->priv->need_emit_icon_changed)
++ queue_update (window); /* not done in get_icons since we call that from
++ * the update
++ */
++
++ return window->priv->icon;
++}
++
++/**
++ * wnck_window_get_mini_icon:
++ * @window: a #WnckWindow.
++ *
++ * Returns the mini-icon to be used for @window. If no mini-icon was found, a
++ * fallback mini-icon is used. wnck_window_get_icon_is_fallback() can be used
++ * to tell if the mini-icon is the fallback mini-icon.
++ *
++ * Return value: the mini-icon for @window. The caller should reference the
++ * returned <classname>GdkPixbuf</classname> if it needs to keep the icon
++ * around.
++ **/
++GdkPixbuf*
++wnck_window_get_mini_icon (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
++
++ get_icons (window);
++ if (window->priv->need_emit_icon_changed)
++ queue_update (window); /* not done in get_icons since we call that from
++ * the update
++ */
++
++ return window->priv->mini_icon;
++}
++
++/**
++ * wnck_window_get_icon_is_fallback:
++ * @window: a #WnckWindow.
++ *
++ * Returns whether a default fallback icon is used for @window (because none
++ * was set on @window).
++ *
++ * Return value: %TRUE if the icon for @window is a fallback, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_get_icon_is_fallback (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++
++ return _wnck_icon_cache_get_is_fallback (window->priv->icon_cache);
++}
++
++/**
++ * wnck_window_get_actions:
++ * @window: a #WnckWindow.
++ *
++ * Returns the actions that can be done for @window.
++ *
++ * Return value: bitmask of actions that can be done for @window.
++ **/
++WnckWindowActions
++wnck_window_get_actions (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), 0);
++
++ return window->priv->actions;
++}
++
++
++/**
++ * wnck_window_get_state:
++ * @window: a #WnckWindow.
++ *
++ * Returns the state of @window.
++ *
++ * Return value: bitmask of active states for @window.
++ **/
++WnckWindowState
++wnck_window_get_state (WnckWindow *window)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), 0);
++
++ return COMPRESS_STATE (window);
++}
++
++/**
++ * wnck_window_get_client_window_geometry:
++ * @window: a #WnckWindow.
++ * @xp: return location for X coordinate in pixels of @window.
++ * @yp: return location for Y coordinate in pixels of @window.
++ * @widthp: return location for width in pixels of @window.
++ * @heightp: return location for height in pixels of @window.
++ *
++ * Gets the size and position of @window, as last received
++ * in a ConfigureNotify event (i.e. this call does not round-trip
++ * to the server, just gets the last size we were notified of).
++ * The X and Y coordinates are relative to the root window.
++ *
++ * The window manager usually adds a frame around windows. If
++ * you need to know the size of @window with the frame, use
++ * wnck_window_get_geometry().
++ *
++ * Since: 2.20
++ **/
++void
++wnck_window_get_client_window_geometry (WnckWindow *window,
++ int *xp,
++ int *yp,
++ int *widthp,
++ int *heightp)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ if (xp)
++ *xp = window->priv->x;
++ if (yp)
++ *yp = window->priv->y;
++ if (widthp)
++ *widthp = window->priv->width;
++ if (heightp)
++ *heightp = window->priv->height;
++}
++
++/**
++ * wnck_window_get_geometry:
++ * @window: a #WnckWindow.
++ * @xp: return location for X coordinate in pixels of @window.
++ * @yp: return location for Y coordinate in pixels of @window.
++ * @widthp: return location for width in pixels of @window.
++ * @heightp: return location for height in pixels of @window.
++ *
++ * Gets the size and position of @window, including decorations. This
++ * function uses the information last received in a ConfigureNotify
++ * event and adjusts it according to the size of the frame that is
++ * added by the window manager (this call does not round-trip to the
++ * server, it just gets the last sizes that were notified). The
++ * X and Y coordinates are relative to the root window.
++ *
++ * If you need to know the actual size of @window ignoring the frame
++ * added by the window manager, use wnck_window_get_client_window_geometry().
++ **/
++void
++wnck_window_get_geometry (WnckWindow *window,
++ int *xp,
++ int *yp,
++ int *widthp,
++ int *heightp)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ if (xp)
++ *xp = window->priv->x - window->priv->left_frame;
++ if (yp)
++ *yp = window->priv->y - window->priv->top_frame;
++ if (widthp)
++ *widthp = window->priv->width + window->priv->left_frame + window->priv->right_frame;
++ if (heightp)
++ *heightp = window->priv->height + window->priv->top_frame + window->priv->bottom_frame;
++}
++
++/**
++ * wnck_window_set_geometry:
++ * @window: a #WnckWindow.
++ * @gravity: the gravity point to use as a reference for the new position.
++ * @geometry_mask: a bitmask containing flags for what should be set.
++ * @x: new X coordinate in pixels of @window.
++ * @y: new Y coordinate in pixels of @window.
++ * @width: new width in pixels of @window.
++ * @height: new height in pixels of @window.
++ *
++ * Sets the size and position of @window. The X and Y coordinates should be
++ * relative to the root window.
++ *
++ * Note that the new size and position apply to @window with its frame added
++ * by the window manager. Therefore, using wnck_window_set_geometry() with
++ * the values returned by wnck_window_get_geometry() should be a no-op, while
++ * using wnck_window_set_geometry() with the values returned by
++ * wnck_window_get_client_window_geometry() should reduce the size of @window
++ * and move it.
++ *
++ * Since: 2.16
++ **/
++void
++wnck_window_set_geometry (WnckWindow *window,
++ WnckWindowGravity gravity,
++ WnckWindowMoveResizeMask geometry_mask,
++ int x,
++ int y,
++ int width,
++ int height)
++{
++ int gravity_and_flags;
++ int source;
++
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ source = _wnck_get_client_type();
++ gravity_and_flags = gravity;
++ gravity_and_flags |= geometry_mask << 8;
++ gravity_and_flags |= source << 12;
++
++ _wnck_set_window_geometry (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ gravity_and_flags, x, y, width, height);
++}
++
++/**
++ * wnck_window_is_visible_on_workspace:
++ * @window: a #WnckWindow.
++ * @workspace: a #WnckWorkspace.
++ *
++ * Like wnck_window_is_on_workspace(), but also checks that
++ * the window is in a visible state (i.e. not minimized or shaded).
++ *
++ * Return value: %TRUE if @window appears on @workspace in normal state, %FALSE
++ * otherwise.
++ **/
++gboolean
++wnck_window_is_visible_on_workspace (WnckWindow *window,
++ WnckWorkspace *workspace)
++{
++ WnckWindowState state;
++
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++ g_return_val_if_fail (WNCK_IS_WORKSPACE (workspace), FALSE);
++
++ state = wnck_window_get_state (window);
++
++ if (state & WNCK_WINDOW_STATE_HIDDEN)
++ return FALSE; /* not visible */
++
++ return wnck_window_is_on_workspace (window, workspace);
++}
++
++/**
++ * wnck_window_set_icon_geometry:
++ * @window: a #WnckWindow.
++ * @x: X coordinate in pixels.
++ * @y: Y coordinate in pixels.
++ * @width: width in pixels.
++ * @height: height in pixels.
++ *
++ * Sets the icon geometry for @window. A typical use case for this is the
++ * destination of the minimization animation of @window.
++ */
++void
++wnck_window_set_icon_geometry (WnckWindow *window,
++ int x,
++ int y,
++ int width,
++ int height)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++
++ if (window->priv->icon_geometry.x == x &&
++ window->priv->icon_geometry.y == y &&
++ window->priv->icon_geometry.width == width &&
++ window->priv->icon_geometry.height == height)
++ return;
++
++ window->priv->icon_geometry.x = x;
++ window->priv->icon_geometry.y = y;
++ window->priv->icon_geometry.width = width;
++ window->priv->icon_geometry.height = height;
++
++ _wnck_set_icon_geometry (window->priv->xwindow,
++ x, y, width, height);
++}
++
++/**
++ * wnck_window_is_on_workspace:
++ * @window: a #WnckWindow.
++ * @workspace: a #WnckWorkspace.
++ *
++ * Returns whether @window appears on @workspace.
++ *
++ * Return value: %TRUE if @window appears on @workspace, %FALSE otherwise.
++ **/
++gboolean
++wnck_window_is_on_workspace (WnckWindow *window,
++ WnckWorkspace *workspace)
++{
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++ g_return_val_if_fail (WNCK_IS_WORKSPACE (workspace), FALSE);
++
++ return wnck_window_is_pinned (window) ||
++ wnck_window_get_workspace (window) == workspace;
++}
++
++/**
++ * wnck_window_is_in_viewport:
++ * @window: a #WnckWindow.
++ * @workspace: a #WnckWorkspace.
++ *
++ * Returns %TRUE if @window appears in the current viewport of @workspace.
++ *
++ * Return value: %TRUE if @window appears in current viewport of @workspace,
++ * %FALSE otherwise.
++ *
++ * Since: 2.4
++ **/
++gboolean
++wnck_window_is_in_viewport (WnckWindow *window,
++ WnckWorkspace *workspace)
++{
++ GdkRectangle window_rect;
++ GdkRectangle viewport_rect;
++
++ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
++ g_return_val_if_fail (WNCK_IS_WORKSPACE (workspace), FALSE);
++
++ if (wnck_window_is_pinned (window) )
++ return TRUE;
++
++ if (wnck_window_get_workspace (window) != workspace)
++ return FALSE;
++
++ viewport_rect.x = wnck_workspace_get_viewport_x (workspace);
++ viewport_rect.y = wnck_workspace_get_viewport_y (workspace);
++ viewport_rect.width = wnck_screen_get_width (window->priv->screen);
++ viewport_rect.height = wnck_screen_get_height (window->priv->screen);
++
++ window_rect.x = window->priv->x - window->priv->left_frame + viewport_rect.x;
++ window_rect.y = window->priv->y - window->priv->top_frame + viewport_rect.y;
++ window_rect.width = window->priv->width + window->priv->left_frame + window->priv->right_frame;
++ window_rect.height = window->priv->height + window->priv->top_frame + window->priv->bottom_frame;
++
++ return gdk_rectangle_intersect (&viewport_rect, &window_rect, &window_rect);
++}
++
++void
++_wnck_window_set_application (WnckWindow *window,
++ WnckApplication *app)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++ g_return_if_fail (app == NULL || WNCK_IS_APPLICATION (app));
++
++ if (app)
++ g_object_ref (G_OBJECT (app));
++ if (window->priv->app)
++ g_object_unref (G_OBJECT (window->priv->app));
++ window->priv->app = app;
++}
++
++void
++_wnck_window_set_class_group (WnckWindow *window,
++ WnckClassGroup *class_group)
++{
++ g_return_if_fail (WNCK_IS_WINDOW (window));
++ g_return_if_fail (class_group == NULL || WNCK_IS_CLASS_GROUP (class_group));
++
++ if (class_group)
++ g_object_ref (G_OBJECT (class_group));
++ if (window->priv->class_group)
++ g_object_unref (G_OBJECT (window->priv->class_group));
++ window->priv->class_group = class_group;
++}
++
++void
++_wnck_window_process_property_notify (WnckWindow *window,
++ XEvent *xevent)
++{
++ if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_STATE"))
++ {
++ window->priv->need_update_state = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("WM_STATE"))
++ {
++ window->priv->need_update_wm_state = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ XA_WM_NAME ||
++ xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_NAME") ||
++ xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_VISIBLE_NAME"))
++ {
++ window->priv->need_update_name = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ XA_WM_ICON_NAME ||
++ xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_ICON_NAME") ||
++ xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_VISIBLE_ICON_NAME"))
++ {
++ window->priv->need_update_icon_name = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_ALLOWED_ACTIONS"))
++ {
++ window->priv->need_update_actions = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_DESKTOP"))
++ {
++ window->priv->need_update_workspace = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_WINDOW_TYPE"))
++ {
++ window->priv->need_update_wintype = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("WM_TRANSIENT_FOR"))
++ {
++ window->priv->need_update_transient_for = TRUE;
++ window->priv->need_update_wintype = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_STARTUP_ID"))
++ {
++ window->priv->need_update_startup_id = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom == XA_WM_CLASS)
++ {
++ window->priv->need_update_wmclass = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_WM_ICON") ||
++ xevent->xproperty.atom ==
++ _wnck_atom_get ("KWM_WIN_ICON"))
++ {
++ _wnck_icon_cache_property_changed (window->priv->icon_cache,
++ xevent->xproperty.atom);
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("WM_HINTS"))
++ {
++ window->priv->need_update_wmhints = TRUE;
++ queue_update (window);
++ }
++ else if (xevent->xproperty.atom ==
++ _wnck_atom_get ("_NET_FRAME_EXTENTS"))
++ {
++ window->priv->need_update_frame_extents = TRUE;
++ queue_update (window);
++ }
++}
++
++void
++_wnck_window_process_configure_notify (WnckWindow *window,
++ XEvent *xevent)
++{
++ if (xevent->xconfigure.send_event)
++ {
++ window->priv->x = xevent->xconfigure.x;
++ window->priv->y = xevent->xconfigure.y;
++ }
++ else
++ {
++ _wnck_get_window_position (WNCK_SCREEN_XSCREEN (window->priv->screen),
++ window->priv->xwindow,
++ &window->priv->x,
++ &window->priv->y);
++ }
++
++ window->priv->width = xevent->xconfigure.width;
++ window->priv->height = xevent->xconfigure.height;
++
++ emit_geometry_changed (window);
++}
++
++static void
++update_wm_state (WnckWindow *window)
++{
++ int state;
++
++ if (!window->priv->need_update_wm_state)
++ return;
++
++ window->priv->need_update_wm_state = FALSE;
++
++ window->priv->wm_state_iconic = FALSE;
++
++ state = _wnck_get_wm_state (window->priv->xwindow);
++
++ if (state == IconicState)
++ window->priv->wm_state_iconic = TRUE;
++}
++
++static void
++update_state (WnckWindow *window)
++{
++ Atom *atoms;
++ int n_atoms;
++ int i;
++ gboolean reread_net_wm_state;
++
++ reread_net_wm_state = window->priv->need_update_state;
++
++ window->priv->need_update_state = FALSE;
++
++ /* This is a bad hack, we always add the
++ * state based on window type in to the state,
++ * even if no state update is pending (since the
++ * state update just means the _NET_WM_STATE prop
++ * changed
++ */
++
++ if (reread_net_wm_state)
++ {
++ gboolean demanded_attention;
++
++ demanded_attention = window->priv->demands_attention;
++
++ window->priv->is_maximized_horz = FALSE;
++ window->priv->is_maximized_vert = FALSE;
++ window->priv->is_sticky = FALSE;
++ window->priv->is_shaded = FALSE;
++ window->priv->is_above = FALSE;
++ window->priv->is_below = FALSE;
++ window->priv->skip_taskbar = FALSE;
++ window->priv->skip_pager = FALSE;
++ window->priv->net_wm_state_hidden = FALSE;
++ window->priv->is_fullscreen = FALSE;
++ window->priv->demands_attention = FALSE;
++
++ atoms = NULL;
++ n_atoms = 0;
++ _wnck_get_atom_list (window->priv->xwindow,
++ _wnck_atom_get ("_NET_WM_STATE"),
++ &atoms, &n_atoms);
++
++ i = 0;
++ while (i < n_atoms)
++ {
++ if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_VERT"))
++ window->priv->is_maximized_vert = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_MAXIMIZED_HORZ"))
++ window->priv->is_maximized_horz = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_HIDDEN"))
++ window->priv->net_wm_state_hidden = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_STICKY"))
++ window->priv->is_sticky = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_SHADED"))
++ window->priv->is_shaded = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_ABOVE"))
++ window->priv->is_above = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_BELOW"))
++ window->priv->is_below = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_FULLSCREEN"))
++ window->priv->is_fullscreen = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_SKIP_TASKBAR"))
++ window->priv->skip_taskbar = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_SKIP_PAGER"))
++ window->priv->skip_pager = TRUE;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_STATE_DEMANDS_ATTENTION"))
++ window->priv->demands_attention = TRUE;
++
++ ++i;
++ }
++
++ if (window->priv->demands_attention != demanded_attention)
++ {
++ if (window->priv->demands_attention)
++ time (&window->priv->needs_attention_time);
++ else if (!window->priv->is_urgent)
++ window->priv->needs_attention_time = 0;
++ }
++
++ g_free (atoms);
++ }
++
++ switch (window->priv->wintype)
++ {
++ case WNCK_WINDOW_DESKTOP:
++ case WNCK_WINDOW_DOCK:
++ case WNCK_WINDOW_SPLASHSCREEN:
++ window->priv->skip_taskbar = TRUE;
++ break;
++
++ case WNCK_WINDOW_TOOLBAR:
++ case WNCK_WINDOW_MENU:
++ case WNCK_WINDOW_UTILITY:
++ case WNCK_WINDOW_DIALOG:
++ /* Skip taskbar if the window is transient
++ * for some main application window
++ */
++ if (wnck_window_get_transient (window) != NULL &&
++ !window->priv->transient_for_root)
++ window->priv->skip_taskbar = TRUE;
++ break;
++
++ case WNCK_WINDOW_NORMAL:
++ break;
++ }
++
++ /* FIXME!!!!!!!!!! What in the world is this buggy duplicate of the code
++ * immediately above this for??!?!?
++ */
++ switch (window->priv->wintype)
++ {
++ case WNCK_WINDOW_DESKTOP:
++ case WNCK_WINDOW_DOCK:
++ case WNCK_WINDOW_TOOLBAR:
++ case WNCK_WINDOW_MENU:
++ case WNCK_WINDOW_SPLASHSCREEN:
++ window->priv->skip_pager = TRUE;
++ break;
++
++ case WNCK_WINDOW_NORMAL:
++ case WNCK_WINDOW_DIALOG:
++ case WNCK_WINDOW_UTILITY:
++ break;
++ }
++
++ /* FIXME we need to recompute this if the window manager changes */
++ if (wnck_screen_net_wm_supports (window->priv->screen,
++ "_NET_WM_STATE_HIDDEN"))
++ {
++ window->priv->is_hidden = window->priv->net_wm_state_hidden;
++
++ /* FIXME this is really broken; need to bring it up on
++ * wm-spec-list. It results in showing an "Unminimize" menu
++ * item on task list, for shaded windows.
++ */
++ window->priv->is_minimized = window->priv->is_hidden;
++ }
++ else
++ {
++ window->priv->is_minimized = window->priv->wm_state_iconic;
++
++ window->priv->is_hidden = window->priv->is_minimized || window->priv->is_shaded;
++ }
++}
++
++static gboolean
++nullstr_equal (const char *str1, const char *str2)
++{
++ if (str1 == NULL || str2 == NULL)
++ return str1 == str2;
++ else
++ return !strcmp (str1, str2);
++}
++
++static void
++update_name (WnckWindow *window)
++{
++ char *new_name;
++
++ if (!window->priv->need_update_name)
++ return;
++
++ window->priv->need_update_name = FALSE;
++
++ new_name = _wnck_get_name (window->priv->xwindow);
++
++ if (!nullstr_equal (window->priv->name, new_name))
++ window->priv->need_emit_name_changed = TRUE;
++
++ g_free (window->priv->name);
++ window->priv->name = new_name;
++}
++
++static void
++update_icon_name (WnckWindow *window)
++{
++ char *new_name = NULL;
++
++ if (!window->priv->need_update_icon_name)
++ return;
++
++ window->priv->need_update_icon_name = FALSE;
++
++ new_name = _wnck_get_icon_name (window->priv->xwindow);
++
++ if (!nullstr_equal (window->priv->icon_name, new_name))
++ window->priv->need_emit_name_changed = TRUE;
++
++ g_free (window->priv->icon_name);
++ window->priv->icon_name = new_name;
++}
++
++static void
++update_workspace (WnckWindow *window)
++{
++ int val;
++ int old;
++
++ if (!window->priv->need_update_workspace)
++ return;
++
++ window->priv->need_update_workspace = FALSE;
++
++ old = window->priv->workspace;
++
++ val = ALL_WORKSPACES;
++ _wnck_get_cardinal (window->priv->xwindow,
++ _wnck_atom_get ("_NET_WM_DESKTOP"),
++ &val);
++
++ window->priv->workspace = val;
++
++ if (old != window->priv->workspace)
++ emit_workspace_changed (window);
++}
++
++static void
++update_actions (WnckWindow *window)
++{
++ Atom *atoms;
++ int n_atoms;
++ int i;
++
++ if (!window->priv->need_update_actions)
++ return;
++
++ window->priv->need_update_actions = FALSE;
++
++ window->priv->actions = 0;
++
++ atoms = NULL;
++ n_atoms = 0;
++ if (!_wnck_get_atom_list (window->priv->xwindow,
++ _wnck_atom_get ("_NET_WM_ALLOWED_ACTIONS"),
++ &atoms,
++ &n_atoms))
++ {
++ window->priv->actions =
++ WNCK_WINDOW_ACTION_MOVE |
++ WNCK_WINDOW_ACTION_RESIZE |
++ WNCK_WINDOW_ACTION_SHADE |
++ WNCK_WINDOW_ACTION_STICK |
++ WNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY |
++ WNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY |
++ WNCK_WINDOW_ACTION_CHANGE_WORKSPACE |
++ WNCK_WINDOW_ACTION_CLOSE |
++ WNCK_WINDOW_ACTION_UNMAXIMIZE_HORIZONTALLY |
++ WNCK_WINDOW_ACTION_UNMAXIMIZE_VERTICALLY |
++ WNCK_WINDOW_ACTION_UNSHADE |
++ WNCK_WINDOW_ACTION_UNSTICK |
++ WNCK_WINDOW_ACTION_MINIMIZE |
++ WNCK_WINDOW_ACTION_UNMINIMIZE |
++ WNCK_WINDOW_ACTION_MAXIMIZE |
++ WNCK_WINDOW_ACTION_UNMAXIMIZE |
++ WNCK_WINDOW_ACTION_FULLSCREEN |
++ WNCK_WINDOW_ACTION_ABOVE |
++ WNCK_WINDOW_ACTION_BELOW;
++ return;
++ }
++
++ i = 0;
++ while (i < n_atoms)
++ {
++ if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_MOVE"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_MOVE;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_RESIZE"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_RESIZE;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_SHADE"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_SHADE |
++ WNCK_WINDOW_ACTION_UNSHADE;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_STICK"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_STICK |
++ WNCK_WINDOW_ACTION_UNSTICK;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_MINIMIZE"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_MINIMIZE |
++ WNCK_WINDOW_ACTION_UNMINIMIZE;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_MAXIMIZE_HORZ"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY |
++ WNCK_WINDOW_ACTION_UNMAXIMIZE_HORIZONTALLY;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_MAXIMIZE_VERT"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY |
++ WNCK_WINDOW_ACTION_UNMAXIMIZE_VERTICALLY;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_CHANGE_DESKTOP"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_CHANGE_WORKSPACE;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_CLOSE"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_CLOSE;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_FULLSCREEN"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_FULLSCREEN;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_ABOVE"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_ABOVE;
++
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_ACTION_BELOW"))
++ window->priv->actions |= WNCK_WINDOW_ACTION_BELOW;
++
++ else
++ {
++ const char *name = _wnck_atom_name (atoms [i]);
++ g_warning ("Unhandled action type %s", name ? name: "(nil)");
++ }
++
++ i++;
++ }
++
++ g_free (atoms);
++
++ if ((window->priv->actions & WNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY) &&
++ (window->priv->actions & WNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY))
++ window->priv->actions |=
++ WNCK_WINDOW_ACTION_MAXIMIZE |
++ WNCK_WINDOW_ACTION_UNMAXIMIZE;
++}
++
++static void
++update_wintype (WnckWindow *window)
++{
++ Atom *atoms;
++ int n_atoms;
++ WnckWindowType type;
++ gboolean found_type;
++
++ if (!window->priv->need_update_wintype)
++ return;
++
++ window->priv->need_update_wintype = FALSE;
++
++ found_type = FALSE;
++ type = WNCK_WINDOW_NORMAL;
++
++ atoms = NULL;
++ n_atoms = 0;
++ if (_wnck_get_atom_list (window->priv->xwindow,
++ _wnck_atom_get ("_NET_WM_WINDOW_TYPE"),
++ &atoms,
++ &n_atoms))
++ {
++ int i;
++
++ i = 0;
++ while (i < n_atoms && !found_type)
++ {
++ /* We break as soon as we find one we recognize,
++ * supposed to prefer those near the front of the list
++ */
++ found_type = TRUE;
++ if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_DESKTOP"))
++ type = WNCK_WINDOW_DESKTOP;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_DOCK"))
++ type = WNCK_WINDOW_DOCK;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_TOOLBAR"))
++ type = WNCK_WINDOW_TOOLBAR;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_MENU"))
++ type = WNCK_WINDOW_MENU;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_DIALOG"))
++ type = WNCK_WINDOW_DIALOG;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_NORMAL"))
++ type = WNCK_WINDOW_NORMAL;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_UTILITY"))
++ type = WNCK_WINDOW_UTILITY;
++ else if (atoms[i] == _wnck_atom_get ("_NET_WM_WINDOW_TYPE_SPLASH"))
++ type = WNCK_WINDOW_SPLASHSCREEN;
++ else
++ found_type = FALSE;
++
++ ++i;
++ }
++
++ g_free (atoms);
++ }
++
++ if (!found_type)
++ {
++ if (window->priv->transient_for != None)
++ {
++ type = WNCK_WINDOW_DIALOG;
++ }
++ else
++ {
++ type = WNCK_WINDOW_NORMAL;
++ }
++ found_type = TRUE;
++ }
++
++ window->priv->wintype = type;
++}
++
++static void
++update_transient_for (WnckWindow *window)
++{
++ Window parent;
++
++ if (!window->priv->need_update_transient_for)
++ return;
++
++ window->priv->need_update_transient_for = FALSE;
++
++ parent = None;
++ if (_wnck_get_window (window->priv->xwindow,
++ _wnck_atom_get ("WM_TRANSIENT_FOR"),
++ &parent) &&
++ parent != window->priv->xwindow)
++ {
++ window->priv->transient_for = parent;
++
++ if (wnck_screen_get_for_root (window->priv->transient_for) != NULL)
++ window->priv->transient_for_root = TRUE;
++ else
++ window->priv->transient_for_root = FALSE;
++ }
++ else
++ {
++ window->priv->transient_for = None;
++ window->priv->transient_for_root = FALSE;
++ }
++}
++
++static void
++update_startup_id (WnckWindow *window)
++{
++ if (!window->priv->need_update_startup_id)
++ return;
++
++ window->priv->need_update_startup_id = FALSE;
++
++ g_free (window->priv->startup_id);
++ window->priv->startup_id =
++ _wnck_get_utf8_property (window->priv->xwindow,
++ _wnck_atom_get ("_NET_STARTUP_ID"));
++}
++
++static void
++update_wmclass (WnckWindow *window)
++{
++ if (!window->priv->need_update_wmclass)
++ return;
++
++ window->priv->need_update_wmclass = FALSE;
++
++ g_free (window->priv->res_class);
++ g_free (window->priv->res_name);
++
++ window->priv->res_class = NULL;
++ window->priv->res_name = NULL;
++
++ _wnck_get_wmclass (window->priv->xwindow,
++ &window->priv->res_class,
++ &window->priv->res_name);
++}
++
++static void
++update_wmhints (WnckWindow *window)
++{
++ XWMHints *hints;
++
++ if (!window->priv->need_update_wmhints)
++ return;
++
++ _wnck_error_trap_push ();
++ hints = XGetWMHints (gdk_display, window->priv->xwindow);
++ _wnck_error_trap_pop ();
++
++ if (hints)
++ {
++ if ((hints->flags & IconPixmapHint) ||
++ (hints->flags & IconMaskHint))
++ _wnck_icon_cache_property_changed (window->priv->icon_cache,
++ _wnck_atom_get ("WM_HINTS"));
++
++ if (hints->flags & WindowGroupHint)
++ window->priv->group_leader = hints->window_group;
++
++ if (hints->flags & XUrgencyHint)
++ {
++ window->priv->is_urgent = TRUE;
++ time (&window->priv->needs_attention_time);
++ }
++ else
++ {
++ window->priv->is_urgent = FALSE;
++ if (!window->priv->demands_attention)
++ window->priv->needs_attention_time = 0;
++ }
++
++ XFree (hints);
++ }
++
++ window->priv->need_update_wmhints = FALSE;
++}
++
++static void
++update_frame_extents (WnckWindow *window)
++{
++ int left, right, top, bottom;
++
++ if (!window->priv->need_update_frame_extents)
++ return;
++
++ window->priv->need_update_frame_extents = FALSE;
++
++ left = right = top = bottom = 0;
++
++ if (!_wnck_get_frame_extents (window->priv->xwindow,
++ &left, &right, &top, &bottom))
++ return;
++
++ if (left != window->priv->left_frame ||
++ right != window->priv->right_frame ||
++ top != window->priv->top_frame ||
++ bottom != window->priv->bottom_frame)
++ {
++ window->priv->left_frame = left;
++ window->priv->right_frame = right;
++ window->priv->top_frame = top;
++ window->priv->bottom_frame = bottom;
++
++ emit_geometry_changed (window);
++ }
++}
++
++static void
++force_update_now (WnckWindow *window)
++{
++ WnckWindowState old_state;
++ WnckWindowState new_state;
++ WnckWindowActions old_actions;
++
++ unqueue_update (window);
++
++ /* Name must be done before all other stuff,
++ * because we have iconsistent state across the
++ * update_name/update_icon_name functions (no window name),
++ * and we have to fix that before we emit any other signals
++ */
++
++ update_name (window);
++ update_icon_name (window);
++
++ if (window->priv->need_emit_name_changed)
++ emit_name_changed (window);
++
++ old_state = COMPRESS_STATE (window);
++ old_actions = window->priv->actions;
++
++ update_startup_id (window); /* no side effects */
++ update_wmclass (window);
++ update_wmhints (window);
++ update_transient_for (window); /* wintype needs this to be first */
++ update_wintype (window);
++ update_wm_state (window);
++ update_state (window); /* must come after the above, since they affect
++ * our calculated state
++ */
++ update_workspace (window); /* emits signals */
++ update_actions (window);
++ update_frame_extents (window); /* emits signals */
++
++ get_icons (window);
++
++ new_state = COMPRESS_STATE (window);
++
++ if (old_state != new_state)
++ emit_state_changed (window, old_state ^ new_state, new_state);
++
++ if (old_actions != window->priv->actions)
++ emit_actions_changed (window, old_actions ^ window->priv->actions,
++ window->priv->actions);
++
++ if (window->priv->need_emit_icon_changed)
++ emit_icon_changed (window);
++}
++
++
++static gboolean
++update_idle (gpointer data)
++{
++ WnckWindow *window = WNCK_WINDOW (data);
++
++ window->priv->update_handler = 0;
++ force_update_now (window);
++ return FALSE;
++}
++
++static void
++queue_update (WnckWindow *window)
++{
++ if (window->priv->update_handler != 0)
++ return;
++
++ window->priv->update_handler = g_idle_add (update_idle, window);
++}
++
++static void
++unqueue_update (WnckWindow *window)
++{
++ if (window->priv->update_handler != 0)
++ {
++ g_source_remove (window->priv->update_handler);
++ window->priv->update_handler = 0;
++ }
++}
++
++static void
++emit_name_changed (WnckWindow *window)
++{
++ window->priv->need_emit_name_changed = FALSE;
++ g_signal_emit (G_OBJECT (window),
++ signals[NAME_CHANGED],
++ 0);
++}
++
++static void
++emit_state_changed (WnckWindow *window,
++ WnckWindowState changed_mask,
++ WnckWindowState new_state)
++{
++ g_signal_emit (G_OBJECT (window),
++ signals[STATE_CHANGED],
++ 0, changed_mask, new_state);
++}
++
++static void
++emit_workspace_changed (WnckWindow *window)
++{
++ g_signal_emit (G_OBJECT (window),
++ signals[WORKSPACE_CHANGED],
++ 0);
++}
++
++static void
++emit_icon_changed (WnckWindow *window)
++{
++ window->priv->need_emit_icon_changed = FALSE;
++ g_signal_emit (G_OBJECT (window),
++ signals[ICON_CHANGED],
++ 0);
++}
++
++static void
++emit_actions_changed (WnckWindow *window,
++ WnckWindowActions changed_mask,
++ WnckWindowActions new_actions)
++{
++ g_signal_emit (G_OBJECT (window),
++ signals[ACTIONS_CHANGED],
++ 0, changed_mask, new_actions);
++}
++
++static void
++emit_geometry_changed (WnckWindow *window)
++{
++ g_signal_emit (G_OBJECT (window),
++ signals[GEOMETRY_CHANGED],
++ 0);
++}
+diff -urN libwnck.orig/libwnck/window.h libwnck.new/libwnck/window.h
+--- libwnck.orig/libwnck/window.h 2007-11-30 14:02:04.860300000 +0000
++++ libwnck.new/libwnck/window.h 2007-11-30 14:02:34.635921000 +0000
+@@ -31,6 +31,7 @@
#include <glib-object.h>
#include <libwnck/screen.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
@@ -2180,7 +15414,7 @@
G_BEGIN_DECLS
-@@ -285,6 +286,14 @@ gboolean wnck_window_has_name (W
+@@ -289,6 +290,14 @@
const char* wnck_window_get_name (WnckWindow *window);
gboolean wnck_window_has_icon_name (WnckWindow *window);
const char* wnck_window_get_icon_name (WnckWindow *window);
@@ -2195,7 +15429,7 @@
WnckApplication* wnck_window_get_application (WnckWindow *window);
WnckWindow* wnck_window_get_transient (WnckWindow *window);
-@@ -316,6 +325,9 @@ gboolean wnck_window_is_fullscreen
+@@ -321,6 +330,9 @@
gboolean wnck_window_is_sticky (WnckWindow *window);
gboolean wnck_window_needs_attention (WnckWindow *window);
gboolean wnck_window_or_transient_needs_attention (WnckWindow *window);
@@ -2205,9 +15439,425 @@
void wnck_window_set_skip_pager (WnckWindow *window,
gboolean skip);
-diff -Nrup libwnck-2.19.4/libwnck/wnck-tsol.c ../libwnck-2.19.4/libwnck/wnck-tsol.c
---- libwnck-2.19.4/libwnck/wnck-tsol.c 1970-01-01 01:00:00.000000000 +0100
-+++ ../libwnck-2.19.4/libwnck/wnck-tsol.c 2007-06-27 15:08:01.842978000 +0200
+diff -urN libwnck.orig/libwnck/window.h.orig libwnck.new/libwnck/window.h.orig
+--- libwnck.orig/libwnck/window.h.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/window.h.orig 2007-11-30 14:02:08.624624000 +0000
+@@ -0,0 +1,412 @@
++/* window object */
++/* vim: set sw=2 et: */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2006-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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.
++ */
++
++#ifndef WNCK_WINDOW_H
++#define WNCK_WINDOW_H
++
++#ifndef WNCK_I_KNOW_THIS_IS_UNSTABLE
++#error "libwnck should only be used if you understand that it's subject to frequent change, and is not supported as a fixed API/ABI or as part of the platform"
++#endif
++
++#include <glib-object.h>
++#include <libwnck/screen.h>
++#include <gdk-pixbuf/gdk-pixbuf.h>
++
++G_BEGIN_DECLS
++
++/**
++ * WnckWindowState:
++ * @WNCK_WINDOW_STATE_MINIMIZED: the window is minimized.
++ * @WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY: the window is horizontically
++ * maximized.
++ * @WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY: the window is vertically maximized.
++ * @WNCK_WINDOW_STATE_SHADED: the window is shaded.
++ * @WNCK_WINDOW_STATE_SKIP_PAGER: the window should not be included on pagers.
++ * @WNCK_WINDOW_STATE_SKIP_TASKLIST: the window should not be included on
++ * tasklists.
++ * @WNCK_WINDOW_STATE_STICKY: the window is sticky (see
++ * wnck_window_is_sticky()).
++ * @WNCK_WINDOW_STATE_HIDDEN: the window is not visible on its #WnckWorkspace
++ * and viewport (when minimized, for example).
++ * @WNCK_WINDOW_STATE_FULLSCREEN: the window is fullscreen.
++ * @WNCK_WINDOW_STATE_DEMANDS_ATTENTION: the window needs attention (because
++ * the window requested activation but the window manager refused it, for
++ * example).
++ * @WNCK_WINDOW_STATE_URGENT: the window requires a response from the user.
++ * @WNCK_WINDOW_STATE_ABOVE: the window is above other windows (see
++ * wnck_window_make_above()).
++ * @WNCK_WINDOW_STATE_BELOW: the window is below other windows (see
++ * wnck_window_make_below()).
++ *
++ * Type used as a bitmask to describe the state of a #WnckWindow.
++ */
++typedef enum
++{
++ WNCK_WINDOW_STATE_MINIMIZED = 1 << 0,
++ WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY = 1 << 1,
++ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY = 1 << 2,
++ WNCK_WINDOW_STATE_SHADED = 1 << 3,
++ WNCK_WINDOW_STATE_SKIP_PAGER = 1 << 4,
++ WNCK_WINDOW_STATE_SKIP_TASKLIST = 1 << 5,
++ WNCK_WINDOW_STATE_STICKY = 1 << 6,
++ WNCK_WINDOW_STATE_HIDDEN = 1 << 7,
++ WNCK_WINDOW_STATE_FULLSCREEN = 1 << 8,
++ WNCK_WINDOW_STATE_DEMANDS_ATTENTION = 1 << 9,
++ WNCK_WINDOW_STATE_URGENT = 1 << 10,
++ WNCK_WINDOW_STATE_ABOVE = 1 << 11,
++ WNCK_WINDOW_STATE_BELOW = 1 << 12
++} WnckWindowState;
++
++/**
++ * WnckWindowActions:
++ * @WNCK_WINDOW_ACTION_MOVE: the window may be moved around the screen.
++ * @WNCK_WINDOW_ACTION_RESIZE: the window may be resized.
++ * @WNCK_WINDOW_ACTION_SHADE: the window may be shaded.
++ * @WNCK_WINDOW_ACTION_STICK: the window may be sticked.
++ * @WNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY: the window may be maximized
++ * horizontally.
++ * @WNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY: the window may be maximized
++ * vertically.
++ * @WNCK_WINDOW_ACTION_CHANGE_WORKSPACE: the window may be moved between
++ * workspaces, or (un)pinned.
++ * @WNCK_WINDOW_ACTION_CLOSE: the window may be closed.
++ * @WNCK_WINDOW_ACTION_UNMAXIMIZE_HORIZONTALLY: the window may be unmaximized
++ * horizontally.
++ * @WNCK_WINDOW_ACTION_UNMAXIMIZE_VERTICALLY: the window may be maximized
++ * vertically.
++ * @WNCK_WINDOW_ACTION_UNSHADE: the window may be unshaded.
++ * @WNCK_WINDOW_ACTION_UNSTICK: the window may be unsticked.
++ * @WNCK_WINDOW_ACTION_MINIMIZE: the window may be minimized.
++ * @WNCK_WINDOW_ACTION_UNMINIMIZE: the window may be unminimized.
++ * @WNCK_WINDOW_ACTION_MAXIMIZE: the window may be maximized.
++ * @WNCK_WINDOW_ACTION_UNMAXIMIZE: the window may be unmaximized.
++ * @WNCK_WINDOW_ACTION_FULLSCREEN: the window may be brought to fullscreen.
++ * @WNCK_WINDOW_ACTION_ABOVE: the window may be made above other windows.
++ * @WNCK_WINDOW_ACTION_BELOW: the window may be made below other windows.
++ *
++ * Type used as a bitmask to describe the actions that can be done for a
++ * #WnckWindow.
++ */
++typedef enum
++{
++ WNCK_WINDOW_ACTION_MOVE = 1 << 0,
++ WNCK_WINDOW_ACTION_RESIZE = 1 << 1,
++ WNCK_WINDOW_ACTION_SHADE = 1 << 2,
++ WNCK_WINDOW_ACTION_STICK = 1 << 3,
++ WNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY = 1 << 4,
++ WNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY = 1 << 5,
++ WNCK_WINDOW_ACTION_CHANGE_WORKSPACE = 1 << 6, /* includes pin/unpin */
++ WNCK_WINDOW_ACTION_CLOSE = 1 << 7,
++ WNCK_WINDOW_ACTION_UNMAXIMIZE_HORIZONTALLY = 1 << 8,
++ WNCK_WINDOW_ACTION_UNMAXIMIZE_VERTICALLY = 1 << 9,
++ WNCK_WINDOW_ACTION_UNSHADE = 1 << 10,
++ WNCK_WINDOW_ACTION_UNSTICK = 1 << 11,
++ WNCK_WINDOW_ACTION_MINIMIZE = 1 << 12,
++ WNCK_WINDOW_ACTION_UNMINIMIZE = 1 << 13,
++ WNCK_WINDOW_ACTION_MAXIMIZE = 1 << 14,
++ WNCK_WINDOW_ACTION_UNMAXIMIZE = 1 << 15,
++ WNCK_WINDOW_ACTION_FULLSCREEN = 1 << 16,
++ WNCK_WINDOW_ACTION_ABOVE = 1 << 17,
++ WNCK_WINDOW_ACTION_BELOW = 1 << 18
++} WnckWindowActions;
++
++/**
++ * WnckWindowType:
++ * @WNCK_WINDOW_NORMAL: the window is a normal window.
++ * @WNCK_WINDOW_DESKTOP: the window is a desktop.
++ * @WNCK_WINDOW_DOCK: the window is a dock or a panel.
++ * @WNCK_WINDOW_DIALOG: the window is a dialog window.
++ * @WNCK_WINDOW_TOOLBAR: the window is a tearoff toolbar.
++ * @WNCK_WINDOW_MENU: the window is a tearoff menu.
++ * @WNCK_WINDOW_UTILITY: the window is a small persistent utility window, such
++ * as a palette or toolbox.
++ * @WNCK_WINDOW_SPLASHSCREEN: the window is a splash screen displayed as an
++ * application is starting up.
++ *
++ * Type describing the semantic type of a #WnckWindow.
++ */
++typedef enum
++{
++ WNCK_WINDOW_NORMAL, /* document/app window */
++ WNCK_WINDOW_DESKTOP, /* desktop background */
++ WNCK_WINDOW_DOCK, /* panel */
++ WNCK_WINDOW_DIALOG, /* dialog */
++ WNCK_WINDOW_TOOLBAR, /* tearoff toolbar */
++ WNCK_WINDOW_MENU, /* tearoff menu */
++ WNCK_WINDOW_UTILITY, /* palette/toolbox window */
++ WNCK_WINDOW_SPLASHSCREEN /* splash screen */
++} WnckWindowType;
++
++/**
++ * WnckWindowGravity:
++ * @WNCK_WINDOW_GRAVITY_CURRENT: keep the current gravity point.
++ * @WNCK_WINDOW_GRAVITY_NORTHWEST: use the left top corner of the frame window
++ * as gravity point.
++ * @WNCK_WINDOW_GRAVITY_NORTH: use the center of the frame window's top side as
++ * gravity point.
++ * @WNCK_WINDOW_GRAVITY_NORTHEAST: use the right top corner of the frame window
++ * as gravity point.
++ * @WNCK_WINDOW_GRAVITY_WEST: use the center of the frame window's left side as
++ * gravity point.
++ * @WNCK_WINDOW_GRAVITY_CENTER: use the center of the frame window as gravity
++ * point.
++ * @WNCK_WINDOW_GRAVITY_EAST: use the center of the frame window's right side
++ * as gravity point.
++ * @WNCK_WINDOW_GRAVITY_SOUTHWEST: use the left bottom corner of the frame
++ * window as gravity point.
++ * @WNCK_WINDOW_GRAVITY_SOUTH: use the center of the frame window's bottom side
++ * as gravity point.
++ * @WNCK_WINDOW_GRAVITY_SOUTHEAST: use the right bottom corner of the frame
++ * window as gravity point.
++ * @WNCK_WINDOW_GRAVITY_STATIC: use the left top corner of the client window as
++ * gravity point.
++ *
++ * Flag used when changing the geometry of a #WnckWindow. This is the gravity
++ * point to use as a reference for the new position.
++ *
++ * Since: 2.16
++ */
++typedef enum
++{
++ WNCK_WINDOW_GRAVITY_CURRENT = 0,
++ WNCK_WINDOW_GRAVITY_NORTHWEST = 1,
++ WNCK_WINDOW_GRAVITY_NORTH = 2,
++ WNCK_WINDOW_GRAVITY_NORTHEAST = 3,
++ WNCK_WINDOW_GRAVITY_WEST = 4,
++ WNCK_WINDOW_GRAVITY_CENTER = 5,
++ WNCK_WINDOW_GRAVITY_EAST = 6,
++ WNCK_WINDOW_GRAVITY_SOUTHWEST = 7,
++ WNCK_WINDOW_GRAVITY_SOUTH = 8,
++ WNCK_WINDOW_GRAVITY_SOUTHEAST = 9,
++ WNCK_WINDOW_GRAVITY_STATIC = 10
++} WnckWindowGravity;
++
++/**
++ * WnckWindowMoveResizeMask:
++ * @WNCK_WINDOW_CHANGE_X: X coordinate of the window should be changed.
++ * @WNCK_WINDOW_CHANGE_Y: Y coordinate of the window should be changed.
++ * @WNCK_WINDOW_CHANGE_WIDTH: width of the window should be changed.
++ * @WNCK_WINDOW_CHANGE_HEIGHT: height of the window should be changed.
++ *
++ * Flag used as a bitmask when changing the geometry of a #WnckWindow. This
++ * indicates which part of the geometry should be changed.
++ *
++ * Since: 2.16
++ */
++typedef enum
++{
++ WNCK_WINDOW_CHANGE_X = 1 << 0,
++ WNCK_WINDOW_CHANGE_Y = 1 << 1,
++ WNCK_WINDOW_CHANGE_WIDTH = 1 << 2,
++ WNCK_WINDOW_CHANGE_HEIGHT = 1 << 3
++} WnckWindowMoveResizeMask;
++
++#define WNCK_TYPE_WINDOW (wnck_window_get_type ())
++#define WNCK_WINDOW(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WNCK_TYPE_WINDOW, WnckWindow))
++#define WNCK_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WNCK_TYPE_WINDOW, WnckWindowClass))
++#define WNCK_IS_WINDOW(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WNCK_TYPE_WINDOW))
++#define WNCK_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WNCK_TYPE_WINDOW))
++#define WNCK_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WNCK_TYPE_WINDOW, WnckWindowClass))
++
++typedef struct _WnckWindowClass WnckWindowClass;
++typedef struct _WnckWindowPrivate WnckWindowPrivate;
++
++/**
++ * WnckWindow:
++ *
++ * The #WnckWindow struct contains only private fields and should not be
++ * directly accessed.
++ */
++struct _WnckWindow
++{
++ GObject parent_instance;
++
++ WnckWindowPrivate *priv;
++};
++
++struct _WnckWindowClass
++{
++ GObjectClass parent_class;
++
++ /* window name or icon name changed */
++ void (* name_changed) (WnckWindow *window);
++
++ /* minimized, maximized, sticky, skip pager, skip task, shaded
++ * may have changed
++ */
++ void (* state_changed) (WnckWindow *window,
++ WnckWindowState changed_mask,
++ WnckWindowState new_state);
++
++ /* Changed workspace or pinned/unpinned state */
++ void (* workspace_changed) (WnckWindow *window);
++
++ /* Changed icon */
++ void (* icon_changed) (WnckWindow *window);
++
++ /* Changed actions */
++ void (* actions_changed) (WnckWindow *window,
++ WnckWindowActions changed_mask,
++ WnckWindowActions new_actions);
++
++ /* Changed size/position */
++ void (* geometry_changed) (WnckWindow *window);
++
++ /* Padding for future expansion */
++ void (* pad1) (void);
++ void (* pad2) (void);
++ void (* pad3) (void);
++ void (* pad4) (void);
++};
++
++GType wnck_window_get_type (void) G_GNUC_CONST;
++
++WnckWindow* wnck_window_get (gulong xwindow);
++
++WnckScreen* wnck_window_get_screen (WnckWindow *window);
++
++gboolean wnck_window_has_name (WnckWindow *window);
++const char* wnck_window_get_name (WnckWindow *window);
++gboolean wnck_window_has_icon_name (WnckWindow *window);
++const char* wnck_window_get_icon_name (WnckWindow *window);
++
++WnckApplication* wnck_window_get_application (WnckWindow *window);
++WnckWindow* wnck_window_get_transient (WnckWindow *window);
++gulong wnck_window_get_group_leader (WnckWindow *window);
++gulong wnck_window_get_xid (WnckWindow *window);
++
++WnckClassGroup *wnck_window_get_class_group (WnckWindow *window);
++
++const char* wnck_window_get_session_id (WnckWindow *window);
++const char* wnck_window_get_session_id_utf8 (WnckWindow *window);
++int wnck_window_get_pid (WnckWindow *window);
++gint wnck_window_get_sort_order (WnckWindow *window);
++void wnck_window_set_sort_order (WnckWindow *window,
++ gint order);
++
++WnckWindowType wnck_window_get_window_type (WnckWindow *window);
++void wnck_window_set_window_type (WnckWindow *window,
++ WnckWindowType wintype);
++
++gboolean wnck_window_is_minimized (WnckWindow *window);
++gboolean wnck_window_is_maximized_horizontally (WnckWindow *window);
++gboolean wnck_window_is_maximized_vertically (WnckWindow *window);
++gboolean wnck_window_is_maximized (WnckWindow *window);
++gboolean wnck_window_is_shaded (WnckWindow *window);
++gboolean wnck_window_is_above (WnckWindow *window);
++gboolean wnck_window_is_below (WnckWindow *window);
++gboolean wnck_window_is_skip_pager (WnckWindow *window);
++gboolean wnck_window_is_skip_tasklist (WnckWindow *window);
++gboolean wnck_window_is_fullscreen (WnckWindow *window);
++gboolean wnck_window_is_sticky (WnckWindow *window);
++gboolean wnck_window_needs_attention (WnckWindow *window);
++gboolean wnck_window_or_transient_needs_attention (WnckWindow *window);
++
++void wnck_window_set_skip_pager (WnckWindow *window,
++ gboolean skip);
++void wnck_window_set_skip_tasklist (WnckWindow *window,
++ gboolean skip);
++void wnck_window_set_fullscreen (WnckWindow *window,
++ gboolean fullscreen);
++
++void wnck_window_close (WnckWindow *window,
++ guint32 timestamp);
++void wnck_window_minimize (WnckWindow *window);
++void wnck_window_unminimize (WnckWindow *window,
++ guint32 timestamp);
++void wnck_window_maximize (WnckWindow *window);
++void wnck_window_unmaximize (WnckWindow *window);
++void wnck_window_maximize_horizontally (WnckWindow *window);
++void wnck_window_unmaximize_horizontally (WnckWindow *window);
++void wnck_window_maximize_vertically (WnckWindow *window);
++void wnck_window_unmaximize_vertically (WnckWindow *window);
++void wnck_window_shade (WnckWindow *window);
++void wnck_window_unshade (WnckWindow *window);
++void wnck_window_make_above (WnckWindow *window);
++void wnck_window_unmake_above (WnckWindow *window);
++void wnck_window_make_below (WnckWindow *window);
++void wnck_window_unmake_below (WnckWindow *window);
++void wnck_window_stick (WnckWindow *window);
++void wnck_window_unstick (WnckWindow *window);
++void wnck_window_keyboard_move (WnckWindow *window);
++void wnck_window_keyboard_size (WnckWindow *window);
++
++WnckWorkspace* wnck_window_get_workspace (WnckWindow *window);
++void wnck_window_move_to_workspace (WnckWindow *window,
++ WnckWorkspace *space);
++
++/* pinned = on all workspaces */
++gboolean wnck_window_is_pinned (WnckWindow *window);
++void wnck_window_pin (WnckWindow *window);
++void wnck_window_unpin (WnckWindow *window);
++
++void wnck_window_activate (WnckWindow *window,
++ guint32 timestamp);
++gboolean wnck_window_is_active (WnckWindow *window);
++gboolean wnck_window_is_most_recently_activated (WnckWindow *window);
++void wnck_window_activate_transient (WnckWindow *window,
++ guint32 timestamp);
++gboolean wnck_window_transient_is_most_recently_activated (WnckWindow *window);
++
++GdkPixbuf* wnck_window_get_icon (WnckWindow *window);
++GdkPixbuf* wnck_window_get_mini_icon (WnckWindow *window);
++
++gboolean wnck_window_get_icon_is_fallback (WnckWindow *window);
++
++void wnck_window_set_icon_geometry (WnckWindow *window,
++ int x,
++ int y,
++ int width,
++ int height);
++
++WnckWindowActions wnck_window_get_actions (WnckWindow *window);
++WnckWindowState wnck_window_get_state (WnckWindow *window);
++
++void wnck_window_get_client_window_geometry (WnckWindow *window,
++ int *xp,
++ int *yp,
++ int *widthp,
++ int *heightp);
++void wnck_window_get_geometry (WnckWindow *window,
++ int *xp,
++ int *yp,
++ int *widthp,
++ int *heightp);
++void wnck_window_set_geometry (WnckWindow *window,
++ WnckWindowGravity gravity,
++ WnckWindowMoveResizeMask geometry_mask,
++ int x,
++ int y,
++ int width,
++ int height);
++
++gboolean wnck_window_is_visible_on_workspace (WnckWindow *window,
++ WnckWorkspace *workspace);
++gboolean wnck_window_is_on_workspace (WnckWindow *window,
++ WnckWorkspace *workspace);
++gboolean wnck_window_is_in_viewport (WnckWindow *window,
++ WnckWorkspace *workspace);
++
++G_END_DECLS
++
++#endif /* WNCK_WINDOW_H */
+diff -urN libwnck.orig/libwnck/wnck-tsol.c libwnck.new/libwnck/wnck-tsol.c
+--- libwnck.orig/libwnck/wnck-tsol.c 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/wnck-tsol.c 2007-11-30 14:02:34.636608000 +0000
@@ -0,0 +1,289 @@
+#include <config.h>
+#ifdef HAVE_XTSOL
@@ -2498,9 +16148,9 @@
+
+
+#endif /* HAVE_XTSOL */
-diff -Nrup libwnck-2.19.4/libwnck/wnck-tsol.h ../libwnck-2.19.4/libwnck/wnck-tsol.h
---- libwnck-2.19.4/libwnck/wnck-tsol.h 1970-01-01 01:00:00.000000000 +0100
-+++ ../libwnck-2.19.4/libwnck/wnck-tsol.h 2007-06-27 15:08:01.843247000 +0200
+diff -urN libwnck.orig/libwnck/wnck-tsol.h libwnck.new/libwnck/wnck-tsol.h
+--- libwnck.orig/libwnck/wnck-tsol.h 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/wnck-tsol.h 2007-11-30 14:02:34.637014000 +0000
@@ -0,0 +1,133 @@
+#ifndef __WNCK_TSOL_H__
+#define __WNCK_TSOL_H__
@@ -2635,8 +16285,9 @@
+
+#endif /* HAVE_XTSOL */
+#endif
---- libwnck-2.20.0/libwnck/workspace.c 2007-09-18 02:36:09.000000000 +0800
-+++ libwnck-2.20.0.new/libwnck/workspace.c 2007-10-22 13:21:01.685347000 +0800
+diff -urN libwnck.orig/libwnck/workspace.c libwnck.new/libwnck/workspace.c
+--- libwnck.orig/libwnck/workspace.c 2007-11-30 14:02:04.937282000 +0000
++++ libwnck.new/libwnck/workspace.c 2007-11-30 14:02:34.638231000 +0000
@@ -26,6 +26,16 @@
#include <config.h>
@@ -3208,10 +16859,10 @@
+ return space->priv->ws_range;
+}
+#endif
-diff -Nrup libwnck-2.19.5/libwnck/workspace.h ../libwnck-2.19.5/libwnck/workspace.h
---- libwnck-2.19.5/libwnck/workspace.h 2007-06-18 21:59:15.000000000 +0200
-+++ ../libwnck-2.19.5/libwnck/workspace.h 2007-06-27 15:08:01.844706000 +0200
-@@ -56,6 +56,11 @@ struct _WnckWorkspaceClass
+diff -urN libwnck.orig/libwnck/workspace.h libwnck.new/libwnck/workspace.h
+--- libwnck.orig/libwnck/workspace.h 2007-11-30 14:02:04.860568000 +0000
++++ libwnck.new/libwnck/workspace.h 2007-11-30 14:02:34.638948000 +0000
+@@ -57,6 +57,11 @@
GObjectClass parent_class;
void (* name_changed) (WnckWorkspace *space);
@@ -3223,7 +16874,7 @@
/* Padding for future expansion */
void (* pad1) (void);
-@@ -73,9 +73,22 @@
+@@ -69,9 +74,22 @@
int wnck_workspace_get_number (WnckWorkspace *space);
const char* wnck_workspace_get_name (WnckWorkspace *space);
@@ -3246,10 +16897,105 @@
void wnck_workspace_activate (WnckWorkspace *space,
guint32 timestamp);
int wnck_workspace_get_width (WnckWorkspace *space);
-diff -Nrup libwnck-2.19.4/libwnck/xutils.c ../libwnck-2.19.4/libwnck/xutils.c
---- libwnck-2.19.4/libwnck/xutils.c 2007-06-18 21:59:14.000000000 +0200
-+++ ../libwnck-2.19.4/libwnck/xutils.c 2007-06-27 15:08:01.845562000 +0200
-@@ -26,6 +26,7 @@
+diff -urN libwnck.orig/libwnck/workspace.h.orig libwnck.new/libwnck/workspace.h.orig
+--- libwnck.orig/libwnck/workspace.h.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/workspace.h.orig 2007-11-30 14:02:08.624896000 +0000
+@@ -0,0 +1,91 @@
++/* workspace object */
++/* vim: set sw=2 et: */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2006-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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.
++ */
++
++#ifndef WNCK_WORKSPACE_H
++#define WNCK_WORKSPACE_H
++
++#include <glib-object.h>
++#include <libwnck/screen.h>
++
++G_BEGIN_DECLS
++
++#define WNCK_TYPE_WORKSPACE (wnck_workspace_get_type ())
++#define WNCK_WORKSPACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WNCK_TYPE_WORKSPACE, WnckWorkspace))
++#define WNCK_WORKSPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WNCK_TYPE_WORKSPACE, WnckWorkspaceClass))
++#define WNCK_IS_WORKSPACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WNCK_TYPE_WORKSPACE))
++#define WNCK_IS_WORKSPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WNCK_TYPE_WORKSPACE))
++#define WNCK_WORKSPACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WNCK_TYPE_WORKSPACE, WnckWorkspaceClass))
++
++typedef struct _WnckWorkspaceClass WnckWorkspaceClass;
++typedef struct _WnckWorkspacePrivate WnckWorkspacePrivate;
++
++/**
++ * WnckWorkspace:
++ *
++ * The #WnckWorkspace struct contains only private fields and should not be
++ * directly accessed.
++ */
++struct _WnckWorkspace
++{
++ GObject parent_instance;
++
++ WnckWorkspacePrivate *priv;
++};
++
++struct _WnckWorkspaceClass
++{
++ GObjectClass parent_class;
++
++ void (* name_changed) (WnckWorkspace *space);
++
++ /* Padding for future expansion */
++ void (* pad1) (void);
++ void (* pad2) (void);
++ void (* pad3) (void);
++ void (* pad4) (void);
++};
++
++GType wnck_workspace_get_type (void) G_GNUC_CONST;
++
++int wnck_workspace_get_number (WnckWorkspace *space);
++const char* wnck_workspace_get_name (WnckWorkspace *space);
++void wnck_workspace_change_name (WnckWorkspace *space,
++ const char *name);
++WnckScreen* wnck_workspace_get_screen (WnckWorkspace *space);
++void wnck_workspace_activate (WnckWorkspace *space,
++ guint32 timestamp);
++int wnck_workspace_get_width (WnckWorkspace *space);
++int wnck_workspace_get_height (WnckWorkspace *space);
++int wnck_workspace_get_viewport_x (WnckWorkspace *space);
++int wnck_workspace_get_viewport_y (WnckWorkspace *space);
++gboolean wnck_workspace_is_virtual (WnckWorkspace *space);
++
++
++int wnck_workspace_get_layout_row (WnckWorkspace *space);
++int wnck_workspace_get_layout_column (WnckWorkspace *space);
++WnckWorkspace* wnck_workspace_get_neighbor (WnckWorkspace *space,
++ WnckMotionDirection direction);
++
++G_END_DECLS
++
++#endif /* WNCK_WORKSPACE_H */
+diff -urN libwnck.orig/libwnck/xutils.c libwnck.new/libwnck/xutils.c
+--- libwnck.orig/libwnck/xutils.c 2007-11-30 14:02:04.938827000 +0000
++++ libwnck.new/libwnck/xutils.c 2007-11-30 14:02:34.640361000 +0000
+@@ -27,6 +27,7 @@
#include <stdio.h>
#include "screen.h"
#include "window.h"
@@ -3257,7 +17003,7 @@
#include "private.h"
#include "inlinepixbufs.h"
-@@ -230,6 +231,21 @@ _wnck_get_atom (Window xwindow,
+@@ -231,6 +232,21 @@
return TRUE;
}
@@ -3279,4 +17025,2699 @@
static char*
text_property_to_utf8 (const XTextProperty *prop)
{
-
+diff -urN libwnck.orig/libwnck/xutils.c.orig libwnck.new/libwnck/xutils.c.orig
+--- libwnck.orig/libwnck/xutils.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ libwnck.new/libwnck/xutils.c.orig 2007-11-30 14:02:08.628726000 +0000
+@@ -0,0 +1,2692 @@
++/* Xlib utils */
++/* vim: set sw=2 et: */
++
++/*
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2005-2007 Vincent Untz
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library 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 "xutils.h"
++#include <string.h>
++#include <stdio.h>
++#include "screen.h"
++#include "window.h"
++#include "private.h"
++#include "inlinepixbufs.h"
++
++gboolean
++_wnck_get_cardinal (Window xwindow,
++ Atom atom,
++ int *val)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ gulong *num;
++ int err, result;
++
++ *val = 0;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, XA_CARDINAL, &type, &format, &nitems,
++ &bytes_after, (void*)&num);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_CARDINAL)
++ {
++ XFree (num);
++ return FALSE;
++ }
++
++ *val = *num;
++
++ XFree (num);
++
++ return TRUE;
++}
++
++int
++_wnck_get_wm_state (Window xwindow)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ gulong *num;
++ int err, result;
++ Atom wm_state;
++ int retval;
++
++ wm_state = _wnck_atom_get ("WM_STATE");
++ retval = NormalState;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ wm_state,
++ 0, G_MAXLONG,
++ False, wm_state, &type, &format, &nitems,
++ &bytes_after, (void*)&num);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return retval;
++
++ if (type != wm_state)
++ {
++ XFree (num);
++ return retval;
++ }
++
++ retval = *num;
++
++ XFree (num);
++
++ return retval;
++}
++
++gboolean
++_wnck_get_window (Window xwindow,
++ Atom atom,
++ Window *val)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ Window *w;
++ int err, result;
++
++ *val = 0;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, XA_WINDOW, &type, &format, &nitems,
++ &bytes_after, (void*)&w);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_WINDOW)
++ {
++ XFree (w);
++ return FALSE;
++ }
++
++ *val = *w;
++
++ XFree (w);
++
++ return TRUE;
++}
++
++gboolean
++_wnck_get_pixmap (Window xwindow,
++ Atom atom,
++ Pixmap *val)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ Window *w;
++ int err, result;
++
++ *val = 0;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, XA_PIXMAP, &type, &format, &nitems,
++ &bytes_after, (void*)&w);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_PIXMAP)
++ {
++ XFree (w);
++ return FALSE;
++ }
++
++ *val = *w;
++
++ XFree (w);
++
++ return TRUE;
++}
++
++gboolean
++_wnck_get_atom (Window xwindow,
++ Atom atom,
++ Atom *val)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ Atom *a;
++ int err, result;
++
++ *val = 0;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, XA_ATOM, &type, &format, &nitems,
++ &bytes_after, (void*)&a);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_ATOM)
++ {
++ XFree (a);
++ return FALSE;
++ }
++
++ *val = *a;
++
++ XFree (a);
++
++ return TRUE;
++}
++
++static char*
++text_property_to_utf8 (const XTextProperty *prop)
++{
++ char **list;
++ int count;
++ char *retval;
++
++ list = NULL;
++
++ count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
++ prop->format,
++ prop->value,
++ prop->nitems,
++ &list);
++
++ if (count == 0)
++ retval = NULL;
++ else
++ {
++ retval = list[0];
++ list[0] = g_strdup (""); /* something to free */
++ }
++
++ g_strfreev (list);
++
++ return retval;
++}
++
++char*
++_wnck_get_text_property (Window xwindow,
++ Atom atom)
++{
++ XTextProperty text;
++ char *retval;
++
++ _wnck_error_trap_push ();
++
++ text.nitems = 0;
++ if (XGetTextProperty (gdk_display,
++ xwindow,
++ &text,
++ atom))
++ {
++ retval = text_property_to_utf8 (&text);
++
++ if (text.value)
++ XFree (text.value);
++ }
++ else
++ {
++ retval = NULL;
++ }
++
++ _wnck_error_trap_pop ();
++
++ return retval;
++}
++
++static char*
++_wnck_get_string_property_latin1 (Window xwindow,
++ Atom atom)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ gchar *str;
++ int err, result;
++ char *retval;
++
++ _wnck_error_trap_push ();
++ str = NULL;
++ result = XGetWindowProperty (gdk_display,
++ xwindow, atom,
++ 0, G_MAXLONG,
++ False, XA_STRING, &type, &format, &nitems,
++ &bytes_after, (guchar **)&str);
++
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return NULL;
++
++ if (type != XA_STRING)
++ {
++ XFree (str);
++ return NULL;
++ }
++
++ retval = g_strdup (str);
++
++ XFree (str);
++
++ return retval;
++}
++
++char*
++_wnck_get_utf8_property (Window xwindow,
++ Atom atom)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ gchar *val;
++ int err, result;
++ char *retval;
++ Atom utf8_string;
++
++ utf8_string = _wnck_atom_get ("UTF8_STRING");
++
++ _wnck_error_trap_push ();
++ type = None;
++ val = NULL;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, utf8_string,
++ &type, &format, &nitems,
++ &bytes_after, (guchar **)&val);
++ err = _wnck_error_trap_pop ();
++
++ if (err != Success ||
++ 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))
++ {
++ g_warning ("Property %s contained invalid UTF-8\n",
++ _wnck_atom_name (atom));
++ XFree (val);
++ return NULL;
++ }
++
++ retval = g_strndup (val, nitems);
++
++ XFree (val);
++
++ return retval;
++}
++
++gboolean
++_wnck_get_window_list (Window xwindow,
++ Atom atom,
++ Window **windows,
++ int *len)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ Window *data;
++ int err, result;
++
++ *windows = NULL;
++ *len = 0;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, XA_WINDOW, &type, &format, &nitems,
++ &bytes_after, (void*)&data);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_WINDOW)
++ {
++ XFree (data);
++ return FALSE;
++ }
++
++ *windows = g_new (Window, nitems);
++ memcpy (*windows, data, sizeof (Window) * nitems);
++ *len = nitems;
++
++ XFree (data);
++
++ return TRUE;
++}
++
++gboolean
++_wnck_get_atom_list (Window xwindow,
++ Atom atom,
++ Atom **atoms,
++ int *len)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ Atom *data;
++ int err, result;
++
++ *atoms = NULL;
++ *len = 0;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, XA_ATOM, &type, &format, &nitems,
++ &bytes_after, (void*)&data);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_ATOM)
++ {
++ XFree (data);
++ return FALSE;
++ }
++
++ *atoms = g_new (Atom, nitems);
++ memcpy (*atoms, data, sizeof (Atom) * nitems);
++ *len = nitems;
++
++ XFree (data);
++
++ return TRUE;
++}
++
++gboolean
++_wnck_get_cardinal_list (Window xwindow,
++ Atom atom,
++ gulong **cardinals,
++ int *len)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ gulong *nums;
++ int err, result;
++
++ *cardinals = NULL;
++ *len = 0;
++
++ _wnck_error_trap_push ();
++ type = None;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, XA_CARDINAL, &type, &format, &nitems,
++ &bytes_after, (void*)&nums);
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_CARDINAL)
++ {
++ XFree (nums);
++ return FALSE;
++ }
++
++ *cardinals = g_new (gulong, nitems);
++ memcpy (*cardinals, nums, sizeof (gulong) * nitems);
++ *len = nitems;
++
++ XFree (nums);
++
++ return TRUE;
++}
++
++char**
++_wnck_get_utf8_list (Window xwindow,
++ Atom atom)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ char *val;
++ int err, result;
++ Atom utf8_string;
++ char **retval;
++ guint i;
++ guint n_strings;
++ char *p;
++
++ utf8_string = _wnck_atom_get ("UTF8_STRING");
++
++ _wnck_error_trap_push ();
++ type = None;
++ val = NULL;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ atom,
++ 0, G_MAXLONG,
++ False, utf8_string,
++ &type, &format, &nitems,
++ &bytes_after, (void*)&val);
++ err = _wnck_error_trap_pop ();
++
++ if (err != Success ||
++ result != Success)
++ return NULL;
++
++ if (type != utf8_string ||
++ format != 8 ||
++ nitems == 0)
++ {
++ if (val)
++ XFree (val);
++ return NULL;
++ }
++
++ /* I'm not sure this is right, but I'm guessing the
++ * property is nul-separated
++ */
++ i = 0;
++ n_strings = 0;
++ while (i < nitems)
++ {
++ if (val[i] == '\0')
++ ++n_strings;
++ ++i;
++ }
++
++ if (val[nitems - 1] != '\0')
++ ++n_strings;
++
++ /* we're guaranteed that val has a nul on the end
++ * by XGetWindowProperty
++ */
++
++ retval = g_new0 (char*, n_strings + 1);
++
++ p = val;
++ i = 0;
++ while (i < n_strings)
++ {
++ if (!g_utf8_validate (p, -1, NULL))
++ {
++ g_warning ("Property %s contained invalid UTF-8\n",
++ _wnck_atom_name (atom));
++ XFree (val);
++ g_strfreev (retval);
++ return NULL;
++ }
++
++ retval[i] = g_strdup (p);
++
++ p = p + strlen (p) + 1;
++ ++i;
++ }
++
++ XFree (val);
++
++ return retval;
++}
++
++void
++_wnck_set_utf8_list (Window xwindow,
++ Atom atom,
++ char **list)
++{
++ Atom utf8_string;
++ GString *flattened;
++ int i;
++
++ utf8_string = _wnck_atom_get ("UTF8_STRING");
++
++ /* flatten to nul-separated list */
++ flattened = g_string_new ("");
++ i = 0;
++ while (list[i] != NULL)
++ {
++ g_string_append_len (flattened, list[i],
++ strlen (list[i]) + 1);
++ ++i;
++ }
++
++ _wnck_error_trap_push ();
++
++ XChangeProperty (gdk_display,
++ xwindow,
++ atom,
++ utf8_string, 8, PropModeReplace,
++ (guchar *) flattened->str, flattened->len);
++
++ _wnck_error_trap_pop ();
++
++ g_string_free (flattened, TRUE);
++}
++
++void
++_wnck_error_trap_push (void)
++{
++ gdk_error_trap_push ();
++}
++
++int
++_wnck_error_trap_pop (void)
++{
++ XSync (gdk_display, False);
++ return gdk_error_trap_pop ();
++}
++
++static GHashTable *atom_hash = NULL;
++static GHashTable *reverse_atom_hash = NULL;
++
++Atom
++_wnck_atom_get (const char *atom_name)
++{
++ Atom retval;
++
++ g_return_val_if_fail (atom_name != NULL, None);
++
++ if (!atom_hash)
++ {
++ atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
++ reverse_atom_hash = g_hash_table_new (NULL, NULL);
++ }
++
++ retval = GPOINTER_TO_UINT (g_hash_table_lookup (atom_hash, atom_name));
++ if (!retval)
++ {
++ retval = XInternAtom (gdk_display, atom_name, FALSE);
++
++ if (retval != None)
++ {
++ char *name_copy;
++
++ name_copy = g_strdup (atom_name);
++
++ g_hash_table_insert (atom_hash,
++ name_copy,
++ GUINT_TO_POINTER (retval));
++ g_hash_table_insert (reverse_atom_hash,
++ GUINT_TO_POINTER (retval),
++ name_copy);
++ }
++ }
++
++ return retval;
++}
++
++const char*
++_wnck_atom_name (Atom atom)
++{
++ if (reverse_atom_hash)
++ return g_hash_table_lookup (reverse_atom_hash, GUINT_TO_POINTER (atom));
++ else
++ return NULL;
++}
++
++static GdkFilterReturn
++filter_func (GdkXEvent *gdkxevent,
++ GdkEvent *event,
++ gpointer data)
++{
++ XEvent *xevent = gdkxevent;
++ int i;
++
++ switch (xevent->type)
++ {
++ case PropertyNotify:
++ {
++ WnckScreen *screen;
++
++ screen = wnck_screen_get_for_root (xevent->xany.window);
++ if (screen != NULL)
++ _wnck_screen_process_property_notify (screen, xevent);
++ else
++ {
++ WnckWindow *window;
++ WnckApplication *app;
++
++ window = wnck_window_get (xevent->xany.window);
++ app = wnck_application_get (xevent->xany.window);
++
++ if (app)
++ _wnck_application_process_property_notify (app, xevent);
++
++ if (window)
++ _wnck_window_process_property_notify (window, xevent);
++ }
++ }
++ break;
++
++ case ConfigureNotify:
++ {
++ WnckWindow *window;
++
++ window = wnck_window_get (xevent->xconfigure.window);
++
++ if (window)
++ _wnck_window_process_configure_notify (window, xevent);
++ }
++ break;
++
++ case SelectionClear:
++ {
++ _wnck_desktop_layout_manager_process_event (xevent);
++ }
++ break;
++
++ case ClientMessage:
++#ifdef HAVE_STARTUP_NOTIFICATION
++ /* We're cheating as officially libsn requires
++ * us to send all events through sn_display_process_event
++ */
++ i = 0;
++ while (i < ScreenCount (gdk_display))
++ {
++ WnckScreen *s;
++
++ s = _wnck_screen_get_existing (i);
++ if (s != NULL)
++ sn_display_process_event (_wnck_screen_get_sn_display (s),
++ xevent);
++
++ ++i;
++ }
++#endif /* HAVE_STARTUP_NOTIFICATION */
++ break;
++ }
++
++ return GDK_FILTER_CONTINUE;
++}
++
++void
++_wnck_event_filter_init (void)
++{
++ static gboolean initialized = FALSE;
++
++ if (!initialized)
++ {
++ gdk_window_add_filter (NULL, filter_func, NULL);
++ initialized = TRUE;
++ }
++}
++
++int
++_wnck_xid_equal (gconstpointer v1,
++ gconstpointer v2)
++{
++ return *((const gulong*) v1) == *((const gulong*) v2);
++}
++
++guint
++_wnck_xid_hash (gconstpointer v)
++{
++ gulong val = * (const gulong *) v;
++
++ /* I'm not sure this works so well. */
++#if G_SIZEOF_LONG > 4
++ return (guint) (val ^ (val >> 32));
++#else
++ return val;
++#endif
++}
++
++void
++_wnck_iconify (Window xwindow)
++{
++ _wnck_error_trap_push ();
++ XIconifyWindow (gdk_display, xwindow, DefaultScreen (gdk_display));
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_deiconify (Window xwindow)
++{
++ /* We need special precautions, because GDK doesn't like
++ * XMapWindow() called on its windows, need to use the
++ * GDK functions
++ */
++ GdkWindow *gdkwindow;
++
++ gdkwindow = gdk_xid_table_lookup (xwindow);
++
++ _wnck_error_trap_push ();
++ if (gdkwindow)
++ gdk_window_show (gdkwindow);
++ else
++ XMapRaised (gdk_display, xwindow);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_close (Screen *screen,
++ Window xwindow,
++ Time timestamp)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = xwindow;
++ xev.xclient.message_type = _wnck_atom_get ("_NET_CLOSE_WINDOW");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = timestamp;
++ xev.xclient.data.l[1] = _wnck_get_client_type ();
++ xev.xclient.data.l[2] = 0;
++ xev.xclient.data.l[3] = 0;
++ xev.xclient.data.l[4] = 0;
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
++#define _NET_WM_MOVERESIZE_SIZE_TOP 1
++#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
++#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
++#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
++#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
++#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
++#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
++#define _NET_WM_MOVERESIZE_MOVE 8
++#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9
++#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10
++
++void
++_wnck_keyboard_move (Screen *screen,
++ Window xwindow)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = xwindow;
++ xev.xclient.message_type = _wnck_atom_get ("_NET_WM_MOVERESIZE");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = 0; /* unused */
++ xev.xclient.data.l[1] = 0; /* unused */
++ xev.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
++ xev.xclient.data.l[3] = 0; /* unused */
++ xev.xclient.data.l[4] = _wnck_get_client_type ();
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_keyboard_size (Screen *screen,
++ Window xwindow)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = xwindow;
++ xev.xclient.message_type = _wnck_atom_get ("_NET_WM_MOVERESIZE");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = 0; /* unused */
++ xev.xclient.data.l[1] = 0; /* unused */
++ xev.xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_KEYBOARD;
++ xev.xclient.data.l[3] = 0; /* unused */
++ xev.xclient.data.l[4] = _wnck_get_client_type ();
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_change_state (Screen *screen,
++ Window xwindow,
++ gboolean add,
++ Atom state1,
++ Atom state2)
++{
++ XEvent xev;
++
++#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
++#define _NET_WM_STATE_ADD 1 /* add/set property */
++#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = xwindow;
++ xev.xclient.message_type = _wnck_atom_get ("_NET_WM_STATE");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
++ xev.xclient.data.l[1] = state1;
++ xev.xclient.data.l[2] = state2;
++ xev.xclient.data.l[3] = _wnck_get_client_type ();
++ xev.xclient.data.l[4] = 0;
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_change_workspace (Screen *screen,
++ Window xwindow,
++ int new_space)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = xwindow;
++ xev.xclient.message_type = _wnck_atom_get ("_NET_WM_DESKTOP");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = new_space;
++ xev.xclient.data.l[1] = _wnck_get_client_type ();
++ xev.xclient.data.l[2] = 0;
++ xev.xclient.data.l[3] = 0;
++ xev.xclient.data.l[4] = 0;
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_activate (Screen *screen,
++ Window xwindow,
++ Time timestamp)
++{
++ XEvent xev;
++
++ if (timestamp == 0)
++ g_warning ("Received a timestamp of 0; window activation may not "
++ "function properly.\n");
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = xwindow;
++ xev.xclient.message_type = _wnck_atom_get ("_NET_ACTIVE_WINDOW");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = _wnck_get_client_type ();
++ xev.xclient.data.l[1] = timestamp;
++ xev.xclient.data.l[2] = 0;
++ xev.xclient.data.l[3] = 0;
++ xev.xclient.data.l[4] = 0;
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_activate_workspace (Screen *screen,
++ int new_active_space,
++ Time timestamp)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = RootWindowOfScreen (screen);
++ xev.xclient.message_type = _wnck_atom_get ("_NET_CURRENT_DESKTOP");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = new_active_space;
++ xev.xclient.data.l[1] = timestamp;
++ xev.xclient.data.l[2] = 0;
++ xev.xclient.data.l[3] = 0;
++ xev.xclient.data.l[4] = 0;
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_change_viewport (Screen *screen,
++ int x,
++ int y)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = RootWindowOfScreen (screen);
++ xev.xclient.message_type = _wnck_atom_get ("_NET_DESKTOP_VIEWPORT");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = x;
++ xev.xclient.data.l[1] = y;
++ xev.xclient.data.l[2] = 0;
++ xev.xclient.data.l[3] = 0;
++ xev.xclient.data.l[4] = 0;
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++void
++_wnck_toggle_showing_desktop (Screen *screen,
++ gboolean show)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = DisplayOfScreen (screen);
++ xev.xclient.window = RootWindowOfScreen (screen);
++ xev.xclient.message_type = _wnck_atom_get ("_NET_SHOWING_DESKTOP");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = show != FALSE;
++ xev.xclient.data.l[1] = 0;
++ xev.xclient.data.l[2] = 0;
++ xev.xclient.data.l[3] = 0;
++ xev.xclient.data.l[4] = 0;
++
++ _wnck_error_trap_push ();
++ XSendEvent (DisplayOfScreen (screen),
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_pop ();
++}
++
++char*
++_wnck_get_session_id (Window xwindow)
++{
++ Window client_leader;
++
++ client_leader = None;
++ _wnck_get_window (xwindow,
++ _wnck_atom_get ("WM_CLIENT_LEADER"),
++ &client_leader);
++
++ if (client_leader == None)
++ return NULL;
++
++ return _wnck_get_string_property_latin1 (client_leader,
++ _wnck_atom_get ("SM_CLIENT_ID"));
++}
++
++int
++_wnck_get_pid (Window xwindow)
++{
++ int val;
++
++ if (!_wnck_get_cardinal (xwindow,
++ _wnck_atom_get ("_NET_WM_PID"),
++ &val))
++ return 0;
++ else
++ return val;
++}
++
++char*
++_wnck_get_name (Window xwindow)
++{
++ char *name;
++
++ name = _wnck_get_utf8_property (xwindow,
++ _wnck_atom_get ("_NET_WM_VISIBLE_NAME"));
++
++ if (name == NULL)
++ name = _wnck_get_utf8_property (xwindow,
++ _wnck_atom_get ("_NET_WM_NAME"));
++
++ if (name == NULL)
++ name = _wnck_get_text_property (xwindow,
++ XA_WM_NAME);
++
++ return name;
++}
++
++char*
++_wnck_get_icon_name (Window xwindow)
++{
++ char *name;
++
++ name = _wnck_get_utf8_property (xwindow,
++ _wnck_atom_get ("_NET_WM_VISIBLE_ICON_NAME"));
++
++ if (name == NULL)
++ name = _wnck_get_utf8_property (xwindow,
++ _wnck_atom_get ("_NET_WM_ICON_NAME"));
++
++ if (name == NULL)
++ name = _wnck_get_text_property (xwindow,
++ XA_WM_ICON_NAME);
++
++ return name;
++}
++
++static char*
++latin1_to_utf8 (const char *latin1)
++{
++ GString *str;
++ const char *p;
++
++ str = g_string_new (NULL);
++
++ p = latin1;
++ while (*p)
++ {
++ g_string_append_unichar (str, (gunichar) *p);
++ ++p;
++ }
++
++ return g_string_free (str, FALSE);
++}
++
++char*
++_wnck_get_res_class_utf8 (Window xwindow)
++{
++ char *res_class;
++
++ _wnck_get_wmclass (xwindow, &res_class, NULL);
++
++ return res_class;
++}
++
++void
++_wnck_get_wmclass (Window xwindow,
++ char **res_class,
++ char **res_name)
++{
++ XClassHint ch;
++ char *retval;
++
++ _wnck_error_trap_push ();
++
++ ch.res_name = NULL;
++ ch.res_class = NULL;
++
++ XGetClassHint (gdk_display, xwindow,
++ &ch);
++
++ _wnck_error_trap_pop ();
++
++ retval = NULL;
++
++ if (res_class)
++ *res_class = NULL;
++
++ if (res_name)
++ *res_name = NULL;
++
++ if (ch.res_name)
++ {
++ if (res_name)
++ *res_name = latin1_to_utf8 (ch.res_name);
++
++ XFree (ch.res_name);
++ }
++
++ if (ch.res_class)
++ {
++ if (res_class)
++ *res_class = latin1_to_utf8 (ch.res_class);
++
++ XFree (ch.res_class);
++ }
++}
++
++gboolean
++_wnck_get_frame_extents (Window xwindow,
++ int *left_frame,
++ int *right_frame,
++ int *top_frame,
++ int *bottom_frame)
++{
++ gulong *p_size;
++ int n_size;
++ gboolean retval;
++
++ retval = FALSE;
++ p_size = NULL;
++ n_size = 0;
++
++ _wnck_get_cardinal_list (xwindow,
++ _wnck_atom_get ("_NET_FRAME_EXTENTS"),
++ &p_size, &n_size);
++
++ if (p_size != NULL && n_size == 4)
++ {
++ *left_frame = p_size[0];
++ *right_frame = p_size[1];
++ *top_frame = p_size[2];
++ *bottom_frame = p_size[3];
++
++ retval = TRUE;
++ }
++
++ if (p_size != NULL)
++ g_free (p_size);
++
++ return retval;
++}
++
++void
++_wnck_select_input (Window xwindow,
++ int mask)
++{
++ GdkWindow *gdkwindow;
++
++ gdkwindow = gdk_xid_table_lookup (xwindow);
++
++ _wnck_error_trap_push ();
++ if (gdkwindow)
++ {
++ /* Avoid breaking GDK's setup,
++ * this somewhat relies on people setting
++ * event masks right after realization
++ * and not changing them again
++ */
++ XWindowAttributes attrs;
++ XGetWindowAttributes (gdk_display, xwindow, &attrs);
++ mask |= attrs.your_event_mask;
++ }
++
++ XSelectInput (gdk_display, xwindow, mask);
++ _wnck_error_trap_pop ();
++}
++
++/* The icon-reading code is copied
++ * from metacity, please sync bugfixes
++ */
++static gboolean
++find_largest_sizes (gulong *data,
++ gulong nitems,
++ int *width,
++ int *height)
++{
++ *width = 0;
++ *height = 0;
++
++ while (nitems > 0)
++ {
++ int w, h;
++ gboolean replace;
++
++ replace = FALSE;
++
++ if (nitems < 3)
++ return FALSE; /* no space for w, h */
++
++ w = data[0];
++ h = data[1];
++
++ if (nitems < ((w * h) + 2))
++ return FALSE; /* not enough data */
++
++ *width = MAX (w, *width);
++ *height = MAX (h, *height);
++
++ data += (w * h) + 2;
++ nitems -= (w * h) + 2;
++ }
++
++ return TRUE;
++}
++
++static gboolean
++find_best_size (gulong *data,
++ gulong nitems,
++ int ideal_width,
++ int ideal_height,
++ int *width,
++ int *height,
++ gulong **start)
++{
++ int best_w;
++ int best_h;
++ gulong *best_start;
++ int max_width, max_height;
++
++ *width = 0;
++ *height = 0;
++ *start = NULL;
++
++ if (!find_largest_sizes (data, nitems, &max_width, &max_height))
++ return FALSE;
++
++ if (ideal_width < 0)
++ ideal_width = max_width;
++ if (ideal_height < 0)
++ ideal_height = max_height;
++
++ best_w = 0;
++ best_h = 0;
++ best_start = NULL;
++
++ while (nitems > 0)
++ {
++ int w, h;
++ gboolean replace;
++
++ replace = FALSE;
++
++ if (nitems < 3)
++ return FALSE; /* no space for w, h */
++
++ w = data[0];
++ h = data[1];
++
++ if (nitems < ((w * h) + 2))
++ break; /* not enough data */
++
++ if (best_start == NULL)
++ {
++ replace = TRUE;
++ }
++ else
++ {
++ /* work with averages */
++ const int ideal_size = (ideal_width + ideal_height) / 2;
++ int best_size = (best_w + best_h) / 2;
++ int this_size = (w + h) / 2;
++
++ /* larger than desired is always better than smaller */
++ if (best_size < ideal_size &&
++ this_size >= ideal_size)
++ replace = TRUE;
++ /* if we have too small, pick anything bigger */
++ else if (best_size < ideal_size &&
++ this_size > best_size)
++ replace = TRUE;
++ /* if we have too large, pick anything smaller
++ * but still >= the ideal
++ */
++ else if (best_size > ideal_size &&
++ this_size >= ideal_size &&
++ this_size < best_size)
++ replace = TRUE;
++ }
++
++ if (replace)
++ {
++ best_start = data + 2;
++ best_w = w;
++ best_h = h;
++ }
++
++ data += (w * h) + 2;
++ nitems -= (w * h) + 2;
++ }
++
++ if (best_start)
++ {
++ *start = best_start;
++ *width = best_w;
++ *height = best_h;
++ return TRUE;
++ }
++ else
++ return FALSE;
++}
++
++static void
++argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata)
++{
++ guchar *p;
++ int i;
++
++ *pixdata = g_new (guchar, len * 4);
++ p = *pixdata;
++
++ /* One could speed this up a lot. */
++ i = 0;
++ while (i < len)
++ {
++ guint argb;
++ guint rgba;
++
++ argb = argb_data[i];
++ rgba = (argb << 8) | (argb >> 24);
++
++ *p = rgba >> 24;
++ ++p;
++ *p = (rgba >> 16) & 0xff;
++ ++p;
++ *p = (rgba >> 8) & 0xff;
++ ++p;
++ *p = rgba & 0xff;
++ ++p;
++
++ ++i;
++ }
++}
++
++static gboolean
++read_rgb_icon (Window xwindow,
++ int ideal_width,
++ int ideal_height,
++ int ideal_mini_width,
++ int ideal_mini_height,
++ int *width,
++ int *height,
++ guchar **pixdata,
++ int *mini_width,
++ int *mini_height,
++ guchar **mini_pixdata)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ int result, err;
++ gulong *data;
++ gulong *best;
++ int w, h;
++ gulong *best_mini;
++ int mini_w, mini_h;
++
++ _wnck_error_trap_push ();
++ type = None;
++ data = NULL;
++ result = XGetWindowProperty (gdk_display,
++ xwindow,
++ _wnck_atom_get ("_NET_WM_ICON"),
++ 0, G_MAXLONG,
++ False, XA_CARDINAL, &type, &format, &nitems,
++ &bytes_after, (void*)&data);
++
++ err = _wnck_error_trap_pop ();
++
++ if (err != Success ||
++ result != Success)
++ return FALSE;
++
++ if (type != XA_CARDINAL)
++ {
++ XFree (data);
++ return FALSE;
++ }
++
++ if (!find_best_size (data, nitems,
++ ideal_width, ideal_height,
++ &w, &h, &best))
++ {
++ XFree (data);
++ return FALSE;
++ }
++
++ if (!find_best_size (data, nitems,
++ ideal_mini_width, ideal_mini_height,
++ &mini_w, &mini_h, &best_mini))
++ {
++ XFree (data);
++ return FALSE;
++ }
++
++ *width = w;
++ *height = h;
++
++ *mini_width = mini_w;
++ *mini_height = mini_h;
++
++ argbdata_to_pixdata (best, w * h, pixdata);
++ argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata);
++
++ XFree (data);
++
++ return TRUE;
++}
++
++static void
++free_pixels (guchar *pixels, gpointer data)
++{
++ g_free (pixels);
++}
++
++static void
++get_pixmap_geometry (Pixmap pixmap,
++ int *w,
++ int *h,
++ int *d)
++{
++ Window root_ignored;
++ int x_ignored, y_ignored;
++ guint width, height;
++ guint border_width_ignored;
++ guint depth;
++
++ if (w)
++ *w = 1;
++ if (h)
++ *h = 1;
++ if (d)
++ *d = 1;
++
++ XGetGeometry (gdk_display,
++ pixmap, &root_ignored, &x_ignored, &y_ignored,
++ &width, &height, &border_width_ignored, &depth);
++
++ if (w)
++ *w = width;
++ if (h)
++ *h = height;
++ if (d)
++ *d = depth;
++}
++
++static GdkPixbuf*
++apply_mask (GdkPixbuf *pixbuf,
++ GdkPixbuf *mask)
++{
++ int w, h;
++ int i, j;
++ GdkPixbuf *with_alpha;
++ guchar *src;
++ guchar *dest;
++ int src_stride;
++ int dest_stride;
++
++ w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf));
++ h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf));
++
++ with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
++
++ dest = gdk_pixbuf_get_pixels (with_alpha);
++ src = gdk_pixbuf_get_pixels (mask);
++
++ dest_stride = gdk_pixbuf_get_rowstride (with_alpha);
++ src_stride = gdk_pixbuf_get_rowstride (mask);
++
++ i = 0;
++ while (i < h)
++ {
++ j = 0;
++ while (j < w)
++ {
++ guchar *s = src + i * src_stride + j * 3;
++ guchar *d = dest + i * dest_stride + j * 4;
++
++ /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0
++ * otherwise
++ */
++ if (s[0] == 0)
++ d[3] = 0; /* transparent */
++ else
++ d[3] = 255; /* opaque */
++
++ ++j;
++ }
++
++ ++i;
++ }
++
++ return with_alpha;
++}
++
++static GdkColormap*
++get_cmap (GdkPixmap *pixmap)
++{
++ GdkColormap *cmap;
++
++ cmap = gdk_drawable_get_colormap (pixmap);
++ if (cmap)
++ g_object_ref (G_OBJECT (cmap));
++
++ if (cmap == NULL)
++ {
++ if (gdk_drawable_get_depth (pixmap) == 1)
++ {
++ /* try null cmap */
++ cmap = NULL;
++ }
++ else
++ {
++ /* Try system cmap */
++ GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (pixmap));
++ cmap = gdk_screen_get_system_colormap (screen);
++ g_object_ref (G_OBJECT (cmap));
++ }
++ }
++
++ /* Be sure we aren't going to blow up due to visual mismatch */
++ if (cmap &&
++ (gdk_colormap_get_visual (cmap)->depth !=
++ gdk_drawable_get_depth (pixmap)))
++ {
++ g_object_unref (G_OBJECT (cmap));
++ cmap = NULL;
++ }
++
++ return cmap;
++}
++
++GdkPixbuf*
++_wnck_gdk_pixbuf_get_from_pixmap (GdkPixbuf *dest,
++ Pixmap xpixmap,
++ int src_x,
++ int src_y,
++ int dest_x,
++ int dest_y,
++ int width,
++ int height)
++{
++ GdkDrawable *drawable;
++ GdkPixbuf *retval;
++ GdkColormap *cmap;
++
++ retval = NULL;
++ cmap = NULL;
++
++ drawable = gdk_xid_table_lookup (xpixmap);
++
++ if (drawable)
++ g_object_ref (G_OBJECT (drawable));
++ else
++ drawable = gdk_pixmap_foreign_new (xpixmap);
++
++ if (drawable)
++ {
++ cmap = get_cmap (drawable);
++
++ /* GDK is supposed to do this but doesn't in GTK 2.0.2,
++ * fixed in 2.0.3
++ */
++ if (width < 0)
++ gdk_drawable_get_size (drawable, &width, NULL);
++ if (height < 0)
++ gdk_drawable_get_size (drawable, NULL, &height);
++
++ retval = gdk_pixbuf_get_from_drawable (dest,
++ drawable,
++ cmap,
++ src_x, src_y,
++ dest_x, dest_y,
++ width, height);
++ }
++
++ if (cmap)
++ g_object_unref (G_OBJECT (cmap));
++ if (drawable)
++ g_object_unref (G_OBJECT (drawable));
++
++ return retval;
++}
++
++static gboolean
++try_pixmap_and_mask (Pixmap src_pixmap,
++ Pixmap src_mask,
++ GdkPixbuf **iconp,
++ int ideal_width,
++ int ideal_height,
++ GdkPixbuf **mini_iconp,
++ int ideal_mini_width,
++ int ideal_mini_height)
++{
++ GdkPixbuf *unscaled = NULL;
++ GdkPixbuf *mask = NULL;
++ int w, h;
++
++ if (src_pixmap == None)
++ return FALSE;
++
++ _wnck_error_trap_push ();
++
++ get_pixmap_geometry (src_pixmap, &w, &h, NULL);
++
++ unscaled = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
++ src_pixmap,
++ 0, 0, 0, 0,
++ w, h);
++
++ if (unscaled && src_mask != None)
++ {
++ get_pixmap_geometry (src_mask, &w, &h, NULL);
++ mask = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
++ src_mask,
++ 0, 0, 0, 0,
++ w, h);
++ }
++
++ _wnck_error_trap_pop ();
++
++ if (mask)
++ {
++ GdkPixbuf *masked;
++
++ masked = apply_mask (unscaled, mask);
++ g_object_unref (G_OBJECT (unscaled));
++ unscaled = masked;
++
++ g_object_unref (G_OBJECT (mask));
++ mask = NULL;
++ }
++
++ if (unscaled)
++ {
++ *iconp =
++ gdk_pixbuf_scale_simple (unscaled,
++ ideal_width > 0 ? ideal_width :
++ gdk_pixbuf_get_width (unscaled),
++ ideal_height > 0 ? ideal_height :
++ gdk_pixbuf_get_height (unscaled),
++ GDK_INTERP_BILINEAR);
++ *mini_iconp =
++ gdk_pixbuf_scale_simple (unscaled,
++ ideal_mini_width > 0 ? ideal_mini_width :
++ gdk_pixbuf_get_width (unscaled),
++ ideal_mini_height > 0 ? ideal_mini_height :
++ gdk_pixbuf_get_height (unscaled),
++ GDK_INTERP_BILINEAR);
++
++ g_object_unref (G_OBJECT (unscaled));
++ return TRUE;
++ }
++ else
++ return FALSE;
++}
++
++static void
++get_kwm_win_icon (Window xwindow,
++ Pixmap *pixmap,
++ Pixmap *mask)
++{
++ Atom type;
++ int format;
++ gulong nitems;
++ gulong bytes_after;
++ Pixmap *icons;
++ int err, result;
++
++ *pixmap = None;
++ *mask = None;
++
++ _wnck_error_trap_push ();
++ icons = NULL;
++ result = XGetWindowProperty (gdk_display, xwindow,
++ _wnck_atom_get ("KWM_WIN_ICON"),
++ 0, G_MAXLONG,
++ False,
++ _wnck_atom_get ("KWM_WIN_ICON"),
++ &type, &format, &nitems,
++ &bytes_after, (void*)&icons);
++
++ err = _wnck_error_trap_pop ();
++ if (err != Success ||
++ result != Success)
++ return;
++
++ if (type != _wnck_atom_get ("KWM_WIN_ICON"))
++ {
++ XFree (icons);
++ return;
++ }
++
++ *pixmap = icons[0];
++ *mask = icons[1];
++
++ XFree (icons);
++
++ return;
++}
++
++typedef enum
++{
++ /* These MUST be in ascending order of preference;
++ * i.e. if we get _NET_WM_ICON and already have
++ * WM_HINTS, we prefer _NET_WM_ICON
++ */
++ USING_NO_ICON,
++ USING_FALLBACK_ICON,
++ USING_KWM_WIN_ICON,
++ USING_WM_HINTS,
++ USING_NET_WM_ICON
++} IconOrigin;
++
++struct _WnckIconCache
++{
++ IconOrigin origin;
++ Pixmap prev_pixmap;
++ Pixmap prev_mask;
++ GdkPixbuf *icon;
++ GdkPixbuf *mini_icon;
++ int ideal_width;
++ int ideal_height;
++ int ideal_mini_width;
++ int ideal_mini_height;
++ guint want_fallback : 1;
++ /* TRUE if these props have changed */
++ guint wm_hints_dirty : 1;
++ guint kwm_win_icon_dirty : 1;
++ guint net_wm_icon_dirty : 1;
++};
++
++WnckIconCache*
++_wnck_icon_cache_new (void)
++{
++ WnckIconCache *icon_cache;
++
++ icon_cache = g_slice_new0 (WnckIconCache);
++
++ icon_cache->origin = USING_NO_ICON;
++ icon_cache->prev_pixmap = None;
++ icon_cache->icon = NULL;
++ icon_cache->mini_icon = NULL;
++ icon_cache->ideal_width = -1; /* won't be a legit width */
++ icon_cache->ideal_height = -1;
++ icon_cache->ideal_mini_width = -1;
++ icon_cache->ideal_mini_height = -1;
++ icon_cache->want_fallback = TRUE;
++ icon_cache->wm_hints_dirty = TRUE;
++ icon_cache->kwm_win_icon_dirty = TRUE;
++ icon_cache->net_wm_icon_dirty = TRUE;
++
++ return icon_cache;
++}
++
++static void
++clear_icon_cache (WnckIconCache *icon_cache,
++ gboolean dirty_all)
++{
++ if (icon_cache->icon)
++ g_object_unref (G_OBJECT (icon_cache->icon));
++ icon_cache->icon = NULL;
++
++ if (icon_cache->mini_icon)
++ g_object_unref (G_OBJECT (icon_cache->mini_icon));
++ icon_cache->mini_icon = NULL;
++
++ icon_cache->origin = USING_NO_ICON;
++
++ if (dirty_all)
++ {
++ icon_cache->wm_hints_dirty = TRUE;
++ icon_cache->kwm_win_icon_dirty = TRUE;
++ icon_cache->net_wm_icon_dirty = TRUE;
++ }
++}
++
++void
++_wnck_icon_cache_free (WnckIconCache *icon_cache)
++{
++ clear_icon_cache (icon_cache, FALSE);
++
++ g_slice_free (WnckIconCache, icon_cache);
++}
++
++void
++_wnck_icon_cache_property_changed (WnckIconCache *icon_cache,
++ Atom atom)
++{
++ if (atom == _wnck_atom_get ("_NET_WM_ICON"))
++ icon_cache->net_wm_icon_dirty = TRUE;
++ else if (atom == _wnck_atom_get ("KWM_WIN_ICON"))
++ icon_cache->kwm_win_icon_dirty = TRUE;
++ else if (atom == _wnck_atom_get ("WM_HINTS"))
++ icon_cache->wm_hints_dirty = TRUE;
++}
++
++gboolean
++_wnck_icon_cache_get_icon_invalidated (WnckIconCache *icon_cache)
++{
++ if (icon_cache->origin <= USING_KWM_WIN_ICON &&
++ icon_cache->kwm_win_icon_dirty)
++ return TRUE;
++ else if (icon_cache->origin <= USING_WM_HINTS &&
++ icon_cache->wm_hints_dirty)
++ return TRUE;
++ else if (icon_cache->origin <= USING_NET_WM_ICON &&
++ icon_cache->net_wm_icon_dirty)
++ return TRUE;
++ else if (icon_cache->origin < USING_FALLBACK_ICON &&
++ icon_cache->want_fallback)
++ return TRUE;
++ else if (icon_cache->origin == USING_NO_ICON)
++ return TRUE;
++ else if (icon_cache->origin == USING_FALLBACK_ICON &&
++ !icon_cache->want_fallback)
++ return TRUE;
++ else
++ return FALSE;
++}
++
++void
++_wnck_icon_cache_set_want_fallback (WnckIconCache *icon_cache,
++ gboolean setting)
++{
++ icon_cache->want_fallback = setting;
++}
++
++gboolean
++_wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache)
++{
++ return icon_cache->origin == USING_FALLBACK_ICON;
++}
++
++static void
++replace_cache (WnckIconCache *icon_cache,
++ IconOrigin origin,
++ GdkPixbuf *new_icon,
++ GdkPixbuf *new_mini_icon)
++{
++ clear_icon_cache (icon_cache, FALSE);
++
++ icon_cache->origin = origin;
++
++ if (new_icon)
++ g_object_ref (G_OBJECT (new_icon));
++
++ icon_cache->icon = new_icon;
++
++ if (new_mini_icon)
++ g_object_ref (G_OBJECT (new_mini_icon));
++
++ icon_cache->mini_icon = new_mini_icon;
++}
++
++static GdkPixbuf*
++scaled_from_pixdata (guchar *pixdata,
++ int w,
++ int h,
++ int new_w,
++ int new_h)
++{
++ GdkPixbuf *src;
++ GdkPixbuf *dest;
++
++ src = gdk_pixbuf_new_from_data (pixdata,
++ GDK_COLORSPACE_RGB,
++ TRUE,
++ 8,
++ w, h, w * 4,
++ free_pixels,
++ NULL);
++
++ if (src == NULL)
++ return NULL;
++
++ if (w != h)
++ {
++ GdkPixbuf *tmp;
++ int size;
++
++ size = MAX (w, h);
++
++ tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size);
++
++ if (tmp != NULL)
++ {
++ gdk_pixbuf_fill (tmp, 0);
++ gdk_pixbuf_copy_area (src, 0, 0, w, h,
++ tmp,
++ (size - w) / 2, (size - h) / 2);
++
++ g_object_unref (src);
++ src = tmp;
++ }
++ }
++
++ if (w != new_w || h != new_h)
++ {
++ dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR);
++
++ g_object_unref (G_OBJECT (src));
++ }
++ else
++ {
++ dest = src;
++ }
++
++ return dest;
++}
++
++gboolean
++_wnck_read_icons (Window xwindow,
++ WnckIconCache *icon_cache,
++ GdkPixbuf **iconp,
++ int ideal_width,
++ int ideal_height,
++ GdkPixbuf **mini_iconp,
++ int ideal_mini_width,
++ int ideal_mini_height)
++{
++ guchar *pixdata;
++ int w, h;
++ guchar *mini_pixdata;
++ int mini_w, mini_h;
++ Pixmap pixmap;
++ Pixmap mask;
++ XWMHints *hints;
++
++ /* Return value is whether the icon changed */
++
++ g_return_val_if_fail (icon_cache != NULL, FALSE);
++
++ *iconp = NULL;
++ *mini_iconp = NULL;
++
++ if (ideal_width != icon_cache->ideal_width ||
++ ideal_height != icon_cache->ideal_height ||
++ ideal_mini_width != icon_cache->ideal_mini_width ||
++ ideal_mini_height != icon_cache->ideal_mini_height)
++ clear_icon_cache (icon_cache, TRUE);
++
++ icon_cache->ideal_width = ideal_width;
++ icon_cache->ideal_height = ideal_height;
++ icon_cache->ideal_mini_width = ideal_mini_width;
++ icon_cache->ideal_mini_height = ideal_mini_height;
++
++ if (!_wnck_icon_cache_get_icon_invalidated (icon_cache))
++ return FALSE; /* we have no new info to use */
++
++ pixdata = NULL;
++
++ /* Our algorithm here assumes that we can't have for example origin
++ * < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE
++ * unless we have tried to read NET_WM_ICON.
++ *
++ * Put another way, if an icon origin is not dirty, then we have
++ * tried to read it at the current size. If it is dirty, then
++ * we haven't done that since the last change.
++ */
++
++ if (icon_cache->origin <= USING_NET_WM_ICON &&
++ icon_cache->net_wm_icon_dirty)
++
++ {
++ icon_cache->net_wm_icon_dirty = FALSE;
++
++ if (read_rgb_icon (xwindow,
++ ideal_width, ideal_height,
++ ideal_mini_width, ideal_mini_height,
++ &w, &h, &pixdata,
++ &mini_w, &mini_h, &mini_pixdata))
++ {
++ *iconp = scaled_from_pixdata (pixdata, w, h, ideal_width, ideal_height);
++
++ *mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h,
++ ideal_mini_width, ideal_mini_height);
++
++ replace_cache (icon_cache, USING_NET_WM_ICON,
++ *iconp, *mini_iconp);
++
++ return TRUE;
++ }
++ }
++
++ if (icon_cache->origin <= USING_WM_HINTS &&
++ icon_cache->wm_hints_dirty)
++ {
++ icon_cache->wm_hints_dirty = FALSE;
++
++ _wnck_error_trap_push ();
++ hints = XGetWMHints (gdk_display, xwindow);
++ _wnck_error_trap_pop ();
++ pixmap = None;
++ mask = None;
++ if (hints)
++ {
++ if (hints->flags & IconPixmapHint)
++ pixmap = hints->icon_pixmap;
++ if (hints->flags & IconMaskHint)
++ mask = hints->icon_mask;
++
++ XFree (hints);
++ hints = NULL;
++ }
++
++ /* We won't update if pixmap is unchanged;
++ * avoids a get_from_drawable() on every geometry
++ * hints change
++ */
++ if ((pixmap != icon_cache->prev_pixmap ||
++ mask != icon_cache->prev_mask) &&
++ pixmap != None)
++ {
++ if (try_pixmap_and_mask (pixmap, mask,
++ iconp, ideal_width, ideal_height,
++ mini_iconp, ideal_mini_width, ideal_mini_height))
++ {
++ icon_cache->prev_pixmap = pixmap;
++ icon_cache->prev_mask = mask;
++
++ replace_cache (icon_cache, USING_WM_HINTS,
++ *iconp, *mini_iconp);
++
++ return TRUE;
++ }
++ }
++ }
++
++ if (icon_cache->origin <= USING_KWM_WIN_ICON &&
++ icon_cache->kwm_win_icon_dirty)
++ {
++ icon_cache->kwm_win_icon_dirty = FALSE;
++
++ get_kwm_win_icon (xwindow, &pixmap, &mask);
++
++ if ((pixmap != icon_cache->prev_pixmap ||
++ mask != icon_cache->prev_mask) &&
++ pixmap != None)
++ {
++ if (try_pixmap_and_mask (pixmap, mask,
++ iconp, ideal_width, ideal_height,
++ mini_iconp, ideal_mini_width, ideal_mini_height))
++ {
++ icon_cache->prev_pixmap = pixmap;
++ icon_cache->prev_mask = mask;
++
++ replace_cache (icon_cache, USING_KWM_WIN_ICON,
++ *iconp, *mini_iconp);
++
++ return TRUE;
++ }
++ }
++ }
++
++ if (icon_cache->want_fallback &&
++ icon_cache->origin < USING_FALLBACK_ICON)
++ {
++ _wnck_get_fallback_icons (iconp,
++ ideal_width,
++ ideal_height,
++ mini_iconp,
++ ideal_mini_width,
++ ideal_mini_height);
++
++ replace_cache (icon_cache, USING_FALLBACK_ICON,
++ *iconp, *mini_iconp);
++
++ return TRUE;
++ }
++
++ if (!icon_cache->want_fallback &&
++ icon_cache->origin == USING_FALLBACK_ICON)
++ {
++ /* Get rid of current icon */
++ clear_icon_cache (icon_cache, FALSE);
++
++ return TRUE;
++ }
++
++ /* found nothing new */
++ return FALSE;
++}
++
++static GdkPixbuf*
++default_icon_at_size (int width,
++ int height)
++{
++
++ GdkPixbuf *base;
++
++ base = gdk_pixbuf_new_from_inline (-1, default_icon_data,
++ FALSE,
++ NULL);
++
++ g_assert (base);
++
++ if ((width < 0 && height < 0) ||
++ (gdk_pixbuf_get_width (base) == width &&
++ gdk_pixbuf_get_height (base) == height))
++ {
++ return base;
++ }
++ else
++ {
++ GdkPixbuf *scaled;
++
++ scaled = gdk_pixbuf_scale_simple (base,
++ width > 0 ? width :
++ gdk_pixbuf_get_width (base),
++ height > 0 ? height :
++ gdk_pixbuf_get_height (base),
++ GDK_INTERP_BILINEAR);
++
++ g_object_unref (G_OBJECT (base));
++
++ return scaled;
++ }
++}
++
++void
++_wnck_get_fallback_icons (GdkPixbuf **iconp,
++ int ideal_width,
++ int ideal_height,
++ GdkPixbuf **mini_iconp,
++ int ideal_mini_width,
++ int ideal_mini_height)
++{
++ if (iconp)
++ *iconp = default_icon_at_size (ideal_width > 0 ? ideal_width :
++ DEFAULT_ICON_WIDTH,
++ ideal_height > 0 ? ideal_height :
++ DEFAULT_ICON_HEIGHT);
++
++ if (mini_iconp)
++ *mini_iconp = default_icon_at_size (ideal_mini_width > 0 ? ideal_mini_width :
++ DEFAULT_MINI_ICON_WIDTH,
++ ideal_mini_height > 0 ? ideal_mini_height :
++ DEFAULT_MINI_ICON_HEIGHT);
++}
++
++
++void
++_wnck_get_window_geometry (Screen *screen,
++ Window xwindow,
++ int *xp,
++ int *yp,
++ int *widthp,
++ int *heightp)
++{
++ int x, y;
++ unsigned int width, height, bw, depth;
++ Window root_window;
++
++ width = 1;
++ height = 1;
++
++ _wnck_error_trap_push ();
++
++ XGetGeometry (gdk_display,
++ xwindow,
++ &root_window,
++ &x, &y, &width, &height, &bw, &depth);
++
++ _wnck_error_trap_pop ();
++
++ _wnck_get_window_position (screen, xwindow, xp, yp);
++
++ if (widthp)
++ *widthp = width;
++ if (heightp)
++ *heightp = height;
++}
++
++void _wnck_set_window_geometry (Screen *screen,
++ Window xwindow,
++ int gravity_and_flags,
++ int x,
++ int y,
++ int width,
++ int height)
++{
++ XEvent xev;
++
++ xev.xclient.type = ClientMessage;
++ xev.xclient.serial = 0;
++ xev.xclient.send_event = True;
++ xev.xclient.display = gdk_display;
++ xev.xclient.window = xwindow;
++ xev.xclient.message_type = _wnck_atom_get ("_NET_MOVERESIZE_WINDOW");
++ xev.xclient.format = 32;
++ xev.xclient.data.l[0] = gravity_and_flags;
++ xev.xclient.data.l[1] = x;
++ xev.xclient.data.l[2] = y;
++ xev.xclient.data.l[3] = width;
++ xev.xclient.data.l[4] = height;
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display,
++ RootWindowOfScreen (screen),
++ False,
++ SubstructureRedirectMask | SubstructureNotifyMask,
++ &xev);
++ _wnck_error_trap_push ();
++}
++
++void
++_wnck_get_window_position (Screen *screen,
++ Window xwindow,
++ int *xp,
++ int *yp)
++{
++ int x, y;
++ Window child;
++
++ x = 0;
++ y = 0;
++
++ _wnck_error_trap_push ();
++ XTranslateCoordinates (gdk_display,
++ xwindow,
++ RootWindowOfScreen (screen),
++ 0, 0,
++ &x, &y, &child);
++ _wnck_error_trap_pop ();
++
++ if (xp)
++ *xp = x;
++ if (yp)
++ *yp = y;
++}
++
++void
++_wnck_set_icon_geometry (Window xwindow,
++ int x,
++ int y,
++ int width,
++ int height)
++{
++ gulong data[4];
++
++ data[0] = x;
++ data[1] = y;
++ data[2] = width;
++ data[3] = height;
++
++ _wnck_error_trap_push ();
++
++ XChangeProperty (gdk_display,
++ xwindow,
++ _wnck_atom_get ("_NET_WM_ICON_GEOMETRY"),
++ XA_CARDINAL, 32, PropModeReplace,
++ (guchar *)&data, 4);
++
++ _wnck_error_trap_pop ();
++}
++
++/* orientation of pager */
++#define _NET_WM_ORIENTATION_HORZ 0
++#define _NET_WM_ORIENTATION_VERT 1
++
++/* starting corner for counting spaces */
++#define _NET_WM_TOPLEFT 0
++#define _NET_WM_TOPRIGHT 1
++#define _NET_WM_BOTTOMRIGHT 2
++#define _NET_WM_BOTTOMLEFT 3
++
++void
++_wnck_set_desktop_layout (Screen *xscreen,
++ int rows,
++ int columns)
++{
++ gulong data[4];
++
++ /* FIXME: hack, hack, hack so as not
++ * to have to add a orientation param
++ * to wnck_screen_try_set_workspace_layout.
++ *
++ * Remove this crack asap.
++ */
++ g_assert ((rows == 0) || (columns == 0));
++
++ data[0] = (columns == 0) ? _NET_WM_ORIENTATION_HORZ : _NET_WM_ORIENTATION_VERT;
++ data[1] = columns;
++ data[2] = rows;
++ data[3] = _NET_WM_TOPLEFT;
++
++ _wnck_error_trap_push ();
++
++ XChangeProperty (gdk_display,
++ RootWindowOfScreen (xscreen),
++ _wnck_atom_get ("_NET_DESKTOP_LAYOUT"),
++ XA_CARDINAL, 32, PropModeReplace,
++ (guchar *)&data, 4);
++
++ _wnck_error_trap_pop ();
++}
++
++typedef struct
++{
++ Window window;
++ Atom timestamp_prop_atom;
++} TimeStampInfo;
++
++static Bool
++timestamp_predicate (Display *display,
++ XEvent *xevent,
++ XPointer arg)
++{
++ TimeStampInfo *info = (TimeStampInfo *)arg;
++
++ if (xevent->type == PropertyNotify &&
++ xevent->xproperty.window == info->window &&
++ xevent->xproperty.atom == info->timestamp_prop_atom)
++ return True;
++
++ return False;
++}
++
++/**
++ * get_server_time:
++ * @display: display from which to get the time
++ * @window: a #Window, used for communication with the server.
++ * The window must have PropertyChangeMask in its
++ * events mask or a hang will result.
++ *
++ * Routine to get the current X server time stamp.
++ *
++ * Return value: the time stamp.
++ **/
++static Time
++get_server_time (Window window)
++{
++ unsigned char c = 'a';
++ XEvent xevent;
++ TimeStampInfo info;
++
++ info.timestamp_prop_atom = _wnck_atom_get ("_TIMESTAMP_PROP");
++ info.window = window;
++
++ XChangeProperty (gdk_display, window,
++ info.timestamp_prop_atom, info.timestamp_prop_atom,
++ 8, PropModeReplace, &c, 1);
++
++ XIfEvent (gdk_display, &xevent,
++ timestamp_predicate, (XPointer)&info);
++
++ return xevent.xproperty.time;
++}
++
++typedef struct
++{
++ int screen_number;
++ int token;
++ Window window;
++ Atom selection_atom;
++ Atom manager_atom;
++} LayoutManager;
++
++static GSList *layout_managers = NULL;
++static int next_token = 1;
++
++static void
++_wnck_free_layout_manager (LayoutManager *lm)
++{
++ _wnck_error_trap_push ();
++ XDestroyWindow (gdk_display, lm->window);
++ _wnck_error_trap_pop ();
++
++ g_slice_free (LayoutManager, lm);
++
++ layout_managers = g_slist_remove (layout_managers, lm);
++}
++
++int
++_wnck_try_desktop_layout_manager (Screen *xscreen,
++ int current_token)
++{
++ Atom selection_atom;
++ Window owner;
++ GSList *tmp;
++ int number;
++ Time timestamp;
++ XClientMessageEvent xev;
++ char buffer[256];
++ LayoutManager *lm;
++
++ number = XScreenNumberOfScreen (xscreen);
++
++ sprintf (buffer, "_NET_DESKTOP_LAYOUT_S%d", number);
++ selection_atom = _wnck_atom_get (buffer);
++
++ owner = XGetSelectionOwner (gdk_display, selection_atom);
++
++ tmp = layout_managers;
++ while (tmp != NULL)
++ {
++ lm = tmp->data;
++
++ if (number == lm->screen_number)
++ {
++ if (current_token == lm->token)
++ {
++ if (owner == lm->window)
++ return current_token; /* we still have the selection */
++ else
++ { /* we lost the selection */
++ _wnck_free_layout_manager (lm);
++ break;
++ }
++ }
++ else
++ return WNCK_NO_MANAGER_TOKEN; /* someone else has it */
++ }
++
++ tmp = tmp->next;
++ }
++
++ if (owner != None)
++ return WNCK_NO_MANAGER_TOKEN; /* someone else has the selection */
++
++ /* No one has the selection at the moment */
++
++ lm = g_slice_new0 (LayoutManager);
++
++ lm->screen_number = number;
++ lm->token = next_token;
++ ++next_token;
++
++ lm->selection_atom = selection_atom;
++ lm->manager_atom = _wnck_atom_get ("MANAGER");
++
++ _wnck_error_trap_push ();
++
++ lm->window = XCreateSimpleWindow (gdk_display,
++ RootWindowOfScreen (xscreen),
++ 0, 0, 10, 10, 0,
++ WhitePixel (gdk_display, number),
++ WhitePixel (gdk_display, number));
++
++ XSelectInput (gdk_display, lm->window, PropertyChangeMask);
++ timestamp = get_server_time (lm->window);
++
++ XSetSelectionOwner (gdk_display, lm->selection_atom,
++ lm->window, timestamp);
++
++ _wnck_error_trap_pop ();
++
++ /* Check to see if we managed to claim the selection. */
++
++ if (XGetSelectionOwner (gdk_display, lm->selection_atom) !=
++ lm->window)
++ {
++ g_free (lm);
++ return WNCK_NO_MANAGER_TOKEN;
++ }
++
++ xev.type = ClientMessage;
++ xev.window = RootWindow (gdk_display, number);
++ xev.message_type = lm->manager_atom;
++ xev.format = 32;
++ xev.data.l[0] = timestamp;
++ xev.data.l[1] = lm->selection_atom;
++ xev.data.l[2] = lm->window;
++ xev.data.l[3] = 0; /* manager specific data */
++ xev.data.l[4] = 0; /* manager specific data */
++
++ _wnck_error_trap_push ();
++ XSendEvent (gdk_display, RootWindow (gdk_display, number),
++ False, StructureNotifyMask, (XEvent *)&xev);
++ _wnck_error_trap_pop ();
++
++ layout_managers = g_slist_prepend (layout_managers,
++ lm);
++
++ return lm->token;
++}
++
++void
++_wnck_release_desktop_layout_manager (Screen *xscreen,
++ int current_token)
++{
++ GSList *tmp;
++ int number;
++ LayoutManager *lm;
++
++ number = XScreenNumberOfScreen (xscreen);
++
++ tmp = layout_managers;
++ while (tmp != NULL)
++ {
++ lm = tmp->data;
++
++ if (number == lm->screen_number)
++ {
++ if (current_token == lm->token)
++ {
++ _wnck_error_trap_push ();
++
++ /* release selection ownership */
++ if (XGetSelectionOwner (gdk_display, lm->selection_atom) !=
++ lm->window)
++ {
++ Time timestamp;
++
++ timestamp = get_server_time (lm->window);
++ XSetSelectionOwner (gdk_display, lm->selection_atom,
++ None, timestamp);
++ }
++
++ _wnck_error_trap_pop ();
++
++ _wnck_free_layout_manager (lm);
++ return;
++ }
++ }
++
++ tmp = tmp->next;
++ }
++}
++
++gboolean
++_wnck_desktop_layout_manager_process_event (XEvent *xev)
++{
++ GSList *tmp;
++ LayoutManager *lm;
++
++ if (xev->type != SelectionClear)
++ return FALSE;
++
++ tmp = layout_managers;
++ while (tmp != NULL)
++ {
++ lm = tmp->data;
++
++ if (xev->xany.window == lm->window &&
++ xev->xselectionclear.selection == lm->selection_atom)
++ {
++ _wnck_free_layout_manager (lm);
++ return TRUE;
++ }
++
++ tmp = tmp->next;
++ }
++
++ return FALSE;
++}