XEmacs 21.4.17 "Jumbo Shrimp".
[chise/xemacs-chise.git.1] / src / glyphs-gtk.c
index 69e59e1..d33e8d0 100644 (file)
@@ -63,6 +63,8 @@ Boston, MA 02111-1307, USA.  */
 #include "insdel.h"
 #include "opaque.h"
 #include "faces.h"
+#include "elhash.h"
+#include "events.h"
 
 #include "imgproc.h"
 
@@ -70,10 +72,19 @@ Boston, MA 02111-1307, USA.  */
 
 #include <setjmp.h>
 
+#if defined (HAVE_XPM)
+#include <X11/xpm.h>
+#endif
+
 #ifdef FILE_CODING
 #include "file-coding.h"
 #endif
 
+extern void enqueue_gtk_dispatch_event (Lisp_Object event);
+
+/* Widget callback hash table callback slot. */
+#define WIDGET_GLYPH_SLOT 0
+
 #if INTBITS == 32
 # define FOUR_BYTE_TYPE unsigned int
 #elif LONGBITS == 32
@@ -412,7 +423,7 @@ gtk_finalize_image_instance (struct Lisp_Image_Instance *p)
 #endif
       else if (IMAGE_INSTANCE_TYPE (p) == IMAGE_SUBWINDOW)
        {
-         abort();
+         ABORT();
        }
       else
        {
@@ -777,7 +788,7 @@ void init_image_instance_from_gdk_pixmap (struct Lisp_Image_Instance *ii,
   gint width, height, depth;
 
   if (!DEVICE_GTK_P (device))
-    abort ();
+    ABORT ();
 
   IMAGE_INSTANCE_DEVICE (ii) = device;
   IMAGE_INSTANCE_TYPE (ii) = IMAGE_COLOR_PIXMAP;
@@ -1023,7 +1034,7 @@ init_image_instance_from_xbm_inline (struct Lisp_Image_Instance *ii,
       break;
 
     default:
-      abort ();
+      ABORT ();
     }
 }
 
@@ -1084,105 +1095,21 @@ gtk_xbm_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
 /**********************************************************************
  *                             XPM                                    *
  **********************************************************************/
-static void
-write_lisp_string_to_temp_file (Lisp_Object string, char *filename_out)
-{
-  Lisp_Object instream, outstream;
-  Lstream *istr, *ostr;
-  char tempbuf[1024]; /* some random amount */
-  int fubar = 0;
-  FILE *tmpfil;
-  static Extbyte_dynarr *conversion_out_dynarr;
-  Bytecount bstart, bend;
-  struct gcpro gcpro1, gcpro2;
-#ifdef FILE_CODING
-  Lisp_Object conv_out_stream;
-  Lstream *costr;
-  struct gcpro gcpro3;
-#endif
-
-  /* This function can GC */
-  if (!conversion_out_dynarr)
-    conversion_out_dynarr = Dynarr_new (Extbyte);
-  else
-    Dynarr_reset (conversion_out_dynarr);
-
-  /* Create the temporary file ... */
-  sprintf (filename_out, "/tmp/emacs%d.XXXXXX", (int) getpid ());
-  mktemp (filename_out);
-  tmpfil = fopen (filename_out, "w");
-  if (!tmpfil)
-    {
-      if (tmpfil)
-       {
-         int old_errno = errno;
-         fclose (tmpfil);
-         unlink (filename_out);
-         errno = old_errno;
-       }
-      report_file_error ("Creating temp file",
-                        list1 (build_string (filename_out)));
-    }
-
-  CHECK_STRING (string);
-  get_string_range_byte (string, Qnil, Qnil, &bstart, &bend,
-                        GB_HISTORICAL_STRING_BEHAVIOR);
-  instream = make_lisp_string_input_stream (string, bstart, bend);
-  istr = XLSTREAM (instream);
-  /* setup the out stream */
-  outstream = make_dynarr_output_stream((unsigned_char_dynarr *)conversion_out_dynarr);
-  ostr = XLSTREAM (outstream);
-#ifdef FILE_CODING
-  /* setup the conversion stream */
-  conv_out_stream = make_encoding_output_stream (ostr, Fget_coding_system(Qbinary));
-  costr = XLSTREAM (conv_out_stream);
-  GCPRO3 (instream, outstream, conv_out_stream);
-#else
-  GCPRO2 (instream, outstream);
-#endif
 
-  /* Get the data while doing the conversion */
+/* strcasecmp() is not sufficiently portable or standard,
+   and it's easier just to write our own. */
+static int
+ascii_strcasecmp (const char *s1, const char *s2)
+{
   while (1)
     {
-      int size_in_bytes = Lstream_read (istr, tempbuf, sizeof (tempbuf));
-      if (!size_in_bytes)
-       break;
-      /* It does seem the flushes are necessary... */
-#ifdef FILE_CODING
-      Lstream_write (costr, tempbuf, size_in_bytes);
-      Lstream_flush (costr);
-#else
-      Lstream_write (ostr, tempbuf, size_in_bytes);
-#endif
-      Lstream_flush (ostr);
-      if (fwrite ((unsigned char *)Dynarr_atp(conversion_out_dynarr, 0),
-                 Dynarr_length(conversion_out_dynarr), 1, tmpfil) != 1)
-       {
-         fubar = 1;
-         break;
-       }
-      /* reset the dynarr */
-      Lstream_rewind(ostr);
+      char c1 = *s1++;
+      char c2 = *s2++;
+      if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A';
+      if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A';
+      if (c1 != c2) return c1 - c2;
+      if (c1 == '\0') return 0;
     }
-  
-  if (fclose (tmpfil) != 0)
-    fubar = 1;
-  Lstream_close (istr);
-#ifdef FILE_CODING
-  Lstream_close (costr);
-#endif
-  Lstream_close (ostr);
-
-  UNGCPRO;
-  Lstream_delete (istr);
-  Lstream_delete (ostr);
-#ifdef FILE_CODING
-  Lstream_delete (costr);
-#endif
-
-  if (fubar)
-    report_file_error ("Writing temp file",
-                      list1 (build_string (filename_out)));
 }
 
 struct color_symbol
@@ -1271,12 +1198,12 @@ gtk_xpm_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
   GdkWindow *window = 0;
   int nsymbols = 0, i = 0;
   struct color_symbol *color_symbols = NULL;
-  GdkColor *transparent_color = NULL;
   Lisp_Object color_symbol_alist = find_keyword_in_vector (instantiator,
                                                           Q_color_symbols);
   enum image_instance_type type;
   int force_mono;
   unsigned int w, h;
+  const unsigned char * volatile dstring;
 
   if (!DEVICE_GTK_P (XDEVICE (device)))
     signal_simple_error ("Not a Gtk device", device);
@@ -1302,20 +1229,58 @@ gtk_xpm_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
 
   assert (!NILP (data));
 
-  /* Need to get the transparent color here */
-  color_symbols = extract_xpm_color_names (device, domain, color_symbol_alist, &nsymbols);
-  for (i = 0; i < nsymbols; i++)
-    {
-      if (!strcasecmp ("BgColor", color_symbols[i].name) ||
-         !strcasecmp ("None", color_symbols[i].name))
-       {
-         transparent_color = &color_symbols[i].color;
-       }
-    }
+  /* Extract all the entries from xpm-color-symbols */
+  color_symbols = extract_xpm_color_names (device, domain, color_symbol_alist,
+                                          &nsymbols);
+
+  assert (!NILP (data));
 
-  write_lisp_string_to_temp_file (data, temp_file_name);
-  pixmap = gdk_pixmap_create_from_xpm (window, &mask, transparent_color, temp_file_name);
-  unlink (temp_file_name);
+
+  LISP_STRING_TO_EXTERNAL(data, dstring, Qbinary);
+
+  /*
+   * GTK only uses the 'c' color entry of an XPM and doesn't use the symbolic
+   * color names at all.  This is unfortunate because the way to change the
+   * colors from lisp is by adding the symbolic names, and the new colors, to
+   * the variable xpm-color-symbols.
+   *
+   * To get around this decode the XPM, add a 'c' entry of the desired color
+   * for each matching symbolic color, recode the XPM and pass it to GTK.  The
+   * decode and recode stages aren't too bad because this also performs the
+   * external to internal format translation, which avoids contortions like
+   * writing the XPM back to disk in order to get it processed.
+   */
+
+  {
+    XpmImage image;
+    XpmInfo info;
+    char** data;
+
+    XpmCreateXpmImageFromBuffer ((char*) dstring, &image, &info);
+
+    for (i = 0; i < nsymbols; i++)
+      {
+       unsigned j;
+
+       for (j = 0; j < image.ncolors; j++)
+         {
+           if (image.colorTable[j].symbolic != NULL &&
+               !ascii_strcasecmp(color_symbols[i].name, image.colorTable[j].symbolic))
+             {
+               image.colorTable[j].c_color = xmalloc(16);
+
+               sprintf(image.colorTable[j].c_color, "#%.4x%.4x%.4x",
+                       color_symbols[i].color.red, color_symbols[i].color.green,
+                       color_symbols[i].color.blue);
+             }
+         }
+      }
+
+    XpmCreateDataFromXpmImage (&data, &image, &info);
+
+    pixmap = gdk_pixmap_create_from_xpm_d (window, &mask, NULL,
+                                          data);
+  }
 
   if (color_symbols) xfree (color_symbols);
 
@@ -1327,7 +1292,7 @@ gtk_xpm_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
   gdk_window_get_geometry (pixmap, NULL, NULL, &w, &h, &depth);
 
   IMAGE_INSTANCE_GTK_PIXMAP (ii) = pixmap;
-  IMAGE_INSTANCE_GTK_MASK (ii) = mask;
+  IMAGE_INSTANCE_PIXMAP_MASK (ii) = (void*) mask;
   IMAGE_INSTANCE_GTK_COLORMAP (ii) = cmap;
   IMAGE_INSTANCE_GTK_PIXELS (ii) = 0;
   IMAGE_INSTANCE_GTK_NPIXELS (ii) = 0;
@@ -1382,7 +1347,7 @@ gtk_xpm_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
       break;
 
     default:
-      abort ();
+      ABORT ();
     }
 }
 #endif /* HAVE_XPM */
@@ -2033,7 +1998,7 @@ gtk_unmap_subwindow (Lisp_Image_Instance *p)
   if (IMAGE_INSTANCE_TYPE (p) == IMAGE_SUBWINDOW)
     {
       /* We don't support subwindows, but we do support widgets... */
-      abort ();
+      ABORT ();
     }
   else                         /* must be a widget */
     {
@@ -2056,13 +2021,14 @@ gtk_map_subwindow (Lisp_Image_Instance *p, int x, int y,
   if (IMAGE_INSTANCE_TYPE (p) == IMAGE_SUBWINDOW)
     {
       /* No subwindow support... */
-      abort ();
+      ABORT ();
     }
   else                         /* must be a widget */
     {
       struct frame *f = XFRAME (IMAGE_INSTANCE_FRAME (p));
       GtkWidget *wid = IMAGE_INSTANCE_GTK_CLIPWIDGET (p);
       GtkAllocation a;
+      int moving;
 
       if (!wid) return;
 
@@ -2071,38 +2037,58 @@ gtk_map_subwindow (Lisp_Image_Instance *p, int x, int y,
       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
+       {
+         if (IMAGE_INSTANCE_GTK_ALREADY_PUT(p))
+           {
+             /* Do nothing... */
+           }
+         else
+           {
+             /* Must make sure we have put the image at least once! */
+             IMAGE_INSTANCE_GTK_ALREADY_PUT(p) = TRUE;
+             gtk_fixed_put (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
+                            wid,
+                            a.x, a.y);
+           }
+       }
 
       if (!IMAGE_INSTANCE_SUBWINDOW_DISPLAYEDP (p))
        {
@@ -2193,11 +2179,19 @@ gtk_redisplay_widget (Lisp_Image_Instance *p)
       ||
       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. */
@@ -2356,6 +2350,10 @@ gtk_widget_instantiate_1 (Lisp_Object image_instance, Lisp_Object instantiator,
   */
   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);
 }
 
@@ -2404,8 +2402,44 @@ FAKE_GTK_WIDGET_INSTANTIATOR(button);
 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;
+    }
+}
+
+\f
+/* Button functions. */
 
 /* Update a button's clicked state. */
 static void
@@ -2427,7 +2461,7 @@ gtk_button_redisplay (Lisp_Object image_instance)
   else
     {
       /* Unknown button type... */
-      abort();
+      ABORT();
     }
 }
 
@@ -2448,6 +2482,9 @@ gtk_button_property (Lisp_Object image_instance, Lisp_Object prop)
   return Qunbound;
 }
 
+\f
+/* Progress gauge functions. */
+
 /* set the properties of a progress gauge */
 static void
 gtk_progress_gauge_redisplay (Lisp_Object image_instance)
@@ -2467,6 +2504,198 @@ gtk_progress_gauge_redisplay (Lisp_Object image_instance)
     }
 }
 
+\f
+/* 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 = Feval (pgui->name);
+
+      CHECK_STRING (pgui->name);
+
+      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(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);
+
+         XEVENT (event)->event_type = misc_user_event;
+         XEVENT (event)->channel = frame;
+         XEVENT (event)->event.eval.function = Qeval;
+         XEVENT (event)->event.eval.object =
+           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);
+         XEVENT (event)->event_type = misc_user_event;
+         XEVENT (event)->channel = frame;
+         XEVENT (event)->event.eval.function = fn;
+         XEVENT (event)->event.eval.object = arg;
+       }
+
+      if (!NILP (event))
+       enqueue_gtk_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)
@@ -2483,6 +2712,7 @@ gtk_tab_control_redisplay (Lisp_Object image_instance)
         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)) ?
@@ -2496,9 +2726,6 @@ gtk_tab_control_redisplay (Lisp_Object image_instance)
                  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;
@@ -2507,8 +2734,14 @@ gtk_tab_control_redisplay (Lisp_Object image_instance)
                  /* 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
@@ -2517,33 +2750,23 @@ gtk_tab_control_redisplay (Lisp_Object image_instance)
          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)))
-           {
-             Lisp_Gui_Item *pgui = XGUI_ITEM (XCAR (rest));
-             char *c_name = NULL;
+         i = 0;
 
-             if (!STRINGP (pgui->name))
-               pgui->name = Feval (pgui->name);
-
-             CHECK_STRING (pgui->name);
-
-             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));
+         LIST_LOOP (rest, XCDR (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
+           {
+             gtk_add_tab_item(image_instance, nb, XCAR(rest),
+                              IMAGE_INSTANCE_FRAME(ii), i);
            }
 
          /* Show all the new widgets we just added... */
@@ -2640,14 +2863,17 @@ image_instantiator_format_create_glyphs_gtk (void)
   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);
@@ -2658,6 +2884,7 @@ image_instantiator_format_create_glyphs_gtk (void)
   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);