From 5e8ee554a218fcf0f7c7a26ed44d2607bdebe800 Mon Sep 17 00:00:00 2001
From: Michael Natterer <mitschel@cs.tu-berlin.de>
Date: Tue, 16 Mar 1999 20:14:07 +0000
Subject: [PATCH] This implements the rest of the unit system (unitrc loading
 and saving and

1999-03-16  Michael Natterer  <mitschel@cs.tu-berlin.de>

        This implements the rest of the unit system (unitrc loading and
        saving and full PDB interface)

        * Makefile.am
        * gimp.1
        * user_install
        * user_install.bat
        * unitrc: new file (default unit database) and some documentation

        * app/Makefile.am
        * app/gimpunit.c
        * app/gimpunit_cmds.h
        * app/unitrc.h: new files enabling the unit database and PDB
        access to the unit system

        * app/app_procs.c: parse and save unitrc
        * app/gimprc.[ch]: enable unit parsing. New function
        init_parse_buffers() to enable unitrc to be loaded before gimprc

        * app/gimage_cmds.[ch]: new PDB procedures which set/return an
        image's unit

        * app/install.c: mention unitrc installation

        * app/xcf.c: new xcf property for user defined units. An image's
        unit is saved as either an integer ID (built in units) or as
        a full unit definition without any ID

        * libgimp/Makefile.am: moved gimpunit.o from libgimpi.a to
        libgimp.a

        * libgimp/gimp.h
        * libgimp/gimpimage.c: get/set an image's unit with PDB calls

        * libgimp/gimpunit.h: this file is now the header for both
        app/gimpunit.c and libgimp/gimpunit.c

        * libgimp/gimpunit.c: does the unit calls as PDB calls now

        * libgimp/gimpunitmenu.[ch]: enable user unit functionality and a
        unit selection dialog

        * libgimp/gimpsizeentry.c: disble hilighting on focus_in_event and
        minor bugfixes

        * plug-ins/tiff/tiff.c: set image unit to "mm" if tiff unit is
        "cm", save "cm" if image unit is metric
---
 ChangeLog                         |  50 ++
 Makefile.am                       |   2 +
 app/Makefile.am                   |   3 +
 app/app_procs.c                   |   4 +
 app/core/gimpunit.c               | 966 ++++++++++++++++++++++++++++++
 app/core/gimpunit.h               |  25 +
 app/dialogs/user-install-dialog.c |  12 +
 app/gimage_cmds.c                 | 145 ++++-
 app/gimage_cmds.h                 |   2 +
 app/gimprc.c                      | 156 ++++-
 app/gimprc.h                      |   3 +
 app/gimpunit.c                    | 966 ++++++++++++++++++++++++++++++
 app/gimpunit_cmds.h               |  35 ++
 app/gui/user-install-dialog.c     |  12 +
 app/install.c                     |  12 +
 app/internal_procs.c              |  16 +
 app/unitrc.h                      |  25 +
 app/user_install.c                |  12 +
 app/xcf.c                         | 103 +++-
 app/xcf/xcf.c                     | 103 +++-
 data/misc/user_install            |   3 +
 data/misc/user_install.bat        |   1 +
 etc/unitrc                        |  55 ++
 gimp.1                            |   9 +
 libgimp/Makefile.am               |   4 +-
 libgimp/gimp.h                    |   4 +
 libgimp/gimpimage.c               |  41 ++
 libgimp/gimpimage_pdb.c           |  41 ++
 libgimp/gimpsizeentry.c           |   9 +-
 libgimp/gimpunit.c                | 277 ++++++---
 libgimp/gimpunit.h                |  69 ++-
 libgimp/gimpunit_pdb.c            | 277 ++++++---
 libgimp/gimpunitcache.c           | 277 ++++++---
 libgimp/gimpunitmenu.c            | 297 ++++++++-
 libgimp/gimpunitmenu.h            |  24 +-
 libgimpbase/gimpunit.h            |  69 ++-
 libgimpwidgets/gimpsizeentry.c    |   9 +-
 libgimpwidgets/gimpunitmenu.c     | 297 ++++++++-
 libgimpwidgets/gimpunitmenu.h     |  24 +-
 plug-ins/common/tiff.c            |  40 +-
 plug-ins/tiff/tiff.c              |  40 +-
 unitrc                            |  55 ++
 user_install                      |   3 +
 user_install.bat                  |   1 +
 44 files changed, 4186 insertions(+), 392 deletions(-)
 create mode 100644 app/core/gimpunit.c
 create mode 100644 app/core/gimpunit.h
 create mode 100644 app/gimpunit.c
 create mode 100644 app/gimpunit_cmds.h
 create mode 100644 app/unitrc.h
 create mode 100644 etc/unitrc
 create mode 100644 unitrc

diff --git a/ChangeLog b/ChangeLog
index 6a2a8110fd..51385343fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,53 @@
+1999-03-16  Michael Natterer  <mitschel@cs.tu-berlin.de>
+
+	This implements the rest of the unit system (unitrc loading and
+	saving and full PDB interface)
+	
+	* Makefile.am
+	* gimp.1
+	* user_install
+	* user_install.bat
+	* unitrc: new file (default unit database) and some documentation
+
+	* app/Makefile.am
+	* app/gimpunit.c
+	* app/gimpunit_cmds.h
+	* app/unitrc.h: new files enabling the unit database and PDB
+	access to the unit system
+
+	* app/app_procs.c: parse and save unitrc
+	* app/gimprc.[ch]: enable unit parsing. New function
+	init_parse_buffers() to enable unitrc to be loaded before gimprc
+
+	* app/gimage_cmds.[ch]: new PDB procedures which set/return an
+	image's unit 
+
+	* app/install.c: mention unitrc installation
+
+	* app/xcf.c: new xcf property for user defined units. An image's
+	unit is saved as either an integer ID (built in units) or as
+	a full unit definition without any ID
+
+	* libgimp/Makefile.am: moved gimpunit.o from libgimpi.a to
+	libgimp.a
+
+	* libgimp/gimp.h
+	* libgimp/gimpimage.c: get/set an image's unit with PDB calls
+
+	* libgimp/gimpunit.h: this file is now the header for both
+	app/gimpunit.c and libgimp/gimpunit.c
+
+	* libgimp/gimpunit.c: does the unit calls as PDB calls now
+
+	* libgimp/gimpunitmenu.[ch]: enable user unit functionality and a
+	unit selection dialog
+
+	* libgimp/gimpsizeentry.c: disble hilighting on focus_in_event and
+	minor bugfixes
+
+	* plug-ins/tiff/tiff.c: set image unit to "mm" if tiff unit is
+	"cm", save "cm" if image unit is metric
+
 Mon Mar 15 14:40:29 1999  Raph Levien  <raph@gimp.org>
 
 	* plug-ins/sharpen/sharpen.c: Fixed the algorithm so that it
diff --git a/Makefile.am b/Makefile.am
index bf88012c05..c796445d27 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,6 +34,7 @@ EXTRA_DIST = \
 	MAINTAINERS		 \
 	TODO			 \
 	gtkrc			 \
+	unitrc			 \
 	gimp_logo.ppm		 \
 	gimp_splash.ppm		 \
 	gimp1_1_splash.ppm	 \
@@ -54,6 +55,7 @@ gimpdata_DATA = \
 	gimprc_user		 \
 	gimprc.5		 \
 	gtkrc			 \
+	unitrc			 \
 	gimp_logo.ppm		 \
 	gimp_splash.ppm		 \
 	gimp1_1_splash.ppm	 \
diff --git a/app/Makefile.am b/app/Makefile.am
index 8845a9986f..49203048de 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -199,6 +199,7 @@ gimp_SOURCES = \
 	gimphistogram.c		\
 	gimphistogram.h		\
 	gimphistogramP.h	\
+	gimpunit.c		\
 	global_edit.c		\
 	global_edit.h		\
 	gradient.c		\
@@ -349,6 +350,8 @@ gimp_SOURCES = \
 	undo.h			\
 	undo_cmds.c		\
 	undo_cmds.h		\
+	unit_cmds.h		\
+	unitrc.h		\
 	wilber.h		\
 	vector2d.c		\
 	vector2d.h		\
diff --git a/app/app_procs.c b/app/app_procs.c
index cc6a47e9d0..ad0aa7bd61 100644
--- a/app/app_procs.c
+++ b/app/app_procs.c
@@ -77,6 +77,7 @@
 #include "tips_dialog.h"
 #include "tools.h"
 #include "undo.h"
+#include "unitrc.h"
 #include "xcf.h"
 #include "errors.h"
 #include "docindex.h"
@@ -507,6 +508,8 @@ app_init (void)
   procedural_db_register (&quit_proc);
 
   RESET_BAR();
+  init_parse_buffers ();
+  parse_unitrc ();         /*  this needs to be done before gimprc loading */
   parse_gimprc ();         /*  parse the local GIMP configuration file  */
   
   if (always_restore_session)
@@ -631,6 +634,7 @@ app_exit_finish (void)
   error_console_free ();
   menus_quit ();
   tile_swap_exit ();
+  save_unitrc ();
 
   /*  Things to do only if there is an interface  */
   if (no_interface == FALSE)
diff --git a/app/core/gimpunit.c b/app/core/gimpunit.c
new file mode 100644
index 0000000000..16e1cfd1c3
--- /dev/null
+++ b/app/core/gimpunit.c
@@ -0,0 +1,966 @@
+/* LIBGIMP - The GIMP Library                                                   
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
+ *
+ * gimpunit.c
+ * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* NOTE:
+ *
+ * one of our header files is in libgimp/ (see the note there)
+ */
+#include "libgimp/gimpunit.h"
+
+#include "unitrc.h"
+#include "gimpunit_cmds.h"
+
+#include "app_procs.h"
+#include "gimprc.h"
+#include "libgimp/gimpintl.h"
+#include "libgimp/gimpenv.h"
+
+/* internal structures */
+
+typedef struct {
+  guint    delete_on_exit;
+  float    factor;
+  gint     digits;
+  gchar   *identifier;
+  gchar   *symbol;
+  gchar   *abbreviation;
+  gchar   *singular;
+  gchar   *plural;
+} GimpUnitDef;
+
+/*  these are the built-in units
+ */
+static GimpUnitDef gimp_unit_defs[UNIT_END] =
+{
+  /* pseudo unit */
+  { FALSE,  0.0, 0, "pixels",      "px", "px", N_("pixel"),      N_("pixels") },
+
+  /* standard units */
+  { FALSE,  1.0, 2, "inches",      "''", "in", N_("inch"),       N_("inches") },
+  { FALSE, 25.4, 1, "millimeters", "mm", "mm", N_("millimeter"), N_("millimeters") },
+
+  /* professional units */
+  { FALSE, 72.0, 0, "points",      "pt", "pt", N_("point"),      N_("points") },
+  { FALSE,  6.0, 1, "picas",       "pc", "pc", N_("pica"),       N_("picas") },
+};
+
+static GSList* user_units = NULL;
+static gint    number_of_user_units = 0;
+
+static int success;
+
+/* private functions */
+
+static GimpUnitDef *
+gimp_unit_get_user_unit (GUnit unit)
+{
+  return g_slist_nth_data (user_units, unit - UNIT_END);
+}
+
+
+/* public functions */
+
+gint
+gimp_unit_get_number_of_units (void)
+{
+  return UNIT_END + number_of_user_units;
+}
+
+gint
+gimp_unit_get_number_of_built_in_units (void)
+{
+  return UNIT_END;
+}
+
+
+GUnit
+gimp_unit_new (gchar  *identifier,
+	       gfloat  factor,
+	       gint    digits,
+	       gchar  *symbol,
+	       gchar  *abbreviation,
+	       gchar  *singular,
+	       gchar  *plural)
+{
+  GimpUnitDef *user_unit;
+
+  user_unit = g_malloc (sizeof (GimpUnitDef));
+  user_unit->delete_on_exit = TRUE;
+  user_unit->factor = factor;
+  user_unit->digits = digits;
+  user_unit->identifier = g_strdup (identifier);
+  user_unit->symbol = g_strdup (symbol);
+  user_unit->abbreviation = g_strdup (abbreviation);
+  user_unit->singular = g_strdup (singular);
+  user_unit->plural = g_strdup (plural);
+
+  user_units = g_slist_append (user_units, user_unit);
+  number_of_user_units++;
+
+  return UNIT_END + number_of_user_units - 1;
+}
+
+
+guint
+gimp_unit_get_deletion_flag (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
+			 (unit < (UNIT_END + number_of_user_units)), FALSE);
+
+  if (unit < UNIT_END)
+    return FALSE;
+
+  return gimp_unit_get_user_unit (unit)->delete_on_exit;
+}
+
+void
+gimp_unit_set_deletion_flag (GUnit  unit,
+			     guint  deletion_flag)
+{
+  g_return_if_fail ( (unit >= UNIT_END) && 
+		     (unit < (UNIT_END + number_of_user_units)));
+
+  gimp_unit_get_user_unit (unit)->delete_on_exit =
+    deletion_flag ? TRUE : FALSE;
+}
+
+
+gfloat
+gimp_unit_get_factor (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].factor );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].factor;
+
+  return gimp_unit_get_user_unit (unit)->factor;
+}
+
+
+gint
+gimp_unit_get_digits (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].digits );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].digits;
+
+  return gimp_unit_get_user_unit (unit)->digits;
+}
+
+
+gchar * 
+gimp_unit_get_identifier (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].identifier );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].identifier;
+
+  return gimp_unit_get_user_unit (unit)->identifier;
+}
+
+
+gchar *
+gimp_unit_get_symbol (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].symbol );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].symbol;
+
+  return gimp_unit_get_user_unit (unit)->symbol;
+}
+
+
+gchar *
+gimp_unit_get_abbreviation (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].abbreviation );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].abbreviation;
+
+  return gimp_unit_get_user_unit (unit)->abbreviation;
+}
+
+
+gchar *
+gimp_unit_get_singular (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gettext(gimp_unit_defs[UNIT_INCH].singular) );
+
+  if (unit < UNIT_END)
+    return gettext (gimp_unit_defs[unit].singular);
+
+  return gimp_unit_get_user_unit (unit)->singular;
+}
+
+
+gchar *
+gimp_unit_get_plural (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gettext(gimp_unit_defs[UNIT_INCH].plural) );
+
+  if (unit < UNIT_END)
+    return gettext (gimp_unit_defs[unit].plural);
+
+  return gimp_unit_get_user_unit (unit)->plural;
+}
+
+
+/*  unitrc functions  **********/
+
+void parse_unitrc (void)
+{
+  char *filename;
+
+  filename = gimp_personal_rc_file ("unitrc");
+  app_init_update_status(NULL, filename, -1);
+  parse_gimprc_file (filename);
+  g_free (filename);
+}
+
+
+void save_unitrc (void)
+{
+  int i;
+  char *filename;
+  FILE *fp;
+
+  filename = gimp_personal_rc_file ("unitrc");
+
+  fp = fopen (filename, "w");
+  g_free (filename);
+  if (!fp)
+    return;
+
+  fprintf(fp, _("# GIMP unitrc\n"));
+  fprintf(fp, _("# This file contains your user unit database. You can\n"));
+  fprintf(fp, _("# modify this list with the unit editor. You are not\n"));
+  fprintf(fp, _("# supposed to edit it manually, but of course you can do.\n"));
+  fprintf(fp, _("# This file will be entirely rewritten every time you\n")); 
+  fprintf(fp, _("# quit the gimp.\n\n"));
+  
+  /* save window geometries */
+  for (i = gimp_unit_get_number_of_built_in_units();
+       i < gimp_unit_get_number_of_units ();
+       i++)
+    if (gimp_unit_get_deletion_flag (i) == FALSE)
+      {
+	fprintf (fp,"(unit-info \"%s\"\n", gimp_unit_get_identifier (i));
+	fprintf (fp,"   (factor %f)\n", gimp_unit_get_factor (i));
+	fprintf (fp,"   (digits %d)\n", gimp_unit_get_digits (i));
+	fprintf (fp,"   (symbol \"%s\")\n", gimp_unit_get_symbol (i));
+	fprintf (fp,"   (abbreviation \"%s\")\n", gimp_unit_get_abbreviation (i));
+	fprintf (fp,"   (singular \"%s\")\n", gimp_unit_get_singular (i));
+	fprintf (fp,"   (plural \"%s\"))\n\n", gimp_unit_get_plural (i));
+      }
+  
+  fclose (fp);
+}
+
+
+/*  PDB stuff  **********/
+
+/********************************
+ *  GIMP_UNIT_GET_NUMBER_OF_UNITS
+ */
+
+static Argument *
+gimp_unit_get_number_of_units_invoker (Argument *args)
+{
+  Argument *return_args;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_number_of_units_proc,
+					 TRUE);
+
+  return_args[1].value.pdb_int = gimp_unit_get_number_of_units ();
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_number_of_units_out_args[] =
+{
+  { PDB_INT32,
+    "#units",
+    "the number of units"
+  }
+};
+
+ProcRecord gimp_unit_get_number_of_units_proc =
+{
+  "gimp_unit_get_number_of_units",
+  "Returns the number of units",
+  "This procedure returns the number of defined units.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  0, NULL,
+  1, gimp_unit_get_number_of_units_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_number_of_units_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_NEW
+ */
+
+static Argument *
+gimp_unit_new_invoker (Argument *args)
+{
+  Argument *return_args;
+
+  return_args= procedural_db_return_args(&gimp_unit_new_proc, TRUE);
+
+  return_args[1].value.pdb_int =
+    gimp_unit_new (g_strdup (args[0].value.pdb_pointer),
+		   args[1].value.pdb_float,
+		   args[2].value.pdb_int,
+		   g_strdup (args[3].value.pdb_pointer),
+		   g_strdup (args[4].value.pdb_pointer),
+		   g_strdup (args[5].value.pdb_pointer),
+		   g_strdup (args[6].value.pdb_pointer));
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_new_args[] =
+{
+  { PDB_STRING,
+    "identifier",
+    "the new unit's identifier"
+  },
+  { PDB_FLOAT,
+    "factor",
+    "the new unit's factor"
+  },
+  { PDB_INT32,
+    "digits",
+    "the new unit's digits"
+  },
+  { PDB_STRING,
+    "symbol",
+    "the new unit's symbol"
+  },
+  { PDB_STRING,
+    "abbreviation",
+    "the new unit's abbreviation"
+  },
+  { PDB_STRING,
+    "singular",
+    "the new unit's singular form"
+  },
+  { PDB_STRING,
+    "plural",
+    "the new unit's plural form"
+  }
+};
+
+ProcArg gimp_unit_new_out_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the new unit's ID",
+  }
+};
+
+ProcRecord gimp_unit_new_proc =
+{
+  "gimp_unit_new",
+  "Creates a new unit and returns it's integer ID",
+  "This procedure creates a new unit and returns it's integer ID. Note that the new unit will have it's deletion flag set to TRUE, so you will have to set it to FALSE with gimp_unit_set_deletion_flag to make it persistent.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  7, gimp_unit_new_args,
+  1, gimp_unit_new_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_new_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_DELETION_FLAG
+ */
+
+static Argument *
+gimp_unit_get_deletion_flag_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_deletion_flag_proc,
+					 success);
+
+  if (success)
+    return_args[1].value.pdb_int = gimp_unit_get_deletion_flag (unit);
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_deletion_flag_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_deletion_flag_out_args[] =
+{
+  { PDB_INT32,
+    "boolean",
+    "the unit's deletion flag",
+  }
+};
+
+ProcRecord gimp_unit_get_deletion_flag_proc =
+{
+  "gimp_unit_get_deletion_flag",
+  "Returns the deletion flag of the unit",
+  "This procedure returns the deletion flag of the unit. If this value is TRUE the unit's definition will not be saved in the user's unitrc file on gimp exit.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_deletion_flag_args,
+  1, gimp_unit_get_deletion_flag_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_deletion_flag_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_SET_DELETION_FLAG
+ */
+
+static Argument *
+gimp_unit_set_deletion_flag_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_END) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+  else
+    gimp_unit_set_deletion_flag (unit, args[1].value.pdb_int);
+
+  return_args= procedural_db_return_args(&gimp_unit_set_deletion_flag_proc,
+					 success);
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_set_deletion_flag_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  },
+  { PDB_INT32,
+    "boolean",
+    "the new deletion flag of the unit",
+  }
+};
+
+ProcRecord gimp_unit_set_deletion_flag_proc =
+{
+  "gimp_unit_set_deletion_flag",
+  "Sets the deletion flag of a unit",
+  "This procedure sets the unit's deletion flag. If the deletion flag of a unit is TRUE on gimp exit, this unit's definition will not be saved in the user's unitrc.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  2, gimp_unit_set_deletion_flag_args,
+  0, NULL,
+
+  /*  Exec method  */
+  { { gimp_unit_set_deletion_flag_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_IDENTIFIER
+ */
+
+static Argument *
+gimp_unit_get_identifier_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_identifier_proc,
+					 success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_identifier (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_identifier_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_identifier_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's textual identifier",
+  }
+};
+
+ProcRecord gimp_unit_get_identifier_proc =
+{
+  "gimp_unit_get_identifier",
+  "Returns the identifier of the unit",
+  "This procedure returns the textual identifier of the unit. For built-in units it will be the english singular form of the unit's name. For user-defined units this should equal to the singular form.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_identifier_args,
+  1, gimp_unit_get_identifier_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_identifier_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_FACTOR
+ */
+
+static Argument *
+gimp_unit_get_factor_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_INCH) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_factor_proc, success);
+
+  return_args[1].value.pdb_float =
+    success ? gimp_unit_get_factor (unit) : 1.0;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_factor_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_factor_out_args[] =
+{
+  { PDB_FLOAT,
+    "float",
+    "the unit's factor",
+  }
+};
+
+ProcRecord gimp_unit_get_factor_proc =
+{
+  "gimp_unit_get_factor",
+  "Returns the factor of the unit",
+  "This procedure returns the unit's factor which indicates how many units make up an inch. Note that asking for the factor of \"pixels\" will produce an error.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_factor_args,
+  1, gimp_unit_get_factor_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_factor_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_DIGITS
+ */
+
+static Argument *
+gimp_unit_get_digits_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_INCH) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_digits_proc, success);
+
+  return_args[1].value.pdb_int =
+    success ? gimp_unit_get_digits (unit) : 0;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_digits_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_digits_out_args[] =
+{
+  { PDB_INT32,
+    "digits",
+    "the unit's number if digits",
+  }
+};
+
+ProcRecord gimp_unit_get_digits_proc =
+{
+  "gimp_unit_get_digits",
+  "Returns the digits of the unit",
+  "This procedure returns the number of digits you should provide in input or output functions to get approximately the same accuracy as with two digits and inches. Note that asking for the digits of \"pixels\" will produce an error.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_digits_args,
+  1, gimp_unit_get_digits_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_digits_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_SYMBOL
+ */
+
+static Argument *
+gimp_unit_get_symbol_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_symbol_proc, success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_symbol (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_symbol_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_symbol_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's symbol",
+  }
+};
+
+ProcRecord gimp_unit_get_symbol_proc =
+{
+  "gimp_unit_get_symbol",
+  "Returns the symbol of the unit",
+  "This procedure returns the symbol of the unit (\"''\" for inches).",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_symbol_args,
+  1, gimp_unit_get_symbol_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_symbol_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_ABBREVIATION
+ */
+
+static Argument *
+gimp_unit_get_abbreviation_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_abbreviation_proc,
+					 success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_abbreviation (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_abbreviation_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_abbreviation_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's abbreviation",
+  }
+};
+
+ProcRecord gimp_unit_get_abbreviation_proc =
+{
+  "gimp_unit_get_abbreviation",
+  "Returns the abbreviation of the unit",
+  "This procedure returns the abbreviation of the unit (\"in\" for inches).",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_abbreviation_args,
+  1, gimp_unit_get_abbreviation_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_abbreviation_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_SINGULAR
+ */
+
+static Argument *
+gimp_unit_get_singular_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_singular_proc, success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_singular (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_singular_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_singular_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's singular form",
+  }
+};
+
+ProcRecord gimp_unit_get_singular_proc =
+{
+  "gimp_unit_get_singular",
+  "Returns the singular for of the unit",
+  "This procedure returns the singular form of the unit.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_singular_args,
+  1, gimp_unit_get_singular_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_singular_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_PLURAL
+ */
+
+static Argument *
+gimp_unit_get_plural_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_plural_proc, success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_plural (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_plural_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_plural_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's plural form",
+  }
+};
+
+ProcRecord gimp_unit_get_plural_proc =
+{
+  "gimp_unit_get_plural",
+  "Returns the plural form of the unit",
+  "This procedure returns the plural form of the unit.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_plural_args,
+  1, gimp_unit_get_plural_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_plural_invoker } },
+};
diff --git a/app/core/gimpunit.h b/app/core/gimpunit.h
new file mode 100644
index 0000000000..1afe409b0e
--- /dev/null
+++ b/app/core/gimpunit.h
@@ -0,0 +1,25 @@
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 __UNITRC_H__
+#define __UNITRC_H__
+
+void parse_unitrc (void);
+void save_unitrc (void);
+
+#endif  /*  __UNITRC_H__  */
diff --git a/app/dialogs/user-install-dialog.c b/app/dialogs/user-install-dialog.c
index 72565e71cf..5c4b9bbc22 100644
--- a/app/dialogs/user-install-dialog.c
+++ b/app/dialogs/user-install-dialog.c
@@ -202,6 +202,18 @@ install_help (InstallCallback callback)
 		   _("\t\tPaths to search for brushes, palettes, gradients\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
 		   _("\t\tpatterns, plug-ins and modules are also configured here.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
+		   _("unitrc\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tThe unitrc is used to store your user units database.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tYou can define additional units and use them just\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tlike you use the built-in units inches, millimeters,\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tpoints and picas. This file is overwritten each time\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tyou quit the GIMP.\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
 		   _("pluginrc\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
diff --git a/app/gimage_cmds.c b/app/gimage_cmds.c
index 58a39244e7..c66d0ba160 100644
--- a/app/gimage_cmds.c
+++ b/app/gimage_cmds.c
@@ -3061,6 +3061,149 @@ ProcRecord gimage_set_resolution_proc =
 
 
 
+/***************************/
+/*  GIMAGE_GET_UNIT  */
+
+static Argument *
+gimage_get_unit_invoker (Argument *args)
+{
+  GImage *gimage;
+  GUnit unit;
+  Argument *return_args;
+
+  unit = UNIT_PIXEL;  /*  pixel is not a valid image unit  */
+
+  success = TRUE;
+  if (success)
+    {
+      int_value = args[0].value.pdb_int;
+      if ((gimage = gimage_get_ID (int_value))) {
+	unit = gimage->unit;
+      } else {
+	success = FALSE;
+      }
+    }
+
+  return_args= procedural_db_return_args(&gimage_get_unit_proc, success);
+
+  if (success)
+  {
+    return_args[1].value.pdb_int = unit;
+  }
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimage_get_unit_args[] =
+{
+  { PDB_IMAGE,
+    "image",
+    "the image"
+  }
+};
+
+ProcArg gimage_get_unit_out_args[] =
+{
+  { PDB_INT32,
+    "unit",
+    "the unit ID of the image's display unit",
+  }
+};
+
+ProcRecord gimage_get_unit_proc =
+{
+  "gimp_image_get_unit",
+  "Returns the unit of the image",
+  "This procedure returns the unit ID of the image's unit. This value is independent of any of the layers in this image. A return value of 0 means the image was invalid. See the gimp_unit_* procedure definitions for the valid range of unit IDs and a description of the unit system.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input arguments  */
+  1,
+  gimage_get_unit_args,
+
+  /*  Output arguments  */
+  1,
+  gimage_get_unit_out_args,
+
+  /*  Exec method  */
+  { { gimage_get_unit_invoker } },
+};
+
+
+/***************************/
+/*  GIMAGE_SET_UNIT  */
+
+static Argument *
+gimage_set_unit_invoker (Argument *args)
+{
+  GImage *gimage;
+  Argument *return_args;
+
+  GUnit unit;
+
+  success = TRUE;
+  if (success)
+    {
+      int_value = args[0].value.pdb_int;
+      if (!(gimage = gimage_get_ID (int_value)))
+	success = FALSE;
+    }
+
+  if (success)
+    {
+      unit = args[1].value.pdb_int;
+      if ((unit < UNIT_INCH) || (unit >= gimp_unit_get_number_of_units ()))
+	success = FALSE;
+      else
+	gimage->unit = unit;
+    }
+
+  return_args= procedural_db_return_args(&gimage_set_unit_proc, success);
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimage_set_unit_args[] =
+{
+  { PDB_IMAGE,
+    "image",
+    "the image"
+  },
+  { PDB_INT32,
+    "unit",
+    "unit ID of the image's new unit",
+  }
+};
+
+ProcRecord gimage_set_unit_proc =
+{
+  "gimp_image_set_unit",
+  "Sets the unit of the image",
+  "This procedure sets the image's unit by it's unit ID. This value is independent of any of the layers in this image. No scaling or resizing is performed. See the gimp_unit_* procedure definitions for the valid range of unit IDs and a description of the unit system.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input arguments  */
+  2,
+  gimage_set_unit_args,
+
+  /*  Output arguments  */
+  0,
+  NULL,
+
+  /*  Exec method  */
+  { { gimage_set_unit_invoker } },
+};
+
+
+
 /******************/
 /*  GIMAGE_WIDTH  */
 
@@ -5072,5 +5215,3 @@ gimp_image_get_channel_by_tattoo_invoker (Argument *args)
 
   return return_args;
 }
-
-
diff --git a/app/gimage_cmds.h b/app/gimage_cmds.h
index 171e02596d..346afd13a8 100644
--- a/app/gimage_cmds.h
+++ b/app/gimage_cmds.h
@@ -62,6 +62,8 @@ extern ProcRecord gimage_get_filename_proc;
 extern ProcRecord gimage_set_filename_proc;
 extern ProcRecord gimage_get_resolution_proc;
 extern ProcRecord gimage_set_resolution_proc;
+extern ProcRecord gimage_get_unit_proc;
+extern ProcRecord gimage_set_unit_proc;
 extern ProcRecord gimage_width_proc;
 extern ProcRecord gimage_height_proc;
 extern ProcRecord gimage_get_cmap_proc;
diff --git a/app/gimprc.c b/app/gimprc.c
index 15bfffb461..fc0a150a0b 100644
--- a/app/gimprc.c
+++ b/app/gimprc.c
@@ -64,12 +64,13 @@ typedef enum {
   TT_IMAGETYPE,
   TT_XCOLORCUBE,
   TT_XPREVSIZE,
-  TT_XRULERUNIT,
+  TT_XUNIT,
   TT_XPLUGIN,
   TT_XPLUGINDEF,
   TT_XMENUPATH,
   TT_XDEVICE,
-  TT_XSESSIONINFO
+  TT_XSESSIONINFO,
+  TT_XUNITINFO
 } TokenType;
 
 typedef struct _ParseFunc ParseFunc;
@@ -165,6 +166,7 @@ static int parse_plug_in_def (gpointer val1p, gpointer val2p);
 static int parse_device (gpointer val1p, gpointer val2p);
 static int parse_menu_path (gpointer val1p, gpointer val2p);
 static int parse_session_info (gpointer val1p, gpointer val2p);
+static int parse_unit_info (gpointer val1p, gpointer val2p);
 
 static int parse_proc_def (PlugInProcDef **proc_def);
 static int parse_proc_arg (ProcArg *arg);
@@ -242,7 +244,7 @@ static ParseFunc funcs[] =
   { "dont-show-rulers",      TT_BOOLEAN,    NULL, &show_rulers },
   { "show-statusbar",        TT_BOOLEAN,    &show_statusbar, NULL },
   { "dont-show-statusbar",   TT_BOOLEAN,    NULL, &show_statusbar },
-  { "default-units",         TT_XRULERUNIT, &default_units, NULL },
+  { "default-units",         TT_XUNIT,      &default_units, NULL },
   { "auto-save",             TT_BOOLEAN,    &auto_save, NULL },
   { "dont-auto-save",        TT_BOOLEAN,    NULL, &auto_save },
   { "cubic-interpolation",   TT_BOOLEAN,    &cubic_interpolation, NULL },
@@ -262,12 +264,13 @@ static ParseFunc funcs[] =
   { "default-image-type",    TT_IMAGETYPE,  &default_type, NULL },
   { "default-xresolution",   TT_FLOAT,      &default_xresolution, NULL },
   { "default-yresolution",   TT_FLOAT,      &default_yresolution, NULL },
-  { "default-resolution-units", TT_XRULERUNIT, &default_resolution_units, NULL },
+  { "default-resolution-units", TT_XUNIT,   &default_resolution_units, NULL },
   { "plug-in",               TT_XPLUGIN,    NULL, NULL },
   { "plug-in-def",           TT_XPLUGINDEF, NULL, NULL },
   { "menu-path",             TT_XMENUPATH,  NULL, NULL },
   { "device",                TT_XDEVICE,    NULL, NULL },
-  { "session-info",          TT_XSESSIONINFO, NULL, NULL},
+  { "session-info",          TT_XSESSIONINFO, NULL, NULL },
+  { "unit-info",             TT_XUNITINFO,  NULL, NULL },
   { "monitor-xresolution",   TT_FLOAT,      &monitor_xres, NULL },
   { "monitor-yresolution",   TT_FLOAT,      &monitor_yres, NULL },
   { "num-processors",        TT_INT,        &num_processors, NULL },
@@ -307,6 +310,15 @@ gimp_system_rc_file ()
   return value;
 }
 
+void
+init_parse_buffers ()
+{
+  parse_info.buffer = g_new (char, 4096);
+  parse_info.tokenbuf = parse_info.buffer + 2048;
+  parse_info.buffer_size = 2048;
+  parse_info.tokenbuf_size = 2048;
+}
+
 void
 parse_gimprc ()
 {
@@ -314,11 +326,6 @@ parse_gimprc ()
   char filename[MAXPATHLEN];
   char *gimp_dir;
 
-  parse_info.buffer = g_new (char, 4096);
-  parse_info.tokenbuf = parse_info.buffer + 2048;
-  parse_info.buffer_size = 2048;
-  parse_info.tokenbuf_size = 2048;
-
   gimp_dir = gimp_directory ();
   add_gimp_directory_token (gimp_dir);
 
@@ -598,7 +605,7 @@ parse_statement ()
 	  return parse_color_cube (funcs[i].val1p, funcs[i].val2p);
 	case TT_XPREVSIZE:
 	  return parse_preview_size (funcs[i].val1p, funcs[i].val2p);
-	case TT_XRULERUNIT:
+	case TT_XUNIT:
 	  return parse_units (funcs[i].val1p, funcs[i].val2p);
 	case TT_XPLUGIN:
 	  return parse_plug_in (funcs[i].val1p, funcs[i].val2p);
@@ -610,6 +617,8 @@ parse_statement ()
 	  return parse_device (funcs[i].val1p, funcs[i].val2p);
 	case TT_XSESSIONINFO:
 	  return parse_session_info (funcs[i].val1p, funcs[i].val2p);
+	case TT_XUNITINFO:
+	  return parse_unit_info (funcs[i].val1p, funcs[i].val2p);
 	}
 
   return parse_unknown (token_sym);
@@ -1879,6 +1888,128 @@ parse_session_info (gpointer val1p,
   return OK;
 }
 
+static int
+parse_unit_info (gpointer val1p, 
+		 gpointer val2p)
+{
+  int token;
+
+  GUnit  unit;
+
+  gchar *identifier   = NULL;
+  float  factor       = 1.0;
+  int    digits       = 2.0;
+  gchar *symbol       = NULL;
+  gchar *abbreviation = NULL;
+  gchar *singular     = NULL;
+  gchar *plural       = NULL;
+
+  token = peek_next_token ();
+  if (!token || (token != TOKEN_STRING))
+    return ERROR;
+  token = get_next_token ();
+  identifier = g_strdup (token_str);
+
+  /* Parse options for unit info */
+
+  while ( peek_next_token () == TOKEN_LEFT_PAREN )
+    {
+      token = get_next_token ();
+
+      token = peek_next_token ();
+      if (!token || (token != TOKEN_SYMBOL))
+	goto parse_unit_info_error_label;
+      token = get_next_token ();
+
+      if (!strcmp ("factor", token_sym))
+	{
+	  token = peek_next_token ();
+	  if (!token || (token != TOKEN_NUMBER))
+	    goto parse_unit_info_error_label;
+	  token = get_next_token ();
+	  factor = token_num;
+	}
+      else if (!strcmp ("digits", token_sym))
+	{
+	  token = peek_next_token ();
+	  if (!token || (token != TOKEN_NUMBER))
+	    goto parse_unit_info_error_label;
+	  token = get_next_token ();
+	  digits = token_int;
+	}
+      else if (!strcmp ("symbol", token_sym))
+	{
+	  token = peek_next_token ();
+	  if (!token || (token != TOKEN_STRING))
+	    goto parse_unit_info_error_label;
+	  token = get_next_token ();
+	  symbol = g_strdup (token_str);
+	}
+      else if (!strcmp ("abbreviation", token_sym))
+	{
+	  token = peek_next_token ();
+	  if (!token || (token != TOKEN_STRING))
+	    goto parse_unit_info_error_label;
+	  token = get_next_token ();
+	  abbreviation = g_strdup (token_str);
+	}
+      else if (!strcmp ("singular", token_sym))
+	{
+	  token = peek_next_token ();
+	  if (!token || (token != TOKEN_STRING))
+	    goto parse_unit_info_error_label;
+	  token = get_next_token ();
+	  singular = g_strdup (token_str);
+	}
+      else if (!strcmp ("plural", token_sym))
+	{
+	  token = peek_next_token ();
+	  if (!token || (token != TOKEN_STRING))
+	    goto parse_unit_info_error_label;
+	  token = get_next_token ();
+	  plural = g_strdup (token_str);
+	}
+      else
+	goto parse_unit_info_error_label;
+      
+      token = peek_next_token ();
+      if (!token || (token != TOKEN_RIGHT_PAREN))
+	goto parse_unit_info_error_label;
+      token = get_next_token ();
+    }
+
+  if (!token || (token != TOKEN_RIGHT_PAREN))
+    goto parse_unit_info_error_label;
+  token = get_next_token ();
+
+  unit = gimp_unit_new (identifier, factor, digits,
+			symbol, abbreviation, singular, plural);
+  /*  make the unit definition persistent  */
+  gimp_unit_set_deletion_flag (unit, FALSE);
+
+  g_free (identifier);
+  g_free (symbol);
+  g_free (abbreviation);
+  g_free (singular);
+  g_free (plural);
+
+  return OK;
+
+ parse_unit_info_error_label:
+  if (identifier)
+    g_free (identifier);
+  if (symbol)
+    g_free (symbol);
+  if (abbreviation)
+    g_free (abbreviation);
+  if (singular)
+    g_free (singular);
+  if (plural)
+    g_free (plural);
+
+  return ERROR;
+}
+
 static int
 parse_unknown (char *token_sym)
 {
@@ -1963,13 +2094,14 @@ value_to_str (char *name)
 	  return color_cube_to_str (funcs[i].val1p, funcs[i].val2p);
 	case TT_XPREVSIZE:
 	  return preview_size_to_str (funcs[i].val1p, funcs[i].val2p);
-	case TT_XRULERUNIT:
+	case TT_XUNIT:
 	  return units_to_str (funcs[i].val1p, funcs[i].val2p);
 	case TT_XPLUGIN:
 	case TT_XPLUGINDEF:
 	case TT_XMENUPATH:
 	case TT_XDEVICE:
 	case TT_XSESSIONINFO:
+	case TT_XUNITINFO:
 	  return NULL;
 	}
   return NULL;
diff --git a/app/gimprc.h b/app/gimprc.h
index 9907c9bf15..5bc7abd7fb 100644
--- a/app/gimprc.h
+++ b/app/gimprc.h
@@ -77,6 +77,9 @@ extern char *    image_title_format;
 
 
 /*  function prototypes  */
+void    init_parse_buffers (); /* this has to be called before any file
+				* is parsed
+				*/
 void    parse_gimprc (void);
 void    parse_gimprc_file (char *filename);
 void    save_gimprc (GList **updated_options, GList **conflicting_options);
diff --git a/app/gimpunit.c b/app/gimpunit.c
new file mode 100644
index 0000000000..16e1cfd1c3
--- /dev/null
+++ b/app/gimpunit.c
@@ -0,0 +1,966 @@
+/* LIBGIMP - The GIMP Library                                                   
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
+ *
+ * gimpunit.c
+ * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* NOTE:
+ *
+ * one of our header files is in libgimp/ (see the note there)
+ */
+#include "libgimp/gimpunit.h"
+
+#include "unitrc.h"
+#include "gimpunit_cmds.h"
+
+#include "app_procs.h"
+#include "gimprc.h"
+#include "libgimp/gimpintl.h"
+#include "libgimp/gimpenv.h"
+
+/* internal structures */
+
+typedef struct {
+  guint    delete_on_exit;
+  float    factor;
+  gint     digits;
+  gchar   *identifier;
+  gchar   *symbol;
+  gchar   *abbreviation;
+  gchar   *singular;
+  gchar   *plural;
+} GimpUnitDef;
+
+/*  these are the built-in units
+ */
+static GimpUnitDef gimp_unit_defs[UNIT_END] =
+{
+  /* pseudo unit */
+  { FALSE,  0.0, 0, "pixels",      "px", "px", N_("pixel"),      N_("pixels") },
+
+  /* standard units */
+  { FALSE,  1.0, 2, "inches",      "''", "in", N_("inch"),       N_("inches") },
+  { FALSE, 25.4, 1, "millimeters", "mm", "mm", N_("millimeter"), N_("millimeters") },
+
+  /* professional units */
+  { FALSE, 72.0, 0, "points",      "pt", "pt", N_("point"),      N_("points") },
+  { FALSE,  6.0, 1, "picas",       "pc", "pc", N_("pica"),       N_("picas") },
+};
+
+static GSList* user_units = NULL;
+static gint    number_of_user_units = 0;
+
+static int success;
+
+/* private functions */
+
+static GimpUnitDef *
+gimp_unit_get_user_unit (GUnit unit)
+{
+  return g_slist_nth_data (user_units, unit - UNIT_END);
+}
+
+
+/* public functions */
+
+gint
+gimp_unit_get_number_of_units (void)
+{
+  return UNIT_END + number_of_user_units;
+}
+
+gint
+gimp_unit_get_number_of_built_in_units (void)
+{
+  return UNIT_END;
+}
+
+
+GUnit
+gimp_unit_new (gchar  *identifier,
+	       gfloat  factor,
+	       gint    digits,
+	       gchar  *symbol,
+	       gchar  *abbreviation,
+	       gchar  *singular,
+	       gchar  *plural)
+{
+  GimpUnitDef *user_unit;
+
+  user_unit = g_malloc (sizeof (GimpUnitDef));
+  user_unit->delete_on_exit = TRUE;
+  user_unit->factor = factor;
+  user_unit->digits = digits;
+  user_unit->identifier = g_strdup (identifier);
+  user_unit->symbol = g_strdup (symbol);
+  user_unit->abbreviation = g_strdup (abbreviation);
+  user_unit->singular = g_strdup (singular);
+  user_unit->plural = g_strdup (plural);
+
+  user_units = g_slist_append (user_units, user_unit);
+  number_of_user_units++;
+
+  return UNIT_END + number_of_user_units - 1;
+}
+
+
+guint
+gimp_unit_get_deletion_flag (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
+			 (unit < (UNIT_END + number_of_user_units)), FALSE);
+
+  if (unit < UNIT_END)
+    return FALSE;
+
+  return gimp_unit_get_user_unit (unit)->delete_on_exit;
+}
+
+void
+gimp_unit_set_deletion_flag (GUnit  unit,
+			     guint  deletion_flag)
+{
+  g_return_if_fail ( (unit >= UNIT_END) && 
+		     (unit < (UNIT_END + number_of_user_units)));
+
+  gimp_unit_get_user_unit (unit)->delete_on_exit =
+    deletion_flag ? TRUE : FALSE;
+}
+
+
+gfloat
+gimp_unit_get_factor (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].factor );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].factor;
+
+  return gimp_unit_get_user_unit (unit)->factor;
+}
+
+
+gint
+gimp_unit_get_digits (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].digits );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].digits;
+
+  return gimp_unit_get_user_unit (unit)->digits;
+}
+
+
+gchar * 
+gimp_unit_get_identifier (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].identifier );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].identifier;
+
+  return gimp_unit_get_user_unit (unit)->identifier;
+}
+
+
+gchar *
+gimp_unit_get_symbol (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].symbol );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].symbol;
+
+  return gimp_unit_get_user_unit (unit)->symbol;
+}
+
+
+gchar *
+gimp_unit_get_abbreviation (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gimp_unit_defs[UNIT_INCH].abbreviation );
+
+  if (unit < UNIT_END)
+    return gimp_unit_defs[unit].abbreviation;
+
+  return gimp_unit_get_user_unit (unit)->abbreviation;
+}
+
+
+gchar *
+gimp_unit_get_singular (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gettext(gimp_unit_defs[UNIT_INCH].singular) );
+
+  if (unit < UNIT_END)
+    return gettext (gimp_unit_defs[unit].singular);
+
+  return gimp_unit_get_user_unit (unit)->singular;
+}
+
+
+gchar *
+gimp_unit_get_plural (GUnit unit)
+{
+  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
+			 (unit < (UNIT_END + number_of_user_units)),
+			 gettext(gimp_unit_defs[UNIT_INCH].plural) );
+
+  if (unit < UNIT_END)
+    return gettext (gimp_unit_defs[unit].plural);
+
+  return gimp_unit_get_user_unit (unit)->plural;
+}
+
+
+/*  unitrc functions  **********/
+
+void parse_unitrc (void)
+{
+  char *filename;
+
+  filename = gimp_personal_rc_file ("unitrc");
+  app_init_update_status(NULL, filename, -1);
+  parse_gimprc_file (filename);
+  g_free (filename);
+}
+
+
+void save_unitrc (void)
+{
+  int i;
+  char *filename;
+  FILE *fp;
+
+  filename = gimp_personal_rc_file ("unitrc");
+
+  fp = fopen (filename, "w");
+  g_free (filename);
+  if (!fp)
+    return;
+
+  fprintf(fp, _("# GIMP unitrc\n"));
+  fprintf(fp, _("# This file contains your user unit database. You can\n"));
+  fprintf(fp, _("# modify this list with the unit editor. You are not\n"));
+  fprintf(fp, _("# supposed to edit it manually, but of course you can do.\n"));
+  fprintf(fp, _("# This file will be entirely rewritten every time you\n")); 
+  fprintf(fp, _("# quit the gimp.\n\n"));
+  
+  /* save window geometries */
+  for (i = gimp_unit_get_number_of_built_in_units();
+       i < gimp_unit_get_number_of_units ();
+       i++)
+    if (gimp_unit_get_deletion_flag (i) == FALSE)
+      {
+	fprintf (fp,"(unit-info \"%s\"\n", gimp_unit_get_identifier (i));
+	fprintf (fp,"   (factor %f)\n", gimp_unit_get_factor (i));
+	fprintf (fp,"   (digits %d)\n", gimp_unit_get_digits (i));
+	fprintf (fp,"   (symbol \"%s\")\n", gimp_unit_get_symbol (i));
+	fprintf (fp,"   (abbreviation \"%s\")\n", gimp_unit_get_abbreviation (i));
+	fprintf (fp,"   (singular \"%s\")\n", gimp_unit_get_singular (i));
+	fprintf (fp,"   (plural \"%s\"))\n\n", gimp_unit_get_plural (i));
+      }
+  
+  fclose (fp);
+}
+
+
+/*  PDB stuff  **********/
+
+/********************************
+ *  GIMP_UNIT_GET_NUMBER_OF_UNITS
+ */
+
+static Argument *
+gimp_unit_get_number_of_units_invoker (Argument *args)
+{
+  Argument *return_args;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_number_of_units_proc,
+					 TRUE);
+
+  return_args[1].value.pdb_int = gimp_unit_get_number_of_units ();
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_number_of_units_out_args[] =
+{
+  { PDB_INT32,
+    "#units",
+    "the number of units"
+  }
+};
+
+ProcRecord gimp_unit_get_number_of_units_proc =
+{
+  "gimp_unit_get_number_of_units",
+  "Returns the number of units",
+  "This procedure returns the number of defined units.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  0, NULL,
+  1, gimp_unit_get_number_of_units_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_number_of_units_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_NEW
+ */
+
+static Argument *
+gimp_unit_new_invoker (Argument *args)
+{
+  Argument *return_args;
+
+  return_args= procedural_db_return_args(&gimp_unit_new_proc, TRUE);
+
+  return_args[1].value.pdb_int =
+    gimp_unit_new (g_strdup (args[0].value.pdb_pointer),
+		   args[1].value.pdb_float,
+		   args[2].value.pdb_int,
+		   g_strdup (args[3].value.pdb_pointer),
+		   g_strdup (args[4].value.pdb_pointer),
+		   g_strdup (args[5].value.pdb_pointer),
+		   g_strdup (args[6].value.pdb_pointer));
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_new_args[] =
+{
+  { PDB_STRING,
+    "identifier",
+    "the new unit's identifier"
+  },
+  { PDB_FLOAT,
+    "factor",
+    "the new unit's factor"
+  },
+  { PDB_INT32,
+    "digits",
+    "the new unit's digits"
+  },
+  { PDB_STRING,
+    "symbol",
+    "the new unit's symbol"
+  },
+  { PDB_STRING,
+    "abbreviation",
+    "the new unit's abbreviation"
+  },
+  { PDB_STRING,
+    "singular",
+    "the new unit's singular form"
+  },
+  { PDB_STRING,
+    "plural",
+    "the new unit's plural form"
+  }
+};
+
+ProcArg gimp_unit_new_out_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the new unit's ID",
+  }
+};
+
+ProcRecord gimp_unit_new_proc =
+{
+  "gimp_unit_new",
+  "Creates a new unit and returns it's integer ID",
+  "This procedure creates a new unit and returns it's integer ID. Note that the new unit will have it's deletion flag set to TRUE, so you will have to set it to FALSE with gimp_unit_set_deletion_flag to make it persistent.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  7, gimp_unit_new_args,
+  1, gimp_unit_new_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_new_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_DELETION_FLAG
+ */
+
+static Argument *
+gimp_unit_get_deletion_flag_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_deletion_flag_proc,
+					 success);
+
+  if (success)
+    return_args[1].value.pdb_int = gimp_unit_get_deletion_flag (unit);
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_deletion_flag_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_deletion_flag_out_args[] =
+{
+  { PDB_INT32,
+    "boolean",
+    "the unit's deletion flag",
+  }
+};
+
+ProcRecord gimp_unit_get_deletion_flag_proc =
+{
+  "gimp_unit_get_deletion_flag",
+  "Returns the deletion flag of the unit",
+  "This procedure returns the deletion flag of the unit. If this value is TRUE the unit's definition will not be saved in the user's unitrc file on gimp exit.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_deletion_flag_args,
+  1, gimp_unit_get_deletion_flag_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_deletion_flag_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_SET_DELETION_FLAG
+ */
+
+static Argument *
+gimp_unit_set_deletion_flag_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_END) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+  else
+    gimp_unit_set_deletion_flag (unit, args[1].value.pdb_int);
+
+  return_args= procedural_db_return_args(&gimp_unit_set_deletion_flag_proc,
+					 success);
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_set_deletion_flag_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  },
+  { PDB_INT32,
+    "boolean",
+    "the new deletion flag of the unit",
+  }
+};
+
+ProcRecord gimp_unit_set_deletion_flag_proc =
+{
+  "gimp_unit_set_deletion_flag",
+  "Sets the deletion flag of a unit",
+  "This procedure sets the unit's deletion flag. If the deletion flag of a unit is TRUE on gimp exit, this unit's definition will not be saved in the user's unitrc.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  2, gimp_unit_set_deletion_flag_args,
+  0, NULL,
+
+  /*  Exec method  */
+  { { gimp_unit_set_deletion_flag_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_IDENTIFIER
+ */
+
+static Argument *
+gimp_unit_get_identifier_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_identifier_proc,
+					 success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_identifier (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_identifier_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_identifier_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's textual identifier",
+  }
+};
+
+ProcRecord gimp_unit_get_identifier_proc =
+{
+  "gimp_unit_get_identifier",
+  "Returns the identifier of the unit",
+  "This procedure returns the textual identifier of the unit. For built-in units it will be the english singular form of the unit's name. For user-defined units this should equal to the singular form.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_identifier_args,
+  1, gimp_unit_get_identifier_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_identifier_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_FACTOR
+ */
+
+static Argument *
+gimp_unit_get_factor_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_INCH) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_factor_proc, success);
+
+  return_args[1].value.pdb_float =
+    success ? gimp_unit_get_factor (unit) : 1.0;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_factor_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_factor_out_args[] =
+{
+  { PDB_FLOAT,
+    "float",
+    "the unit's factor",
+  }
+};
+
+ProcRecord gimp_unit_get_factor_proc =
+{
+  "gimp_unit_get_factor",
+  "Returns the factor of the unit",
+  "This procedure returns the unit's factor which indicates how many units make up an inch. Note that asking for the factor of \"pixels\" will produce an error.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_factor_args,
+  1, gimp_unit_get_factor_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_factor_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_DIGITS
+ */
+
+static Argument *
+gimp_unit_get_digits_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_INCH) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_digits_proc, success);
+
+  return_args[1].value.pdb_int =
+    success ? gimp_unit_get_digits (unit) : 0;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_digits_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_digits_out_args[] =
+{
+  { PDB_INT32,
+    "digits",
+    "the unit's number if digits",
+  }
+};
+
+ProcRecord gimp_unit_get_digits_proc =
+{
+  "gimp_unit_get_digits",
+  "Returns the digits of the unit",
+  "This procedure returns the number of digits you should provide in input or output functions to get approximately the same accuracy as with two digits and inches. Note that asking for the digits of \"pixels\" will produce an error.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_digits_args,
+  1, gimp_unit_get_digits_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_digits_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_SYMBOL
+ */
+
+static Argument *
+gimp_unit_get_symbol_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_symbol_proc, success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_symbol (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_symbol_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_symbol_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's symbol",
+  }
+};
+
+ProcRecord gimp_unit_get_symbol_proc =
+{
+  "gimp_unit_get_symbol",
+  "Returns the symbol of the unit",
+  "This procedure returns the symbol of the unit (\"''\" for inches).",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_symbol_args,
+  1, gimp_unit_get_symbol_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_symbol_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_ABBREVIATION
+ */
+
+static Argument *
+gimp_unit_get_abbreviation_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_abbreviation_proc,
+					 success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_abbreviation (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_abbreviation_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_abbreviation_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's abbreviation",
+  }
+};
+
+ProcRecord gimp_unit_get_abbreviation_proc =
+{
+  "gimp_unit_get_abbreviation",
+  "Returns the abbreviation of the unit",
+  "This procedure returns the abbreviation of the unit (\"in\" for inches).",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_abbreviation_args,
+  1, gimp_unit_get_abbreviation_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_abbreviation_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_SINGULAR
+ */
+
+static Argument *
+gimp_unit_get_singular_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_singular_proc, success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_singular (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_singular_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_singular_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's singular form",
+  }
+};
+
+ProcRecord gimp_unit_get_singular_proc =
+{
+  "gimp_unit_get_singular",
+  "Returns the singular for of the unit",
+  "This procedure returns the singular form of the unit.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_singular_args,
+  1, gimp_unit_get_singular_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_singular_invoker } },
+};
+
+
+/******************************
+ *  GIMP_UNIT_GET_PLURAL
+ */
+
+static Argument *
+gimp_unit_get_plural_invoker (Argument *args)
+{
+  Argument *return_args;
+  GUnit unit;
+
+  success = TRUE;
+
+  unit = args[0].value.pdb_int;
+  if ((unit < UNIT_PIXEL) || (unit >= gimp_unit_get_number_of_units ()))
+    success = FALSE;
+
+  return_args= procedural_db_return_args(&gimp_unit_get_plural_proc, success);
+
+  return_args[1].value.pdb_pointer =
+    success ? g_strdup (gimp_unit_get_plural (unit)) : NULL;
+
+  return return_args;
+}
+
+/*  The procedure definition  */
+ProcArg gimp_unit_get_plural_args[] =
+{
+  { PDB_INT32,
+    "unit ID",
+    "the unit's integer ID"
+  }
+};
+
+ProcArg gimp_unit_get_plural_out_args[] =
+{
+  { PDB_STRING,
+    "string",
+    "the unit's plural form",
+  }
+};
+
+ProcRecord gimp_unit_get_plural_proc =
+{
+  "gimp_unit_get_plural",
+  "Returns the plural form of the unit",
+  "This procedure returns the plural form of the unit.",
+  "Michael Natterer",
+  "Michael Natterer",
+  "1999",
+  PDB_INTERNAL,
+
+  /*  Input & output arguments  */
+  1, gimp_unit_get_plural_args,
+  1, gimp_unit_get_plural_out_args,
+
+  /*  Exec method  */
+  { { gimp_unit_get_plural_invoker } },
+};
diff --git a/app/gimpunit_cmds.h b/app/gimpunit_cmds.h
new file mode 100644
index 0000000000..0f5073a98f
--- /dev/null
+++ b/app/gimpunit_cmds.h
@@ -0,0 +1,35 @@
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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  __GIMP_UNIT_CMDS_H__
+#define  __GIMP_UNIT_CMDS_H__
+
+#include "procedural_db.h"
+
+extern ProcRecord gimp_unit_get_number_of_units_proc;
+extern ProcRecord gimp_unit_new_proc;
+extern ProcRecord gimp_unit_get_deletion_flag_proc;
+extern ProcRecord gimp_unit_set_deletion_flag_proc;
+extern ProcRecord gimp_unit_get_identifier_proc;
+extern ProcRecord gimp_unit_get_factor_proc;
+extern ProcRecord gimp_unit_get_digits_proc;
+extern ProcRecord gimp_unit_get_symbol_proc;
+extern ProcRecord gimp_unit_get_abbreviation_proc;
+extern ProcRecord gimp_unit_get_singular_proc;
+extern ProcRecord gimp_unit_get_plural_proc;
+
+#endif  /*  __GIMP_UNIT_CMDS_H__  */
diff --git a/app/gui/user-install-dialog.c b/app/gui/user-install-dialog.c
index 72565e71cf..5c4b9bbc22 100644
--- a/app/gui/user-install-dialog.c
+++ b/app/gui/user-install-dialog.c
@@ -202,6 +202,18 @@ install_help (InstallCallback callback)
 		   _("\t\tPaths to search for brushes, palettes, gradients\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
 		   _("\t\tpatterns, plug-ins and modules are also configured here.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
+		   _("unitrc\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tThe unitrc is used to store your user units database.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tYou can define additional units and use them just\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tlike you use the built-in units inches, millimeters,\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tpoints and picas. This file is overwritten each time\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tyou quit the GIMP.\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
 		   _("pluginrc\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
diff --git a/app/install.c b/app/install.c
index 72565e71cf..5c4b9bbc22 100644
--- a/app/install.c
+++ b/app/install.c
@@ -202,6 +202,18 @@ install_help (InstallCallback callback)
 		   _("\t\tPaths to search for brushes, palettes, gradients\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
 		   _("\t\tpatterns, plug-ins and modules are also configured here.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
+		   _("unitrc\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tThe unitrc is used to store your user units database.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tYou can define additional units and use them just\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tlike you use the built-in units inches, millimeters,\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tpoints and picas. This file is overwritten each time\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tyou quit the GIMP.\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
 		   _("pluginrc\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
diff --git a/app/internal_procs.c b/app/internal_procs.c
index ed2294c016..db035d8c4f 100644
--- a/app/internal_procs.c
+++ b/app/internal_procs.c
@@ -50,6 +50,7 @@
 #include "gimage_cmds.h"
 #include "gimage_mask_cmds.h"
 #include "gimprc.h"
+#include "gimpunit_cmds.h"
 #include "gradient.h"
 #include "histogram_tool.h"
 #include "hue_saturation.h"
@@ -179,6 +180,8 @@ internal_procs_init ()
   procedural_db_register (&gimage_set_filename_proc); pcount++;
   procedural_db_register (&gimage_get_resolution_proc); pcount++;
   procedural_db_register (&gimage_set_resolution_proc); pcount++;
+  procedural_db_register (&gimage_get_unit_proc); pcount++;
+  procedural_db_register (&gimage_set_unit_proc); pcount++;
   procedural_db_register (&gimage_width_proc); pcount++;
   procedural_db_register (&gimage_height_proc); pcount++;
   procedural_db_register (&gimage_get_cmap_proc); pcount++;
@@ -420,6 +423,19 @@ internal_procs_init ()
   app_init_update_status(NULL, _("Procedural database"),
 			 pcount/total_pcount);
 
+  /*  Unit Procedures  */
+  procedural_db_register (&gimp_unit_get_number_of_units_proc); pcount++;
+  procedural_db_register (&gimp_unit_new_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_deletion_flag_proc); pcount++;
+  procedural_db_register (&gimp_unit_set_deletion_flag_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_identifier_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_factor_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_digits_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_symbol_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_abbreviation_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_singular_proc); pcount++;
+  procedural_db_register (&gimp_unit_get_plural_proc); pcount++;
+
   /*  Procedural Database  */
   procedural_db_register (&procedural_db_dump_proc); pcount++;
   procedural_db_register (&procedural_db_query_proc); pcount++;
diff --git a/app/unitrc.h b/app/unitrc.h
new file mode 100644
index 0000000000..1afe409b0e
--- /dev/null
+++ b/app/unitrc.h
@@ -0,0 +1,25 @@
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 __UNITRC_H__
+#define __UNITRC_H__
+
+void parse_unitrc (void);
+void save_unitrc (void);
+
+#endif  /*  __UNITRC_H__  */
diff --git a/app/user_install.c b/app/user_install.c
index 72565e71cf..5c4b9bbc22 100644
--- a/app/user_install.c
+++ b/app/user_install.c
@@ -202,6 +202,18 @@ install_help (InstallCallback callback)
 		   _("\t\tPaths to search for brushes, palettes, gradients\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
 		   _("\t\tpatterns, plug-ins and modules are also configured here.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
+		   _("unitrc\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tThe unitrc is used to store your user units database.\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tYou can define additional units and use them just\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tlike you use the built-in units inches, millimeters,\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tpoints and picas. This file is overwritten each time\n"), -1);
+  gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
+		   _("\t\tyou quit the GIMP.\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font_emphasis, NULL, NULL,
 		   _("pluginrc\n"), -1);
   gtk_text_insert (GTK_TEXT (text), font, NULL, NULL,
diff --git a/app/xcf.c b/app/xcf.c
index d8c8b8c676..0737096e8b 100644
--- a/app/xcf.c
+++ b/app/xcf.c
@@ -69,7 +69,8 @@ typedef enum
   PROP_TATTOO = 20,
   PROP_PARASITES = 21,
   PROP_UNIT = 22,
-  PROP_PATHS = 23
+  PROP_PATHS = 23,
+  PROP_USER_UNIT = 24
 } PropType;
 
 typedef enum
@@ -614,10 +615,14 @@ xcf_save_image_props (XcfInfo *info,
   if (parasite_list_length(gimage->parasites) > 0)
     xcf_save_prop (info, PROP_PARASITES, gimage->parasites);
 
-  xcf_save_prop (info, PROP_UNIT, gimage->unit);
+  if (gimage->unit < gimp_unit_get_number_of_built_in_units ())
+    xcf_save_prop (info, PROP_UNIT, gimage->unit);
 
   xcf_save_prop (info, PROP_PATHS, gimage->paths);
 
+  if (gimage->unit >= gimp_unit_get_number_of_built_in_units ())
+    xcf_save_prop (info, PROP_USER_UNIT, gimage->unit);
+
   xcf_save_prop (info, PROP_END);
 }
 
@@ -1150,6 +1155,39 @@ xcf_save_prop (XcfInfo  *info,
 	}
       }
       break;
+    case PROP_USER_UNIT:
+      {
+	GUnit   unit;
+	gchar  *unit_strings[5];
+	float   factor;
+	guint32 digits;
+
+	unit = va_arg (args, guint32);
+
+	/* write the entire unit definition */
+	unit_strings[0] = gimp_unit_get_identifier (unit);
+	factor          = gimp_unit_get_factor (unit);
+	digits          = gimp_unit_get_digits (unit);
+	unit_strings[1] = gimp_unit_get_symbol (unit);
+	unit_strings[2] = gimp_unit_get_abbreviation (unit);
+	unit_strings[3] = gimp_unit_get_singular (unit);
+	unit_strings[4] = gimp_unit_get_plural (unit);
+
+	size =
+	  2 * 4 +
+	  strlen (unit_strings[0]) + 5 +
+	  strlen (unit_strings[1]) + 5 +
+	  strlen (unit_strings[2]) + 5 +
+	  strlen (unit_strings[3]) + 5 +
+	  strlen (unit_strings[4]) + 5;
+
+	info->cp += xcf_write_int32 (info->fp, (guint32*) &prop_type, 1);
+	info->cp += xcf_write_int32 (info->fp, &size, 1);
+	info->cp += xcf_write_float (info->fp, &factor, 1);
+	info->cp += xcf_write_int32 (info->fp, &digits, 1);
+	info->cp += xcf_write_string (info->fp, unit_strings, 5);
+      }
+      break;
     }
 
   va_end (args);
@@ -1784,20 +1822,65 @@ xcf_load_image_props (XcfInfo *info,
 	   
 	   if ((unit >= gimp_unit_get_number_of_units()) )
 	     {
-	       g_message(_("Warning, unit out of range in XCF file, falling back to pixels"));
-	       unit = UNIT_PIXEL;
+	       g_message(_("Warning, unit out of range in XCF file, falling back to inches"));
+	       unit = UNIT_INCH;
 	     }
 
 	   gimage->unit = unit;
 	 }
 	 break;
 	case PROP_PATHS:
-	{
-	  PathsList *paths = read_bzpaths(gimage,info);
-	  /* add to gimage */
-	  gimp_image_set_paths(gimage,paths);
-	}
-	break;
+	  {
+	    PathsList *paths = read_bzpaths(gimage,info);
+	    /* add to gimage */
+	    gimp_image_set_paths(gimage,paths);
+	  }
+	  break;
+	case PROP_USER_UNIT:
+	  {
+	    gchar  *unit_strings[5];
+	    float   factor;
+	    guint32 digits;
+	    GUnit   unit;
+	    gint    num_units;
+	    gint    i;
+
+	    info->cp += xcf_read_float (info->fp, &factor, 1);
+	    info->cp += xcf_read_int32 (info->fp, &digits, 1);
+	    info->cp += xcf_read_string (info->fp, unit_strings, 5);
+
+	    num_units = gimp_unit_get_number_of_units ();
+	    
+	    for (unit = gimp_unit_get_number_of_built_in_units ();
+		 unit < num_units; unit++)
+	      {
+		/* if the factor and the identifier match some unit
+		 * in unitrc, use the unitrc unit
+		 */
+		if ((ABS (gimp_unit_get_factor (unit) - factor) < 1e-5) &&
+		    (strcmp (unit_strings[0],
+			     gimp_unit_get_identifier (unit)) == 0))
+		  {
+		    break;
+		  }
+	      }
+	    
+	    /* no match */
+	    if (unit == num_units)
+	      unit = gimp_unit_new (unit_strings[0],
+				    factor,
+				    digits,
+				    unit_strings[1],
+				    unit_strings[2],
+				    unit_strings[3],
+				    unit_strings[4]);
+
+	    gimage->unit = unit;
+
+	    for (i = 0; i < 5; i++)
+	      g_free (unit_strings[i]);
+	  }
+	 break;
 	default:
 	  g_message (_("unexpected/unknown image property: %d (skipping)"), prop_type);
 
diff --git a/app/xcf/xcf.c b/app/xcf/xcf.c
index d8c8b8c676..0737096e8b 100644
--- a/app/xcf/xcf.c
+++ b/app/xcf/xcf.c
@@ -69,7 +69,8 @@ typedef enum
   PROP_TATTOO = 20,
   PROP_PARASITES = 21,
   PROP_UNIT = 22,
-  PROP_PATHS = 23
+  PROP_PATHS = 23,
+  PROP_USER_UNIT = 24
 } PropType;
 
 typedef enum
@@ -614,10 +615,14 @@ xcf_save_image_props (XcfInfo *info,
   if (parasite_list_length(gimage->parasites) > 0)
     xcf_save_prop (info, PROP_PARASITES, gimage->parasites);
 
-  xcf_save_prop (info, PROP_UNIT, gimage->unit);
+  if (gimage->unit < gimp_unit_get_number_of_built_in_units ())
+    xcf_save_prop (info, PROP_UNIT, gimage->unit);
 
   xcf_save_prop (info, PROP_PATHS, gimage->paths);
 
+  if (gimage->unit >= gimp_unit_get_number_of_built_in_units ())
+    xcf_save_prop (info, PROP_USER_UNIT, gimage->unit);
+
   xcf_save_prop (info, PROP_END);
 }
 
@@ -1150,6 +1155,39 @@ xcf_save_prop (XcfInfo  *info,
 	}
       }
       break;
+    case PROP_USER_UNIT:
+      {
+	GUnit   unit;
+	gchar  *unit_strings[5];
+	float   factor;
+	guint32 digits;
+
+	unit = va_arg (args, guint32);
+
+	/* write the entire unit definition */
+	unit_strings[0] = gimp_unit_get_identifier (unit);
+	factor          = gimp_unit_get_factor (unit);
+	digits          = gimp_unit_get_digits (unit);
+	unit_strings[1] = gimp_unit_get_symbol (unit);
+	unit_strings[2] = gimp_unit_get_abbreviation (unit);
+	unit_strings[3] = gimp_unit_get_singular (unit);
+	unit_strings[4] = gimp_unit_get_plural (unit);
+
+	size =
+	  2 * 4 +
+	  strlen (unit_strings[0]) + 5 +
+	  strlen (unit_strings[1]) + 5 +
+	  strlen (unit_strings[2]) + 5 +
+	  strlen (unit_strings[3]) + 5 +
+	  strlen (unit_strings[4]) + 5;
+
+	info->cp += xcf_write_int32 (info->fp, (guint32*) &prop_type, 1);
+	info->cp += xcf_write_int32 (info->fp, &size, 1);
+	info->cp += xcf_write_float (info->fp, &factor, 1);
+	info->cp += xcf_write_int32 (info->fp, &digits, 1);
+	info->cp += xcf_write_string (info->fp, unit_strings, 5);
+      }
+      break;
     }
 
   va_end (args);
@@ -1784,20 +1822,65 @@ xcf_load_image_props (XcfInfo *info,
 	   
 	   if ((unit >= gimp_unit_get_number_of_units()) )
 	     {
-	       g_message(_("Warning, unit out of range in XCF file, falling back to pixels"));
-	       unit = UNIT_PIXEL;
+	       g_message(_("Warning, unit out of range in XCF file, falling back to inches"));
+	       unit = UNIT_INCH;
 	     }
 
 	   gimage->unit = unit;
 	 }
 	 break;
 	case PROP_PATHS:
-	{
-	  PathsList *paths = read_bzpaths(gimage,info);
-	  /* add to gimage */
-	  gimp_image_set_paths(gimage,paths);
-	}
-	break;
+	  {
+	    PathsList *paths = read_bzpaths(gimage,info);
+	    /* add to gimage */
+	    gimp_image_set_paths(gimage,paths);
+	  }
+	  break;
+	case PROP_USER_UNIT:
+	  {
+	    gchar  *unit_strings[5];
+	    float   factor;
+	    guint32 digits;
+	    GUnit   unit;
+	    gint    num_units;
+	    gint    i;
+
+	    info->cp += xcf_read_float (info->fp, &factor, 1);
+	    info->cp += xcf_read_int32 (info->fp, &digits, 1);
+	    info->cp += xcf_read_string (info->fp, unit_strings, 5);
+
+	    num_units = gimp_unit_get_number_of_units ();
+	    
+	    for (unit = gimp_unit_get_number_of_built_in_units ();
+		 unit < num_units; unit++)
+	      {
+		/* if the factor and the identifier match some unit
+		 * in unitrc, use the unitrc unit
+		 */
+		if ((ABS (gimp_unit_get_factor (unit) - factor) < 1e-5) &&
+		    (strcmp (unit_strings[0],
+			     gimp_unit_get_identifier (unit)) == 0))
+		  {
+		    break;
+		  }
+	      }
+	    
+	    /* no match */
+	    if (unit == num_units)
+	      unit = gimp_unit_new (unit_strings[0],
+				    factor,
+				    digits,
+				    unit_strings[1],
+				    unit_strings[2],
+				    unit_strings[3],
+				    unit_strings[4]);
+
+	    gimage->unit = unit;
+
+	    for (i = 0; i < 5; i++)
+	      g_free (unit_strings[i]);
+	  }
+	 break;
 	default:
 	  g_message (_("unexpected/unknown image property: %d (skipping)"), prop_type);
 
diff --git a/data/misc/user_install b/data/misc/user_install
index 208f23ebf5..e67970810c 100755
--- a/data/misc/user_install
+++ b/data/misc/user_install
@@ -16,6 +16,9 @@ mkdir $2
 echo "cp $1/gimprc_user $2/gimprc"
 cp $1/gimprc_user $2/gimprc
 
+echo "cp $1/unitrc $2/unitrc"
+cp $1/unitrc $2/unitrc
+
 echo "cp $1/gtkrc $2/gtkrc"
 cp $1/gtkrc $2/gtkrc
 
diff --git a/data/misc/user_install.bat b/data/misc/user_install.bat
index d1bf74b1e3..c37957a899 100644
--- a/data/misc/user_install.bat
+++ b/data/misc/user_install.bat
@@ -2,6 +2,7 @@
 @echo off
 mkdir %2
 copy %1\gimprc_user %2\gimprc
+copy %1\unitrc %2\unitrc
 copy %1\gtkrc %2\gtkrc
 mkdir %2\brushes
 mkdir %2\gradients
diff --git a/etc/unitrc b/etc/unitrc
new file mode 100644
index 0000000000..7e9ed1b51b
--- /dev/null
+++ b/etc/unitrc
@@ -0,0 +1,55 @@
+# GIMP unitrc
+# This file contains your user unit database. You can
+# modify this list with the unit editor. You are not
+# supposed to edit it manually, but of course you can do.
+# This file will be entirely rewritten every time
+# you quit the gimp.
+
+(unit-info "centimeters"
+   (factor 2.540000)
+   (digits 2)
+   (symbol "cm")
+   (abbreviation "cm")
+   (singular "centimeter")
+   (plural "centimeters"))
+
+(unit-info "meters"
+   (factor 0.025400)
+   (digits 4)
+   (symbol "m")
+   (abbreviation "m")
+   (singular "meter")
+   (plural "meters"))
+
+(unit-info "feet"
+   (factor 0.083333)
+   (digits 4)
+   (symbol "'")
+   (abbreviation "ft")
+   (singular "foot")
+   (plural "feet"))
+
+(unit-info "yards"
+   (factor 0.027778)
+   (digits 4)
+   (symbol "yd")
+   (abbreviation "yd")
+   (singular "yard")
+   (plural "yards"))
+
+(unit-info "typorg. points"
+   (factor 72.270000)
+   (digits 0)
+   (symbol "tpt")
+   (abbreviation "tpt")
+   (singular "typogr. point")
+   (plural "typorg. points"))
+
+(unit-info "typorg. picas"
+   (factor 6.022500)
+   (digits 1)
+   (symbol "tpc")
+   (abbreviation "tpc")
+   (singular "typogr. pica")
+   (plural "typorg. picas"))
+
diff --git a/gimp.1 b/gimp.1
index fe02345fed..da21008ff1 100644
--- a/gimp.1
+++ b/gimp.1
@@ -96,6 +96,15 @@ and the user gimprc can override the sytem
 settings. \fB$PREFIX\fP/share/gimp/gimprc_user is the default gimprc
 placed in users home directories the first time gimp is ran.
 
+\fB$HOME\fP/.gimp/unitrc - user unit database. It contains convenient
+units a user may like to define. These units can be used just like the
+built-in units inches, millimeters, points and picas.
+
+\fB$PREFIX\fP/share/gimp/unitrc - default user unit database. It
+contains the unit definitions for centimeters, meters, feet, yards,
+typographic points and typographic picas and is placed in users home
+directories the first time the gimp is ran.
+
 \fB$HOME\fP/.gimp/gtkrc - users set of GTK config settings. Options
 such as widget color and fonts sizes can be set here.
 
diff --git a/libgimp/Makefile.am b/libgimp/Makefile.am
index f005106690..b386b939af 100644
--- a/libgimp/Makefile.am
+++ b/libgimp/Makefile.am
@@ -40,8 +40,6 @@ libgimpi_a_SOURCES = \
 	gimpprotocol.h		\
 	gimpsizeentry.c		\
 	gimpsizeentry.h		\
-	gimpunit.c		\
-	gimpunit.h		\
 	gimpunitmenu.c		\
 	gimpunitmenu.h		\
 	gimpwire.c		\
@@ -75,6 +73,8 @@ libgimp_la_SOURCES = \
 	gimpprotocol.c	\
 	gimpprotocol.h	\
 	gimptile.c	\
+	gimpunit.c	\
+	gimpunit.h	\
 	gimpwire.c	\
 	gimpwire.h	\
 	gserialize.c	\
diff --git a/libgimp/gimp.h b/libgimp/gimp.h
index 83f8c307d8..e12097c588 100644
--- a/libgimp/gimp.h
+++ b/libgimp/gimp.h
@@ -26,6 +26,7 @@
 #include "libgimp/gimpenv.h"
 #include "libgimp/parasite.h"
 #include "libgimp/parasiteP.h"
+#include "libgimp/gimpunit.h"
 
 #ifdef NATIVE_WIN32
 #  ifdef LIBGIMP_COMPILATION
@@ -491,6 +492,9 @@ void       gimp_image_set_resolution        (gint32     image_ID,
 void       gimp_image_get_resolution        (gint32     image_ID,
 					     float      *xresolution,
 					     float      *yresolution);
+void       gimp_image_set_unit              (gint32     image_ID,
+					     GUnit      unit);
+GUnit      gimp_image_get_unit              (gint32     image_ID);
 gint32     gimp_image_get_layer_by_tattoo   (gint32  image_ID,
 					     gint32 tattoo);
 gint32     gimp_image_get_channel_by_tattoo (gint32  image_ID,
diff --git a/libgimp/gimpimage.c b/libgimp/gimpimage.c
index c5fd4c45be..9292e19dca 100644
--- a/libgimp/gimpimage.c
+++ b/libgimp/gimpimage.c
@@ -1033,6 +1033,47 @@ gimp_image_set_resolution (gint32  image_ID,
   gimp_destroy_params (return_vals, nreturn_vals);
 }
 
+GUnit
+gimp_image_get_unit (gint32  image_ID)
+{
+  GParam *return_vals;
+  int nreturn_vals;
+  GUnit unit;
+
+  g_return_if_fail(unit);
+
+  return_vals = gimp_run_procedure ("gimp_image_get_unit",
+				    &nreturn_vals,
+				    PARAM_IMAGE, image_ID,
+				    PARAM_END);
+
+  /* error return value */
+  unit = UNIT_INCH;
+
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    unit = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return unit;
+}
+
+void
+gimp_image_set_unit (gint32  image_ID,
+		     GUnit   unit)
+{
+  GParam *return_vals;
+  int nreturn_vals;
+
+  return_vals = gimp_run_procedure ("gimp_image_set_unit",
+				    &nreturn_vals,
+				    PARAM_IMAGE, image_ID,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+}
+
 gint32
 gimp_image_get_layer_by_tattoo (gint32  image_ID, gint32 tattoo)
 {
diff --git a/libgimp/gimpimage_pdb.c b/libgimp/gimpimage_pdb.c
index c5fd4c45be..9292e19dca 100644
--- a/libgimp/gimpimage_pdb.c
+++ b/libgimp/gimpimage_pdb.c
@@ -1033,6 +1033,47 @@ gimp_image_set_resolution (gint32  image_ID,
   gimp_destroy_params (return_vals, nreturn_vals);
 }
 
+GUnit
+gimp_image_get_unit (gint32  image_ID)
+{
+  GParam *return_vals;
+  int nreturn_vals;
+  GUnit unit;
+
+  g_return_if_fail(unit);
+
+  return_vals = gimp_run_procedure ("gimp_image_get_unit",
+				    &nreturn_vals,
+				    PARAM_IMAGE, image_ID,
+				    PARAM_END);
+
+  /* error return value */
+  unit = UNIT_INCH;
+
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    unit = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return unit;
+}
+
+void
+gimp_image_set_unit (gint32  image_ID,
+		     GUnit   unit)
+{
+  GParam *return_vals;
+  int nreturn_vals;
+
+  return_vals = gimp_run_procedure ("gimp_image_set_unit",
+				    &nreturn_vals,
+				    PARAM_IMAGE, image_ID,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+}
+
 gint32
 gimp_image_get_layer_by_tattoo (gint32  image_ID, gint32 tattoo)
 {
diff --git a/libgimp/gimpsizeentry.c b/libgimp/gimpsizeentry.c
index 9a25fa084b..33f1a471e4 100644
--- a/libgimp/gimpsizeentry.c
+++ b/libgimp/gimpsizeentry.c
@@ -233,7 +233,9 @@ gimp_size_entry_new (gint             number_of_fields,
 					    gsef->min_value, 
 					    gsef->max_value,
 					    1.0, 10.0, 0.0));
-      gsef->value_spinbutton = gtk_spin_button_new (adjustment, 1.0, 3);
+      gsef->value_spinbutton =
+	gtk_spin_button_new (adjustment, 1.0,
+			     MIN (gimp_unit_get_digits (unit), 5) + 1);
       gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON(gsef->value_spinbutton),
 				       GTK_SHADOW_NONE);
       gtk_widget_set_usize (gsef->value_spinbutton, spinbutton_usize, 0);
@@ -789,7 +791,7 @@ gimp_size_entry_update_unit (GimpSizeEntry *gse,
 					gsef->refval_digits);
 	  else
 	    gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
-					MAX(gimp_unit_get_digits (unit) + 1, 3));
+					MIN(gimp_unit_get_digits (unit), 5) + 1);
 	}
       else if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
 	{
@@ -856,7 +858,8 @@ gimp_size_entry_focus_in_callback (GtkWidget *widget,
 				   GdkEvent  *event,
 				   gpointer   data)
 {
-  gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
+
+  /* gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1); */
 
   return TRUE;
 }
diff --git a/libgimp/gimpunit.c b/libgimp/gimpunit.c
index c59ef50071..087f55d3a5 100644
--- a/libgimp/gimpunit.c
+++ b/libgimp/gimpunit.c
@@ -1,4 +1,7 @@
-/* gimpunit.c
+/* LIBGIMP - The GIMP Library                                                   
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
+ *
+ * gimpunit.c
  * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
  *
  * This library is free software; you can redistribute it and/or
@@ -17,6 +20,7 @@
  * Boston, MA 02111-1307, USA.
  */
 
+#include "gimp.h"
 #include "gimpunit.h"
 #include "libgimp/gimpintl.h"
 
@@ -47,25 +51,28 @@ static GimpUnitDef gimp_unit_defs[UNIT_END] =
   { FALSE,  6.0, 1, "picas",       "pc", "pc", N_("pica"),       N_("picas") },
 };
 
-static GSList* user_units = NULL;
-static gint    number_of_user_units = 0;
-
-
-/* private functions */
-
-static GimpUnitDef *
-gimp_unit_get_user_unit (GUnit unit)
-{
-  return g_slist_nth_data (user_units, unit - UNIT_END);
-}
-
 
 /* public functions */
 
 gint
 gimp_unit_get_number_of_units (void)
 {
-  return UNIT_END + number_of_user_units;
+  GParam *return_vals;
+  int nreturn_vals;
+
+  int number;
+
+  return_vals = gimp_run_procedure ("gimp_unit_get_number_of_units",
+				    &nreturn_vals,
+				    PARAM_END);
+
+  number = UNIT_END;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    number = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return number;
 }
 
 gint
@@ -84,141 +91,267 @@ gimp_unit_new (gchar  *identifier,
 	       gchar  *singular,
 	       gchar  *plural)
 {
-  GimpUnitDef *user_unit;
+  GParam *return_vals;
+  int nreturn_vals;
 
-  user_unit = g_malloc (sizeof (GimpUnitDef));
-  user_unit->delete_on_exit = TRUE;
-  user_unit->factor = factor;
-  user_unit->digits = digits;
-  user_unit->identifier = g_strdup (identifier);
-  user_unit->symbol = g_strdup (symbol);
-  user_unit->abbreviation = g_strdup (abbreviation);
-  user_unit->singular = g_strdup (singular);
-  user_unit->plural = g_strdup (plural);
+  GUnit unit;
 
-  user_units = g_slist_append (user_units, user_unit);
-  number_of_user_units++;
+  return_vals = gimp_run_procedure ("gimp_unit_new",
+				    &nreturn_vals,
+				    PARAM_STRING, g_strdup (identifier),
+				    PARAM_FLOAT, factor,
+				    PARAM_INT32, digits,
+				    PARAM_STRING, g_strdup (symbol),
+				    PARAM_STRING, g_strdup (abbreviation),
+				    PARAM_STRING, g_strdup (singular),
+				    PARAM_STRING, g_strdup (plural),
+				    PARAM_END);
 
-  return UNIT_END + number_of_user_units - 1;
+  unit = UNIT_INCH;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    unit = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return unit;
 }
 
 
 guint
 gimp_unit_get_deletion_flag (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)), FALSE);
+  GParam *return_vals;
+  int nreturn_vals;
+
+  guint flag;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, TRUE);
 
   if (unit < UNIT_END)
     return FALSE;
 
-  return gimp_unit_get_user_unit (unit)->delete_on_exit;
+  return_vals = gimp_run_procedure ("gimp_unit_get_deletion_flag",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  flag = TRUE;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    flag = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return flag;
 }
 
 void
 gimp_unit_set_deletion_flag (GUnit  unit,
 			     guint  deletion_flag)
 {
-  g_return_if_fail ( (unit >= UNIT_END) && 
-		     (unit < (UNIT_END + number_of_user_units)));
+  GParam *return_vals;
+  int nreturn_vals;
 
-  gimp_unit_get_user_unit (unit)->delete_on_exit = deletion_flag;
+  g_return_if_fail (unit >= UNIT_PIXEL);
+
+  if (unit < UNIT_END)
+    return;
+
+  return_vals = gimp_run_procedure ("gimp_unit_set_deletion_flag",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_INT32, deletion_flag,
+				    PARAM_END);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
 }
 
 
 gfloat
 gimp_unit_get_factor (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].factor );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gfloat factor;
+
+  g_return_val_if_fail (unit >= UNIT_INCH, 1.0);
 
   if (unit < UNIT_END)
     return gimp_unit_defs[unit].factor;
 
-  return gimp_unit_get_user_unit (unit)->factor;
+  return_vals = gimp_run_procedure ("gimp_unit_get_factor",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  factor = 1.0;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    factor = return_vals[1].data.d_float;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return factor;
 }
 
 
 gint
 gimp_unit_get_digits (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].digits );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gint digits;
+
+  g_return_val_if_fail (unit >= UNIT_INCH, 2.0);
 
   if (unit < UNIT_END)
     return gimp_unit_defs[unit].digits;
 
-  return gimp_unit_get_user_unit (unit)->digits;
+  return_vals = gimp_run_procedure ("gimp_unit_get_digits",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  digits = 2.0;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    digits = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return digits;
 }
 
 
-const gchar * 
+gchar * 
 gimp_unit_get_identifier (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].identifier );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *identifier;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gimp_unit_defs[unit].identifier;
+    return g_strdup (gimp_unit_defs[unit].identifier);
 
-  return gimp_unit_get_user_unit (unit)->identifier;
+  return_vals = gimp_run_procedure ("gimp_unit_get_identifier",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  identifier = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    identifier = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return identifier ? identifier : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_symbol (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].symbol );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *symbol;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gimp_unit_defs[unit].symbol;
+    return g_strdup (gimp_unit_defs[unit].symbol);
 
-  return gimp_unit_get_user_unit (unit)->symbol;
+  return_vals = gimp_run_procedure ("gimp_unit_get_symbol",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  symbol = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    symbol = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return symbol ? symbol : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_abbreviation (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].abbreviation );
+  GParam *return_vals;
+  int nreturn_vals;
 
-  if (unit < UNIT_END)
-    return gimp_unit_defs[unit].abbreviation;
+  gchar *abbreviation;
 
-  return gimp_unit_get_user_unit (unit)->abbreviation;
+  return_vals = gimp_run_procedure ("gimp_unit_get_abbreviation",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  abbreviation = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    abbreviation = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return abbreviation ? abbreviation : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_singular (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gettext(gimp_unit_defs[UNIT_INCH].singular) );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *singular;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gettext(gimp_unit_defs[unit].singular);
+    return g_strdup (gettext (gimp_unit_defs[unit].singular));
 
-  return gimp_unit_get_user_unit (unit)->singular;
+  return_vals = gimp_run_procedure ("gimp_unit_get_singular",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  singular = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    singular = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return singular ? singular : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_plural (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gettext(gimp_unit_defs[UNIT_INCH].plural) );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *plural;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gettext(gimp_unit_defs[unit].plural);
+    return g_strdup (gettext (gimp_unit_defs[unit].plural));
 
-  return gimp_unit_get_user_unit (unit)->plural;
+  return_vals = gimp_run_procedure ("gimp_unit_get_plural",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  plural = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    plural = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return plural ? plural : g_strdup ("");
 }
diff --git a/libgimp/gimpunit.h b/libgimp/gimpunit.h
index 29f08c5479..ef7726d9ea 100644
--- a/libgimp/gimpunit.h
+++ b/libgimp/gimpunit.h
@@ -1,4 +1,7 @@
-/* gimpunit.h
+/* LIBGIMP - The GIMP Library                                                   
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
+ *
+ * gimpunit.h
  * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
  *
  * This library is free software; you can redistribute it and/or
@@ -17,24 +20,23 @@
  * Boston, MA 02111-1307, USA.
  */
 
+/* NOTE:
+ *
+ * This file serves as header for both app/gimpunit.c and libgimp/gimpunit.c
+ * because the unit functions are needed by widgets which are used by both
+ * the gimp app and plugins.
+ */
+
 #ifndef __GIMPUNIT_H__
 #define __GIMPUNIT_H__
 
-
 #include <glib.h>
 
-
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
 
 
-/* I've put this here and not to libgimp/gimpenums.h, because if this
- * file includes libgimp/gimpenums.h there is a name clash wherever
- * someone includes libgimp/gimpunit.h and app/gimpimage.h
- * (the constants RGB, GRAY and INDEXED are defined in both
- * gimpenums.h and gimpimage.h) (is this a bug? don't know...)
- */
 typedef enum
 {
   UNIT_PIXEL = 0,
@@ -42,12 +44,13 @@ typedef enum
   UNIT_MM    = 2,
   UNIT_POINT = 3,
   UNIT_PICA  = 4,
-  UNIT_END /* never use UNIT_END but gimp_unit_get_number_of_units() instead */
+  UNIT_END   = 5     /* never use UNIT_END but
+			gimp_unit_get_number_of_units() instead */
 } GUnit;
 
 
-gint           gimp_unit_get_number_of_units          (void);
-gint           gimp_unit_get_number_of_built_in_units (void);
+gint     gimp_unit_get_number_of_units          (void);
+gint     gimp_unit_get_number_of_built_in_units (void);
 
 /* Create a new user unit and returns it's ID.
  *
@@ -55,13 +58,13 @@ gint           gimp_unit_get_number_of_built_in_units (void);
  * set to TRUE. You will have to set it to FALSE after creation to make
  * the unit definition persistant.
  */
-GUnit          gimp_unit_new                 (gchar  *identifier,
-					      gfloat  factor,
-					      gint    digits,
-					      gchar  *symbol,
-					      gchar  *abbreviation,
-					      gchar  *singular,
-					      gchar  *plural);
+GUnit    gimp_unit_new                 (gchar  *identifier,
+					gfloat  factor,
+					gint    digits,
+					gchar  *symbol,
+					gchar  *abbreviation,
+					gchar  *singular,
+					gchar  *plural);
 
 /* The following functions fall back to inch (not pixel, as pixel is not
  * a 'real' unit) if the value passed is out of range.
@@ -72,19 +75,16 @@ GUnit          gimp_unit_new                 (gchar  *identifier,
 /* If the deletion flag for a unit is TRUE on GIMP exit, this unit
  * will not be saved in the user units database.
  */
-guint          gimp_unit_get_deletion_flag   (GUnit  unit);
-void           gimp_unit_set_deletion_flag   (GUnit  unit,
+guint    gimp_unit_get_deletion_flag   (GUnit  unit);
+void     gimp_unit_set_deletion_flag   (GUnit  unit,
 					      guint  deletion_flag);
 
-/* This one is an untranslated string for gimprc */
-const gchar *  gimp_unit_get_identifier      (GUnit  unit);
-
 /* The meaning of 'factor' is:
  * distance_in_units == ( factor * distance_in_inches )
  *
  * Returns 0 for unit == UNIT_PIXEL as we don't have resolution info here
  */
-gfloat         gimp_unit_get_factor          (GUnit  unit);
+gfloat   gimp_unit_get_factor          (GUnit  unit);
 
 /* The following function gives a hint how many digits a spinbutton
  * should provide to get approximately the accuracy of an inch-spinbutton
@@ -92,12 +92,21 @@ gfloat         gimp_unit_get_factor          (GUnit  unit);
  *
  * Returns 0 for unit == UNIT_PIXEL as we don't have resolution info here.
  */
-gint           gimp_unit_get_digits          (GUnit  unit);
+gint     gimp_unit_get_digits          (GUnit  unit);
 
-const gchar *  gimp_unit_get_symbol          (GUnit  unit);
-const gchar *  gimp_unit_get_abbreviation    (GUnit  unit);
-const gchar *  gimp_unit_get_singular        (GUnit  unit);
-const gchar *  gimp_unit_get_plural          (GUnit  unit);
+/* NOTE:
+ *
+ * the gchar pointer returned is constant in the gimp application but must
+ * be g_free()'d by plug-ins.
+ */
+
+/* This one is an untranslated string for gimprc */
+gchar *  gimp_unit_get_identifier      (GUnit  unit);
+
+gchar *  gimp_unit_get_symbol          (GUnit  unit);
+gchar *  gimp_unit_get_abbreviation    (GUnit  unit);
+gchar *  gimp_unit_get_singular        (GUnit  unit);
+gchar *  gimp_unit_get_plural          (GUnit  unit);
 
 #ifdef __cplusplus
 }
diff --git a/libgimp/gimpunit_pdb.c b/libgimp/gimpunit_pdb.c
index c59ef50071..087f55d3a5 100644
--- a/libgimp/gimpunit_pdb.c
+++ b/libgimp/gimpunit_pdb.c
@@ -1,4 +1,7 @@
-/* gimpunit.c
+/* LIBGIMP - The GIMP Library                                                   
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
+ *
+ * gimpunit.c
  * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
  *
  * This library is free software; you can redistribute it and/or
@@ -17,6 +20,7 @@
  * Boston, MA 02111-1307, USA.
  */
 
+#include "gimp.h"
 #include "gimpunit.h"
 #include "libgimp/gimpintl.h"
 
@@ -47,25 +51,28 @@ static GimpUnitDef gimp_unit_defs[UNIT_END] =
   { FALSE,  6.0, 1, "picas",       "pc", "pc", N_("pica"),       N_("picas") },
 };
 
-static GSList* user_units = NULL;
-static gint    number_of_user_units = 0;
-
-
-/* private functions */
-
-static GimpUnitDef *
-gimp_unit_get_user_unit (GUnit unit)
-{
-  return g_slist_nth_data (user_units, unit - UNIT_END);
-}
-
 
 /* public functions */
 
 gint
 gimp_unit_get_number_of_units (void)
 {
-  return UNIT_END + number_of_user_units;
+  GParam *return_vals;
+  int nreturn_vals;
+
+  int number;
+
+  return_vals = gimp_run_procedure ("gimp_unit_get_number_of_units",
+				    &nreturn_vals,
+				    PARAM_END);
+
+  number = UNIT_END;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    number = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return number;
 }
 
 gint
@@ -84,141 +91,267 @@ gimp_unit_new (gchar  *identifier,
 	       gchar  *singular,
 	       gchar  *plural)
 {
-  GimpUnitDef *user_unit;
+  GParam *return_vals;
+  int nreturn_vals;
 
-  user_unit = g_malloc (sizeof (GimpUnitDef));
-  user_unit->delete_on_exit = TRUE;
-  user_unit->factor = factor;
-  user_unit->digits = digits;
-  user_unit->identifier = g_strdup (identifier);
-  user_unit->symbol = g_strdup (symbol);
-  user_unit->abbreviation = g_strdup (abbreviation);
-  user_unit->singular = g_strdup (singular);
-  user_unit->plural = g_strdup (plural);
+  GUnit unit;
 
-  user_units = g_slist_append (user_units, user_unit);
-  number_of_user_units++;
+  return_vals = gimp_run_procedure ("gimp_unit_new",
+				    &nreturn_vals,
+				    PARAM_STRING, g_strdup (identifier),
+				    PARAM_FLOAT, factor,
+				    PARAM_INT32, digits,
+				    PARAM_STRING, g_strdup (symbol),
+				    PARAM_STRING, g_strdup (abbreviation),
+				    PARAM_STRING, g_strdup (singular),
+				    PARAM_STRING, g_strdup (plural),
+				    PARAM_END);
 
-  return UNIT_END + number_of_user_units - 1;
+  unit = UNIT_INCH;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    unit = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return unit;
 }
 
 
 guint
 gimp_unit_get_deletion_flag (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)), FALSE);
+  GParam *return_vals;
+  int nreturn_vals;
+
+  guint flag;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, TRUE);
 
   if (unit < UNIT_END)
     return FALSE;
 
-  return gimp_unit_get_user_unit (unit)->delete_on_exit;
+  return_vals = gimp_run_procedure ("gimp_unit_get_deletion_flag",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  flag = TRUE;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    flag = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return flag;
 }
 
 void
 gimp_unit_set_deletion_flag (GUnit  unit,
 			     guint  deletion_flag)
 {
-  g_return_if_fail ( (unit >= UNIT_END) && 
-		     (unit < (UNIT_END + number_of_user_units)));
+  GParam *return_vals;
+  int nreturn_vals;
 
-  gimp_unit_get_user_unit (unit)->delete_on_exit = deletion_flag;
+  g_return_if_fail (unit >= UNIT_PIXEL);
+
+  if (unit < UNIT_END)
+    return;
+
+  return_vals = gimp_run_procedure ("gimp_unit_set_deletion_flag",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_INT32, deletion_flag,
+				    PARAM_END);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
 }
 
 
 gfloat
 gimp_unit_get_factor (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].factor );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gfloat factor;
+
+  g_return_val_if_fail (unit >= UNIT_INCH, 1.0);
 
   if (unit < UNIT_END)
     return gimp_unit_defs[unit].factor;
 
-  return gimp_unit_get_user_unit (unit)->factor;
+  return_vals = gimp_run_procedure ("gimp_unit_get_factor",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  factor = 1.0;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    factor = return_vals[1].data.d_float;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return factor;
 }
 
 
 gint
 gimp_unit_get_digits (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].digits );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gint digits;
+
+  g_return_val_if_fail (unit >= UNIT_INCH, 2.0);
 
   if (unit < UNIT_END)
     return gimp_unit_defs[unit].digits;
 
-  return gimp_unit_get_user_unit (unit)->digits;
+  return_vals = gimp_run_procedure ("gimp_unit_get_digits",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  digits = 2.0;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    digits = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return digits;
 }
 
 
-const gchar * 
+gchar * 
 gimp_unit_get_identifier (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].identifier );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *identifier;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gimp_unit_defs[unit].identifier;
+    return g_strdup (gimp_unit_defs[unit].identifier);
 
-  return gimp_unit_get_user_unit (unit)->identifier;
+  return_vals = gimp_run_procedure ("gimp_unit_get_identifier",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  identifier = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    identifier = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return identifier ? identifier : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_symbol (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].symbol );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *symbol;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gimp_unit_defs[unit].symbol;
+    return g_strdup (gimp_unit_defs[unit].symbol);
 
-  return gimp_unit_get_user_unit (unit)->symbol;
+  return_vals = gimp_run_procedure ("gimp_unit_get_symbol",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  symbol = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    symbol = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return symbol ? symbol : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_abbreviation (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].abbreviation );
+  GParam *return_vals;
+  int nreturn_vals;
 
-  if (unit < UNIT_END)
-    return gimp_unit_defs[unit].abbreviation;
+  gchar *abbreviation;
 
-  return gimp_unit_get_user_unit (unit)->abbreviation;
+  return_vals = gimp_run_procedure ("gimp_unit_get_abbreviation",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  abbreviation = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    abbreviation = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return abbreviation ? abbreviation : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_singular (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gettext(gimp_unit_defs[UNIT_INCH].singular) );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *singular;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gettext(gimp_unit_defs[unit].singular);
+    return g_strdup (gettext (gimp_unit_defs[unit].singular));
 
-  return gimp_unit_get_user_unit (unit)->singular;
+  return_vals = gimp_run_procedure ("gimp_unit_get_singular",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  singular = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    singular = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return singular ? singular : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_plural (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gettext(gimp_unit_defs[UNIT_INCH].plural) );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *plural;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gettext(gimp_unit_defs[unit].plural);
+    return g_strdup (gettext (gimp_unit_defs[unit].plural));
 
-  return gimp_unit_get_user_unit (unit)->plural;
+  return_vals = gimp_run_procedure ("gimp_unit_get_plural",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  plural = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    plural = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return plural ? plural : g_strdup ("");
 }
diff --git a/libgimp/gimpunitcache.c b/libgimp/gimpunitcache.c
index c59ef50071..087f55d3a5 100644
--- a/libgimp/gimpunitcache.c
+++ b/libgimp/gimpunitcache.c
@@ -1,4 +1,7 @@
-/* gimpunit.c
+/* LIBGIMP - The GIMP Library                                                   
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
+ *
+ * gimpunit.c
  * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
  *
  * This library is free software; you can redistribute it and/or
@@ -17,6 +20,7 @@
  * Boston, MA 02111-1307, USA.
  */
 
+#include "gimp.h"
 #include "gimpunit.h"
 #include "libgimp/gimpintl.h"
 
@@ -47,25 +51,28 @@ static GimpUnitDef gimp_unit_defs[UNIT_END] =
   { FALSE,  6.0, 1, "picas",       "pc", "pc", N_("pica"),       N_("picas") },
 };
 
-static GSList* user_units = NULL;
-static gint    number_of_user_units = 0;
-
-
-/* private functions */
-
-static GimpUnitDef *
-gimp_unit_get_user_unit (GUnit unit)
-{
-  return g_slist_nth_data (user_units, unit - UNIT_END);
-}
-
 
 /* public functions */
 
 gint
 gimp_unit_get_number_of_units (void)
 {
-  return UNIT_END + number_of_user_units;
+  GParam *return_vals;
+  int nreturn_vals;
+
+  int number;
+
+  return_vals = gimp_run_procedure ("gimp_unit_get_number_of_units",
+				    &nreturn_vals,
+				    PARAM_END);
+
+  number = UNIT_END;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    number = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return number;
 }
 
 gint
@@ -84,141 +91,267 @@ gimp_unit_new (gchar  *identifier,
 	       gchar  *singular,
 	       gchar  *plural)
 {
-  GimpUnitDef *user_unit;
+  GParam *return_vals;
+  int nreturn_vals;
 
-  user_unit = g_malloc (sizeof (GimpUnitDef));
-  user_unit->delete_on_exit = TRUE;
-  user_unit->factor = factor;
-  user_unit->digits = digits;
-  user_unit->identifier = g_strdup (identifier);
-  user_unit->symbol = g_strdup (symbol);
-  user_unit->abbreviation = g_strdup (abbreviation);
-  user_unit->singular = g_strdup (singular);
-  user_unit->plural = g_strdup (plural);
+  GUnit unit;
 
-  user_units = g_slist_append (user_units, user_unit);
-  number_of_user_units++;
+  return_vals = gimp_run_procedure ("gimp_unit_new",
+				    &nreturn_vals,
+				    PARAM_STRING, g_strdup (identifier),
+				    PARAM_FLOAT, factor,
+				    PARAM_INT32, digits,
+				    PARAM_STRING, g_strdup (symbol),
+				    PARAM_STRING, g_strdup (abbreviation),
+				    PARAM_STRING, g_strdup (singular),
+				    PARAM_STRING, g_strdup (plural),
+				    PARAM_END);
 
-  return UNIT_END + number_of_user_units - 1;
+  unit = UNIT_INCH;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    unit = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return unit;
 }
 
 
 guint
 gimp_unit_get_deletion_flag (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)), FALSE);
+  GParam *return_vals;
+  int nreturn_vals;
+
+  guint flag;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, TRUE);
 
   if (unit < UNIT_END)
     return FALSE;
 
-  return gimp_unit_get_user_unit (unit)->delete_on_exit;
+  return_vals = gimp_run_procedure ("gimp_unit_get_deletion_flag",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  flag = TRUE;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    flag = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return flag;
 }
 
 void
 gimp_unit_set_deletion_flag (GUnit  unit,
 			     guint  deletion_flag)
 {
-  g_return_if_fail ( (unit >= UNIT_END) && 
-		     (unit < (UNIT_END + number_of_user_units)));
+  GParam *return_vals;
+  int nreturn_vals;
 
-  gimp_unit_get_user_unit (unit)->delete_on_exit = deletion_flag;
+  g_return_if_fail (unit >= UNIT_PIXEL);
+
+  if (unit < UNIT_END)
+    return;
+
+  return_vals = gimp_run_procedure ("gimp_unit_set_deletion_flag",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_INT32, deletion_flag,
+				    PARAM_END);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
 }
 
 
 gfloat
 gimp_unit_get_factor (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].factor );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gfloat factor;
+
+  g_return_val_if_fail (unit >= UNIT_INCH, 1.0);
 
   if (unit < UNIT_END)
     return gimp_unit_defs[unit].factor;
 
-  return gimp_unit_get_user_unit (unit)->factor;
+  return_vals = gimp_run_procedure ("gimp_unit_get_factor",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  factor = 1.0;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    factor = return_vals[1].data.d_float;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return factor;
 }
 
 
 gint
 gimp_unit_get_digits (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].digits );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gint digits;
+
+  g_return_val_if_fail (unit >= UNIT_INCH, 2.0);
 
   if (unit < UNIT_END)
     return gimp_unit_defs[unit].digits;
 
-  return gimp_unit_get_user_unit (unit)->digits;
+  return_vals = gimp_run_procedure ("gimp_unit_get_digits",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  digits = 2.0;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    digits = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return digits;
 }
 
 
-const gchar * 
+gchar * 
 gimp_unit_get_identifier (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) && 
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].identifier );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *identifier;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gimp_unit_defs[unit].identifier;
+    return g_strdup (gimp_unit_defs[unit].identifier);
 
-  return gimp_unit_get_user_unit (unit)->identifier;
+  return_vals = gimp_run_procedure ("gimp_unit_get_identifier",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  identifier = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    identifier = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return identifier ? identifier : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_symbol (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].symbol );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *symbol;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gimp_unit_defs[unit].symbol;
+    return g_strdup (gimp_unit_defs[unit].symbol);
 
-  return gimp_unit_get_user_unit (unit)->symbol;
+  return_vals = gimp_run_procedure ("gimp_unit_get_symbol",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  symbol = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    symbol = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return symbol ? symbol : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_abbreviation (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gimp_unit_defs[UNIT_INCH].abbreviation );
+  GParam *return_vals;
+  int nreturn_vals;
 
-  if (unit < UNIT_END)
-    return gimp_unit_defs[unit].abbreviation;
+  gchar *abbreviation;
 
-  return gimp_unit_get_user_unit (unit)->abbreviation;
+  return_vals = gimp_run_procedure ("gimp_unit_get_abbreviation",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  abbreviation = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    abbreviation = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return abbreviation ? abbreviation : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_singular (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gettext(gimp_unit_defs[UNIT_INCH].singular) );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *singular;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gettext(gimp_unit_defs[unit].singular);
+    return g_strdup (gettext (gimp_unit_defs[unit].singular));
 
-  return gimp_unit_get_user_unit (unit)->singular;
+  return_vals = gimp_run_procedure ("gimp_unit_get_singular",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  singular = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    singular = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return singular ? singular : g_strdup ("");
 }
 
 
-const gchar *
+gchar *
 gimp_unit_get_plural (GUnit unit)
 {
-  g_return_val_if_fail ( (unit >= UNIT_PIXEL) &&
-			 (unit < (UNIT_END + number_of_user_units)),
-			 gettext(gimp_unit_defs[UNIT_INCH].plural) );
+  GParam *return_vals;
+  int nreturn_vals;
+
+  gchar *plural;
+
+  g_return_val_if_fail (unit >= UNIT_PIXEL, g_strdup (""));
 
   if (unit < UNIT_END)
-    return gettext(gimp_unit_defs[unit].plural);
+    return g_strdup (gettext (gimp_unit_defs[unit].plural));
 
-  return gimp_unit_get_user_unit (unit)->plural;
+  return_vals = gimp_run_procedure ("gimp_unit_get_plural",
+				    &nreturn_vals,
+				    PARAM_INT32, unit,
+				    PARAM_END);
+
+  plural = NULL;
+  if (return_vals[0].data.d_status == STATUS_SUCCESS)
+    plural = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return plural ? plural : g_strdup ("");
 }
diff --git a/libgimp/gimpunitmenu.c b/libgimp/gimpunitmenu.c
index 77db93da4a..911fac16e0 100644
--- a/libgimp/gimpunitmenu.c
+++ b/libgimp/gimpunitmenu.c
@@ -59,6 +59,7 @@ gimp_unit_menu_class_init (GimpUnitMenuClass *class)
 static void
 gimp_unit_menu_init (GimpUnitMenu *gum)
 {
+  gum->selection = NULL;
   gum->unit = UNIT_PIXEL;
   gum->start = 0;
 }
@@ -91,51 +92,85 @@ gimp_unit_menu_get_type ()
 
 
 GtkWidget*
-gimp_unit_menu_new (gchar    *format,
-		    GUnit     unit,
-		    gboolean  with_pixels,
-		    gboolean  with_custom)
+gimp_unit_menu_new (gchar       *format,
+		    GUnit        unit,
+		    gboolean     with_pixels,
+		    gboolean     with_custom)
 {
-  GimpUnitMenu  *gum;
-  GtkWidget     *menu;
-  GtkWidget     *menuitem;
-  GUnit          u;
+  GimpUnitMenu *gum;
+  GtkWidget    *menu;
+  GtkWidget    *menuitem;
+  GUnit         u;
+
+  g_return_val_if_fail ((unit >= UNIT_PIXEL) &&
+			(unit < gimp_unit_get_number_of_units ()), NULL);
 
   if (with_custom); /* avoid 'unused variable' compiler warning */
 
   gum = gtk_type_new (gimp_unit_menu_get_type ());
 
+  gum->format = g_strdup (format);
   /* if we don't want pixels, start with inches */
   gum->start = with_pixels ? UNIT_PIXEL : UNIT_INCH;
 
-  if ( (unit < gum->start) || (unit >= UNIT_END) )
+  if (unit < gum->start)
     unit = gum->start;
 
   menu = gtk_menu_new();
   for (u = gum->start; u < gimp_unit_get_number_of_built_in_units(); u++)
     {
       menuitem =
-	gtk_menu_item_new_with_label (gimp_unit_menu_build_string(format, u) );
+	gtk_menu_item_new_with_label (gimp_unit_menu_build_string (format, u));
       gtk_menu_append (GTK_MENU (menu), menuitem);
       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
 			  (GtkSignalFunc) gimp_unit_menu_callback, gum);
-      gtk_widget_show(menuitem);
       gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu", (gpointer)u);
+      gtk_widget_show(menuitem);
 
       /* add a separator after pixels */
       if (u == UNIT_PIXEL)
 	{
 	  menuitem = gtk_menu_item_new ();
-	  gtk_widget_show (menuitem);
 	  gtk_menu_append (GTK_MENU (menu), menuitem);
+	  gtk_widget_show (menuitem);
 	}
-    } 
+    }
+  if (unit >= gimp_unit_get_number_of_built_in_units ())
+    {
+      menuitem = gtk_menu_item_new ();
+      gtk_menu_append (GTK_MENU (menu), menuitem);
+      gtk_widget_show (menuitem);
+      
+      menuitem =
+	gtk_menu_item_new_with_label (gimp_unit_menu_build_string (format, unit));
+      gtk_menu_append (GTK_MENU (menu), menuitem);
+      gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+			  (GtkSignalFunc) gimp_unit_menu_callback, gum);
+      gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu", (gpointer)unit);
+      gtk_widget_show(menuitem);
+    }
 
+  menuitem = gtk_menu_item_new ();
+  gtk_menu_append (GTK_MENU (menu), menuitem);
+  gtk_widget_show (menuitem);
+      
+  menuitem =
+    gtk_menu_item_new_with_label (_("More..."));
+  gtk_menu_append (GTK_MENU (menu), menuitem);
+  gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+		      (GtkSignalFunc) gimp_unit_menu_callback, gum);
+  gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu",
+		       (gpointer)65536);
+  gtk_widget_show(menuitem);
+  
   gtk_option_menu_set_menu (GTK_OPTION_MENU (gum), menu);
 
   gum->unit = unit;
   gtk_option_menu_set_history (GTK_OPTION_MENU (gum),
-			       unit - gum->start + (with_pixels ? 1 : 0));
+			       ((unit < UNIT_END) ?
+				(unit - gum->start +
+				 (with_pixels ? 1 : 0)) :
+				(UNIT_END + (with_pixels ? 2 : 0))));
   
   return GTK_WIDGET (gum);
 }
@@ -145,17 +180,54 @@ void
 gimp_unit_menu_set_unit (GimpUnitMenu *gum,
 			 GUnit         unit)
 {
+  GtkWidget *menuitem = NULL;
+  GList     *items;
+  gint       user_unit;
+
   g_return_if_fail (gum != NULL);
   g_return_if_fail (GIMP_IS_UNIT_MENU (gum));
+  g_return_if_fail ((unit >= gum->start) &&
+		    (unit < gimp_unit_get_number_of_units ()));
 
-  /* replace UNIT_END when unit database is there */
+  if (unit == gum->unit)
+    return;
 
-  g_return_if_fail ((unit >= gum->start) && (unit < UNIT_END));
+  items = GTK_MENU_SHELL (GTK_OPTION_MENU (gum)->menu)->children;
+  user_unit = UNIT_END + ((gum->start == UNIT_PIXEL) ? 2 : 0);
+
+  if (unit >= UNIT_END)
+    {
+      if ((g_list_length (items) - 3) >= user_unit)
+	{
+	  gtk_widget_destroy (GTK_WIDGET (g_list_nth_data (items,
+							   user_unit - 1)));
+	  gtk_widget_destroy (GTK_WIDGET (g_list_nth_data (items,
+							   user_unit - 1)));
+	}
+
+      menuitem = gtk_menu_item_new ();
+      gtk_menu_append (GTK_MENU (GTK_OPTION_MENU (gum)->menu), menuitem);
+      gtk_menu_reorder_child (GTK_MENU (GTK_OPTION_MENU (gum)->menu),
+			      menuitem, user_unit - 1);
+      gtk_widget_show(menuitem);
+      
+      menuitem =
+	gtk_menu_item_new_with_label (gimp_unit_menu_build_string (gum->format, unit));
+      gtk_menu_append (GTK_MENU (GTK_OPTION_MENU (gum)->menu), menuitem);
+      gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+			  (GtkSignalFunc) gimp_unit_menu_callback, gum);
+      gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu", (gpointer)unit);
+      gtk_menu_reorder_child (GTK_MENU (GTK_OPTION_MENU (gum)->menu),
+			      menuitem, user_unit);
+      gtk_widget_show(menuitem);
+    }
 
   gum->unit = unit;
   gtk_option_menu_set_history (GTK_OPTION_MENU (gum),
-			       unit - gum->start +
-			       ((gum->start == UNIT_PIXEL) ? 1 : 0));
+			       ((unit < UNIT_END) ?
+				(unit - gum->start +
+				 ((gum->start == UNIT_PIXEL) ? 1 : 0)) :
+				(UNIT_END + ((gum->start == UNIT_PIXEL) ? 2 : 0))));
 }
 
 GUnit
@@ -260,6 +332,175 @@ gimp_unit_menu_build_string (gchar *format, GUnit unit)
 }
 
 
+/* private callbacks of gimp_unit_menu_create_selection ()
+ */
+
+static void
+gimp_unit_menu_selection_select_callback (GtkWidget *widget,
+					  gpointer   data)
+{
+  GimpUnitMenu *gum;
+  GUnit         unit;
+
+  gum = GIMP_UNIT_MENU (data);
+
+  if (gum->selection && GTK_CLIST (gum->clist)->selection)
+    {
+      unit = (GUnit)gtk_clist_get_row_data (GTK_CLIST (gum->clist),
+					    (int)(GTK_CLIST (gum->clist)->selection->data));
+      gimp_unit_menu_set_unit (gum, unit);
+      gtk_signal_emit (GTK_OBJECT (gum),
+		       gimp_unit_menu_signals[GUM_UNIT_CHANGED_SIGNAL]);
+      
+      gtk_widget_destroy (gum->selection);
+      gum->selection = NULL;
+      gum->clist = NULL;
+    }
+}
+
+static void
+gimp_unit_menu_selection_close_callback (GtkWidget *widget,
+					 gpointer   data)
+{
+  GimpUnitMenu *gum;
+
+  gum = GIMP_UNIT_MENU (data);
+
+  if (gum->selection)
+    {
+      gtk_widget_destroy (gum->selection);
+      gum->selection = NULL;
+      gum->clist = NULL;
+    }
+}
+
+static gint
+gimp_unit_menu_selection_delete_callback (GtkWidget *widget,
+					  GdkEvent  *event,
+					  gpointer   data)
+{
+  gimp_unit_menu_selection_close_callback (NULL, data);
+
+  return TRUE;
+}
+
+/* private function of gimp_unit_menu_callback ()
+ */
+
+static void
+gimp_unit_menu_create_selection (GimpUnitMenu *gum)
+{
+  GtkWidget *hbox;
+  GtkWidget *hbbox;
+  GtkWidget *vbox;
+  GtkWidget *scrolled_win;
+  GtkWidget *button;
+  gchar     *row[2];
+  GUnit      unit;
+  gint       num_units;
+  gchar      buffer[32];
+
+  gum->selection = gtk_dialog_new ();
+  gtk_window_set_wmclass (GTK_WINDOW (gum->selection),
+			  "unitselection", "Gimp");
+  gtk_window_set_title (GTK_WINDOW (gum->selection), _("Unit Selection"));
+  gtk_window_set_policy(GTK_WINDOW(gum->selection), FALSE, TRUE, FALSE);
+
+  vbox = gtk_vbox_new (FALSE, 2);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (gum->selection)->vbox), vbox);
+  gtk_container_border_width (GTK_CONTAINER (vbox), 2);
+  gtk_widget_show (vbox);
+
+  gtk_signal_connect (GTK_OBJECT (gum->selection), "delete_event",
+                      GTK_SIGNAL_FUNC (gimp_unit_menu_selection_delete_callback),
+                      gum);
+
+  gtk_signal_connect (GTK_OBJECT (gum), "destroy",
+                      GTK_SIGNAL_FUNC (gimp_unit_menu_selection_close_callback),
+                      gum);
+  gtk_signal_connect (GTK_OBJECT (gum), "unmap",
+                      GTK_SIGNAL_FUNC (gimp_unit_menu_selection_close_callback),
+                      gum);
+
+  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+  gum->clist = gtk_clist_new (2);
+  gtk_clist_set_shadow_type (GTK_CLIST (gum->clist), GTK_SHADOW_IN);
+  gtk_clist_set_selection_mode (GTK_CLIST (gum->clist), GTK_SELECTION_BROWSE);
+  gtk_widget_set_usize (gum->clist, 200, 150);
+
+  gtk_clist_set_column_title (GTK_CLIST (gum->clist), 0, _("Unit"));
+  gtk_clist_set_column_auto_resize (GTK_CLIST (gum->clist), 0, TRUE);
+  gtk_clist_set_column_title (GTK_CLIST (gum->clist), 1, _("Factor"));
+  gtk_clist_set_column_auto_resize (GTK_CLIST (gum->clist), 1, TRUE);
+  gtk_clist_column_titles_show (GTK_CLIST (gum->clist));
+
+  hbox = gtk_hbox_new (FALSE, 8);
+  gtk_container_border_width (GTK_CONTAINER (hbox), 0);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+  gtk_widget_show (hbox);
+
+  gtk_box_pack_start (GTK_BOX (hbox), scrolled_win, TRUE, TRUE, 0); 
+  gtk_container_add (GTK_CONTAINER (scrolled_win), gum->clist);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+				  GTK_POLICY_AUTOMATIC,
+				  GTK_POLICY_ALWAYS);
+
+  gtk_widget_show(scrolled_win);
+  gtk_widget_show(gum->clist);
+
+  gtk_container_border_width (GTK_CONTAINER (GTK_DIALOG (gum->selection)->action_area), 2);
+  gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (gum->selection)->action_area),
+			   FALSE);
+  hbbox = gtk_hbutton_box_new ();
+  gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4);
+  gtk_box_pack_end(GTK_BOX (GTK_DIALOG (gum->selection)->action_area), hbbox,
+		   FALSE, FALSE, 0);
+  gtk_widget_show(hbbox);
+
+  button = gtk_button_new_with_label (_("Select"));
+  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked",
+		      (GtkSignalFunc) gimp_unit_menu_selection_select_callback,
+		      gum);
+  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+  gtk_widget_show (button);
+
+  button = gtk_button_new_with_label (_("Close"));
+  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked",
+		      (GtkSignalFunc) gimp_unit_menu_selection_close_callback,
+		      gum);
+  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+  gtk_widget_grab_default (button);
+  gtk_widget_show (button);
+
+  num_units = gimp_unit_get_number_of_units ();
+
+  for (unit = UNIT_END; unit < num_units; unit++)
+    {
+      row[0] = gimp_unit_menu_build_string (gum->format, unit);
+      g_snprintf (buffer, sizeof(buffer), "(%f)",
+		  gimp_unit_get_factor (unit));
+      row[1] = buffer;
+
+      gtk_clist_append (GTK_CLIST (gum->clist), row);
+      gtk_clist_set_row_data (GTK_CLIST (gum->clist),
+			      unit - UNIT_END, (gpointer)unit);
+    }
+
+  /* Now show the dialog */
+  gtk_widget_show(vbox);
+  gtk_widget_show(gum->selection);
+
+  if(gum->unit >= UNIT_END) 
+    {
+      gtk_clist_select_row (GTK_CLIST (gum->clist), gum->unit - UNIT_END, 0);
+      gtk_clist_moveto (GTK_CLIST (gum->clist), gum->unit - UNIT_END,
+			0, 0.0, 0.0); 
+    }
+}
+
+
 static void
 gimp_unit_menu_callback (GtkWidget *widget,
 			 gpointer   data)
@@ -274,7 +515,25 @@ gimp_unit_menu_callback (GtkWidget *widget,
   if (gum->unit == new_unit)
     return;
 
-  gum->unit = new_unit;
+  if (new_unit == 65536)
+    {
+      gtk_option_menu_set_history (GTK_OPTION_MENU (gum),
+				   ((gum->unit < UNIT_END) ?
+				    (gum->unit - gum->start +
+				     ((gum->start == UNIT_PIXEL) ? 1 : 0)) :
+				    (UNIT_END + ((gum->start == UNIT_PIXEL) ?
+						 2 : 0))));
+      if (! gum->selection)
+	gimp_unit_menu_create_selection (gum);
+      return;
+    }
+  else if (gum->selection)
+    {
+      gtk_widget_destroy (gum->selection);
+      gum->selection = NULL;
+    }
+
+  gimp_unit_menu_set_unit (gum, new_unit);
   gtk_signal_emit (GTK_OBJECT (gum),
 		   gimp_unit_menu_signals[GUM_UNIT_CHANGED_SIGNAL]);
 }
diff --git a/libgimp/gimpunitmenu.h b/libgimp/gimpunitmenu.h
index 56814f0222..98999a4cdd 100644
--- a/libgimp/gimpunitmenu.h
+++ b/libgimp/gimpunitmenu.h
@@ -43,6 +43,13 @@ typedef struct _GimpUnitMenuClass  GimpUnitMenuClass;
 struct _GimpUnitMenu
 {
   GtkOptionMenu  optionmenu;
+
+  /* private stuff */
+  GtkWidget     *selection;
+  GtkWidget     *clist;
+
+  /* public */
+  gchar         *format;
   GUnit          unit;
   GUnit          start;
 };
@@ -54,7 +61,7 @@ struct _GimpUnitMenuClass
   void (* gimp_unit_menu) (GimpUnitMenu *gum);
 };
 
-guint          gimp_unit_menu_get_type            (void);
+guint          gimp_unit_menu_get_type (void);
 
 /* format      -- a printf-like format string for the menu items
  * unit        -- the unit selected on widget creation
@@ -71,14 +78,14 @@ guint          gimp_unit_menu_get_type            (void);
  *            %p -- plural
  *            %% -- literal percent
  */
-GtkWidget*     gimp_unit_menu_new                 (gchar*   format,
-						   GUnit    unit,
-						   gboolean with_pixels,
-						   gboolean with_custom);
+GtkWidget*     gimp_unit_menu_new      (gchar       *format,
+					GUnit        unit,
+					gboolean     with_pixels,
+					gboolean     with_custom);
 
-void           gimp_unit_menu_set_unit            (GimpUnitMenu *gum, 
-					           GUnit unit);
-GUnit          gimp_unit_menu_get_unit            (GimpUnitMenu *gum);
+void           gimp_unit_menu_set_unit (GimpUnitMenu *gum, 
+					GUnit         unit);
+GUnit          gimp_unit_menu_get_unit (GimpUnitMenu *gum);
 
 #ifdef __cplusplus
 }
@@ -86,4 +93,3 @@ GUnit          gimp_unit_menu_get_unit            (GimpUnitMenu *gum);
 
 
 #endif /* __GIMP_UNIT_MENU_H__ */
-
diff --git a/libgimpbase/gimpunit.h b/libgimpbase/gimpunit.h
index 29f08c5479..ef7726d9ea 100644
--- a/libgimpbase/gimpunit.h
+++ b/libgimpbase/gimpunit.h
@@ -1,4 +1,7 @@
-/* gimpunit.h
+/* LIBGIMP - The GIMP Library                                                   
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
+ *
+ * gimpunit.h
  * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
  *
  * This library is free software; you can redistribute it and/or
@@ -17,24 +20,23 @@
  * Boston, MA 02111-1307, USA.
  */
 
+/* NOTE:
+ *
+ * This file serves as header for both app/gimpunit.c and libgimp/gimpunit.c
+ * because the unit functions are needed by widgets which are used by both
+ * the gimp app and plugins.
+ */
+
 #ifndef __GIMPUNIT_H__
 #define __GIMPUNIT_H__
 
-
 #include <glib.h>
 
-
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
 
 
-/* I've put this here and not to libgimp/gimpenums.h, because if this
- * file includes libgimp/gimpenums.h there is a name clash wherever
- * someone includes libgimp/gimpunit.h and app/gimpimage.h
- * (the constants RGB, GRAY and INDEXED are defined in both
- * gimpenums.h and gimpimage.h) (is this a bug? don't know...)
- */
 typedef enum
 {
   UNIT_PIXEL = 0,
@@ -42,12 +44,13 @@ typedef enum
   UNIT_MM    = 2,
   UNIT_POINT = 3,
   UNIT_PICA  = 4,
-  UNIT_END /* never use UNIT_END but gimp_unit_get_number_of_units() instead */
+  UNIT_END   = 5     /* never use UNIT_END but
+			gimp_unit_get_number_of_units() instead */
 } GUnit;
 
 
-gint           gimp_unit_get_number_of_units          (void);
-gint           gimp_unit_get_number_of_built_in_units (void);
+gint     gimp_unit_get_number_of_units          (void);
+gint     gimp_unit_get_number_of_built_in_units (void);
 
 /* Create a new user unit and returns it's ID.
  *
@@ -55,13 +58,13 @@ gint           gimp_unit_get_number_of_built_in_units (void);
  * set to TRUE. You will have to set it to FALSE after creation to make
  * the unit definition persistant.
  */
-GUnit          gimp_unit_new                 (gchar  *identifier,
-					      gfloat  factor,
-					      gint    digits,
-					      gchar  *symbol,
-					      gchar  *abbreviation,
-					      gchar  *singular,
-					      gchar  *plural);
+GUnit    gimp_unit_new                 (gchar  *identifier,
+					gfloat  factor,
+					gint    digits,
+					gchar  *symbol,
+					gchar  *abbreviation,
+					gchar  *singular,
+					gchar  *plural);
 
 /* The following functions fall back to inch (not pixel, as pixel is not
  * a 'real' unit) if the value passed is out of range.
@@ -72,19 +75,16 @@ GUnit          gimp_unit_new                 (gchar  *identifier,
 /* If the deletion flag for a unit is TRUE on GIMP exit, this unit
  * will not be saved in the user units database.
  */
-guint          gimp_unit_get_deletion_flag   (GUnit  unit);
-void           gimp_unit_set_deletion_flag   (GUnit  unit,
+guint    gimp_unit_get_deletion_flag   (GUnit  unit);
+void     gimp_unit_set_deletion_flag   (GUnit  unit,
 					      guint  deletion_flag);
 
-/* This one is an untranslated string for gimprc */
-const gchar *  gimp_unit_get_identifier      (GUnit  unit);
-
 /* The meaning of 'factor' is:
  * distance_in_units == ( factor * distance_in_inches )
  *
  * Returns 0 for unit == UNIT_PIXEL as we don't have resolution info here
  */
-gfloat         gimp_unit_get_factor          (GUnit  unit);
+gfloat   gimp_unit_get_factor          (GUnit  unit);
 
 /* The following function gives a hint how many digits a spinbutton
  * should provide to get approximately the accuracy of an inch-spinbutton
@@ -92,12 +92,21 @@ gfloat         gimp_unit_get_factor          (GUnit  unit);
  *
  * Returns 0 for unit == UNIT_PIXEL as we don't have resolution info here.
  */
-gint           gimp_unit_get_digits          (GUnit  unit);
+gint     gimp_unit_get_digits          (GUnit  unit);
 
-const gchar *  gimp_unit_get_symbol          (GUnit  unit);
-const gchar *  gimp_unit_get_abbreviation    (GUnit  unit);
-const gchar *  gimp_unit_get_singular        (GUnit  unit);
-const gchar *  gimp_unit_get_plural          (GUnit  unit);
+/* NOTE:
+ *
+ * the gchar pointer returned is constant in the gimp application but must
+ * be g_free()'d by plug-ins.
+ */
+
+/* This one is an untranslated string for gimprc */
+gchar *  gimp_unit_get_identifier      (GUnit  unit);
+
+gchar *  gimp_unit_get_symbol          (GUnit  unit);
+gchar *  gimp_unit_get_abbreviation    (GUnit  unit);
+gchar *  gimp_unit_get_singular        (GUnit  unit);
+gchar *  gimp_unit_get_plural          (GUnit  unit);
 
 #ifdef __cplusplus
 }
diff --git a/libgimpwidgets/gimpsizeentry.c b/libgimpwidgets/gimpsizeentry.c
index 9a25fa084b..33f1a471e4 100644
--- a/libgimpwidgets/gimpsizeentry.c
+++ b/libgimpwidgets/gimpsizeentry.c
@@ -233,7 +233,9 @@ gimp_size_entry_new (gint             number_of_fields,
 					    gsef->min_value, 
 					    gsef->max_value,
 					    1.0, 10.0, 0.0));
-      gsef->value_spinbutton = gtk_spin_button_new (adjustment, 1.0, 3);
+      gsef->value_spinbutton =
+	gtk_spin_button_new (adjustment, 1.0,
+			     MIN (gimp_unit_get_digits (unit), 5) + 1);
       gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON(gsef->value_spinbutton),
 				       GTK_SHADOW_NONE);
       gtk_widget_set_usize (gsef->value_spinbutton, spinbutton_usize, 0);
@@ -789,7 +791,7 @@ gimp_size_entry_update_unit (GimpSizeEntry *gse,
 					gsef->refval_digits);
 	  else
 	    gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
-					MAX(gimp_unit_get_digits (unit) + 1, 3));
+					MIN(gimp_unit_get_digits (unit), 5) + 1);
 	}
       else if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
 	{
@@ -856,7 +858,8 @@ gimp_size_entry_focus_in_callback (GtkWidget *widget,
 				   GdkEvent  *event,
 				   gpointer   data)
 {
-  gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
+
+  /* gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1); */
 
   return TRUE;
 }
diff --git a/libgimpwidgets/gimpunitmenu.c b/libgimpwidgets/gimpunitmenu.c
index 77db93da4a..911fac16e0 100644
--- a/libgimpwidgets/gimpunitmenu.c
+++ b/libgimpwidgets/gimpunitmenu.c
@@ -59,6 +59,7 @@ gimp_unit_menu_class_init (GimpUnitMenuClass *class)
 static void
 gimp_unit_menu_init (GimpUnitMenu *gum)
 {
+  gum->selection = NULL;
   gum->unit = UNIT_PIXEL;
   gum->start = 0;
 }
@@ -91,51 +92,85 @@ gimp_unit_menu_get_type ()
 
 
 GtkWidget*
-gimp_unit_menu_new (gchar    *format,
-		    GUnit     unit,
-		    gboolean  with_pixels,
-		    gboolean  with_custom)
+gimp_unit_menu_new (gchar       *format,
+		    GUnit        unit,
+		    gboolean     with_pixels,
+		    gboolean     with_custom)
 {
-  GimpUnitMenu  *gum;
-  GtkWidget     *menu;
-  GtkWidget     *menuitem;
-  GUnit          u;
+  GimpUnitMenu *gum;
+  GtkWidget    *menu;
+  GtkWidget    *menuitem;
+  GUnit         u;
+
+  g_return_val_if_fail ((unit >= UNIT_PIXEL) &&
+			(unit < gimp_unit_get_number_of_units ()), NULL);
 
   if (with_custom); /* avoid 'unused variable' compiler warning */
 
   gum = gtk_type_new (gimp_unit_menu_get_type ());
 
+  gum->format = g_strdup (format);
   /* if we don't want pixels, start with inches */
   gum->start = with_pixels ? UNIT_PIXEL : UNIT_INCH;
 
-  if ( (unit < gum->start) || (unit >= UNIT_END) )
+  if (unit < gum->start)
     unit = gum->start;
 
   menu = gtk_menu_new();
   for (u = gum->start; u < gimp_unit_get_number_of_built_in_units(); u++)
     {
       menuitem =
-	gtk_menu_item_new_with_label (gimp_unit_menu_build_string(format, u) );
+	gtk_menu_item_new_with_label (gimp_unit_menu_build_string (format, u));
       gtk_menu_append (GTK_MENU (menu), menuitem);
       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
 			  (GtkSignalFunc) gimp_unit_menu_callback, gum);
-      gtk_widget_show(menuitem);
       gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu", (gpointer)u);
+      gtk_widget_show(menuitem);
 
       /* add a separator after pixels */
       if (u == UNIT_PIXEL)
 	{
 	  menuitem = gtk_menu_item_new ();
-	  gtk_widget_show (menuitem);
 	  gtk_menu_append (GTK_MENU (menu), menuitem);
+	  gtk_widget_show (menuitem);
 	}
-    } 
+    }
+  if (unit >= gimp_unit_get_number_of_built_in_units ())
+    {
+      menuitem = gtk_menu_item_new ();
+      gtk_menu_append (GTK_MENU (menu), menuitem);
+      gtk_widget_show (menuitem);
+      
+      menuitem =
+	gtk_menu_item_new_with_label (gimp_unit_menu_build_string (format, unit));
+      gtk_menu_append (GTK_MENU (menu), menuitem);
+      gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+			  (GtkSignalFunc) gimp_unit_menu_callback, gum);
+      gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu", (gpointer)unit);
+      gtk_widget_show(menuitem);
+    }
 
+  menuitem = gtk_menu_item_new ();
+  gtk_menu_append (GTK_MENU (menu), menuitem);
+  gtk_widget_show (menuitem);
+      
+  menuitem =
+    gtk_menu_item_new_with_label (_("More..."));
+  gtk_menu_append (GTK_MENU (menu), menuitem);
+  gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+		      (GtkSignalFunc) gimp_unit_menu_callback, gum);
+  gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu",
+		       (gpointer)65536);
+  gtk_widget_show(menuitem);
+  
   gtk_option_menu_set_menu (GTK_OPTION_MENU (gum), menu);
 
   gum->unit = unit;
   gtk_option_menu_set_history (GTK_OPTION_MENU (gum),
-			       unit - gum->start + (with_pixels ? 1 : 0));
+			       ((unit < UNIT_END) ?
+				(unit - gum->start +
+				 (with_pixels ? 1 : 0)) :
+				(UNIT_END + (with_pixels ? 2 : 0))));
   
   return GTK_WIDGET (gum);
 }
@@ -145,17 +180,54 @@ void
 gimp_unit_menu_set_unit (GimpUnitMenu *gum,
 			 GUnit         unit)
 {
+  GtkWidget *menuitem = NULL;
+  GList     *items;
+  gint       user_unit;
+
   g_return_if_fail (gum != NULL);
   g_return_if_fail (GIMP_IS_UNIT_MENU (gum));
+  g_return_if_fail ((unit >= gum->start) &&
+		    (unit < gimp_unit_get_number_of_units ()));
 
-  /* replace UNIT_END when unit database is there */
+  if (unit == gum->unit)
+    return;
 
-  g_return_if_fail ((unit >= gum->start) && (unit < UNIT_END));
+  items = GTK_MENU_SHELL (GTK_OPTION_MENU (gum)->menu)->children;
+  user_unit = UNIT_END + ((gum->start == UNIT_PIXEL) ? 2 : 0);
+
+  if (unit >= UNIT_END)
+    {
+      if ((g_list_length (items) - 3) >= user_unit)
+	{
+	  gtk_widget_destroy (GTK_WIDGET (g_list_nth_data (items,
+							   user_unit - 1)));
+	  gtk_widget_destroy (GTK_WIDGET (g_list_nth_data (items,
+							   user_unit - 1)));
+	}
+
+      menuitem = gtk_menu_item_new ();
+      gtk_menu_append (GTK_MENU (GTK_OPTION_MENU (gum)->menu), menuitem);
+      gtk_menu_reorder_child (GTK_MENU (GTK_OPTION_MENU (gum)->menu),
+			      menuitem, user_unit - 1);
+      gtk_widget_show(menuitem);
+      
+      menuitem =
+	gtk_menu_item_new_with_label (gimp_unit_menu_build_string (gum->format, unit));
+      gtk_menu_append (GTK_MENU (GTK_OPTION_MENU (gum)->menu), menuitem);
+      gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+			  (GtkSignalFunc) gimp_unit_menu_callback, gum);
+      gtk_object_set_data (GTK_OBJECT (menuitem), "gimp_unit_menu", (gpointer)unit);
+      gtk_menu_reorder_child (GTK_MENU (GTK_OPTION_MENU (gum)->menu),
+			      menuitem, user_unit);
+      gtk_widget_show(menuitem);
+    }
 
   gum->unit = unit;
   gtk_option_menu_set_history (GTK_OPTION_MENU (gum),
-			       unit - gum->start +
-			       ((gum->start == UNIT_PIXEL) ? 1 : 0));
+			       ((unit < UNIT_END) ?
+				(unit - gum->start +
+				 ((gum->start == UNIT_PIXEL) ? 1 : 0)) :
+				(UNIT_END + ((gum->start == UNIT_PIXEL) ? 2 : 0))));
 }
 
 GUnit
@@ -260,6 +332,175 @@ gimp_unit_menu_build_string (gchar *format, GUnit unit)
 }
 
 
+/* private callbacks of gimp_unit_menu_create_selection ()
+ */
+
+static void
+gimp_unit_menu_selection_select_callback (GtkWidget *widget,
+					  gpointer   data)
+{
+  GimpUnitMenu *gum;
+  GUnit         unit;
+
+  gum = GIMP_UNIT_MENU (data);
+
+  if (gum->selection && GTK_CLIST (gum->clist)->selection)
+    {
+      unit = (GUnit)gtk_clist_get_row_data (GTK_CLIST (gum->clist),
+					    (int)(GTK_CLIST (gum->clist)->selection->data));
+      gimp_unit_menu_set_unit (gum, unit);
+      gtk_signal_emit (GTK_OBJECT (gum),
+		       gimp_unit_menu_signals[GUM_UNIT_CHANGED_SIGNAL]);
+      
+      gtk_widget_destroy (gum->selection);
+      gum->selection = NULL;
+      gum->clist = NULL;
+    }
+}
+
+static void
+gimp_unit_menu_selection_close_callback (GtkWidget *widget,
+					 gpointer   data)
+{
+  GimpUnitMenu *gum;
+
+  gum = GIMP_UNIT_MENU (data);
+
+  if (gum->selection)
+    {
+      gtk_widget_destroy (gum->selection);
+      gum->selection = NULL;
+      gum->clist = NULL;
+    }
+}
+
+static gint
+gimp_unit_menu_selection_delete_callback (GtkWidget *widget,
+					  GdkEvent  *event,
+					  gpointer   data)
+{
+  gimp_unit_menu_selection_close_callback (NULL, data);
+
+  return TRUE;
+}
+
+/* private function of gimp_unit_menu_callback ()
+ */
+
+static void
+gimp_unit_menu_create_selection (GimpUnitMenu *gum)
+{
+  GtkWidget *hbox;
+  GtkWidget *hbbox;
+  GtkWidget *vbox;
+  GtkWidget *scrolled_win;
+  GtkWidget *button;
+  gchar     *row[2];
+  GUnit      unit;
+  gint       num_units;
+  gchar      buffer[32];
+
+  gum->selection = gtk_dialog_new ();
+  gtk_window_set_wmclass (GTK_WINDOW (gum->selection),
+			  "unitselection", "Gimp");
+  gtk_window_set_title (GTK_WINDOW (gum->selection), _("Unit Selection"));
+  gtk_window_set_policy(GTK_WINDOW(gum->selection), FALSE, TRUE, FALSE);
+
+  vbox = gtk_vbox_new (FALSE, 2);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (gum->selection)->vbox), vbox);
+  gtk_container_border_width (GTK_CONTAINER (vbox), 2);
+  gtk_widget_show (vbox);
+
+  gtk_signal_connect (GTK_OBJECT (gum->selection), "delete_event",
+                      GTK_SIGNAL_FUNC (gimp_unit_menu_selection_delete_callback),
+                      gum);
+
+  gtk_signal_connect (GTK_OBJECT (gum), "destroy",
+                      GTK_SIGNAL_FUNC (gimp_unit_menu_selection_close_callback),
+                      gum);
+  gtk_signal_connect (GTK_OBJECT (gum), "unmap",
+                      GTK_SIGNAL_FUNC (gimp_unit_menu_selection_close_callback),
+                      gum);
+
+  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+  gum->clist = gtk_clist_new (2);
+  gtk_clist_set_shadow_type (GTK_CLIST (gum->clist), GTK_SHADOW_IN);
+  gtk_clist_set_selection_mode (GTK_CLIST (gum->clist), GTK_SELECTION_BROWSE);
+  gtk_widget_set_usize (gum->clist, 200, 150);
+
+  gtk_clist_set_column_title (GTK_CLIST (gum->clist), 0, _("Unit"));
+  gtk_clist_set_column_auto_resize (GTK_CLIST (gum->clist), 0, TRUE);
+  gtk_clist_set_column_title (GTK_CLIST (gum->clist), 1, _("Factor"));
+  gtk_clist_set_column_auto_resize (GTK_CLIST (gum->clist), 1, TRUE);
+  gtk_clist_column_titles_show (GTK_CLIST (gum->clist));
+
+  hbox = gtk_hbox_new (FALSE, 8);
+  gtk_container_border_width (GTK_CONTAINER (hbox), 0);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+  gtk_widget_show (hbox);
+
+  gtk_box_pack_start (GTK_BOX (hbox), scrolled_win, TRUE, TRUE, 0); 
+  gtk_container_add (GTK_CONTAINER (scrolled_win), gum->clist);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+				  GTK_POLICY_AUTOMATIC,
+				  GTK_POLICY_ALWAYS);
+
+  gtk_widget_show(scrolled_win);
+  gtk_widget_show(gum->clist);
+
+  gtk_container_border_width (GTK_CONTAINER (GTK_DIALOG (gum->selection)->action_area), 2);
+  gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (gum->selection)->action_area),
+			   FALSE);
+  hbbox = gtk_hbutton_box_new ();
+  gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4);
+  gtk_box_pack_end(GTK_BOX (GTK_DIALOG (gum->selection)->action_area), hbbox,
+		   FALSE, FALSE, 0);
+  gtk_widget_show(hbbox);
+
+  button = gtk_button_new_with_label (_("Select"));
+  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked",
+		      (GtkSignalFunc) gimp_unit_menu_selection_select_callback,
+		      gum);
+  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+  gtk_widget_show (button);
+
+  button = gtk_button_new_with_label (_("Close"));
+  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked",
+		      (GtkSignalFunc) gimp_unit_menu_selection_close_callback,
+		      gum);
+  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+  gtk_widget_grab_default (button);
+  gtk_widget_show (button);
+
+  num_units = gimp_unit_get_number_of_units ();
+
+  for (unit = UNIT_END; unit < num_units; unit++)
+    {
+      row[0] = gimp_unit_menu_build_string (gum->format, unit);
+      g_snprintf (buffer, sizeof(buffer), "(%f)",
+		  gimp_unit_get_factor (unit));
+      row[1] = buffer;
+
+      gtk_clist_append (GTK_CLIST (gum->clist), row);
+      gtk_clist_set_row_data (GTK_CLIST (gum->clist),
+			      unit - UNIT_END, (gpointer)unit);
+    }
+
+  /* Now show the dialog */
+  gtk_widget_show(vbox);
+  gtk_widget_show(gum->selection);
+
+  if(gum->unit >= UNIT_END) 
+    {
+      gtk_clist_select_row (GTK_CLIST (gum->clist), gum->unit - UNIT_END, 0);
+      gtk_clist_moveto (GTK_CLIST (gum->clist), gum->unit - UNIT_END,
+			0, 0.0, 0.0); 
+    }
+}
+
+
 static void
 gimp_unit_menu_callback (GtkWidget *widget,
 			 gpointer   data)
@@ -274,7 +515,25 @@ gimp_unit_menu_callback (GtkWidget *widget,
   if (gum->unit == new_unit)
     return;
 
-  gum->unit = new_unit;
+  if (new_unit == 65536)
+    {
+      gtk_option_menu_set_history (GTK_OPTION_MENU (gum),
+				   ((gum->unit < UNIT_END) ?
+				    (gum->unit - gum->start +
+				     ((gum->start == UNIT_PIXEL) ? 1 : 0)) :
+				    (UNIT_END + ((gum->start == UNIT_PIXEL) ?
+						 2 : 0))));
+      if (! gum->selection)
+	gimp_unit_menu_create_selection (gum);
+      return;
+    }
+  else if (gum->selection)
+    {
+      gtk_widget_destroy (gum->selection);
+      gum->selection = NULL;
+    }
+
+  gimp_unit_menu_set_unit (gum, new_unit);
   gtk_signal_emit (GTK_OBJECT (gum),
 		   gimp_unit_menu_signals[GUM_UNIT_CHANGED_SIGNAL]);
 }
diff --git a/libgimpwidgets/gimpunitmenu.h b/libgimpwidgets/gimpunitmenu.h
index 56814f0222..98999a4cdd 100644
--- a/libgimpwidgets/gimpunitmenu.h
+++ b/libgimpwidgets/gimpunitmenu.h
@@ -43,6 +43,13 @@ typedef struct _GimpUnitMenuClass  GimpUnitMenuClass;
 struct _GimpUnitMenu
 {
   GtkOptionMenu  optionmenu;
+
+  /* private stuff */
+  GtkWidget     *selection;
+  GtkWidget     *clist;
+
+  /* public */
+  gchar         *format;
   GUnit          unit;
   GUnit          start;
 };
@@ -54,7 +61,7 @@ struct _GimpUnitMenuClass
   void (* gimp_unit_menu) (GimpUnitMenu *gum);
 };
 
-guint          gimp_unit_menu_get_type            (void);
+guint          gimp_unit_menu_get_type (void);
 
 /* format      -- a printf-like format string for the menu items
  * unit        -- the unit selected on widget creation
@@ -71,14 +78,14 @@ guint          gimp_unit_menu_get_type            (void);
  *            %p -- plural
  *            %% -- literal percent
  */
-GtkWidget*     gimp_unit_menu_new                 (gchar*   format,
-						   GUnit    unit,
-						   gboolean with_pixels,
-						   gboolean with_custom);
+GtkWidget*     gimp_unit_menu_new      (gchar       *format,
+					GUnit        unit,
+					gboolean     with_pixels,
+					gboolean     with_custom);
 
-void           gimp_unit_menu_set_unit            (GimpUnitMenu *gum, 
-					           GUnit unit);
-GUnit          gimp_unit_menu_get_unit            (GimpUnitMenu *gum);
+void           gimp_unit_menu_set_unit (GimpUnitMenu *gum, 
+					GUnit         unit);
+GUnit          gimp_unit_menu_get_unit (GimpUnitMenu *gum);
 
 #ifdef __cplusplus
 }
@@ -86,4 +93,3 @@ GUnit          gimp_unit_menu_get_unit            (GimpUnitMenu *gum);
 
 
 #endif /* __GIMP_UNIT_MENU_H__ */
-
diff --git a/plug-ins/common/tiff.c b/plug-ins/common/tiff.c
index 8411e35f0e..76422f6384 100644
--- a/plug-ins/common/tiff.c
+++ b/plug-ins/common/tiff.c
@@ -35,7 +35,9 @@
 #include <tiffio.h>
 #include "gtk/gtk.h"
 #include "libgimp/gimp.h"
-
+#ifdef GIMP_HAVE_RESOLUTION_INFO
+#include "libgimp/gimpunit.h"
+#endif
 
 typedef struct
 {
@@ -467,33 +469,34 @@ static gint32 load_image (char *filename) {
   /* any resolution info in the file? */
 #ifdef GIMP_HAVE_RESOLUTION_INFO
   {
-    float xres=0.0, yres=0.0;
-    unsigned short units;
+    float xres = 72.0, yres = 72.0;
+    unsigned short read_unit;
+    GUnit unit = UNIT_PIXEL; /* invalid unit */
 
     if (TIFFGetField (tif, TIFFTAG_XRESOLUTION, &xres)) {
       if (TIFFGetField (tif, TIFFTAG_YRESOLUTION, &yres)) {
 
-	if (TIFFGetField (tif, TIFFTAG_RESOLUTIONUNIT, &units)) {
-	  switch(units) {
+	if (TIFFGetField (tif, TIFFTAG_RESOLUTIONUNIT, &read_unit)) {
+	  switch(read_unit) {
 	  case RESUNIT_NONE:
 	    /* ImageMagick writes files with this silly resunit */
 	    g_message("TIFF warning: resolution units meaningless, "
 		      "forcing 72 dpi\n");
-	    xres = 72.0;
-	    yres = 72.0;
 	    break;
 
 	  case RESUNIT_INCH:
+	    unit = UNIT_INCH;
 	    break;
 
 	  case RESUNIT_CENTIMETER:
 	    xres *= 2.54;
 	    yres *= 2.54;
+	    unit = UNIT_MM; /* as this is our default metric unit */
 	    break;
 
 	  default:
 	    g_message("TIFF file error: unknown resolution unit type %d, "
-		      "assuming dpi\n", units);
+		      "assuming dpi\n", read_unit);
 	  }
 	} else { /* no res unit tag */
 	  /* old AppleScan software produces these */
@@ -514,6 +517,8 @@ static gint32 load_image (char *filename) {
 
       /* now set the new image's resolution info */
       gimp_image_set_resolution (image, xres, yres);
+      if (unit != UNIT_PIXEL)
+	gimp_image_set_unit(image, unit);
     }
 
     /* no x res tag => we assume we have no resolution info, so we
@@ -1135,14 +1140,31 @@ static gint save_image (char *filename, gint32 image, gint32 layer) {
   {
       float xresolution;
       float yresolution;
+      unsigned short save_unit = RESUNIT_INCH;
+      GUnit unit;
+      float factor;
 
       gimp_image_get_resolution (image, &xresolution, &yresolution);
+      unit = gimp_image_get_unit (image);
+      factor = gimp_unit_get_factor (unit);
+
+      /*  if we have a metric unit, save the resolution as centimeters
+       */
+      if ((ABS(factor - 0.0254) < 1e-5) ||  /* m */
+	  (ABS(factor - 0.254) < 1e-5) ||   /* why not ;) */
+	  (ABS(factor - 2.54) < 1e-5) ||    /* cm */
+	  (ABS(factor - 25.4) < 1e-5))      /* mm */
+	{
+	  save_unit = RESUNIT_CENTIMETER;
+	  xresolution /= 2.54;
+	  yresolution /= 2.54;
+	}
 
       if (xresolution > 1e-5 && yresolution > 1e-5)
       {
 	  TIFFSetField (tif, TIFFTAG_XRESOLUTION, xresolution);
 	  TIFFSetField (tif, TIFFTAG_YRESOLUTION, yresolution);
-	  TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+	  TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, save_unit);
       }
   }
 #endif /* GIMP_HAVE_RESOLUTION_INFO */
diff --git a/plug-ins/tiff/tiff.c b/plug-ins/tiff/tiff.c
index 8411e35f0e..76422f6384 100644
--- a/plug-ins/tiff/tiff.c
+++ b/plug-ins/tiff/tiff.c
@@ -35,7 +35,9 @@
 #include <tiffio.h>
 #include "gtk/gtk.h"
 #include "libgimp/gimp.h"
-
+#ifdef GIMP_HAVE_RESOLUTION_INFO
+#include "libgimp/gimpunit.h"
+#endif
 
 typedef struct
 {
@@ -467,33 +469,34 @@ static gint32 load_image (char *filename) {
   /* any resolution info in the file? */
 #ifdef GIMP_HAVE_RESOLUTION_INFO
   {
-    float xres=0.0, yres=0.0;
-    unsigned short units;
+    float xres = 72.0, yres = 72.0;
+    unsigned short read_unit;
+    GUnit unit = UNIT_PIXEL; /* invalid unit */
 
     if (TIFFGetField (tif, TIFFTAG_XRESOLUTION, &xres)) {
       if (TIFFGetField (tif, TIFFTAG_YRESOLUTION, &yres)) {
 
-	if (TIFFGetField (tif, TIFFTAG_RESOLUTIONUNIT, &units)) {
-	  switch(units) {
+	if (TIFFGetField (tif, TIFFTAG_RESOLUTIONUNIT, &read_unit)) {
+	  switch(read_unit) {
 	  case RESUNIT_NONE:
 	    /* ImageMagick writes files with this silly resunit */
 	    g_message("TIFF warning: resolution units meaningless, "
 		      "forcing 72 dpi\n");
-	    xres = 72.0;
-	    yres = 72.0;
 	    break;
 
 	  case RESUNIT_INCH:
+	    unit = UNIT_INCH;
 	    break;
 
 	  case RESUNIT_CENTIMETER:
 	    xres *= 2.54;
 	    yres *= 2.54;
+	    unit = UNIT_MM; /* as this is our default metric unit */
 	    break;
 
 	  default:
 	    g_message("TIFF file error: unknown resolution unit type %d, "
-		      "assuming dpi\n", units);
+		      "assuming dpi\n", read_unit);
 	  }
 	} else { /* no res unit tag */
 	  /* old AppleScan software produces these */
@@ -514,6 +517,8 @@ static gint32 load_image (char *filename) {
 
       /* now set the new image's resolution info */
       gimp_image_set_resolution (image, xres, yres);
+      if (unit != UNIT_PIXEL)
+	gimp_image_set_unit(image, unit);
     }
 
     /* no x res tag => we assume we have no resolution info, so we
@@ -1135,14 +1140,31 @@ static gint save_image (char *filename, gint32 image, gint32 layer) {
   {
       float xresolution;
       float yresolution;
+      unsigned short save_unit = RESUNIT_INCH;
+      GUnit unit;
+      float factor;
 
       gimp_image_get_resolution (image, &xresolution, &yresolution);
+      unit = gimp_image_get_unit (image);
+      factor = gimp_unit_get_factor (unit);
+
+      /*  if we have a metric unit, save the resolution as centimeters
+       */
+      if ((ABS(factor - 0.0254) < 1e-5) ||  /* m */
+	  (ABS(factor - 0.254) < 1e-5) ||   /* why not ;) */
+	  (ABS(factor - 2.54) < 1e-5) ||    /* cm */
+	  (ABS(factor - 25.4) < 1e-5))      /* mm */
+	{
+	  save_unit = RESUNIT_CENTIMETER;
+	  xresolution /= 2.54;
+	  yresolution /= 2.54;
+	}
 
       if (xresolution > 1e-5 && yresolution > 1e-5)
       {
 	  TIFFSetField (tif, TIFFTAG_XRESOLUTION, xresolution);
 	  TIFFSetField (tif, TIFFTAG_YRESOLUTION, yresolution);
-	  TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+	  TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, save_unit);
       }
   }
 #endif /* GIMP_HAVE_RESOLUTION_INFO */
diff --git a/unitrc b/unitrc
new file mode 100644
index 0000000000..7e9ed1b51b
--- /dev/null
+++ b/unitrc
@@ -0,0 +1,55 @@
+# GIMP unitrc
+# This file contains your user unit database. You can
+# modify this list with the unit editor. You are not
+# supposed to edit it manually, but of course you can do.
+# This file will be entirely rewritten every time
+# you quit the gimp.
+
+(unit-info "centimeters"
+   (factor 2.540000)
+   (digits 2)
+   (symbol "cm")
+   (abbreviation "cm")
+   (singular "centimeter")
+   (plural "centimeters"))
+
+(unit-info "meters"
+   (factor 0.025400)
+   (digits 4)
+   (symbol "m")
+   (abbreviation "m")
+   (singular "meter")
+   (plural "meters"))
+
+(unit-info "feet"
+   (factor 0.083333)
+   (digits 4)
+   (symbol "'")
+   (abbreviation "ft")
+   (singular "foot")
+   (plural "feet"))
+
+(unit-info "yards"
+   (factor 0.027778)
+   (digits 4)
+   (symbol "yd")
+   (abbreviation "yd")
+   (singular "yard")
+   (plural "yards"))
+
+(unit-info "typorg. points"
+   (factor 72.270000)
+   (digits 0)
+   (symbol "tpt")
+   (abbreviation "tpt")
+   (singular "typogr. point")
+   (plural "typorg. points"))
+
+(unit-info "typorg. picas"
+   (factor 6.022500)
+   (digits 1)
+   (symbol "tpc")
+   (abbreviation "tpc")
+   (singular "typogr. pica")
+   (plural "typorg. picas"))
+
diff --git a/user_install b/user_install
index 208f23ebf5..e67970810c 100755
--- a/user_install
+++ b/user_install
@@ -16,6 +16,9 @@ mkdir $2
 echo "cp $1/gimprc_user $2/gimprc"
 cp $1/gimprc_user $2/gimprc
 
+echo "cp $1/unitrc $2/unitrc"
+cp $1/unitrc $2/unitrc
+
 echo "cp $1/gtkrc $2/gtkrc"
 cp $1/gtkrc $2/gtkrc
 
diff --git a/user_install.bat b/user_install.bat
index d1bf74b1e3..c37957a899 100644
--- a/user_install.bat
+++ b/user_install.bat
@@ -2,6 +2,7 @@
 @echo off
 mkdir %2
 copy %1\gimprc_user %2\gimprc
+copy %1\unitrc %2\unitrc
 copy %1\gtkrc %2\gtkrc
 mkdir %2\brushes
 mkdir %2\gradients