changeset 2168:95fee4a1420e

[xemacs-hg @ 2004-07-07 12:00:58 by malcolmp] Working GK tab_control widget. Other GTK widgets drawn with the correct location and size.
author malcolmp
date Wed, 07 Jul 2004 12:01:07 +0000
parents 54e1ecdc5778
children dbe0b63537e8
files lisp/ChangeLog lisp/widgets-gtk.el src/ChangeLog src/console-gtk-impl.h src/console-gtk.h src/frame-gtk.c src/glyphs-gtk.c src/gtk-xemacs.c src/gui-gtk.c src/gui-x.c src/menubar-gtk.c src/scrollbar-gtk.c src/ui-gtk.c
diffstat 13 files changed, 456 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Wed Jul 07 01:52:17 2004 +0000
+++ b/lisp/ChangeLog	Wed Jul 07 12:01:07 2004 +0000
@@ -1,3 +1,10 @@
+2004-07-02  Malcolm Purvis  <malcolmp@xemacs.org>
+
+	* widgets-gtk.el (gtk-widget-instantiate-notebook-internal):
+	Loading items into the notebook is now done by C code.
+	* widgets-gtk.el (gtk-widget-instantiate-internal):
+	Style no longer changed.
+
 2004-06-24  Jerry James  <james@xemacs.org>
 
 	* cl-compat.el: Synch with Emacs 21.3.
--- a/lisp/widgets-gtk.el	Wed Jul 07 01:52:17 2004 +0000
+++ b/lisp/widgets-gtk.el	Wed Jul 07 12:01:07 2004 +0000
@@ -90,11 +90,11 @@
 (defun gtk-widget-instantiate-notebook-internal (plist instance)
   (let ((widget (gtk-notebook-new))
 	(items (plist-get plist :items)))
-    (while items
-      (gtk-notebook-append-page widget
-				(gtk-vbox-new nil 3)
-				(gtk-label-new (aref (car items) 0)))
-      (setq items (cdr items)))
+;    (while items
+;      (gtk-notebook-append-page widget
+;				(gtk-vbox-new nil 3)
+;				(gtk-label-new (aref (car items) 0)))
+;      (setq items (cdr items)))
     widget))
 
 (defun gtk-widget-instantiate-progress-internal (plist instance)
@@ -135,11 +135,11 @@
 	 (plist (cdr (map 'list 'identity instantiator)))
 	 (widget (funcall (or (get type 'instantiator) 'ignore)
 			  plist instance)))
-    (add-timeout 0.1 (lambda (obj)
-		       (gtk-widget-set-style obj
-					     (gtk-widget-get-style
-					      (frame-property nil 'text-widget))))
-		 widget)
+;    (add-timeout 0.1 (lambda (obj)
+;		       (gtk-widget-set-style obj
+;					     (gtk-widget-get-style
+;					      (frame-property nil 'text-widget))))
+;		 widget)
     widget))
 
 (defun gtk-widget-property-internal ()
--- a/src/ChangeLog	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/ChangeLog	Wed Jul 07 12:01:07 2004 +0000
@@ -1,3 +1,38 @@
+2004-07-07  Malcolm Purvis  <malcolmp@xemacs.org>
+
+	* console-gtk-impl.h:
+	* console-gtk-impl.h (struct gtk_frame):
+	* console-gtk-impl.h (FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE):
+	  Add hash tables track tab_control callback data.
+	  Added #defines for GTK object data names.
+	* console-gtk.h: Declaration of gtk_widget_to_frame().
+	* frame-gtk.c (gtk_widget_to_frame): New.
+	* frame-gtk.c (gtk_create_widgets):
+	* frame-gtk.c (allocate_gtk_frame_struct):
+	* frame-gtk.c (gtk_mark_frame):
+	Manage frame callbakc hash tables.
+	* glyphs-gtk.c (gtk_map_subwindow): Fix size and moving bugs.
+	* glyphs-gtk.c (gtk_redisplay_widget): Use correct size for widgets.
+	* glyphs-gtk.c (gtk_widget_instantiate_1): Force reconsideration
+	of widget size.
+	* glyphs-gtk.c (gtk_widget_query_geometry): New
+	* glyphs-gtk.c (gtk_register_gui_item): New
+	* glyphs-gtk.c (gtk_add_tab_item): New
+	* glyphs-gtk.c (gtk_tab_control_callback): New
+	* glyphs-gtk.c (gtk_tab_control_instantiate): New
+	* glyphs-gtk.c (gtk_tab_control_redisplay): Set notebook page.
+	Use correct list when loading item.
+	* glyphs-gtk.c (image_instantiator_format_create_glyphs_gtk):
+	Register new methods.
+	* gtk-xemacs.c (gtk_xemacs_size_allocate): Use XEmacs size, not
+	default size, for widgets.
+	* gui-gtk.c: Improved comment.
+	* gui-x.c:  Improved comment.
+	* menubar-gtk.c (__generic_button_callback): Use
+	gtk_widget_to_frame instead of __get_channel.
+	* scrollbar-gtk.c:
+	* ui-gtk.c: Use symbolic names for GTK object data.
+
 2004-07-06  Lutz Euler  <lutz.euler@freenet.de>
 
 	* syntax.c (setup_syntax_cache): Fix behavior for buffers
--- a/src/console-gtk-impl.h	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/console-gtk-impl.h	Wed Jul 07 12:01:07 2004 +0000
@@ -1,4 +1,4 @@
-/* Define X specific console, device, and frame object for XEmacs.
+/* Define GTK specific console, device, and frame object for XEmacs.
    Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
    Copyright (C) 2002 Ben Wing.
@@ -28,6 +28,8 @@
    Ultimately based on FSF, then later on JWZ work for Lemacs.
    Rewritten over time by Ben Wing and Chuck Thompson (original
       multi-device work by Chuck Thompson).
+   Gtk version by William M. Perry
+
  */
 
 #ifndef INCLUDED_console_gtk_impl_h_
@@ -172,6 +174,12 @@
   /* Are we iconfied right now? */
   unsigned int iconified_p :1;
 
+  /* Data for widget callbacks.  It is impossible to pass all the necessary
+     data through the GTK signal API so instead it is registered here and the
+     hash key is passed instead. */
+  Lisp_Object widget_instance_hash_table;
+  Lisp_Object widget_callback_hash_table;
+  Lisp_Object widget_callback_ex_hash_table;
 };
 
 #define FRAME_GTK_DATA(f) FRAME_TYPE_DATA (f, gtk)
@@ -195,8 +203,19 @@
 #define FRAME_GTK_TOTALLY_VISIBLE_P(f) (FRAME_GTK_DATA (f)->totally_visible_p)
 #define FRAME_GTK_VISIBLE_P(f) (FRAME_GTK_DATA (f)->visible_p)
 #define FRAME_GTK_TOP_LEVEL_FRAME_P(f) (FRAME_GTK_DATA (f)->top_level_frame_p)
+#define FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE(f) (FRAME_GTK_DATA (f)->widget_instance_hash_table)
+#define FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE(f) (FRAME_GTK_DATA (f)->widget_callback_hash_table)
+#define FRAME_GTK_WIDGET_CALLBACK_EX_HASH_TABLE(f) (FRAME_GTK_DATA (f)->widget_callback_ex_hash_table)
 
 extern struct console_type *gtk_console_type;
 
+/* Special data used to quickly identify the frame that contains a widget. */
+#define GTK_DATA_FRAME_IDENTIFIER "xemacs::frame"
+
+/* The hashcode in the frame hash table of a tab_control tab's callback data. */
+#define GTK_DATA_TAB_HASHCODE_IDENTIFIER "xemacs::tab_hashcode"
+
+#define GTK_DATA_GUI_IDENTIFIER "xemacs::gui_id"
+
 #endif /* HAVE_GTK */
 #endif /* INCLUDED_console_gtk_impl_h_ */
--- a/src/console-gtk.h	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/console-gtk.h	Wed Jul 07 12:01:07 2004 +0000
@@ -48,6 +48,7 @@
 
 extern int gtk_selection_timeout;
 
+struct frame *gtk_widget_to_frame (GtkWidget *);
 struct frame *gtk_any_window_to_frame (struct device *d, GdkWindow *);
 struct frame *gtk_window_to_frame (struct device *d, GdkWindow *);
 struct frame *gtk_any_widget_or_parent_to_frame (struct device *d, GtkWidget *widget);
--- a/src/frame-gtk.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/frame-gtk.c	Wed Jul 07 12:01:07 2004 +0000
@@ -1,4 +1,4 @@
-/* Functions for the X window system.
+/* Functions for the GTK toolkit.
    Copyright (C) 1989, 1992-5, 1997 Free Software Foundation, Inc.
    Copyright (C) 1995, 1996, 2002, 2003 Ben Wing.
 
@@ -39,6 +39,7 @@
 #include "dragdrop.h"
 #endif
 
+#include "elhash.h"
 #include "console-gtk-impl.h"
 #include "glyphs-gtk.h"
 #include "objects-gtk-impl.h"
@@ -55,7 +56,6 @@
 #define INTERNAL_BORDER_WIDTH 0
 
 #define TRANSIENT_DATA_IDENTIFIER "xemacs::transient_for"
-#define FRAME_DATA_IDENTIFIER "xemacs::frame"
 #define UNMAPPED_DATA_IDENTIFIER "xemacs::initially_unmapped"
 
 #define STUPID_X_SPECIFIC_GTK_STUFF
@@ -113,6 +113,23 @@
 /*                          helper functions                            */
 /************************************************************************/
 
+/* Return the Emacs frame-object which contains the given widget. */
+struct frame *
+gtk_widget_to_frame (GtkWidget *w)
+{
+  struct frame *f = NULL;
+
+  for (; w; w = w->parent)
+    {
+      if ((f = (struct frame *) gtk_object_get_data (GTK_OBJECT (w),
+						     GTK_DATA_FRAME_IDENTIFIER)))
+	return (f);
+    }
+
+  return (selected_frame());
+}
+
+
 /* Return the Emacs frame-object corresponding to an X window */
 struct frame *
 gtk_window_to_frame (struct device *d, GdkWindow *wdesc)
@@ -824,7 +841,9 @@
 
   gtk_container_set_border_width (GTK_CONTAINER (shell), 0);
 
-  gtk_object_set_data (GTK_OBJECT (shell), FRAME_DATA_IDENTIFIER, f);
+  /* Add a mapping from widget to frame to help widget callbacks quickly find
+     their corresponding frame. */
+  gtk_object_set_data (GTK_OBJECT (shell), GTK_DATA_FRAME_IDENTIFIER, f);
 
   FRAME_GTK_SHELL_WIDGET (f) = shell;
 
@@ -953,6 +972,18 @@
   FRAME_GTK_MENUBAR_DATA (f) = Qnil;
   for (i = 0; i < 3; i++)
     FRAME_GTK_LISP_WIDGETS (f)[i] = Qnil;
+
+  /*
+    Hashtables of callback data for glyphs on the frame.  Make them EQ because
+    we only use ints as keys.  Otherwise we run into stickiness in redisplay
+    because internal_equal() can QUIT.  See enter_redisplay_critical_section().
+*/
+  FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE (f) =
+    make_lisp_hash_table (50, HASH_TABLE_VALUE_WEAK, HASH_TABLE_EQ);
+  FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE (f) =
+    make_lisp_hash_table (50, HASH_TABLE_VALUE_WEAK, HASH_TABLE_EQ);
+  FRAME_GTK_WIDGET_CALLBACK_EX_HASH_TABLE (f) =
+    make_lisp_hash_table (50, HASH_TABLE_VALUE_WEAK, HASH_TABLE_EQ);
 }
 
 
@@ -1040,6 +1071,9 @@
   mark_object (FRAME_GTK_LISP_WIDGETS (f)[0]);
   mark_object (FRAME_GTK_LISP_WIDGETS (f)[1]);
   mark_object (FRAME_GTK_LISP_WIDGETS (f)[2]);
+  mark_object (FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE (f));
+  mark_object (FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE (f));
+  mark_object (FRAME_GTK_WIDGET_CALLBACK_EX_HASH_TABLE (f));
 }
 
 static void
--- a/src/glyphs-gtk.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/glyphs-gtk.c	Wed Jul 07 12:01:07 2004 +0000
@@ -62,6 +62,8 @@
 #include "lstream.h"
 #include "opaque.h"
 #include "window.h"
+#include "elhash.h"
+#include "events.h"
 
 #include "console-gtk-impl.h"
 #include "glyphs-gtk.h"
@@ -76,6 +78,9 @@
 #include <X11/xpm.h>
 #endif
 
+/* Widget callback hash table callback slot. */
+#define WIDGET_GLYPH_SLOT 0
+
 DECLARE_IMAGE_INSTANTIATOR_FORMAT (nothing);
 DECLARE_IMAGE_INSTANTIATOR_FORMAT (string);
 DECLARE_IMAGE_INSTANTIATOR_FORMAT (formatted_string);
@@ -1951,6 +1956,7 @@
       struct frame *f = XFRAME (IMAGE_INSTANCE_FRAME (p));
       GtkWidget *wid = IMAGE_INSTANCE_GTK_CLIPWIDGET (p);
       GtkAllocation a;
+      int moving;
 
       if (!wid) return;
 
@@ -1959,36 +1965,41 @@
       a.width = dga->width;
       a.height = dga->height;
 
+      /* Is the widget cganging position? */
+      moving = (a.x != wid->allocation.x) ||
+	(a.y != wid->allocation.y);
+
       if ((a.width  != wid->allocation.width)  ||
-	  (a.height != wid->allocation.height))
+	  (a.height != wid->allocation.height) ||
+	  moving)
 	{
 	  gtk_widget_size_allocate (IMAGE_INSTANCE_GTK_CLIPWIDGET (p), &a);
 	}
 
-      /* #### FIXME DAMMIT */
-      if ((wid->allocation.x != -dga->xoffset) ||
-	  (wid->allocation.y != -dga->yoffset))
+      if (moving)
 	{
 	  guint32 old_flags = GTK_WIDGET_FLAGS (FRAME_GTK_TEXT_WIDGET (f));
 
-	  /* Fucking GtkFixed widget queues a resize when you add a widget.
+	  /* GtkFixed widget queues a resize when you add a widget.
 	  ** But only if it is visible.
 	  ** losers.
 	  */
 	  GTK_WIDGET_FLAGS(FRAME_GTK_TEXT_WIDGET (f)) &= ~GTK_VISIBLE;
+
 	  if (IMAGE_INSTANCE_GTK_ALREADY_PUT(p))
 	    {
 	      gtk_fixed_move (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
 			      wid,
-			      -dga->xoffset, -dga->yoffset);
+			      a.x, a.y);
 	    }
 	  else
 	    {
 	      IMAGE_INSTANCE_GTK_ALREADY_PUT(p) = TRUE;
 	      gtk_fixed_put (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
 			     wid,
-			     -dga->xoffset, -dga->yoffset);
+			     a.x, a.y);
 	    }
+
 	  GTK_WIDGET_FLAGS(FRAME_GTK_TEXT_WIDGET (f)) = old_flags;
 	}
       else
@@ -2003,7 +2014,7 @@
 	      IMAGE_INSTANCE_GTK_ALREADY_PUT(p) = TRUE;
 	      gtk_fixed_put (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
 			     wid,
-			     -dga->xoffset, -dga->yoffset);
+			     a.x, a.y);
 	    }
 	}
 
@@ -2093,11 +2104,19 @@
       ||
       IMAGE_INSTANCE_TEXT_CHANGED (p))
     {
+      GtkRequisition r;
+      GtkAllocation a = IMAGE_INSTANCE_GTK_CLIPWIDGET (p)->allocation;
+
       assert (IMAGE_INSTANCE_GTK_WIDGET_ID (p) &&
 	      IMAGE_INSTANCE_GTK_CLIPWIDGET (p)) ;
 
-      /* #### Resize the widget! */
-      /* gtk_widget_size_allocate () */
+      a.width = r.width = IMAGE_INSTANCE_WIDTH (p);
+      a.height = r.height = IMAGE_INSTANCE_HEIGHT (p);
+
+      /* Force the widget's preferred and actual size to what we say it shall
+	 be. */
+      gtk_widget_size_request (IMAGE_INSTANCE_GTK_CLIPWIDGET (p), &r);
+      gtk_widget_size_allocate (IMAGE_INSTANCE_GTK_CLIPWIDGET (p), &a);
     }
 
   /* Adjust offsets within the frame. */
@@ -2255,6 +2274,10 @@
   */
   IMAGE_INSTANCE_GTK_CLIPWIDGET (ii) = w;
 
+  /* The current theme may produce a widget of a different size that what we
+     expect so force reconsideration of the widget's size. */
+  IMAGE_INSTANCE_LAYOUT_CHANGED (ii) = 1;
+
   return (Qt);
 }
 
@@ -2303,8 +2326,44 @@
 FAKE_GTK_WIDGET_INSTANTIATOR(progress_gauge);
 FAKE_GTK_WIDGET_INSTANTIATOR(edit_field);
 FAKE_GTK_WIDGET_INSTANTIATOR(combo_box);
-FAKE_GTK_WIDGET_INSTANTIATOR(tab_control);
 FAKE_GTK_WIDGET_INSTANTIATOR(label);
+/* Note: tab_control has a custom instantiator (see below) */
+
+/*
+  Ask the widget to return it's preferred size.  This device method must
+  defined for all widgets that also have format specific version of
+  query_geometry defined in glyphs-widget.c.  This is because those format
+  specific versions return sizes that are appropriate for the X widgets.  For
+  GTK, the size of a widget can change at runtime due to the user changing
+  their theme.
+
+  This method can be called before the widget is instantiated.  This is
+  because instantiate_image_instantiator() is tying to be helpful to other
+  toolkits and supply sane geometry values to them.  This is not appropriate
+  for GTK and can be ignored.
+
+  This method can be used by all widgets.
+*/
+static void
+gtk_widget_query_geometry (Lisp_Object image_instance,
+			   int* width, int* height,
+			   enum image_instance_geometry disp, Lisp_Object domain)
+{
+  Lisp_Image_Instance *p = XIMAGE_INSTANCE (image_instance);
+
+  if (p->data != NULL)
+    {
+      GtkWidget *w = IMAGE_INSTANCE_GTK_CLIPWIDGET (p);
+      GtkRequisition r;
+
+      gtk_widget_size_request(w, &r);
+      *height= r.height;
+      *width = r.width;
+    }
+}
+
+
+/* Button functions. */
 
 /* Update a button's clicked state. */
 static void
@@ -2347,6 +2406,9 @@
   return Qunbound;
 }
 
+
+/* Progress gauge functions. */
+
 /* set the properties of a progress gauge */
 static void
 gtk_progress_gauge_redisplay (Lisp_Object image_instance)
@@ -2366,6 +2428,202 @@
     }
 }
 
+
+/* Tab Control functions. */
+
+/*
+  Register a widget's callbacks with the frame's hashtable.  The hashtable is
+  weak so deregistration is handled automatically.  Tab controls have per-tab
+  callback list functions and the GTK callback architecture is not
+  sufficiently flexible to deal with this.  Instead, the functions are
+  registered here and the id is passed through the callback loop.
+ */
+static int
+gtk_register_gui_item (Lisp_Object image_instance, Lisp_Object gui,
+		       Lisp_Object domain)
+{
+  struct frame *f = XFRAME(DOMAIN_FRAME(domain));
+  int id = gui_item_id_hash(FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE(f),
+			    gui, WIDGET_GLYPH_SLOT);
+
+  Fputhash(make_int(id), image_instance,
+	   FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE (f));
+  Fputhash(make_int(id), XGUI_ITEM (gui)->callback,
+	   FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE (f));
+  Fputhash(make_int(id), XGUI_ITEM (gui)->callback_ex,
+	   FRAME_GTK_WIDGET_CALLBACK_EX_HASH_TABLE (f));
+  return id;
+}
+
+/*
+  Append the given item as a tab to the notebook. Callbacks, etc are all
+  setup.
+ */
+static void
+gtk_add_tab_item(Lisp_Object image_instance,
+		 GtkNotebook* nb, Lisp_Object item,
+		 Lisp_Object domain, int i)
+{
+  Lisp_Object name;
+  int hash_id = 0;
+  char *c_name = NULL;
+  GtkWidget* box;
+
+  if (GUI_ITEMP (item))
+    {
+      Lisp_Gui_Item *pgui = XGUI_ITEM (item);
+
+      if (!STRINGP (pgui->name))
+	pgui->name = eval_within_redisplay (pgui->name);
+
+      if (!STRINGP (pgui->name)) {
+	warn_when_safe (Qredisplay, Qwarning,
+			"Name does not evaluate to string");
+
+	return;
+      }
+
+      hash_id = gtk_register_gui_item (image_instance, item, domain);
+      name = pgui->name;
+    }
+  else
+    {
+      CHECK_STRING (item);
+      name = item;
+    }
+
+  TO_EXTERNAL_FORMAT (LISP_STRING, name,
+		      C_STRING_ALLOCA, c_name,
+		      Qctext);
+
+  /* Dummy widget that the notbook wants to display when a tab is selected. */
+  box = gtk_vbox_new (FALSE, 3);
+
+  /*
+    Store the per-tab callback data id in the tab.  The callback functions
+    themselves could have been stored in the widget but this avoids having to
+    worry about the garbage collector running between here and the callback
+    function.
+  */
+  gtk_object_set_data(GTK_OBJECT(box), GTK_DATA_TAB_HASHCODE_IDENTIFIER,
+		      (gpointer) hash_id);
+
+  gtk_notebook_append_page (nb, box, gtk_label_new (c_name));
+}
+
+/* Signal handler for the switch-page signal. */
+static void gtk_tab_control_callback(GtkNotebook *notebook,
+				     GtkNotebookPage *page,
+				     gint page_num,
+				     gpointer user_data)
+{
+  /*
+    This callback is called for every selection, not just user selection.
+    We're only interested in user selection, which occurs outside of
+    redisplay.
+  */
+
+  if (!in_display)
+    {
+      Lisp_Object image_instance, callback, callback_ex;
+      Lisp_Object frame, event;
+      int update_subwindows_p = 0;
+      struct frame *f = gtk_widget_to_frame(GTK_WIDGET(notebook));
+      int id;
+
+      if (!f)
+	return;
+      frame = wrap_frame (f);
+
+      id             = (int) gtk_object_get_data(GTK_OBJECT(page->child),
+						 GTK_DATA_TAB_HASHCODE_IDENTIFIER);
+      image_instance = Fgethash(make_int_verify(id),
+				FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE(f), Qnil);
+      callback       = Fgethash(make_int(id),
+				FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE(f), Qnil);
+      callback_ex    = Fgethash(make_int(id),
+				FRAME_GTK_WIDGET_CALLBACK_EX_HASH_TABLE(f), Qnil);
+      update_subwindows_p = 1;
+
+      /* It is possible for a widget action to cause it to get out of
+	 sync with its instantiator. Thus it is necessary to signal
+	 this possibility. */
+      if (IMAGE_INSTANCEP (image_instance))
+	XIMAGE_INSTANCE_WIDGET_ACTION_OCCURRED (image_instance) = 1;
+      
+      if (!NILP (callback_ex) && !UNBOUNDP (callback_ex))
+	{
+	  event = Fmake_event (Qnil, Qnil);
+
+	  XSET_EVENT_TYPE (event, misc_user_event);
+	  XSET_EVENT_CHANNEL (event, frame);
+	  XSET_EVENT_MISC_USER_FUNCTION (event, Qeval);
+	  XSET_EVENT_MISC_USER_OBJECT (event, list4 (Qfuncall, callback_ex, image_instance, event));
+	}
+      else if (NILP (callback) || UNBOUNDP (callback))
+	event = Qnil;
+      else
+	{
+	  Lisp_Object fn, arg;
+
+	  event = Fmake_event (Qnil, Qnil);
+
+	  get_gui_callback (callback, &fn, &arg);
+	  XSET_EVENT_TYPE (event, misc_user_event);
+	  XSET_EVENT_CHANNEL (event, frame);
+	  XSET_EVENT_MISC_USER_FUNCTION (event, fn);
+	  XSET_EVENT_MISC_USER_OBJECT (event, arg);
+	}
+
+      if (!NILP (event))
+	enqueue_dispatch_event (event);
+
+      /* The result of this evaluation could cause other instances to change so
+	 enqueue an update callback to check this. */
+      if (update_subwindows_p && !NILP (event))
+	enqueue_magic_eval_event (update_widget_instances, frame);
+    }
+}
+
+/* Create a tab_control widget.  The special handling of the individual tabs
+   means that the normal instantiation code cannot be used. */
+static void
+gtk_tab_control_instantiate (Lisp_Object image_instance,
+			     Lisp_Object instantiator,
+			     Lisp_Object pointer_fg,
+			     Lisp_Object pointer_bg,
+			     int dest_mask, Lisp_Object domain)
+{
+  Lisp_Object rest;
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  int i = 0;
+  int selected = 0;
+  GtkNotebook *nb;
+
+  /* The normal instantiation is still needed. */
+  gtk_widget_instantiate (image_instance, instantiator, pointer_fg,
+			  pointer_bg, dest_mask, domain);
+
+  nb = GTK_NOTEBOOK (IMAGE_INSTANCE_GTK_CLIPWIDGET (ii));
+
+  /* Add items to the tab, find the current selection */
+  LIST_LOOP (rest, XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii)))
+    {
+      gtk_add_tab_item (image_instance, nb, XCAR (rest), domain, i);
+
+      if (gui_item_selected_p (XCAR (rest)))
+	selected = i;
+
+      i++;
+    }
+
+  gtk_notebook_set_page(nb, selected);
+
+  /* Call per-tab lisp callback when a tab is pressed. */
+  gtk_signal_connect (GTK_OBJECT (nb), "switch-page",
+		      GTK_SIGNAL_FUNC (gtk_tab_control_callback), NULL);
+}
+
 /* Set the properties of a tab control */
 static void
 gtk_tab_control_redisplay (Lisp_Object image_instance)
@@ -2382,6 +2640,7 @@
 	 one. */
       if (tab_control_order_only_changed (image_instance))
 	{
+	  int i = 0;
 	  Lisp_Object rest, selected =
 	    gui_item_list_find_selected
 	    (NILP (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)) ?
@@ -2395,9 +2654,6 @@
 		  Lisp_Object old_selected =gui_item_list_find_selected
 		    (XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii)));
 
-		  /* Need to focus on the widget... */
-		  stderr_out ("Hey, change the tab-focus you boob...\n");
-
 		  /* Pick up the new selected item. */
 		  XGUI_ITEM (old_selected)->selected =
 		    XGUI_ITEM (XCAR (rest))->selected;
@@ -2406,8 +2662,13 @@
 		  /* We're not actually changing the items anymore. */
 		  IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii) = 0;
 		  IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) = Qnil;
+
+		  gtk_notebook_set_page(GTK_NOTEBOOK (IMAGE_INSTANCE_GTK_CLIPWIDGET (ii)),
+					i);
 		  break;
 		}
+
+	      i++;
 	    }
 	}
       else
@@ -2416,37 +2677,23 @@
 	  GtkNotebook *nb = GTK_NOTEBOOK (IMAGE_INSTANCE_GTK_CLIPWIDGET (ii));
 	  guint num_pages = g_list_length (nb->children);
 	  Lisp_Object rest;
-
+	  int i;
+
+	  /* Why is there no API to remove everything from a notebook? */
 	  if (num_pages >= 0)
 	    {
-	      int i;
 	      for (i = num_pages; i >= 0; --i)
 		{
 		  gtk_notebook_remove_page (nb, i);
 		}
 	    }
 
-	  LIST_LOOP (rest, XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii)))
+	  i = 0;
+
+	  LIST_LOOP (rest, XCDR (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
 	    {
-	      Lisp_Gui_Item *pgui = XGUI_ITEM (XCAR (rest));
-	      char *c_name = NULL;
-
-	      if (!STRINGP (pgui->name))
-		pgui->name = eval_within_redisplay (pgui->name);
-
-	      if (!STRINGP (pgui->name))
-		warn_when_safe (Qredisplay, Qwarning,
-				"Name does not evaluate to string");
-	      else
-		{
-		  TO_EXTERNAL_FORMAT (LISP_STRING, pgui->name,
-				      C_STRING_ALLOCA, c_name,
-				      Qctext);
-
-		  gtk_notebook_append_page (nb,
-					    gtk_vbox_new (FALSE, 3),
-					    gtk_label_new (c_name));
-		}
+	      gtk_add_tab_item(image_instance, nb, XCAR(rest),
+			       IMAGE_INSTANCE_FRAME(ii), i);
 	    }
 
 	  /* Show all the new widgets we just added... */
@@ -2541,14 +2788,17 @@
   IIFORMAT_HAS_DEVMETHOD (gtk, button, property);
   IIFORMAT_HAS_DEVMETHOD (gtk, button, instantiate);
   IIFORMAT_HAS_DEVMETHOD (gtk, button, redisplay);
+  IIFORMAT_HAS_SHARED_DEVMETHOD (gtk, button, query_geometry, widget);
   /* general widget methods. */
   INITIALIZE_DEVICE_IIFORMAT (gtk, widget);
   IIFORMAT_HAS_DEVMETHOD (gtk, widget, property);
+  IIFORMAT_HAS_DEVMETHOD (gtk, widget, query_geometry);
 
   /* progress gauge */
   INITIALIZE_DEVICE_IIFORMAT (gtk, progress_gauge);
   IIFORMAT_HAS_DEVMETHOD (gtk, progress_gauge, redisplay);
   IIFORMAT_HAS_DEVMETHOD (gtk, progress_gauge, instantiate);
+  IIFORMAT_HAS_SHARED_DEVMETHOD (gtk, progress_gauge, query_geometry, widget);
   /* text field */
   INITIALIZE_DEVICE_IIFORMAT (gtk, edit_field);
   IIFORMAT_HAS_DEVMETHOD (gtk, edit_field, instantiate);
@@ -2559,6 +2809,7 @@
   INITIALIZE_DEVICE_IIFORMAT (gtk, tab_control);
   IIFORMAT_HAS_DEVMETHOD (gtk, tab_control, instantiate);
   IIFORMAT_HAS_DEVMETHOD (gtk, tab_control, redisplay);
+  IIFORMAT_HAS_SHARED_DEVMETHOD (gtk, tab_control, query_geometry, widget);
   /* label */
   INITIALIZE_DEVICE_IIFORMAT (gtk, label);
   IIFORMAT_HAS_DEVMETHOD (gtk, label, instantiate);
--- a/src/gtk-xemacs.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/gtk-xemacs.c	Wed Jul 07 12:01:07 2004 +0000
@@ -271,14 +271,58 @@
       }
 }
 
+/* Assign a size and position to the child widgets.  This differs from the
+   super class method in that for all widgets except the scrollbars the size
+   and position are not caclulated here.  This is because these widgets have
+   this function performed for them by the redisplay code (see
+   gtk_map_subwindow()). If the superclass method is called then the widgets
+   can change size and position as the two pieces of code move the widgets at
+   random.
+*/
 static void
 gtk_xemacs_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
 {
     GtkXEmacs *x = GTK_XEMACS (widget);
+    GtkFixed *fixed = GTK_FIXED (widget);
     struct frame *f = GTK_XEMACS_FRAME (x);
     int columns, rows;
+    GList *children;
+    guint16 border_width;
 
-    parent_class->size_allocate(widget, allocation);
+    widget->allocation = *allocation;
+    if (GTK_WIDGET_REALIZED (widget))
+      gdk_window_move_resize (widget->window,
+			      allocation->x, 
+			      allocation->y,
+			      allocation->width, 
+			      allocation->height);
+
+    border_width = GTK_CONTAINER (fixed)->border_width;
+  
+    children = fixed->children;
+    while (children)
+      {
+	GtkFixedChild* child = children->data;
+	children = children->next;
+      
+	/*
+	  Scrollbars are the only widget that is managed by GTK.  See
+	  comments in gtk_create_scrollbar_instance().
+	*/
+	if (GTK_WIDGET_VISIBLE (child->widget) &&
+	    gtk_type_is_a(GTK_OBJECT_TYPE(child->widget), GTK_TYPE_SCROLLBAR))
+	  {
+	    GtkAllocation child_allocation;
+	    GtkRequisition child_requisition;
+
+	    gtk_widget_get_child_requisition (child->widget, &child_requisition);
+	    child_allocation.x = child->x + border_width;
+	    child_allocation.y = child->y + border_width;
+	    child_allocation.width = child_requisition.width;
+	    child_allocation.height = child_requisition.height;
+	    gtk_widget_size_allocate (child->widget, &child_allocation);
+	  }
+      }
 
     if (f)
       {
--- a/src/gui-gtk.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/gui-gtk.c	Wed Jul 07 12:01:07 2004 +0000
@@ -1,4 +1,4 @@
-/* General GUI code -- X-specific. (menubars, scrollbars, toolbars, dialogs)
+/* General GUI code -- GTK-specific. (menubars, scrollbars, toolbars, dialogs)
    Copyright (C) 1995 Board of Trustees, University of Illinois.
    Copyright (C) 1995, 1996, 2002 Ben Wing.
    Copyright (C) 1995 Sun Microsystems, Inc.
--- a/src/gui-x.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/gui-x.c	Wed Jul 07 12:01:07 2004 +0000
@@ -218,7 +218,10 @@
 }
 
 /* The following is actually called from somewhere within XtDispatchEvent(),
-   called from XtAppProcessEvent() in event-Xt.c */
+   called from XtAppProcessEvent() in event-Xt.c.
+
+   Callback function for widgets and menus.
+*/
 
 void
 popup_selection_callback (Widget widget, LWLIB_ID ignored_id,
--- a/src/menubar-gtk.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/menubar-gtk.c	Wed Jul 07 12:01:07 2004 +0000
@@ -734,28 +734,13 @@
   return (menu_item);
 }
 
-static struct frame *
-__get_channel (GtkWidget *w)
-{
-  struct frame *f = NULL;
-
-  for (; w; w = w->parent)
-    {
-      if ((f = (struct frame *) gtk_object_get_data (GTK_OBJECT (w), "xemacs::frame")))
-	return (f);
-    }
-
-  return (selected_frame());
-}
-
-
 /* Called whenever a button, radio, or toggle is selected in the menu */
 static void
 __generic_button_callback (GtkMenuItem *item, gpointer user_data)
 {
   Lisp_Object callback, function, data, channel;
 
-  channel = wrap_frame (__get_channel (GTK_WIDGET (item)));
+  channel = wrap_frame (gtk_widget_to_frame (GTK_WIDGET (item)));
 
   callback = VOID_TO_LISP (user_data);
 
--- a/src/scrollbar-gtk.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/scrollbar-gtk.c	Wed Jul 07 12:01:07 2004 +0000
@@ -1,4 +1,4 @@
-/* scrollbar implementation -- X interface.
+/* scrollbar implementation -- GTK interface.
    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
    Copyright (C) 1994 Amdhal Corporation.
    Copyright (C) 1995 Sun Microsystems, Inc.
@@ -97,8 +97,8 @@
   SCROLLBAR_GTK_VDRAG_ORIG_VALUE (instance) = -1;
   SCROLLBAR_GTK_LAST_VALUE (instance) = adj->value;
 
-  gtk_object_set_data (GTK_OBJECT (adj), "xemacs::gui_id", (void *) SCROLLBAR_GTK_ID (instance));
-  gtk_object_set_data (GTK_OBJECT (adj), "xemacs::frame", f);
+  gtk_object_set_data (GTK_OBJECT (adj), GTK_DATA_GUI_IDENTIFIER, (void *) SCROLLBAR_GTK_ID (instance));
+  gtk_object_set_data (GTK_OBJECT (adj), GTK_DATA_FRAME_IDENTIFIER, f);
 
   sb = GTK_SCROLLBAR (vertical ? gtk_vscrollbar_new (adj) : gtk_hscrollbar_new (adj));
   SCROLLBAR_GTK_WIDGET (instance) = GTK_WIDGET (sb);
@@ -364,9 +364,9 @@
 {
   /* This function can GC */
   int vertical = (int) user_data;
-  struct frame *f = (struct frame*) gtk_object_get_data (GTK_OBJECT (adj), "xemacs::frame");
+  struct frame *f = (struct frame*) gtk_object_get_data (GTK_OBJECT (adj), GTK_DATA_FRAME_IDENTIFIER);
   struct scrollbar_instance *instance;
-  GUI_ID id = (GUI_ID) gtk_object_get_data (GTK_OBJECT (adj), "xemacs::gui_id");
+  GUI_ID id = (GUI_ID) gtk_object_get_data (GTK_OBJECT (adj), GTK_DATA_GUI_IDENTIFIER);
   Lisp_Object win, frame;
   struct window_mirror *mirror;
   Lisp_Object event_type = Qnil;
--- a/src/ui-gtk.c	Wed Jul 07 01:52:17 2004 +0000
+++ b/src/ui-gtk.c	Wed Jul 07 12:01:07 2004 +0000
@@ -11,7 +11,7 @@
 #include "lisp.h"
 
 #include "buffer.h"
-#include "console-gtk.h"
+#include "console-gtk-impl.h"
 #include "device.h"
 #include "elhash.h"
 #include "event-gtk.h"
@@ -980,7 +980,7 @@
   emacs_gtk_object_data *data = NULL;
   GUI_ID id = 0;
 
-  id = (GUI_ID) gtk_object_get_data (obj, "xemacs::gui_id");
+  id = (GUI_ID) gtk_object_get_data (obj, GTK_DATA_GUI_IDENTIFIER);
 
   if (id)
     {
@@ -996,7 +996,7 @@
       retval = wrap_emacs_gtk_object (data);
 
       id = new_gui_id ();
-      gtk_object_set_data (obj, "xemacs::gui_id", (gpointer) id);
+      gtk_object_set_data (obj, GTK_DATA_GUI_IDENTIFIER, (gpointer) id);
       gcpro_popup_callbacks (id, retval);
       gtk_object_ref (obj);
       gtk_signal_connect (obj, "destroy", GTK_SIGNAL_FUNC (__notice_object_destruction), (gpointer)id);