diff src/glyphs-gtk.c @ 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 16489ca72b3d
children 4ec724310f33
line wrap: on
line diff
--- 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);