Re: Reminder: shared library applets deskguide and tasklist.



On 22 Jan 2001 19:09:48 -0500, jacob berkman wrote:
> On 11 Jan 2001 21:48:43 -0500, Miguel de Icaza wrote:
> > 
> > I have patches to make the tasklist and the deskguide shared
> > libraries.  They are a bit outdated now, but I think we should get
> > them into CVS.
> 
> 
> So here is my super hacked up version.  It is against HEAD gnome-core.


Crap.  Forgot to attach.  Not used to evo yet.

jacob
-- 
"The people who made the Macintosh produced a miracle, but that
 doesn't mean their code was wonderful." -- Bob Cringely
? tasklist.diff
? tasklist_properties.lo
? libtasklist_applet.la
? to-1.80.patch
? to-1.81.patch
? to-1.82.patch
? tasklist_config.lo
? gwmh.lo
? gstc.lo
? tasklist-shlib-grouping.patch
? tasklist_icon.lo
? tasklist_menu.lo
? tasklist_applet.lo
? to-1.79.patch
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/Makefile.am,v
retrieving revision 1.14
diff -u -r1.14 Makefile.am
--- Makefile.am	2000/04/15 17:11:38	1.14
+++ Makefile.am	2001/01/23 04:00:11
@@ -7,29 +7,41 @@
 	-I$(top_builddir)/panel \
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"	\
 	-I$(includedir) $(GNOME_INCLUDEDIR)		\
-	-DVERSION=\""$(VERSION)"\" \
 	@PIXBUF_CFLAGS@
 
-bin_PROGRAMS = tasklist_applet
+#bin_PROGRAMS = tasklist_applet
+lib_LTLIBRARIES = libtasklist_applet.la
 
-tasklist_applet_SOURCES = tasklist_applet.c \
-			  tasklist_config.c \
-			  tasklist_icon.c \
-			  tasklist_menu.c \
-			  tasklist_properties.c \
-			  gstc.c \
-			  gwmh.c
+#tasklist_applet_SOURCES = 	\
+#	 tasklist_applet.c 	\
+#	 tasklist_config.c 	\
+#	 tasklist_icon.c 	\
+#	 tasklist_menu.c 	\
+#	 tasklist_properties.c 	\
+#	 gstc.c 			\
+#	 gwmh.c
+#
 
-tasklist_applet_LDADD = ../../panel/libpanel_applet.la \
-		     $(GNOME_LIBDIR) $(ORB_LIBS)         \
-		     $(GNOMEUI_LIBS) $(GNORBA_LIBS)      \
-		     $(INTLLIBS) @PIXBUF_LIBS@
+libtasklist_applet_la_SOURCES =\
+	pixmaps.h 		\
+	tasklist_applet.h 	\
+	tasklist_applet.c 	\
+	tasklist_config.c 	\
+	tasklist_icon.c 	\
+	tasklist_menu.c 	\
+	tasklist_properties.c 	\
+	gstc.c 			\
+	gwmh.c
 
-EXTRA_DIST = \
-	pixmaps.h \
-	tasklist_applet.h \
-	tasklist_applet.gnorba \
-	tasklist_applet.desktop \
+#libtasklist_applet_la_LIBADD =				\
+#	../../panel/libpanel_applet.la 		\
+#	$(GNOME_LIBDIR) $(ORB_LIBS)		\
+#	$(GNOMEUI_LIBS) $(GNORBA_LIBS)		\
+#	$(INTLLIBS) @PIXBUF_LIBS@
+
+EXTRA_DIST = 			\
+	tasklist_applet.gnorba 	\
+	tasklist_applet.desktop	\
 	unknown.xpm		\
 	gnome-tasklist.png
 
Index: tasklist_applet.c
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/tasklist_applet.c,v
retrieving revision 1.82
diff -u -r1.82 tasklist_applet.c
--- tasklist_applet.c	2000/12/21 07:11:35	1.82
+++ tasklist_applet.c	2001/01/23 04:00:11
@@ -5,122 +5,93 @@
 #include "gstc.h"
 #include "gwmh.h"
 
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
 #include "tasklist_applet.h"
 #include "unknown.xpm"
-
-/* Prototypes */
-static void cb_properties (void);
-static void cb_about (void);
-gchar *fixup_task_label (TasklistTask *task);
-gboolean is_task_visible (TasklistTask *task);
-void draw_task (TasklistTask *task, GdkRectangle *rect);
-TasklistTask *find_gwmh_task (GwmhTask *gwmh_task);
-gboolean desk_notifier (gpointer func_data, GwmhDesk *desk, GwmhDeskInfoMask change_mask);
-gboolean task_notifier (gpointer func_data, GwmhTask *gwmh_task, GwmhTaskNotifyType ntype, GwmhTaskInfoMask imask);
-gboolean cb_button_press_event (GtkWidget *widget, GdkEventButton *event);
-gboolean cb_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time);
-void cb_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time);
-gboolean cb_motion_timeout (gpointer data);
-gboolean cb_expose_event (GtkWidget *widget, GdkEventExpose *event);
-void create_applet (void);
-TasklistTask *task_get_xy (gint x, gint y);
-GList *get_visible_tasks (void);
-gint get_horz_rows(void);
-gboolean show_task (TasklistTask *task);
-
-GNOME_Panel_OrientType tasklist_orient; /* Tasklist orient */
-GtkWidget *handle; /* The handle box */
-GtkWidget *applet; /* The applet */
-GtkWidget *area; /* The drawing area used to display tasks */
-GList *tasks = NULL; /* The list of tasks used */
-
-#define MOTION_TIMEOUT 500 /* Show task motion_task if cursor over task for ... msec */
-int motion_timeout = 0; /* Show task motion_task after MOTION_TIMEOUT in drag_motion */
-TasklistTask *motion_task = NULL; /* Task to show after motion_timeout */
-
-TasklistIcon *unknown_icon = NULL; /* The unknown icon */
 
-gint vert_height=0; /* Vertical height, used for resizing */
-gint horz_width=0;  /* Horizontal width, used for resizing */
-
-gint panel_size = 48;
+/* from gtkhandlebox.c */
+#define DRAG_HANDLE_SIZE 10
 
-extern TasklistConfig Config;
+/* define to x for debugging output */
+#define d(x)
 
 /* sorting the list... */
 static gint
-lexographic_compare_func (gconstpointer a, gconstpointer b)
+tlsort (TasklistTask *la, TasklistTask *lb)
 {
-       const TasklistTask *la = (const TasklistTask*)a;
-       const TasklistTask *lb = (const TasklistTask*)b;
+       char *sa, *sb;
 
-       if (la->gwmh_task->name == NULL && lb->gwmh_task->name == NULL)
-	       return 0;
-       else if (la->gwmh_task->name == NULL)
+       d(g_print ("la: %p\tlb: %p\n", la, lb));
+
+       sa = la->task_group ? la->group_name : (la->group ? la->group->group_name : la->gwmh_task->name);
+       sb = lb->task_group ? lb->group_name : (lb->group ? lb->group->group_name : lb->gwmh_task->name);
+
+       if (sa && sb)
+	       return strcasecmp (sa, sb);
+       else if (sa)
 	       return 1;
-       else if (lb->gwmh_task->name == NULL)
+       else if (sb)
 	       return -1;
-
-       return strcasecmp (la->gwmh_task->name, lb->gwmh_task->name);
+       else
+	       return 0;
 }
 
-static gint
-creation_compare_func (gconstpointer a, gconstpointer b)
+static void
+clamp_size (Tasklist *tasklist, int *size)
 {
-       const TasklistTask *la = (const TasklistTask*)a;
-       const TasklistTask *lb = (const TasklistTask*)b;
+	int free_space;
 
-       return lb->serial_number - la->serial_number;
-}
+	free_space = applet_widget_get_free_space (APPLET_WIDGET (tasklist->applet));
 
-static gint
-tasklist_sorting_compare_func (gconstpointer a, gconstpointer b)
-{
-       /*
-        * This is dumb, but here's why:
-        * the intent is that there be lots of compare functions
-        * (maybe putting the icons at the top, or by desktop,
-        * or whatever.  Right now there are just two: the old
-        * one and the one I like.  BUT, I don't know enough about
-        * how to make gnome_config_xxx map a list of things into
-        * functions, so for now I just have a boolean that controls
-        * which.  Hopefully the next person who adds sorting functions
-        * will know more about gnome than I do and will fix this hack.
-        * - johnh isi edu, 24-Nov-00
-        */
-       return Config.sort_tasklist ?
-	       lexographic_compare_func (a, b) :
-	       creation_compare_func (a, b);
+	if (free_space > 0 && free_space < *size)
+		*size = free_space;
 }
 
-static void
-clamp_size_if_never_push (int *size)
+void
+tasklist_clean_menu (TasklistTask *task)
 {
-	if (Config.vert_never_push) {
-		int free_space = applet_widget_get_free_space
-			(APPLET_WIDGET (applet));
-		if (free_space > 0 &&
-		    free_space < *size)
-			*size = free_space;
+	if(task->menu) {
+		d(g_print ("group had a menu: %p\t", task->menu));
+		gtk_widget_unref (task->menu);
+		d(g_print ("%p\n", task->menu));
 	}
 }
 
+static char *
+get_task_class (GwmhTask *task)
+{
+	XClassHint hint;
+	char *retval;
 
-/* from gtkhandlebox.c */
-#define DRAG_HANDLE_SIZE 10
+	if (!XGetClassHint (GDK_DISPLAY (), task->xwin, &hint))
+		return NULL;
+
+	d(g_print ("name: %s\tclass: %s\n", hint.res_name, hint.res_class));
+	retval = g_strdup (hint.res_class);
+
+	XFree (hint.res_name);
+	XFree (hint.res_class);
+
+	return retval;
+}
 
 /* get the horz_rows depending on the configuration settings */
-gint
-get_horz_rows(void)
+static gint
+get_horz_rows(Tasklist *tasklist)
 {
 	int result;
 
-	if (Config.follow_panel_size)
-		result = panel_size/ROW_HEIGHT;
+	g_return_val_if_fail (tasklist != NULL, 1);
+			      
+	if (tasklist->config.follow_panel_size)
+		result = tasklist->panel_size/ROW_HEIGHT;
 	else
-		result = Config.horz_rows;
+		result = tasklist->config.horz_rows;
 
 	if (result < 1)
 		result = 1;
@@ -130,141 +101,122 @@
 
 /* Shorten a label that is too long */
 gchar *
-fixup_task_label (TasklistTask *task)
-{
-	gchar *str, *tempstr;
-	gint len, label_len;
-
-	label_len = gdk_string_width (area->style->font,
-				      task->gwmh_task->name);
+tasklist_task_get_label (TasklistTask *task, int width, gboolean add_groupcount)
+{	
+	Tasklist *tasklist = task->tasklist;
+	gchar *das_string;
+	gchar *str, *tempstr, *groupcount = NULL;
+	gint len, label_len, overhead, allowed_width;
+
+	das_string = task->gwmh_task->name;
+
+	label_len = gdk_string_width (tasklist->area->style->font, das_string);
+
+	overhead = tasklist->config.show_mini_icons ? 30 : 6;
+
+	if (add_groupcount) {
+		groupcount = g_strdup_printf ("(%d) ", g_slist_length (task->vtasks 
+								       ? task->vtasks 
+								       : task->group->vtasks));
+		
+		overhead += 10 + gdk_string_width (tasklist->area->style->font, 
+						   groupcount);
+	}
 	
 	if (GWMH_TASK_ICONIFIED (task->gwmh_task))
-		label_len += gdk_string_width (area->style->font,
-					       "[]");
+		overhead += gdk_string_width (tasklist->area->style->font, "[]");	
 
-	if (label_len > task->width - ROW_HEIGHT) {
+	allowed_width = width - overhead;
+	
+	if ( (width > 0) && (label_len > allowed_width) ) {
 		GdkWChar *wstr;
+
+		g_assert (width > 0);
 
-		len = strlen (task->gwmh_task->name);
+		len = strlen (das_string);
 		wstr = g_new (GdkWChar, len + 3);
-		len = gdk_mbstowcs (wstr, task->gwmh_task->name, len);
+		len = gdk_mbstowcs (wstr, das_string, len);
+		/* ok, the below thing is broken */
 		if ( len < 0 ) { /* if the conversion is failed */
 			wstr[0] = wstr[1] = wstr[2] = '?'; 
 			wstr[3] = '\0'; /* wcscpy(wstr,"???");*/
 			len = 3;
-			label_len = gdk_text_width_wc(area->style->font,
+			label_len = gdk_text_width_wc(tasklist->area->style->font,
                                                       wstr, len);
-			if (label_len <= task->width 
-			    - (Config.show_mini_icons ? 24:6)) {
+			if (label_len <= allowed_width) {
 				str = gdk_wcstombs(wstr);
 				g_free(wstr);
 				return str;
-			}
+				}
 		}
 		wstr[len] = wstr[len+1] = '.';
 		wstr[len+2] = '\0'; /*wcscat(wstr,"..");*/
 		len--;
-
+		
 		for (; len > 0; len--) {
 			wstr[len] = '.';
 			wstr[len + 3] = '\0';
 			
-			label_len = gdk_text_width_wc (area->style->font,
+			label_len = gdk_text_width_wc (tasklist->area->style->font,
 						       wstr, len + 3);
 			
-			if (GWMH_TASK_ICONIFIED (task->gwmh_task))
-				label_len += gdk_string_width (area->style->font,
-							       "[]");
-			if (label_len <= task->width - (Config.show_mini_icons ? 24:6))
+			if (label_len <= allowed_width)
 				break;
 		}
 		str = gdk_wcstombs (wstr);
 		g_free (wstr);
+	} else {
+		str = g_strdup (das_string);
 	}
-	else
-		str = g_strdup (task->gwmh_task->name);
 
-	if (GWMH_TASK_ICONIFIED (task->gwmh_task)) {
-		tempstr = g_strdup_printf ("[%s]", str);
+	if (task->gwmh_task && GWMH_TASK_ICONIFIED (task->gwmh_task)) {
+		tempstr = g_strdup_printf ("[%s]", str);	
 		g_free(str);
 		str = tempstr;
 	}
-
-	return str;
-}
-
-/* Check what task (if any) is at position x,y on the tasklist */
-TasklistTask *
-task_get_xy (gint x, gint y)
-{
-	GList *temp_tasks, *temp;
-	TasklistTask *task;
-
-	temp_tasks = get_visible_tasks ();
-
-	for (temp = temp_tasks; temp != NULL; temp = temp->next) {
-		task = (TasklistTask *)temp->data;
-		if (x > task->x &&
-		    x < task->x + task->width &&
-		    y > task->y &&
-		    y < task->y + task->height) {
-			g_list_free (temp_tasks);
-			return task;
-		}
+	
+	if (groupcount) {
+		tempstr = g_strconcat (groupcount, str, NULL);
+		g_free (str);
+		str = tempstr;
 	}
-
-	if (temp_tasks != NULL)
-		g_list_free (temp_tasks);
-
-	return NULL;
-}
-
-
-/* Check which tasks are "visible",
-   if they should be drawn onto the tasklist */
-GList *
-get_visible_tasks (void)
-{
-	GList *temp_tasks;
-	GList *visible_tasks = NULL;
 
-	temp_tasks = tasks;
-	while (temp_tasks) {
-		if (is_task_visible ((TasklistTask *) temp_tasks->data))
-                        visible_tasks = g_list_insert_sorted (visible_tasks, temp_tasks->data, tasklist_sorting_compare_func);
-		temp_tasks = temp_tasks->next;
-	}
-	return visible_tasks;
+	return str;
 }
 
 /* Check if a task is "visible", 
    if it should be drawn onto the tasklist */
-gboolean
+static gboolean
 is_task_visible (TasklistTask *task)
 {
+	Tasklist *tasklist;
 	GwmhDesk *desk_info;
 
+	if (!task || task->destroyed || task->task_group)
+		return FALSE;
+
+	tasklist = task->tasklist;
+
 	desk_info = gwmh_desk_get_config ();
 
 	if (GWMH_TASK_SKIP_TASKBAR (task->gwmh_task))
 		return FALSE;
 	
-
 	if (task->gwmh_task->desktop != desk_info->current_desktop ||
 	    task->gwmh_task->harea != desk_info->current_harea ||
 	    task->gwmh_task->varea != desk_info->current_varea) {
 		if (!GWMH_TASK_STICKY (task->gwmh_task)) {
-			if (!Config.all_desks_minimized && 
-			    !Config.all_desks_normal)
+			if (!tasklist->config.all_desks_minimized && 
+			    !tasklist->config.all_desks_normal)
 				return FALSE;
 				
-			else if (Config.all_desks_minimized && 
-				 !Config.all_desks_normal) {
+			else if (tasklist->config.all_desks_minimized && 
+				 !tasklist->config.all_desks_normal) {
 				if (!GWMH_TASK_ICONIFIED (task->gwmh_task))
 					return FALSE;
 			}
-			else if (Config.all_desks_normal && 
-				 !Config.all_desks_minimized) {
+			else if (tasklist->config.all_desks_normal && 
+				 !tasklist->config.all_desks_minimized) {
 				if (GWMH_TASK_ICONIFIED (task->gwmh_task))
 					return FALSE;
 			}
@@ -272,148 +224,381 @@
 	}			
 
 	if (GWMH_TASK_ICONIFIED (task->gwmh_task)) {
-		if (!Config.show_minimized)
+		if (!tasklist->config.show_minimized)
 			return FALSE;
 	} else {
-		if (!Config.show_normal)
+		if (!tasklist->config.show_normal)
 			return FALSE;
 	}
 		
 	return TRUE;
 }
 
+static gboolean
+is_task_really_visible (TasklistTask *task)
+{
+	g_return_val_if_fail (task != NULL, FALSE);
+
+	if (!task->tasklist->config.enable_grouping)
+		return is_task_visible (task);
+
+	/* we can probably unroll the length test */
+	if (task->group && g_slist_length (task->group->vtasks) > task->tasklist->config.grouping_min)
+		return FALSE;
+	else if (task->task_group)
+		return g_slist_length (task->vtasks) > task->tasklist->config.grouping_min;;
+	return is_task_visible (task);
+}
+
+static void
+print_task (TasklistTask *task, gpointer null)
+{
+	return;
+
+	if (!task)
+		g_print (" * * NULL TASK * *\n");
+	else if (task->group)
+		g_print ("task: %p (%p) [%d, %d]: %s\n", 
+			 task, task->gwmh_task,
+			 is_task_visible (task),
+			 is_task_really_visible (task),
+			 task->gwmh_task->name);
+	else if (task->task_group) {
+		g_print ("group: %p [%d]: %s\n", task, is_task_really_visible (task), task->group_name);
+		g_slist_foreach (task->tasks, (GFunc)print_task, NULL);
+		g_print ("/\n");
+	} else {
+		g_print ("Unknown task: %p\n", task);
+		g_assert_not_reached ();
+	}
+}
+
+static void
+fixup_group (TasklistTask *group)
+{
+	TasklistTask *task;
+	GSList *item;
+	
+	g_return_if_fail (group != NULL);
+	
+	group->focused_task = NULL;
+	group->gwmh_task->iconified = TRUE;
+	
+	g_slist_free (group->vtasks);
+	group->vtasks = NULL;
+	
+	for (item = group->tasks; item; item = item->next) {
+		task = (TasklistTask *)item->data;
+		if (is_task_visible (task)) {
+			group->vtasks = g_slist_prepend (group->vtasks, task);
+			if (!task->gwmh_task->iconified)
+				group->gwmh_task->iconified = FALSE;
+			if (task->gwmh_task->focused)
+				group->focused_task = task;
+		}
+	}
+}
+
+static gboolean
+fixup_vtask (TasklistTask *task, gpointer forcep)
+{
+	gint force = GPOINTER_TO_INT (forcep);
+	gboolean visible;
+
+	/* why not layout if we are confused */
+	g_return_val_if_fail (task, TRUE);
+
+	if (task->task_group)
+		fixup_group (task);
+
+	visible = is_task_really_visible (task);
+
+	if (visible == task->visible)
+		return FALSE;
+
+	task->visible = visible;
+	task->tasklist->vtasks = visible 
+		? g_slist_insert_sorted (task->tasklist->vtasks, task, tlsort)
+		: g_slist_remove (task->tasklist->vtasks, task);
+
+	if (task->tasklist->config.enable_grouping) {
+		if (task->task_group) {
+			g_slist_foreach (task->tasks, (GFunc)fixup_vtask, forcep);
+		} else if (task->group) {
+			fixup_vtask (task->group, GINT_TO_POINTER (FALSE));
+		}
+	}
+
+	d(g_print (">>>>>\tfixup_vtask "));
+	d(print_task (task, NULL));
+	d(g_slist_foreach (task->tasklist->vtasks, (GFunc)print_task, NULL));
+	d(g_print ("<<<<<\n"));
+
+	return TRUE;
+}
+
+static void
+redo_groups (gchar *groupname, TasklistTask *task, Tasklist *tasklist)
+{
+	print_task (task, NULL);
+
+	fixup_group (task);
+	task->visible = is_task_really_visible (task);
+	if (task->visible)
+		tasklist->vtasks = g_slist_insert_sorted (tasklist->vtasks, task, tlsort);
+}
+
+void
+tasklist_redo_vtasks (Tasklist *tasklist)
+{
+	TasklistTask *task;
+	GList *item;
+	
+	if (tasklist->vtasks) {
+		g_slist_free (tasklist->vtasks);
+		tasklist->vtasks = NULL;
+	}
+
+	d(g_print ("\n\n\n\n\n\n\n\n\n\nredo_vtasks\n\n\n\n\n\n\n\n\n\n"));
+
+	if (tasklist->config.enable_grouping)
+		g_hash_table_foreach (tasklist->groups, (GHFunc)redo_groups, tasklist);
+
+	for (item = gwmh_task_list_get (); item; item = item->next) {
+		task = g_hash_table_lookup (tasklist->tasks, item->data);
+
+		/* this should never actually happen */
+		if (!task) continue;
+
+		task->visible = is_task_really_visible (task);
+		
+		if (task->visible)
+			tasklist->vtasks = g_slist_insert_sorted (tasklist->vtasks, task, tlsort);
+	}
+
+#if 0
+	if (!tasklist->config.sort_tasklist)
+		tasklist->vtasks = g_slist_reverse (tasklist->vtasks);
+#endif
+	d(g_print ("\n\n\n\n\n\n\n\n\n\nvtasks: %d\n", g_slist_length (tasklist->vtasks)));
+
+	d(g_print (">>>>>\tredo_vtasks:\n"));
+	d(g_slist_foreach (tasklist->vtasks, (GFunc)print_task, NULL));
+	d(g_print ("<<<<<\n"));
+}
+
+/* Check what task (if any) is at position x,y on the tasklist */
+static TasklistTask *
+task_get_xy (Tasklist *tasklist, gint x, gint y)
+{
+	GSList *temp_tasks, *temp;
+	TasklistTask *task;
+
+	temp_tasks = tasklist->vtasks;
+
+	for (temp = temp_tasks; temp != NULL; temp = temp->next) {
+		task = (TasklistTask *)temp->data;
+		if (x > task->x &&
+		    x < task->x + task->width &&
+		    y > task->y &&
+		    y < task->y + task->height)
+			return task;
+	}
+
+	return NULL;
+}
+
+static void
+draw_dot (GdkWindow *window, GdkGC *lgc, GdkGC *dgc, int x, int y)
+{
+	gdk_draw_point (window, dgc, x,   y);
+	gdk_draw_point (window, lgc, x+1, y+1);
+}
+
 /* Draw a single task */
 void
-draw_task (TasklistTask *task, GdkRectangle *rect)
+tasklist_draw_task (TasklistTask *task, GdkRectangle *rect)
 {
+	TasklistTask *real_task;
 	gchar *tempstr;
 	gint text_height, text_width;
+	gboolean focused;
 
 	/* For mini icons */
 	TasklistIcon *icon;
 	GdkPixbuf *pixbuf;
+	
+	Tasklist *tasklist;
 
-	if (!is_task_visible (task))
+	if (!is_task_really_visible (task))
 		return;
 
-	gtk_paint_box (area->style, area->window,
-		       GWMH_TASK_FOCUSED (task->gwmh_task) ?
-		       GTK_STATE_ACTIVE : GTK_STATE_NORMAL,
-		       GWMH_TASK_FOCUSED (task->gwmh_task) ?
-		       GTK_SHADOW_IN : GTK_SHADOW_OUT,
-		       rect, area, "button",
-		       task->x, task->y,
-		       task->width, task->height);
-
-	if (task->gwmh_task->name) {
-		tempstr = fixup_task_label (task);
-		text_height = gdk_string_height (area->style->font, "1");
-		text_width = gdk_string_width (area->style->font, tempstr);
-		gdk_draw_string (area->window,
-				 area->style->font,
-				 GWMH_TASK_FOCUSED (task->gwmh_task) ?
-				 area->style->fg_gc[GTK_STATE_ACTIVE] :
-				 area->style->fg_gc[GTK_STATE_NORMAL],
-				 task->x +
-				 (Config.show_mini_icons ? 10 : 0) +
-				 ((task->width - text_width) / 2),
-				 task->y + ((task->height - text_height) / 2) + text_height,
+	/* is_task_visible should return FALSE for task == NULL */
+	g_assert (task != NULL);
+
+	tasklist = task->tasklist;
+	
+	real_task = task;
+	if (is_task_visible (task->focused_task) && GWMH_TASK_FOCUSED (task->focused_task->gwmh_task))
+		task = task->focused_task;
+
+	focused = GWMH_TASK_FOCUSED (task->gwmh_task) || real_task->menu != NULL;
+
+	gtk_paint_box (tasklist->area->style, tasklist->area->window,
+		       focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL,		       
+		       focused ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
+		       rect, tasklist->area, "button",
+		       real_task->x, real_task->y,
+		       real_task->width, real_task->height);
+
+	tempstr = tasklist_task_get_label (task, real_task->width, real_task->task_group);
+	if (tempstr) {
+		text_height = gdk_string_height (tasklist->area->style->font, "1");
+		text_width = gdk_string_width (tasklist->area->style->font, tempstr);
+		gdk_draw_string (tasklist->area->window,
+				 tasklist->area->style->font,
+				 focused ?
+				 tasklist->area->style->fg_gc[GTK_STATE_ACTIVE] :
+				 tasklist->area->style->fg_gc[GTK_STATE_NORMAL],
+				 real_task->x +
+				 (tasklist->config.show_mini_icons ? 10 : 0) +
+				 ((real_task->width - text_width) / 2),
+				 real_task->y + ((real_task->height - text_height) / 2) + text_height,
 				 tempstr);
 
 		g_free (tempstr);
 	}
 
-	if (Config.show_mini_icons) {
+	if (tasklist->config.show_mini_icons) {
 		icon = task->icon;
 		
-		if (GWMH_TASK_ICONIFIED (task->gwmh_task))
+		if ( GWMH_TASK_ICONIFIED (task->gwmh_task))
 			pixbuf = icon->minimized;
 		else
 			pixbuf = icon->normal;
+
+		gdk_pixbuf_render_to_drawable_alpha (
+			pixbuf,
+			tasklist->area->window,
+			0, 0,
+			real_task->x + 3 + (16 - gdk_pixbuf_get_width (pixbuf)) / 2,
+			real_task->y + (real_task->height - gdk_pixbuf_get_height (pixbuf)) / 2,
+			gdk_pixbuf_get_width (pixbuf),
+			gdk_pixbuf_get_height (pixbuf),
+			GDK_PIXBUF_ALPHA_BILEVEL,
+			127,
+			GDK_RGB_DITHER_NORMAL,
+			gdk_pixbuf_get_width (pixbuf),
+			gdk_pixbuf_get_height (pixbuf));
+
+	}
+
+	if (real_task->task_group) {
+		GtkStyle *style;
+		GdkWindow *window;
+		GdkGC *lgc, *dgc;
+		int x, y, i, j;
+
+		style = tasklist->area->style;
+
+		lgc = style->light_gc[focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL];
+		dgc = style->dark_gc[focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL];
+
+		window = tasklist->area->window;
+
+		x = real_task->x + real_task->width - style->klass->ythickness - 10;
+		y = real_task->y + style->klass->xthickness + 2;
+
+		for (i = 0; i < 3; i++) {
+			for (j = i; j < 3; j++) {
+				draw_dot (window, lgc, dgc, x + j*3, y + i*3);
+			}
+		}
 
-		gdk_pixbuf_render_to_drawable_alpha (pixbuf,
-						     area->window,
-						     0, 0,
-						     task->x + 3 + (16 - gdk_pixbuf_get_width (pixbuf)) / 2,
-						     task->y + (task->height - gdk_pixbuf_get_height (pixbuf)) / 2,
-						     gdk_pixbuf_get_width (pixbuf),
-						     gdk_pixbuf_get_height (pixbuf),
-						     GDK_PIXBUF_ALPHA_BILEVEL,
-						     127,
-						     GDK_RGB_DITHER_NORMAL,
-						     gdk_pixbuf_get_width (pixbuf),
-						     gdk_pixbuf_get_height (pixbuf));
+		
 
+#if 0
+		gtk_draw_arrow (tasklist->area->style, tasklist->area->window,
+				focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL,
+				GTK_SHADOW_ETCHED_IN,
+				GTK_ARROW_DOWN, TRUE,
+				real_task->x + real_task->width - 12,
+				real_task->y + (real_task->height - 8) / 2,
+				9, 8);
+#endif
 	}
+
 }
 
+
 static int
-max_width (GList *temp_tasks)
+max_width (GSList *tasks)
 {
-	GList *li;
+	TasklistTask *task;
+	GSList *li;
 	int maxwidth = 0;
+	char *s;
 
-	for (li = temp_tasks; li != NULL; li = li->next) {
-		int width;
-		TasklistTask *task = li->data;
-		const char *name = task->gwmh_task->name != NULL ? 
-			task->gwmh_task->name : "";
+	for (li = tasks; li; li = li->next) {
+		task = li->data;
 
 		if (task->fullwidth < 0) {
-			width = gdk_string_width (area->style->font, name);
-
-			if (GWMH_TASK_ICONIFIED (task->gwmh_task))
-				width += gdk_string_width (area->style->font,
-							   "[]");
-
-			task->fullwidth = width;
-		} else {
-			width = task->fullwidth;
+			s = tasklist_task_get_label (task, -1, task->task_group);
+			task->fullwidth = gdk_string_width (
+				task->tasklist->area->style->font, s);
+			g_free (s);
 		}
 
-		if (Config.show_mini_icons)
-			width += 10;
-
-		width += ROW_HEIGHT;
-
-		if (width > maxwidth)
-			maxwidth = width;
+		maxwidth = MAX (maxwidth,
+				task->fullwidth + 
+				(task->tasklist->config.show_mini_icons 
+				 ? ROW_HEIGHT + 10 : 0));
 	}
-
-	return maxwidth;
+	return MIN (maxwidth, 512);
 }
 
 /* Layout the tasklist */
-void
-layout_tasklist (gboolean call_change_size)
+static int
+real_layout_tasklist (Tasklist *tasklist, gboolean call_change_size)
 {
 	gint j = 0, k = 0, num = 0, p = 0;
-	GList *temp_tasks, *temp;
 	TasklistTask *task;
+	GSList *temp = NULL;
 	/* gint extra_space; */
 	gint num_rows = 0, num_cols = 0;
 	gint curx = 0, cury = 0, curwidth = 0, curheight = 0;
+
+	tasklist->layout_timeout = 0;
+	d(g_message ("Layout!"));
+
+	/*gdk_beep ();*/
 	
-	temp_tasks = get_visible_tasks ();
-	num = g_list_length (temp_tasks);
+	if (!tasklist->vtasks) {
+		d(g_message ("no tasks :(\n"));
+		gtk_widget_draw (tasklist->area, NULL);
+		return FALSE;
+	}
+
+	num = g_slist_length (tasklist->vtasks);
 	
-	switch (applet_widget_get_panel_orient (APPLET_WIDGET (applet))) {
+	switch (applet_widget_get_panel_orient (APPLET_WIDGET (tasklist->applet))) {
 	case ORIENT_UP:
 	case ORIENT_DOWN:
 		if (num == 0) {
-			if (Config.horz_fixed)
-				horz_width = Config.horz_width;
+			if (tasklist->config.horz_fixed)
+				tasklist->horz_width = tasklist->config.horz_width;
 			else
-				horz_width = 4;
+				tasklist->horz_width = 4;
 			
-			change_size (FALSE, -1);
+			if (call_change_size)
+				tasklist_change_size (tasklist, FALSE, -1);
 			
-			gtk_widget_draw (area, NULL);
-			return;
+			gtk_widget_draw (tasklist->area, NULL);
+			return FALSE;
 		}
 
 		while (p < num) {
-			if (num < get_horz_rows())
+			if (num < get_horz_rows(tasklist))
 				num_rows = num;
 			
 			j++;
@@ -422,33 +607,34 @@
 			if (num_rows < k + 1)
 				num_rows = k + 1;
 			
-			if (get_horz_rows () == 0 || j >= ((num + get_horz_rows() - 1) / get_horz_rows())) {
+			if (get_horz_rows (tasklist) == 0 || j >= ((num + get_horz_rows(tasklist) - 1) / get_horz_rows(tasklist))) {
 				j = 0;
 				k++;
 			}
 			p++;
 		}
 		
-		if (Config.horz_fixed) {
-			curheight = (ROW_HEIGHT * get_horz_rows() - 0) / num_rows;
-			curwidth = (Config.horz_width - 0) / num_cols;
+		if (tasklist->config.horz_fixed) {
+			curheight = (ROW_HEIGHT * get_horz_rows(tasklist)) / num_rows;
+			curwidth = (tasklist->config.horz_width) / num_cols;
 
 		} else {
 			int width;
 
-			width = Config.horz_taskwidth * num_cols + DRAG_HANDLE_SIZE;
+			width = tasklist->config.horz_taskwidth * num_cols + DRAG_HANDLE_SIZE;
+			if (tasklist->config.horz_never_push)
+				clamp_size (tasklist, &width);
 
-			clamp_size_if_never_push (&width);
-
 			width -= DRAG_HANDLE_SIZE;
-
-			curheight = (ROW_HEIGHT * get_horz_rows() - 0) / num_rows;
-			curwidth = width / num_cols;
 
+			curheight = (ROW_HEIGHT * get_horz_rows(tasklist)) / num_rows;
+#if 0
 			/* If the total width is higher than allowed, 
 			   we use the "fixed" way instead */
-			if ((curwidth * num_cols) > Config.horz_width)
-				curwidth = (Config.horz_width - 0) / num_cols;
+			if ((curwidth * num_cols) > tasklist->config.horz_width)
+				curwidth = (tasklist->config.horz_width - 0) / num_cols;
+#endif
+			curwidth = width / num_cols;
 		}
 
 
@@ -456,7 +642,7 @@
 		cury = 0;
 
 
-		for (temp = temp_tasks; temp != NULL; temp = temp->next) {
+		for (temp = tasklist->vtasks; temp != NULL; temp = temp->next) {
 			task = (TasklistTask *) temp->data;
 			
 			task->x = curx;
@@ -464,11 +650,11 @@
 			task->width = curwidth;
 			task->height = curheight;
 			
-			if (Config.horz_fixed) {
+			if (tasklist->config.horz_fixed) {
 				curx += curwidth;
 			
-				if (curx >= Config.horz_width ||
-				    curx + curwidth > Config.horz_width) {
+				if (curx >= tasklist->config.horz_width ||
+				    curx + curwidth > tasklist->config.horz_width) {
 					cury += curheight;
 					curx = 0;
 				}
@@ -483,13 +669,13 @@
 			}
 		}
 
-		if (Config.horz_fixed)
-			horz_width = Config.horz_width;
+		if (tasklist->config.horz_fixed)
+			tasklist->horz_width = tasklist->config.horz_width;
 		else
-			horz_width = num_cols * curwidth + 4;
+			tasklist->horz_width = num_cols * curwidth + 4;
 
 		if (call_change_size)
-			change_size (FALSE, -1);
+			tasklist_change_size (tasklist, FALSE, -1);
 
 		break;
 
@@ -497,49 +683,46 @@
 	case ORIENT_RIGHT:
 
 		if (num == 0) {
-			if (Config.vert_fixed)
-				vert_height = Config.vert_height;
+			if (tasklist->config.vert_fixed)
+				tasklist->vert_height = tasklist->config.vert_height;
 			else
-				vert_height = 4;
+				tasklist->vert_height = 4;
 			
 			if (call_change_size)
-				change_size (FALSE, -1);
+				tasklist_change_size (tasklist, FALSE, -1);
 			
-			gtk_widget_draw (area, NULL);
-			return;
+			gtk_widget_draw (tasklist->area, NULL);
+			return FALSE;
 		}
 
 		curheight = ROW_HEIGHT;
-		if (Config.follow_panel_size)
-			curwidth = panel_size - 0;
+		if (tasklist->config.follow_panel_size)
+			curwidth = tasklist->panel_size;
 		else
-			curwidth = Config.vert_width - 0;
-
-		if (Config.vert_width_full) {
-			int fullwidth = max_width (temp_tasks);
-
-			if (fullwidth > curwidth)
-				curwidth = fullwidth;
-		}
+			curwidth = tasklist->config.vert_width;
 		
+		if (tasklist->config.vert_width_full)
+			curwidth = MAX (curwidth, max_width (tasklist->vtasks));
+
 		num_cols = 1;
 		num_rows = num;
 		
 		curx = 0;
 		cury = 0;
 
-		if (Config.vert_fixed) {
-			vert_height = Config.vert_height;
+		if (tasklist->config.vert_fixed) {
+			tasklist->vert_height = tasklist->config.vert_height;
 		} else {
-			vert_height = curheight * num_rows + 4 + DRAG_HANDLE_SIZE;
-			clamp_size_if_never_push (&vert_height);
-			vert_height -= DRAG_HANDLE_SIZE;
+			tasklist->vert_height = curheight * num_rows + 4 + DRAG_HANDLE_SIZE;
+			if (tasklist->config.vert_never_push)
+				clamp_size (tasklist, &tasklist->vert_height);
+			tasklist->vert_height -= DRAG_HANDLE_SIZE;
 		}
 		
 		if (call_change_size)
-			change_size (FALSE, curwidth);
+			tasklist_change_size (tasklist, FALSE, curwidth);
 
-		for (temp = temp_tasks; temp != NULL; temp = temp->next) {
+		for (temp = tasklist->vtasks; temp != NULL; temp = temp->next) {
 			task = (TasklistTask *) temp->data;
 			
 			task->x = curx;
@@ -549,9 +732,9 @@
 			
 			curx += curwidth;
 
-			if (curx >= (Config.follow_panel_size?
-				     panel_size:
-				     Config.vert_width) - 0) {
+			if (curx >= (tasklist->config.follow_panel_size?
+				     tasklist->panel_size:
+				     tasklist->config.vert_width) - 0) {
 				cury += curheight;
 				curx = 0;
 			}
@@ -559,22 +742,37 @@
 
 		break;
 	}
+
+	gtk_widget_draw (tasklist->area, NULL);
+
+	return FALSE;
+}
 
-	if (temp_tasks != NULL)
-		g_list_free (temp_tasks);
+/* this now actually just queues a relayout */
+void
+tasklist_layout_tasklist (Tasklist *tasklist)
+{
+	g_return_if_fail (tasklist);
 
+	/* don't queue another timeout */
+	if (tasklist->layout_timeout) {
+		d(g_message ("Skipped layout!"));
+		return;
+	}
 	
-	gtk_widget_draw (area, NULL);
+	d(g_message ("Adding layout callback..."));
+	tasklist->layout_timeout = g_idle_add ((GSourceFunc)real_layout_tasklist, tasklist);
 }
 
+#if 0
 /* Get a task from the list that has got the given gwmh_task */
-TasklistTask *
-find_gwmh_task (GwmhTask *gwmh_task)
+static TasklistTask *
+find_gwmh_task (Tasklist *tasklist, GwmhTask *gwmh_task)
 {
 	GList *temp_tasks;
 	TasklistTask *task;
 
-	temp_tasks = tasks;
+	temp_tasks = tasklist->tasks;
 
 	while (temp_tasks) {
 		task = (TasklistTask *)temp_tasks->data;
@@ -585,234 +783,389 @@
 	
 	return NULL;
 }
+#endif
 
 /* This routine gets called when desktops are switched etc */
-gboolean
+static gboolean
 desk_notifier (gpointer func_data, GwmhDesk *desk,
 	       GwmhDeskInfoMask change_mask)
 {
-	if (Config.all_desks_minimized && 
-	    Config.all_desks_normal)
+	Tasklist *tasklist = (Tasklist *) func_data;
+	
+	if (tasklist->config.all_desks_minimized && 
+	    tasklist->config.all_desks_normal)
 		return TRUE;
 
-	layout_tasklist (TRUE);
+	tasklist_redo_vtasks (tasklist);
+	tasklist_layout_tasklist (tasklist);
 
 	return TRUE;
 }
 
+static void
+tasklist_group_destroy (TasklistTask *group)
+{
+	d(g_print (" *** destroying group: %s\n", group->group_name));
+
+	tasklist_icon_destroy (group);
+
+	g_hash_table_remove (group->tasklist->groups, group->group_name);
+
+	g_free (group->gwmh_task);
+	g_free (group->group_name);
+
+	tasklist_clean_menu (group);
+
+	g_free (group);
+}
+
+static void
+tasklist_task_destroy (GwmhTask *gtask, Tasklist *tasklist)
+{
+	TasklistTask *ttask;
+	
+	g_return_if_fail (gtask != NULL);
+	g_return_if_fail (tasklist != NULL);	
+	
+	ttask = g_hash_table_lookup (tasklist->tasks, gtask);
+	
+	if (!ttask) {
+		g_warning ("Task not found in tasklist: %p; not destroying", gtask);
+		return;
+	}
+
+	d(g_print (" *** removing: %p (%s)\n", ttask, gtask->name));
+	
+	ttask->destroyed = TRUE;
+
+	g_hash_table_remove (tasklist->tasks, gtask);
+	
+	if (ttask == tasklist->motion_task)
+		tasklist->motion_task = NULL;
+
+        /* this is broken */
+#if 0
+	if (ttask->menuitem)
+		gtk_widget_destroy (ttask->menuitem);
+#endif
+	tasklist_icon_destroy (ttask);
+	tasklist_clean_menu (ttask);
+
+	if (ttask->group) {
+		if (ttask->group->focused_task == ttask)
+			ttask->group->focused_task = NULL;
+
+		ttask->group->tasks = g_slist_remove (ttask->group->tasks, ttask);
+
+		if (!ttask->group->tasks)
+			tasklist_group_destroy (ttask->group);
+		else
+			fixup_vtask (ttask->group, GINT_TO_POINTER (FALSE));
+	}
+
+	tasklist->vtasks = g_slist_remove (tasklist->vtasks, ttask);
+
+	g_free (ttask);
+
+	tasklist_layout_tasklist (tasklist);
+}
+
+static TasklistTask *
+tasklist_group_new (TasklistTask *first_task, char *group_name)
+{
+	TasklistTask *group;
+
+	g_return_val_if_fail (first_task != NULL, NULL);
+	g_return_val_if_fail (group_name != NULL, NULL);
+
+	group = g_new0 (TasklistTask, 1);
+	group->tasklist = first_task->tasklist;
+	group->task_group = TRUE;
+	group->group_name = group_name;
+	group->fullwidth = -1;
+	g_hash_table_insert (group->tasklist->groups, group_name, group);
+
+	gdk_pixbuf_ref (first_task->icon->normal);
+	gdk_pixbuf_ref (first_task->icon->minimized);
+
+	group->icon = g_new (TasklistIcon, 1);
+	group->icon->normal    = first_task->icon->normal;
+	group->icon->minimized = first_task->icon->minimized;
+
+	group->tasks = g_slist_prepend (group->tasks, first_task);
+
+	group->gwmh_task = g_new0 (GwmhTask, 1);
+	group->gwmh_task->name = group_name;
+
+	return group;
+}
+
+/* 
+ * this is void since we don't need to get the return value when a
+ * task is created and we can just create a lot of tasks from the gwmh
+ * task glist
+ */
+
+static void
+tasklist_task_new (GwmhTask *gtask, Tasklist *tasklist)
+{
+	TasklistTask *ttask;
+	char *class;
+
+	g_return_if_fail (gtask != NULL);
+	g_return_if_fail (tasklist != NULL);
+	g_return_if_fail (tasklist->tasks != NULL);
+
+	d(g_print ("Adding task: %s\n", gtask->name));
+
+	ttask = g_new0 (TasklistTask, 1);
+
+	ttask->tasklist = tasklist;
+	ttask->gwmh_task = gtask;
+
+	g_hash_table_insert (tasklist->tasks, gtask, ttask);
+
+	ttask->wmhints_icon = tasklist_icon_get_pixmap (ttask);
+
+	tasklist_icon_set (ttask);
+	ttask->fullwidth = -1;
+
+	class = get_task_class (gtask);
+	if (!class)
+		return;
+
+	ttask->group = g_hash_table_lookup (tasklist->groups, class);
+
+	if (!ttask->group)
+		ttask->group = tasklist_group_new (ttask, class);
+	else {
+		ttask->group->tasks = g_slist_prepend (ttask->group->tasks, ttask);
+		g_free (class);
+		fixup_vtask (ttask->group, GINT_TO_POINTER (TRUE));
+	}
+}
+
+
 /* This routine gets called when tasks are created/destroyed etc */
-gboolean
+static gboolean
 task_notifier (gpointer func_data, GwmhTask *gwmh_task,
 	       GwmhTaskNotifyType ntype,
 	       GwmhTaskInfoMask imask)
 {
-	gboolean que_draw, que_layout;
-        static gint master_serial_number = 0;
+	Tasklist *tasklist = (Tasklist *) func_data;
 	TasklistTask *task;
-	
+	gboolean resize = FALSE;
+
 	switch (ntype)
 	{
 	case GWMH_NOTIFY_INFO_CHANGED:
-		que_draw = FALSE;
-		que_layout = FALSE;
-		task = find_gwmh_task (gwmh_task);
+		task = g_hash_table_lookup (tasklist->tasks, gwmh_task);
+		if (!task) {
+			g_warning ("Getting info about task we don't know about: %p", gwmh_task);
+			break;
+		}
+
+		if (imask & GWMH_TASK_INFO_FOCUSED && task->gwmh_task->focused && task->group)
+			task->group->focused_task = task;
+
+		/* we only need to re-layout if the task has changed
+		 * visibility status.  If it has, its group will also get fixed up
+		 */
+
+		/* this probably should be optimized and only done when the title changes */
+		resize = (tasklist->config.vert_width_full && 
+			  (tasklist->orient == ORIENT_LEFT || tasklist->orient == ORIENT_RIGHT));
+		task->fullwidth = -1;
+
+		if (fixup_vtask (task, GINT_TO_POINTER (FALSE))) {
+			if (resize)
+				tasklist_change_size (tasklist, TRUE, -1);
+			else
+				tasklist_layout_tasklist (tasklist);
+		} else {
+			if (resize)
+				tasklist_change_size (tasklist, TRUE, -1);
+			else
+				tasklist_draw_task (task->group && is_task_really_visible (task->group)
+						    ? task->group : task, NULL);
+		}
 
+#if 0
 		if (imask & GWMH_TASK_INFO_WM_HINTS) {
-			task->fullwidth = -1;
-			if (tasklist_icon_get_pixmap (task) !=
-			    task->wmhints_icon) {
-				tasklist_icon_destroy (task);
-				tasklist_icon_set (task);
-				que_draw = TRUE;
+			if (tasklist_icon_get_pixmap (task) != task->wmhints_icon) {
+				tasklist_icon_destroy (tasklist, task);
+				tasklist_icon_set (tasklist, task);
+				tasklist_draw_task (tasklist, task, NULL);
 			}
 		}
 		if (imask & GWMH_TASK_INFO_GSTATE)
-			que_layout = TRUE;
-		if (imask & GWMH_TASK_INFO_ICONIFIED) {
-			task->fullwidth = -1;
-			que_layout = TRUE;
-		}
+			tasklist_layout_tasklist (tasklist);
+		if (imask & GWMH_TASK_INFO_ICONIFIED)
+			tasklist_layout_tasklist (tasklist);
+
 		if (imask & GWMH_TASK_INFO_FOCUSED)
-			que_draw = TRUE;
-		if (imask & GWMH_TASK_INFO_MISC) {
-			task->fullwidth = -1;
-			que_draw = TRUE;
-
-			/* this might change the size,
-			 * if width full is on. */
-			if (Config.vert_width_full &&
-			    (tasklist_orient == ORIENT_LEFT ||
-			     tasklist_orient == ORIENT_RIGHT))
-				que_layout = TRUE;
-		}
+			tasklist_draw_task (tasklist, task, NULL);
+		if (imask & GWMH_TASK_INFO_MISC)
+			tasklist_draw_task (tasklist, task, NULL);
 		if (imask & GWMH_TASK_INFO_DESKTOP) {
-			if ( ! Config.all_desks_minimized ||
-			     ! Config.all_desks_normal)
-				que_layout = TRUE;
-		}
+			if (tasklist->config.all_desks_minimized && 
+			    tasklist->config.all_desks_normal)
+				break;
 
 		if (que_layout)
 			/* Redraw entire tasklist */
-			layout_tasklist (TRUE);
-		else if (que_draw)
-			/* We can get away with redrawing the task only */
-			draw_task (task, NULL);
+			tasklist_layout_tasklist (tasklist);
+		}
+#endif
 		break;
 	case GWMH_NOTIFY_NEW:
-		task = g_malloc0 (sizeof (TasklistTask));
-		task->gwmh_task = gwmh_task;
-		task->wmhints_icon = tasklist_icon_get_pixmap (task);
-		tasklist_icon_set (task);
-                task->serial_number = master_serial_number++; /* wrapping seems unlikely :-) */
-                tasks = g_list_insert_sorted (tasks, task, tasklist_sorting_compare_func);
-	        layout_tasklist (TRUE);
+		tasklist_task_new (gwmh_task, tasklist);
+		tasklist_layout_tasklist (tasklist);
 		break;
 	case GWMH_NOTIFY_DESTROY:
-		task = find_gwmh_task (gwmh_task);
-		if(task) {
-			tasks = g_list_remove (tasks, task);
-			if (task == motion_task) {
-				motion_task = NULL;
-			}
-			tasklist_icon_destroy (task);
-			if(task->menu)
-				gtk_widget_destroy(task->menu);
-			g_free (task);
-			layout_tasklist (TRUE);
-		}
+		tasklist_task_destroy (gwmh_task, tasklist);
 		break;
 	default:
-		g_print ("Unknown ntype: %d\n", ntype);
+		d(g_print ("Unknown ntype: %d\n", ntype));
 	}
 
 	return TRUE;
 }
 
 /* Show the task if need. Return TRUE if so */
-gboolean
-show_task (TasklistTask *task)
+static gboolean
+show_task (Tasklist *tasklist, TasklistTask *task)
 {
-	if (GWMH_TASK_ICONIFIED (task->gwmh_task) || !GWMH_TASK_FOCUSED (task->gwmh_task)) {
-	
-		if (!(Config.move_to_current && GWMH_TASK_ICONIFIED (task->gwmh_task))) {
-			GwmhDesk *desk_info;
-			desk_info = gwmh_desk_get_config ();
-					
-			if (task->gwmh_task->desktop != desk_info->current_desktop ||
-			    task->gwmh_task->harea != desk_info->current_harea ||
-			    task->gwmh_task->varea != desk_info->current_varea) {
-				gwmh_desk_set_current_area (task->gwmh_task->desktop,
-							    task->gwmh_task->harea,
-							    task->gwmh_task->varea);
-			}
-		}
+	if (!GWMH_TASK_ICONIFIED (task->gwmh_task) && GWMH_TASK_FOCUSED (task->gwmh_task))
+		return FALSE;
+
 	
-		gwmh_task_show (task->gwmh_task);
-		/* Why is a focus needed here?
-		   gwmh_task_show is supposed to give focus */
-		gwmh_task_focus (task->gwmh_task);
-		gwmh_task_focus (task->gwmh_task);
-		
+	if (!(tasklist->config.move_to_current && GWMH_TASK_ICONIFIED (task->gwmh_task))) {
+		GwmhDesk *desk_info;
+		desk_info = gwmh_desk_get_config ();
 		
-		return TRUE;
+		if (task->gwmh_task->desktop != desk_info->current_desktop ||
+		    task->gwmh_task->harea != desk_info->current_harea ||
+		    task->gwmh_task->varea != desk_info->current_varea) {
+			gwmh_desk_set_current_area (task->gwmh_task->desktop,
+						    task->gwmh_task->harea,
+						    task->gwmh_task->varea);
+		}
 	}
 	
-	return FALSE;
+	gwmh_task_show (task->gwmh_task);
+#if 0
+	/* Why is a focus needed here?
+	   gwmh_task_show is supposed to give focus */
+	
+	/*
+	 * i think this is a sawfish bug: when "give
+	 * uniconised windows focused" is unchecked it
+	 * probably doesn't reassign focus. -- jacob
+	 */
+	
+	gwmh_task_focus (task->gwmh_task);
+	gwmh_task_focus (task->gwmh_task);
+	
+#endif 
+	return TRUE; 
 }
 
 /* This routine gets called when the mouse is pressed */
-gboolean
-cb_button_press_event (GtkWidget *widget, GdkEventButton *event)
+static gboolean
+cb_button_press_event (GtkWidget *widget, GdkEventButton *event, Tasklist *tasklist)
 {
 	TasklistTask *task;
 	
-	task = task_get_xy ((gint)event->x, (gint)event->y);
+	task = task_get_xy (tasklist, (gint)event->x, (gint)event->y);
 
 	if (!task)
 		return FALSE;
 
 	if (event->button == 1) {
-
-		if (! show_task (task)) {
+		if (task->task_group)
+			tasklist_group_popup (task, event->button, event->time);
+		else if (! show_task (tasklist, task))
 			gwmh_task_iconify (task->gwmh_task);
-		}
-
+		
 		return TRUE;
 	}
-
+	
 	if (event->button == 3) {
 		gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
 					      "button_press_event");
-		menu_popup (task, event->button, event->time);
+		tasklist_menu_popup (task, event->button, event->time);
 		return TRUE;
 	}
 
 	return FALSE;
 }
 
-void
-cb_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
+static void
+cb_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, Tasklist *tasklist)
+{
+	if (tasklist->motion_timeout) {
+		gtk_timeout_remove (tasklist->motion_timeout);
+		tasklist->motion_timeout = 0;
+		tasklist->motion_task = NULL;
+	}
+}
+
+static gboolean 
+cb_motion_timeout (gpointer user_data)
 {
-	if (motion_timeout) {
-		gtk_timeout_remove (motion_timeout);
-		motion_timeout = 0;
-		motion_task = NULL;
+	Tasklist *tasklist = user_data;
+	
+	if (tasklist->motion_task) {
+		show_task (tasklist, tasklist->motion_task);
 	}
+
+	tasklist->motion_timeout = 0;
+	tasklist->motion_task = NULL;
+	
+	return FALSE;	
 }
 
 /* This routine gets called when user drag something over the tasklist */
-gboolean 
-cb_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time)
+static gboolean 
+cb_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, Tasklist *tasklist)
 {
 	TasklistTask *task;
 	
 	gdk_drag_status (context, 0, time);
 	
-	task = task_get_xy (x, y);
+	task = task_get_xy (tasklist, x, y);
 	
-	if (task != motion_task) {
+	if (task != tasklist->motion_task) {
 	
-		if (motion_timeout) {
-			gtk_timeout_remove (motion_timeout);
+		if (tasklist->motion_timeout) {
+			gtk_timeout_remove (tasklist->motion_timeout);
 		}
 	
-		motion_task = task;
+		tasklist->motion_task = task;
 
 		if (task) {	
-			motion_timeout = gtk_timeout_add (MOTION_TIMEOUT, cb_motion_timeout, widget);		
+			tasklist->motion_timeout = gtk_timeout_add (MOTION_TIMEOUT, cb_motion_timeout, tasklist);		
 		} else {
-			motion_timeout = 0;
+			tasklist->motion_timeout = 0;
 		}
 	}
 	
 	return TRUE;
 }
 
-gboolean 
-cb_motion_timeout (gpointer data)
-{
-	if (motion_task) {
-		show_task (motion_task);
-	}
-
-	motion_timeout = 0;
-	motion_task = NULL;
-	
-	return FALSE;	
-}
-
 /* This routine gets called when the tasklist is exposed */
-gboolean
-cb_expose_event (GtkWidget *widget, GdkEventExpose *event)
+static gboolean
+cb_expose_event (GtkWidget *widget, GdkEventExpose *event, Tasklist *tasklist)
 {
-	GList *temp_tasks, *temp;
+	GSList *temp_tasks, *temp;
 	TasklistTask *task;
 
-	temp_tasks = get_visible_tasks ();
+	temp_tasks = tasklist->vtasks;
 
-	gtk_paint_flat_box (area->style, area->window,
-			    area->state, GTK_SHADOW_NONE,
-			    &event->area, area, "button",
+	gtk_paint_flat_box (tasklist->area->style, tasklist->area->window,
+			    tasklist->area->state, GTK_SHADOW_NONE,
+			    &event->area, tasklist->area, "button",
 			    0, 0, -1, -1);
 	
 	for (temp = temp_tasks; temp != NULL; temp = temp->next) {
@@ -825,30 +1178,29 @@
 		rect.height = task->height;
 
 		if(gdk_rectangle_intersect(&event->area, &rect, &dest))
-			draw_task (task, &dest);
+			tasklist_draw_task (task, &dest);
 	}
 
-	if (temp_tasks != NULL)
-		g_list_free (temp_tasks);
-
 	return FALSE;
 }
 
 /* This routine gets called when the user selects "properties" */
 static void
-cb_properties (void)
+cb_properties (AppletWidget *applet, Tasklist *tasklist)
 {
-	display_properties ();
+	tasklist_display_properties (tasklist);
 }
 
 /* This routine gets called when the user selects "about" */
 static void
-cb_about (void)
+cb_about (AppletWidget *applet, Tasklist *tasklist)
 {
 	static GtkWidget *dialog = NULL;
 
 	const char *authors[] = {
 		"Anders Carlsson (andersca gnu org)",
+		"Miguel de Icaza (miguel ximian com)",
+		"Jacob Berkman (jacob ximian com)",
 		NULL
 	};
 
@@ -860,12 +1212,13 @@
 		return;
 	}
 	
-	dialog = gnome_about_new ("Gnome Tasklist",
-				  VERSION,
-				  "Copyright (C) 1999 Anders Carlsson",
-				  authors,
-				  "A tasklist for the GNOME desktop environment.\nIcons by Tuomas Kuosmanen (tigert gimp org).",
-				  NULL);
+	dialog = gnome_about_new (
+		"Gnome Tasklist",
+		VERSION,
+		_("Copyright (C) 1999 Anders Carlsson"),
+		authors,
+		_("A tasklist for the GNOME desktop environment.\nIcons by Tuomas Kuosmanen (tigert gimp org)."),
+		NULL);
 	gtk_signal_connect (GTK_OBJECT(dialog), "destroy",
 			    GTK_SIGNAL_FUNC(gtk_widget_destroyed), &dialog);
 
@@ -875,223 +1228,264 @@
 
 /* Ignore mouse button 1 clicks */
 static gboolean
-ignore_1st_click (GtkWidget *widget, GdkEvent *event)
+ignore_1st_click (GtkWidget *widget, GdkEvent *event, Tasklist *tasklist)
 {
 	GdkEventButton *buttonevent = (GdkEventButton *)event;
 
 	if (event->type == GDK_BUTTON_PRESS &&
 	    buttonevent->button == 1) {
-		if (buttonevent->window != area->window)
+		if (buttonevent->window != tasklist->area->window)
 			buttonevent->button = 2;
 	}
 	if (event->type == GDK_BUTTON_RELEASE &&
 	    buttonevent->button == 1) {
-		if (buttonevent->window != area->window)
+		if (buttonevent->window != tasklist->area->window)
 			buttonevent->button = 2;
 	}
 	 
 	return FALSE;
 }
- 
+
 /*
  * Resort the task list.
  * (This is only here because tasks and tasklist_sorting_compare_func
  * are not globally defined.)
  */
 void
-resort_tasklist (void)
+resort_tasklist (Tasklist *tasklist)
 {
-       tasks = g_list_sort (tasks, tasklist_sorting_compare_func);
+	tasklist->vtasks = g_slist_sort (tasklist->vtasks, tlsort);
 }
 
 /* Changes size of the applet */
 void
-change_size (gboolean layout, int fullwidth)
+tasklist_change_size (Tasklist *tasklist, gboolean layout, int fullwidth)
 {
 	gboolean force_change = FALSE;
-	static int old_handle_width = -1;
-	static int old_handle_height = -1;
-	static int old_width = -1;
-	static int old_height = -1;
 	int handle_width = 0;
 	int handle_height = 0;
 	int width = 0;
 	int height = 0;
+
 
-	switch (applet_widget_get_panel_orient (APPLET_WIDGET (applet))) {
+	switch (applet_widget_get_panel_orient (APPLET_WIDGET (tasklist->applet))) {
 	case ORIENT_UP:
 	case ORIENT_DOWN:
-/*		if (Config.horz_fixed)
-			horz_width = Config.horz_width;
-		else
-		horz_width = 4;*/
+		width = tasklist->horz_width;
+		handle_width = tasklist->horz_width + DRAG_HANDLE_SIZE;
+		height = handle_height = get_horz_rows (tasklist) * ROW_HEIGHT;
 
-		width = horz_width;
-		handle_width = DRAG_HANDLE_SIZE + horz_width;
-		height = handle_height = get_horz_rows() * ROW_HEIGHT;
-
-		if (GTK_HANDLE_BOX (handle)->handle_position != GTK_POS_LEFT) {
-			GTK_HANDLE_BOX (handle)->handle_position = GTK_POS_LEFT;
-			force_change = TRUE;
-		}
+		GTK_HANDLE_BOX (tasklist->handle)->handle_position = GTK_POS_LEFT;
 		break;
 	case ORIENT_LEFT:
 	case ORIENT_RIGHT:
-/*		if (Config.vert_fixed)
-			vert_height = Config.vert_height;
-		else
-		vert_height = 4;*/
-
-		if (Config.follow_panel_size)
-			width = panel_size;
-		else
-			width = Config.vert_width;
-
-		if (Config.vert_width_full) {
-			if (fullwidth < 0) {
-				GList *temp_tasks = get_visible_tasks ();
+		width = tasklist->config.follow_panel_size
+			? tasklist->panel_size 
+			: tasklist->config.vert_width;
+
+		if (tasklist->config.vert_width_full) {
+			if (fullwidth < 0)
+				fullwidth = max_width (tasklist->vtasks);
 
-				fullwidth = max_width (temp_tasks);
-
-				g_list_free (temp_tasks);
-			}
-
-			if (fullwidth > width)
-				width = fullwidth;
+			width = MAX (width, fullwidth);
 		}
 
-		if (GTK_HANDLE_BOX (handle)->handle_position != GTK_POS_TOP) {
-			GTK_HANDLE_BOX (handle)->handle_position = GTK_POS_TOP;
-			force_change = TRUE;
-		}
-
 		handle_width = width;
-		handle_height = vert_height + DRAG_HANDLE_SIZE;
-		height = vert_height;
-	}
-
-	if (force_change ||
-	    old_width != width ||
-	    old_height != height ||
-	    old_handle_width != handle_width ||
-	    old_handle_height != handle_height) {
-		gtk_widget_set_usize (handle,
-				      handle_width,
-				      handle_height);
-		gtk_drawing_area_size (GTK_DRAWING_AREA (area), 
-				       width, height);
-		old_width = width;
-		old_height = height;
-		old_handle_width = handle_width;
-		old_handle_height = handle_height;
+		handle_height = tasklist->vert_height + DRAG_HANDLE_SIZE;
+		height = tasklist->vert_height;
+			
+		GTK_HANDLE_BOX (tasklist->handle)->handle_position = GTK_POS_TOP;
 	}
 
+	gtk_widget_set_usize (tasklist->handle, handle_width, handle_height);
+	gtk_drawing_area_size (GTK_DRAWING_AREA (tasklist->area), width, height);
+	
 	if (layout)
-		layout_tasklist (FALSE);
+		tasklist_layout_tasklist (tasklist);
 }
 
 /* Called when the panel's orient changes */
 static void
-cb_change_orient (GtkWidget *widget, GNOME_Panel_OrientType orient)
+cb_change_orient (GtkWidget *widget, GNOME_Panel_OrientType orient, Tasklist *tasklist)
 {
-	
-	tasklist_orient = orient;
+	tasklist->orient = orient;
 
 	/* Change size accordingly */
-	change_size (TRUE, -1);
+	tasklist_change_size (tasklist, TRUE, -1);
 }
 
 static void
-cb_change_pixel_size (GtkWidget *widget, int size)
+cb_change_pixel_size (GtkWidget *widget, int size, Tasklist *tasklist)
 {
-	panel_size = size;
+	tasklist->panel_size = size;
 	
 	/* Change size accordingly */
-	if(Config.follow_panel_size)
-		change_size (TRUE, -1);
+	if(tasklist->config.follow_panel_size)
+		tasklist_change_size (tasklist, TRUE, -1);
 }
 
 static void
-cb_help (GtkWidget *w, gpointer data)
+cb_help (AppletWidget *w, Tasklist *tasklist)
 {
 	GnomeHelpMenuEntry help_entry = { "tasklist_applet",
 					  "index.html" };
 	gnome_help_display(NULL, &help_entry);
 }
 
+static void
+tasklist_destroy (GtkObject *applet_widget, Tasklist *tasklist)
+{
+	gwmh_task_notifier_remove (tasklist->task_notifier_id);
+	gwmh_desk_notifier_remove (tasklist->desk_notifier_id);
+
+	tasklist->task_notifier_id = -1;
+	tasklist->desk_notifier_id = -1;
+}
+
 /* Create the applet */
-void
-create_applet (void)
+static Tasklist *
+tasklist_new (void)
 {
+	Tasklist *tasklist;
 	GtkWidget *hbox;
+
+	tasklist = g_new0 (Tasklist, 1);
+	tasklist->panel_size = 48;
+	tasklist->horz_width = 0;
+	tasklist->vert_height = 0;
+
+	tasklist->applet = applet_widget_new ("tasklist_applet");
+	if (!tasklist->applet) {
+		g_warning (_("Tasklist: Unable to create applet widget"));
+		g_free (tasklist);
+		return NULL;
+	}
 
-	applet = applet_widget_new ("tasklist_applet");
+#warning should we be doing this?
+	/* gtk_widget_ref (tasklist->applet); */
 	
 	hbox = gtk_hbox_new (FALSE, 0);
 	gtk_widget_show (hbox);
+	
+	tasklist->handle = gtk_handle_box_new ();
+	gtk_signal_connect (GTK_OBJECT (tasklist->handle), "event",
+			    GTK_SIGNAL_FUNC (ignore_1st_click), tasklist);
+
+	tasklist->area = gtk_drawing_area_new ();
+
 
+	gtk_widget_ensure_style (tasklist->area);
+	tasklist->unknown_icon = g_new (TasklistIcon, 1);
+	tasklist->unknown_icon->normal = gdk_pixbuf_new_from_xpm_data (unknown_xpm);
+	tasklist->unknown_icon->minimized = tasklist_icon_create_minimized_icon (tasklist, tasklist->unknown_icon->normal);
+
+#if 0
+	gtk_container_add (GTK_CONTAINER (tasklist->handle), tasklist->area);
+#endif
+
 	/* we must bind signals BEFORE applet_widget_add to avoid
 	 * a race */
-	gtk_signal_connect (GTK_OBJECT (applet), "change-orient",
-			    GTK_SIGNAL_FUNC (cb_change_orient), NULL);
-	gtk_signal_connect (GTK_OBJECT (applet), "save-session",
-			    GTK_SIGNAL_FUNC (write_config), NULL);
-	gtk_signal_connect (GTK_OBJECT (applet), "change-pixel-size",
-			    GTK_SIGNAL_FUNC (cb_change_pixel_size), NULL);
-
-	applet_widget_add (APPLET_WIDGET (applet), hbox);
-	
-	handle = gtk_handle_box_new ();
-	gtk_signal_connect (GTK_OBJECT (handle), "event",
-			    GTK_SIGNAL_FUNC (ignore_1st_click), NULL);
-
-	area = gtk_drawing_area_new ();
-
-	gtk_widget_show (area);
-	gtk_widget_show (handle);
-	gtk_container_add (GTK_CONTAINER (handle), area);
-	gtk_container_add (GTK_CONTAINER (hbox), handle);
+	gtk_signal_connect (GTK_OBJECT (tasklist->applet), "change-orient",
+			    GTK_SIGNAL_FUNC (cb_change_orient), tasklist);
+	gtk_signal_connect (GTK_OBJECT (tasklist->applet), "save-session",
+			    GTK_SIGNAL_FUNC (tasklist_write_config), tasklist);
+	gtk_signal_connect (GTK_OBJECT (tasklist->applet), "change-pixel-size",
+			    GTK_SIGNAL_FUNC (cb_change_pixel_size), tasklist);
 
-	gtk_widget_set_events (area, GDK_EXPOSURE_MASK | 
+	gtk_widget_set_events (tasklist->area, GDK_EXPOSURE_MASK | 
 			       GDK_BUTTON_PRESS_MASK |
 			       GDK_BUTTON_RELEASE_MASK);
-	gtk_signal_connect (GTK_OBJECT (area), "expose_event",
-			    GTK_SIGNAL_FUNC (cb_expose_event), NULL);
-	gtk_signal_connect (GTK_OBJECT (area), "button_press_event",
-			    GTK_SIGNAL_FUNC (cb_button_press_event), NULL);
-	gtk_signal_connect (GTK_OBJECT (area), "drag_motion",
-			    GTK_SIGNAL_FUNC (cb_drag_motion), NULL);
-	gtk_signal_connect (GTK_OBJECT (area), "drag_leave",
-			    GTK_SIGNAL_FUNC (cb_drag_leave), NULL);			    
-			    
-			    
-	gtk_drag_dest_set (GTK_WIDGET (area), 0,
-	 		   NULL, 0, GDK_ACTION_COPY);  
-
-	applet_widget_register_stock_callback (APPLET_WIDGET (applet),
-					       "properties",
-					       GNOME_STOCK_MENU_PROP,
-					       _("Properties..."),
-					       (AppletCallbackFunc) cb_properties,
-					       NULL);
-	applet_widget_register_stock_callback (APPLET_WIDGET (applet),
-					       "help",
-					       GNOME_STOCK_PIXMAP_HELP,
-					       _("Help"),
-					       (AppletCallbackFunc) cb_help,
-					       NULL);
-
-	applet_widget_register_stock_callback (APPLET_WIDGET (applet),
-					       "about",
-					       GNOME_STOCK_MENU_ABOUT,
-					       _("About..."),
-					       (AppletCallbackFunc) cb_about,
-					       NULL);
+	gtk_signal_connect (GTK_OBJECT (tasklist->area), "expose_event",
+			    GTK_SIGNAL_FUNC (cb_expose_event), tasklist);
+	gtk_signal_connect (GTK_OBJECT (tasklist->area), "button_press_event",
+			    GTK_SIGNAL_FUNC (cb_button_press_event), tasklist);
+	gtk_signal_connect (GTK_OBJECT (tasklist->area), "drag_motion",
+			    GTK_SIGNAL_FUNC (cb_drag_motion), tasklist);
+	gtk_signal_connect (GTK_OBJECT (tasklist->area), "drag_leave",
+			    GTK_SIGNAL_FUNC (cb_drag_leave), tasklist);			    
+			    			    
+	gtk_drag_dest_set (GTK_WIDGET (tasklist->area), 0,
+	 		   NULL, 0, GDK_ACTION_COPY);
+
+	/*
+	 * we add the area *after* the widget so that we get events on it
+	 */
+	gtk_container_add (GTK_CONTAINER (hbox), tasklist->handle);
+	applet_widget_add (APPLET_WIDGET (tasklist->applet), hbox);
+	gtk_container_add (GTK_CONTAINER (tasklist->handle), tasklist->area);
+	
+	tasklist_read_config (tasklist);
+	
+	applet_widget_register_stock_callback (
+		APPLET_WIDGET (tasklist->applet),
+		"properties",
+		GNOME_STOCK_MENU_PROP,
+		_("Properties..."),
+		(AppletCallbackFunc) cb_properties,
+		tasklist);
+
+	applet_widget_register_stock_callback (
+		APPLET_WIDGET (tasklist->applet),
+		"help",
+		GNOME_STOCK_PIXMAP_HELP,
+		_("Help"),
+		(AppletCallbackFunc) cb_help,
+		tasklist);
+
+	applet_widget_register_stock_callback (
+		APPLET_WIDGET (tasklist->applet),
+		"about",
+		GNOME_STOCK_MENU_ABOUT,
+		_("About..."),
+		(AppletCallbackFunc) cb_about,
+		tasklist);
+
+	tasklist->panel_size = applet_widget_get_panel_pixel_size(APPLET_WIDGET(tasklist->applet));
+	tasklist->orient = applet_widget_get_panel_orient(APPLET_WIDGET(tasklist->applet));
+
+	tasklist->task_notifier_id = gwmh_task_notifier_add (task_notifier, tasklist);
+	tasklist->desk_notifier_id = gwmh_desk_notifier_add (desk_notifier, tasklist);
+
+	gtk_signal_connect (GTK_OBJECT (tasklist->applet), "destroy",
+			    GTK_SIGNAL_FUNC (tasklist_destroy), tasklist);
+	
+	gtk_widget_show_all (tasklist->area);
+	gtk_widget_show_all (tasklist->handle);
+
+	tasklist_change_size (tasklist, TRUE, -1);
+
+	tasklist->tasks = g_hash_table_new (g_direct_hash, g_direct_equal);
+	tasklist->groups = g_hash_table_new (g_str_hash, g_str_equal);
+
+	g_list_foreach (gwmh_task_list_get (), (GFunc)tasklist_task_new, tasklist);
+
+	return tasklist;
+}
+
+static void
+tasklist_init (void)
+{
+	gwmh_init ();
+}
+
+static void
+tasklist_freeze (Tasklist *tasklist)
+{
+	tasklist->frozen = TRUE;
 }
 
+static void
+tasklist_thaw (Tasklist *tasklist)
+{
+	tasklist->frozen = FALSE;
+}
+
+#ifdef APPLET_COMPILE_AS_PROCESS
 gint
 main (gint argc, gchar *argv[])
 {
+	Tasklist *tasklist;
+	
 	/* Initialize i18n */
 	bindtextdomain (PACKAGE, GNOMELOCALEDIR);
 	textdomain (PACKAGE);
@@ -1108,25 +1502,76 @@
 	gtk_widget_set_default_colormap (gdk_rgb_get_cmap ());
 	gtk_widget_set_default_visual (gdk_rgb_get_visual ());
 
-	gwmh_init ();
-	gwmh_task_notifier_add (task_notifier, NULL);
-	gwmh_desk_notifier_add (desk_notifier, NULL);
+	tasklist_init ();
 	
-	create_applet ();
+	tasklist = create_applet ();
 
-	read_config ();
-	panel_size = applet_widget_get_panel_pixel_size(APPLET_WIDGET(applet));
-	tasklist_orient = applet_widget_get_panel_orient(APPLET_WIDGET(applet));
+	tasklist_change_size (tasklist, TRUE);
 
-	change_size (TRUE, -1);
-
 	gtk_widget_show (applet);
 
-	unknown_icon = g_new (TasklistIcon, 1);
-	unknown_icon->normal = gdk_pixbuf_new_from_xpm_data (unknown_xpm);
-	unknown_icon->minimized = tasklist_icon_create_minimized_icon (unknown_icon->normal);
-
 	applet_widget_gtk_main ();
 
 	return 0;
 }
+#else
+static GtkWidget *
+make_new_applet (const char *goad_id)
+{
+	static int inited = 0;
+	Tasklist *tasklist;
+	
+	if (!inited){
+		tasklist_init ();
+		inited = 1;
+	}
+	tasklist = tasklist_new ();
+
+	if (!tasklist)
+		return NULL;
+
+	gtk_widget_show_all (tasklist->applet);
+
+	return tasklist->applet;
+}
+
+static CORBA_Object
+activator (PortableServer_POA poa,
+	   const char *goad_id,
+	   const char **params,
+	   gpointer *impl_ptr,
+	   CORBA_Environment *ev)
+{
+	GtkWidget *widget;
+
+	widget = make_new_applet (goad_id);
+	if (widget == NULL) {
+		g_warning (_("Don't know how to activate `%s'\n"), goad_id);
+		return CORBA_OBJECT_NIL;
+	}
+
+	return applet_widget_corba_activate (widget, poa, goad_id,
+					     params, impl_ptr, ev);
+}
+static void
+deactivator (PortableServer_POA poa,
+	     const char *goad_id,
+	     gpointer impl_ptr,
+	     CORBA_Environment *ev)
+{
+	applet_widget_corba_deactivate (poa, goad_id, impl_ptr, ev);
+}
+
+static const char *repo_id[]={ "IDL:GNOME/Applet:1.0", NULL };
+static GnomePluginObject applets_list[] = { 
+	{ repo_id, "tasklist_applet", NULL, "Task list applet",
+	  &activator, &deactivator },
+	{ NULL }
+};
+
+GnomePlugin GNOME_Plugin_info = { 
+	applets_list,
+	NULL
+};
+
+#endif
Index: tasklist_applet.gnorba
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/tasklist_applet.gnorba,v
retrieving revision 1.1
diff -u -r1.1 tasklist_applet.gnorba
--- tasklist_applet.gnorba	1999/07/05 22:11:39	1.1
+++ tasklist_applet.gnorba	2001/01/23 04:00:11
@@ -1,5 +1,5 @@
 [tasklist_applet]
-type=exe
+type=shlib
 repo_id=IDL:GNOME/Applet:1.0
 description=GNOME Tasklist
-location_info=tasklist_applet
+location_info=libtasklist_applet.so
Index: tasklist_applet.h
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/tasklist_applet.h,v
retrieving revision 1.28
diff -u -r1.28 tasklist_applet.h
--- tasklist_applet.h	2000/12/20 23:31:30	1.28
+++ tasklist_applet.h	2001/01/23 04:00:11
@@ -1,14 +1,15 @@
+#ifndef _TASKLIST_APPLET_H_
+#define _TASKLIST_APPLET_H_
+
 #include <X11/Xatom.h>
 #include <X11/Xlib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include "applet-widget.h"
 #include "gwmh.h"
 
-#ifndef TASKLIST_APPLET_H
-#define TASKLIST_APPLET_H
-
 /* The row height of a task */
 #define ROW_HEIGHT 24
+typedef struct _Tasklist Tasklist;
 typedef struct _TasklistTask TasklistTask;
 typedef struct _TasklistConfig TasklistConfig;
 typedef struct _TasklistIcon TasklistIcon;
@@ -24,22 +25,52 @@
 typedef enum
 {
 	MENU_ACTION_CLOSE,
+	MENU_ACTION_SHOW,
+	MENU_ACTION_HIDE,
 	MENU_ACTION_SHOW_HIDE,
+	MENU_ACTION_SHADE,
+	MENU_ACTION_UNSHADE,
 	MENU_ACTION_SHADE_UNSHADE,
+	MENU_ACTION_STICK,
+	MENU_ACTION_UNSTICK,
 	MENU_ACTION_STICK_UNSTICK,
 	MENU_ACTION_KILL,
 	MENU_ACTION_LAST
 } MenuAction;
 
 struct _TasklistTask {
+	Tasklist *tasklist;
 	gint x, y;
 	gint width, height;
 	gint fullwidth;
 	TasklistIcon *icon;
 	Pixmap wmhints_icon;
-	GwmhTask *gwmh_task;
 	GtkWidget *menu;
-        gint serial_number;  /* for sorting */
+
+	/* 
+	 * if we are a group, this is not a real task but a false one
+	 * filled in with what the task group should be like
+	 */
+	GwmhTask *gwmh_task;
+
+	/* this could be a union */
+	/* for real tasks */
+	TasklistTask *group;
+	GtkWidget *menuitem; /* the menuitem in the groups menu */
+
+	/* for task groups */
+	char *group_name;
+	GSList *tasks;  /* all tasks in group */
+	GSList *vtasks; /* visible tasks in group */
+	TasklistTask *focused_task; /* the task in our group last focused */
+	
+	/* whether we are in the vtasks list */
+	gboolean visible;
+	/* whether we are a task group */
+	gboolean task_group;
+
+	/* set when we get a notify that the gwmh window is destroyed */
+	gboolean destroyed;
 };
 
 struct _TasklistConfig {
@@ -51,46 +82,133 @@
 	gboolean all_desks_minimized; /* Show minimized tasks on all desktops */
 	gboolean confirm_before_kill; /* Confirm before killing windows */
 	gboolean move_to_current; /* Move iconified tasks to current workspace */
-        gboolean sort_tasklist; /* show the task list in sorted order? */
 	
 	/* Follow the panel sizes */
 	gboolean follow_panel_size;
+
+	/*
+	 * Stuff for horizontal mode
+	 */
+
+	/* The width of the tasklist */
+	gint horz_width;
+
+	/* Number of rows */	
+	gint horz_rows;
+
+	/* Fixed or dynamic sizing */
+	gboolean horz_fixed;
+
+	/* Width of a single task (for dynamic sizing) */
+	gint horz_taskwidth; 
+
+	/* in dynamic mode, never push applets */
+	gboolean horz_never_push;
+
+	/*
+	 * Stuff for vertical mode
+	 */
+	/* The height of the tasklist */
+	gint vert_height;
+	
+	/* The width of the tasklist */
+	gint vert_width; 
+
+	/* Fixed or dynamic sizing */
+	gboolean vert_fixed; 
 
-	/* Stuff for horizontal mode */
-	gint horz_width; /* The width of the tasklist */
-	gint horz_rows; /* Number of rows */
-	gboolean horz_fixed; /* Fixed or dynamic sizing */
-	gint horz_taskwidth; /* Width of a single task (for dynamic sizing) */
-	gboolean horz_never_push; /* In dynamic mode, never push applets */
-
-	/* Stuff for vertical mode */
-	gint vert_height; /* The height of the tasklist */
-	gint vert_width; /* The width of the tasklist */
-	gboolean vert_fixed; /* Fixed or dynamic sizing */
-	gboolean vert_width_full;  /* a mode where th width is the maximum width */
-				   /* of any window title. */
-	gboolean vert_never_push; /* In dynamic mode, never push applets */
+	/* a mode where the width is the max width of any window title */
+	gboolean vert_width_full;
 
+	/* in dynamic mode, never push applets */
+	gboolean vert_never_push;
+
+	/* grouping options */
+	gboolean enable_grouping;
+	gint grouping_min;
 };
 
 struct _TasklistIcon {
 	GdkPixbuf *normal;
 	GdkPixbuf *minimized;
 };
+
+struct _Tasklist {
+	GNOME_Panel_OrientType orient; /* Tasklist orient */
+	GtkWidget *handle; /* The handle box */
+	GtkWidget *applet; /* The applet */
+	GtkWidget *area; /* The drawing area used to display tasks */
+
+	/* The list of tasks used */
+	GHashTable *tasks;
+
+	/* our task groups, grouped by WM_CLASS */
+	GHashTable *groups;
+
+	/* 
+	 * list of visible tasks.  this should be set by the gwmh
+	 * listner 
+	 */
+	GSList *vtasks;
+
+	/* idle timeout for re-laying out */
+	guint layout_timeout;
+#define MOTION_TIMEOUT 500 /* Show task motion_task if cursor over task for ... msec */
+
+	/* Show task motion_task after MOTION_TIMEOUT in drag_motion */
+	int motion_timeout;
+
+	/* Task to show after motion_timeout */
+	TasklistTask *motion_task; 
+
+	/* Vertical height, used for resizing */
+	gint vert_height;
+
+	/* Horizontal width, used for resizing */
+	gint horz_width;  
+
+	gint panel_size;
+
+	/* The configuration */
+	TasklistConfig config;
+	TasklistIcon *unknown_icon; /* The unknown icon */
+
+	/*
+	 * Used during configuration editing
+	 */
+	/* The tasklist properties configuration */
+	TasklistConfig PropsConfig;
+
+	/* The Property box */
+	GtkWidget *prop;
+
+	guint task_notifier_id;
+	guint desk_notifier_id;
+
+	gboolean frozen;
+};
 
-void menu_popup (TasklistTask *task, guint button, guint32 activate_time);
-void display_properties (void);
-void read_config (void);
-gboolean write_config (gpointer data,
-		       const gchar *privcfgpath,
-		       const gchar *globcfgpath);
-void resort_tasklist (void);
-/* fullwidth can be -1 so if it's to be recomputed (only if needed */
-void change_size (gboolean layout, int fullwidth);
-void layout_tasklist (gboolean call_change_size);
-GdkPixbuf *tasklist_icon_create_minimized_icon (GdkPixbuf *pixbuf);
-void tasklist_icon_set (TasklistTask *task);
-void tasklist_icon_destroy (TasklistTask *task);
-Pixmap tasklist_icon_get_pixmap (TasklistTask *task);
+void   	   tasklist_menu_popup          (TasklistTask *task, guint button,
+					 guint32 activate_time);
+void   	   tasklist_group_popup         (TasklistTask *task, guint button,
+					 guint32 activate_time);
+void       tasklist_freeze              (Tasklist *tasklist);
+void       tasklist_thaw                (Tasklist *tasklist);
+void       tasklist_draw_task           (TasklistTask *task, GdkRectangle *rect);
+void   	   tasklist_display_properties  (Tasklist *);
+void   	   tasklist_read_config         (Tasklist *);
+void       tasklist_clean_menu          (TasklistTask *);
+gboolean   tasklist_write_config        (GtkWidget *w,
+					 const gchar *privcfgpath,
+					 const gchar *globcfgpath,
+					 gpointer data);
+gchar     *tasklist_task_get_label      (TasklistTask *, int, gboolean add_groupcount);
+void       tasklist_change_size         (Tasklist *, gboolean layout, int fullwidth);
+void       tasklist_redo_vtasks         (Tasklist *);
+void       tasklist_layout_tasklist     (Tasklist *tasklist);
+GdkPixbuf *tasklist_icon_create_minimized_icon (Tasklist *, GdkPixbuf *pixbuf);
+void       tasklist_icon_set        (TasklistTask *task);
+void       tasklist_icon_destroy    (TasklistTask *task);
+Pixmap     tasklist_icon_get_pixmap (TasklistTask *task);
 
-#endif /* TASKLIST_APPLET_H */
+#endif /* _TASKLIST_APPLET_H_ */
Index: tasklist_config.c
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/tasklist_config.c,v
retrieving revision 1.23
diff -u -r1.23 tasklist_config.c
--- tasklist_config.c	2000/12/20 23:31:30	1.23
+++ tasklist_config.c	2001/01/23 04:00:11
@@ -1,104 +1,75 @@
 #include "tasklist_applet.h"
 
-/* The applet */
-extern GtkWidget *applet;
-
-/* The configuration */
-TasklistConfig Config;
-
-gboolean write_config (gpointer data,
-		   const gchar *privcfgpath,
-		   const gchar *globcfgpath)
+gboolean
+tasklist_write_config (GtkWidget *w, const gchar *privcfgpath, const gchar *globcfgpath, gpointer data)
 {
+	Tasklist *tasklist = (Tasklist *) data;
+
 	gnome_config_push_prefix (privcfgpath 
 				  ? privcfgpath
-				  : APPLET_WIDGET (applet)->privcfgpath);
-
-	gnome_config_set_bool ("tasklist/follow_panel_size",
-			       Config.follow_panel_size);
+				  : APPLET_WIDGET (tasklist->applet)->privcfgpath);
 
-	gnome_config_set_bool ("tasklist/horz_fixed",
-			       Config.horz_fixed);
-	gnome_config_set_int ("tasklist/horz_width", 
-			      Config.horz_width);
-	gnome_config_set_int ("tasklist/horz_rows", 
-			      Config.horz_rows);
-	gnome_config_set_int ("tasklist/horz_taskwidth",
-			      Config.horz_taskwidth);
-	gnome_config_set_bool ("tasklist/horz_never_push",
-			       Config.horz_never_push);
-
-	gnome_config_set_bool ("tasklist/vert_fixed",
-			       Config.vert_fixed);	
-	gnome_config_set_int ("tasklist/vert_height", 
-			      Config.vert_height);
-	gnome_config_set_int ("tasklist/vert_width",
-			      Config.vert_width);
-	gnome_config_set_bool ("tasklist/vert_width_full",
-			       Config.vert_width_full);
-	gnome_config_set_bool ("tasklist/vert_never_push",
-			       Config.vert_never_push);
-
-	
-	gnome_config_set_bool ("tasklist/show_mini_icons",
-			       Config.show_mini_icons);
-	gnome_config_set_bool ("tasklist/show_normal",
-			       Config.show_normal);
-	gnome_config_set_bool ("tasklist/show_minimized",
-			       Config.show_minimized);
-
-	gnome_config_set_bool ("tasklist/all_desks_normal",
-			       Config.all_desks_normal);
-	gnome_config_set_bool ("tasklist/all_desks_minimized",
-			       Config.all_desks_minimized);
-
-	gnome_config_set_bool ("tasklist/confirm_before_kill",
-			       Config.confirm_before_kill);
-	gnome_config_set_bool ("tasklist/move_to_current",
-			       Config.move_to_current);
-        gnome_config_set_bool ("tasklist/sort_tasklist",
-			       /* XXX: see comment in
-				* tasklist_applet.c:bool_compare_func for
-				* what's wrong here */
-                               Config.sort_tasklist);
+	gnome_config_set_bool ("tasklist/follow_panel_size",   tasklist->config.follow_panel_size);
+	gnome_config_set_bool ("tasklist/horz_fixed",          tasklist->config.horz_fixed);
+	gnome_config_set_bool ("tasklist/horz_never_push",     tasklist->config.horz_never_push);
+	gnome_config_set_int  ("tasklist/horz_width",          tasklist->config.horz_width);
+	gnome_config_set_int  ("tasklist/horz_rows",           tasklist->config.horz_rows);
+	gnome_config_set_int  ("tasklist/horz_taskwidth",      tasklist->config.horz_taskwidth);
+	gnome_config_set_bool ("tasklist/vert_fixed",          tasklist->config.vert_fixed);
+	gnome_config_set_int  ("tasklist/vert_height",         tasklist->config.vert_height);
+	gnome_config_set_bool ("tasklist/vert_never_push",     tasklist->config.vert_never_push);
+	gnome_config_set_int  ("tasklist/vert_width",          tasklist->config.vert_width);
+	gnome_config_set_int  ("tasklist/vert_width_full",     tasklist->config.vert_width_full);       
+	gnome_config_set_bool ("tasklist/show_mini_icons",     tasklist->config.show_mini_icons);
+	gnome_config_set_bool ("tasklist/show_normal",         tasklist->config.show_normal);
+	gnome_config_set_bool ("tasklist/show_minimized",      tasklist->config.show_minimized);
+	gnome_config_set_bool ("tasklist/all_desks_normal",    tasklist->config.all_desks_normal);
+	gnome_config_set_bool ("tasklist/all_desks_minimized", tasklist->config.all_desks_minimized);
+	gnome_config_set_bool ("tasklist/confirm_before_kill", tasklist->config.confirm_before_kill);
+	gnome_config_set_bool ("tasklist/move_to_current",     tasklist->config.move_to_current);
+	gnome_config_set_bool ("tasklist/enable_grouping",     tasklist->config.enable_grouping);
+	gnome_config_set_int  ("tasklist/grouping_min",        tasklist->config.grouping_min);
+			       
 	gnome_config_sync ();
 	
 	gnome_config_pop_prefix ();
 	return FALSE;
 }
 
-void read_config (void)
+void
+tasklist_read_config (Tasklist *tasklist)
 {
-	gnome_config_push_prefix (APPLET_WIDGET (applet)->privcfgpath);
+	gnome_config_push_prefix (APPLET_WIDGET (tasklist->applet)->privcfgpath);
 
-	Config.follow_panel_size = gnome_config_get_bool ("tasklist/follow_panel_size=true");
+	tasklist->config.follow_panel_size = gnome_config_get_bool ("tasklist/follow_panel_size=true");
 
-	Config.horz_fixed = gnome_config_get_bool ("tasklist/horz_fixed=true");
+	tasklist->config.horz_fixed = gnome_config_get_bool ("tasklist/horz_fixed=true");
 	/* if the screen is not too wide, make it default to 300 */
 	if (gdk_screen_width () <= 800)
-		Config.horz_width = gnome_config_get_int ("tasklist/horz_width=300");
+		tasklist->config.horz_width = gnome_config_get_int ("tasklist/horz_width=300");
 	else
-		Config.horz_width = gnome_config_get_int ("tasklist/horz_width=450");
-	Config.horz_rows = gnome_config_get_int ("tasklist/horz_rows=2");
-	Config.horz_taskwidth = gnome_config_get_int ("tasklist/horz_taskwidth=150");
-	Config.horz_never_push = gnome_config_get_bool ("tasklist/horz_never_push=true");
-
-	Config.vert_fixed = gnome_config_get_bool ("tasklist/vert_fixed=true");
-	Config.vert_width = gnome_config_get_int ("tasklist/vert_width=48");
-	Config.vert_height = gnome_config_get_int ("tasklist/vert_height=300");
-	Config.vert_width_full = gnome_config_get_bool ("tasklist/vert_width_full=false");
-	Config.vert_never_push = gnome_config_get_bool ("tasklist/vert_never_push=true");
+		tasklist->config.horz_width = gnome_config_get_int ("tasklist/horz_width=450");
+	tasklist->config.horz_never_push = gnome_config_get_bool ("tasklist/horz_never_push=false");
+	tasklist->config.horz_rows = gnome_config_get_int ("tasklist/horz_rows=2");
+	tasklist->config.horz_taskwidth = gnome_config_get_int ("tasklist/horz_taskwidth=150");
+	tasklist->config.vert_fixed = gnome_config_get_bool ("tasklist/vert_fixed=true");
+	tasklist->config.vert_never_push = gnome_config_get_bool ("tasklist/vert_never_push=false");
+	tasklist->config.vert_width = gnome_config_get_int ("tasklist/vert_width=48");
+	tasklist->config.vert_width_full = gnome_config_get_bool ("tasklist/vert_width_full=false");
+	tasklist->config.vert_height = gnome_config_get_int ("tasklist/vert_height=300");
 
-	Config.confirm_before_kill = gnome_config_get_bool ("tasklist/confirm_before_kill=true");
+	tasklist->config.confirm_before_kill = gnome_config_get_bool ("tasklist/confirm_before_kill=true");
 	
-	Config.show_mini_icons = gnome_config_get_bool ("tasklist/show_mini_icons=true");
-	Config.show_normal = gnome_config_get_bool ("tasklist/show_normal=true");
-	Config.show_minimized = gnome_config_get_bool ("tasklist/show_minimized=true");
-	Config.all_desks_normal = gnome_config_get_bool ("tasklist/all_desks_normal=false");
-	Config.all_desks_minimized = gnome_config_get_bool ("tasklist/all_desks_minimized=false");
-	Config.move_to_current = gnome_config_get_bool ("tasklist/move_to_current=false");
-        Config.sort_tasklist = gnome_config_get_bool ("tasklist/sort_tasklist=true");
+	tasklist->config.show_mini_icons = gnome_config_get_bool ("tasklist/show_mini_icons=true");
+	tasklist->config.show_normal = gnome_config_get_bool ("tasklist/show_normal=true");
+	tasklist->config.show_minimized = gnome_config_get_bool ("tasklist/show_minimized=true");
+	tasklist->config.all_desks_normal = gnome_config_get_bool ("tasklist/all_desks_normal=false");
+	tasklist->config.all_desks_minimized = gnome_config_get_bool ("tasklist/all_desks_minimized=false");
+	tasklist->config.move_to_current = gnome_config_get_bool ("tasklist/move_to_current=false");
 	
+	tasklist->config.enable_grouping = gnome_config_get_bool ("tasklist/enable_grouping=true");
+	tasklist->config.grouping_min    = gnome_config_get_int  ("tasklist/grouping_min=3");
+
 	gnome_config_pop_prefix ();
 }
 
Index: tasklist_icon.c
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/tasklist_icon.c,v
retrieving revision 1.6
diff -u -r1.6 tasklist_icon.c
--- tasklist_icon.c	2000/11/15 05:13:08	1.6
+++ tasklist_icon.c	2001/01/23 04:00:11
@@ -10,9 +10,6 @@
 static gboolean tasklist_icon_check_x (TasklistTask *task);
 static void tasklist_icon_set_minimized (TasklistTask *task);
 
-extern GtkWidget *area; /* The drawing area used to display tasks */
-extern TasklistIcon *unknown_icon; /* The unknown icon */
-
 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
 
 /* Shamelessly stolen from gwmh.c by Tim Janik */
@@ -22,6 +19,9 @@
 {
 	XWMHints *wmhints;
 	Pixmap pixmap;
+
+	if (task == NULL)
+		return 0;
 	
 	wmhints = XGetWMHints (GDK_DISPLAY (), task->gwmh_task->xwin);
 
@@ -133,7 +133,8 @@
 tasklist_icon_check_mini (TasklistTask *task)
 {
 	GdkGC *gc;
-	int x, y, b, width, height, depth;
+	int x, y;
+	guint b, width, height, depth;
 	guint32 *atomdata;
 	Window root;
 	GdkImage *image;
@@ -180,7 +181,7 @@
 	
 	pixbuf = gdk_pixbuf_get_from_drawable (NULL,
 					       pixmap,
-					       gtk_widget_get_colormap (area),
+					       gtk_widget_get_colormap (task->tasklist->area),
 					       0, 0,
 					       0, 0,
 					       width, height);
@@ -189,8 +190,8 @@
 	if (size > 1 && atomdata[1]) {
 		mask = gdk_pixmap_new (NULL, width, height, depth);
 		gc = gdk_gc_new (mask);
-		gdk_gc_set_background (gc, &area->style->black);
-		gdk_gc_set_foreground (gc, &area->style->white);
+		gdk_gc_set_background (gc, &task->tasklist->area->style->black);
+		gdk_gc_set_foreground (gc, &task->tasklist->area->style->white);
 		XCopyPlane (GDK_DISPLAY (), atomdata[1], GDK_WINDOW_XWINDOW (mask),
 			    GDK_GC_XGC (gc), 0, 0, width, height, 0, 0, 1);
 		gdk_gc_unref (gc);
@@ -263,12 +264,12 @@
 		return FALSE;
 	}
 	
-	pixmap = gdk_pixmap_new (area->window, width, height, -1);	
+	pixmap = gdk_pixmap_new (task->tasklist->area->window, width, height, -1);	
 	gc = gdk_gc_new (pixmap);
 
 	if (depth == 1) {
-		gdk_gc_set_background (gc, &area->style->white);
-		gdk_gc_set_foreground (gc, &area->style->black);
+		gdk_gc_set_background (gc, &task->tasklist->area->style->white);
+		gdk_gc_set_foreground (gc, &task->tasklist->area->style->black);
 		XCopyPlane (GDK_DISPLAY (), wmhints->icon_pixmap, GDK_WINDOW_XWINDOW (pixmap),
 			   GDK_GC_XGC (gc), 0, 0, width, height, 0, 0, 1);
 	}
@@ -276,7 +277,7 @@
 		XCopyArea (GDK_DISPLAY (), wmhints->icon_pixmap, GDK_WINDOW_XWINDOW (pixmap),
 			   GDK_GC_XGC (gc), 0, 0, width, height, 0, 0);
 	}
-	pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, gtk_widget_get_colormap (area), 
+	pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, gtk_widget_get_colormap (task->tasklist->area), 
 					       0, 0,
 					       0, 0, width, height);
 	gdk_gc_destroy (gc);
@@ -288,8 +289,8 @@
 	if (wmhints->flags & IconMaskHint) {
 	       mask = gdk_pixmap_new (NULL, width, height, depth);
 	       gc = gdk_gc_new (mask);
-	       gdk_gc_set_background (gc, &area->style->black);
-	       gdk_gc_set_foreground (gc, &area->style->white);
+	       gdk_gc_set_background (gc, &task->tasklist->area->style->black);
+	       gdk_gc_set_foreground (gc, &task->tasklist->area->style->white);
 	       XCopyPlane (GDK_DISPLAY (), wmhints->icon_mask, GDK_WINDOW_XWINDOW (mask),
 			   GDK_GC_XGC (gc), 0, 0, width, height, 0, 0, 1);
 	       gdk_gc_unref (gc);
@@ -333,10 +334,13 @@
 void
 tasklist_icon_set (TasklistTask *task)
 {
-	task->icon = g_new (TasklistIcon, 1);
+	if (task == NULL)
+		return;
+	
+	task->icon = g_new0 (TasklistIcon, 1);
 
-	task->icon->normal = unknown_icon->normal;
-	task->icon->minimized = unknown_icon->minimized;
+	task->icon->normal    = task->tasklist->unknown_icon->normal;
+	task->icon->minimized = task->tasklist->unknown_icon->minimized;
 
 	if (!tasklist_icon_check_x (task))
 		tasklist_icon_check_mini (task);
@@ -349,19 +353,22 @@
 tasklist_icon_set_minimized (TasklistTask *task)
 {
 	if (task->icon->minimized &&
-		task->icon->minimized != unknown_icon->minimized)
+	    task->icon->minimized != task->tasklist->unknown_icon->minimized)
 		gdk_pixbuf_unref (task->icon->minimized);
 
-	task->icon->minimized = tasklist_icon_create_minimized_icon (task->icon->normal);
+	task->icon->minimized = tasklist_icon_create_minimized_icon (task->tasklist, task->icon->normal);
 }
 
 void
 tasklist_icon_destroy (TasklistTask *task)
 {
-	if (task->icon->normal != unknown_icon->normal)
+	if (task == NULL)
+		return;
+	
+	if (task->icon->normal != task->tasklist->unknown_icon->normal)
 		gdk_pixbuf_unref (task->icon->normal);
 	
-	if (task->icon->minimized != unknown_icon->minimized)
+	if (task->icon->minimized != task->tasklist->unknown_icon->minimized)
 		gdk_pixbuf_unref (task->icon->minimized);
 
 	g_free (task->icon);
@@ -369,7 +376,7 @@
 
 /* Stolen from gnome-pixmap.c */
 GdkPixbuf *
-tasklist_icon_create_minimized_icon (GdkPixbuf *pixbuf)
+tasklist_icon_create_minimized_icon (Tasklist *tasklist, GdkPixbuf *pixbuf)
 {
 	GdkPixbuf *target;
 	gint i, j;
@@ -380,7 +387,7 @@
 	gint32 red, green, blue;
 	GdkColor color;
 	
-	color = area->style->bg[GTK_STATE_NORMAL];
+	color = tasklist->area->style->bg[GTK_STATE_NORMAL];
 	red = color.red / 255;
 	blue = color.blue / 255;
 	green = color.green / 255;
Index: tasklist_menu.c
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/tasklist_menu.c,v
retrieving revision 1.18
diff -u -r1.18 tasklist_menu.c
--- tasklist_menu.c	2000/12/20 22:34:23	1.18
+++ tasklist_menu.c	2001/01/23 04:00:11
@@ -4,31 +4,23 @@
 #include "tasklist_applet.h"
 #include "pixmaps.h"
 
-GtkWidget *get_popup_menu (TasklistTask *task);
-void add_menu_item (gchar *name, GtkWidget *menu, MenuAction action, gchar **xpm);
-gboolean cb_menu (GtkWidget *widget, gpointer data);
-void cb_to_desktop (GtkWidget *widget, gpointer data);
-void cb_menu_position (GtkMenu *menu, gint *x, gint *y, gpointer user_data);
-
-extern TasklistConfig Config;
-extern GtkWidget *area;
-extern GtkWidget *applet;
-TasklistTask *current_task;
+/* define to x for debug output */
+#define d(x)
 
 /* Callback for menu positioning */
-void
+static void
 cb_menu_position (GtkMenu *menu, gint *x, gint *y, gpointer user_data)
 {
 	GtkRequisition mreq;
 	gint wx, wy;
-	TasklistTask *task;
+	TasklistTask *task = gtk_object_get_data (GTK_OBJECT (menu), "task");
 
-	current_task = task = (TasklistTask *)user_data;
+	g_return_if_fail (task != NULL);
 
 	gtk_widget_get_child_requisition (GTK_WIDGET (menu), &mreq);
-	gdk_window_get_origin (area->window, &wx, &wy);
+	gdk_window_get_origin (task->tasklist->area->window, &wx, &wy);
 
-	switch (applet_widget_get_panel_orient (APPLET_WIDGET (applet))) {
+	switch (applet_widget_get_panel_orient (APPLET_WIDGET (task->tasklist->applet))) {
 	case ORIENT_UP:
 		*x = wx + task->x;
 		*y = wy - mreq.height + task->y;
@@ -50,89 +42,132 @@
 
 }
 
-/* Callback for menus */
-gboolean
-cb_menu (GtkWidget *widget, gpointer data)
+static gboolean
+do_action (TasklistTask *task, gpointer data)
 {
-	switch (GPOINTER_TO_INT (data)) {
+	MenuAction action = GPOINTER_TO_INT (data);
+	Tasklist *tasklist = task->tasklist;
+	GtkWidget *dialog;
+	gint retval;
+
+	switch (action) {
 	case MENU_ACTION_SHADE_UNSHADE:
-		if (GWMH_TASK_SHADED (current_task->gwmh_task))
-			gwmh_task_unset_gstate_flags (current_task->gwmh_task,
-						      GWMH_STATE_SHADED);
-		else
-			gwmh_task_set_gstate_flags (current_task->gwmh_task,
-						    GWMH_STATE_SHADED);
+		action = GWMH_TASK_SHADED (task->gwmh_task) ? MENU_ACTION_UNSHADE : MENU_ACTION_SHADE;
 		break;
 	case MENU_ACTION_STICK_UNSTICK:
-		if (GWMH_TASK_STICKY (current_task->gwmh_task))
-			gwmh_task_unset_gstate_flags (current_task->gwmh_task,
-						      GWMH_STATE_STICKY);
-		else
-			gwmh_task_set_gstate_flags (current_task->gwmh_task,
-						    GWMH_STATE_STICKY);
+		action = GWMH_TASK_STICKY (task->gwmh_task) ? MENU_ACTION_UNSTICK : MENU_ACTION_STICK;
 		break;
-	case MENU_ACTION_KILL:
-		if (Config.confirm_before_kill) {
-			GtkWidget *dialog;
-			gint retval;
-
-			dialog = gnome_message_box_new(_("Warning! Unsaved changes will be lost!\nProceed?"),
-						       GNOME_MESSAGE_BOX_WARNING,
-						       GNOME_STOCK_BUTTON_YES,
-						       GNOME_STOCK_BUTTON_NO,
-						       NULL);
-			gtk_widget_show(dialog);
-			retval = gnome_dialog_run(GNOME_DIALOG(dialog));
-
-			if (retval)
-				return TRUE;
+	case MENU_ACTION_SHOW_HIDE:
+		action = GWMH_TASK_ICONIFIED (task->gwmh_task) ? MENU_ACTION_SHOW : MENU_ACTION_HIDE;
+		break;
+	default:
+		break;
+	}
 
-			gwmh_task_kill(current_task->gwmh_task);
-		}
-		else
-			gwmh_task_kill (current_task->gwmh_task);
+	switch (action) {
+	case MENU_ACTION_UNSHADE:
+		gwmh_task_unset_gstate_flags (task->gwmh_task,
+					      GWMH_STATE_SHADED);
 		break;
-	case MENU_ACTION_SHOW_HIDE:
-		if (GWMH_TASK_ICONIFIED (current_task->gwmh_task)) {
-			gwmh_desk_set_current_area (current_task->gwmh_task->desktop,
-						    current_task->gwmh_task->harea,
-						    current_task->gwmh_task->varea);
-			gwmh_task_show (current_task->gwmh_task);
+	case MENU_ACTION_SHADE:
+		gwmh_task_set_gstate_flags (task->gwmh_task,
+					    GWMH_STATE_SHADED);
+		break;
+	case MENU_ACTION_UNSTICK:
+		gwmh_task_unset_gstate_flags (task->gwmh_task,
+					      GWMH_STATE_STICKY);
+		break;
+	case MENU_ACTION_STICK:
+		gwmh_task_set_gstate_flags (task->gwmh_task,
+					    GWMH_STATE_STICKY);
+		break;
+	case MENU_ACTION_KILL:
+		if (!tasklist->config.confirm_before_kill) {
+			gwmh_task_kill (task->gwmh_task);
+			break;
 		}
-		else
-			gwmh_task_iconify (current_task->gwmh_task);
+		dialog = gnome_message_box_new(_("Warning! Unsaved changes will be lost!\nProceed?"),
+					       GNOME_MESSAGE_BOX_WARNING,
+					       GNOME_STOCK_BUTTON_YES,
+					       GNOME_STOCK_BUTTON_NO,
+					       NULL);
+		gtk_widget_show(dialog);
+		retval = gnome_dialog_run(GNOME_DIALOG(dialog));
+		
+		if (retval)
+			return TRUE;
+		
+		gwmh_task_kill(task->gwmh_task);
+		break;
+	case MENU_ACTION_SHOW:
+		gwmh_desk_set_current_area (task->gwmh_task->desktop,
+					    task->gwmh_task->harea,
+					    task->gwmh_task->varea);
+		gwmh_task_show (task->gwmh_task);
+		gwmh_task_raise (task->gwmh_task);
+		gwmh_task_focus (task->gwmh_task);
 		break;
+	case MENU_ACTION_HIDE:
+		gwmh_task_iconify (task->gwmh_task);
+		break;
 	case MENU_ACTION_CLOSE:
-		gwmh_task_close (current_task->gwmh_task);
+		gwmh_task_close (task->gwmh_task);
 		break;
 		
 	default:
-		g_print ("Menu Callback: %d\n", GPOINTER_TO_INT (data));
+		d(g_print ("Menu Callback: %d\n", GPOINTER_TO_INT (data)));
 	}
 	return FALSE;
+
 }
 
-/* Open a popup menu with window operations */
-void
-menu_popup (TasklistTask *task, guint button, guint32 activate_time)
+/* Callback for menus */
+static gboolean
+cb_menu (GtkWidget *widget, gpointer data)
 {
-	if(task->menu)
-		gtk_widget_destroy(task->menu);
+	MenuAction action = GPOINTER_TO_INT (data);
+	GtkWidget *dialog;
+	gint retval;
+	int config_save = 0;
 
-	task->menu = get_popup_menu (task);
+	TasklistTask *task = gtk_object_get_data (GTK_OBJECT (widget), "task");
 
-	gtk_signal_connect(GTK_OBJECT(task->menu), "destroy",
-			   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
-			   &task->menu);
+	g_return_val_if_fail (task != NULL, FALSE);
 
-	gtk_menu_popup (GTK_MENU (task->menu), NULL, NULL,
-			cb_menu_position, task,
-			button, activate_time);
+	if (!task->task_group)
+		return do_action (task, data);
+
+	if (!task->vtasks)
+		return FALSE;
+	
+	if (action == MENU_ACTION_KILL) {
+		config_save = task->tasklist->config.confirm_before_kill;
+		if (config_save) {
+			dialog = gnome_message_box_new(_("Warning! Unsaved changes will be lost!\nProceed?"),
+						       GNOME_MESSAGE_BOX_WARNING,
+						       GNOME_STOCK_BUTTON_YES,
+						       GNOME_STOCK_BUTTON_NO,
+						       NULL);
+			gtk_widget_show(dialog);
+			retval = gnome_dialog_run(GNOME_DIALOG(dialog));
+			
+			if (retval)
+				return TRUE;
+			
+			task->tasklist->config.confirm_before_kill = FALSE;
+		}
+	}
+	g_slist_foreach (task->vtasks, (GFunc)do_action, data);
+
+	if (action == MENU_ACTION_KILL)
+		task->tasklist->config.confirm_before_kill = config_save;
+
+	return FALSE;
 }
 
 /* Add a menu item to the popup menu */
-void 
-add_menu_item (gchar *name, GtkWidget *menu, MenuAction action, gchar **xpm)
+static void 
+add_menu_item (TasklistTask *task, gchar *name, GtkWidget *menu, MenuAction action, gchar **xpm)
 {
 	GtkWidget *menuitem;
 	GdkPixmap *pixmap;
@@ -145,33 +180,49 @@
 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 	gtk_container_add (GTK_CONTAINER (menuitem), label);
 	if (xpm) {
-		pixmap = gdk_pixmap_create_from_xpm_d (area->window, &mask, NULL, xpm);
+		pixmap = gdk_pixmap_create_from_xpm_d (task->tasklist->area->window, &mask, NULL, xpm);
 		gtkpixmap = gtk_pixmap_new (pixmap, mask);
 		gtk_pixmap_menu_item_set_pixmap (GTK_PIXMAP_MENU_ITEM (menuitem), gtkpixmap);
 	}
-	
+
+	gtk_object_set_data (GTK_OBJECT (menuitem), "task", task);
+
 	gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
 			    GTK_SIGNAL_FUNC (cb_menu), GINT_TO_POINTER (action));
 
+	if (task->group && task->group->menu)
+		gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
+					   GTK_SIGNAL_FUNC (gtk_menu_shell_deactivate),
+					   GTK_OBJECT (task->group->menu));
+
 	gtk_widget_show_all (menuitem);
 	gtk_menu_append (GTK_MENU (menu), menuitem);
 
 }
 
 /* Called when "Send to desktop" is used */
-void
+static void
 cb_to_desktop (GtkWidget *widget, gpointer data)
 {
-	gwmh_task_set_desktop (current_task->gwmh_task, 
+	TasklistTask *task = gtk_object_get_data (GTK_OBJECT (widget), "task");
+	
+	gwmh_task_set_desktop (task->gwmh_task, 
 			       GPOINTER_TO_INT (data));
-	gwmh_task_set_desktop (current_task->gwmh_task, 
+	gwmh_task_set_desktop (task->gwmh_task, 
 			       GPOINTER_TO_INT (data));
-	layout_tasklist (TRUE);
+	tasklist_layout_tasklist (task->tasklist);
+}
+
+static void
+destroy_menu (GtkWidget *w, gpointer null)
+{
+	d(g_print ("Destroying menu\n"));
+	gtk_widget_unref (w);
 }
 
 /* Create a popup menu */
-GtkWidget 
-*get_popup_menu (TasklistTask *task)
+static GtkWidget *
+get_popup_menu (TasklistTask *task)
 {
 	GtkWidget *menu, *menuitem; /*, *desktop, *label, *gtkpixmap;*/
 	/*GdkPixmap *pixmap;*/
@@ -182,21 +233,27 @@
 	/*int i, curworkspace;*/
 
 	menu = gtk_menu_new ();
+	gtk_signal_connect (GTK_OBJECT (menu), "deactivate",
+			    GTK_SIGNAL_FUNC (destroy_menu),
+			    NULL);
+
 	gtk_widget_show (menu);
+
+	gtk_object_set_data (GTK_OBJECT (menu), "task", task);
 
-	add_menu_item (GWMH_TASK_ICONIFIED (task->gwmh_task)
+	add_menu_item (task, GWMH_TASK_ICONIFIED (task->gwmh_task)
 		       ? _("Restore") : _("Iconify"), 
 		       menu, MENU_ACTION_SHOW_HIDE,
 		       GWMH_TASK_ICONIFIED (task->gwmh_task)
 		       ? tasklist_restore_xpm : tasklist_iconify_xpm);
 
-	add_menu_item (GWMH_TASK_SHADED (task->gwmh_task)
+	add_menu_item (task, GWMH_TASK_SHADED (task->gwmh_task)
 		       ? _("Unshade") : _("Shade"), 
 		       menu, MENU_ACTION_SHADE_UNSHADE,
 		       GWMH_TASK_SHADED (task->gwmh_task)
 		       ? tasklist_unshade_xpm: tasklist_shade_xpm);
 
-	add_menu_item (GWMH_TASK_STICKY (task->gwmh_task)
+	add_menu_item (task, GWMH_TASK_STICKY (task->gwmh_task)
 		       ? _("Unstick") : _("Stick"), 
 		       menu, MENU_ACTION_STICK_UNSTICK,
 		       GWMH_TASK_STICKY (task->gwmh_task)
@@ -206,7 +263,7 @@
 	label = gtk_label_new (_("To desktop"));
 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 	gtk_container_add (GTK_CONTAINER (menuitem), label);
-	pixmap = gdk_pixmap_create_from_xpm_d (area->window, &mask, NULL,
+	pixmap = gdk_pixmap_create_from_xpm_d (tasklist->area->window, &mask, NULL,
 					       tasklist_send_to_desktop_xpm);
 	gtkpixmap = gtk_pixmap_new (pixmap, mask);
 	gtk_pixmap_menu_item_set_pixmap (GTK_PIXMAP_MENU_ITEM (menuitem), gtkpixmap);
@@ -229,6 +286,7 @@
 			menuitem = gtk_menu_item_new_with_label (wsname);
 			gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
 					    GTK_SIGNAL_FUNC (cb_to_desktop), i);
+			gtk_object_set_user_data (GTK_OBJECT (menuitem), tasklist);
 			if (i == curworkspace)
 				gtk_widget_set_sensitive (menuitem, FALSE);
 			gtk_widget_show (menuitem);
@@ -242,14 +300,295 @@
 	gtk_widget_show (menuitem);
 	gtk_menu_append (GTK_MENU (menu), menuitem);
 #endif
-	add_menu_item (_("Close window"), menu, MENU_ACTION_CLOSE,
+	add_menu_item (task, _("Close window"), menu, MENU_ACTION_CLOSE,
 		       tasklist_close_xpm);
 
 	menuitem = gtk_menu_item_new ();
 	gtk_widget_show (menuitem);
 	gtk_menu_append (GTK_MENU (menu), menuitem);
+	
+	add_menu_item (task, _("Kill app"), menu, MENU_ACTION_KILL, tasklist_kill_xpm);
+	
+	return menu;
+}
+
+/* Create a popup menu */
+static GtkWidget *
+get_group_popup_menu (TasklistTask *task)
+{
+	GtkWidget *menu, *menuitem; /*, *desktop, *label, *gtkpixmap;*/
+	/*GdkPixmap *pixmap;*/
+	/*GdkBitmap *mask;*/
+	/*GwmhDesk *desk_info;*/
+
+	/*gchar *wsname;*/
+	/*int i, curworkspace;*/
+
+	menu = gtk_menu_new ();
+	gtk_signal_connect (GTK_OBJECT (menu), "deactivate",
+			    GTK_SIGNAL_FUNC (destroy_menu),
+			    NULL);
+	gtk_widget_show (menu);
+
+	gtk_object_set_data (GTK_OBJECT (menu), "task", task);
+
+	/* if (iconified window in group) */
+	add_menu_item (task, _("Restore All"), menu,
+		       MENU_ACTION_SHOW, tasklist_restore_xpm);
+
+	add_menu_item (task, _("Iconify All"), menu,
+		       MENU_ACTION_HIDE, tasklist_iconify_xpm);
+
+	add_menu_item (task, _("Unshade All"), menu,
+		       MENU_ACTION_UNSHADE, tasklist_unshade_xpm);
+
+	add_menu_item (task, _("Shade All"), menu,
+		       MENU_ACTION_SHADE, tasklist_shade_xpm);
+
+	add_menu_item (task, _("Unstick All"), menu,
+		       MENU_ACTION_UNSTICK, tasklist_unstick_xpm);
+
+	add_menu_item (task, _("Stick All"), menu,
+		       MENU_ACTION_STICK, tasklist_stick_xpm);
+
+#if 0
+	menuitem = gtk_pixmap_menu_item_new ();
+	label = gtk_label_new (_("To desktop"));
+	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+	gtk_container_add (GTK_CONTAINER (menuitem), label);
+	pixmap = gdk_pixmap_create_from_xpm_d (tasklist->area->window, &mask, NULL,
+					       tasklist_send_to_desktop_xpm);
+	gtkpixmap = gtk_pixmap_new (pixmap, mask);
+	gtk_pixmap_menu_item_set_pixmap (GTK_PIXMAP_MENU_ITEM (menuitem), gtkpixmap);
+	gtk_widget_show_all (menuitem);
+	gtk_menu_append (GTK_MENU (menu), menuitem);
+
+	if (!GWMH_TASK_STICKY (task->gwmh_task)) {
+		desktop = gtk_menu_new ();
+		gtk_widget_show (desktop);
+		gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), desktop);
+		
+		desk_info = gwmh_desk_get_config ();
+		curworkspace = desk_info->current_desktop;
+
+		for (i=0; i<desk_info->n_desktops;i++) {
+			if (desk_info->desktop_names[i])
+				wsname = g_strdup_printf ("%s", desk_info->desktop_names[i]);
+			else
+				wsname = g_strdup_printf ("%d", i);
+			menuitem = gtk_menu_item_new_with_label (wsname);
+			gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+					    GTK_SIGNAL_FUNC (cb_to_desktop), i);
+			gtk_object_set_user_data (GTK_OBJECT (menuitem), tasklist);
+			if (i == curworkspace)
+				gtk_widget_set_sensitive (menuitem, FALSE);
+			gtk_widget_show (menuitem);
+			gtk_menu_append (GTK_MENU (desktop), menuitem);
+			g_free (wsname);
+		}
+	} else 
+		gtk_widget_set_sensitive (menuitem, FALSE);
+
+	menuitem = gtk_menu_item_new ();
+	gtk_widget_show (menuitem);
+	gtk_menu_append (GTK_MENU (menu), menuitem);
+#endif
+	add_menu_item (task, _("Close All"), menu,
+		       MENU_ACTION_CLOSE, tasklist_close_xpm);
+		       
+
+	menuitem = gtk_menu_item_new ();
+	gtk_widget_show (menuitem);
+	gtk_menu_append (GTK_MENU (menu), menuitem);
 	
-	add_menu_item (_("Kill app"), menu, MENU_ACTION_KILL, tasklist_kill_xpm);
+	add_menu_item (task, _("Kill All"), menu, MENU_ACTION_KILL, tasklist_kill_xpm);
 	
 	return menu;
+}
+
+static void
+redraw_task (GtkWidget *w, TasklistTask *task)
+{
+	tasklist_draw_task (task, NULL);
+}
+
+/* Open a popup menu with window operations */
+void
+tasklist_menu_popup (TasklistTask *task, guint button, guint32 activate_time)
+{
+	tasklist_clean_menu (task);
+
+	task->menu = task->task_group 
+		? get_group_popup_menu (task)
+		: get_popup_menu (task);
+
+	gtk_signal_connect(GTK_OBJECT(task->menu), "destroy",
+			   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+			   &task->menu);
+
+	tasklist_draw_task (task, NULL);
+	gtk_signal_connect (GTK_OBJECT (task->menu), "destroy",
+			    GTK_SIGNAL_FUNC (redraw_task), task);
+
+	gtk_menu_popup (GTK_MENU (task->menu), NULL, NULL,
+			cb_menu_position, task,
+			button, activate_time);
+}
+
+/*most of this function stolen from the real gtk_menu_popup*/
+static void
+restore_grabs(GtkWidget *w, gpointer data)
+{
+	GtkWidget *menu_item = data;
+	GtkMenu *menu = GTK_MENU(menu_item->parent); 
+	GtkWidget *xgrab_shell;
+	GtkWidget *parent;
+
+	d(g_print ("restore_grabs\n"));
+	/* Find the last viewable ancestor, and make an X grab on it
+	 */
+	parent = GTK_WIDGET (menu);
+	xgrab_shell = NULL;
+	while (parent) {
+		gboolean viewable = TRUE;
+		GtkWidget *tmp = parent;
+
+		while (tmp) {
+			if (!GTK_WIDGET_MAPPED (tmp)) {
+				viewable = FALSE;
+				break;
+			}
+			tmp = tmp->parent;
+		}
+
+		if (viewable)
+			xgrab_shell = parent;
+
+		parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
+	}
+
+	/*only grab if this HAD a grab before*/
+	if (xgrab_shell && (GTK_MENU_SHELL (xgrab_shell)->have_xgrab)) {
+		GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);
+
+		GTK_MENU_SHELL (xgrab_shell)->have_xgrab = 
+			(gdk_pointer_grab (xgrab_shell->window, TRUE,
+					   GDK_BUTTON_PRESS_MASK |
+					   GDK_BUTTON_RELEASE_MASK |
+					   GDK_ENTER_NOTIFY_MASK |
+					   GDK_LEAVE_NOTIFY_MASK,
+					   NULL, cursor, 0) == 0);
+		gdk_cursor_destroy (cursor);
+	}
+	
+	gtk_grab_add (GTK_WIDGET (menu));
+}
+
+static gboolean
+cb_show_popup (GtkWidget *w, GdkEventButton *event, TasklistTask *task)
+{
+	if (event->type != GDK_BUTTON_PRESS) return FALSE;
+
+	if (event->button == 1) {
+		d(g_print ("click!\n"));
+		gwmh_desk_set_current_area (task->gwmh_task->desktop,
+					    task->gwmh_task->harea,
+					    task->gwmh_task->varea);
+		gwmh_task_show (task->gwmh_task);
+		gwmh_task_raise (task->gwmh_task);
+		gwmh_task_focus (task->gwmh_task);
+		return TRUE;
+	}
+	
+	if (event->button == 3) {
+		/*gtk_signal_emit_stop_by_name (GTK_OBJECT (w), "button_press_event");*/
+		tasklist_clean_menu (task);
+
+		task->menu = get_popup_menu (task);
+
+		gtk_signal_connect (GTK_OBJECT(task->menu), "destroy",
+				    GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+				    &task->menu);
+
+		gtk_signal_connect(GTK_OBJECT(task->menu),"deactivate",
+				   GTK_SIGNAL_FUNC(restore_grabs), w);
+
+		gtk_menu_popup (GTK_MENU (task->menu), NULL, NULL,
+				NULL, NULL, event->button, event->time);
+
+		return FALSE;
+	}
+	return FALSE;
+}
+
+static void
+create_task_item (TasklistTask *task, TasklistTask *group)
+{
+	GtkWidget *pixmap, *label;
+	GdkPixmap *pix;
+	GdkBitmap *bit;
+	gchar *s;
+
+	gdk_pixbuf_render_pixmap_and_mask (
+		task->gwmh_task->iconified
+		? task->icon->minimized
+		: task->icon->normal, &pix, &bit, 128);
+
+	pixmap = gtk_pixmap_new (pix, bit);
+
+	/* are we leaking the pixmap and bitmap? */
+	
+	task->menuitem = gtk_pixmap_menu_item_new ();
+	gtk_pixmap_menu_item_set_pixmap (GTK_PIXMAP_MENU_ITEM (task->menuitem), pixmap);
+
+	s = tasklist_task_get_label (task, group->width, FALSE);
+	label = gtk_label_new (s);
+	g_free (s);
+	gtk_container_add (GTK_CONTAINER (task->menuitem), label);
+
+	gtk_menu_append (GTK_MENU (group->menu), task->menuitem);
+
+	gtk_object_set_data (GTK_OBJECT (task->menuitem), "task", task);
+
+	gtk_signal_connect (GTK_OBJECT (task->menuitem), "button_press_event",
+			    GTK_SIGNAL_FUNC (cb_show_popup), task);
+	/* this is broken */
+#if 0
+	gtk_signal_connect (GTK_OBJECT (task->menuitem), "destroy",
+			    GTK_SIGNAL_FUNC (gtk_widget_destroyed), &task->menuitem);
+#endif
+}
+
+/* Open a popup menu with windows in a group */
+void
+tasklist_group_popup (TasklistTask *task, guint button, guint32 activate_time)
+{
+	TasklistTask *subtask;
+	GSList *item;
+
+	tasklist_clean_menu (task);
+
+	task->menu = gtk_menu_new ();
+	gtk_signal_connect (GTK_OBJECT (task->menu), "deactivate",
+			    GTK_SIGNAL_FUNC (destroy_menu),
+			    NULL);
+
+	g_slist_foreach (task->vtasks, (GFunc)create_task_item, task);
+
+	gtk_widget_show_all (task->menu);
+	gtk_signal_connect (GTK_OBJECT (task->menu), "destroy",
+			    GTK_SIGNAL_FUNC (gtk_widget_destroyed),
+			    &task->menu);
+
+	tasklist_draw_task (task, NULL);
+	gtk_signal_connect (GTK_OBJECT (task->menu), "destroy",
+			    GTK_SIGNAL_FUNC (redraw_task), task);
+
+	gtk_object_set_data (GTK_OBJECT (task->menu), "task", task);
+
+	tasklist_draw_task (task, NULL);
+
+	gtk_menu_popup (GTK_MENU (task->menu), NULL, NULL,
+			cb_menu_position, task,
+			button, activate_time);
 }
Index: tasklist_properties.c
===================================================================
RCS file: /cvs/gnome/gnome-core/applets/tasklist/tasklist_properties.c,v
retrieving revision 1.31
diff -u -r1.31 tasklist_properties.c
--- tasklist_properties.c	2000/12/20 23:31:30	1.31
+++ tasklist_properties.c	2001/01/23 04:00:11
@@ -1,37 +1,30 @@
 #include <config.h>
 #include "tasklist_applet.h"
 
-/* The tasklist configuration */
-extern TasklistConfig Config;
-
-/* The tasklist properties configuration */
-TasklistConfig PropsConfig;
-
-/* The Property box */
-GtkWidget *prop = NULL;
-
 /* Callback for apply */
 static void
 cb_apply (GtkWidget *widget, gint page, gpointer data)
 {
-
+	Tasklist *tasklist = data;
+	
 	/* Copy the Property struct back to the Config struct */
-	memcpy (&Config, &PropsConfig, sizeof (TasklistConfig));
+	memcpy (&tasklist->config, &tasklist->PropsConfig, sizeof (TasklistConfig));
 
-        /* Resort and redraw everything */
-        resort_tasklist ();
-	change_size (TRUE, -1);
+	/* Redraw everything */
+	tasklist_redo_vtasks (tasklist);
+	tasklist_change_size (tasklist, TRUE, -1);
 }
 
 /* Callback for radio buttons */
 static void
 cb_radio_button (GtkWidget *widget, gint *data)
 {
-
+	Tasklist *tasklist = gtk_object_get_user_data (GTK_OBJECT (widget));
+	
 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
 		*data = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (widget),
 							      "number"));
-		gnome_property_box_changed (GNOME_PROPERTY_BOX (prop));
+		gnome_property_box_changed (GNOME_PROPERTY_BOX (tasklist->prop));
 	}
 }
 
@@ -39,7 +32,9 @@
 static void
 cb_spin_button (GtkWidget *widget, gint *data)
 {
-	gnome_property_box_changed (GNOME_PROPERTY_BOX (prop));
+	Tasklist *tasklist = gtk_object_get_user_data (GTK_OBJECT (widget));
+	
+	gnome_property_box_changed (GNOME_PROPERTY_BOX (tasklist->prop));
 	
 	*data = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
 }
@@ -48,7 +43,9 @@
 static void
 cb_check_button (GtkWidget *widget, gboolean *data)
 {
-	gnome_property_box_changed (GNOME_PROPERTY_BOX (prop));
+	Tasklist *tasklist = gtk_object_get_user_data (GTK_OBJECT (widget));
+	
+	gnome_property_box_changed (GNOME_PROPERTY_BOX (tasklist->prop));
 
 	*data = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
 }
@@ -64,7 +61,8 @@
  
 /* Create a spin button */
 static GtkWidget *
-create_spin_button (gchar *name,
+create_spin_button (Tasklist *tasklist,
+		    gchar *name,
 		    gint *init_value,
 		    gfloat min_value,
 		    gfloat max_value,
@@ -85,6 +83,7 @@
 	hbox = gtk_hbox_new (TRUE, GNOME_PAD_SMALL);
 
 	spin = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1, 0);
+	gtk_object_set_user_data (GTK_OBJECT (spin), tasklist);
 	gtk_signal_connect (GTK_OBJECT (spin), "changed",
 			    GTK_SIGNAL_FUNC (cb_spin_button), init_value);
 						
@@ -101,7 +100,7 @@
 
 /* Create a radio button */
 static GtkWidget *
-create_radio_button (gchar *name, GSList **group, 
+create_radio_button (Tasklist *tasklist, gchar *name, GSList **group, 
 		     gint number, gint *change_value)
 {
 	GtkWidget *radiobutton;
@@ -116,19 +115,20 @@
 			    GTK_SIGNAL_FUNC (cb_radio_button), change_value);
 	gtk_object_set_data (GTK_OBJECT (radiobutton), "number",
 			     GINT_TO_POINTER (number));
+	gtk_object_set_user_data (GTK_OBJECT (radiobutton), tasklist);
 
-
 	return radiobutton;
 }
 
 /* Create a check button */
 static GtkWidget *
-create_check_button (gchar *name, gboolean *change_value)
+create_check_button (Tasklist *tasklist, gchar *name, gboolean *change_value)
 {
 	GtkWidget *checkbutton;
 
 	checkbutton = gtk_check_button_new_with_label (name);
 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), *change_value);
+	gtk_object_set_user_data (GTK_OBJECT (checkbutton), tasklist);
 	gtk_signal_connect (GTK_OBJECT (checkbutton), "toggled",
 			    GTK_SIGNAL_FUNC (cb_check_button), change_value);
 	return checkbutton;
@@ -136,7 +136,7 @@
 
 /* Create the size page */
 static void
-create_size_page (void)
+create_size_page (Tasklist *tasklist)
 {
 	GtkWidget *hbox,/* *table,*/ *frame, *vbox, *topbox;
 	GSList *vertgroup = NULL, *horzgroup = NULL;
@@ -145,8 +145,9 @@
 	topbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
 	gtk_container_border_width (GTK_CONTAINER (topbox), GNOME_PAD_SMALL);
 	
-	autobutton = create_check_button (_("Follow panel size"),
-					  &PropsConfig.follow_panel_size);
+	autobutton = create_check_button (
+		tasklist, _("Follow panel size"),
+		&tasklist->PropsConfig.follow_panel_size);
 	gtk_box_pack_start (GTK_BOX (topbox),
 			    autobutton,
 			    FALSE, TRUE, 0);
@@ -161,47 +162,54 @@
 	vbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
 	gtk_container_border_width (GTK_CONTAINER (vbox), GNOME_PAD_SMALL);
 
-	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_spin_button (_("Tasklist width:"),
-						&PropsConfig.horz_width,
-						48,
-						8192,
-						10),
-			    FALSE, TRUE, 0);
-	w = create_spin_button (_("Rows of tasks:"),
-				&PropsConfig.horz_rows,
-				1,
-				8,
-				1);
+	gtk_box_pack_start (
+		GTK_BOX (vbox),
+		create_spin_button (
+			tasklist, _("Tasklist width:"),
+			&tasklist->PropsConfig.horz_width,
+			48,
+			8192,
+			10),
+		FALSE, TRUE, 0);
+	w = create_spin_button (
+		tasklist, _("Rows of tasks:"),
+		&tasklist->PropsConfig.horz_rows,
+		1,
+		8,
+		1);
 	gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, TRUE, 0);
 	gtk_signal_connect (GTK_OBJECT (autobutton), "toggled",
-			   GTK_SIGNAL_FUNC (cb_check_button_disable),
-			   w);
+			    GTK_SIGNAL_FUNC (cb_check_button_disable),
+			    w);
 	cb_check_button_disable (autobutton, w);
 
-	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_spin_button (_("Default task size:"),
-						&PropsConfig.horz_taskwidth,
-						48,
-						350,
-						10),
-			    FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (vbox),
+		create_spin_button (
+			tasklist, _("Default task size:"),
+			&tasklist->PropsConfig.horz_taskwidth,
+			48,
+			350,
+			10),
+		FALSE, TRUE, 0);
 
 
 	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_radio_button (_("Tasklist width is fixed"),
-						 &horzgroup, TRUE, &PropsConfig.horz_fixed),
+			    create_radio_button (
+				    tasklist, _("Tasklist width is fixed"),
+				    &horzgroup, TRUE, &tasklist->PropsConfig.horz_fixed),
 			    FALSE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_radio_button (_("Tasklist width is dynamic"), 
-						 &horzgroup, FALSE, &PropsConfig.horz_fixed),
+			    create_radio_button (
+				    tasklist, _("Tasklist width is dynamic"), 
+				    &horzgroup, FALSE, &tasklist->PropsConfig.horz_fixed),
 			    FALSE, TRUE, 0);
 
 	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_check_button (_("Only use empty space"),
-						 &PropsConfig.horz_never_push),
+			    create_check_button (tasklist, _("Only use empty space"),
+						 &tasklist->PropsConfig.horz_never_push),
 			    FALSE, TRUE, 0);
-	
+
 	gtk_container_add (GTK_CONTAINER (frame), vbox);
 	
 	frame = gtk_frame_new (_("Vertical"));
@@ -210,16 +218,17 @@
 	vbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
 	gtk_container_border_width (GTK_CONTAINER (vbox), GNOME_PAD_SMALL);
 
-	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_spin_button (_("Tasklist height:"),
-						&PropsConfig.vert_height,
-						48,
-						1024*8,
-						10),
-			    FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (vbox),
+		create_spin_button (tasklist, _("Tasklist height:"),
+				    &tasklist->PropsConfig.vert_height,
+				    48,
+				    1024*8,
+				    10),
+		FALSE, TRUE, 0);
 
-	w = create_spin_button (_("Tasklist width:"),
-				&PropsConfig.vert_width,
+	w = create_spin_button (tasklist, _("Tasklist width:"),
+				&tasklist->PropsConfig.vert_width,
 				48,
 				512,
 				10);
@@ -229,34 +238,39 @@
 			   w);
 	cb_check_button_disable (autobutton, w);
 
-	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_radio_button (_("Tasklist height is fixed"),
-						 &vertgroup, TRUE, &PropsConfig.vert_fixed),
-			    FALSE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_radio_button (_("Tasklist height is dynamic"), 
-						 &vertgroup, FALSE, &PropsConfig.vert_fixed),
-			    FALSE, TRUE, 0);
+			    create_radio_button (
+				    tasklist,_("Tasklist height is fixed"),
+				    &vertgroup, TRUE, &tasklist->PropsConfig.vert_fixed),
+			    FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (vbox),
+		create_radio_button (
+			tasklist, _("Tasklist height is dynamic"), 
+			&vertgroup, FALSE, &tasklist->PropsConfig.vert_fixed),
+		FALSE, TRUE, 0);
+
 
 	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_check_button (_("Only use empty space"),
-						 &PropsConfig.vert_never_push),
+			    create_check_button (tasklist, _("Only use empty space"),
+						 &tasklist->PropsConfig.vert_never_push),
 			    FALSE, TRUE, 0);
-	
 
 	gtk_box_pack_start (GTK_BOX (vbox),
-			    create_check_button (_("Tasklist width is that of longest title"),
-						 &PropsConfig.vert_width_full),
+			    create_check_button (tasklist, 
+						 _("Tasklist width is that of longest title"),
+						 &tasklist->PropsConfig.vert_width_full),
 			    FALSE, TRUE, 0);
-
+	
 	gtk_container_add (GTK_CONTAINER (frame), vbox);
 
-	gnome_property_box_append_page (GNOME_PROPERTY_BOX (prop), topbox,
-					gtk_label_new (_("Size")));
+	gnome_property_box_append_page (
+		GNOME_PROPERTY_BOX (tasklist->prop), topbox,
+		gtk_label_new (_("Size")));
 }
 
 static void
-create_display_page (void)
+create_display_page (Tasklist *tasklist)
 {
 	GtkWidget *vbox, *frame;
 	GtkWidget *miscbox, *taskbox;
@@ -270,44 +284,82 @@
 	gtk_box_pack_start_defaults (GTK_BOX (vbox), frame);
 
 	taskbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
+	gtk_container_border_width (GTK_CONTAINER (taskbox), GNOME_PAD_SMALL);
 	gtk_container_add (GTK_CONTAINER (frame), taskbox);
 	
-	gtk_box_pack_start (GTK_BOX (taskbox),
-			    create_check_button (_("Show normal applications"), &PropsConfig.show_normal),
-			    FALSE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (taskbox),
-			    create_check_button (_("Show iconified (minimized) applications"), &PropsConfig.show_minimized),
-			    FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (taskbox),
+		create_check_button (
+			tasklist, _("Show normal applications"),
+			&tasklist->PropsConfig.show_normal),
+		FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (taskbox),
+		create_check_button (
+			tasklist, _("Show iconified (minimized) applications"),
+			&tasklist->PropsConfig.show_minimized),
+		FALSE, TRUE, 0);
 			    
-	gtk_box_pack_start (GTK_BOX (taskbox),
-			    create_check_button (_("Show normal applications on all desktops"), &PropsConfig.all_desks_normal),
-			    FALSE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (taskbox),
-			    create_check_button (_("Show iconified (minimized) applications on all desktops"), &PropsConfig.all_desks_minimized),
-			    FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (taskbox),
+		create_check_button (
+			tasklist, _("Show normal applications on all desktops"),
+			&tasklist->PropsConfig.all_desks_normal),
+		FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (taskbox),
+		create_check_button (tasklist,
+				     _("Show iconified (minimized) applications on all desktops"),
+				     &tasklist->PropsConfig.all_desks_minimized),
+		FALSE, TRUE, 0);
 
 	frame = gtk_frame_new (_("Miscellaneous"));
 	gtk_container_border_width (GTK_CONTAINER (frame), GNOME_PAD_SMALL);
 	gtk_box_pack_start_defaults (GTK_BOX (vbox), frame);
 	
 	miscbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
+	gtk_container_border_width (GTK_CONTAINER (miscbox), GNOME_PAD_SMALL);
 	gtk_container_add (GTK_CONTAINER (frame), miscbox);
 
-	gtk_box_pack_start (GTK_BOX (miscbox),
-			    create_check_button (_("Show mini icons"), &PropsConfig.show_mini_icons),
-			    FALSE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (miscbox),
-			    create_check_button (_("Confirm before killing windows"), &PropsConfig.confirm_before_kill),
-			    FALSE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (miscbox),
-			    create_check_button (_("Move iconified tasks to current workspace when restoring"), &PropsConfig.move_to_current),
-			    FALSE, TRUE, 0);
-        gtk_box_pack_start (GTK_BOX (miscbox),
-                            create_check_button (_("Keep tasks sorted"),
-						 &PropsConfig.sort_tasklist),
-			    FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (miscbox),
+		create_check_button (
+			tasklist, _("Show mini icons"),
+			&tasklist->PropsConfig.show_mini_icons),
+		FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (miscbox),
+		create_check_button (
+			tasklist,
+			_("Confirm before killing windows"),
+			&tasklist->PropsConfig.confirm_before_kill),
+		FALSE, TRUE, 0);
+	gtk_box_pack_start (
+		GTK_BOX (miscbox),
+		create_check_button (
+			tasklist,
+			_("Move iconified tasks to current workspace when restoring"),
+			&tasklist->PropsConfig.move_to_current),
+		FALSE, TRUE, 0);
+
+	gtk_box_pack_start (
+		GTK_BOX (miscbox),
+		create_check_button (
+			tasklist,
+			_("Enable task grouping"),
+			&tasklist->PropsConfig.enable_grouping),
+		FALSE, TRUE, 0);
+
+	gtk_box_pack_start (
+		GTK_BOX (miscbox),
+		create_spin_button (
+			tasklist,
+			_("Number of tasks before grouping occurs"),
+			&tasklist->PropsConfig.grouping_min,
+			1, 10, 3),
+		FALSE, TRUE, 0);
 
-	gnome_property_box_append_page (GNOME_PROPERTY_BOX (prop), vbox,
+	gnome_property_box_append_page (GNOME_PROPERTY_BOX (tasklist->prop), vbox,
 					gtk_label_new (_("Display")));
 }
 
@@ -321,37 +373,33 @@
 
 /* Display property dialog */
 void
-display_properties (void)
+tasklist_display_properties (Tasklist *tasklist)
 {
-	if (prop != NULL)
+	if (tasklist->prop != NULL)
 	{
-		gdk_window_show (prop->window);
-		gdk_window_raise (prop->window);
+		gdk_window_show (tasklist->prop->window);
+		gdk_window_raise (tasklist->prop->window);
 		return;
 
 	}
-	/* Copy memory from the tasklist config 
-	   to the tasklist properties config. */
-	memcpy (&PropsConfig, &Config, sizeof (TasklistConfig));
-
-	prop = gnome_property_box_new ();
-	gtk_window_set_title (GTK_WINDOW (prop), _("Tasklist properties"));
-	gtk_signal_connect (GTK_OBJECT (prop), "apply",
-			    GTK_SIGNAL_FUNC (cb_apply), NULL);
-	gtk_signal_connect (GTK_OBJECT (prop), "destroy",
+	/*
+	 * Copy memory from the tasklist config 
+	 * to the tasklist properties config.
+	 */
+	memcpy (&tasklist->PropsConfig, &tasklist->config, sizeof (TasklistConfig));
+
+	tasklist->prop = gnome_property_box_new ();
+	gtk_window_set_title (GTK_WINDOW (tasklist->prop), _("Tasklist properties"));
+	gtk_window_set_wmclass (GTK_WINDOW (tasklist->prop), "tasklist", "Tasklist");
+	gtk_signal_connect (GTK_OBJECT (tasklist->prop), "apply",
+			    GTK_SIGNAL_FUNC (cb_apply), tasklist);
+	gtk_signal_connect (GTK_OBJECT (tasklist->prop), "destroy",
 			    GTK_SIGNAL_FUNC (gtk_widget_destroyed),
-			    &prop);
-	gtk_signal_connect (GTK_OBJECT (prop), "help",
+			    &tasklist->prop);
+	gtk_signal_connect (GTK_OBJECT (tasklist->prop), "help",
 			    GTK_SIGNAL_FUNC (phelp_cb), NULL);
-	create_display_page ();
-	create_size_page ();
+	create_display_page (tasklist);
+	create_size_page (tasklist);
 
-	gtk_widget_show_all (prop);
+	gtk_widget_show_all (tasklist->prop);
 }
-
-
-
-
-
-
-


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