+\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);
+}
+