Fixing window grouping



Hi,

So here is the first cut at a patch that fixes #76159.  I would
appreciate it very much if someone with good knowledge of libwnck
(hi, Havoc!) could take a look.

The patch has several problems:

- I am not freeing stuff from tasklist->priv->applications in
wnck_tasklist_remove() --- I guess the code needs to walk the
->applications list looking for the application object that contains the
window that got removed.

- The title of the group buttons comes from the WM_CLASS.res_class. 
This is an ugly string --- we could probably get something better from
the corresponding WnckApplication object for those windows that have it.

- Icons for groups don't work.  We can probably do the same as the
preceding item.

Other than that, it seems to group windows just fine and it is working
nicely on my machine.

Comments?  I'd like to get this fixed soon, so advice would be useful :)

Thanks,

  Federico
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;
+}


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