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	30 Sep 2003 04:14:30 -0000
@@ -1,3 +1,56 @@
+2003-09-29  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.
+
+	* 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].
+
 2003-07-29  Arvind Samptur <arvind.samptur@wipro.com>
 
 	* libwnck/xutils.c (_wnck_get_utf8_list): Number of
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	30 Sep 2003 04:14:30 -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	30 Sep 2003 04:14:31 -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	30 Sep 2003 04:14:33 -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,24 @@
                   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,
+                  G_STRUCT_OFFSET (WnckScreenClass, class_group_opened),
+                  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,
+                  G_STRUCT_OFFSET (WnckScreenClass, class_group_closed),
+                  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 +782,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 +825,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 +842,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 +861,21 @@
             }
           
           _wnck_application_add_window (app, window);
+
+	  /* Class group */
+
+	  xid = wnck_window_get_xid (window);
+
+	  res_class = _wnck_get_res_class_utf8 (xid);
+
+	  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 +899,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 +961,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 +970,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 +979,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 +1007,13 @@
         }
       
       emit_window_closed (screen, window);
-      
-      tmp = tmp->next;
     }
 
-  tmp = closed_apps;
-  while (tmp != NULL)
-    {
-      WnckApplication *app;
+  for (tmp = closed_apps; tmp; tmp = tmp->next)
+    emit_application_closed (screen, WNCK_APPLICATION (tmp->data));
 
-      app = 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 +1022,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 +1430,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	30 Sep 2003 04:14:33 -0000
@@ -28,6 +28,7 @@
 
 /* forward decls */
 typedef struct _WnckApplication WnckApplication;
+typedef struct _WnckClassGroup  WnckClassGroup;
 typedef struct _WnckWindow      WnckWindow;
 typedef struct _WnckWorkspace   WnckWorkspace;
 
@@ -79,6 +80,12 @@
   /* app gone */
   void (* application_closed)       (WnckScreen      *screen,
                                      WnckApplication *app);
+  /* new class group */
+  void (* class_group_opened)       (WnckScreen     *screen,
+                                     WnckClassGroup *class_group);
+  /* class group gone */
+  void (* class_group_closed)       (WnckScreen     *screen,
+                                     WnckClassGroup *class_group);
 
   /* New background */
   void (* background_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	30 Sep 2003 04:14:41 -0000
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include "tasklist.h"
 #include "window.h"
+#include "class-group.h"
 #include "window-action-menu.h"
 #include "workspace.h"
 #include "application.h"
@@ -66,10 +67,10 @@
 
 typedef enum
 {
+  WNCK_TASK_CLASS_GROUP,
   WNCK_TASK_APPLICATION,
   WNCK_TASK_WINDOW,
   WNCK_TASK_STARTUP_SEQUENCE
-
 } WnckTaskType;
 
 struct _WnckTask
@@ -83,7 +84,8 @@
   GtkWidget *label;
 
   WnckTaskType type;
-  
+
+  WnckClassGroup *class_group;
   WnckApplication *application;
   WnckWindow *window;
 #ifdef HAVE_STARTUP_NOTIFICATION
@@ -122,17 +124,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;
   
@@ -174,6 +178,8 @@
 						 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);
@@ -371,6 +377,12 @@
       task->application = NULL;
     }
 
+  if (task->class_group)
+    {
+      g_object_unref (task->class_group);
+      task->class_group = NULL;
+    }
+
 #ifdef HAVE_STARTUP_NOTIFICATION
   if (task->startup_sequence)
     {
@@ -429,6 +441,7 @@
   
   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,7 +486,7 @@
    * 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); */
   
@@ -482,6 +495,9 @@
   
   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 +682,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 +692,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 +725,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 +771,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 +779,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 +801,7 @@
       l = l->next;
     }
 
-  l = tasklist->priv->applications;
+  l = tasklist->priv->class_groups;
   while (l != NULL)
     {
       WnckTask *task = WNCK_TASK (l->data);
@@ -852,7 +868,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 +894,25 @@
       lowest_range = val;
     }
 
-  while (ungrouped_apps != NULL &&
+  while (ungrouped_class_groups != NULL &&
 	 tasklist->priv->grouping != WNCK_TASKLIST_NEVER_GROUP)
     {
+#if 0
+      volatile int i = 1;
+      g_print ("panel PID: %d\n", getpid ());
+      while (i)
+	;
+#endif
+
       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 +921,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 +976,7 @@
 {
   GtkAllocation child_allocation;
   WnckTasklist *tasklist;
-  WnckTask *app_task;
+  WnckTask *class_group_task;
   int n_windows;
   int n_startup_sequences;
   GList *l;
@@ -964,7 +987,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 +997,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 +1009,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 +1040,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 +1051,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 +1155,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 +1205,12 @@
 	  break;
 	}
     }
+
+  /* FIXME: (Federico) Find out which application corresponds to the deleted
+   * window and get rid of it.
+   */
   
-  tmp = tasklist->priv->applications;
+  tmp = tasklist->priv->class_groups;
   while (tmp != NULL)
     {
       WnckTask *task = WNCK_TASK (tmp->data);
@@ -1191,10 +1218,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 +1350,7 @@
   GList *l;
   
   tasklist->priv->active_task = NULL;
-  tasklist->priv->active_app = NULL;
+  tasklist->priv->active_class_group = NULL;
   
   if (tasklist->priv->windows)
     {
@@ -1341,6 +1368,8 @@
   g_assert (tasklist->priv->windows == NULL);
   g_assert (g_hash_table_size (tasklist->priv->win_hash) == 0);
 
+  /* FIXME: (Federico) What to free here? */
+#if 0
   if (tasklist->priv->applications)
     {
       l = tasklist->priv->applications;
@@ -1356,6 +1385,23 @@
     }
   g_assert (tasklist->priv->applications == NULL);
   g_assert (g_hash_table_size (tasklist->priv->app_hash) == 0);
+#endif
+
+  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);
 }
 
 static void
@@ -1364,10 +1410,12 @@
   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 +1440,57 @@
 	  
 	  gtk_widget_set_parent (win_task->button, GTK_WIDGET (tasklist));
 	  gtk_widget_show (win_task->button);
-	  
+
+	  /* Application */
+
 	  app = wnck_window_get_application (win);
 	  app_task = g_hash_table_lookup (tasklist->priv->app_hash, app);
 
 	  if (app_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);
-	      
+
 	      tasklist->priv->applications = g_list_prepend (tasklist->priv->applications,
 							     app_task);
 	      g_hash_table_insert (tasklist->priv->app_hash, app, app_task);
 	    }
 	  
-	    app_task->windows = g_list_prepend (app_task->windows, win_task);
+	  app_task->windows = g_list_prepend (app_task->windows, win_task);
+
+	  /* 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)
+	    {
+	      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);
 	}
       
       l = l->next;
     }
 
   /* Sort the application lists */
-  l = tasklist->priv->applications;
+  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 +1533,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 +1797,7 @@
   GtkWidget *image;
   GList *l, *list;
   
-  if (task->application == NULL)
+  if (task->application == NULL && task->class_group == NULL)
     return;
   
   if (task->menu == NULL)
@@ -1813,6 +1879,9 @@
 
   switch (task->type)
     {
+    case WNCK_TASK_CLASS_GROUP:
+      wnck_task_popup_menu (task, FALSE);
+      break;
     case WNCK_TASK_APPLICATION:
       wnck_task_popup_menu (task, FALSE);
       break;
@@ -1838,6 +1907,16 @@
   
   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_APPLICATION:
       return g_strdup_printf ("%s (%d)",
                               wnck_application_get_icon_name (task->application),
@@ -1963,6 +2042,12 @@
   
   switch (task->type)    
     {
+    case WNCK_TASK_CLASS_GROUP:
+      /* FIXME: (Federico) Where do we get the icon? */
+      _wnck_get_fallback_icons (NULL, 0, 0,
+				&pixbuf, MINI_ICON_SIZE, MINI_ICON_SIZE);
+      break;
+      
     case WNCK_TASK_APPLICATION:
       pixbuf =  wnck_task_scale_icon (wnck_application_get_mini_icon (task->application),
 				      FALSE);
@@ -2050,19 +2135,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);
 	}
     }
     
@@ -2150,10 +2237,18 @@
 
   switch (task->type)
     {
+    case WNCK_TASK_CLASS_GROUP:
+      wnck_task_popup_menu (task,
+			    event->button == 3);
+      return TRUE;
+
     case WNCK_TASK_APPLICATION:
+      g_assert_not_reached ();
+#if 0
       wnck_task_popup_menu (task,
                             event->button == 3);
       return TRUE;
+#endif
       break;
 
     case WNCK_TASK_WINDOW:
@@ -2288,6 +2383,10 @@
 
   switch (task->type)
     {
+    case WNCK_TASK_CLASS_GROUP:
+      /* FIXME: (Federico) handle class_group changes? */
+      break;
+
     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);
@@ -2304,6 +2403,9 @@
 
     case WNCK_TASK_STARTUP_SEQUENCE:
       break;
+
+    default:
+      g_assert_not_reached ();
     }
 }
 
@@ -2342,6 +2444,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,6 +2467,12 @@
   
   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_APPLICATION:
       xid1_1 = xid1_2 = wnck_application_get_xid (task1->application);
       break;
@@ -2368,6 +2487,12 @@
 
   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_APPLICATION:
       xid2_1 = xid2_2 = wnck_application_get_xid (task2->application);
       break;
@@ -2432,6 +2557,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);
@@ -2452,15 +2578,33 @@
   task->type = WNCK_TASK_APPLICATION;
   task->application = g_object_ref (application);
   task->window = NULL;
+  task->class_group = NULL;
   task->tasklist = tasklist;
-  
+
+  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->application = NULL;
+  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_OBJECT (task),
                            G_CONNECT_AFTER);
-  
+
   return task;
 }
 
@@ -2476,6 +2620,7 @@
   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	30 Sep 2003 04:14:41 -0000
@@ -32,7 +32,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	30 Sep 2003 04:14:42 -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	30 Sep 2003 04:14:42 -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-09-29 22:08:27.000000000 -0500
@@ -0,0 +1,67 @@
+/* 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 <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;
+};
+
+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_name (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-09-29 22:08:16.000000000 -0500
@@ -0,0 +1,282 @@
+/* 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;
+  GList *windows;
+};
+
+
+/* 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);
+
+
+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;
+}
+
+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->windows = 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);
+
+  /* FIXME: free the window list, or assume that it has already been freed? */
+
+  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);
+}
+
+/**
+ * _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);
+
+  /* FIXME: should we monitor class group changes on the window? */
+}
+
+/**
+ * _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_name:
+ * @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_name (WnckClassGroup *class_group)
+{
+  WnckClassGroupPrivate *priv;
+
+  g_return_val_if_fail (class_group != NULL, NULL);
+
+  priv = class_group->priv;
+  return priv->res_class;
+}
