diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/Makefile.am metacity-2.4.1-whistory/src/Makefile.am
--- metacity-2.4.1/src/Makefile.am	Tue Aug 20 16:18:18 2002
+++ metacity-2.4.1-whistory/src/Makefile.am	Fri Nov  8 16:08:38 2002
@@ -49,6 +49,8 @@
 	resizepopup.h				\
 	screen.c				\
 	screen.h				\
+	history.c				\
+	history.h				\
 	session.c				\
 	session.h				\
 	stack.c					\
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/history.c metacity-2.4.1-whistory/src/history.c
--- metacity-2.4.1/src/history.c	Wed Dec 31 19:00:00 1969
+++ metacity-2.4.1-whistory/src/history.c	Tue Nov 19 22:46:22 2002
@@ -0,0 +1,916 @@
+/* Metacity Window History */
+
+/* 
+ * Copyright (C) 2002 Benjamin Kahn
+ * based heavily on, and portions from Metacity Session Management
+ * Copyright (C) 2001 Havoc Pennington
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* A discussion on saving history:
+ *
+ * res_title contains the current window title.  orig_title
+ * stores the first title the window had.  We use this to track
+ * the life of the window.   That way, nautilus will start up in the
+ * same place if you start it in the same directory, but could show
+ * up in a different spot if started in a different directory (say
+ * start-here:/// and system:///home
+ *
+ * num starts out at -1 -- undefined.  In the placement of the window, 
+ * this value is updated to be one more than the number of similar windows.
+ * we look for res_class / orig_name / role / <num> from zero until we 
+ * can't find it anymore.  Then we assign num.
+ * 
+ * on move, (update) we check to renumber the window so if window
+ * numbers are 0, 1, 2, 5, 6, 8 and we move 6 it'll get renumbered
+ * to 3.
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "main.h"
+#include "util.h"
+#include "display.h"
+#include "workspace.h"
+#include "history.h"
+
+static GHashTable *window_history_list = NULL;
+
+static MetaWindowHistoryInfo *history_info_new (void);
+static void save_history                       (void);
+static void history_info_free                  (MetaWindowHistoryInfo *info);
+static void load_state                         (void);
+static void save_history_entry                 (gpointer               key,
+			                        gpointer               value,
+			                        gpointer               userdata);
+static gboolean both_null_or_matching          (const char            *a,
+						const char            *b);
+static const char* window_type_to_string       (MetaWindowType         type);
+static MetaWindowType window_type_from_string  (const char            *str);
+static const char* window_gravity_to_string    (int                    gravity);
+static int window_gravity_from_string          (const char            *str);
+static char* encode_text_as_utf8_markup        (const char            *text);
+static char* decode_text_from_utf8             (const char            *text);
+static char *make_window_key                   (MetaWindow            *window);
+static char *make_window_key_history           (MetaWindowHistoryInfo *info);
+GList *meta_history_find_windows                (MetaWindow            *window);
+
+static gboolean
+both_null_or_matching (const char *a,
+                       const char *b)
+{
+  if (a == NULL && b == NULL)
+    return TRUE;
+  else if (a && b && strcmp (a, b) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+GList *
+meta_history_find_windows (MetaWindow *window)
+{
+  /* Find windows that matter (on same workspace
+   * as placed window, may be shaded - if shaded we pretend it isn't
+   * for placement purposes)
+   */
+  GSList *all_windows;
+  GList *windows = NULL;
+  GSList *tmp;
+  
+  all_windows = meta_display_list_windows (window->display);
+  
+  tmp = all_windows;
+  while (tmp != NULL)
+    {
+      MetaWindow *w = tmp->data;
+      
+      if (w != window && 
+	  meta_window_shares_some_workspace (window, w))
+	windows = g_list_prepend (windows, w);
+      
+      tmp = tmp->next;
+    }
+  return windows;
+}
+
+int
+meta_history_next_window (MetaWindow *window)
+{
+  GList *windows = meta_history_find_windows (window);
+  return meta_history_next_window_full (windows, window);
+}
+
+int
+meta_history_next_window_full (GList *windows, 
+			       MetaWindow *window)
+{
+  GList *tmp;
+  int max = window->num;
+  int num;
+  int found;
+
+  /* This function has worst case of n^2, but n is usually small, and 
+   * worst case is very unlikely. 
+   */
+  
+  /* max can be -1, in which case we need to scan until the first empty spot. */
+  for (num = 0; num != max; num++) 
+    {
+
+      found = 0;
+      for (tmp = windows; tmp != NULL; tmp = tmp->next) 
+	{
+	  MetaWindow *w;
+	  
+	  w = tmp->data;
+
+	  /* Does this window match? */
+	  if (both_null_or_matching (window->res_class, w->res_class) &&
+	      both_null_or_matching (window->orig_name, w->orig_name) &&
+	      both_null_or_matching (window->role, w->role)) 
+	    {
+
+	      if (num == w->num) 
+		{
+		  found = 1;
+		  break;
+		}
+	    }
+	}
+	      
+      if (!found) 
+	{
+	  window->num = num;
+	  return window->num;
+	}
+    }
+  
+  /* We didn't find anything below us, we're in the right order. */
+  return window->num;
+}
+
+static char *
+make_window_key (MetaWindow *window)
+{
+  char *num = g_strdup ("0");
+  char *key = NULL;
+  char *role = NULL;
+
+  if (window->num > 0)
+    num = g_strdup_printf ("%d", window->num);
+
+  if (window->role) 
+    role = window->role;
+  else
+    role = " ";
+  
+  key = g_strconcat (window->res_class, "/", 
+		     window->orig_name, "/", 
+		     role, "/",  
+		     num, NULL);
+  g_free (num);
+
+  return key;
+}
+
+static char *
+make_window_key_history (MetaWindowHistoryInfo *hist)
+{
+  char *num = g_strdup ("0");
+  char *key = NULL;
+  char *role = NULL;
+
+  if (hist->num > 0)
+    num = g_strdup_printf ("%d", hist->num);
+
+  if (hist->role) 
+    role = hist->role;
+  else
+    role = " ";
+  
+  key = g_strconcat (hist->res_class, "/", 
+		     hist->res_name, "/", 
+		     role, "/",  
+		     num, NULL);
+  g_free (num);
+
+  return key;
+}
+
+const MetaWindowHistoryInfo* 
+meta_history_lookup  (MetaWindow *window)
+{
+  gchar *key;
+  MetaWindowHistoryInfo *result = NULL;
+  
+  if (!window) return NULL;
+
+  key = make_window_key (window);
+
+  result = (MetaWindowHistoryInfo *) g_hash_table_lookup (window_history_list,
+						      key);
+  
+  g_free (key);
+  return result;
+}
+
+const MetaWindowHistoryInfo*
+meta_history_lookup_key (char *key)
+{
+ return (MetaWindowHistoryInfo *) g_hash_table_lookup (window_history_list,
+						       key); 
+}
+
+void
+meta_history_update  (MetaWindow *window)
+{
+  MetaWindowHistoryInfo *win;
+  gchar *key;
+
+  if (!window) return;
+  if (!window->user_has_move_resized) return;
+
+  win = history_info_new ();
+  
+  win->res_class   = g_strdup (window->res_class);
+  win->res_name    = g_strdup (window->res_name);
+  win->title       = g_strdup (window->title);
+  win->role        = g_strdup (window->role);
+  win->type        = window->type;
+  win->rect.x      = window->user_rect.x;
+  win->rect.y      = window->user_rect.y;
+  win->rect.width  = window->user_rect.width;
+  win->rect.height = window->user_rect.height;
+  win->num         = window->num; /* meta_history_find_next_window (window); */
+
+  /* Re-number the window if we need to. */
+  key = make_window_key (window);
+
+  g_hash_table_insert (window_history_list, (gpointer) key, (gpointer) win);
+
+  return;
+}
+
+void
+meta_history_init (void)
+{
+  /* Initialize the hash table of windows. */
+  window_history_list = g_hash_table_new_full (g_str_hash, 
+					       g_str_equal,
+					       g_free,
+					       (GDestroyNotify) history_info_free);
+
+  /* Now load the list of saved window positions and store them. */
+  load_state ();
+}
+
+void
+meta_history_shutdown (void)
+{
+  /* Save out the file. */
+  save_history ();
+
+  return;
+}
+
+typedef enum
+{
+  WINDOW_TAG_GEOMETRY
+} WindowTag;
+
+typedef struct
+{
+  MetaWindowHistoryInfo *info;
+} ParseData;
+
+static void start_element_handler (GMarkupParseContext  *context,
+                                   const gchar          *element_name,
+                                   const gchar         **attribute_names,
+                                   const gchar         **attribute_values,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void end_element_handler   (GMarkupParseContext  *context,
+                                   const gchar          *element_name,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void text_handler          (GMarkupParseContext  *context,
+                                   const gchar          *text,
+                                   gsize                 text_len,
+                                   gpointer              user_data,
+                                   GError              **error);
+
+static GMarkupParser metacity_history_parser = {
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  NULL,
+  NULL
+};
+
+static void
+history_info_free (MetaWindowHistoryInfo *info)
+{
+  g_free (info->res_class);
+  g_free (info->res_name);
+  g_free (info->title);
+  g_free (info->role);
+
+  g_free (info);
+}
+
+static MetaWindowHistoryInfo *
+history_info_new (void)
+{
+  MetaWindowHistoryInfo *info;
+
+  info = g_new0 (MetaWindowHistoryInfo, 1);
+
+  info->type = META_WINDOW_NORMAL;
+  
+  return info;
+}
+
+static const char*
+window_type_to_string (MetaWindowType type)
+{
+  switch (type)
+    {
+    case META_WINDOW_NORMAL:
+      return "normal";
+      break;
+    case META_WINDOW_DESKTOP:
+      return "desktop";
+      break;
+    case META_WINDOW_DOCK:
+      return "dock";
+      break;
+    case META_WINDOW_DIALOG:
+      return "dialog";
+      break;
+    case META_WINDOW_MODAL_DIALOG:
+      return "modal_dialog";
+      break;
+    case META_WINDOW_TOOLBAR:
+      return "toolbar";
+      break;
+    case META_WINDOW_MENU:
+      return "menu";
+      break;
+    case META_WINDOW_SPLASHSCREEN:
+      return "splashscreen";
+      break;
+    case META_WINDOW_UTILITY:
+      return "utility";
+      break;
+    }
+
+  return "";
+} 
+
+static MetaWindowType
+window_type_from_string (const char *str)
+{
+  if (strcmp (str, "normal") == 0)
+    return META_WINDOW_NORMAL;
+  else if (strcmp (str, "desktop") == 0)
+    return META_WINDOW_DESKTOP;
+  else if (strcmp (str, "dock") == 0)
+    return META_WINDOW_DOCK;
+  else if (strcmp (str, "dialog") == 0)
+    return META_WINDOW_DIALOG;
+  else if (strcmp (str, "modal_dialog") == 0)
+    return META_WINDOW_MODAL_DIALOG;
+  else if (strcmp (str, "toolbar") == 0)
+    return META_WINDOW_TOOLBAR;
+  else if (strcmp (str, "menu") == 0)
+    return META_WINDOW_MENU;
+  else if (strcmp (str, "utility") == 0)
+    return META_WINDOW_UTILITY;
+  else if (strcmp (str, "splashscreen") == 0)
+    return META_WINDOW_SPLASHSCREEN;
+  else
+    return META_WINDOW_NORMAL;
+}
+
+static const char*
+window_gravity_to_string (int gravity)
+{
+  switch (gravity)
+    {
+    case NorthWestGravity:
+      return "NorthWestGravity";
+      break;
+    case NorthGravity:
+      return "NorthGravity";
+      break;
+    case NorthEastGravity:
+      return "NorthEastGravity";
+      break;
+    case WestGravity:
+      return "WestGravity";
+      break;
+    case CenterGravity:
+      return "CenterGravity";
+      break;
+    case EastGravity:
+      return "EastGravity";
+      break;
+    case SouthWestGravity:
+      return "SouthWestGravity";
+      break;
+    case SouthGravity:
+      return "SouthGravity";
+      break;
+    case SouthEastGravity:
+      return "SouthEastGravity";
+      break;
+    case StaticGravity:
+      return "StaticGravity";
+      break;
+    default:
+      return "NorthWestGravity";
+      break;
+    }
+}
+  
+static int
+window_gravity_from_string (const char *str)
+{
+  if (strcmp (str, "NorthWestGravity") == 0)
+    return NorthWestGravity;
+  else if (strcmp (str, "NorthGravity") == 0)
+    return NorthGravity;
+  else if (strcmp (str, "NorthEastGravity") == 0)
+    return NorthEastGravity;
+  else if (strcmp (str, "WestGravity") == 0)
+    return WestGravity;
+  else if (strcmp (str, "CenterGravity") == 0)
+    return CenterGravity;
+  else if (strcmp (str, "EastGravity") == 0)
+    return EastGravity;
+  else if (strcmp (str, "SouthWestGravity") == 0)
+    return SouthWestGravity;
+  else if (strcmp (str, "SouthGravity") == 0)
+    return SouthGravity;
+  else if (strcmp (str, "SouthEastGravity") == 0)
+    return SouthEastGravity;
+  else if (strcmp (str, "StaticGravity") == 0)
+    return StaticGravity;
+  else
+    return NorthWestGravity;
+}
+
+static char*
+encode_text_as_utf8_markup (const char *text)
+{
+  /* text can be any encoding, and is nul-terminated.
+   * we pretend it's Latin-1 and encode as UTF-8
+   */
+  GString *str;
+  const char *p;
+  char *escaped;
+  
+  str = g_string_new ("");
+
+  p = text;
+  while (*p)
+    {
+      g_string_append_unichar (str, *p);
+      ++p;
+    }
+
+  escaped = g_markup_escape_text (str->str, str->len);
+  g_string_free (str, TRUE);
+  
+  return escaped;
+}
+
+static char*
+decode_text_from_utf8 (const char *text)
+{
+  /* Convert back from the encoded (but not escaped) UTF-8 */
+  GString *str;
+  const char *p;
+
+  str = g_string_new ("");
+  
+  p = text;
+  while (*p)
+    {
+      /* obviously this barfs if the UTF-8 contains chars > 255 */
+      g_string_append_c (str, g_utf8_get_char (p));
+
+      p = g_utf8_next_char (p);
+    }
+
+  return g_string_free (str, FALSE);
+}
+
+static void 
+load_state (void)
+{
+  GMarkupParseContext *context;
+  GError *error;
+  ParseData parse_data;
+  char *text;
+  gsize length;
+  char *history_file;
+  
+  history_file = g_strconcat (g_get_home_dir (),
+                              "/.metacity/window_history",
+                              NULL);
+
+  error = NULL;
+  if (!g_file_get_contents (history_file,
+                            &text,
+                            &length,
+                            &error))
+    {
+      meta_warning (_("Failed to read saved history file %s: %s\n"),
+                    history_file, error->message);
+      g_error_free (error);
+      g_free (history_file);
+      history_file = NULL;
+      return;
+    }
+  
+  meta_topic (META_DEBUG_HISTORY, "Parsing saved history file %s\n", history_file);
+  g_free (history_file);
+  history_file = NULL;
+  
+  parse_data.info = NULL;
+  
+  context = g_markup_parse_context_new (&metacity_history_parser,
+                                        0, &parse_data, NULL);
+
+  error = NULL;
+  if (!g_markup_parse_context_parse (context,
+                                     text,
+                                     length,
+                                     &error))
+    goto error;
+  
+  
+  error = NULL;
+  if (!g_markup_parse_context_end_parse (context, &error))
+    goto error;
+
+  g_markup_parse_context_free (context);
+
+  goto out;
+
+ error:
+  
+  meta_warning (_("Failed to parse saved history file: %s\n"),
+                error->message);
+  g_error_free (error);
+
+  if (parse_data.info)
+    history_info_free (parse_data.info);
+
+ out:
+  
+  g_free (text);
+
+  return;
+}
+
+
+static void
+start_element_handler  (GMarkupParseContext *context,
+                        const gchar         *element_name,
+                        const gchar        **attribute_names,
+                        const gchar        **attribute_values,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+
+  if (strcmp (element_name, "metacity_history") == 0)
+    {
+      return; /* Right now this is a placeholder tag.  Probably not needed. */
+    }
+  else if (strcmp (element_name, "window") == 0)
+    {
+      int i;
+      
+      if (pd->info)
+        {
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_PARSE,
+                       _("nested <window> tag"));
+          return;
+        }
+      
+      pd->info = history_info_new ();
+
+      i = 0;
+      while (attribute_names[i])
+        {
+          const char *name;
+          const char *val;
+          
+          name = attribute_names[i];
+          val = attribute_values[i];
+          
+	  if (strcmp (name, "class") == 0)
+            {
+              if (*val)
+                pd->info->res_class = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "name") == 0)
+            {
+              if (*val)
+                pd->info->res_name = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "title") == 0)
+            {
+              if (*val)
+                pd->info->title = g_strdup (val);
+            }
+          else if (strcmp (name, "role") == 0)
+            {
+              if (*val)
+                pd->info->role = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "type") == 0)
+            {
+              if (*val)
+                pd->info->type = window_type_from_string (val);
+            }
+	  else if (strcmp (name, "num") == 0)
+	    {
+	      if (*val)
+		pd->info->num = atoi (val);
+	    }
+          else
+            {
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                           _("Unknown attribute %s on <window> element"),
+                           name);
+              history_info_free (pd->info);
+              pd->info = NULL;
+              return;
+            }
+          
+          ++i;
+        }
+    }
+  else if (strcmp (element_name, "geometry") == 0)
+    {
+      int i;
+
+      i = 0;
+      while (attribute_names[i])
+        {
+          const char *name;
+          const char *val;
+          
+          name = attribute_names[i];
+          val = attribute_values[i];
+          
+          if (strcmp (name, "x") == 0)
+            {
+              if (*val)
+                pd->info->rect.x = atoi (val);
+            }
+          else if (strcmp (name, "y") == 0)
+            {
+              if (*val)
+                pd->info->rect.y = atoi (val);
+            }
+          else if (strcmp (name, "width") == 0)
+            {
+              if (*val)
+                pd->info->rect.width = atoi (val);
+            }
+          else if (strcmp (name, "height") == 0)
+            {
+              if (*val)
+                pd->info->rect.height = atoi (val);
+            }
+          else if (strcmp (name, "gravity") == 0)
+            {
+              if (*val)
+                pd->info->gravity = window_gravity_from_string (val);
+            }
+          else
+            {
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                           _("Unknown attribute %s on <geometry> element"),
+                           name);
+              return;
+            }
+          
+          ++i;
+        }
+
+      meta_topic (META_DEBUG_SM, "Loaded geometry %d,%d %dx%d gravity %s\n",
+                  pd->info->rect.x,
+                  pd->info->rect.y,
+                  pd->info->rect.width,
+                  pd->info->rect.height,
+                  window_gravity_to_string (pd->info->gravity));
+    }
+  else
+    {
+      g_set_error (error,
+                   G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                   _("Unknown element %s"),
+                   element_name);
+      return;
+    }
+}
+
+static void
+end_element_handler    (GMarkupParseContext *context,
+                        const gchar         *element_name,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+
+  if (strcmp (element_name, "window") == 0)
+    {
+      gchar *stored_name;
+
+      g_assert (pd->info);
+
+      stored_name = make_window_key_history (pd->info);
+      g_hash_table_insert (window_history_list, stored_name, pd->info);
+      
+      meta_topic (META_DEBUG_HISTORY, "Loaded window info from history with class: %s name: %s role: %s\n",
+                  pd->info->res_class ? pd->info->res_class : "(none)",
+                  pd->info->res_name ? pd->info->res_name : "(none)",
+                  pd->info->role ? pd->info->role : "(none)");
+
+      
+      pd->info = NULL;
+    }
+}
+
+static void
+text_handler           (GMarkupParseContext *context,
+                        const gchar         *text,
+                        gsize                text_len,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+  
+  /* Right now we don't have any elements where we care about their
+   * content
+   */
+}
+
+static void
+save_history (void)
+{
+  char *metacity_dir;
+  char *history_file;
+  FILE *outfile;
+  
+  outfile = NULL;
+  
+  metacity_dir = g_strconcat (g_get_home_dir (), "/.metacity",
+                              NULL);
+  
+  history_file = g_strconcat (metacity_dir,
+			      "/window_history",
+			      NULL);
+
+  if (mkdir (metacity_dir, 0700) < 0 &&
+      errno != EEXIST)
+    {
+      meta_warning (_("Could not create directory '%s': %s\n"),
+                    metacity_dir, g_strerror (errno));
+    }
+
+  meta_topic (META_DEBUG_HISTORY, "Saving history to '%s'\n", history_file);
+  
+  outfile = fopen (history_file, "w");
+
+  if (outfile == NULL)
+    {
+      meta_warning (_("Could not open history file '%s' for writing: %s\n"),
+                    history_file, g_strerror (errno));
+      goto out;
+    }
+
+  /* The file format is:
+   * <metacity_history>
+   *   <window id="bar" class="XTerm" name="xterm" title="/foo/bar" role="blah" type="normal" num="0">
+   *     <geometry x="100" y="100" width="200" height="200" gravity="northwest"/>
+   *   </window>
+   *   <window ....>
+   *   </window>
+   *   :
+   * </metacity_session>
+   *
+   * Note that attributes on <window> are the match info we use to
+   * see if the saved state applies to a restored window, and
+   * child elements are the saved state to be applied.
+   * 
+   */
+  
+  fprintf (outfile, "<metacity_history>\n");
+  
+  g_hash_table_foreach (window_history_list, save_history_entry, (gpointer) outfile);
+
+  fputs ("</metacity_history>\n", outfile);
+  
+ out:
+  if (outfile)
+    {
+      /* FIXME need a dialog for this */
+      if (ferror (outfile))
+        {
+          meta_warning (_("Error writing history file '%s': %s\n"),
+                        history_file, g_strerror (errno));
+        }
+      if (fclose (outfile))
+        {
+          meta_warning (_("Error closing history file '%s': %s\n"),
+                        history_file, g_strerror (errno));
+        }
+    }
+  
+  g_free (metacity_dir);
+}
+
+static void
+save_history_entry (gpointer key, gpointer value, gpointer userdata)
+{
+  char *res_class;
+  char *res_name;
+  char *role;
+  FILE *outfile = userdata;
+  MetaWindowHistoryInfo *window = value;
+  
+  /* client id, class, name, role are not expected to be
+   * in UTF-8 (I think they are in XPCS which is Latin-1?
+   * in practice they are always ascii though.)
+   */
+  
+  res_class = window->res_class ?
+    encode_text_as_utf8_markup (window->res_class) : NULL;
+  res_name = window->res_name ?
+    encode_text_as_utf8_markup (window->res_name) : NULL;
+  role = window->role ?
+    encode_text_as_utf8_markup (window->role) : NULL;
+  
+  fprintf (outfile,
+	   "  <window class=\"%s\" name=\"%s\" title=\"%s\" role=\"%s\" type=\"%s\" num=\"%d\">\n",
+	   res_class ? res_class : "",
+	   res_name ? res_name : "",
+	   window->title ? window->title : "",
+	   role ? role : "",
+	   window_type_to_string (window->type),
+	   window->num);
+
+  g_free (res_class);
+  g_free (res_name);
+  g_free (role);
+              
+  fprintf (outfile,
+	   "    <geometry x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" gravity=\"%s\"/>\n",
+	   window->rect.x, window->rect.y, window->rect.width, window->rect.height, "NorthWestGravity");
+  
+  fputs ("  </window>\n", outfile);
+
+}
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/history.h metacity-2.4.1-whistory/src/history.h
--- metacity-2.4.1/src/history.h	Wed Dec 31 19:00:00 1969
+++ metacity-2.4.1-whistory/src/history.h	Tue Nov 19 22:46:22 2002
@@ -0,0 +1,61 @@
+/* Metacity Window History */
+
+/* 
+ * Copyright (C) 2002 Benjamin Kahn 
+ * portions Copyright (C) 2001 Havoc Pennington
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_HISTORY_H
+#define META_HISTORY_H
+
+#include "window.h"
+
+typedef struct _MetaWindowHistoryInfo MetaWindowHistoryInfo;
+
+struct _MetaWindowHistoryInfo
+{
+  /* Fields we use to match against */
+
+  char *res_class;
+  char *res_name;
+  char *title;
+  char *role;
+  int   gravity;
+  int   num;
+  MetaWindowType type;
+
+  /* Information we restore */
+  /* width/height should be multiplied by resize inc and
+   * added to base size; position should be interpreted in
+   * light of gravity. This preserves semantics of the
+   * window size/pos, even if fonts/themes change, etc.
+   */
+  MetaRectangle rect;
+};
+
+const MetaWindowHistoryInfo* meta_history_lookup           (MetaWindow *window);
+const MetaWindowHistoryInfo* meta_history_lookup_key       (char       *key);
+void                         meta_history_update           (MetaWindow *window);
+int                          meta_history_find_next_window (MetaWindow *window);
+int                          meta_history_next_window      (MetaWindow *window);
+int                          meta_history_next_window_full (GList *windows, MetaWindow *window);
+
+void meta_history_init (void);
+void meta_history_shutdown (void);
+
+#endif /* META_HISTORY_H */
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/main.c metacity-2.4.1-whistory/src/main.c
--- metacity-2.4.1/src/main.c	Wed Jul 10 08:26:00 2002
+++ metacity-2.4.1-whistory/src/main.c	Tue Nov 19 22:46:41 2002
@@ -25,6 +25,7 @@
 #include "display.h"
 #include "errors.h"
 #include "ui.h"
+#include "history.h"
 #include "session.h"
 #include "prefs.h"
 
@@ -309,6 +310,9 @@
   if (!meta_display_open (NULL))
     meta_exit (META_EXIT_ERROR);
   
+  /* Load the window history so we can place windows. */
+  meta_history_init ();
+
   g_main_run (meta_main_loop);
 
   {
@@ -329,6 +333,7 @@
   }
 
   meta_session_shutdown ();
+  meta_history_shutdown ();
   
   if (meta_restart_after_quit)
     {
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/place.c metacity-2.4.1-whistory/src/place.c
--- metacity-2.4.1/src/place.c	Fri Jul 12 19:03:40 2002
+++ metacity-2.4.1-whistory/src/place.c	Tue Nov 19 22:47:19 2002
@@ -22,6 +22,7 @@
 #include <config.h>
 
 #include "place.h"
+#include "history.h"
 #include "workspace.h"
 #include "prefs.h"
 #include <gdk/gdkregion.h>
@@ -395,6 +396,89 @@
     ((rect->y + rect->height) < (xsi->y_origin + xsi->height));
 }
 
+/* Lookup the window position used before and try to use that
+ * again.  
+ * 
+ * We need to watch out for windows already placed in that
+ * position and fail if we find one.
+ */
+static gboolean
+find_last_placement (MetaWindow *window,
+		     MetaFrameGeometry *fgeom,
+		     /* visible windows on relevant workspaces */
+		     GList      *windows,
+		     int         x,
+		     int         y,
+		     int        *new_x,
+		     int        *new_y)
+{
+  MetaWindowHistoryInfo *old_place = NULL;
+  int place_x, place_y;
+  int x_threshold, y_threshold;
+  GList *tmp;
+
+  meta_history_next_window_full (windows, window);
+  old_place = meta_history_lookup (window);
+  
+  /* If we can't find any data, just return. */
+  if (!old_place) return FALSE;
+
+  place_x = old_place->rect.x;
+  place_y = old_place->rect.y;
+
+  /* Now we need to scan through all the windows looking 
+   * for those which are close to us, return FALSE if we
+   * find something like that.
+   */
+
+  if (fgeom)
+    {
+      x_threshold = MAX (fgeom->left_width, 15);
+      y_threshold = MAX (fgeom->top_height, 15);
+    }
+  else
+    {
+      x_threshold = 15;
+      y_threshold = 15;
+    }
+
+  tmp = windows;
+  while (tmp != NULL)
+    {
+      MetaWindow *w;
+      int wx, wy;
+
+      w = tmp->data;
+      
+      /* we want frame position, not window position */
+      if (w->frame)
+        {
+          wx = w->frame->rect.x;
+          wy = w->frame->rect.y;
+        }
+      else
+        {
+          wx = w->rect.x;
+          wy = w->rect.y;
+        }
+      
+      if (ABS (wx - place_x) <= x_threshold &&
+          ABS (wy - place_y) <= y_threshold)
+        {
+	  /* Oops.  Something is already there.  Abort! */
+	  return FALSE;
+	}
+      tmp = tmp->next;
+    }
+
+  *new_x = place_x;
+  *new_y = place_y;
+
+  /* FIXME: Should we change the size of the window as well? */
+
+  return TRUE;
+}
+
 /* Find the leftmost, then topmost, empty area on the workspace
  * that can contain the new window.
  *
@@ -719,7 +803,7 @@
       goto done;
     }
   
-  /* Find windows that matter (not minimized, on same workspace
+  /* Find windows that matter (on same workspace
    * as placed window, may be shaded - if shaded we pretend it isn't
    * for placement purposes)
    */
@@ -734,8 +818,7 @@
       {
         MetaWindow *w = tmp->data;
 
-        if (!w->minimized &&
-            w != window && 
+        if (w != window && 
             meta_window_shares_some_workspace (window, w))
           windows = g_list_prepend (windows, w);
 
@@ -750,6 +833,10 @@
   x = xi->x_origin;
   y = xi->y_origin;
 
+  /* If we already know where the user likes their window, use that. */
+  if (find_last_placement (window, fgeom, windows, x, y, &x, &y))
+    goto done;
+
   if (find_first_fit (window, fgeom, windows, x, y, &x, &y))
     goto done;
   
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/util.c metacity-2.4.1-whistory/src/util.c
--- metacity-2.4.1/src/util.c	Sat Aug 24 16:54:23 2002
+++ metacity-2.4.1-whistory/src/util.c	Fri Nov  8 16:08:38 2002
@@ -254,6 +254,8 @@
       return "XINERAMA";
     case META_DEBUG_KEYBINDINGS:
       return "KEYBINDINGS";
+    case META_DEBUG_HISTORY:
+      return "HISTORY";
     }
 
   return "Window manager";
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/util.h metacity-2.4.1-whistory/src/util.h
--- metacity-2.4.1/src/util.h	Mon Aug 12 17:32:12 2002
+++ metacity-2.4.1-whistory/src/util.h	Fri Nov  8 16:08:38 2002
@@ -58,7 +58,8 @@
   META_DEBUG_PLACEMENT    = 1 << 9,
   META_DEBUG_PING         = 1 << 10,
   META_DEBUG_XINERAMA     = 1 << 11,
-  META_DEBUG_KEYBINDINGS  = 1 << 12
+  META_DEBUG_KEYBINDINGS  = 1 << 12,
+  META_DEBUG_HISTORY      = 1 << 13
 
 } MetaDebugTopic;
 
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/window.c metacity-2.4.1-whistory/src/window.c
--- metacity-2.4.1/src/window.c	Tue Sep  3 22:44:52 2002
+++ metacity-2.4.1-whistory/src/window.c	Tue Nov 19 22:46:58 2002
@@ -29,6 +29,7 @@
 #include "keybindings.h"
 #include "ui.h"
 #include "place.h"
+#include "history.h"
 #include "session.h"
 #include "effects.h"
 #include "prefs.h"
@@ -394,6 +395,9 @@
   window->sm_client_id = NULL;
   window->wm_client_machine = NULL;
 
+  window->orig_name = NULL;
+  window->num = -1;
+
   window->net_wm_pid = -1;
   
   window->xtransient_for = None;
@@ -987,6 +991,7 @@
   g_free (window->role);
   g_free (window->res_class);
   g_free (window->res_name);
+  g_free (window->orig_name);
   g_free (window->title);
   g_free (window->icon_name);
   g_free (window->desc);
@@ -2416,11 +2421,14 @@
     {
       int newx, newy;
       meta_window_get_position (window, &newx, &newy);
+      meta_history_next_window (window);
+      meta_history_update (window);
       meta_topic (META_DEBUG_GEOMETRY,
                   "New size/position %d,%d %dx%d (user %d,%d %dx%d)\n",
                   newx, newy, window->rect.width, window->rect.height,
                   window->user_rect.x, window->user_rect.y,
                   window->user_rect.width, window->user_rect.height);
+
     }
   else
     {
@@ -2466,6 +2474,12 @@
                                     user_op ? META_USER_MOVE_RESIZE : 0,
                                     NorthWestGravity,
                                     x, y, w, h);
+
+  if (user_op) 
+    {
+      meta_history_next_window (window);
+      meta_history_update (window);
+    }
 }
 
 void
@@ -4567,6 +4581,12 @@
   if (ch.res_name)
     {
       window->res_name = g_strdup (ch.res_name);
+
+      /* This is used by the history system. */
+      if (window->orig_name == NULL)
+	{
+	  window->orig_name = g_strdup (ch.res_name);
+	}
       XFree (ch.res_name);
     }
 
diff -N -b -r -u --exclude metacity-properties.desktop --exclude inlinepixbufs.h --exclude .intltool-merge-cache --exclude metacity.spec --exclude intltool-merge --exclude intltool-update --exclude config.log --exclude intltool-extract --exclude Makefile.in metacity-2.4.1/src/window.h metacity-2.4.1-whistory/src/window.h
--- metacity-2.4.1/src/window.h	Mon Aug 12 17:32:13 2002
+++ metacity-2.4.1-whistory/src/window.h	Tue Nov 19 22:46:58 2002
@@ -80,6 +80,10 @@
   char *sm_client_id;
   char *wm_client_machine;
 
+  /* Needed for Window History Tracking */
+  char *orig_name;
+  int num;
+
   int net_wm_pid;
   
   Window xtransient_for;
