changeset 18073:8bad8a91d128

merge of '4211412db7d18ca946c93c5a6ba8931ff8244de0' and 'f9fde664d495819679adeb390328646e1208ee22'
author Richard Nelson <wabz@pidgin.im>
date Fri, 08 Jun 2007 13:06:21 +0000
parents 3fc94e7c7056 (current diff) bb9cd8dfc61c (diff)
children 7e309149360f c885a9ccd301
files
diffstat 16 files changed, 813 insertions(+), 171 deletions(-) [+]
line wrap: on
line diff
--- a/doc/finch.1.in	Thu Jun 07 21:14:54 2007 +0000
+++ b/doc/finch.1.in	Fri Jun 08 13:06:21 2007 +0000
@@ -109,6 +109,15 @@
 .B Ctrl \+ o
 Bring up the menu (if there is one) for a window. Note that currently only the
 buddylist has a menu.
+.TP
+.B Alt \+ Shift \+ .
+Switch to the next workspace
+.TP
+.B Alt \+ Shift \+ ,
+Switch to the previous workspace
+.TP
+.B Alt \+ s
+Show the workspace list
 
 .SH FILES
 \fI~/.gntrc\fR: configuration file for gnt applications.
@@ -134,6 +143,33 @@
 .br
 
 .br
+# Workspaces are created simply by adding Workspace-X groups as follows:
+.br
+[Workspace-1]
+.br
+name = blist
+.br
+# window-names specifies that windows with these semi-colon separated names are placed
+into this workspace
+.br
+window-names = buddylist;debug-window
+.br
+
+.br
+[Workspace-2]
+.br
+name = IM
+.br
+window-names = conversation-window
+.br
+# window-titles specifies that windows with these semi-colon separated titles are placed
+into this workspace. These are matched as substrings. Window titles take precedence over
+names.
+.br
+window-titles = Preferences;Pounce
+.br
+
+.br
 [colors]
 .br
 # The RGB values range in [0, 1000]
--- a/finch/gntblist.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/gntblist.c	Fri Jun 08 13:06:21 2007 +0000
@@ -640,9 +640,18 @@
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
 	{
 		PurpleBuddy *buddy = (PurpleBuddy *)node;
-		PurpleConversation *conv =  purple_conversation_new(PURPLE_CONV_TYPE_IM,
-					purple_buddy_get_account(buddy),
-					purple_buddy_get_name(buddy));
+		PurpleConversation *conv;
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+					purple_buddy_get_name(buddy),
+					purple_buddy_get_account(buddy));
+		if (!conv) {
+			conv =  purple_conversation_new(PURPLE_CONV_TYPE_IM,
+						purple_buddy_get_account(buddy),
+						purple_buddy_get_name(buddy));
+		} else {
+			FinchConv *ggconv = conv->ui_data;
+			gnt_window_present(ggconv->window);
+		}
 		finch_conversation_set_active(conv);
 	}
 	else if (PURPLE_BLIST_NODE_IS_CHAT(node))
@@ -2308,6 +2317,8 @@
 				ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
 	g_signal_connect_data(G_OBJECT(ggblist->tree), "lost-focus", G_CALLBACK(remove_peripherals),
 				ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+	g_signal_connect_data(G_OBJECT(ggblist->window), "workspace-hidden", G_CALLBACK(remove_peripherals),
+				ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
 	g_signal_connect(G_OBJECT(ggblist->tree), "size_changed", G_CALLBACK(size_changed_cb), NULL);
 	g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL);
 	g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL);
--- a/finch/libgnt/Makefile.am	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/Makefile.am	Fri Jun 08 13:06:21 2007 +0000
@@ -1,6 +1,6 @@
 EXTRA_DIST=genmarshal
 
-SUBDIRS = . wms
+SUBDIRS = . 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = gnt.pc
 
@@ -32,6 +32,7 @@
 	gntutils.c \
 	gntwindow.c \
 	gntwm.c \
+	gntws.c \
 	gntmain.c
 
 libgnt_la_headers = \
@@ -58,6 +59,7 @@
 	gntutils.h \
 	gntwindow.h \
 	gntwm.h \
+	gntws.h \
 	gnt.h
 
 CLEANFILES = \
--- a/finch/libgnt/gnt.h	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gnt.h	Fri Jun 08 13:06:21 2007 +0000
@@ -43,6 +43,7 @@
  */
 gboolean gnt_ascii_only(void);
 
+void gnt_window_present(GntWidget *window);
 /**
  * 
  * @param widget
--- a/finch/libgnt/gntmain.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntmain.c	Fri Jun 08 13:06:21 2007 +0000
@@ -116,7 +116,7 @@
 	GntWidget *widget = NULL;
 	PANEL *p = NULL;
 
-	if (!wm->ordered || buffer[0] != 27)
+	if (!wm->cws->ordered || buffer[0] != 27)
 		return FALSE;
 	
 	buffer++;
@@ -172,7 +172,7 @@
 	
 	if (event == GNT_LEFT_MOUSE_DOWN && widget && widget != wm->_list.window &&
 			!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
-		if (widget != wm->ordered->data) {
+		if (widget != wm->cws->ordered->data) {
 			gnt_wm_raise_window(wm, widget);
 		}
 		if (y == widget->priv.y) {
@@ -183,7 +183,7 @@
 	} else if (event == GNT_MOUSE_UP) {
 		if (button == MOUSE_NONE && y == getmaxy(stdscr) - 1) {
 			/* Clicked on the taskbar */
-			int n = g_list_length(wm->list);
+			int n = g_list_length(wm->cws->list);
 			if (n) {
 				int width = getmaxx(stdscr) / n;
 				gnt_bindable_perform_action_named(GNT_BINDABLE(wm), "switch-window-n", x/width, NULL);
@@ -484,6 +484,10 @@
  * Stuff for 'window management' *
  *********************************/
 
+void gnt_window_present(GntWidget *window) {
+	gnt_wm_raise_window(wm, window);
+}
+
 void gnt_screen_occupy(GntWidget *widget)
 {
 	gnt_wm_new_window(wm, widget);
@@ -515,7 +519,7 @@
 
 	if (widget == wm->_list.window)
 		return TRUE;
-	if (wm->ordered && wm->ordered->data == widget) {
+	if (wm->cws->ordered && wm->cws->ordered->data == widget) {
 		if (GNT_IS_BOX(widget) &&
 				(GNT_BOX(widget)->active == w || widget == w))
 			return TRUE;
@@ -528,7 +532,7 @@
 	while (widget->parent)
 		widget = widget->parent;
 
-	if (wm->ordered && wm->ordered->data == widget)
+	if (wm->cws->ordered && wm->cws->ordered->data == widget)
 		return;
 
 	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_URGENT);
--- a/finch/libgnt/gntmenu.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntmenu.c	Fri Jun 08 13:06:21 2007 +0000
@@ -242,7 +242,10 @@
 static void
 gnt_menu_hide(GntWidget *widget)
 {
-	GntMenu *menu = GNT_MENU(widget);
+	GntMenu *sub, *menu = GNT_MENU(widget);
+
+	while ((sub = menu->submenu))
+		gnt_widget_hide(GNT_WIDGET(sub));
 	if (menu->parentmenu)
 		menu->parentmenu->submenu = NULL;
 }
--- a/finch/libgnt/gntstyle.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Fri Jun 08 13:06:21 2007 +0000
@@ -22,11 +22,15 @@
 
 #include "gntstyle.h"
 #include "gntcolors.h"
+#include "gntws.h"
 
 #include <glib.h>
 #include <ctype.h>
+#include <glib/gprintf.h>
 #include <string.h>
 
+#define MAX_WORKSPACES 99
+
 #if GLIB_CHECK_VERSION(2,6,0)
 static GKeyFile *gkfile;
 #endif
@@ -116,6 +120,45 @@
 	return (char *)gnt_key_translate(key);
 }
 
+void gnt_style_read_workspaces(GntWM *wm)
+{
+#if GLIB_CHECK_VERSION(2,6,0)
+	int i;
+	gchar *name;
+	gsize c;
+
+	for (i = 1; i < MAX_WORKSPACES; ++i) {
+		int j;
+		GntWS *ws;
+		gchar **titles;
+		char *group = calloc(12, 1);
+		g_sprintf(group, "Workspace-%d", i);
+		name = g_key_file_get_value(gkfile, group, "name", NULL);
+		if (!name)
+			return;
+
+		ws = g_object_new(GNT_TYPE_WS, NULL);
+		gnt_ws_set_name(ws, name);
+		gnt_wm_add_workspace(wm, ws);
+		g_free(name);
+
+		titles = g_key_file_get_string_list(gkfile, group, "window-names", &c, NULL);
+		if (titles) {
+			for (j = 0; j < c; ++j)
+				g_hash_table_replace(wm->name_places, g_strdup(titles[j]), ws);
+			g_strfreev(titles);
+		}
+
+		titles = g_key_file_get_string_list(gkfile, group, "window-titles", &c, NULL);
+		if (titles) {
+			for (j = 0; j < c; ++j)
+				g_hash_table_replace(wm->title_places, g_strdup(titles[j]), ws);
+			g_strfreev(titles);
+		}
+		g_free(group);
+	}
+#endif
+}
 void gnt_style_read_actions(GType type, GntBindableClass *klass)
 {
 #if GLIB_CHECK_VERSION(2,6,0)
--- a/finch/libgnt/gntstyle.h	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Fri Jun 08 13:06:21 2007 +0000
@@ -21,6 +21,7 @@
  */
 
 #include "gnt.h"
+#include "gntwm.h"
 
 typedef enum
 {
@@ -66,6 +67,8 @@
  */
 void gnt_style_read_actions(GType type, GntBindableClass *klass);
 
+void gnt_style_read_workspaces(GntWM *wm);
+
 /**
  * 
  */
--- a/finch/libgnt/gnttree.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gnttree.c	Fri Jun 08 13:06:21 2007 +0000
@@ -303,7 +303,7 @@
 
 		notfirst = TRUE;
 
-		if (len > width) {
+		if (len > width - 2) {
 			len = width - 1;
 			cut = TRUE;
 		}
--- a/finch/libgnt/gntwindow.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntwindow.c	Fri Jun 08 13:06:21 2007 +0000
@@ -27,9 +27,13 @@
 
 enum
 {
-	SIGS = 1,
+	SIG_WORKSPACE_HIDE,
+	SIG_WORKSPACE_SHOW,
+	SIGS,
 };
 
+static guint signals[SIGS] = { 0 };
+
 static GntBoxClass *parent_class = NULL;
 
 static void (*org_destroy)(GntWidget *widget);
@@ -64,6 +68,24 @@
 	org_destroy = wid_class->destroy;
 	wid_class->destroy = gnt_window_destroy;
 
+	signals[SIG_WORKSPACE_HIDE] =
+		g_signal_new("workspace-hidden",
+					 G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST,
+					 0,
+					 NULL, NULL,
+					 g_cclosure_marshal_VOID__VOID,
+					 G_TYPE_NONE, 0);
+
+	signals[SIG_WORKSPACE_SHOW] =
+		g_signal_new("workspace-shown",
+					 G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST,
+					 0,
+					 NULL, NULL,
+					 g_cclosure_marshal_VOID__VOID,
+					 G_TYPE_NONE, 0);
+
 	gnt_bindable_class_register_action(bindable, "show-menu", show_menu,
 				GNT_KEY_CTRL_O, NULL);
 	gnt_bindable_register_binding(bindable, "show-menu", GNT_KEY_F10, NULL);
@@ -131,6 +153,20 @@
 	return wid;
 }
 
+void
+gnt_window_workspace_hiding(GntWindow *window)
+{
+	if (window->menu)
+		gnt_widget_hide(GNT_WIDGET(window->menu));
+	g_signal_emit(window, signals[SIG_WORKSPACE_HIDE], 0);
+}
+
+void
+gnt_window_workspace_showing(GntWindow *window)
+{
+	g_signal_emit(window, signals[SIG_WORKSPACE_SHOW], 0);
+}
+
 void gnt_window_set_menu(GntWindow *window, GntMenu *menu)
 {
 	/* If a menu already existed, then destroy that first. */
--- a/finch/libgnt/gntwindow.h	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntwindow.h	Fri Jun 08 13:06:21 2007 +0000
@@ -95,6 +95,9 @@
  */
 void gnt_window_set_menu(GntWindow *window, GntMenu *menu);
 
+void gnt_window_workspace_hiding(GntWindow *);
+void gnt_window_workspace_showing(GntWindow *);
+
 G_END_DECLS
 
 #endif /* GNT_WINDOW_H */
--- a/finch/libgnt/gntwm.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntwm.c	Fri Jun 08 13:06:21 2007 +0000
@@ -28,6 +28,8 @@
 #include "config.h"
 
 #include <ctype.h>
+#include <glib/gprintf.h>
+#include <gmodule.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
@@ -69,6 +71,8 @@
 static void gnt_wm_give_focus(GntWM *wm, GntWidget *widget);
 static void update_window_in_list(GntWM *wm, GntWidget *wid);
 static void shift_window(GntWM *wm, GntWidget *widget, int dir);
+static gboolean workspace_next(GntBindable *wm, GList *n);
+static gboolean workspace_prev(GntBindable *wm, GList *n);
 
 #ifndef NO_WIDECHAR
 static int widestringwidth(wchar_t *wide);
@@ -78,6 +82,7 @@
 static int write_timeout;
 static time_t last_active_time;
 static gboolean idle_update;
+static GList *act = NULL; /* list of WS with unseen activitiy */
 
 static GList *
 g_list_bring_to_front(GList *list, gpointer data)
@@ -96,61 +101,8 @@
 	g_free(node);
 }
 
-static void
-draw_taskbar(GntWM *wm, gboolean reposition)
-{
-	static WINDOW *taskbar = NULL;
-	GList *iter;
-	int n, width = 0;
-	int i;
-
-	if (taskbar == NULL) {
-		taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0);
-	} else if (reposition) {
-		int Y_MAX = getmaxy(stdscr) - 1;
-		mvwin(taskbar, Y_MAX, 0);
-	}
-
-	wbkgdset(taskbar, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
-	werase(taskbar);
-
-	n = g_list_length(wm->list);
-	if (n)
-		width = getmaxx(stdscr) / n;
-
-	for (i = 0, iter = wm->list; iter; iter = iter->next, i++)
-	{
-		GntWidget *w = iter->data;
-		int color;
-		const char *title;
-
-		if (w == wm->ordered->data) {
-			/* This is the current window in focus */
-			color = GNT_COLOR_TITLE;
-		} else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_URGENT)) {
-			/* This is a window with the URGENT hint set */
-			color = GNT_COLOR_URGENT;
-		} else {
-			color = GNT_COLOR_NORMAL;
-		}
-		wbkgdset(taskbar, '\0' | COLOR_PAIR(color));
-		if (iter->next)
-			mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width);
-		else
-			mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), getmaxx(stdscr) - width * i);
-		title = GNT_BOX(w)->title;
-		mvwprintw(taskbar, 0, width * i, "%s", title ? title : "<gnt>");
-		if (i)
-			mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | COLOR_PAIR(GNT_COLOR_NORMAL));
-
-		update_window_in_list(wm, w);
-	}
-
-	wrefresh(taskbar);
-}
-
-static void
-copy_win(GntWidget *widget, GntNode *node)
+void
+gnt_wm_copy_win(GntWidget *widget, GntNode *node)
 {
 	WINDOW *src, *dst;
 	int shadow;
@@ -221,6 +173,33 @@
 #endif
 }
 
+static void
+update_act_msg()
+{
+	GntWidget *label;
+	GList *iter;
+	static GntWidget *message = NULL;
+	GString *text = g_string_new("act: ");
+	if (message)
+		gnt_widget_destroy(message);
+	if (g_list_length(act) == 0)
+		return;
+	for (iter = act; iter; iter = iter->next) {
+		GntWS *ws = iter->data;
+		g_string_append_printf(text, "%s, ", gnt_ws_get_name(ws));
+	}
+	g_string_erase(text, text->len - 2, 2);
+	message = gnt_vbox_new(FALSE);
+	label = gnt_label_new_with_format(text->str, GNT_TEXT_FLAG_BOLD | GNT_TEXT_FLAG_HIGHLIGHT);
+	GNT_WIDGET_UNSET_FLAGS(GNT_BOX(message), GNT_WIDGET_CAN_TAKE_FOCUS);
+	GNT_WIDGET_SET_FLAGS(GNT_BOX(message), GNT_WIDGET_TRANSIENT);
+	gnt_box_add_widget(GNT_BOX(message), label);
+	gnt_widget_set_name(message, "wm-message");
+	gnt_widget_set_position(message, 0, 0);
+	gnt_widget_draw(message);
+	g_string_free(text, TRUE);
+}
+
 static gboolean
 update_screen(GntWM *wm)
 {
@@ -351,9 +330,19 @@
 gnt_wm_init(GTypeInstance *instance, gpointer class)
 {
 	GntWM *wm = GNT_WM(instance);
-	wm->list = NULL;
-	wm->ordered = NULL;
+	wm->workspaces = NULL;
+	wm->name_places = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	wm->title_places = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	gnt_style_read_workspaces(wm);
+	if (wm->workspaces == NULL) {
+		wm->cws = g_object_new(GNT_TYPE_WS, NULL);
+		gnt_ws_set_name(wm->cws, "default");
+		gnt_wm_add_workspace(wm, wm->cws);
+	} else {
+		wm->cws = wm->workspaces->data;
+	}
 	wm->event_stack = FALSE;
+	wm->tagged = NULL;
 	wm->windows = NULL;
 	wm->actions = NULL;
 	wm->nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node);
@@ -362,6 +351,7 @@
 		read_window_positions(wm);
 	g_timeout_add(IDLE_CHECK_INTERVAL * 1000, check_idle, NULL);
 	time(&last_active_time);
+	gnt_wm_switch_workspace(wm, 0);
 }
 
 static void
@@ -373,23 +363,23 @@
 	if (wm->_list.window || wm->menu)
 		return;
 
-	if (!wm->ordered || !wm->ordered->next)
+	if (!wm->cws->ordered || !wm->cws->ordered->next)
 		return;
 
-	w = wm->ordered->data;
-	pos = g_list_index(wm->list, w);
+	w = wm->cws->ordered->data;
+	pos = g_list_index(wm->cws->list, w);
 	pos += direction;
 
 	if (pos < 0)
-		wid = g_list_last(wm->list)->data;
-	else if (pos >= g_list_length(wm->list))
-		wid = wm->list->data;
+		wid = g_list_last(wm->cws->list)->data;
+	else if (pos >= g_list_length(wm->cws->list))
+		wid = wm->cws->list->data;
 	else if (pos >= 0)
-		wid = g_list_nth_data(wm->list, pos);
+		wid = g_list_nth_data(wm->cws->list, pos);
 
-	wm->ordered = g_list_bring_to_front(wm->ordered, wid);
+	wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, wid);
 
-	gnt_wm_raise_window(wm, wm->ordered->data);
+	gnt_wm_raise_window(wm, wm->cws->ordered->data);
 
 	if (w != wid) {
 		gnt_widget_set_focus(w, FALSE);
@@ -420,7 +410,7 @@
 	GList *l;
 	int n;
 
-	if (!wm->ordered)
+	if (!wm->cws->ordered)
 		return TRUE;
 
 	if (list)
@@ -428,9 +418,9 @@
 	else
 		n = 0;
 
-	w = wm->ordered->data;
+	w = wm->cws->ordered->data;
 
-	if ((l = g_list_nth(wm->list, n)) != NULL)
+	if ((l = g_list_nth(wm->cws->list, n)) != NULL)
 	{
 		gnt_wm_raise_window(wm, l->data);
 	}
@@ -449,17 +439,17 @@
 	GntWidget *window;
 	GntNode *node;
 
-	if (!wm->ordered)
+	if (!wm->cws->ordered)
 		return TRUE;
 
-	window = wm->ordered->data;
+	window = wm->cws->ordered->data;
 	node = g_hash_table_lookup(wm->nodes, window);
 	if (!node)
 		return TRUE;
 
 	if (node->scroll) {
 		node->scroll--;
-		copy_win(window, node);
+		gnt_wm_copy_win(window, node);
 		update_screen(wm);
 	}
 	return TRUE;
@@ -473,10 +463,10 @@
 	GntNode *node;
 	int w, h;
 
-	if (!wm->ordered)
+	if (!wm->cws->ordered)
 		return TRUE;
 
-	window = wm->ordered->data;
+	window = wm->cws->ordered->data;
 	node = g_hash_table_lookup(wm->nodes, window);
 	if (!node)
 		return TRUE;
@@ -484,7 +474,7 @@
 	gnt_widget_get_size(window, &w, &h);
 	if (h - node->scroll > getmaxy(node->window)) {
 		node->scroll++;
-		copy_win(window, node);
+		gnt_wm_copy_win(window, node);
 		update_screen(wm);
 	}
 	return TRUE;
@@ -498,8 +488,8 @@
 	if (wm->_list.window)
 		return TRUE;
 
-	if (wm->ordered) {
-		gnt_widget_destroy(wm->ordered->data);
+	if (wm->cws->ordered) {
+		gnt_widget_destroy(wm->cws->ordered->data);
 	}
 
 	return TRUE;
@@ -512,10 +502,10 @@
 	GntWidget *widget, *tree, *win, *active;
 	char *title;
 
-	if (!wm->ordered)
+	if (!wm->cws->ordered)
 		return TRUE;
 
-	widget = wm->ordered->data;
+	widget = wm->cws->ordered->data;
 	if (!GNT_IS_BOX(widget))
 		return TRUE;
 	active = GNT_BOX(widget)->active;
@@ -562,85 +552,123 @@
 static void
 window_list_activate(GntTree *tree, GntWM *wm)
 {
-	GntWidget *widget = gnt_tree_get_selection_data(GNT_TREE(tree));
+	GntBindable *sel = gnt_tree_get_selection_data(GNT_TREE(tree));
 
-	if (!wm->ordered || !widget)
+	gnt_widget_destroy(wm->_list.window);
+
+	if (!sel)
 		return;
 
-	gnt_widget_destroy(wm->_list.window);
-	gnt_wm_raise_window(wm, widget);
+	if (GNT_IS_WS(sel)) {
+		gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, sel));
+	} else {
+		gnt_wm_raise_window(wm, GNT_WIDGET(sel));
+	}
 }
 
 static void
-populate_window_list(GntWM *wm)
+populate_window_list(GntWM *wm, gboolean workspace)
 {
 	GList *iter;
 	GntTree *tree = GNT_TREE(wm->windows->tree);
-	for (iter = wm->list; iter; iter = iter->next) {
-		GntBox *box = GNT_BOX(iter->data);
+	if (!workspace) {
+		for (iter = wm->cws->list; iter; iter = iter->next) {
+			GntBox *box = GNT_BOX(iter->data);
 
-		gnt_tree_add_row_last(tree, box,
-				gnt_tree_create_row(tree, box->title), NULL);
-		update_window_in_list(wm, GNT_WIDGET(box));
+			gnt_tree_add_row_last(tree, box,
+					gnt_tree_create_row(tree, box->title), NULL);
+			update_window_in_list(wm, GNT_WIDGET(box));
+		}
+	} else {
+		GList *ws = wm->workspaces;
+		for (; ws; ws = ws->next) {
+			gnt_tree_add_row_last(tree, ws->data,
+					gnt_tree_create_row(tree, gnt_ws_get_name(GNT_WS(ws->data))), NULL);
+			for (iter = GNT_WS(ws->data)->list; iter; iter = iter->next) {
+				GntBox *box = GNT_BOX(iter->data);
+
+				gnt_tree_add_row_last(tree, box,
+						gnt_tree_create_row(tree, box->title), ws->data);
+				update_window_in_list(wm, GNT_WIDGET(box));
+			}
+		}
 	}
 }
 
 static gboolean
 window_list_key_pressed(GntWidget *widget, const char *text, GntWM *wm)
 {
-	if (text[1] == 0 && wm->ordered) {
-		GntWidget *sel = gnt_tree_get_selection_data(GNT_TREE(widget));
+	if (text[1] == 0 && wm->cws->ordered) {
+		GntBindable *sel = gnt_tree_get_selection_data(GNT_TREE(widget));
 		switch (text[0]) {
 			case '-':
 			case ',':
-				shift_window(wm, sel, -1);
+				if (GNT_IS_WS(sel)) {
+					/* reorder the workspace. */
+				} else
+					shift_window(wm, GNT_WIDGET(sel), -1);
 				break;
 			case '=':
 			case '.':
-				shift_window(wm, sel, 1);
+				if (GNT_IS_WS(sel)) {
+					/* reorder the workspace. */
+				} else
+					shift_window(wm, GNT_WIDGET(sel), 1);
 				break;
 			default:
 				return FALSE;
 		}
 		gnt_tree_remove_all(GNT_TREE(widget));
-		populate_window_list(wm);
+		populate_window_list(wm, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "workspace")));
 		gnt_tree_set_selected(GNT_TREE(widget), sel);
 		return TRUE;
 	}
 	return FALSE;
 }
 
+static void
+list_of_windows(GntWM *wm, gboolean workspace)
+{
+	GntWidget *tree, *win;
+	setup__list(wm);
+	wm->windows = &wm->_list;
+
+	win = wm->windows->window;
+	tree = wm->windows->tree;
+
+	gnt_box_set_title(GNT_BOX(win), workspace ? "Workspace List" : "Window List");
+	
+	populate_window_list(wm, workspace);
+
+	if (wm->cws->ordered)
+		gnt_tree_set_selected(GNT_TREE(tree), wm->cws->ordered->data);
+	else if (workspace)
+		gnt_tree_set_selected(GNT_TREE(tree), wm->cws);
+
+	g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(window_list_activate), wm);
+	g_signal_connect(G_OBJECT(tree), "key_pressed", G_CALLBACK(window_list_key_pressed), wm);
+	g_object_set_data(G_OBJECT(tree), "workspace", GINT_TO_POINTER(workspace));
+
+	gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3);
+	gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2);
+	gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4);
+
+	gnt_widget_show(win);
+}
+
 static gboolean
 window_list(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
-	GntWidget *tree, *win;
 
 	if (wm->_list.window || wm->menu)
 		return TRUE;
 
-	if (!wm->ordered)
+	if (!wm->cws->ordered)
 		return TRUE;
 
-	setup__list(wm);
-	wm->windows = &wm->_list;
-
-	win = wm->windows->window;
-	tree = wm->windows->tree;
-
-	gnt_box_set_title(GNT_BOX(win), "Window List");
-	
-	populate_window_list(wm);
+	list_of_windows(wm, FALSE);
 
-	gnt_tree_set_selected(GNT_TREE(tree), wm->ordered->data);
-	g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(window_list_activate), wm);
-	g_signal_connect(G_OBJECT(tree), "key_pressed", G_CALLBACK(window_list_key_pressed), wm);
-
-	gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3);
-	gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2);
-	gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4);
-
-	gnt_widget_show(win);
 	return TRUE;
 }
 
@@ -780,7 +808,7 @@
 static void
 shift_window(GntWM *wm, GntWidget *widget, int dir)
 {
-	GList *all = wm->list;
+	GList *all = wm->cws->list;
 	GList *list = g_list_find(all, widget);
 	int length, pos;
 	if (!list)
@@ -800,8 +828,8 @@
 
 	all = g_list_insert(all, widget, pos);
 	all = g_list_delete_link(all, list);
-	wm->list = all;
-	draw_taskbar(wm, FALSE);
+	wm->cws->list = all;
+	gnt_ws_draw_taskbar(wm->cws, FALSE);
 }
 
 static gboolean
@@ -811,7 +839,7 @@
 	if (wm->_list.window)
 		return TRUE;
 
-	shift_window(wm, wm->ordered->data, -1);
+	shift_window(wm, wm->cws->ordered->data, -1);
 	return TRUE;
 }
 
@@ -822,7 +850,7 @@
 	if (wm->_list.window)
 		return TRUE;
 
-	shift_window(wm, wm->ordered->data, 1);
+	shift_window(wm, wm->cws->ordered->data, 1);
 	return TRUE;
 }
 
@@ -946,7 +974,7 @@
 	for (i = 0; i < h; i += reverse_char(d, i, 0, set));
 	for (i = 0; i < h; i += reverse_char(d, i, w-1, set));
 
-	copy_win(win, g_hash_table_lookup(wm->nodes, win));
+	gnt_wm_copy_win(win, g_hash_table_lookup(wm->nodes, win));
 	update_screen(wm);
 }
 
@@ -956,11 +984,11 @@
 	GntWM *wm = GNT_WM(bindable);
 	if (wm->_list.window || wm->menu)
 		return TRUE;
-	if (!wm->ordered)
+	if (!wm->cws->ordered)
 		return TRUE;
 
 	wm->mode = GNT_KP_MODE_MOVE;
-	window_reverse(GNT_WIDGET(wm->ordered->data), TRUE, wm);
+	window_reverse(GNT_WIDGET(wm->cws->ordered->data), TRUE, wm);
 
 	return TRUE;
 }
@@ -971,11 +999,11 @@
 	GntWM *wm = GNT_WM(bindable);
 	if (wm->_list.window || wm->menu)
 		return TRUE;
-	if (!wm->ordered)
+	if (!wm->cws->ordered)
 		return TRUE;
 
 	wm->mode = GNT_KP_MODE_RESIZE;
-	window_reverse(GNT_WIDGET(wm->ordered->data), TRUE, wm);
+	window_reverse(GNT_WIDGET(wm->cws->ordered->data), TRUE, wm);
 
 	return TRUE;
 }
@@ -1002,11 +1030,12 @@
 	GntWM *wm = GNT_WM(bindable);
 
 	endwin();
+	refresh();
+	curs_set(0);   /* endwin resets the cursor to normal */
 
 	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL);
 	update_screen(wm);
-	draw_taskbar(wm, TRUE);
-	curs_set(0);   /* endwin resets the cursor to normal */
+	gnt_ws_draw_taskbar(wm->cws, TRUE);
 
 	return FALSE;
 }
@@ -1037,6 +1066,68 @@
 	return TRUE;
 }
 
+static void remove_tag(gpointer wid, gpointer wim)
+{
+	GntWM *wm = GNT_WM(wim);
+	GntWidget *w = GNT_WIDGET(wid);
+	wm->tagged = g_list_remove(wm->tagged, w);
+	mvwhline(w->window, 0, 1, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), 3);
+	gnt_widget_draw(w);
+}
+
+static gboolean
+tag_widget(GntBindable *b, GList *params)
+{
+	GntWM *wm = GNT_WM(b);
+	GntWidget *widget;
+
+	if (!wm->cws->ordered)
+		return FALSE;
+	widget = wm->cws->ordered->data;
+
+	if (g_list_find(wm->tagged, widget)) {
+		remove_tag(widget, wm);
+		return TRUE;
+	}
+
+	wm->tagged = g_list_prepend(wm->tagged, widget);
+	wbkgdset(widget->window, ' ' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT));
+	mvwprintw(widget->window, 0, 1, "[T]");
+	gnt_widget_draw(widget);
+	return TRUE;
+}
+
+static void
+widget_move_ws(gpointer wid, gpointer w)
+{
+	GntWM *wm = GNT_WM(w);
+	gnt_wm_widget_move_workspace(wm, wm->cws, GNT_WIDGET(wid));
+}
+
+static gboolean
+place_tagged(GntBindable *b, GList *params)
+{
+	GntWM *wm = GNT_WM(b);
+	g_list_foreach(wm->tagged, widget_move_ws, wm);
+	g_list_foreach(wm->tagged, remove_tag, wm);
+	g_list_free(wm->tagged);
+	wm->tagged = NULL;
+	return TRUE;
+}
+
+static gboolean
+workspace_list(GntBindable *b, GList *params)
+{
+	GntWM *wm = GNT_WM(b);
+
+	if (wm->_list.window || wm->menu)
+		return TRUE;
+
+	list_of_windows(wm, TRUE);
+
+	return TRUE;
+}
+
 static void
 gnt_wm_class_init(GntWMClass *klass)
 {
@@ -1171,6 +1262,16 @@
 				"\033" GNT_KEY_CTRL_K, NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-widget", help_for_widget,
 				"\033" "/", NULL);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-next", workspace_next,
+				"\033" ">", NULL);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-prev", workspace_prev,
+				"\033" "<", NULL);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-tag", tag_widget,
+				"\033" "t", NULL);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "place-tagged", place_tagged,
+				"\033" "T", NULL);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-list", workspace_list,
+				"\033" "s", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "toggle-clipboard",
 				toggle_clipboard, "\033" "C", NULL);
 
@@ -1215,6 +1316,118 @@
 
 	return type;
 }
+void
+gnt_wm_add_workspace(GntWM *wm, GntWS *ws)
+{
+	wm->workspaces = g_list_append(wm->workspaces, ws);
+}
+
+gboolean
+gnt_wm_switch_workspace(GntWM *wm, gint n)
+{
+	GntWS *s = g_list_nth_data(wm->workspaces, n);
+	if (!s)
+		return FALSE;
+
+	if (wm->_list.window) {
+		gnt_widget_destroy(wm->_list.window);
+	}
+	gnt_ws_hide(wm->cws, wm->nodes);
+	wm->cws = s;
+	gnt_ws_show(wm->cws, wm->nodes);
+
+	gnt_ws_draw_taskbar(wm->cws, TRUE);
+	update_screen(wm);
+	if (wm->cws->ordered) {
+		gnt_widget_set_focus(wm->cws->ordered->data, TRUE);
+		gnt_wm_raise_window(wm, wm->cws->ordered->data);
+	}
+
+	if (act && g_list_find(act, wm->cws)) {
+		act = g_list_remove(act, wm->cws);
+		update_act_msg();
+	}
+	return TRUE;
+}
+
+gboolean
+gnt_wm_switch_workspace_prev(GntWM *wm)
+{
+	int n = g_list_index(wm->workspaces, wm->cws);
+	return gnt_wm_switch_workspace(wm, --n);
+}
+
+gboolean
+gnt_wm_switch_workspace_next(GntWM *wm)
+{
+	int n = g_list_index(wm->workspaces, wm->cws);
+	return gnt_wm_switch_workspace(wm, ++n);
+}
+
+static gboolean
+workspace_next(GntBindable *wm, GList *n)
+{
+	return gnt_wm_switch_workspace_next(GNT_WM(wm));
+}
+
+static gboolean
+workspace_prev(GntBindable *wm, GList *n)
+{
+	return gnt_wm_switch_workspace_prev(GNT_WM(wm));
+}
+
+void
+gnt_wm_widget_move_workspace(GntWM *wm, GntWS *neww, GntWidget *widget)
+{
+	GntWS *oldw = gnt_wm_widget_find_workspace(wm, widget);
+	GntNode *node;
+	if (!oldw || oldw == neww)
+		return;
+	node = g_hash_table_lookup(wm->nodes, widget);
+	if (node && node->ws == neww)
+		return;
+
+	if (node)
+		node->ws = neww;
+
+	gnt_ws_remove_widget(oldw, widget);
+	gnt_ws_add_widget(neww, widget);
+	if (neww == wm->cws) {
+		gnt_ws_widget_show(widget, wm->nodes);
+	} else {
+		gnt_ws_widget_hide(widget, wm->nodes);
+	}
+}
+
+static gint widget_in_workspace(gconstpointer workspace, gconstpointer wid)
+{
+	GntWS *s = (GntWS *)workspace;
+	if (s->list && g_list_find(s->list, wid))
+		return 0;
+	return 1;
+}
+
+GntWS *gnt_wm_widget_find_workspace(GntWM *wm, GntWidget *widget)
+{
+	GList *l = g_list_find_custom(wm->workspaces, widget, widget_in_workspace);
+	if (l)
+		return l->data;
+	return NULL;
+}
+
+static void free_workspaces(gpointer data, gpointer n)
+{
+	GntWS *s = data;
+	g_free(s->name);
+}
+
+void gnt_wm_set_workspaces(GntWM *wm, GList *workspaces)
+{
+	g_list_foreach(wm->workspaces, free_workspaces, NULL);
+	wm->workspaces = workspaces;
+	gnt_wm_switch_workspace(wm, 0);
+}
+
 static void
 update_window_in_list(GntWM *wm, GntWidget *wid)
 {
@@ -1223,7 +1436,7 @@
 	if (wm->windows == NULL)
 		return;
 
-	if (wid == wm->ordered->data)
+	if (wm->cws->ordered && wid == wm->cws->ordered->data)
 		flag |= GNT_TEXT_FLAG_DIM;
 	else if (GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT))
 		flag |= GNT_TEXT_FLAG_BOLD;
@@ -1231,6 +1444,29 @@
 	gnt_tree_set_row_flags(GNT_TREE(wm->windows->tree), wid, flag);
 }
 
+static gboolean
+match_title(gpointer title, gpointer n, gpointer wid_title)
+{
+	/* maybe check for regex.h? */
+	if (g_strrstr((gchar *)wid_title, (gchar *)title))
+		return TRUE;
+	return FALSE;
+}
+
+static GntWS *
+new_widget_find_workspace(GntWM *wm, GntWidget *widget, gchar *wid_title)
+{
+	GntWS *ret;
+	const gchar *name;
+	ret = g_hash_table_find(wm->title_places, match_title, wid_title);
+	if (ret)
+		return ret;
+	name = gnt_widget_get_name(widget);
+	if (name)
+		ret = g_hash_table_lookup(wm->name_places, name);
+	return ret ? ret : wm->cws;
+}
+
 static void
 gnt_wm_new_window_real(GntWM *wm, GntWidget *widget)
 {
@@ -1277,7 +1513,7 @@
 		w = MIN(w, maxx);
 		h = MIN(h, maxy);
 		node->window = newwin(h + shadow, w + shadow, y, x);
-		copy_win(widget, node);
+		gnt_wm_copy_win(widget, node);
 	}
 #endif
 
@@ -1285,18 +1521,25 @@
 	set_panel_userptr(node->panel, node);
 
 	if (!transient) {
+		GntWS *ws = wm->cws;
 		if (node->me != wm->_list.window) {
 			GntWidget *w = NULL;
 
-			if (wm->ordered)
-				w = wm->ordered->data;
+			if (GNT_IS_BOX(widget)) {
+				char *title = GNT_BOX(widget)->title;
+				ws = new_widget_find_workspace(wm, widget, title);
+			}
 
-			wm->list = g_list_append(wm->list, widget);
+			if (ws->ordered)
+				w = ws->ordered->data;
+
+			node->ws = ws;
+			ws->list = g_list_append(ws->list, widget);
 
 			if (wm->event_stack)
-				wm->ordered = g_list_prepend(wm->ordered, widget);
+				ws->ordered = g_list_prepend(ws->ordered, widget);
 			else
-				wm->ordered = g_list_append(wm->ordered, widget);
+				ws->ordered = g_list_append(ws->ordered, widget);
 
 			gnt_widget_set_focus(widget, TRUE);
 			if (w)
@@ -1308,6 +1551,8 @@
 		} else {
 			bottom_panel(node->panel);     /* New windows should not grab focus */
 			gnt_widget_set_urgent(node->me);
+			if (wm->cws != ws)
+				gnt_ws_widget_hide(widget, wm->nodes);
 		}
 	}
 }
@@ -1341,13 +1586,13 @@
 				&& GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS)) {
 			gnt_tree_add_row_last(GNT_TREE(wm->windows->tree), widget,
 					gnt_tree_create_row(GNT_TREE(wm->windows->tree), GNT_BOX(widget)->title),
-					NULL);
+					g_object_get_data(G_OBJECT(wm->windows->tree), "workspace") ? wm->cws : NULL);
 			update_window_in_list(wm, widget);
 		}
 	}
 
 	update_screen(wm);
-	draw_taskbar(wm, FALSE);
+	gnt_ws_draw_taskbar(wm->cws, FALSE);
 }
 
 void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget)
@@ -1357,9 +1602,12 @@
 
 void gnt_wm_window_close(GntWM *wm, GntWidget *widget)
 {
+	GntWS *s;
 	GntNode *node;
 	int pos;
 
+	s = gnt_wm_widget_find_workspace(wm, widget);
+
 	if ((node = g_hash_table_lookup(wm->nodes, widget)) == NULL)
 		return;
 
@@ -1370,18 +1618,20 @@
 		gnt_tree_remove(GNT_TREE(wm->windows->tree), widget);
 	}
 
-	pos = g_list_index(wm->list, widget);
+	if (s) {
+		pos = g_list_index(s->list, widget);
 
-	if (pos != -1) {
-		wm->list = g_list_remove(wm->list, widget);
-		wm->ordered = g_list_remove(wm->ordered, widget);
+		if (pos != -1) {
+			s->list = g_list_remove(s->list, widget);
+			s->ordered = g_list_remove(s->ordered, widget);
 
-		if (wm->ordered)
-			gnt_wm_raise_window(wm, wm->ordered->data);
+			if (s->ordered && wm->cws == s)
+				gnt_wm_raise_window(wm, s->ordered->data);
+		}
 	}
 
 	update_screen(wm);
-	draw_taskbar(wm, FALSE);
+	gnt_ws_draw_taskbar(wm->cws, FALSE);
 }
 
 time_t gnt_wm_get_idle_time()
@@ -1402,10 +1652,10 @@
 	}
 
 	/* Do some manual checking */
-	if (wm->ordered && wm->mode != GNT_KP_MODE_NORMAL) {
+	if (wm->cws->ordered && wm->mode != GNT_KP_MODE_NORMAL) {
 		int xmin = 0, ymin = 0, xmax = getmaxx(stdscr), ymax = getmaxy(stdscr) - 1;
 		int x, y, w, h;
-		GntWidget *widget = GNT_WIDGET(wm->ordered->data);
+		GntWidget *widget = GNT_WIDGET(wm->cws->ordered->data);
 		int ox, oy, ow, oh;
 
 		gnt_widget_get_position(widget, &x, &y);
@@ -1481,8 +1731,8 @@
 		ret = gnt_widget_key_pressed(GNT_WIDGET(wm->menu), keys);
 	else if (wm->_list.window)
 		ret = gnt_widget_key_pressed(wm->_list.window, keys);
-	else if (wm->ordered)
-		ret = gnt_widget_key_pressed(GNT_WIDGET(wm->ordered->data), keys);
+	else if (wm->cws->ordered)
+		ret = gnt_widget_key_pressed(GNT_WIDGET(wm->cws->ordered->data), keys);
 	return ret;
 }
 
@@ -1616,9 +1866,9 @@
 		return;
 	
 	if (widget != wm->_list.window && !GNT_IS_MENU(widget) &&
-				wm->ordered->data != widget) {
-		GntWidget *w = wm->ordered->data;
-		wm->ordered = g_list_bring_to_front(wm->ordered, widget);
+				wm->cws->ordered->data != widget) {
+		GntWidget *w = wm->cws->ordered->data;
+		wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget);
 		gnt_widget_set_focus(w, FALSE);
 	}
 
@@ -1632,27 +1882,35 @@
 		top_panel(nd->panel);
 	}
 	update_screen(wm);
-	draw_taskbar(wm, FALSE);
+	gnt_ws_draw_taskbar(wm->cws, FALSE);
 }
 
 void gnt_wm_update_window(GntWM *wm, GntWidget *widget)
 {
-	GntNode *node;
+	GntNode *node = NULL;
+	GntWS *ws;
 
 	while (widget->parent)
 		widget = widget->parent;
 	if (!GNT_IS_MENU(widget))
 		gnt_box_sync_children(GNT_BOX(widget));
 
+	ws = gnt_wm_widget_find_workspace(wm, widget);
 	node = g_hash_table_lookup(wm->nodes, widget);
 	if (node == NULL) {
 		gnt_wm_new_window(wm, widget);
 	} else
 		g_signal_emit(wm, signals[SIG_UPDATE_WIN], 0, node);
 
-	copy_win(widget, node);
-	update_screen(wm);
-	draw_taskbar(wm, FALSE);
+	if (ws == wm->cws || GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
+		gnt_wm_copy_win(widget, node);
+		update_screen(wm);
+		gnt_ws_draw_taskbar(wm->cws, FALSE);
+	} else if (ws != wm->cws && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_URGENT)) {
+		if (!act || (act && !g_list_find(act, ws)))
+			act = g_list_prepend(act, ws);
+		update_act_msg();
+	}
 }
 
 gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget)
@@ -1665,6 +1923,9 @@
 
 void gnt_wm_raise_window(GntWM *wm, GntWidget *widget)
 {
+	GntWS *ws = gnt_wm_widget_find_workspace(wm, widget);
+	if (wm->cws != ws)
+		gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws));
 	g_signal_emit(wm, signals[SIG_GIVE_FOCUS], 0, widget);
 }
 
--- a/finch/libgnt/gntwm.h	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/gntwm.h	Fri Jun 08 13:06:21 2007 +0000
@@ -20,9 +20,12 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#ifndef GNTWM_H
+#define GNTWM_H
 
 #include "gntwidget.h"
 #include "gntmenu.h"
+#include "gntws.h"
 
 #include <panel.h>
 #include <time.h>
@@ -48,9 +51,10 @@
 	WINDOW *window;
 	int scroll;
 	PANEL *panel;
+	GntWS *ws;
 } GntNode;
 
-typedef struct _GnttWM GntWM;
+typedef struct _GntWM GntWM;
 
 typedef struct _GntPosition
 {
@@ -67,14 +71,15 @@
 	void (*callback)();
 } GntAction;
 
-struct _GnttWM
+struct _GntWM
 {
 	GntBindable inherit;
 
 	GMainLoop *loop;
 
-	GList *list;      /* List of windows ordered on their creation time */
-	GList *ordered;   /* List of windows ordered on their focus */
+	GList *workspaces;
+	GList *tagged; /* tagged windows */
+	GntWS *cws;
 
 	struct {
 		GntWidget *window;
@@ -84,6 +89,8 @@
 		*actions;         /* Action-list window */
 
 	GHashTable *nodes;    /* GntWidget -> GntNode */
+	GHashTable *name_places;    /* window name -> ws*/
+	GHashTable *title_places;    /* window title -> ws */
 
 	GList *acts;          /* List of actions */
 
@@ -173,6 +180,15 @@
  */
 GType gnt_wm_get_gtype(void);
 
+void gnt_wm_add_workspace(GntWM *wm, GntWS *ws);
+
+gboolean gnt_wm_switch_workspace(GntWM *wm, gint n);
+gboolean gnt_wm_switch_workspace_prev(GntWM *wm);
+gboolean gnt_wm_switch_workspace_next(GntWM *wm);
+void gnt_wm_widget_move_workspace(GntWM *wm, GntWS *neww, GntWidget *widget);
+void gnt_wm_set_workspaces(GntWM *wm, GList *workspaces);
+GntWS *gnt_wm_widget_find_workspace(GntWM *wm, GntWidget *widget);
+
 /**
  * 
  * @param wm
@@ -254,6 +270,8 @@
  */
 void gnt_wm_set_event_stack(GntWM *wm, gboolean set);
 
+void gnt_wm_copy_win(GntWidget *widget, GntNode *node);
+
 /**
  * 
  *
@@ -262,3 +280,4 @@
 time_t gnt_wm_get_idle_time(void);
 
 G_END_DECLS
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/gntws.c	Fri Jun 08 13:06:21 2007 +0000
@@ -0,0 +1,161 @@
+#include <gmodule.h>
+
+#include "gntbox.h"
+#include "gntwidget.h"
+#include "gntwindow.h"
+#include "gntwm.h"
+#include "gntws.h"
+
+static void
+widget_hide(gpointer data, gpointer nodes)
+{
+	GntWidget *widget = GNT_WIDGET(data);
+	GntNode *node = g_hash_table_lookup(nodes, widget);
+	if (GNT_IS_WINDOW(widget))
+		gnt_window_workspace_hiding(GNT_WINDOW(widget));
+	hide_panel(node->panel);
+}
+
+static void
+widget_show(gpointer data, gpointer nodes)
+{
+	GntNode *node = g_hash_table_lookup(nodes, data);
+	GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(data), GNT_WIDGET_INVISIBLE);
+	if (node) {
+		show_panel(node->panel);
+		gnt_wm_copy_win(GNT_WIDGET(data), node);
+	}
+}
+
+void
+gnt_ws_draw_taskbar(GntWS *ws, gboolean reposition)
+{
+	static WINDOW *taskbar = NULL;
+	GList *iter;
+	int n, width = 0;
+	int i;
+
+	if (taskbar == NULL) {
+		taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0);
+	} else if (reposition) {
+		int Y_MAX = getmaxy(stdscr) - 1;
+		mvwin(taskbar, Y_MAX, 0);
+	}
+
+	wbkgdset(taskbar, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
+	werase(taskbar);
+
+	n = g_list_length(ws->list);
+	if (n)
+		width = getmaxx(stdscr) / n;
+
+	for (i = 0, iter = ws->list; iter; iter = iter->next, i++) {
+		GntWidget *w = iter->data;
+		int color;
+		const char *title;
+
+		if (w == ws->ordered->data) {
+			/* This is the current window in focus */
+			color = GNT_COLOR_TITLE;
+		} else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_URGENT)) {
+			/* This is a window with the URGENT hint set */
+			color = GNT_COLOR_URGENT;
+		} else {
+			color = GNT_COLOR_NORMAL;
+		}
+		wbkgdset(taskbar, '\0' | COLOR_PAIR(color));
+		if (iter->next)
+			mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width);
+		else
+			mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), getmaxx(stdscr) - width * i);
+		title = GNT_BOX(w)->title;
+		mvwprintw(taskbar, 0, width * i, "%s", title ? title : "<gnt>");
+		if (i)
+			mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | COLOR_PAIR(GNT_COLOR_NORMAL));
+	}
+	wrefresh(taskbar);
+}
+
+static void
+gnt_ws_init(GTypeInstance *instance, gpointer class)
+{
+	GntWS *ws = GNT_WS(instance);
+	ws->list = NULL;
+	ws->ordered = NULL;
+	ws->name = NULL;
+}
+
+void gnt_ws_add_widget(GntWS *ws, GntWidget* wid)
+{
+	ws->list = g_list_append(ws->list, wid);
+	ws->ordered = g_list_prepend(ws->ordered, wid);
+}
+
+void gnt_ws_remove_widget(GntWS *ws, GntWidget* wid)
+{
+	ws->list = g_list_remove(ws->list, wid);
+	ws->ordered = g_list_remove(ws->ordered, wid);
+}
+
+void
+gnt_ws_set_name(GntWS *ws, const gchar *name)
+{
+	g_free(ws->name);
+	ws->name = g_strdup(name);
+}
+
+void
+gnt_ws_hide(GntWS *ws, GHashTable *nodes)
+{
+	g_list_foreach(ws->ordered, widget_hide, nodes);
+}
+
+void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes) {
+	widget_hide(widget, nodes);
+}
+
+void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes) {
+	widget_show(widget, nodes);
+}
+
+void
+gnt_ws_show(GntWS *ws, GHashTable *nodes)
+{
+	GList *l;
+	for (l = g_list_last(ws->ordered); l; l = g_list_previous(l))
+		widget_show(l->data, nodes);
+}
+
+GType
+gnt_ws_get_gtype(void)
+{
+	static GType type = 0;
+
+	if(type == 0) {
+		static const GTypeInfo info = {
+			sizeof(GntWSClass),
+			NULL,					/* base_init		*/
+			NULL,					/* base_finalize	*/
+			NULL,
+			/*(GClassInitFunc)gnt_ws_class_init,*/
+			NULL,
+			NULL,					/* class_data		*/
+			sizeof(GntWS),
+			0,						/* n_preallocs		*/
+			gnt_ws_init,			/* instance_init	*/
+			NULL					/* value_table		*/
+		};
+
+		type = g_type_register_static(GNT_TYPE_BINDABLE,
+									  "GntWS",
+									  &info, 0);
+	}
+
+	return type;
+}
+
+const char * gnt_ws_get_name(GntWS *ws)
+{
+	return ws->name;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/gntws.h	Fri Jun 08 13:06:21 2007 +0000
@@ -0,0 +1,59 @@
+#ifndef GNTWS_H
+#define GNTWS_H
+
+#include "gntwidget.h"
+
+#include <panel.h>
+
+#define GNT_TYPE_WS				(gnt_ws_get_gtype())
+#define GNT_WS(obj)				(G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WS, GntWS))
+#define GNT_IS_WS(obj)			(G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WS))
+#define GNT_IS_WS_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WS))
+#define GNT_WS_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_WS, GntWSClass))
+
+typedef struct _GntWS GntWS;
+
+struct _GntWS
+{
+	GntBindable inherit;
+	gchar *name;
+	GList *list;
+	GList *ordered;
+	gpointer ui_data;
+	
+	void *res1;
+	void *res2;
+	void *res3;
+	void *res4;
+};
+
+typedef struct _GntWSClass GntWSClass;
+
+struct _GntWSClass
+{
+	GntBindableClass parent;
+
+	void (*draw_taskbar)(GntWS *ws, gboolean );
+
+	void (*res1)(void);
+	void (*res2)(void);
+	void (*res3)(void);
+	void (*res4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType gnt_ws_get_gtype(void);
+
+void gnt_ws_set_name(GntWS *, const gchar *);
+void gnt_ws_add_widget(GntWS *, GntWidget *);
+void gnt_ws_remove_widget(GntWS *, GntWidget *);
+void gnt_ws_widget_hide(GntWidget *, GHashTable *nodes);
+void gnt_ws_widget_show(GntWidget *, GHashTable *nodes);
+void gnt_ws_draw_taskbar(GntWS *, gboolean reposition);
+void gnt_ws_hide(GntWS *, GHashTable *);
+void gnt_ws_show(GntWS *, GHashTable *);
+
+const char * gnt_ws_get_name(GntWS *ws);
+
+#endif
--- a/finch/libgnt/wms/s.c	Thu Jun 07 21:14:54 2007 +0000
+++ b/finch/libgnt/wms/s.c	Fri Jun 08 13:06:21 2007 +0000
@@ -121,7 +121,7 @@
 static GntWidget *
 find_widget(GntWM *wm, const char *wname)
 {
-	const GList *iter = wm->list;
+	const GList *iter = wm->cws->list;
 	for (; iter; iter = iter->next) {
 		GntWidget *widget = iter->data;
 		const char *name = gnt_widget_get_name(widget);