Re: Fixing window grouping



On Wed, 2003-10-01 at 18:00, Havoc Pennington wrote:

>  - pick name of an arbitrary WnckApplication in the class group
>  - if no name set on any WnckApplication, if all the windows in the 
>      group have the same title, you could use that
>  - otherwise use the name of the class

Done.  The code now does this:

- If all the group leaders have the same title, use that.
- Else, if all the windows have the same title, use that.
- Else, use the WM_CLASS.

WnckClassGroup has a name_changed signal, so bizarre apps that change
this should work fine.

Emacs and everything else I tested seem to work nicely with this scheme.

> Or other heuristic along these lines. Of course icon works the same way.

Done.

>  - if adding/removing/changing ABI, the libtool versioning needs to be
>      updated in configure.in

Done.  See below; I put the signal handler pointers inside the existing
#ifdef.

>  - I don't really understand what's going on in tasklist.c, 
>      specifically why WNCK_TASK_APPLICATION continues to exist

Fixed.  I had left the application bits there because I was not sure if
we would use them for anything in the tasklist.

[We still need WnckApplication to keep track of group leaders, but it's
all behind-the-scenes now.]

>  - there are various FIXME and #if 0

Fixed.

>  - WnckClassGroup has a get_name() method returning the class, 
>      I would have: get_name() (human-readable name), get_class or 
>      get_res_class returning the actual res class string, and
> get_icon().

Done.

>  - in screen.h you break the ABI, which means the libtool needs 
>      adjusting as above, but also it's time to add padding to all the 
>      vtables (see WnckScreenClass #if 0)

OK, I put the signals in an #if block, just like the
showing_desktop_changed signal.  This would be for inclusion in 2.2. 
For 2.4 and later, it would probably be a good idea to add the padding
fields.

>  - _wnck_get_res_class_utf8() is a round trip; as such, I think it 
>      should be done only once. Which is just a matter of using 
>      wnck_window_get_resource_class instead in screen.c I think.

Oops, you are right.  Done.

>  -  You are messing up my beautiful while loops! for loops are the 
>       spawn of evil!

Naah.  A for loop is more clear; it reads as

	iterate_over (list) {
		blah blah;
	}

where in a while loop you have to decode what the hell is being
iterated.

The GNOME Programming Guidelines says you should use for loops to
iterate over lists, anyway...

>  - shouldn't your copyright notice say Novell? ;-)  

Mu :)

> Thanks for the patch; does this mean you're the libwnck maintainer now?

Eek, I don't know.  Ideally wnck should just be a Solved Problem(tm),
but I guess it does need a maintainer.  I guess I can do tarball
releases every now and then if needed.

  Federico
? confdefs.h
? libwnck/class-group.c
? libwnck/class-group.h
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/libwnck/ChangeLog,v
retrieving revision 1.174.2.19
diff -u -r1.174.2.19 ChangeLog
--- ChangeLog	31 Jul 2003 10:08:02 -0000	1.174.2.19
+++ ChangeLog	3 Oct 2003 21:36:57 -0000
@@ -1,3 +1,89 @@
+2003-10-03  Federico Mena Quintero  <federico ximian com>
+
+	* libwnck/tasklist.c (WnckTaskType): Added a WNCK_TASK_CLASS_GROUP
+	type.
+	(struct _WnckTask): Added a class_group field, analogous to the
+	application and window fields.
+	(struct _WnckTasklistPrivate): Added class_groups and
+	class_group_hash fields.  Replaced the active_app field with an
+	active_class_group field.
+	(wnck_task_new_from_window): Set the class_group of the task from
+	the window.
+	(wnck_tasklist_update_lists): Create a WnckClassGroup if
+	necessary; do not use widgets for applications.
+	(wnck_task_new_from_class_group): New function.
+	(wnck_task_new_from_application): Set the class_group of the task
+	to NULL.
+	(wnck_task_new_from_startup_sequence): Likewise.
+	(wnck_task_compare): Sort groups before everything else.
+	(wnck_task_state_changed): Use the class_group rather than the
+	application.
+	(wnck_task_get_text): Handle class groups.
+	(wnck_task_button_toggled): Likewise.
+	(wnck_task_popup_menu): Handle class groups as well as
+	applications.
+	(wnck_tasklist_free_tasks): s/active_app/active_class_group.
+	(wnck_tasklist_change_active_task): Likewise.
+	(wnck_task_finalize): Unref the class_group.
+	(wnck_tasklist_init): Create the class_group_hash.
+	(wnck_tasklist_finalize): Free the class_group_hash.
+	(wnck_tasklist_size_request): Use the class groups rather than
+	applications.
+	(wnck_tasklist_size_allocate): Likewise.
+	(wnck_tasklist_forall): Likewise.
+	(wnck_tasklist_remove): Likewise.
+	(wnck_task_get_highest_scored): Likewise.
+	(wnck_tasklist_score_groups): Likewise.
+	(wnck_task_new_from_application): Do not create widgets.
+	(wnck_task_button_toggled): We don't need the window state here,
+	so don't fetch it.
+	(wnck_task_get_text): Use a window's name rather than its icon
+	name.  They seem to be the same for most windows, and Emacs screws
+	up the icon name, setting it to "emacs" only --- it's useless.
+	(WnckTaskType): Removed WNCK_TASK_APPLICATION.
+	(struct _WnckTask): Removed the application-related fields.
+	(struct _WnckTasklistPrivate): Likewise.
+	(wnck_task_finalize): Likewise.
+	(wnck_tasklist_finalize): Likewise.
+	(wnck_tasklist_free_tasks): Likewise.
+	(wnck_tasklist_update_lists): Likewise.
+	(wnck_task_popup_menu): Likewise.
+	(wnck_task_button_toggled): Likewise.
+	(wnck_task_get_text): Likewise.
+	(wnck_task_get_icon): Likewise.
+	(wnck_task_button_press_event): Likewise.
+	(wnck_task_create_widgets): Likewise.
+	(wnck_task_compare): Likewise.
+	(wnck_task_new_from_window): Likewise.
+	(wnck_task_new_from_startup_sequence): Likewise.
+	(wnck_task_app_name_changed): Removed.
+	(wnck_task_new_from_application): Removed.
+	(wnck_task_class_group_expose): Renamed from wnck_task_app_expose().
+
+	* libwnck/screen.h (struct _WnckScreenClass): Added
+	::class_group_opened() and ::class_group_closed() signals.
+
+	* libwnck/screen.c (update_client_list): Handle class groups by
+	creating new ones as needed and getting rid of empty ones.
+	(emit_class_group_opened): New function.
+	(emit_application_closed): New function.
+	(wnck_screen_class_init): Create the new signals.
+
+	* libwnck/window.c (struct _WnckWindowPrivate): Added a
+	class_group field.
+	(wnck_window_get_class_group): New function.
+	(_wnck_window_set_class_group): New function.
+
+	* libwnck/class-group.[ch]: New files that implement a simple
+	set of windows grouped by their resource class names.
+
+	* libwnck/Makefile.am: Added class-group.[ch].
+
+	* libwnck/test-tasklist.c (main): Set a default size so I don't
+	have to resize the test window every time.
+
+	* configure.in: Increment LIBWNCK_CURRENT and LIBWNCK_AGE.
+
 2003-07-29  Arvind Samptur <arvind samptur wipro com>
 
 	* libwnck/xutils.c (_wnck_get_utf8_list): Number of
Index: configure.in
===================================================================
RCS file: /cvs/gnome/libwnck/configure.in,v
retrieving revision 1.73.2.16
diff -u -r1.73.2.16 configure.in
--- configure.in	1 Jul 2003 17:24:42 -0000	1.73.2.16
+++ configure.in	3 Oct 2003 21:36:57 -0000
@@ -8,7 +8,7 @@
 dnl libtool versioning for libwnck
 
 dnl increment if the interface has additions, changes, removals.
-LIBWNCK_CURRENT=9
+LIBWNCK_CURRENT=10
 
 dnl increment any time the source changes; set to
 dnl  0 if you increment CURRENT
@@ -17,7 +17,7 @@
 dnl increment if any interfaces have been added; set to 0
 dnl  if any interfaces have been removed. removal has
 dnl  precedence over adding, so set to 0 if both happened.
-LIBWNCK_AGE=5
+LIBWNCK_AGE=6
 
 AC_SUBST(LIBWNCK_CURRENT)
 AC_SUBST(LIBWNCK_REVISION)
Index: libwnck/Makefile.am
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/Makefile.am,v
retrieving revision 1.16
diff -u -r1.16 Makefile.am
--- libwnck/Makefile.am	31 Oct 2002 05:43:44 -0000	1.16
+++ libwnck/Makefile.am	3 Oct 2003 21:36:57 -0000
@@ -10,6 +10,7 @@
 	libwnck.h		\
 	pager.h			\
 	application.h		\
+	class-group.h		\
 	screen.h		\
 	tasklist.h		\
 	util.h			\
@@ -39,6 +40,7 @@
 
 libwnck_1_la_SOURCES= 		\
 	application.c		\
+	class-group.c		\
 	inlinepixbufs.h		\
 	pager.c			\
 	private.h		\
Index: libwnck/private.h
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/private.h,v
retrieving revision 1.13.2.1
diff -u -r1.13.2.1 private.h
--- libwnck/private.h	23 Jan 2003 02:12:21 -0000	1.13.2.1
+++ libwnck/private.h	3 Oct 2003 21:36:57 -0000
@@ -62,6 +62,10 @@
 
 void _wnck_window_set_application    (WnckWindow      *window,
                                       WnckApplication *app);
+
+void _wnck_window_set_class_group (WnckWindow     *window,
+				   WnckClassGroup *class_group);
+
 void _wnck_application_add_window    (WnckApplication *app,
                                       WnckWindow      *window);
 void _wnck_application_remove_window (WnckApplication *app,
Index: libwnck/screen.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/screen.c,v
retrieving revision 1.22
diff -u -r1.22 screen.c
--- libwnck/screen.c	3 Nov 2002 19:04:18 -0000	1.22
+++ libwnck/screen.c	3 Oct 2003 21:36:57 -0000
@@ -23,6 +23,7 @@
 #include "window.h"
 #include "workspace.h"
 #include "application.h"
+#include "class-group.h"
 #include "xutils.h"
 #include "private.h"
 #include <gdk/gdk.h>
@@ -81,6 +82,8 @@
   WORKSPACE_DESTROYED,
   APPLICATION_OPENED,
   APPLICATION_CLOSED,
+  CLASS_GROUP_OPENED,
+  CLASS_GROUP_CLOSED,
   BACKGROUND_CHANGED,
   SHOWING_DESKTOP_CHANGED,
   LAST_SIGNAL
@@ -116,6 +119,10 @@
                                            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);
 
@@ -251,6 +258,34 @@
                   g_cclosure_marshal_VOID__OBJECT,
                   G_TYPE_NONE, 1, WNCK_TYPE_APPLICATION);
 
+  signals[CLASS_GROUP_OPENED] =
+    g_signal_new ("class_group_opened",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+#if 0
+                  /* FIXME when we can break ABI add this */
+                  G_STRUCT_OFFSET (WnckScreenClass, class_group_opened),
+#else
+		  0,
+#endif
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, WNCK_TYPE_CLASS_GROUP);
+
+  signals[CLASS_GROUP_CLOSED] =
+    g_signal_new ("class_group_closed",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+#if 0
+                  /* FIXME when we can break ABI add this */
+                  G_STRUCT_OFFSET (WnckScreenClass, class_group_closed),
+#else
+		  0,
+#endif
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, WNCK_TYPE_CLASS_GROUP);
+
   signals[BACKGROUND_CHANGED] =
     g_signal_new ("background_changed",
                   G_OBJECT_CLASS_TYPE (object_class),
@@ -757,8 +792,8 @@
   GList *new_list;
   GList *created;
   GList *closed;
-  GList *created_apps;
-  GList *closed_apps;
+  GList *created_apps, *closed_apps;
+  GList *created_class_groups, *closed_class_groups;
   GList *tmp;
   int i;
   GHashTable *new_hash;
@@ -800,6 +835,8 @@
   closed = NULL;
   created_apps = NULL;
   closed_apps = NULL;
+  created_class_groups = NULL;
+  closed_class_groups = NULL;
 
   new_hash = g_hash_table_new (NULL, NULL);
   
@@ -815,10 +852,15 @@
         {
           Window leader;
           WnckApplication *app;
+	  const char *res_class;
+	  WnckClassGroup *class_group;
+	  gulong xid;
           
           window = _wnck_window_create (stack[i], screen);
           created = g_list_prepend (created, window);
 
+	  /* Application */
+
           leader = wnck_window_get_group_leader (window);
           
           app = wnck_application_get (leader);
@@ -829,6 +871,21 @@
             }
           
           _wnck_application_add_window (app, window);
+
+	  /* Class group */
+
+	  xid = wnck_window_get_xid (window);
+
+	  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_stack_list = g_list_prepend (new_stack_list, window);
@@ -852,15 +909,25 @@
       if (g_hash_table_lookup (new_hash, window) == NULL)
         {
           WnckApplication *app;
+	  WnckClassGroup *class_group;
           
           closed = g_list_prepend (closed, window);
 
-          app = wnck_window_get_application (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;
@@ -904,6 +971,8 @@
       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;
@@ -911,7 +980,7 @@
     }
 
   g_list_free (screen->priv->mapped_windows);
-  g_list_free (screen->priv->stacked_windows);  
+  g_list_free (screen->priv->stacked_windows);
   screen->priv->mapped_windows = new_list;
   screen->priv->stacked_windows = new_stack_list;
 
@@ -920,31 +989,22 @@
    * don't handle it, but we do warn about it using reentrancy_guard
    */
 
-  /* Sequence is: application_opened, window_opened, window_closed,
-   * application_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
+  /* 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
    */
-  tmp = created_apps;
-  while (tmp != NULL)
-    {
-      emit_application_opened (screen, WNCK_APPLICATION (tmp->data));
-      
-      tmp = tmp->next;
-    }
-  
-  tmp = created;
-  while (tmp != NULL)
-    {
-      emit_window_opened (screen, WNCK_WINDOW (tmp->data));
-      
-      tmp = tmp->next;
-    }
+  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;
-  tmp = closed;
-  while (tmp != NULL)
+  for (tmp = closed; tmp; tmp = tmp->next)
     {
       WnckWindow *window;
 
@@ -957,21 +1017,13 @@
         }
       
       emit_window_closed (screen, window);
-      
-      tmp = tmp->next;
     }
 
-  tmp = closed_apps;
-  while (tmp != NULL)
-    {
-      WnckApplication *app;
-
-      app = WNCK_APPLICATION (tmp->data);
+  for (tmp = closed_apps; tmp; tmp = tmp->next)
+    emit_application_closed (screen, WNCK_APPLICATION (tmp->data));
 
-      emit_application_closed (screen, app);
-      
-      tmp = tmp->next;
-    }
+  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);
@@ -980,29 +1032,17 @@
     emit_active_window_changed (screen);
   
   /* Now free the closed windows */
-  tmp = closed;
-  while (tmp != NULL)
-    {
-      WnckWindow *window = tmp->data;
-
-      _wnck_window_destroy (window);
-      
-      tmp = tmp->next;
-    }
+  for (tmp = closed; tmp; tmp = tmp->next)
+    _wnck_window_destroy (WNCK_WINDOW (tmp->data));
 
   /* Free the closed apps */
-  tmp = closed_apps;
-  while (tmp != NULL)
-    {
-      WnckApplication *app;
+  for (tmp = closed_apps; tmp; tmp = tmp->next)
+    _wnck_application_destroy (WNCK_APPLICATION (tmp->data));
 
-      app = 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));
 
-      _wnck_application_destroy (app);
-      
-      tmp = tmp->next;
-    }
-  
   g_list_free (closed);
   g_list_free (created);
   g_list_free (closed_apps);
@@ -1400,6 +1440,24 @@
   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
Index: libwnck/screen.h
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/screen.h,v
retrieving revision 1.12
diff -u -r1.12 screen.h
--- libwnck/screen.h	3 Nov 2002 19:04:18 -0000	1.12
+++ libwnck/screen.h	3 Oct 2003 21:36:57 -0000
@@ -28,6 +28,7 @@
 
 /* forward decls */
 typedef struct _WnckApplication WnckApplication;
+typedef struct _WnckClassGroup  WnckClassGroup;
 typedef struct _WnckWindow      WnckWindow;
 typedef struct _WnckWorkspace   WnckWorkspace;
 
@@ -86,6 +87,12 @@
 #if 0
   /* FIXME uncomment all this next time we feel like breaking ABI */
 
+  /* new class group */
+  void (* class_group_opened)       (WnckScreen     *screen,
+                                     WnckClassGroup *class_group);
+  /* class group gone */
+  void (* class_group_closed)       (WnckScreen     *screen,
+                                     WnckClassGroup *class_group);
   /* Toggle showing desktop */
   void (* showing_desktop_changed)  (WnckScreen      *screen);
   
Index: libwnck/tasklist.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/tasklist.c,v
retrieving revision 1.45.2.2
diff -u -r1.45.2.2 tasklist.c
--- libwnck/tasklist.c	25 Jan 2003 21:16:58 -0000	1.45.2.2
+++ libwnck/tasklist.c	3 Oct 2003 21:36:58 -0000
@@ -24,9 +24,9 @@
 #include <stdio.h>
 #include "tasklist.h"
 #include "window.h"
+#include "class-group.h"
 #include "window-action-menu.h"
 #include "workspace.h"
-#include "application.h"
 #include "xutils.h"
 #include "private.h"
 
@@ -38,7 +38,7 @@
  *  Maybe fine tune size_allocate() some more...
  *  Better vertical layout handling
  *  prefs
- *  ellipsizing lables
+ *  ellipsizing labels
  *  support for right-click menu merging w/ bonobo for the applet
  *
  */ 
@@ -66,10 +66,9 @@
 
 typedef enum
 {
-  WNCK_TASK_APPLICATION,
+  WNCK_TASK_CLASS_GROUP,
   WNCK_TASK_WINDOW,
   WNCK_TASK_STARTUP_SEQUENCE
-
 } WnckTaskType;
 
 struct _WnckTask
@@ -83,8 +82,8 @@
   GtkWidget *label;
 
   WnckTaskType type;
-  
-  WnckApplication *application;
+
+  WnckClassGroup *class_group;
   WnckWindow *window;
 #ifdef HAVE_STARTUP_NOTIFICATION
   SnStartupSequence *startup_sequence;
@@ -93,11 +92,12 @@
   gdouble grouping_score;
 
   GList *windows; /* List of the WnckTask for the window,
-		     if this is an application */
+		     if this is a class group */
   gulong state_changed_tag;
   gulong icon_changed_tag;
   gulong name_changed_tag;
-  gulong app_name_changed_tag;
+  gulong class_name_changed_tag;
+  gulong class_icon_changed_tag;
   
   /* task menu */
   GtkWidget *menu;
@@ -122,19 +122,19 @@
   WnckScreen *screen;
 
   WnckTask *active_task; /* NULL if active window not in tasklist */
-  WnckTask *active_app; /* 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 *applications;
 
   /* Not handled by update_lists */
   GList *startup_sequences;
   
+  GHashTable *class_group_hash;
   GHashTable *win_hash;
-  GHashTable *app_hash;
   
   GtkTooltips *tooltips;
 
@@ -172,8 +172,8 @@
 
 static WnckTask *wnck_task_new_from_window      (WnckTasklist    *tasklist,
 						 WnckWindow      *window);
-static WnckTask *wnck_task_new_from_application (WnckTasklist    *tasklist,
-						 WnckApplication *application);
+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);
@@ -340,13 +340,20 @@
       task->name_changed_tag = 0;
     }
 
-  if (task->app_name_changed_tag)
+  if (task->class_name_changed_tag)
     {
-      g_signal_handler_disconnect (task->application,
-				   task->app_name_changed_tag);
-      task->app_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)
+    {
+      g_signal_handler_disconnect (task->class_group,
+				   task->class_icon_changed_tag);
+      task->class_icon_changed_tag = 0;
+    }
+
   if (task->menu)
     {
       gtk_widget_destroy (task->menu);
@@ -365,10 +372,10 @@
       task->window = NULL;
     }
 
-  if (task->application)
+  if (task->class_group)
     {
-      g_object_unref (task->application);
-      task->application = NULL;
+      g_object_unref (task->class_group);
+      task->class_group = NULL;
     }
 
 #ifdef HAVE_STARTUP_NOTIFICATION
@@ -428,7 +435,7 @@
   tasklist->priv->include_all_workspaces = FALSE;
   
   tasklist->priv->win_hash = g_hash_table_new (NULL, NULL);
-  tasklist->priv->app_hash = g_hash_table_new (NULL, NULL);
+  tasklist->priv->class_group_hash = g_hash_table_new (NULL, NULL);
   
   tasklist->priv->grouping = WNCK_TASKLIST_AUTO_GROUP;
   tasklist->priv->grouping_limit = DEFAULT_GROUPING_LIMIT;
@@ -473,15 +480,15 @@
    * buttons in container destruction
    */
   g_assert (tasklist->priv->windows == NULL);
-  g_assert (tasklist->priv->applications == NULL);
+  g_assert (tasklist->priv->class_groups == NULL);
   g_assert (tasklist->priv->startup_sequences == NULL);
   /* wnck_tasklist_free_tasks (tasklist); */
   
   g_hash_table_destroy (tasklist->priv->win_hash);
   tasklist->priv->win_hash = NULL;
   
-  g_hash_table_destroy (tasklist->priv->app_hash);
-  tasklist->priv->app_hash = NULL;
+  g_hash_table_destroy (tasklist->priv->class_group_hash);
+  tasklist->priv->class_group_hash = NULL;
   
   if (tasklist->priv->activate_timeout_id != 0)
     gtk_timeout_remove (tasklist->priv->activate_timeout_id);
@@ -666,9 +673,9 @@
 
 static void
 wnck_tasklist_score_groups (WnckTasklist *tasklist,
-			    GList        *ungrouped_apps)
+			    GList        *ungrouped_class_groups)
 {
-  WnckTask *app_task;
+  WnckTask *class_group_task;
   WnckTask *win_task;
   GList *l, *w;
   const char *first_name = NULL;
@@ -676,15 +683,15 @@
   int n_same_title;
   double same_window_ratio;
 
-  l = ungrouped_apps;
+  l = ungrouped_class_groups;
   while (l != NULL)
     {
-      app_task = WNCK_TASK (l->data);
+      class_group_task = WNCK_TASK (l->data);
 
-      n_windows = g_list_length (app_task->windows);
+      n_windows = g_list_length (class_group_task->windows);
 
       n_same_title = 0;
-      w = app_task->windows;
+      w = class_group_task->windows;
       while (w != NULL)
 	{
 	  win_task = WNCK_TASK (w->data);
@@ -709,38 +716,38 @@
        *        XP groups by least used, so we probably want to add
        *        total focused time to this expression.
        */
-      app_task->grouping_score = -same_window_ratio * 5 + n_windows;
+      class_group_task->grouping_score = -same_window_ratio * 5 + n_windows;
 
       l = l->next;
     }
 }
 
 static GList *
-wnck_task_get_highest_scored (GList     *ungrouped_apps,
-			      WnckTask **app_task_out)
+wnck_task_get_highest_scored (GList     *ungrouped_class_groups,
+			      WnckTask **class_group_task_out)
 {
-  WnckTask *app_task;
+  WnckTask *class_group_task;
   WnckTask *best_task = NULL;
   double max_score = -1000000000.0; /* Large negative score */
   GList *l;
  
-  l = ungrouped_apps;
+  l = ungrouped_class_groups;
   while (l != NULL)
     {
-      app_task = WNCK_TASK (l->data);
+      class_group_task = WNCK_TASK (l->data);
 
-      if (app_task->grouping_score >= max_score)
+      if (class_group_task->grouping_score >= max_score)
 	{
-	  max_score = app_task->grouping_score;
-	  best_task = app_task;
+	  max_score = class_group_task->grouping_score;
+	  best_task = class_group_task;
 	}
       
       l = l->next;
     }
 
-  *app_task_out = best_task;
+  *class_group_task_out = best_task;
 
-  return g_list_remove (ungrouped_apps, best_task);
+  return g_list_remove (ungrouped_class_groups, best_task);
 }
 
 static void
@@ -755,7 +762,7 @@
   /* int u_width, u_height; */
   GList *l;
   GArray *array;
-  GList *ungrouped_apps;
+  GList *ungrouped_class_groups;
   int n_windows;
   int n_startup_sequences;
   int n_rows;
@@ -763,7 +770,7 @@
   int n_grouped_buttons;
   gboolean score_set;
   int val;
-  WnckTask *app_task;
+  WnckTask *class_group_task;
   int lowest_range;
   int grouping_limit;
   
@@ -785,7 +792,7 @@
       l = l->next;
     }
 
-  l = tasklist->priv->applications;
+  l = tasklist->priv->class_groups;
   while (l != NULL)
     {
       WnckTask *task = WNCK_TASK (l->data);
@@ -852,7 +859,7 @@
   n_windows = g_list_length (tasklist->priv->windows);
   n_startup_sequences = g_list_length (tasklist->priv->startup_sequences);
   n_grouped_buttons = 0;
-  ungrouped_apps = g_list_copy (tasklist->priv->applications);
+  ungrouped_class_groups = g_list_copy (tasklist->priv->class_groups);
   score_set = FALSE;
 
   grouping_limit = MIN (tasklist->priv->grouping_limit,
@@ -878,18 +885,18 @@
       lowest_range = val;
     }
 
-  while (ungrouped_apps != NULL &&
+  while (ungrouped_class_groups != NULL &&
 	 tasklist->priv->grouping != WNCK_TASKLIST_NEVER_GROUP)
     {
       if (!score_set)
 	{
-	  wnck_tasklist_score_groups (tasklist, ungrouped_apps);
+	  wnck_tasklist_score_groups (tasklist, ungrouped_class_groups);
 	  score_set = TRUE;
 	}
 
-      ungrouped_apps = wnck_task_get_highest_scored (ungrouped_apps, &app_task);
+      ungrouped_class_groups = wnck_task_get_highest_scored (ungrouped_class_groups, &class_group_task);
 
-      n_grouped_buttons += g_list_length (app_task->windows) - 1;
+      n_grouped_buttons += g_list_length (class_group_task->windows) - 1;
 
       wnck_tasklist_layout (&fake_allocation,
 			    tasklist->priv->max_button_width,
@@ -898,7 +905,7 @@
 			    &n_cols, &n_rows);
       if (n_cols != last_n_cols &&
 	  (tasklist->priv->grouping == WNCK_TASKLIST_AUTO_GROUP ||
-	   ungrouped_apps == NULL))
+	   ungrouped_class_groups == NULL))
 	{
 	  val = n_cols * tasklist->priv->max_button_width;
 	  if (val >= lowest_range)
@@ -953,7 +960,7 @@
 {
   GtkAllocation child_allocation;
   WnckTasklist *tasklist;
-  WnckTask *app_task;
+  WnckTask *class_group_task;
   int n_windows;
   int n_startup_sequences;
   GList *l;
@@ -964,7 +971,7 @@
   int n_grouped_buttons;
   int i;
   gboolean score_set;
-  GList *ungrouped_apps;
+  GList *ungrouped_class_groups;
   WnckTask *win_task;
   GList *visible_tasks = NULL;
   int grouping_limit;
@@ -974,7 +981,7 @@
   n_windows = g_list_length (tasklist->priv->windows);
   n_startup_sequences = g_list_length (tasklist->priv->startup_sequences);
   n_grouped_buttons = 0;
-  ungrouped_apps = g_list_copy (tasklist->priv->applications);
+  ungrouped_class_groups = g_list_copy (tasklist->priv->class_groups);
   score_set = FALSE;
 
   grouping_limit = MIN (tasklist->priv->grouping_limit,
@@ -986,27 +993,27 @@
 				       tasklist->priv->max_button_height,
 				       n_startup_sequences + n_windows,
 				       &n_cols, &n_rows);
-  while (ungrouped_apps != NULL &&
+  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_apps);
+	  wnck_tasklist_score_groups (tasklist, ungrouped_class_groups);
 	  score_set = TRUE;
 	}
 
-      ungrouped_apps = wnck_task_get_highest_scored (ungrouped_apps, &app_task);
+      ungrouped_class_groups = wnck_task_get_highest_scored (ungrouped_class_groups, &class_group_task);
 
-      n_grouped_buttons += g_list_length (app_task->windows) - 1;
+      n_grouped_buttons += g_list_length (class_group_task->windows) - 1;
 
-      if (g_list_length (app_task->windows) > 1)
+      if (g_list_length (class_group_task->windows) > 1)
 	{
-	  visible_tasks = g_list_prepend (visible_tasks, app_task);
+	  visible_tasks = g_list_prepend (visible_tasks, class_group_task);
 	  
-	  /* Hide all this apps windows */
-	  l = app_task->windows;
+	  /* Hide all this group's windows */
+	  l = class_group_task->windows;
 	  while (l != NULL)
 	    {
 	      win_task = WNCK_TASK (l->data);
@@ -1017,8 +1024,8 @@
 	}
       else
 	{
-	  visible_tasks = g_list_prepend (visible_tasks, app_task->windows->data);
-	  gtk_widget_set_child_visible (GTK_WIDGET (app_task->button), FALSE);
+	  visible_tasks = g_list_prepend (visible_tasks, class_group_task->windows->data);
+	  gtk_widget_set_child_visible (GTK_WIDGET (class_group_task->button), FALSE);
 	}
       
       button_width = wnck_tasklist_layout (allocation,
@@ -1028,14 +1035,14 @@
 					   &n_cols, &n_rows);
     }
 
-  /* Add all ungrouped windows to visible_tasks, and hide their apps */
-  l = ungrouped_apps;
+  /* Add all ungrouped windows to visible_tasks, and hide their class groups */
+  l = ungrouped_class_groups;
   while (l != NULL)
     {
-      app_task = WNCK_TASK (l->data);
+      class_group_task = WNCK_TASK (l->data);
       
-      visible_tasks = g_list_concat (visible_tasks, g_list_copy (app_task->windows));
-      gtk_widget_set_child_visible (GTK_WIDGET (app_task->button), FALSE);
+      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);
       l = l->next;
     }
 
@@ -1132,7 +1139,7 @@
       (* callback) (task->button, callback_data);
     }
   
-  tmp = tasklist->priv->applications;
+  tmp = tasklist->priv->class_groups;
   while (tmp != NULL)
     {
       WnckTask *task = WNCK_TASK (tmp->data);
@@ -1182,8 +1189,8 @@
 	  break;
 	}
     }
-  
-  tmp = tasklist->priv->applications;
+
+  tmp = tasklist->priv->class_groups;
   while (tmp != NULL)
     {
       WnckTask *task = WNCK_TASK (tmp->data);
@@ -1191,10 +1198,10 @@
       
       if (task->button == widget)
 	{
-	  g_hash_table_remove (tasklist->priv->app_hash,
-			       task->application);
-	  tasklist->priv->applications =
-	    g_list_remove (tasklist->priv->applications,
+	  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);
@@ -1323,7 +1330,7 @@
   GList *l;
   
   tasklist->priv->active_task = NULL;
-  tasklist->priv->active_app = NULL;
+  tasklist->priv->active_class_group = NULL;
   
   if (tasklist->priv->windows)
     {
@@ -1341,9 +1348,9 @@
   g_assert (tasklist->priv->windows == NULL);
   g_assert (g_hash_table_size (tasklist->priv->win_hash) == 0);
 
-  if (tasklist->priv->applications)
+  if (tasklist->priv->class_groups)
     {
-      l = tasklist->priv->applications;
+      l = tasklist->priv->class_groups;
       while (l != NULL)
 	{
 	  WnckTask *task = WNCK_TASK (l->data);
@@ -1354,8 +1361,8 @@
 	  gtk_widget_destroy (task->button);
 	}
     }
-  g_assert (tasklist->priv->applications == NULL);
-  g_assert (g_hash_table_size (tasklist->priv->app_hash) == 0);
+  g_assert (tasklist->priv->class_groups == NULL);
+  g_assert (g_hash_table_size (tasklist->priv->class_group_hash) == 0);
 }
 
 static void
@@ -1363,11 +1370,11 @@
 {
   GList *windows;
   WnckWindow *win;
-  WnckApplication *app;
+  WnckClassGroup *class_group;
   GList *l;
   WnckWorkspace *active_workspace;
-  WnckTask *app_task;
   WnckTask *win_task;
+  WnckTask *class_group_task;
 
   wnck_tasklist_free_tasks (tasklist);
   
@@ -1392,39 +1399,41 @@
 	  
 	  gtk_widget_set_parent (win_task->button, GTK_WIDGET (tasklist));
 	  gtk_widget_show (win_task->button);
-	  
-	  app = wnck_window_get_application (win);
-	  app_task = g_hash_table_lookup (tasklist->priv->app_hash, app);
 
-	  if (app_task == NULL)
+	  /* Class group */
+
+	  class_group = wnck_window_get_class_group (win);
+	  class_group_task = g_hash_table_lookup (tasklist->priv->class_group_hash, class_group);
+
+	  if (class_group_task == NULL)
 	    {
-	      app_task = wnck_task_new_from_application (tasklist, app);
-	      gtk_widget_set_parent (app_task->button, GTK_WIDGET (tasklist));
-	      gtk_widget_show (app_task->button);
+	      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->applications = g_list_prepend (tasklist->priv->applications,
-							     app_task);
-	      g_hash_table_insert (tasklist->priv->app_hash, app, app_task);
+	      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);
 	    }
-	  
-	    app_task->windows = g_list_prepend (app_task->windows, win_task);
+
+	  class_group_task->windows = g_list_prepend (class_group_task->windows, win_task);
 	}
       
       l = l->next;
     }
 
-  /* Sort the application lists */
-  l = tasklist->priv->applications;
+  /* Sort the class group list */
+  l = tasklist->priv->class_groups;
   while (l)
     {
-      app_task = WNCK_TASK (l->data);
+      class_group_task = WNCK_TASK (l->data);
 
-      app_task->windows = g_list_sort (app_task->windows, wnck_task_compare);
+      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 (app_task);
+      wnck_task_update_visible_state (class_group_task);
       
       
       l = l->next;
@@ -1467,29 +1476,29 @@
 
   if (active_task)
     {
-      active_task = g_hash_table_lookup (tasklist->priv->app_hash,
-					 active_task->application);
+      active_task = g_hash_table_lookup (tasklist->priv->class_group_hash,
+					 active_task->class_group);
 
       if (active_task &&
-	  active_task == tasklist->priv->active_app)
+	  active_task == tasklist->priv->active_class_group)
 	return;
 
-      if (tasklist->priv->active_app)
+      if (tasklist->priv->active_class_group)
 	{
-	  tasklist->priv->active_app->really_toggling = TRUE;
-	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_app->button),
+	  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_app->really_toggling = FALSE;
+	  tasklist->priv->active_class_group->really_toggling = FALSE;
 	}
   
-      tasklist->priv->active_app = active_task;
+      tasklist->priv->active_class_group = active_task;
   
-      if (tasklist->priv->active_app)
+      if (tasklist->priv->active_class_group)
 	{
-	  tasklist->priv->active_app->really_toggling = TRUE;
-	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_app->button),
+	  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_app->really_toggling = FALSE;
+	  tasklist->priv->active_class_group->really_toggling = FALSE;
 	}
     }
 }
@@ -1731,7 +1740,7 @@
   GtkWidget *image;
   GList *l, *list;
   
-  if (task->application == NULL)
+  if (task->class_group == NULL)
     return;
   
   if (task->menu == NULL)
@@ -1799,8 +1808,6 @@
 wnck_task_button_toggled (GtkButton *button,
 			  WnckTask  *task)
 {
-  WnckWindowState state;
-
   /* Did we really want to change the state of the togglebutton? */
   if (task->really_toggling)
     return;
@@ -1813,7 +1820,7 @@
 
   switch (task->type)
     {
-    case WNCK_TASK_APPLICATION:
+    case WNCK_TASK_CLASS_GROUP:
       wnck_task_popup_menu (task, FALSE);
       break;
     case WNCK_TASK_WINDOW:
@@ -1821,9 +1828,6 @@
 	return;
       
       wnck_tasklist_activate_task_window (task);
-
-      /* FIXME why is this here? WTF? */
-      state = wnck_window_get_state (task->window);
       break;
     case WNCK_TASK_STARTUP_SEQUENCE:
       break;
@@ -1838,15 +1842,19 @@
   
   switch (task->type)
     {
-    case WNCK_TASK_APPLICATION:
-      return g_strdup_printf ("%s (%d)",
-                              wnck_application_get_icon_name (task->application),
-                              g_list_length (task->windows));
-      break;
+    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:
       state = wnck_window_get_state (task->window);
-      name = wnck_window_get_icon_name (task->window);
+      name = wnck_window_get_name (task->window);
       
       if (state & WNCK_WINDOW_STATE_MINIMIZED)
 	return g_strdup_printf ("[%s]", name);
@@ -1961,12 +1969,13 @@
 
   pixbuf = NULL;
   
-  switch (task->type)    
+  switch (task->type)
     {
-    case WNCK_TASK_APPLICATION:
-      pixbuf =  wnck_task_scale_icon (wnck_application_get_mini_icon (task->application),
-				      FALSE);
+    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);
 
@@ -2050,19 +2059,21 @@
   
   if (changed_mask & WNCK_WINDOW_STATE_MINIMIZED)
     {
-      WnckTask *win_task, *app_task;
+      WnckTask *win_task;
 
       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);
 	  
-	  app_task = g_hash_table_lookup (tasklist->priv->app_hash,
-					  win_task->application);
+	  class_group_task = g_hash_table_lookup (tasklist->priv->class_group_hash, win_task->class_group);
 
-	  if (app_task)
-	    wnck_task_update_visible_state (app_task);
+	  if (class_group_task)
+	    wnck_task_update_visible_state (class_group_task);
 	}
     }
     
@@ -2089,8 +2100,18 @@
 }
 
 static void
-wnck_task_app_name_changed (WnckApplication *app,
-                            gpointer         data)
+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);
 
@@ -2150,11 +2171,10 @@
 
   switch (task->type)
     {
-    case WNCK_TASK_APPLICATION:
+    case WNCK_TASK_CLASS_GROUP:
       wnck_task_popup_menu (task,
-                            event->button == 3);
+			    event->button == 3);
       return TRUE;
-      break;
 
     case WNCK_TASK_WINDOW:
       if (event->button == 1)
@@ -2288,11 +2308,13 @@
 
   switch (task->type)
     {
-    case WNCK_TASK_APPLICATION:
-      task->app_name_changed_tag = g_signal_connect (G_OBJECT (task->application), "name_changed",
-                                                     G_CALLBACK (wnck_task_app_name_changed), task);
+    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);
@@ -2304,6 +2326,9 @@
 
     case WNCK_TASK_STARTUP_SEQUENCE:
       break;
+
+    default:
+      g_assert_not_reached ();
     }
 }
 
@@ -2316,9 +2341,9 @@
 
 
 gboolean
-wnck_task_app_expose (GtkWidget        *widget,
-		      GdkEventExpose   *event,
-		      gpointer          data)
+wnck_task_class_group_expose (GtkWidget        *widget,
+			      GdkEventExpose   *event,
+			      gpointer          data)
 {
   GtkStyle *style;
   GdkGC *lgc, *dgc;
@@ -2342,6 +2367,17 @@
 }
 
 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)
 {
@@ -2354,9 +2390,12 @@
   
   switch (task1->type)
     {
-    case WNCK_TASK_APPLICATION:
-      xid1_1 = xid1_2 = wnck_application_get_xid (task1->application);
-      break;
+    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:
       xid1_1 = wnck_window_get_group_leader (task1->window);
       xid1_2 = wnck_window_get_xid (task1->window);
@@ -2368,9 +2407,12 @@
 
   switch (task2->type)
     {
-    case WNCK_TASK_APPLICATION:
-      xid2_1 = xid2_2 = wnck_application_get_xid (task2->application);
-      break;
+    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:
       xid2_1 = wnck_window_get_group_leader (task2->window);
       xid2_2 = wnck_window_get_xid (task2->window);
@@ -2431,7 +2473,7 @@
 
   task->type = WNCK_TASK_WINDOW;
   task->window = g_object_ref (window);
-  task->application = g_object_ref (wnck_window_get_application (window));
+  task->class_group = g_object_ref (wnck_window_get_class_group (window));
   task->tasklist = tasklist;
   
   wnck_task_create_widgets (task);
@@ -2442,25 +2484,25 @@
 }
 
 static WnckTask *
-wnck_task_new_from_application (WnckTasklist    *tasklist,
-				WnckApplication *application)
+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_APPLICATION;
-  task->application = g_object_ref (application);
+  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);
-  
+
   g_signal_connect_object (task->button, "expose_event",
-                           G_CALLBACK (wnck_task_app_expose),
+                           G_CALLBACK (wnck_task_class_group_expose),
                            G_OBJECT (task),
                            G_CONNECT_AFTER);
-  
+
   return task;
 }
 
@@ -2474,8 +2516,8 @@
   task = g_object_new (WNCK_TYPE_TASK, NULL);
 
   task->type = WNCK_TASK_STARTUP_SEQUENCE;
-  task->application = NULL;
   task->window = NULL;
+  task->class_group = NULL;
   task->startup_sequence = sequence;
   sn_startup_sequence_ref (task->startup_sequence);
   task->tasklist = tasklist;
Index: libwnck/test-tasklist.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/test-tasklist.c,v
retrieving revision 1.7
diff -u -r1.7 test-tasklist.c
--- libwnck/test-tasklist.c	14 May 2002 16:55:38 -0000	1.7
+++ libwnck/test-tasklist.c	3 Oct 2003 21:36:58 -0000
@@ -18,6 +18,7 @@
   wnck_screen_force_update (screen);
   
   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_default_size (GTK_WINDOW (win), 200, 100);
   gtk_window_stick (GTK_WINDOW (win));
   /*   wnck_gtk_window_set_dock_type (GTK_WINDOW (win)); */
 
@@ -32,7 +33,7 @@
 
   tasklist = wnck_tasklist_new (screen);
 
-  wnck_tasklist_set_grouping (WNCK_TASKLIST (tasklist), WNCK_TASKLIST_AUTO_GROUP);
+  wnck_tasklist_set_grouping (WNCK_TASKLIST (tasklist), WNCK_TASKLIST_ALWAYS_GROUP);
   frame = gtk_frame_new (NULL);
   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
   gtk_container_add (GTK_CONTAINER (win), frame);
Index: libwnck/window.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/window.c,v
retrieving revision 1.33.2.1
diff -u -r1.33.2.1 window.c
--- libwnck/window.c	23 Jan 2003 02:12:21 -0000	1.33.2.1
+++ libwnck/window.c	3 Oct 2003 21:36:58 -0000
@@ -21,6 +21,7 @@
 
 #include <string.h>
 #include "window.h"
+#include "class-group.h"
 #include "xutils.h"
 #include "private.h"
 #include "wnck-enum-types.h"
@@ -49,6 +50,7 @@
   Window xwindow;
   WnckScreen *screen;
   WnckApplication *app;
+  WnckClassGroup *class_group;
   Window group_leader;
   Window transient_for;
   char *name;
@@ -475,6 +477,14 @@
   return window->priv->xwindow;
 }
 
+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
@@ -1296,6 +1306,20 @@
   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
Index: libwnck/window.h
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/window.h,v
retrieving revision 1.19
diff -u -r1.19 window.h
--- libwnck/window.h	31 Oct 2002 05:43:44 -0000	1.19
+++ libwnck/window.h	3 Oct 2003 21:36:58 -0000
@@ -136,6 +136,8 @@
 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);
--- /dev/null	2003-01-30 04:24:37.000000000 -0600
+++ libwnck/class-group.h	2003-10-03 15:54:58.000000000 -0500
@@ -0,0 +1,76 @@
+/* class group object */
+
+/*
+ * Copyright (C) 2003 Ximian, Inc.
+ * Authors: Federico Mena-Quintero <federico ximian com>
+ *
+ * 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_CLASS_GROUP_H
+#define WNCK_CLASS_GROUP_H
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libwnck/screen.h>
+
+G_BEGIN_DECLS
+
+#define WNCK_TYPE_CLASS_GROUP              (wnck_class_group_get_type ())
+#define WNCK_CLASS_GROUP(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), WNCK_TYPE_CLASS_GROUP, WnckClassGroup))
+#define WNCK_CLASS_GROUP_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), WNCK_TYPE_CLASS_GROUP, WnckClassGroupClass))
+#define WNCK_IS_CLASS_GROUP(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), WNCK_TYPE_CLASS_GROUP))
+#define WNCK_IS_CLASS_GROUP_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), WNCK_TYPE_CLASS_GROUP))
+#define WNCK_CLASS_GROUP_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), WNCK_TYPE_CLASS_GROUP, WnckClassGroupClass))
+
+typedef struct _WnckClassGroupClass   WnckClassGroupClass;
+typedef struct _WnckClassGroupPrivate WnckClassGroupPrivate;
+
+struct _WnckClassGroup
+{
+  GObject parent_instance;
+
+  WnckClassGroupPrivate *priv;
+};
+
+struct _WnckClassGroupClass
+{
+  GObjectClass parent_class;
+
+  void (* name_changed) (WnckApplication *app);
+  void (* icon_changed) (WnckApplication *app);
+};
+
+GType wnck_class_group_get_type (void) G_GNUC_CONST;
+
+WnckClassGroup *wnck_class_group_get (const char *res_class);
+
+GList *wnck_class_group_get_windows (WnckClassGroup *class_group);
+const char * wnck_class_group_get_res_class (WnckClassGroup *class_group);
+
+const char * wnck_class_group_get_name (WnckClassGroup *class_group);
+
+GdkPixbuf *wnck_class_group_get_icon (WnckClassGroup *class_group);
+GdkPixbuf *wnck_class_group_get_mini_icon (WnckClassGroup *class_group);
+
+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);
+
+G_END_DECLS
+
+#endif
--- /dev/null	2003-01-30 04:24:37.000000000 -0600
+++ libwnck/class-group.c	2003-10-03 16:21:40.000000000 -0500
@@ -0,0 +1,611 @@
+/* class group object */
+
+/*
+ * Copyright (C) 2003 Ximian, Inc.
+ * Authors: Federico Mena-Quintero <federico ximian com>
+ *
+ * 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 "class-group.h"
+#include "window.h"
+#include "private.h"
+
+
+/* Private part of the WnckClassGroup structure */
+struct _WnckClassGroupPrivate {
+  char *res_class;
+  char *name;
+  GList *windows;
+
+  GdkPixbuf *icon;
+  GdkPixbuf *mini_icon;
+};
+
+#define ICON_SIZE 32
+#define MINI_ICON_SIZE 16
+
+/* Hash table that maps res_class strings -> WnckClassGroup instances */
+static GHashTable *class_group_hash = NULL;
+
+
+
+static void wnck_class_group_class_init  (WnckClassGroupClass *class);
+static void wnck_class_group_init        (WnckClassGroup      *class_group);
+static void wnck_class_group_finalize    (GObject             *object);
+
+enum {
+  NAME_CHANGED,
+  ICON_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static gpointer *parent_class;
+
+
+GType
+wnck_class_group_get_type (void)
+{
+  static GType object_type = 0;
+
+  g_type_init ();
+
+  if (!object_type)
+    {
+      static const GTypeInfo object_info =
+      {
+        sizeof (WnckClassGroupClass),
+        (GBaseInitFunc) NULL,
+        (GBaseFinalizeFunc) NULL,
+        (GClassInitFunc) wnck_class_group_class_init,
+        NULL,           /* class_finalize */
+        NULL,           /* class_data */
+        sizeof (WnckClassGroup),
+        0,              /* n_preallocs */
+        (GInstanceInitFunc) wnck_class_group_init,
+      };
+
+      object_type = g_type_register_static (G_TYPE_OBJECT,
+                                            "WnckClassGroup",
+                                            &object_info, 0);
+    }
+
+  return object_type;
+}
+
+static void
+wnck_class_group_class_init (WnckClassGroupClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  parent_class = g_type_class_peek_parent (class);
+
+  gobject_class->finalize = wnck_class_group_finalize;
+
+  signals[NAME_CHANGED] =
+    g_signal_new ("name_changed",
+                  G_OBJECT_CLASS_TYPE (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (WnckClassGroupClass, name_changed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+  signals[ICON_CHANGED] =
+    g_signal_new ("icon_changed",
+                  G_OBJECT_CLASS_TYPE (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (WnckClassGroupClass, icon_changed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+wnck_class_group_init (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  priv = g_new (WnckClassGroupPrivate, 1);
+  class_group->priv = priv;
+
+  priv->res_class = NULL;
+  priv->name = NULL;
+  priv->windows = NULL;
+
+  priv->icon = NULL;
+  priv->mini_icon = NULL;
+}
+
+static void
+wnck_class_group_finalize (GObject *object)
+{
+  WnckClassGroup *class_group;
+  WnckClassGroupPrivate *priv;
+
+  class_group = WNCK_CLASS_GROUP (object);
+  priv = class_group->priv;
+
+  if (priv->res_class)
+    g_free (priv->res_class);
+
+  if (priv->name)
+    g_free (priv->name);
+
+  g_list_free (priv->windows);
+
+  if (priv->icon)
+    g_object_unref (priv->icon);
+
+  if (priv->mini_icon)
+    g_object_unref (priv->mini_icon);
+
+  g_free (priv);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * wnck_class_group_get:
+ * @res_class: Name of the sought resource class.
+ *
+ * Gets an existing class group based on its resource class name.
+ *
+ * Return value: An existing #WnckClassGroup, or NULL if there is no groups with
+ * the specified @res_class.
+ **/
+WnckClassGroup *
+wnck_class_group_get (const char *res_class)
+{
+  if (!class_group_hash)
+    return NULL;
+  else
+    return g_hash_table_lookup (class_group_hash, res_class ? res_class : "");
+}
+
+/**
+ * _wnck_class_group_create:
+ * @res_class: Name of the resource class for the group.
+ *
+ * Creates a new WnckClassGroup with the specified resource class name.  If
+ * @res_class is #NULL, then windows without a resource class name will get
+ * grouped under this class group.
+ *
+ * Return value: A newly-created #WnckClassGroup, or an existing one that
+ * matches the @res_class.
+ **/
+WnckClassGroup *
+_wnck_class_group_create (const char *res_class)
+{
+  WnckClassGroup *class_group;
+  WnckClassGroupPrivate *priv;
+
+  if (class_group_hash == NULL)
+    class_group_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+  g_return_val_if_fail (g_hash_table_lookup (class_group_hash, res_class ? res_class : "") == NULL,
+			NULL);
+
+  class_group = g_object_new (WNCK_TYPE_CLASS_GROUP, NULL);
+  priv = class_group->priv;
+
+  priv->res_class = g_strdup (res_class ? res_class : "");
+
+  g_hash_table_insert (class_group_hash, priv->res_class, class_group);
+  /* Hash now owns one ref, caller gets none */
+
+  return class_group;
+}
+
+/**
+ * _wnck_class_group_destroy:
+ * @class_group: A window class group.
+ *
+ * Destroys the specified @class_group.
+ **/
+void
+_wnck_class_group_destroy (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_if_fail (WNCK_IS_CLASS_GROUP (class_group));
+
+  priv = class_group->priv;
+
+  g_hash_table_remove (class_group_hash, priv->res_class);
+
+  g_free (priv->res_class);
+  priv->res_class = NULL;
+
+  /* remove hash's ref on the class group */
+  g_object_unref (class_group);
+}
+
+static const char *
+get_name_from_applications (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+  const char *first_name;
+  GList *l;
+
+  priv = class_group->priv;
+
+  /* Try to get the name from the group leaders.  If all have the same name, we
+   * can use that.
+   */
+
+  first_name = NULL;
+
+  for (l = priv->windows; l; l = l->next)
+    {
+      WnckWindow *w;
+      WnckApplication *app;
+
+      w = WNCK_WINDOW (l->data);
+      app = wnck_window_get_application (w);
+
+      if (!first_name)
+	{
+	  if (app)
+	    first_name = wnck_application_get_name (app);
+	}
+      else
+	{
+	  if (!app || g_utf8_collate (first_name, wnck_application_get_name (app)) != 0)
+	    break;
+	}
+    }
+
+  if (!l)
+    {
+      /* All names are the same, so use one of them */
+      return first_name;
+    }
+  else
+    return NULL;
+}
+
+static const char *
+get_name_from_windows (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+  const char *first_name;
+  GList *l;
+
+  priv = class_group->priv;
+
+  /* Try to get the name from windows, following the same rationale as
+   * get_name_from_applications()
+   */
+
+  first_name = NULL;
+
+  for (l = priv->windows; l; l = l->next)
+    {
+      WnckWindow *window;
+
+      window = WNCK_WINDOW (l->data);
+
+      if (!first_name)
+	first_name = wnck_window_get_name (window);
+      else
+	if (g_utf8_collate (first_name, wnck_window_get_name (window)) != 0)
+	  break;
+    }
+
+  if (!l)
+    {
+      /* All names are the same, so use one of them */
+      return first_name;
+    }
+  else
+    return NULL;
+}
+
+
+/* Gets a sensible name for the class group from the application group leaders
+ * or from individual windows.
+ */
+static void
+set_name (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+  const char *new_name;
+
+  priv = class_group->priv;
+
+  if (priv->name)
+    {
+      g_free (priv->name);
+      priv->name = NULL;
+    }
+
+  new_name = get_name_from_applications (class_group);
+
+  if (!new_name)
+    {
+      new_name = get_name_from_windows (class_group);
+
+      if (!new_name)
+	new_name = priv->res_class;
+    }
+
+  g_assert (new_name != NULL);
+
+  if (!priv->name || g_utf8_collate (priv->name, new_name) != 0)
+    {
+      g_free (priv->name);
+      priv->name = g_strdup (new_name);
+
+      g_signal_emit (G_OBJECT (class_group), signals[NAME_CHANGED], 0);
+    }
+}
+
+/* Walks the list of applications, trying to get an icon from them */
+static void
+get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon)
+{
+  WnckClassGroupPrivate *priv;
+  GList *l;
+
+  priv = class_group->priv;
+
+  *icon = NULL;
+  *mini_icon = NULL;
+
+  for (l = priv->windows; l; l = l->next)
+    {
+      WnckWindow *window;
+      WnckApplication *app;
+
+      window = WNCK_WINDOW (l->data);
+      app = wnck_window_get_application (window);
+      if (app)
+	{
+	  *icon = wnck_application_get_icon (app);
+	  *mini_icon = wnck_application_get_mini_icon (app);
+
+	  if (*icon && *mini_icon)
+	    return;
+	  else
+	    {
+	      *icon = NULL;
+	      *mini_icon = NULL;
+	    }
+	}
+    }
+}
+
+/* Walks the list of windows, trying to get an icon from them */
+static void
+get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon)
+{
+  WnckClassGroupPrivate *priv;
+  GList *l;
+
+  priv = class_group->priv;
+
+  *icon = NULL;
+  *mini_icon = NULL;
+
+  for (l = priv->windows; l; l = l->next)
+    {
+      WnckWindow *window;
+
+      window = WNCK_WINDOW (l->data);
+
+      *icon = wnck_window_get_icon (window);
+      *mini_icon = wnck_window_get_mini_icon (window);
+
+      if (*icon && *mini_icon)
+	return;
+      else
+	{
+	  *icon = NULL;
+	  *mini_icon = NULL;
+	}
+    }
+}
+
+/* Gets a sensible icon and mini_icon for the class group from the application
+ * group leaders or from individual windows.
+ */
+static void
+set_icon (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+  GdkPixbuf *icon, *mini_icon;
+
+  priv = class_group->priv;
+
+  get_icons_from_applications (class_group, &icon, &mini_icon);
+
+  if (!icon || !mini_icon)
+    get_icons_from_windows (class_group, &icon, &mini_icon);
+
+  if (!icon || !mini_icon)
+      _wnck_get_fallback_icons (&icon, ICON_SIZE, ICON_SIZE,
+				&mini_icon, MINI_ICON_SIZE, MINI_ICON_SIZE);
+
+  g_assert (icon && mini_icon);
+
+  if (priv->icon)
+    g_object_unref (priv->icon);
+
+  if (priv->mini_icon)
+    g_object_unref (priv->mini_icon);
+
+  priv->icon = g_object_ref (icon);
+  priv->mini_icon = g_object_ref (mini_icon);
+
+  g_signal_emit (G_OBJECT (class_group), signals[ICON_CHANGED], 0);
+}
+
+/**
+ * _wnck_class_group_add_window:
+ * @class_group: A window class group.
+ * @window: A window.
+ *
+ * Adds a window to a class group.  You should only do this if the resource
+ * class of the window matches the @class_group<!-- -->'s.
+ **/
+void
+_wnck_class_group_add_window (WnckClassGroup *class_group,
+                              WnckWindow     *window)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_if_fail (WNCK_IS_CLASS_GROUP (class_group));
+  g_return_if_fail (WNCK_IS_WINDOW (window));
+  g_return_if_fail (wnck_window_get_class_group (window) == NULL);
+
+  priv = class_group->priv;
+
+  priv->windows = g_list_prepend (priv->windows, window);
+  _wnck_window_set_class_group (window, class_group);
+
+  set_name (class_group);
+  set_icon (class_group);
+
+  /* FIXME: should we monitor class group changes on the window?  The ICCCM says
+   * that clients should never change WM_CLASS unless the window is withdrawn.
+   */
+}
+
+/**
+ * _wnck_class_group_remove_window:
+ * @class_group: A window class group.
+ * @window: A window.
+ * 
+ * Removes a window from the list of windows that are grouped under the
+ * specified @class_group.
+ **/
+void
+_wnck_class_group_remove_window (WnckClassGroup *class_group,
+				 WnckWindow     *window)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_if_fail (WNCK_IS_CLASS_GROUP (class_group));
+  g_return_if_fail (WNCK_IS_WINDOW (window));
+  g_return_if_fail (wnck_window_get_class_group (window) == class_group);
+
+  priv = class_group->priv;
+
+  priv->windows = g_list_remove (priv->windows, window);
+  _wnck_window_set_class_group (window, NULL);
+}
+
+/**
+ * wnck_class_group_get_windows:
+ * @class_group: A window class group.
+ * 
+ * Gets the list of windows that are grouped in a @class_group.
+ * 
+ * Return value: A list of windows, or NULL if the group contains no windows.
+ * The list should not be freed, as it belongs to the @class_group.
+ **/
+GList *
+wnck_class_group_get_windows (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_val_if_fail (class_group != NULL, NULL);
+
+  priv = class_group->priv;
+  return priv->windows;
+}
+
+/**
+ * wnck_class_group_get_res_class:
+ * @class_group: A window class group.
+ * 
+ * Queries the resource class name for a class group.
+ * 
+ * Return value: The resource class name of the specified @class_group, or the
+ * empty string if the group has no name.  The string should not be freed.
+ **/
+const char *
+wnck_class_group_get_res_class (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_val_if_fail (class_group != NULL, NULL);
+
+  priv = class_group->priv;
+  return priv->res_class;
+}
+
+/**
+ * wnck_class_group_get_name:
+ * @class_group: A window class group.
+ * 
+ * Queries the human-readable name for a class group.
+ * 
+ * Return value: Name of the class group.
+ **/
+const char *
+wnck_class_group_get_name (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_val_if_fail (class_group != NULL, NULL);
+
+  priv = class_group->priv;
+
+  return priv->name;
+}
+
+/**
+ * wnck_class_group_get_icon:
+ * @class_group: A window class group.
+ * 
+ * Queries the icon to be used for a class group.
+ * 
+ * Return value: The icon to use.
+ **/
+GdkPixbuf *
+wnck_class_group_get_icon (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_val_if_fail (class_group != NULL, NULL);
+
+  priv = class_group->priv;
+
+  return priv->icon;
+}
+
+/**
+ * wnck_class_group_get_mini_icon:
+ * @class_group: A window class group.
+ * 
+ * Queries the mini-icon to be used for a class group.
+ * 
+ * Return value: The mini-icon to use.
+ **/
+GdkPixbuf *
+wnck_class_group_get_mini_icon (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_val_if_fail (class_group != NULL, NULL);
+
+  priv = class_group->priv;
+
+  return priv->mini_icon;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]