2007-11-30 Stephen Browne <[email protected]> gnome-2-20 NEVADA_80
authorstephen
Fri, 30 Nov 2007 15:38:48 +0000
branchgnome-2-20
changeset 10755 59d4fbd5042f
parent 10754 8815a967b429
child 10756 f3b5d21d1eb8
2007-11-30 Stephen Browne <[email protected]> * patches/libwnck-01-trusted-extensions.diff: update to fix 6586812 * patches/metacity-08-trusted-extensions.diff: update to fix 6586812 and 6501883 * patches/nautilus-10-trusted-extensions.diff: update to fix 6501711
ChangeLog
patches/libwnck-01-trusted-extensions.diff
patches/metacity-08-trusted-extensions.diff
patches/nautilus-10-trusted-extensions.diff
--- a/ChangeLog	Fri Nov 30 11:06:05 2007 +0000
+++ b/ChangeLog	Fri Nov 30 15:38:48 2007 +0000
@@ -1,3 +1,10 @@
+2007-11-30  Stephen Browne  <[email protected]>
+
+	* patches/libwnck-01-trusted-extensions.diff: update to fix 6586812
+	* patches/metacity-08-trusted-extensions.diff: update to fix 6586812 
+	and 6501883
+	* patches/nautilus-10-trusted-extensions.diff: update to fix 6501711
+
 2007-11-29  Stephen Browne <[email protected]>
 
         * patches/gnome-panel-11-trusted-extensions.diff:
--- 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;
++}
--- a/patches/metacity-08-trusted-extensions.diff	Fri Nov 30 11:06:05 2007 +0000
+++ b/patches/metacity-08-trusted-extensions.diff	Fri Nov 30 15:38:48 2007 +0000
@@ -1,6 +1,6 @@
-diff -urN -x '*~' -x '*.rej*' meta.orig/config.h.in meta.new/config.h.in
---- meta.orig/config.h.in	2007-09-19 18:45:48.932183000 +0100
-+++ meta.new/config.h.in	2007-09-19 18:50:12.437446000 +0100
+diff -urN meta.orig/config.h.in meta.new/config.h.in
+--- meta.orig/config.h.in	2007-11-30 12:02:50.255141000 +0000
++++ meta.new/config.h.in	2007-11-30 12:04:04.870200000 +0000
 @@ -78,12 +78,18 @@
  /* Define to 1 if you have the <sys/stat.h> header file. */
  #undef HAVE_SYS_STAT_H
@@ -30,10 +30,10 @@
  /* Name of package */
  #undef PACKAGE
  
-diff -urN -x '*~' -x '*.rej*' meta.orig/configure.in meta.new/configure.in
---- meta.orig/configure.in	2007-09-19 18:45:48.934795000 +0100
-+++ meta.new/configure.in	2007-09-19 18:50:12.438067000 +0100
-@@ -346,6 +346,19 @@
+diff -urN meta.orig/configure.in meta.new/configure.in
+--- meta.orig/configure.in	2007-11-30 12:02:50.258591000 +0000
++++ meta.new/configure.in	2007-11-30 12:04:04.870862000 +0000
+@@ -345,6 +345,19 @@
  fi
  
  CPPFLAGS="$metacity_save_cppflags"
@@ -53,9 +53,58 @@
  
  SHAPE_LIBS=
  found_shape=no
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/Makefile.am meta.new/src/Makefile.am
---- meta.orig/src/Makefile.am	2007-09-19 18:45:49.483868000 +0100
-+++ meta.new/src/Makefile.am	2007-09-19 18:50:12.438964000 +0100
+diff -urN meta.orig/configure.in.orig meta.new/configure.in.orig
+--- meta.orig/configure.in.orig	2007-11-30 12:02:50.845745000 +0000
++++ meta.new/configure.in.orig	2007-11-30 12:03:01.872385000 +0000
+@@ -213,9 +213,12 @@
+ ## or the render-specific check later
+ have_xrender=no
+ 
+-XCOMPOSITE_VERSION=0.2
++## If cm is on the system, assume that Xcomposite is present.
++## On Solaris, the Xorg extensions do not have pc files, so
++## we can't test for those until that changes.
++##
+ AC_MSG_CHECKING([Xcomposite >= $XCOMPOSITE_VERSION])
+-if $PKG_CONFIG --atleast-version $XCOMPOSITE_VERSION xcomposite; then
++if $PKG_CONFIG cm; then
+    have_xcomposite=yes
+ else
+    have_xcomposite=no
+@@ -234,7 +237,7 @@
+ 
+ if test x$have_xcomposite = xyes; then
+   echo "Building with CompositeExt"
+-  METACITY_PC_MODULES="$METACITY_PC_MODULES xcomposite >= $XCOMPOSITE_VERSION xfixes xrender xdamage cm"
++  METACITY_PC_MODULES="$METACITY_PC_MODULES cm"
+   AC_DEFINE(HAVE_COMPOSITE_EXTENSIONS, , [Building with compositing manager support])
+ 
+   ## force on render also
+@@ -247,11 +250,7 @@
+ if test x$have_xcomposite = xno; then
+   XRENDER_VERSION=0.0
+   AC_MSG_CHECKING([xrender >= $XRENDER_VERSION])
+-  if $PKG_CONFIG --atleast-version $XRENDER_VERSION xrender; then
+-     have_xrender=yes
+-  else
+-     have_xrender=no
+-  fi
++  have_xrender=yes
+   AC_MSG_RESULT($have_xrender)
+ 
+   if test x$enable_render = xyes; then
+@@ -265,7 +264,7 @@
+ 
+   if test x$have_xrender = xyes; then
+      echo "Building with Render"
+-     METACITY_PC_MODULES="$METACITY_PC_MODULES xrender >= $XRENDER_VERSION"
++     METACITY_PC_MODULES="$METACITY_PC_MODULES"
+   fi
+ fi ## have_composite
+ 
+diff -urN meta.orig/src/Makefile.am meta.new/src/Makefile.am
+--- meta.orig/src/Makefile.am	2007-11-30 12:02:50.363359000 +0000
++++ meta.new/src/Makefile.am	2007-11-30 12:04:04.871834000 +0000
 @@ -4,7 +4,7 @@
  
  INCLUDES=@METACITY_CFLAGS@ -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" -DMETACITY_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\" -DMETACITY_PKGDATADIR=\"$(pkgdatadir)\" -DMETACITY_DATADIR=\"$(datadir)\" -DG_LOG_DOMAIN=\"metacity\" -DSN_API_NOT_YET_FROZEN=1
@@ -103,9 +152,9 @@
  
  libmetacity_private_la_LDFLAGS = -no-undefined
  libmetacity_private_la_LIBADD  = @METACITY_LIBS@
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/display.c meta.new/src/display.c
---- meta.orig/src/display.c	2007-09-19 18:45:49.481980000 +0100
-+++ meta.new/src/display.c	2007-09-19 18:50:12.441710000 +0100
+diff -urN meta.orig/src/display.c meta.new/src/display.c
+--- meta.orig/src/display.c	2007-11-30 12:02:50.361365000 +0000
++++ meta.new/src/display.c	2007-11-30 12:04:04.874836000 +0000
 @@ -66,6 +66,9 @@
  #include <X11/Xcursor/Xcursor.h>
  #endif
@@ -186,9 +235,9 @@
  #if 0
              else if (event->xproperty.atom ==
                       display->atom_net_restack_window)
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/display.h meta.new/src/display.h
---- meta.orig/src/display.h	2007-09-19 18:45:49.269114000 +0100
-+++ meta.new/src/display.h	2007-09-19 18:50:12.444264000 +0100
+diff -urN meta.orig/src/display.h meta.new/src/display.h
+--- meta.orig/src/display.h	2007-11-30 12:02:50.359168000 +0000
++++ meta.new/src/display.h	2007-11-30 12:04:04.877162000 +0000
 @@ -184,6 +184,9 @@
    Atom atom_net_wm_visible_name;
    Atom atom_net_wm_visible_icon_name;
@@ -209,9 +258,9 @@
    MetaScreen *active_screen;
    GHashTable *window_ids;
    int error_traps;
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/frame.c meta.new/src/frame.c
---- meta.orig/src/frame.c	2007-09-19 18:45:49.576672000 +0100
-+++ meta.new/src/frame.c	2007-09-19 18:50:12.445319000 +0100
+diff -urN meta.orig/src/frame.c meta.new/src/frame.c
+--- meta.orig/src/frame.c	2007-11-30 12:02:50.535217000 +0000
++++ meta.new/src/frame.c	2007-11-30 12:04:04.878052000 +0000
 @@ -28,6 +28,9 @@
  #include "bell.h"
  #include "errors.h"
@@ -240,9 +289,9 @@
    /* Move keybindings to frame instead of window */
    meta_window_grab_keys (window);
  
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/frame.h meta.new/src/frame.h
---- meta.orig/src/frame.h	2007-09-19 18:45:49.482106000 +0100
-+++ meta.new/src/frame.h	2007-09-19 18:50:12.445628000 +0100
+diff -urN meta.orig/src/frame.h meta.new/src/frame.h
+--- meta.orig/src/frame.h	2007-11-30 12:02:50.361498000 +0000
++++ meta.new/src/frame.h	2007-11-30 12:04:04.878411000 +0000
 @@ -25,17 +25,19 @@
  #define META_FRAME_H
  
@@ -266,9 +315,9 @@
  
  struct _MetaFrame
  {
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/frames.c meta.new/src/frames.c
---- meta.orig/src/frames.c	2007-09-19 18:45:49.746832000 +0100
-+++ meta.new/src/frames.c	2007-09-19 18:50:12.447810000 +0100
+diff -urN meta.orig/src/frames.c meta.new/src/frames.c
+--- meta.orig/src/frames.c	2007-11-30 12:02:50.761660000 +0000
++++ meta.new/src/frames.c	2007-11-30 12:04:04.880291000 +0000
 @@ -571,7 +571,7 @@
  
    g_assert (window);
@@ -346,9 +395,9 @@
 +}
 +#endif
 +
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/frames.h meta.new/src/frames.h
---- meta.orig/src/frames.h	2007-09-19 18:45:49.482786000 +0100
-+++ meta.new/src/frames.h	2007-09-19 18:50:12.449071000 +0100
+diff -urN meta.orig/src/frames.h meta.new/src/frames.h
+--- meta.orig/src/frames.h	2007-11-30 12:02:50.362311000 +0000
++++ meta.new/src/frames.h	2007-11-30 12:04:04.881498000 +0000
 @@ -28,6 +28,9 @@
  #include <gdk/gdkx.h>
  #include "common.h"
@@ -379,9 +428,9 @@
 +			    MetaTrustedLabel *label);
 +#endif
  #endif
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/keybindings.c meta.new/src/keybindings.c
---- meta.orig/src/keybindings.c	2007-09-19 18:45:49.485348000 +0100
-+++ meta.new/src/keybindings.c	2007-09-19 18:50:12.451069000 +0100
+diff -urN meta.orig/src/keybindings.c meta.new/src/keybindings.c
+--- meta.orig/src/keybindings.c	2007-11-30 12:02:50.512931000 +0000
++++ meta.new/src/keybindings.c	2007-11-30 12:04:04.883719000 +0000
 @@ -48,6 +48,11 @@
  #define GNOME_SYS_SUSPEND "gnome-sys-suspend"
  
@@ -450,9 +499,9 @@
  static void
  handle_run_command (MetaDisplay    *display,
                      MetaScreen     *screen,
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/prefs.c meta.new/src/prefs.c
---- meta.orig/src/prefs.c	2007-09-19 18:45:49.748157000 +0100
-+++ meta.new/src/prefs.c	2007-09-19 18:50:12.454333000 +0100
+diff -urN meta.orig/src/prefs.c meta.new/src/prefs.c
+--- meta.orig/src/prefs.c	2007-11-30 12:02:50.763297000 +0000
++++ meta.new/src/prefs.c	2007-11-30 12:22:01.910864000 +0000
 @@ -31,6 +31,9 @@
  #endif
  #include <string.h>
@@ -527,7 +576,7 @@
  #ifdef HAVE_GCONF
    gconf_client_notify_add (default_client, "/apps/metacity",
                             change_notify,
-@@ -951,6 +983,75 @@
+@@ -951,6 +983,91 @@
        if (update_workspace_name (key, str))
          queue_changed (META_PREF_WORKSPACE_NAMES);
      }
@@ -548,7 +597,7 @@
 +
 +     str = value ? gconf_value_get_string (value) : NULL;
 +
-+     /* Check if the label is in range if not set it to USER_MIN_SL 
++     /* Check if the label is in range if not set it to USER_MIN_SL or role MIN label
 +      * NOTE : if USER_MIN_SL is not properly set you can have an infinite loop here */
 +
 +     if (str && !tsol_label_is_in_user_range (str))
@@ -572,7 +621,23 @@
 +				 "trusted workspace label", i);
 +		   }
 +		 else
-+		   meta_prefs_change_workspace_label (i, tsol_label_get_min ());
++		  {
++		    const char *name = meta_prefs_get_workspace_role (i);
++		    if (name)
++		      {
++		      char *min_role_label = NULL;
++		      if (tsol_label_is_in_role_range (str, name, min_role_label))
++			{
++			  if (min_role_label)
++			    
++			    meta_prefs_change_workspace_label (i, min_role_label);
++			  else 
++			    meta_prefs_change_workspace_label (i, tsol_label_get_min ());
++			}
++		      else 
++			meta_prefs_change_workspace_label (i, tsol_label_get_min());
++		      }
++		  }
 +	       }
 +	   }
 +       }
@@ -603,7 +668,7 @@
    else if (strcmp (key, KEY_BUTTON_LAYOUT) == 0)
      {
        const char *str;
-@@ -1631,6 +1732,42 @@
+@@ -1631,6 +1748,42 @@
          value = MAX_REASONABLE_WORKSPACES;
      }
    
@@ -646,7 +711,7 @@
    num_workspaces = value;
  
    return old != num_workspaces;
-@@ -1890,6 +2027,16 @@
+@@ -1890,6 +2043,16 @@
  
      case META_PREF_COMPOSITING_MANAGER:
        return "COMPOSITING_MANAGER";
@@ -663,7 +728,7 @@
      }
  
    return "(unknown)";
-@@ -2291,6 +2438,53 @@
+@@ -2291,6 +2454,53 @@
  #endif /* HAVE_GCONF */
  }
  
@@ -717,7 +782,7 @@
  static void
  init_workspace_names (void)
  {
-@@ -2806,6 +3000,72 @@
+@@ -2806,6 +3016,72 @@
  }
  
  #ifdef HAVE_GCONF
@@ -790,7 +855,7 @@
  static gboolean
  update_workspace_name (const char  *name,
                         const char  *value)
-@@ -2883,6 +3143,152 @@
+@@ -2883,6 +3159,152 @@
    return TRUE;
  }
  #endif /* HAVE_GCONF */
@@ -943,7 +1008,7 @@
  
  const char*
  meta_prefs_get_workspace_name (int i)
-@@ -2958,6 +3364,29 @@
+@@ -2958,6 +3380,29 @@
  }
  
  #ifdef HAVE_GCONF
@@ -973,9 +1038,9 @@
  static char*
  gconf_key_for_workspace_name (int i)
  {
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/prefs.h meta.new/src/prefs.h
---- meta.orig/src/prefs.h	2007-09-19 18:45:49.747649000 +0100
-+++ meta.new/src/prefs.h	2007-09-19 18:50:12.454958000 +0100
+diff -urN meta.orig/src/prefs.h meta.new/src/prefs.h
+--- meta.orig/src/prefs.h	2007-11-30 12:02:50.762620000 +0000
++++ meta.new/src/prefs.h	2007-11-30 12:04:04.887122000 +0000
 @@ -60,6 +60,10 @@
    META_PREF_CURSOR_THEME,
    META_PREF_CURSOR_SIZE,
@@ -1004,9 +1069,9 @@
  /* Screen bindings */
  #define META_KEYBINDING_WORKSPACE_1              "switch_to_workspace_1"
  #define META_KEYBINDING_WORKSPACE_2              "switch_to_workspace_2"
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/preview-widget.c meta.new/src/preview-widget.c
---- meta.orig/src/preview-widget.c	2007-09-19 18:45:49.746960000 +0100
-+++ meta.new/src/preview-widget.c	2007-09-19 18:50:12.455796000 +0100
+diff -urN meta.orig/src/preview-widget.c meta.new/src/preview-widget.c
+--- meta.orig/src/preview-widget.c	2007-11-30 12:02:50.761830000 +0000
++++ meta.new/src/preview-widget.c	2007-11-30 12:04:04.887833000 +0000
 @@ -21,6 +21,7 @@
   * 02111-1307, USA.
   */
@@ -1015,7 +1080,7 @@
  #include "preview-widget.h"
  
  static void     meta_preview_class_init    (MetaPreviewClass *klass);
-@@ -250,7 +251,11 @@
+@@ -257,7 +258,11 @@
                               &preview->button_layout,
                               button_states,
                               meta_preview_get_mini_icon (),
@@ -1028,9 +1093,475 @@
      }
  
    /* draw child */
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/screen.c meta.new/src/screen.c
---- meta.orig/src/screen.c	2007-09-19 18:45:49.268970000 +0100
-+++ meta.new/src/screen.c	2007-09-19 18:50:12.457152000 +0100
+diff -urN meta.orig/src/preview-widget.c.orig meta.new/src/preview-widget.c.orig
+--- meta.orig/src/preview-widget.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ meta.new/src/preview-widget.c.orig	2007-11-30 12:03:02.814621000 +0000
+@@ -0,0 +1,462 @@
++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
++
++/* Metacity theme preview widget */
++
++/* 
++ * Copyright (C) 2002 Havoc Pennington
++ * 
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ * 
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ */
++
++#include "preview-widget.h"
++
++static void     meta_preview_class_init    (MetaPreviewClass *klass);
++static void     meta_preview_init          (MetaPreview      *preview);
++static void     meta_preview_size_request  (GtkWidget        *widget,
++                                            GtkRequisition   *req);
++static void     meta_preview_size_allocate (GtkWidget        *widget,
++                                            GtkAllocation    *allocation);
++static gboolean meta_preview_expose        (GtkWidget        *widget,
++                                            GdkEventExpose   *event);
++static void     meta_preview_finalize      (GObject          *object);
++
++static GtkWidgetClass *parent_class;
++
++GtkType
++meta_preview_get_type (void)
++{
++  static GtkType preview_type = 0;
++
++  if (!preview_type)
++    {
++      static const GtkTypeInfo preview_info =
++      {
++	"MetaPreview",
++	sizeof (MetaPreview),
++	sizeof (MetaPreviewClass),
++	(GtkClassInitFunc) meta_preview_class_init,
++	(GtkObjectInitFunc) meta_preview_init,
++	/* reserved_1 */ NULL,
++        /* reserved_2 */ NULL,
++        (GtkClassInitFunc) NULL,
++      };
++
++      preview_type = gtk_type_unique (GTK_TYPE_BIN, &preview_info);
++    }
++
++  return preview_type;
++}
++
++static void
++meta_preview_class_init (MetaPreviewClass *class)
++{
++  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
++  GtkWidgetClass *widget_class;
++
++  widget_class = (GtkWidgetClass*) class;
++  parent_class = gtk_type_class (GTK_TYPE_BIN);
++
++  gobject_class->finalize = meta_preview_finalize;
++
++  widget_class->expose_event = meta_preview_expose;
++  widget_class->size_request = meta_preview_size_request;
++  widget_class->size_allocate = meta_preview_size_allocate;
++}
++
++static void
++meta_preview_init (MetaPreview *preview)
++{
++  int i;
++  
++  GTK_WIDGET_SET_FLAGS (preview, GTK_NO_WINDOW);
++
++  i = 0;
++  while (i < MAX_BUTTONS_PER_CORNER)
++    {
++      preview->button_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST;
++      preview->button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
++      ++i;
++    }
++  
++  preview->button_layout.left_buttons[0] = META_BUTTON_FUNCTION_MENU;
++
++  preview->button_layout.right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE;
++  preview->button_layout.right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE;
++  preview->button_layout.right_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
++  
++  preview->type = META_FRAME_TYPE_NORMAL;
++  preview->flags =
++    META_FRAME_ALLOWS_DELETE |
++    META_FRAME_ALLOWS_MENU |
++    META_FRAME_ALLOWS_MINIMIZE |
++    META_FRAME_ALLOWS_MAXIMIZE |
++    META_FRAME_ALLOWS_VERTICAL_RESIZE |
++    META_FRAME_ALLOWS_HORIZONTAL_RESIZE |
++    META_FRAME_HAS_FOCUS |
++    META_FRAME_ALLOWS_SHADE |
++    META_FRAME_ALLOWS_MOVE;
++  
++  preview->left_width = -1;
++  preview->right_width = -1;
++  preview->top_height = -1;
++  preview->bottom_height = -1;
++}
++
++GtkWidget*
++meta_preview_new (void)
++{
++  MetaPreview *preview;
++  
++  preview = gtk_type_new (META_TYPE_PREVIEW);
++  
++  return GTK_WIDGET (preview);
++}
++
++static void
++meta_preview_finalize (GObject *object)
++{
++  MetaPreview *preview;
++
++  preview = META_PREVIEW (object);
++
++  g_free (preview->title);
++  preview->title = NULL;
++  
++  G_OBJECT_CLASS (parent_class)->finalize (object);
++}
++
++static void
++ensure_info (MetaPreview *preview)
++{
++  GtkWidget *widget;
++
++  widget = GTK_WIDGET (preview);
++  
++  if (preview->layout == NULL)
++    {
++      PangoFontDescription *font_desc;
++      double scale;
++      PangoAttrList *attrs;
++      PangoAttribute *attr;
++
++      if (preview->theme)        
++        scale = meta_theme_get_title_scale (preview->theme,
++                                            preview->type,
++                                            preview->flags);
++      else
++        scale = 1.0;
++      
++      preview->layout = gtk_widget_create_pango_layout (widget,
++                                                        preview->title);
++      
++      font_desc = meta_gtk_widget_get_font_desc (widget, scale, NULL);
++      
++      preview->text_height =
++        meta_pango_font_desc_get_text_height (font_desc,
++                                              gtk_widget_get_pango_context (widget));
++          
++      attrs = pango_attr_list_new ();
++      
++      attr = pango_attr_size_new (pango_font_description_get_size (font_desc));
++      attr->start_index = 0;
++      attr->end_index = G_MAXINT;
++      
++      pango_attr_list_insert (attrs, attr);
++      
++      pango_layout_set_attributes (preview->layout, attrs);
++      
++      pango_attr_list_unref (attrs);      
++  
++      pango_font_description_free (font_desc);
++    }
++
++  if (preview->top_height < 0)
++    {
++      if (preview->theme)
++        {
++          meta_theme_get_frame_borders (preview->theme,
++                                        preview->type,
++                                        preview->text_height,
++                                        preview->flags,
++                                        &preview->top_height,
++                                        &preview->bottom_height,
++                                        &preview->left_width,
++                                        &preview->right_width);
++        }
++      else
++        {
++          preview->top_height = 0;
++          preview->bottom_height = 0;
++          preview->left_width = 0;
++          preview->right_width = 0;
++        }
++    }
++}
++
++static gboolean
++meta_preview_expose (GtkWidget      *widget,
++                     GdkEventExpose *event)
++{
++  MetaPreview *preview;
++  int border_width;
++  int client_width;
++  int client_height;
++  MetaButtonState button_states[META_BUTTON_TYPE_LAST] =
++  {
++    META_BUTTON_STATE_NORMAL,
++    META_BUTTON_STATE_NORMAL,
++    META_BUTTON_STATE_NORMAL,
++    META_BUTTON_STATE_NORMAL
++  };
++  
++  g_return_val_if_fail (META_IS_PREVIEW (widget), FALSE);
++  g_return_val_if_fail (event != NULL, FALSE);
++
++  preview = META_PREVIEW (widget);
++
++  ensure_info (preview);
++
++  border_width = GTK_CONTAINER (widget)->border_width;
++  
++  client_width = widget->allocation.width - preview->left_width - preview->right_width - border_width * 2;
++  client_height = widget->allocation.height - preview->top_height - preview->bottom_height - border_width * 2;
++
++  if (client_width < 0)
++    client_width = 1;
++  if (client_height < 0)
++    client_height = 1;  
++  
++  if (preview->theme)
++    {
++      border_width = GTK_CONTAINER (widget)->border_width;
++      
++      meta_theme_draw_frame (preview->theme,
++                             widget,
++                             widget->window,
++                             &event->area,
++                             widget->allocation.x + border_width,
++                             widget->allocation.y + border_width,
++                             preview->type,
++                             preview->flags,
++                             client_width, client_height,
++                             preview->layout,
++                             preview->text_height,
++                             &preview->button_layout,
++                             button_states,
++                             meta_preview_get_mini_icon (),
++                             meta_preview_get_icon ());
++    }
++
++  /* draw child */
++  return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
++}
++
++static void
++meta_preview_size_request (GtkWidget      *widget,
++                           GtkRequisition *req)
++{
++  MetaPreview *preview;
++
++  preview = META_PREVIEW (widget);
++
++  ensure_info (preview);
++
++  req->width = preview->left_width + preview->right_width;
++  req->height = preview->top_height + preview->bottom_height;
++  
++  if (GTK_BIN (preview)->child &&
++      GTK_WIDGET_VISIBLE (GTK_BIN (preview)->child))
++    {
++      GtkRequisition child_requisition;
++
++      gtk_widget_size_request (GTK_BIN (preview)->child, &child_requisition);
++
++      req->width += child_requisition.width;
++      req->height += child_requisition.height;
++    }
++  else
++    {
++#define NO_CHILD_WIDTH 80
++#define NO_CHILD_HEIGHT 20
++      req->width += NO_CHILD_WIDTH;
++      req->height += NO_CHILD_HEIGHT;
++    }
++
++  req->width += GTK_CONTAINER (widget)->border_width * 2;
++  req->height += GTK_CONTAINER (widget)->border_width * 2;
++}
++
++static void
++meta_preview_size_allocate (GtkWidget         *widget,
++                            GtkAllocation     *allocation)
++{
++  MetaPreview *preview;
++  int border_width;
++  GtkAllocation child_allocation;
++  
++  preview = META_PREVIEW (widget);
++
++  ensure_info (preview);
++  
++  widget->allocation = *allocation;
++
++  border_width = GTK_CONTAINER (widget)->border_width;
++  
++  if (GTK_BIN (widget)->child &&
++      GTK_WIDGET_VISIBLE (GTK_BIN (widget)->child))
++    {
++      child_allocation.x = widget->allocation.x + border_width + preview->left_width;
++      child_allocation.y = widget->allocation.y + border_width + preview->top_height;
++      
++      child_allocation.width = MAX (1, widget->allocation.width - border_width * 2 - preview->left_width - preview->right_width);
++      child_allocation.height = MAX (1, widget->allocation.height - border_width * 2 - preview->top_height - preview->bottom_height);
++
++      gtk_widget_size_allocate (GTK_BIN (widget)->child, &child_allocation);
++    }
++}
++
++static void
++clear_cache (MetaPreview *preview)
++{
++  if (preview->layout)
++    {
++      g_object_unref (G_OBJECT (preview->layout));
++      preview->layout = NULL;
++    }
++
++  preview->left_width = -1;
++  preview->right_width = -1;
++  preview->top_height = -1;
++  preview->bottom_height = -1;
++}
++
++void
++meta_preview_set_theme (MetaPreview    *preview,
++                        MetaTheme      *theme)
++{
++  g_return_if_fail (META_IS_PREVIEW (preview));
++
++  preview->theme = theme;
++  
++  clear_cache (preview);
++
++  gtk_widget_queue_resize (GTK_WIDGET (preview));
++}
++
++void
++meta_preview_set_title (MetaPreview    *preview,
++                        const char     *title)
++{
++  g_return_if_fail (META_IS_PREVIEW (preview));
++
++  g_free (preview->title);
++  preview->title = g_strdup (title);
++  
++  clear_cache (preview);
++
++  gtk_widget_queue_resize (GTK_WIDGET (preview));
++}
++
++void
++meta_preview_set_frame_type (MetaPreview    *preview,
++                             MetaFrameType   type)
++{
++  g_return_if_fail (META_IS_PREVIEW (preview));
++
++  preview->type = type;
++
++  clear_cache (preview);
++
++  gtk_widget_queue_resize (GTK_WIDGET (preview));
++}
++
++void
++meta_preview_set_frame_flags (MetaPreview    *preview,
++                              MetaFrameFlags  flags)
++{
++  g_return_if_fail (META_IS_PREVIEW (preview));
++
++  preview->flags = flags;
++
++  clear_cache (preview);
++
++  gtk_widget_queue_resize (GTK_WIDGET (preview));
++}
++
++void
++meta_preview_set_button_layout (MetaPreview            *preview,
++                                const MetaButtonLayout *button_layout)
++{
++  g_return_if_fail (META_IS_PREVIEW (preview));
++  
++  preview->button_layout = *button_layout;  
++  
++  gtk_widget_queue_draw (GTK_WIDGET (preview));
++}
++
++#include "inlinepixbufs.h"
++
++GdkPixbuf*
++meta_preview_get_icon (void)
++{
++  static GdkPixbuf *default_icon = NULL;
++
++  if (default_icon == NULL)
++    {
++      GdkPixbuf *base;
++
++      base = gdk_pixbuf_new_from_inline (-1, default_icon_data,
++                                         FALSE,
++                                         NULL);
++
++      g_assert (base);
++
++      default_icon = gdk_pixbuf_scale_simple (base,
++                                              META_ICON_WIDTH,
++                                              META_ICON_HEIGHT,
++                                              GDK_INTERP_BILINEAR);
++
++      g_object_unref (G_OBJECT (base));
++    }
++  
++  return default_icon;
++}
++
++GdkPixbuf*
++meta_preview_get_mini_icon (void)
++{
++  static GdkPixbuf *default_icon = NULL;
++
++  if (default_icon == NULL)
++    {
++      GdkPixbuf *base;
++
++      base = gdk_pixbuf_new_from_inline (-1, default_icon_data,
++                                         FALSE,
++                                         NULL);
++
++      g_assert (base);
++
++      default_icon = gdk_pixbuf_scale_simple (base,
++                                              META_MINI_ICON_WIDTH,
++                                              META_MINI_ICON_HEIGHT,
++                                              GDK_INTERP_BILINEAR);
++
++      g_object_unref (G_OBJECT (base));
++    }
++  
++  return default_icon;
++}
+diff -urN meta.orig/src/screen.c meta.new/src/screen.c
+--- meta.orig/src/screen.c	2007-11-30 12:02:50.358994000 +0000
++++ meta.new/src/screen.c	2007-11-30 12:04:04.889309000 +0000
 @@ -38,6 +38,7 @@
  #include "stack.h"
  #include "xprops.h"
@@ -1084,9 +1615,9 @@
  
    meta_screen_queue_workarea_recalc (screen);
  }
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/tabpopup.c meta.new/src/tabpopup.c
---- meta.orig/src/tabpopup.c	2007-09-19 18:45:49.746549000 +0100
-+++ meta.new/src/tabpopup.c	2007-09-19 18:50:12.458771000 +0100
+diff -urN meta.orig/src/tabpopup.c meta.new/src/tabpopup.c
+--- meta.orig/src/tabpopup.c	2007-11-30 12:02:50.761216000 +0000
++++ meta.new/src/tabpopup.c	2007-11-30 12:04:04.890422000 +0000
 @@ -33,6 +33,10 @@
  #include "frame.h"
  #include <gtk/gtk.h>
@@ -1164,7 +1695,7 @@
  static GdkPixbuf*
  dimm_icon (GdkPixbuf *pixbuf)
  {
-@@ -303,6 +366,13 @@
+@@ -306,6 +369,13 @@
  
    gtk_box_pack_end (GTK_BOX (vbox), popup->label, FALSE, FALSE, 0);
  
@@ -1178,7 +1709,7 @@
    max_label_width = 0;
    top = 0;
    bottom = 1;
-@@ -935,6 +1005,34 @@
+@@ -938,6 +1008,34 @@
          }
      }
  
@@ -1213,9 +1744,956 @@
    return TRUE;
  }
  
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/theme-viewer.c meta.new/src/theme-viewer.c
---- meta.orig/src/theme-viewer.c	2007-09-19 18:45:49.577513000 +0100
-+++ meta.new/src/theme-viewer.c	2007-09-19 18:50:12.460636000 +0100
+diff -urN meta.orig/src/tabpopup.c.orig meta.new/src/tabpopup.c.orig
+--- meta.orig/src/tabpopup.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ meta.new/src/tabpopup.c.orig	2007-11-30 12:03:02.814100000 +0000
+@@ -0,0 +1,943 @@
++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
++
++/* Metacity popup window thing showing windows you can tab to */
++
++/* 
++ * Copyright (C) 2001 Havoc Pennington
++ * Copyright (C) 2002 Red Hat, Inc.
++ * Copyright (C) 2005 Elijah Newren
++ * 
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ * 
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ */
++
++#include <config.h>
++
++#include "util.h"
++#include "core.h"
++#include "tabpopup.h"
++#include "workspace.h" /* FIXME should not be included in this file */
++#include "draw-workspace.h"
++#include "frame.h"
++#include <gtk/gtk.h>
++#include <math.h>
++
++#define OUTSIDE_SELECT_RECT 2
++#define INSIDE_SELECT_RECT 2
++
++typedef struct _TabEntry TabEntry;
++
++struct _TabEntry
++{
++  MetaTabEntryKey  key;
++  char            *title;
++  GdkPixbuf       *icon, *dimmed_icon;
++  GtkWidget       *widget;
++  GdkRectangle     rect;
++  GdkRectangle     inner_rect;
++  guint blank : 1;
++};
++
++struct _MetaTabPopup
++{
++  GtkWidget *window;
++  GtkWidget *label;
++  GList *current;
++  GList *entries;
++  TabEntry *current_selected_entry;
++  GtkWidget *outline_window;
++  gboolean outline;
++};
++
++static GtkWidget* selectable_image_new (GdkPixbuf *pixbuf);
++static void       select_image         (GtkWidget *widget);
++static void       unselect_image       (GtkWidget *widget);
++
++static GtkWidget* selectable_workspace_new (MetaWorkspace *workspace);
++static void       select_workspace         (GtkWidget *widget);
++static void       unselect_workspace       (GtkWidget *widget);
++
++static gboolean
++outline_window_expose (GtkWidget      *widget,
++                       GdkEventExpose *event,
++                       gpointer        data)
++{
++  MetaTabPopup *popup;
++  TabEntry *te;  
++  
++  popup = data;
++
++  if (!popup->outline || popup->current_selected_entry == NULL)
++    return FALSE;
++
++  te = popup->current_selected_entry;
++  
++  gdk_draw_rectangle (widget->window,
++                      widget->style->white_gc,
++                      FALSE,
++                      0, 0,
++                      te->rect.width - 1,
++                      te->rect.height - 1);
++
++  gdk_draw_rectangle (widget->window,
++                      widget->style->white_gc,
++                      FALSE,
++                      te->inner_rect.x - 1, te->inner_rect.y - 1,
++                      te->inner_rect.width + 1,
++                      te->inner_rect.height + 1);
++
++  return FALSE;
++}
++
++static GdkPixbuf*
++dimm_icon (GdkPixbuf *pixbuf)
++{
++  int x, y, pixel_stride, row_stride;
++  guchar *row, *pixels;
++  int w, h;
++  GdkPixbuf *dimmed_pixbuf;
++
++  if (gdk_pixbuf_get_has_alpha (pixbuf))
++    {
++      dimmed_pixbuf = gdk_pixbuf_copy (pixbuf);
++    }
++  else
++    {
++      dimmed_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
++    }
++
++  w = gdk_pixbuf_get_width (dimmed_pixbuf);
++  h = gdk_pixbuf_get_height (dimmed_pixbuf);      
++
++  pixel_stride = 4;
++
++  row = gdk_pixbuf_get_pixels (dimmed_pixbuf);
++  row_stride = gdk_pixbuf_get_rowstride (dimmed_pixbuf);
++
++  for (y = 0; y < h; y++)
++    {
++      pixels = row;                     
++      for (x = 0; x < w; x++) 
++        {
++          pixels[3] /= 2;                               
++          pixels += pixel_stride;
++        }                       
++      row += row_stride;
++    }
++  return dimmed_pixbuf;
++}
++
++static TabEntry*  
++tab_entry_new (const MetaTabEntry *entry, 
++               gint                screen_width,
++               gboolean            outline)
++{
++  TabEntry *te;
++
++  /* FIXME: make max title size some random relationship to the
++   * screen, avg char width of our font would be a better number.
++   */
++  int max_chars_per_title = screen_width / 15;
++  
++  te = g_new (TabEntry, 1);
++  te->key = entry->key;
++  te->title = NULL;
++  if (entry->title)
++    {
++      gchar *str;
++      gchar *tmp;
++      gchar *formatter = "%s";
++
++      str = meta_g_utf8_strndup (entry->title, max_chars_per_title);
++
++      if (entry->hidden)
++        {
++          formatter = "[%s]";
++        }
++
++      tmp = g_markup_printf_escaped (formatter, str);
++      g_free (str);
++      str = tmp;
++
++      if (entry->demands_attention) 
++        {         
++          /* Escape the whole line of text then markup the text and 
++           * copy it back into the original buffer.
++           */
++          tmp = g_strdup_printf ("<b>%s</b>", str);
++          g_free (str);
++          str = tmp;
++        }
++
++        te->title=g_strdup(str);
++
++      g_free (str);
++    }
++  te->widget = NULL;
++  te->icon = entry->icon;
++  te->blank = entry->blank;
++  te->dimmed_icon = NULL;
++  if (te->icon)
++    {
++      g_object_ref (G_OBJECT (te->icon));
++      if (entry->hidden)
++        te->dimmed_icon = dimm_icon (entry->icon);
++    }
++  
++  if (outline)
++    {
++      te->rect.x = entry->rect.x;
++      te->rect.y = entry->rect.y;
++      te->rect.width = entry->rect.width;
++      te->rect.height = entry->rect.height;
++
++      te->inner_rect.x = entry->inner_rect.x;
++      te->inner_rect.y = entry->inner_rect.y;
++      te->inner_rect.width = entry->inner_rect.width;
++      te->inner_rect.height = entry->inner_rect.height;
++    }
++  return te;
++}
++
++MetaTabPopup*
++meta_ui_tab_popup_new (const MetaTabEntry *entries,
++                       int                 screen_number,
++                       int                 entry_count,
++                       int                 width,
++                       gboolean            outline)
++{
++  MetaTabPopup *popup;
++  int i, left, right, top, bottom;
++  int height;
++  GtkWidget *table;
++  GtkWidget *vbox;
++  GtkWidget *align;
++  GList *tmp;
++  GtkWidget *frame;
++  int max_label_width; /* the actual max width of the labels we create */
++  AtkObject *obj;
++  GdkScreen *screen;
++  int screen_width;
++  
++  popup = g_new (MetaTabPopup, 1);
++
++  popup->outline_window = gtk_window_new (GTK_WINDOW_POPUP);
++
++  screen = gdk_display_get_screen (gdk_display_get_default (),
++                                   screen_number);
++  gtk_window_set_screen (GTK_WINDOW (popup->outline_window),
++                         screen);
++
++  gtk_widget_set_app_paintable (popup->outline_window, TRUE);
++  gtk_widget_realize (popup->outline_window);
++
++  g_signal_connect (G_OBJECT (popup->outline_window), "expose_event",
++                    G_CALLBACK (outline_window_expose), popup);
++  
++  popup->window = gtk_window_new (GTK_WINDOW_POPUP);
++
++  gtk_window_set_screen (GTK_WINDOW (popup->window),
++                         screen);
++
++  gtk_window_set_position (GTK_WINDOW (popup->window),
++                           GTK_WIN_POS_CENTER_ALWAYS);
++  /* enable resizing, to get never-shrink behavior */
++  gtk_window_set_resizable (GTK_WINDOW (popup->window),
++                            TRUE);
++  popup->current = NULL;
++  popup->entries = NULL;
++  popup->current_selected_entry = NULL;
++  popup->outline = outline;
++
++  screen_width = gdk_screen_get_width (screen);
++  for (i = 0; i < entry_count; ++i)
++    {
++      TabEntry* new_entry = tab_entry_new (&entries[i], screen_width, outline);
++      popup->entries = g_list_prepend (popup->entries, new_entry);
++    }
++
++  popup->entries = g_list_reverse (popup->entries);
++    
++  g_assert (width > 0);
++  height = i / width;
++  if (i % width)
++    height += 1;
++
++  table = gtk_table_new (height, width, FALSE);
++  vbox = gtk_vbox_new (FALSE, 0);
++  
++  frame = gtk_frame_new (NULL);
++  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
++  gtk_container_set_border_width (GTK_CONTAINER (table), 1);
++  gtk_container_add (GTK_CONTAINER (popup->window),
++                     frame);
++  gtk_container_add (GTK_CONTAINER (frame),
++                     vbox);
++
++  align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
++
++  gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
++
++  gtk_container_add (GTK_CONTAINER (align),
++                     table);
++
++  popup->label = gtk_label_new ("");
++
++  /* Set the accessible role of the label to a status bar so it
++   * will emit name changed events that can be used by screen
++   * readers.
++   */
++  obj = gtk_widget_get_accessible (popup->label);
++  atk_object_set_role (obj, ATK_ROLE_STATUSBAR);
++
++  gtk_misc_set_padding (GTK_MISC (popup->label), 3, 3);
++
++  gtk_box_pack_end (GTK_BOX (vbox), popup->label, FALSE, FALSE, 0);
++
++  max_label_width = 0;
++  top = 0;
++  bottom = 1;
++  tmp = popup->entries;
++
++  while (tmp && top < height)
++    {      
++      left = 0;
++      right = 1;
++
++      while (tmp && left < width)
++        {
++          GtkWidget *image;
++          GtkRequisition req;
++
++          TabEntry *te;
++
++          te = tmp->data;
++
++          if (te->blank)
++            {
++              /* just stick a widget here to avoid special cases */
++              image = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
++            }
++          else if (outline)
++            {
++              if (te->dimmed_icon)
++                {
++                  image = selectable_image_new (te->dimmed_icon);
++                }
++              else 
++                {
++                  image = selectable_image_new (te->icon);
++                }
++
++              gtk_misc_set_padding (GTK_MISC (image),
++                                    INSIDE_SELECT_RECT + OUTSIDE_SELECT_RECT + 1,
++                                    INSIDE_SELECT_RECT + OUTSIDE_SELECT_RECT + 1);
++              gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.5);
++            }   
++          else
++            {
++              image = selectable_workspace_new ((MetaWorkspace *) te->key);
++            }
++
++          te->widget = image;
++
++          gtk_table_attach (GTK_TABLE (table),
++                            te->widget,
++                            left, right,           top, bottom,
++                            0,                     0,
++                            0,                     0);
++
++          /* Efficiency rules! */
++          gtk_label_set_markup (GTK_LABEL (popup->label),
++                              te->title);
++          gtk_widget_size_request (popup->label, &req);
++          max_label_width = MAX (max_label_width, req.width);
++          
++          tmp = tmp->next;
++          
++          ++left;
++          ++right;
++        }
++      
++      ++top;
++      ++bottom;
++    }
++
++  /* remove all the temporary text */
++  gtk_label_set_text (GTK_LABEL (popup->label), "");
++
++  max_label_width += 20; /* add random padding */
++  
++  gtk_window_set_default_size (GTK_WINDOW (popup->window),
++                               max_label_width,
++                               -1);
++  
++  return popup;
++}
++
++static void
++free_tab_entry (gpointer data, gpointer user_data)
++{
++  TabEntry *te;
++
++  te = data;
++  
++  g_free (te->title);
++  if (te->icon)
++    g_object_unref (G_OBJECT (te->icon));
++  if (te->dimmed_icon)
++    g_object_unref (G_OBJECT (te->dimmed_icon));
++
++  g_free (te);
++}
++
++void
++meta_ui_tab_popup_free (MetaTabPopup *popup)
++{
++  meta_verbose ("Destroying tab popup window\n");
++  
++  gtk_widget_destroy (popup->outline_window);
++  gtk_widget_destroy (popup->window);
++  
++  g_list_foreach (popup->entries, free_tab_entry, NULL);
++
++  g_list_free (popup->entries);
++  
++  g_free (popup);
++}
++
++void
++meta_ui_tab_popup_set_showing (MetaTabPopup *popup,
++                               gboolean      showing)
++{
++  if (showing)
++    {
++      gtk_widget_show_all (popup->window);
++    }
++  else
++    {
++      if (GTK_WIDGET_VISIBLE (popup->window))
++        {
++          meta_verbose ("Hiding tab popup window\n");
++          gtk_widget_hide (popup->window);
++          meta_core_increment_event_serial (gdk_display);
++        }
++    }
++}
++
++static void
++display_entry (MetaTabPopup *popup,
++               TabEntry     *te)
++{
++  GdkRectangle rect;
++  GdkRegion *region;
++  GdkRegion *inner_region;
++
++  
++  if (popup->current_selected_entry)
++  {
++    if (popup->outline)
++      unselect_image (popup->current_selected_entry->widget);
++    else
++      unselect_workspace (popup->current_selected_entry->widget);
++  }
++  
++  gtk_label_set_markup (GTK_LABEL (popup->label), te->title);
++
++  if (popup->outline)
++    select_image (te->widget);
++  else
++    select_workspace (te->widget);
++
++  if (popup->outline)
++    {
++      /* Do stuff behind gtk's back */
++      gdk_window_hide (popup->outline_window->window);
++      meta_core_increment_event_serial (gdk_display);
++  
++      rect = te->rect;
++      rect.x = 0;
++      rect.y = 0;
++
++      gdk_window_move_resize (popup->outline_window->window,
++                              te->rect.x, te->rect.y,
++                              te->rect.width, te->rect.height);
++  
++      gdk_window_set_background (popup->outline_window->window,
++                                 &popup->outline_window->style->black);
++  
++      region = gdk_region_rectangle (&rect);
++      inner_region = gdk_region_rectangle (&te->inner_rect);
++      gdk_region_subtract (region, inner_region);
++      gdk_region_destroy (inner_region);
++  
++      gdk_window_shape_combine_region (popup->outline_window->window,
++                                       region,
++                                       0, 0);
++
++      gdk_region_destroy (region);
++  
++      /* This should piss off gtk a bit, but we don't want to raise
++       * above the tab popup.  So, instead of calling gtk_widget_show,
++       * we manually set the window as mapped and then manually map it
++       * with gdk functions.
++       */
++      GTK_WIDGET_SET_FLAGS (popup->outline_window, GTK_MAPPED);
++      gdk_window_show_unraised (popup->outline_window->window);
++    }
++
++  /* Must be before we handle an expose for the outline window */
++  popup->current_selected_entry = te;
++}
++
++void
++meta_ui_tab_popup_forward (MetaTabPopup *popup)
++{
++  if (popup->current != NULL)
++    popup->current = popup->current->next;
++
++  if (popup->current == NULL)
++    popup->current = popup->entries;
++  
++  if (popup->current != NULL)
++    {
++      TabEntry *te;
++
++      te = popup->current->data;
++
++      display_entry (popup, te);
++    }
++}
++
++void
++meta_ui_tab_popup_backward (MetaTabPopup *popup)
++{
++  if (popup->current != NULL)
++    popup->current = popup->current->prev;
++
++  if (popup->current == NULL)
++    popup->current = g_list_last (popup->entries);
++  
++  if (popup->current != NULL)
++    {
++      TabEntry *te;
++
++      te = popup->current->data;
++
++      display_entry (popup, te);
++    }
++}
++
++MetaTabEntryKey
++meta_ui_tab_popup_get_selected (MetaTabPopup *popup)
++{
++  if (popup->current)
++    {
++      TabEntry *te;
++
++      te = popup->current->data;
++
++      return te->key;
++    }
++  else
++    return (MetaTabEntryKey)None;
++}
++
++void
++meta_ui_tab_popup_select (MetaTabPopup *popup,
++                          MetaTabEntryKey key)
++{
++  GList *tmp;
++
++  /* Note, "key" may not be in the list of entries; other code assumes
++   * it's OK to pass in a key that isn't.
++   */
++  
++  tmp = popup->entries;
++  while (tmp != NULL)
++    {
++      TabEntry *te;
++
++      te = tmp->data;
++
++      if (te->key == key)
++        {
++          popup->current = tmp;
++          
++          display_entry (popup, te);
++
++          return;
++        }
++      
++      tmp = tmp->next;
++    }
++}
++
++#define META_TYPE_SELECT_IMAGE            (meta_select_image_get_type ())
++#define META_SELECT_IMAGE(obj)            (GTK_CHECK_CAST ((obj), META_TYPE_SELECT_IMAGE, MetaSelectImage))
++
++typedef struct _MetaSelectImage       MetaSelectImage;
++typedef struct _MetaSelectImageClass  MetaSelectImageClass;
++
++struct _MetaSelectImage
++{
++  GtkImage parent_instance;
++  guint selected : 1;
++};
++
++struct _MetaSelectImageClass
++{
++  GtkImageClass parent_class;
++};
++
++
++static GType meta_select_image_get_type (void) G_GNUC_CONST;
++
++static GtkWidget*
++selectable_image_new (GdkPixbuf *pixbuf)
++{
++  GtkWidget *w;
++
++  w = g_object_new (meta_select_image_get_type (), NULL);
++  gtk_image_set_from_pixbuf (GTK_IMAGE (w), pixbuf); 
++
++  return w;
++}
++
++static void
++select_image (GtkWidget *widget)
++{
++  META_SELECT_IMAGE (widget)->selected = TRUE;
++  gtk_widget_queue_draw (widget);
++}
++
++static void
++unselect_image (GtkWidget *widget)
++{
++  META_SELECT_IMAGE (widget)->selected = FALSE;
++  gtk_widget_queue_draw (widget);
++}
++
++static void     meta_select_image_class_init   (MetaSelectImageClass *klass);
++static gboolean meta_select_image_expose_event (GtkWidget            *widget,
++                                                GdkEventExpose       *event);
++
++static GtkImageClass *parent_class;
++
++GType
++meta_select_image_get_type (void)
++{
++  static GtkType image_type = 0;
++
++  if (!image_type)
++    {
++      static const GTypeInfo image_info =
++      {
++        sizeof (MetaSelectImageClass),
++        NULL,           /* base_init */
++        NULL,           /* base_finalize */
++        (GClassInitFunc) meta_select_image_class_init,
++        NULL,           /* class_finalize */
++        NULL,           /* class_data */
++        sizeof (MetaSelectImage),
++        16,             /* n_preallocs */
++        (GInstanceInitFunc) NULL,
++      };
++
++      image_type = g_type_register_static (GTK_TYPE_IMAGE, "MetaSelectImage", &image_info, 0);
++    }
++
++  return image_type;
++}
++
++static void
++meta_select_image_class_init (MetaSelectImageClass *klass)
++{
++  GtkWidgetClass *widget_class;
++  
++  parent_class = gtk_type_class (gtk_image_get_type ());
++
++  widget_class = GTK_WIDGET_CLASS (klass);
++  
++  widget_class->expose_event = meta_select_image_expose_event;
++}
++
++static gboolean
++meta_select_image_expose_event (GtkWidget      *widget,
++                                GdkEventExpose *event)
++{
++  if (META_SELECT_IMAGE (widget)->selected)
++    {
++      int x, y, w, h;
++      GtkMisc *misc;
++
++      misc = GTK_MISC (widget);
++      
++      x = (widget->allocation.x * (1.0 - misc->xalign) +
++           (widget->allocation.x + widget->allocation.width
++            - (widget->requisition.width - misc->xpad * 2)) *
++           misc->xalign) + 0.5;
++      y = (widget->allocation.y * (1.0 - misc->yalign) +
++           (widget->allocation.y + widget->allocation.height
++            - (widget->requisition.height - misc->ypad * 2)) *
++           misc->yalign) + 0.5;
++
++      x -= INSIDE_SELECT_RECT + 1;
++      y -= INSIDE_SELECT_RECT + 1;      
++      
++      w = widget->requisition.width - OUTSIDE_SELECT_RECT * 2 - 1;
++      h = widget->requisition.height - OUTSIDE_SELECT_RECT * 2 - 1;
++
++      gdk_draw_rectangle (widget->window,
++                          widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
++                          FALSE,
++                          x, y, w, h);
++      gdk_draw_rectangle (widget->window,
++                          widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
++                          FALSE,
++                          x - 1, y - 1, w + 2, h + 2);
++      
++#if 0
++      gdk_draw_rectangle (widget->window,
++                          widget->style->bg_gc[GTK_STATE_SELECTED],
++                          TRUE,
++                          x, y, w, h);
++#endif
++#if 0      
++      gtk_paint_focus (widget->style, widget->window,
++                       &event->area, widget, "meta-tab-image",
++                       x, y, w, h);
++#endif
++    }
++
++  return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
++}
++
++#define META_TYPE_SELECT_WORKSPACE   (meta_select_workspace_get_type ())
++#define META_SELECT_WORKSPACE(obj)   (GTK_CHECK_CAST ((obj), META_TYPE_SELECT_WORKSPACE, MetaSelectWorkspace))
++
++typedef struct _MetaSelectWorkspace       MetaSelectWorkspace;
++typedef struct _MetaSelectWorkspaceClass  MetaSelectWorkspaceClass;
++
++struct _MetaSelectWorkspace
++{
++  GtkDrawingArea parent_instance;
++  MetaWorkspace *workspace;
++  guint selected : 1;
++};
++
++struct _MetaSelectWorkspaceClass
++{
++  GtkDrawingAreaClass parent_class;
++};
++
++
++static GType meta_select_workspace_get_type (void) G_GNUC_CONST;
++
++#define SELECT_OUTLINE_WIDTH 2
++#define MINI_WORKSPACE_WIDTH 48
++
++static GtkWidget*
++selectable_workspace_new (MetaWorkspace *workspace)
++{
++  GtkWidget *widget;
++  double screen_aspect;
++  
++  widget = g_object_new (meta_select_workspace_get_type (), NULL);
++
++  screen_aspect = (double) workspace->screen->rect.height /
++                  (double) workspace->screen->rect.width;
++  
++  /* account for select rect */ 
++  gtk_widget_set_size_request (widget,
++                               MINI_WORKSPACE_WIDTH + SELECT_OUTLINE_WIDTH * 2,
++                               MINI_WORKSPACE_WIDTH * screen_aspect + SELECT_OUTLINE_WIDTH * 2);
++
++  META_SELECT_WORKSPACE (widget)->workspace = workspace;
++
++  return widget;
++}
++
++static void
++select_workspace (GtkWidget *widget)
++{
++  META_SELECT_WORKSPACE(widget)->selected = TRUE;
++  gtk_widget_queue_draw (widget);
++}
++
++static void
++unselect_workspace (GtkWidget *widget)
++{
++  META_SELECT_WORKSPACE (widget)->selected = FALSE;
++  gtk_widget_queue_draw (widget);
++}
++
++static void meta_select_workspace_class_init (MetaSelectWorkspaceClass *klass);
++
++static gboolean meta_select_workspace_expose_event (GtkWidget      *widget,
++                                                    GdkEventExpose *event);
++
++GType
++meta_select_workspace_get_type (void)
++{
++  static GtkType workspace_type = 0;
++
++  if (!workspace_type)
++    {
++      static const GTypeInfo workspace_info =
++      {
++        sizeof (MetaSelectWorkspaceClass),
++        NULL,           /* base_init */
++        NULL,           /* base_finalize */
++        (GClassInitFunc) meta_select_workspace_class_init,
++        NULL,           /* class_finalize */
++        NULL,           /* class_data */
++        sizeof (MetaSelectWorkspace),
++        16,             /* n_preallocs */
++        (GInstanceInitFunc) NULL,
++      };
++
++      workspace_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, 
++                                               "MetaSelectWorkspace", 
++                                               &workspace_info, 
++                                               0);
++    }
++
++  return workspace_type;
++}
++
++static void
++meta_select_workspace_class_init (MetaSelectWorkspaceClass *klass)
++{
++  GtkWidgetClass *widget_class;
++  
++  widget_class = GTK_WIDGET_CLASS (klass);
++  
++  widget_class->expose_event = meta_select_workspace_expose_event;
++}
++
++/**
++ * meta_convert_meta_to_wnck() converts a MetaWindow to a
++ * WnckWindowDisplayInfo window that is used to build a thumbnail of a
++ * workspace.
++ **/
++static WnckWindowDisplayInfo
++meta_convert_meta_to_wnck (MetaWindow *window, MetaScreen *screen)
++{
++  WnckWindowDisplayInfo wnck_window;
++  wnck_window.icon = window->icon;
++  wnck_window.mini_icon = window->mini_icon;
++  
++  wnck_window.is_active = FALSE;
++  if (window == window->display->expected_focus_window)
++    wnck_window.is_active = TRUE;
++
++  if (window->frame)
++    {
++      wnck_window.x = window->frame->rect.x;
++      wnck_window.y = window->frame->rect.y;
++      wnck_window.width = window->frame->rect.width;
++      wnck_window.height = window->frame->rect.height;
++    }
++  else
++    {
++      wnck_window.x = window->rect.x;
++      wnck_window.y = window->rect.y;
++      wnck_window.width = window->rect.width;
++      wnck_window.height = window->rect.height;
++    }
++  return wnck_window;
++}
++
++
++static gboolean
++meta_select_workspace_expose_event (GtkWidget      *widget,
++                                    GdkEventExpose *event)
++{
++  MetaWorkspace *workspace;
++  WnckWindowDisplayInfo *windows;
++  int i, n_windows;
++  GList *tmp, *list;
++
++  workspace = META_SELECT_WORKSPACE (widget)->workspace;
++              
++  list = meta_stack_list_windows (workspace->screen->stack, workspace);
++  n_windows = g_list_length (list);
++  windows = g_new (WnckWindowDisplayInfo, n_windows);
++
++  tmp = list;
++  i = 0;
++  while (tmp != NULL)
++    {
++      MetaWindow *window;
++      gboolean ignoreable_sticky;
++
++      window = tmp->data;
++
++      ignoreable_sticky = window->on_all_workspaces &&
++                          workspace != workspace->screen->active_workspace;
++
++      if (window->skip_pager || 
++          !meta_window_showing_on_its_workspace (window) ||
++          window->unmaps_pending ||
++          ignoreable_sticky)
++        {
++          --n_windows;
++        }
++      else
++        {
++          windows[i] = meta_convert_meta_to_wnck (window, workspace->screen);
++          i++;
++        }
++      tmp = tmp->next;
++    }
++
++  g_list_free (list);
++
++  wnck_draw_workspace (widget,
++                       widget->window,
++                       SELECT_OUTLINE_WIDTH,
++                       SELECT_OUTLINE_WIDTH,
++                       widget->allocation.width - SELECT_OUTLINE_WIDTH * 2,
++                       widget->allocation.height - SELECT_OUTLINE_WIDTH * 2,
++                       workspace->screen->rect.width,
++                       workspace->screen->rect.height,
++                       NULL,
++                       (workspace->screen->active_workspace == workspace),
++                       windows,
++                       n_windows);
++
++  g_free (windows);
++  
++  if (META_SELECT_WORKSPACE (widget)->selected)
++    {
++      i = SELECT_OUTLINE_WIDTH - 1;
++      while (i >= 0)
++        {
++          gdk_draw_rectangle (widget->window,
++                              widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
++                              FALSE,
++                              i, 
++                              i,
++                              widget->allocation.width - i * 2 - 1,
++                              widget->allocation.height - i * 2 - 1);
++
++          --i;
++        }
++    }
++
++  return TRUE;
++}
++
+diff -urN meta.orig/src/theme-viewer.c meta.new/src/theme-viewer.c
+--- meta.orig/src/theme-viewer.c	2007-11-30 12:02:50.536292000 +0000
++++ meta.new/src/theme-viewer.c	2007-11-30 12:04:04.891597000 +0000
 @@ -989,7 +989,11 @@
                               &button_layout,
                               button_states,
@@ -1229,9 +2707,9 @@
  
        g_object_unref (G_OBJECT (pixmap));
        
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/theme.c meta.new/src/theme.c
---- meta.orig/src/theme.c	2007-09-19 18:45:49.267179000 +0100
-+++ meta.new/src/theme.c	2007-09-19 19:07:54.448124000 +0100
+diff -urN meta.orig/src/theme.c meta.new/src/theme.c
+--- meta.orig/src/theme.c	2007-11-30 12:02:50.356868000 +0000
++++ meta.new/src/theme.c	2007-11-30 12:04:04.895555000 +0000
 @@ -32,6 +32,9 @@
  #include <string.h>
  #include <stdlib.h>
@@ -1655,9 +3133,9 @@
      case META_FRAME_PIECE_TITLEBAR:
        return "titlebar";
      case META_FRAME_PIECE_TITLEBAR_MIDDLE:
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/theme.h meta.new/src/theme.h
---- meta.orig/src/theme.h	2007-09-19 18:45:49.267804000 +0100
-+++ meta.new/src/theme.h	2007-09-19 18:50:12.467878000 +0100
+diff -urN meta.orig/src/theme.h meta.new/src/theme.h
+--- meta.orig/src/theme.h	2007-11-30 12:02:50.357660000 +0000
++++ meta.new/src/theme.h	2007-11-30 12:04:04.898043000 +0000
 @@ -42,6 +42,9 @@
  typedef struct _MetaTheme MetaTheme;
  typedef struct _MetaPositionExprEnv MetaPositionExprEnv;
@@ -1773,9 +3251,9 @@
  
  void meta_theme_get_frame_borders (MetaTheme         *theme,
                                     MetaFrameType      type,
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/trusted-pics.h meta.new/src/trusted-pics.h
+diff -urN meta.orig/src/trusted-pics.h meta.new/src/trusted-pics.h
 --- meta.orig/src/trusted-pics.h	1970-01-01 01:00:00.000000000 +0100
-+++ meta.new/src/trusted-pics.h	2007-09-19 18:50:12.469122000 +0100
++++ meta.new/src/trusted-pics.h	2007-11-30 12:04:04.910130000 +0000
 @@ -0,0 +1,728 @@
 +/* Metacity trusted image */
 +
@@ -2505,10 +3983,10 @@
 +
 +
 +#endif /*TRUSTED_PICS_H */
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/trusted.c meta.new/src/trusted.c
+diff -urN meta.orig/src/trusted.c meta.new/src/trusted.c
 --- meta.orig/src/trusted.c	1970-01-01 01:00:00.000000000 +0100
-+++ meta.new/src/trusted.c	2007-09-19 19:09:23.478850000 +0100
-@@ -0,0 +1,1124 @@
++++ meta.new/src/trusted.c	2007-11-30 13:52:01.411193000 +0000
+@@ -0,0 +1,1291 @@
 +/* Metacity trusted */
 +
 +/* 
@@ -2572,6 +4050,12 @@
 +   return handle;
 +}
 +
++static 
++void * dlopen_bsm (void)
++{
++	return dlopen ("/usr/lib/libbsm.so", RTLD_LAZY);
++}
++
 +static gboolean 
 +tsol_is_multi_label_session (void)
 +{
@@ -2593,6 +4077,7 @@
 +  static gpointer tsol_handle = NULL;
 +  static gpointer xtsol_handle = NULL;
 +  static gpointer gnometsol_handle = NULL;
++  static gpointer bsm_handle = NULL;
 +
 +    if (!_trusted_extensions_initialised) {
 +        char *label = NULL;
@@ -2602,10 +4087,12 @@
 +            return FALSE;
 +
 +        tsol_handle = dlopen_tsol ();
-+        if (tsol_handle != NULL)
-+            xtsol_handle = dlopen_xtsol ();
-+        if (tsol_handle && xtsol_handle) {
-+
++        xtsol_handle = dlopen_xtsol ();
++	bsm_handle = dlopen_bsm ();  
++
++        if (tsol_handle && xtsol_handle && bsm_handle) {
++	   /* libbsm function (only interested in the one) */
++	   libbsm_getdevicerange = (bsm_getdevicerange) dlsym (bsm_handle, "getdevicerange");
 +           /* Replacement libtsol functions */
 +           libtsol_label_to_str = (tsol_label_to_str) dlsym (tsol_handle, "label_to_str"); 
 +           libtsol_str_to_label = (tsol_str_to_label) dlsym (tsol_handle, "str_to_label");
@@ -2613,12 +4100,12 @@
 +
 +
 +           /* Other misc. libtsol functions */
-+           /* libtsol_blminimum = (tsol_blminimum) dlsym (tsol_handle, "blminimum"); */
-+           /* libtsol_blmaximum = (tsol_blmaximum) dlsym (tsol_handle, "blmaximum"); */
-+           libtsol_blinrange = (tsol_blinrange) dlsym (tsol_handle, "blinrange");
-+           /* libtsol_getuserrange = (tsol_getuserrange) dlsym (tsol_handle, "getuserrange"); */
++            libtsol_blminimum = (tsol_blminimum) dlsym (tsol_handle, "blminimum"); 
++            libtsol_blmaximum = (tsol_blmaximum) dlsym (tsol_handle, "blmaximum"); 
++            libtsol_blinrange = (tsol_blinrange) dlsym (tsol_handle, "blinrange");
++            libtsol_getuserrange = (tsol_getuserrange) dlsym (tsol_handle, "getuserrange"); 
 +           /* libtsol_blabel_alloc = (tsol_blabel_alloc) dlsym (tsol_handle, "blabel_alloc"); */
-+           /* libtsol_blabel_free  = (tsol_blabel_free)  dlsym (tsol_handle, "blabel_free"); */
++            libtsol_blabel_free  = (tsol_blabel_free)  dlsym (tsol_handle, "blabel_free"); 
 +           /* libtsol_bsllow  = (tsol_bsllow)  dlsym (tsol_handle, "bsllow"); */
 +           /* libtsol_bslhigh = (tsol_bslhigh) dlsym (tsol_handle, "bslhigh"); */
 +
@@ -2636,19 +4123,18 @@
 +	   libxtsol_XTSOLgetResUID = (xtsol_XTSOLgetResUID) dlsym (xtsol_handle,
 +									     "XTSOLgetResUID");
 +
-+           if (/*libtsol_stobsl == NULL ||
++           if (libbsm_getdevicerange == NULL || 
++	     /*libtsol_stobsl == NULL ||
 +               libtsol_bsltos == NULL || */
 +               libtsol_label_to_str == NULL || 
 +               libtsol_str_to_label == NULL ||
 +               libtsol_m_label_free == NULL ||
-+               /*libtsol_blminimum == NULL ||
-+               libtsol_blmaximum == NULL ||*/
++               libtsol_blminimum == NULL ||
++               libtsol_blmaximum == NULL ||
 +               libtsol_blinrange == NULL ||
-+               /* libtsol_getdevicerange == NULL || 
 +               libtsol_getuserrange == NULL ||
-+               libtsol_blabel_alloc == NULL ||
 +               libtsol_blabel_free  == NULL ||
-+               libtsol_bsllow  == NULL ||
++               /* libtsol_bsllow  == NULL ||
 +               libtsol_bslhigh == NULL || */
 +               libxtsol_XTSOLgetClientLabel == NULL ||
 +               libxtsol_XTSOLIsWindowTrusted == NULL ||
@@ -2680,7 +4166,7 @@
 +	  }
 +	
 +    }
-+    return ((tsol_handle != NULL) && (xtsol_handle != NULL) && (gnometsol_handle != NULL));
++    return ((tsol_handle != NULL) && (xtsol_handle != NULL) && (gnometsol_handle != NULL) && (bsm_handle != NULL));
 +}
 +
 +static gboolean
@@ -3147,7 +4633,7 @@
 +  return FALSE;
 +}
 +
-+gboolean tsol_meta_workspace_has_roleas_role (MetaWorkspace *ws)
++gboolean tsol_meta_workspace_has_role (MetaWorkspace *ws)
 +{
 +  char **role_list = NULL;
 +  int nb_roles;
@@ -3183,10 +4669,48 @@
 +    return FALSE;
 +}
 +
++char * tsol_meta_workspace_get_role (MetaWorkspace *ws)
++{
++  char **role_list = NULL;
++  int nb_roles;
++
++  if (!tsol_is_available ())
++    return NULL;
++
++  if (meta_prop_get_utf8_list (ws->screen->display,
++                   ws->screen->xroot,
++                   ws->screen->display->atom_net_desktop_roles,
++                   &role_list, &nb_roles))
++    {
++      int ws_id = meta_workspace_index (ws);
++      if (ws_id > nb_roles) /* something is wrong here we don't have the same number of roles/ws */
++    {
++      g_strfreev (role_list);
++      return NULL;
++    }
++      if (role_list[ws_id] != NULL && strcmp (role_list[ws_id], "") != 0)
++    {
++      char *return_role_name = NULL;
++      struct passwd *pwd;
++      pwd = getpwuid (getuid ());
++      if (strcmp (role_list[ws_id], pwd->pw_name) == 0) /* role is normal user */
++        {
++          g_strfreev (role_list);
++          return NULL;
++        }
++      return_role_name = g_strdup (role_list[ws_id]);
++      g_strfreev (role_list);
++      return return_role_name;
++    }
++      g_strfreev (role_list);
++    }
++    return NULL;
++}
++
 +gboolean tsol_meta_window_can_move_to_workspace (MetaWindow *win,
 +					    MetaWorkspace *ws)
 +{
-+  if (tsol_meta_workspace_has_roleas_role (ws))
++  if (tsol_meta_workspace_has_role (ws))
 +    {
 +      MetaTrustedLabel * label = tsol_meta_window_label_get (win);
 +      /* SUN_BRANDING TJDS */
@@ -3416,6 +4940,105 @@
 +  return TRUE;
 +}
 +
++/*
++ * These private (hint hint) functions assume that they have been called
++ * from within a trusted desktop session. The caller must ensure that
++ * this is the case otherwise it will trigger a load of the potentially
++ * non existant tsol and xtsol libs. That would be bad!
++ */
++static blrange_t *
++get_display_range (void)
++{
++  blrange_t       *range = NULL;
++
++  range = libbsm_getdevicerange ("framebuffer");
++  if (range == NULL) {
++    range = g_malloc (sizeof (blrange_t));
++    range->lower_bound = libtsol_blabel_alloc ();
++    range->upper_bound = libtsol_blabel_alloc ();
++    libtsol_bsllow  (range->lower_bound);
++    libtsol_bslhigh (range->upper_bound);
++  }
++  return (range);
++}
++
++
++/* tsol_label_is_in_role_range
++ *
++ * return FALSE if the label is not in the username role range
++ * not if the role exist and has a range it is returned via role_range
++ * Note if note NULL role_range needs to be freed
++ */
++
++gboolean
++tsol_label_is_in_role_range (const char * label, const char * username, char *min_role_label)
++{
++  /* partial copy of _wnck_workspace_update_role in libwnck */
++  int           error;
++  blrange_t     *role_range;
++  blrange_t    *disp_range;
++  m_label_t *mlabel = NULL;
++  min_role_label = NULL;
++
++  /* validate the label passed */
++
++  if (libtsol_str_to_label (label, &mlabel, MAC_LABEL, L_NO_CORRECTION, &error) < 0)
++    {
++      g_warning("Could not validate sensitivity label \"%s\"", label);
++      g_free (role_range);
++      return FALSE;
++    }
++
++  /*
++   * This is a role workspace so we need to construct the correct label range
++   * instead of relying on USER_MIN_SL and USER_MAX_SL
++   */
++  if ((role_range = libtsol_getuserrange (username)) == NULL)
++    {
++      g_warning ("Couldn't get label range for %s\n", username);
++      return FALSE;
++    }
++
++  /* Get display device's range */
++  if ((disp_range = get_display_range ()) == NULL)
++    {
++      g_warning ("Couldn't get the display's device range");
++      return FALSE;
++    }
++
++  /*
++   * Determine the low & high bound of the label range
++   * where the role user can operate. This is the
++   * intersection of display label range & role label
++   * range.
++   */
++  libtsol_blmaximum (role_range->lower_bound, disp_range->lower_bound);
++  libtsol_blminimum (role_range->upper_bound, disp_range->upper_bound);
++
++  libtsol_blabel_free (disp_range->lower_bound);
++  libtsol_blabel_free (disp_range->upper_bound);
++  g_free (disp_range);
++
++  /* check if in range */
++
++  if (!libtsol_blinrange (mlabel, role_range))
++    {
++      libtsol_m_label_free (mlabel);
++      libtsol_label_to_str (role_range->lower_bound, &min_role_label, M_INTERNAL, DEF_NAMES, &error);
++      libtsol_blabel_free (role_range->lower_bound);
++      libtsol_blabel_free (role_range->upper_bound);
++      g_free (role_range);
++      return FALSE;
++    }
++
++  libtsol_blabel_free (role_range->lower_bound);
++  libtsol_blabel_free (role_range->upper_bound);
++  g_free (role_range);
++
++  libtsol_m_label_free (mlabel);
++
++  return TRUE;
++}
 +
 +
 +/* boolean is used to select between label or roles */
@@ -3437,11 +5060,18 @@
 +      if (label)
 +	{
 +	  name = meta_prefs_get_workspace_label (i);
-+	  /* default to min label range if the workspace label isn't defined */
-+	  if (name == NULL) 
-+	    name = tsol_label_get_min ();
-+	  if (!tsol_label_is_in_user_range (name))
-+	    name = tsol_label_get_min ();
++	
++	   if (!tsol_meta_workspace_has_role (meta_screen_get_workspace_by_index (screen, i)))
++        {
++          /* default to min label range if the workspace label isn't defined */
++          /* printf ("set min label on a workspace (%d) that as a role !\n", i); */
++          if (name == NULL)
++        name = tsol_label_get_min ();
++          if (!tsol_label_is_in_user_range (name))
++        {
++          name = tsol_label_get_min ();
++        }
++        }
 +	}
 +      else
 +	name = meta_prefs_get_workspace_role (i);
@@ -3523,13 +5153,28 @@
 +      * NOTE : if USER_MIN_SL is not properly set you can have an infinite loop here */
 +      if (names[i] && !tsol_label_is_in_user_range (names[i]))
 +	{
-+	  g_free (names[i]);
-+	  names[i] = g_strdup (tsol_label_get_min ());
++	  if (!tsol_meta_workspace_has_role (meta_screen_get_workspace_by_index (screen, i)))
++        {
++          g_free (names[i]);
++          names[i] = g_strdup (tsol_label_get_min ());
++        }
++      else
++        {
++          char *min_role_label = NULL;
++          char *role = tsol_meta_workspace_get_role (meta_screen_get_workspace_by_index (screen, i));
++          if (!tsol_label_is_in_role_range (names[i], role, min_role_label))
++        {
++          if (min_role_label)
++              names[i] = min_role_label;
++          else
++              names[i] = g_strdup (tsol_label_get_min ());
++        }
++        }
 +	}
 +	  
 +      meta_topic (META_DEBUG_PREFS,
-+                  "Setting workspace label %d name to \"%s\" due to _NET_DESKTOP_LABELS change\n",
-+                  i, names[i] ? names[i] : "null");
++		"Setting workspace label %d name to \"%s\" due to _NET_DESKTOP_LABELS change ROLE workspace :%s \n", i, names[i] ? names[i] : "null",
++          tsol_meta_workspace_has_role (meta_screen_get_workspace_by_index (screen, i)) ? "TRUE" : "FALSE");
 +      meta_prefs_change_workspace_label (i, names[i]);
 +      
 +      ++i;
@@ -3633,10 +5278,10 @@
 +
 +
 +#endif
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/trusted.h meta.new/src/trusted.h
+diff -urN meta.orig/src/trusted.h meta.new/src/trusted.h
 --- meta.orig/src/trusted.h	1970-01-01 01:00:00.000000000 +0100
-+++ meta.new/src/trusted.h	2007-09-19 18:50:12.470811000 +0100
-@@ -0,0 +1,193 @@
++++ meta.new/src/trusted.h	2007-11-30 13:44:47.747423000 +0000
+@@ -0,0 +1,202 @@
 +/* Metacity trusted */
 +
 +/* 
@@ -3747,6 +5392,8 @@
 +					ResourceType resourceFlag,
 +					uid_t *uidp);
 +
++/* libbsm provides getdevicerange(3TSOL) - don't believe the man page */
++typedef blrange_t*      (*bsm_getdevicerange) (const char *device);
 +
 +tsol_label_to_str	libtsol_label_to_str;
 +tsol_str_to_label	libtsol_str_to_label;
@@ -3759,6 +5406,8 @@
 +tsol_blabel_free	libtsol_blabel_free;
 +tsol_bsllow		libtsol_bsllow;
 +tsol_bslhigh		libtsol_bslhigh;
++/* libbsm functions */
++bsm_getdevicerange      libbsm_getdevicerange;
 +
 +gnome_tsol_constraint_image_render libgnome_tsol_constraint_image_render;
 +gnome_tsol_constraint_image_set_border libgnome_tsol_constraint_image_set_border;
@@ -3798,7 +5447,7 @@
 +
 +gboolean tsol_should_label_layout_be_black (MetaColorSpec *bkg);
 +gboolean tsol_meta_workspace_has_role (MetaWorkspace *ws);
-+gboolean tsol_meta_workspace_has_roleas_role (MetaWorkspace *ws);
++gboolean tsol_meta_workspace_has_role (MetaWorkspace *ws);
 +
 +gboolean tsol_meta_window_can_move_to_workspace (MetaWindow    *win,
 +						 MetaWorkspace *ws);
@@ -3822,6 +5471,11 @@
 +const char     *tsol_label_get_min ();
 +const char     *tsol_label_get_max ();
 +
++gboolean tsol_label_is_in_role_range (const char * label,
++                      const char * username,
++                      char *min_role_label);
++
++
 +ConstraintImage * tsol_get_highlight_stripe (char     *name,
 +					     GdkColor *label_color);
 +
@@ -3830,9 +5484,9 @@
 +
 +#endif /*HAVE_XTSOL*/
 +#endif /*TRUSTED_H*/
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/ui.c meta.new/src/ui.c
---- meta.orig/src/ui.c	2007-09-19 18:45:49.577670000 +0100
-+++ meta.new/src/ui.c	2007-09-19 18:50:12.471446000 +0100
+diff -urN meta.orig/src/ui.c meta.new/src/ui.c
+--- meta.orig/src/ui.c	2007-11-30 12:02:50.536477000 +0000
++++ meta.new/src/ui.c	2007-11-30 12:04:04.948000000 +0000
 @@ -953,6 +953,16 @@
    return threshold;
  }
@@ -3850,9 +5504,9 @@
  MetaUIDirection
  meta_ui_get_direction (void)
  {
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/ui.h meta.new/src/ui.h
---- meta.orig/src/ui.h	2007-09-19 18:45:49.576430000 +0100
-+++ meta.new/src/ui.h	2007-09-19 18:50:12.471931000 +0100
+diff -urN meta.orig/src/ui.h meta.new/src/ui.h
+--- meta.orig/src/ui.h	2007-11-30 12:02:50.534883000 +0000
++++ meta.new/src/ui.h	2007-11-30 12:04:04.948489000 +0000
 @@ -30,6 +30,7 @@
  #include <X11/Xutil.h>
  #include <glib.h>
@@ -3874,9 +5528,9 @@
  #include "tabpopup.h"
  
  #endif
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/window.c meta.new/src/window.c
---- meta.orig/src/window.c	2007-09-19 18:45:49.750606000 +0100
-+++ meta.new/src/window.c	2007-09-19 18:50:12.475952000 +0100
+diff -urN meta.orig/src/window.c meta.new/src/window.c
+--- meta.orig/src/window.c	2007-11-30 12:02:50.766362000 +0000
++++ meta.new/src/window.c	2007-11-30 13:45:11.830592000 +0000
 @@ -29,6 +29,7 @@
  #include "edge-resistance.h"
  #include "util.h"
@@ -3906,7 +5560,7 @@
  
    /* And border width, size_hints are the "request" */
    window->border_width = attrs->border_width;
-@@ -730,6 +735,10 @@
+@@ -757,6 +762,10 @@
    meta_stack_add (window->screen->stack, 
                    window);
  
@@ -3917,12 +5571,12 @@
    /* Put our state back where it should be,
     * passing TRUE for is_configure_request, ICCCM says
     * initial map is handled same as configure request
-@@ -1268,6 +1277,17 @@
+@@ -1298,6 +1307,17 @@
  meta_window_located_on_workspace (MetaWindow    *window,
                                    MetaWorkspace *workspace)
  {
 +#ifdef HAVE_XTSOL
-+  if (tsol_meta_workspace_has_roleas_role (workspace))
++  if (tsol_meta_workspace_has_role (workspace))
 +    {
 +      /* SUN_BRANDING TJDS */
 +      if (window->on_all_workspaces && window->decorated && strcmp (tsol_meta_window_label_get (window)->name, _("Trusted Path")) != 0)
@@ -3935,7 +5589,7 @@
    return (window->on_all_workspaces && window->screen == workspace->screen) ||
      (window->workspace == workspace);
  }
-@@ -1706,7 +1726,12 @@
+@@ -1736,7 +1756,12 @@
                G_PRIORITY_DEFAULT_IDLE   /* UPDATE_ICON */
              };
  
@@ -3949,51 +5603,22 @@
              {
                idle_calc_showing,
                idle_move_resize,
-@@ -3179,7 +3204,23 @@
-       if (window->shaded)
-         new_h = fgeom.top_height;
-       else
-+#ifdef HAVE_XTSOL	
-+	{ /* Trusted Frame Layout Modification TFLM */
-+	  if (tsol_is_available ())
-+	    {
-+	      if (window->maximized_vertically)
-+		{
-+		  /*remove label size from height if maximized */
-+		  window->rect.height -= fgeom.top_height;
-+		}
-+	      new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height + fgeom.top_height;
-+	    }
-+	  else
-         new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height;
-+	}
-+#else
-+        new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height;
-+#endif      
+@@ -3138,9 +3163,13 @@
+               is_user_action ? " (user move/resize)" : "",
+               old_rect.x, old_rect.y, old_rect.width, old_rect.height);
+   
+-  if (window->frame)
++  if (window->frame) 
++  {
+     meta_frame_calc_geometry (window->frame,
+                               &fgeom);
++    if (tsol_is_available ())
++      fgeom.top_height += fgeom.top_height; /* Trusted Frame Layout Modification TFLM */
++  }
  
-       frame_size_dx = new_w - window->frame->rect.width;
-       frame_size_dy = new_h - window->frame->rect.height;
-@@ -3383,7 +3424,19 @@
- 
-   values.border_width = 0;
-   values.x = client_move_x;
-+#ifdef HAVE_XTSOL
-+  if (tsol_is_available () && window->frame)
-+    { 
-+      values.y = client_move_y + fgeom.top_height;  /* Trusted Frame Layout Modification TFLM */
-+      /* added padding for tlabel */
-+    }
-+  else
-+    {
-   values.y = client_move_y;
-+    }
-+#else  
-+  values.y = client_move_y;
-+#endif  
-   values.width = window->rect.width;
-   values.height = window->rect.height;
-   
-@@ -4044,6 +4097,11 @@
+   new_rect.x = root_x_nw;
+   new_rect.y = root_y_nw;
+@@ -4074,6 +4103,11 @@
  meta_window_change_workspace (MetaWindow    *window,
                                MetaWorkspace *workspace)
  {
@@ -4005,7 +5630,7 @@
    meta_window_change_workspace_without_transients (window, workspace);
  
    meta_window_foreach_transient (window, change_workspace_foreach,
-@@ -5135,16 +5193,17 @@
+@@ -5165,16 +5199,17 @@
                GList* link;
                link = g_list_find (window->screen->active_workspace->mru_list, 
                                    window);
@@ -4033,7 +5658,7 @@
              }
  
            if (window->frame)
-@@ -6476,6 +6535,17 @@
+@@ -6506,6 +6541,17 @@
  
    if (!window->on_all_workspaces)
      {
@@ -4051,7 +5676,7 @@
        ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR;
  
        if (layout.current_col > 0)
-@@ -6488,6 +6558,7 @@
+@@ -6518,6 +6564,7 @@
        if ((layout.current_row < layout.rows - 1) &&
            ((layout.current_row + 1) * layout.cols + layout.current_col < n_workspaces))
          ops |= META_MENU_OP_MOVE_DOWN;
@@ -4059,9 +5684,71 @@
      }
  
    meta_screen_free_workspace_layout (&layout);
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/window.h meta.new/src/window.h
---- meta.orig/src/window.h	2007-09-19 18:45:49.483342000 +0100
-+++ meta.new/src/window.h	2007-09-19 18:50:12.476587000 +0100
+diff -urN meta.orig/src/window.c.orig meta.new/src/window.c.orig
+--- meta.orig/src/window.c.orig	2007-11-30 12:02:50.841083000 +0000
++++ meta.new/src/window.c.orig	2007-11-30 12:03:02.822862000 +0000
+@@ -190,6 +190,20 @@
+     }
+ }
+ 
++static
++prefs_changed_callback (MetaPreference pref,
++                        gpointer        data)
++{
++  MetaWindow *window = data;
++
++  if (pref == META_PREF_WINDOW_RAISE_ON_FRAME_ONLY)
++    {
++      if (window == window->display->focus_window)
++        if (!meta_prefs_get_window_raise_on_frame_only ())
++          meta_display_grab_focus_window_button (window->display, window);
++    }
++}
++
+ MetaWindow*
+ meta_window_new (MetaDisplay *display,
+                  Window       xwindow,
+@@ -802,6 +816,8 @@
+   if (!display->display_opening && !window->initially_iconic)
+     unminimize_window_and_all_transient_parents (window);
+ 
++  meta_prefs_add_listener (prefs_changed_callback, window);
++
+   meta_error_trap_pop (display, FALSE); /* pop the XSync()-reducing trap */
+   meta_display_ungrab (display);
+  
+@@ -988,6 +1004,8 @@
+       meta_stack_thaw (window->screen->stack);
+     }
+ 
++  meta_prefs_remove_listener (prefs_changed_callback, window);
++
+   meta_window_shutdown_group (window); /* safe to do this early as
+                                         * group.c won't re-add to the
+                                         * group if window->unmanaging
+@@ -5183,8 +5201,7 @@
+            *
+            * There is dicussion in bugs 102209, 115072, and 461577
+            */
+-          if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
+-              !meta_prefs_get_raise_on_click())
++           if (meta_prefs_get_window_raise_on_frame_only () || meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK )
+             meta_display_ungrab_focus_window_button (window->display, window);
+         }
+     }
+@@ -5224,8 +5241,7 @@
+           meta_window_update_layer (window);
+ 
+           /* Re-grab for click to focus and raise-on-click, if necessary */
+-          if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
+-              !meta_prefs_get_raise_on_click ())
++          if (meta_prefs_get_window_raise_on_frame_only () ||  meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
+             meta_display_grab_focus_window_button (window->display, window);
+        }
+     }
+diff -urN meta.orig/src/window.h meta.new/src/window.h
+--- meta.orig/src/window.h	2007-11-30 12:02:50.362956000 +0000
++++ meta.new/src/window.h	2007-11-30 12:04:04.984361000 +0000
 @@ -32,6 +32,8 @@
  #include "util.h"
  #include "stack.h"
@@ -4071,9 +5758,9 @@
  #include <X11/Xutil.h>
  #include <gdk-pixbuf/gdk-pixbuf.h>
  
-diff -urN -x '*~' -x '*.rej*' meta.orig/src/workspace.c meta.new/src/workspace.c
---- meta.orig/src/workspace.c	2007-09-19 18:45:49.485968000 +0100
-+++ meta.new/src/workspace.c	2007-09-19 18:50:12.479678000 +0100
+diff -urN meta.orig/src/workspace.c meta.new/src/workspace.c
+--- meta.orig/src/workspace.c	2007-11-30 12:02:50.513659000 +0000
++++ meta.new/src/workspace.c	2007-11-30 12:04:04.987074000 +0000
 @@ -93,6 +93,10 @@
     */
    
--- a/patches/nautilus-10-trusted-extensions.diff	Fri Nov 30 11:06:05 2007 +0000
+++ b/patches/nautilus-10-trusted-extensions.diff	Fri Nov 30 15:38:48 2007 +0000
@@ -1,6 +1,6 @@
-diff -urN -x '*.orig' -x '*.lo' naut.orig/libnautilus-private/Makefile.am naut.new/libnautilus-private/Makefile.am
---- naut.orig/libnautilus-private/Makefile.am	2007-10-23 16:06:05.254850000 +0100
-+++ naut.new/libnautilus-private/Makefile.am	2007-10-23 16:06:39.609471000 +0100
+diff -urN -x '*.orig' naut.orig/libnautilus-private/Makefile.am naut.new/libnautilus-private/Makefile.am
+--- naut.orig/libnautilus-private/Makefile.am	2007-11-30 10:59:52.473650000 +0000
++++ naut.new/libnautilus-private/Makefile.am	2007-11-30 11:06:49.850683000 +0000
 @@ -189,6 +189,8 @@
  	nautilus-trash-monitor.h \
  	nautilus-tree-view-drag-dest.c \
@@ -10,9 +10,28 @@
  	nautilus-ui-utilities.c \
  	nautilus-ui-utilities.h \
  	nautilus-undo-manager.c \
-diff -urN -x '*.orig' -x '*.lo' naut.orig/libnautilus-private/nautilus-tsol-extensions.c naut.new/libnautilus-private/nautilus-tsol-extensions.c
+diff -urN -x '*.orig' naut.orig/libnautilus-private/nautilus-column-utilities.c naut.new/libnautilus-private/nautilus-column-utilities.c
+--- naut.orig/libnautilus-private/nautilus-column-utilities.c	2007-11-30 10:59:52.554734000 +0000
++++ naut.new/libnautilus-private/nautilus-column-utilities.c	2007-11-30 11:04:57.717327000 +0000
+@@ -113,6 +113,7 @@
+ 					       "label", _("MIME Type"),
+ 					       "description", _("The mime type of the file."),
+ 					       NULL));
++#ifdef HAVE_SELINUX
+ 	columns = g_list_append (columns,
+ 				 g_object_new (NAUTILUS_TYPE_COLUMN,
+ 					       "name", "selinux_context",
+@@ -120,6 +121,7 @@
+ 					       "label", _("SELinux Context"),
+ 					       "description", _("The SELinux security context of the file."),
+ 					       NULL));
++#endif
+ 	
+ 	return columns;
+ }
+diff -urN -x '*.orig' naut.orig/libnautilus-private/nautilus-tsol-extensions.c naut.new/libnautilus-private/nautilus-tsol-extensions.c
 --- naut.orig/libnautilus-private/nautilus-tsol-extensions.c	1970-01-01 01:00:00.000000000 +0100
-+++ naut.new/libnautilus-private/nautilus-tsol-extensions.c	2007-10-23 16:06:39.610014000 +0100
++++ naut.new/libnautilus-private/nautilus-tsol-extensions.c	2007-11-30 11:06:49.851210000 +0000
 @@ -0,0 +1,303 @@
 +/*
 + * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
@@ -317,9 +336,9 @@
 +	
 +	XSync (x_dpy, FALSE);
 +}
-diff -urN -x '*.orig' -x '*.lo' naut.orig/libnautilus-private/nautilus-tsol-extensions.h naut.new/libnautilus-private/nautilus-tsol-extensions.h
+diff -urN -x '*.orig' naut.orig/libnautilus-private/nautilus-tsol-extensions.h naut.new/libnautilus-private/nautilus-tsol-extensions.h
 --- naut.orig/libnautilus-private/nautilus-tsol-extensions.h	1970-01-01 01:00:00.000000000 +0100
-+++ naut.new/libnautilus-private/nautilus-tsol-extensions.h	2007-10-23 16:06:39.610273000 +0100
++++ naut.new/libnautilus-private/nautilus-tsol-extensions.h	2007-11-30 11:06:49.851468000 +0000
 @@ -0,0 +1,20 @@
 +/*
 + * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
@@ -341,9 +360,9 @@
 +void nautilus_tsol_update_paste_location_property (GtkClipboard *cb, char *dir);
 +
 +#endif
-diff -urN -x '*.orig' -x '*.lo' naut.orig/src/file-manager/fm-directory-view.c naut.new/src/file-manager/fm-directory-view.c
---- naut.orig/src/file-manager/fm-directory-view.c	2007-10-23 16:06:06.623123000 +0100
-+++ naut.new/src/file-manager/fm-directory-view.c	2007-10-23 16:06:39.616440000 +0100
+diff -urN -x '*.orig' naut.orig/src/file-manager/fm-directory-view.c naut.new/src/file-manager/fm-directory-view.c
+--- naut.orig/src/file-manager/fm-directory-view.c	2007-11-30 10:59:54.163225000 +0000
++++ naut.new/src/file-manager/fm-directory-view.c	2007-11-30 11:06:49.857002000 +0000
 @@ -105,6 +105,7 @@
  #include <libnautilus-private/nautilus-trash-monitor.h>
  #include <libnautilus-private/nautilus-ui-utilities.h>
@@ -460,10 +479,10 @@
  	
  	gtk_clipboard_request_contents (get_clipboard (view),
  					copied_files_atom,
-diff -urN -x '*.orig' -x '*.lo' naut.orig/src/file-manager/fm-properties-window.c naut.new/src/file-manager/fm-properties-window.c
---- naut.orig/src/file-manager/fm-properties-window.c	2007-10-23 16:06:06.619536000 +0100
-+++ naut.new/src/file-manager/fm-properties-window.c	2007-10-23 16:53:32.469346000 +0100
-@@ -83,6 +83,8 @@
+diff -urN -x '*.orig' naut.orig/src/file-manager/fm-properties-window.c naut.new/src/file-manager/fm-properties-window.c
+--- naut.orig/src/file-manager/fm-properties-window.c	2007-11-30 10:59:54.107643000 +0000
++++ naut.new/src/file-manager/fm-properties-window.c	2007-11-30 11:06:49.861499000 +0000
+@@ -85,6 +85,8 @@
  #include <string.h>
  #include <cairo.h>
  
@@ -472,7 +491,7 @@
  #if HAVE_SYS_STATVFS_H
  #include <sys/statvfs.h>
  #endif
-@@ -4529,7 +4531,9 @@
+@@ -6922,7 +6924,9 @@
  {
  	GtkWidget *vbox, *button, *hbox;
  	GtkTable *page_table;
@@ -482,7 +501,7 @@
  	GList *file_list;
  	guint last_row;
  
-@@ -4568,11 +4572,23 @@
+@@ -6961,11 +6965,23 @@
  		}
  		
  		gtk_table_set_row_spacing (page_table, page_table->nrows - 1, 18);
@@ -507,9 +526,9 @@
  		append_title_value_pair
  			(window, page_table, _("Last changed:"), 
  			 "date_permissions", _("--"),
-diff -urN -x '*.orig' -x '*.lo' naut.orig/src/nautilus-application.c naut.new/src/nautilus-application.c
---- naut.orig/src/nautilus-application.c	2007-10-23 16:06:06.385621000 +0100
-+++ naut.new/src/nautilus-application.c	2007-10-23 16:06:39.647166000 +0100
+diff -urN -x '*.orig' naut.orig/src/nautilus-application.c naut.new/src/nautilus-application.c
+--- naut.orig/src/nautilus-application.c	2007-11-30 10:59:53.740211000 +0000
++++ naut.new/src/nautilus-application.c	2007-11-30 11:06:49.864704000 +0000
 @@ -101,6 +101,9 @@
  #include <gdk/gdkx.h>
  #include <X11/Xlib.h>
@@ -529,9 +548,9 @@
  	selection_atom = gdk_atom_intern (selection_name, FALSE);
  	g_free (selection_name);
  
-diff -urN -x '*.orig' -x '*.lo' naut.orig/src/nautilus-desktop-window.c naut.new/src/nautilus-desktop-window.c
---- naut.orig/src/nautilus-desktop-window.c	2007-10-23 16:06:06.442497000 +0100
-+++ naut.new/src/nautilus-desktop-window.c	2007-10-23 16:06:39.647643000 +0100
+diff -urN -x '*.orig' naut.orig/src/nautilus-desktop-window.c naut.new/src/nautilus-desktop-window.c
+--- naut.orig/src/nautilus-desktop-window.c	2007-11-30 10:59:53.862682000 +0000
++++ naut.new/src/nautilus-desktop-window.c	2007-11-30 11:06:49.865292000 +0000
 @@ -35,6 +35,10 @@
  #include <libgnome/gnome-macros.h>
  #include <libgnomevfs/gnome-vfs-utils.h>
@@ -564,9 +583,9 @@
  	/* Point window at the desktop folder.
  	 * Note that nautilus_desktop_window_init is too early to do this.
  	 */
-diff -urN -x '*.orig' -x '*.lo' naut.orig/src/nautilus-window-manage-views.c naut.new/src/nautilus-window-manage-views.c
---- naut.orig/src/nautilus-window-manage-views.c	2007-10-23 16:06:06.440683000 +0100
-+++ naut.new/src/nautilus-window-manage-views.c	2007-10-23 16:06:39.655541000 +0100
+diff -urN -x '*.orig' naut.orig/src/nautilus-window-manage-views.c naut.new/src/nautilus-window-manage-views.c
+--- naut.orig/src/nautilus-window-manage-views.c	2007-11-30 10:59:53.840642000 +0000
++++ naut.new/src/nautilus-window-manage-views.c	2007-11-30 11:06:49.866490000 +0000
 @@ -68,6 +68,7 @@
  #include <libnautilus-private/nautilus-trash-directory.h>
  #include <libnautilus-private/nautilus-view-factory.h>