]> git.eshelyaron.com Git - emacs.git/commitdiff
Native Gtk+ tabs, first try.
authorJan Djärv <jan.h.d@swipnet.se>
Mon, 22 Mar 2010 12:22:43 +0000 (13:22 +0100)
committerJan Djärv <jan.h.d@swipnet.se>
Mon, 22 Mar 2010 12:22:43 +0000 (13:22 +0100)
lisp/loadup.el
src/gtkutil.c
src/gtkutil.h
src/keyboard.c
src/termhooks.h
src/xdisp.c
src/xfns.c
src/xftfont.c
src/xterm.c
src/xterm.h

index 85222ce7d9e98daedbda7b79dd3f87029fd2bf92..0cf3a4c2088f456e7c1121eb78411d90e731745b 100644 (file)
 (if (or (featurep 'system-font-setting) (featurep 'font-render-setting))
     (load "font-setting"))
 
+(if (featurep 'tabs)
+    (load "native-tabs"))
+
 (if (featurep 'x)
     (progn
       (load "x-dnd")
index 2764382e8a13456cfeabd63bced4a3001985e91a..2254978a79e932866ff321a2e34a89fd8c7d5dd9 100644 (file)
@@ -753,6 +753,274 @@ xg_pix_to_gcolor (w, pixel, c)
   gdk_colormap_query_color (map, pixel, c);
 }
 
+/***********************************************************************
+                              Tab functions
+ ***********************************************************************/
+#define XG_TAB_KEY "emacs-tab-key"
+static int xg_tab_nr;
+
+/* Callback called when the current tab changes.  */
+
+static void
+xg_switch_page_cb (GtkNotebook     *notebook,
+                   GtkNotebookPage *page,
+                   guint            page_num,
+                   gpointer         user_data)
+{
+  BLOCK_INPUT;
+  GtkWidget *w = gtk_notebook_get_nth_page (notebook, page_num);
+  FRAME_PTR f = (FRAME_PTR) user_data;
+  if (w != FRAME_GTK_WIDGET (f)) 
+    {
+      GtkWidget *old = FRAME_GTK_WIDGET (f);
+      GList *children = old ? GTK_FIXED (old)->children : NULL;
+      GSList *todo = NULL, *iter;
+      struct input_event event;
+      Lisp_Object frame;
+      char *key = g_object_get_data (G_OBJECT (w), XG_TAB_KEY);
+
+      if (!w->window) gtk_widget_realize (w);
+      FRAME_GTK_WIDGET (f) = w;
+      FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (w);
+      for ( ; children; children = g_list_next (children)) 
+        {
+          GtkFixedChild *child = (GtkFixedChild*)children->data;
+          if (GTK_IS_EVENT_BOX (child->widget)) 
+            {
+              GtkFixedChild *node = xmalloc (sizeof(*node));
+              *node = *child;
+              todo = g_slist_prepend (todo, node);
+            }
+        }
+
+      for (iter = todo; iter; iter = iter->next) 
+        {
+          GtkFixedChild *child = (GtkFixedChild*)iter->data;
+          GtkWidget *wevbox = child->widget;
+          g_object_ref (G_OBJECT (wevbox));
+          gtk_container_remove (GTK_CONTAINER (old), wevbox);
+          gtk_fixed_put (GTK_FIXED (w), wevbox, child->x, child->y);
+          g_object_unref (G_OBJECT (wevbox));
+          xfree (child);
+          iter->data = NULL;
+        }
+      if (todo) g_slist_free (todo);
+
+      SET_FRAME_GARBAGED (f);
+      cancel_mouse_face (f);
+
+      if (old) 
+        {
+          char *oldkey = g_object_get_data (G_OBJECT (old), XG_TAB_KEY);
+          XSETFRAME (frame, f);
+          EVENT_INIT (event);
+          event.kind = TAB_CHANGED_EVENT;
+          event.frame_or_window = frame;
+          event.arg = Fcons (make_string (key, strlen (key)),
+                             make_string (oldkey, strlen (oldkey)));
+          kbd_buffer_store_event (&event);
+        }
+    }
+
+  UNBLOCK_INPUT;
+}
+
+static void
+xg_fixed_destroy_cb (GtkWidget *widget,
+                     gpointer client_data)
+{
+  char *key = g_object_get_data (G_OBJECT (widget), XG_TAB_KEY);
+  xfree (key);
+}
+
+/* Add a new tab and make it current.
+   Returns a unique identifier for this tab.  */
+
+const char *
+xg_add_tab (FRAME_PTR f,
+            const char *name)
+{
+  GtkNotebook *wnote = GTK_NOTEBOOK (f->output_data.x->notebook_widget);
+  GtkWidget *wfixed = gtk_fixed_new ();
+  GdkColor bg;
+  GtkRcStyle *style;
+  char buf[64];
+  char *key;
+  int n;
+
+  gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
+  sprintf (buf, "Page %d", xg_tab_nr++);
+  key = xstrdup (buf);
+  g_object_set_data (G_OBJECT (wfixed), XG_TAB_KEY, key);
+
+  gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
+  /* We don't want this widget double buffered, because we draw on it
+     with regular X drawing primitives, so from a GTK/GDK point of
+     view, the widget is totally blank.  When an expose comes, this
+     will make the widget blank, and then Emacs redraws it.  This flickers
+     a lot, so we turn off double buffering.  */
+  gtk_widget_set_double_buffered (wfixed, FALSE);
+  gtk_widget_add_events (wfixed,
+                         GDK_POINTER_MOTION_MASK
+                         | GDK_EXPOSURE_MASK
+                         | GDK_BUTTON_PRESS_MASK
+                         | GDK_BUTTON_RELEASE_MASK
+                         | GDK_KEY_PRESS_MASK
+                         | GDK_ENTER_NOTIFY_MASK
+                         | GDK_LEAVE_NOTIFY_MASK
+                         | GDK_FOCUS_CHANGE_MASK
+                         | GDK_STRUCTURE_MASK
+                         | GDK_VISIBILITY_NOTIFY_MASK);
+
+
+  /* Since GTK clears its window by filling with the background color,
+     we must keep X and GTK background in sync.  */
+  xg_pix_to_gcolor (wfixed, FRAME_BACKGROUND_PIXEL (f), &bg);
+  gtk_widget_modify_bg (wfixed, GTK_STATE_NORMAL, &bg);
+
+  /* Also, do not let any background pixmap to be set, this looks very
+     bad as Emacs overwrites the background pixmap with its own idea
+     of background color.  */
+  style = gtk_widget_get_modifier_style (wfixed);
+
+  /* Must use g_strdup because gtk_widget_modify_style does g_free.  */
+  style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
+  gtk_widget_modify_style (wfixed, style);
+  gtk_widget_show (wfixed);
+  n = gtk_notebook_append_page_menu (wnote,
+                                     wfixed,
+                                     name ? gtk_label_new (name) : NULL,
+                                     NULL);
+  gtk_notebook_set_tab_reorderable (wnote, wfixed, TRUE);
+
+  if (n > 0) 
+    {
+      gtk_notebook_set_current_page (wnote, n);
+      xg_switch_page_cb (wnote, NULL, n, f);
+    }
+  
+  g_signal_connect (G_OBJECT (wfixed),
+                    "destroy",
+                    G_CALLBACK (xg_fixed_destroy_cb), 0);
+
+  return key;
+}
+
+/* Delete a tab by its unique identifier KEY.
+   If no KEY is given, delete the current tab.  */
+
+void
+xg_delete_tab (FRAME_PTR f,
+               const char *key)
+{
+  GtkNotebook *wnote = GTK_NOTEBOOK (f->output_data.x->notebook_widget);
+  int i, pages = gtk_notebook_get_n_pages (wnote);
+  int page_to_remove = -1;
+  int current_page = gtk_notebook_get_current_page (wnote);
+  if (pages == 1) return;
+
+  if (!key) 
+    {
+      page_to_remove = current_page;
+    }
+  else
+    {
+      for (i = 0; i < pages; ++i) 
+        {
+          GtkWidget *w = gtk_notebook_get_nth_page (wnote, i);
+          char *k;
+          if (!w) continue;
+          k =  g_object_get_data (G_OBJECT (w), XG_TAB_KEY);
+          if (k && strcmp (k, key) == 0)
+            {
+              page_to_remove = i;
+              break;
+            }
+        }
+    }
+  
+  if (page_to_remove >= 0 && page_to_remove < pages)
+    {
+      if (page_to_remove == current_page)
+        {
+          int new_page = page_to_remove + 1;
+          if (new_page == pages) new_page = page_to_remove - 1;
+          gtk_notebook_set_current_page (wnote, new_page);
+          xg_switch_page_cb (wnote, NULL, new_page, f);
+        }
+      gtk_notebook_remove_page (wnote, page_to_remove);
+    }
+}
+
+/* Delete all tabs except the current tab.  */
+
+void
+xg_delete_all_tabs (FRAME_PTR f)
+{
+  GtkNotebook *wnote = GTK_NOTEBOOK (f->output_data.x->notebook_widget);
+  int i, pages = gtk_notebook_get_n_pages (wnote);
+  int current_page = gtk_notebook_get_current_page (wnote);
+  if (pages == 1) return;
+  
+  /* First delete tabs after current_page, so current_page becomes last.  */
+  for (i = current_page+1; i < pages; ++i)
+    gtk_notebook_remove_page (wnote, i);
+  
+  /* Then delete the rest.  */
+  for (i = 0; i < current_page; ++i)
+    gtk_notebook_remove_page (wnote, 0);
+}
+
+/* Make the next tab current.  If there are no next tabs, wrap around to 0.  */
+
+void
+xg_tab_next (FRAME_PTR f)
+{
+  GtkNotebook *wnote = GTK_NOTEBOOK (f->output_data.x->notebook_widget);
+  int current_page = gtk_notebook_get_current_page (wnote);
+  int pages = gtk_notebook_get_n_pages (wnote);
+  int switch_to;
+
+  if (pages == 1) return;
+  if (current_page == pages-1) 
+    switch_to = 0;
+  else
+    switch_to = current_page+1;
+  gtk_notebook_set_current_page (wnote, switch_to);
+
+}
+
+/* Make the previous tab current.  If current is first, wrap around to last.  */
+
+void
+xg_tab_previous (FRAME_PTR f)
+{
+  GtkNotebook *wnote = GTK_NOTEBOOK (f->output_data.x->notebook_widget);
+  int current_page = gtk_notebook_get_current_page (wnote);
+  int pages = gtk_notebook_get_n_pages (wnote);
+  int switch_to;
+
+  if (pages == 1) return;
+  if (current_page == 0) 
+    switch_to = pages-1;
+  else
+    switch_to = current_page-1;
+  gtk_notebook_set_current_page (wnote, switch_to);
+}
+
+
+void
+xg_set_tab_label (FRAME_PTR f,
+                  const char *label)
+{
+  GtkNotebook *wnote = GTK_NOTEBOOK (f->output_data.x->notebook_widget);
+  int current_page = gtk_notebook_get_current_page (wnote);
+  GtkWidget *w = gtk_notebook_get_nth_page (wnote, current_page);
+  const char *txt = gtk_notebook_get_tab_label_text (wnote, w);
+  if (txt == NULL || strcmp (txt, label) != 0)
+    gtk_notebook_set_tab_label_text (wnote, w, label);
+}
+
 /* Create and set up the GTK widgets for frame F.
    Return 0 if creation failed, non-zero otherwise.  */
 
@@ -762,9 +1030,7 @@ xg_create_frame_widgets (f)
 {
   GtkWidget *wtop;
   GtkWidget *wvbox;
-  GtkWidget *wfixed;
-  GdkColor bg;
-  GtkRcStyle *style;
+  GtkWidget *wnote;
   char *title = 0;
 
   BLOCK_INPUT;
@@ -777,13 +1043,13 @@ xg_create_frame_widgets (f)
   xg_set_screen (wtop, f);
 
   wvbox = gtk_vbox_new (FALSE, 0);
-  wfixed = gtk_fixed_new ();  /* Must have this to place scroll bars  */
+  wnote = gtk_notebook_new ();
 
-  if (! wtop || ! wvbox || ! wfixed)
+  if (! wtop || ! wvbox || ! wnote)
     {
       if (wtop) gtk_widget_destroy (wtop);
       if (wvbox) gtk_widget_destroy (wvbox);
-      if (wfixed) gtk_widget_destroy (wfixed);
+      if (wnote) gtk_widget_destroy (wnote);
 
       UNBLOCK_INPUT;
       return 0;
@@ -792,7 +1058,6 @@ xg_create_frame_widgets (f)
   /* Use same names as the Xt port does.  I.e. Emacs.pane.emacs by default */
   gtk_widget_set_name (wtop, EMACS_CLASS);
   gtk_widget_set_name (wvbox, "pane");
-  gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
 
   /* If this frame has a title or name, set it in the title bar.  */
   if (! NILP (f->title)) title = SSDATA (ENCODE_UTF_8 (f->title));
@@ -801,24 +1066,17 @@ xg_create_frame_widgets (f)
   if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
 
   FRAME_GTK_OUTER_WIDGET (f) = wtop;
-  FRAME_GTK_WIDGET (f) = wfixed;
   f->output_data.x->vbox_widget = wvbox;
-
-  gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
+  f->output_data.x->notebook_widget = wnote;
 
   gtk_container_add (GTK_CONTAINER (wtop), wvbox);
-  gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
-
+  gtk_box_pack_end (GTK_BOX (wvbox), wnote, TRUE, TRUE, 0);
+  g_signal_connect (G_OBJECT (wnote), "switch-page",
+                    G_CALLBACK (xg_switch_page_cb), f);
+                              
   if (FRAME_EXTERNAL_TOOL_BAR (f))
     update_frame_tool_bar (f);
 
-  /* We don't want this widget double buffered, because we draw on it
-     with regular X drawing primitives, so from a GTK/GDK point of
-     view, the widget is totally blank.  When an expose comes, this
-     will make the widget blank, and then Emacs redraws it.  This flickers
-     a lot, so we turn off double buffering.  */
-  gtk_widget_set_double_buffered (wfixed, FALSE);
-
   gtk_window_set_wmclass (GTK_WINDOW (wtop),
                           SSDATA (Vx_resource_name),
                           SSDATA (Vx_resource_class));
@@ -834,44 +1092,17 @@ xg_create_frame_widgets (f)
   xg_set_geometry (f);
   f->win_gravity
     = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
-
-  gtk_widget_add_events (wfixed,
-                         GDK_POINTER_MOTION_MASK
-                         | GDK_EXPOSURE_MASK
-                         | GDK_BUTTON_PRESS_MASK
-                         | GDK_BUTTON_RELEASE_MASK
-                         | GDK_KEY_PRESS_MASK
-                         | GDK_ENTER_NOTIFY_MASK
-                         | GDK_LEAVE_NOTIFY_MASK
-                         | GDK_FOCUS_CHANGE_MASK
-                         | GDK_STRUCTURE_MASK
-                         | GDK_VISIBILITY_NOTIFY_MASK);
+  xg_add_tab (f, NULL);
+  gtk_notebook_popup_enable (GTK_NOTEBOOK (wnote));
+  gtk_notebook_set_scrollable (GTK_NOTEBOOK (wnote), TRUE);
+  GtkWidget *wfixed = gtk_notebook_get_nth_page (GTK_NOTEBOOK (wnote), 0);
 
   /* Must realize the windows so the X window gets created.  It is used
      by callers of this function.  */
   gtk_widget_realize (wfixed);
-  FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
-
-  /* Since GTK clears its window by filling with the background color,
-     we must keep X and GTK background in sync.  */
-  xg_pix_to_gcolor (wfixed, FRAME_BACKGROUND_PIXEL (f), &bg);
-  gtk_widget_modify_bg (wfixed, GTK_STATE_NORMAL, &bg);
-
-  /* Also, do not let any background pixmap to be set, this looks very
-     bad as Emacs overwrites the background pixmap with its own idea
-     of background color.  */
-  style = gtk_widget_get_modifier_style (wfixed);
-
-  /* Must use g_strdup because gtk_widget_modify_style does g_free.  */
-  style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
-  gtk_widget_modify_style (wfixed, style);
-
-  /* GTK does not set any border, and they look bad with GTK.  */
-  /* That they look bad is no excuse for imposing this here.  --Stef
-     It should be done by providing the proper default in Fx_create_Frame.
-  f->border_width = 0;
-  f->internal_border_width = 0; */
 
+  FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
+  FRAME_GTK_WIDGET (f) = wfixed;
   UNBLOCK_INPUT;
 
   return 1;
@@ -4230,6 +4461,7 @@ xg_initialize ()
 
   id_to_widget.max_size = id_to_widget.used = 0;
   id_to_widget.widgets = 0;
+  xg_tab_nr = 1;
 
   /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
      bindings.  It doesn't seem to be any way to remove properties,
index 602228f97be2cc99e06288eb89b157ea546ad71e..194f2df9eef0bae190a3c813e53e652916637e9d 100644 (file)
@@ -135,6 +135,12 @@ extern char *xg_get_file_name P_ ((FRAME_PTR f,
                                    int only_dir_p));
 
 extern char *xg_get_font_name P_ ((FRAME_PTR f, char *));
+extern const char *xg_add_tab P_ ((FRAME_PTR f, const char *name));
+extern void xg_delete_tab P_ ((FRAME_PTR f, const char *name));
+extern void xg_delete_all_tabs P_ ((FRAME_PTR f));
+extern void xg_set_tab_label P_ ((FRAME_PTR f, const char *label));
+extern void xg_tab_next P_ ((FRAME_PTR f));
+extern void xg_tab_previous P_ ((FRAME_PTR f));
 
 extern GtkWidget *xg_create_widget P_ ((char *type,
                                         char *name,
index 48d39235e3f5e36ff120c0167030a9471042c897..471ac9a42636e20e16798c1f2f18e7dc48468ce6 100644 (file)
@@ -490,6 +490,7 @@ Lisp_Object Qsave_session;
 Lisp_Object Qdbus_event;
 #endif
 Lisp_Object Qconfig_changed_event;
+Lisp_Object Qtab_changed_event;
 
 /* Lisp_Object Qmouse_movement; - also an event header */
 
@@ -4317,6 +4318,11 @@ kbd_buffer_get_event (kbp, used_mouse_menu, end_time)
          obj = make_lispy_event (event);
          kbd_fetch_ptr = event + 1;
        }
+      else if (event->kind == TAB_CHANGED_EVENT)
+       {
+         obj = make_lispy_event (event);
+         kbd_fetch_ptr = event + 1;
+       }
       else
        {
          /* If this event is on a different frame, return a switch-frame this
@@ -6234,6 +6240,12 @@ make_lispy_event (event)
        return Fcons (Qconfig_changed_event,
                       Fcons (event->arg,
                              Fcons (event->frame_or_window, Qnil)));
+
+    case TAB_CHANGED_EVENT:
+       return Fcons (Qtab_changed_event,
+                      Fcons (event->arg,
+                             Fcons (event->frame_or_window, Qnil)));
+
 #ifdef HAVE_GPM
     case GPM_CLICK_EVENT:
       {
@@ -11852,6 +11864,8 @@ syms_of_keyboard ()
 
   Qconfig_changed_event = intern_c_string ("config-changed-event");
   staticpro (&Qconfig_changed_event);
+  Qtab_changed_event = intern_c_string ("tab-changed-event");
+  staticpro (&Qtab_changed_event);
 
   Qmenu_enable = intern_c_string ("menu-enable");
   staticpro (&Qmenu_enable);
@@ -12598,6 +12612,8 @@ keys_of_keyboard ()
 
   initial_define_lispy_key (Vspecial_event_map, "config-changed-event",
                            "ignore");
+  initial_define_lispy_key (Vspecial_event_map, "tab-changed-event",
+                           "ignore");
 }
 
 /* Mark the pointers in the kboard objects.
index 2b4011627c83fb1f15c5f0863b8d61437a5a6206..546a0d562b2438c5816b955f9f5f79e66047570d 100644 (file)
@@ -185,6 +185,7 @@ enum event_kind
 #endif
 
   , CONFIG_CHANGED_EVENT
+  , TAB_CHANGED_EVENT
 
 #ifdef WINDOWSNT
   /* Generated when an APPCOMMAND event is received, in response to
index 9ece458e77eb3cb1feea31f88016bbaf049693c6..12909627a2c5f16856567c9fdea095aea76e40f9 100644 (file)
@@ -25256,7 +25256,7 @@ init_xdisp ()
   current_header_line_height = current_mode_line_height = -1;
 
   CHARPOS (this_line_start_pos) = 0;
-
+  
   mini_w = XWINDOW (minibuf_window);
   root_window = FRAME_ROOT_WINDOW (XFRAME (WINDOW_FRAME (mini_w)));
 
index 45bea1e4ec241dfaee2b043985c2e8bc2ecb419f..7d3237966b140c98897e0f25e62955ac043c3ec8 100644 (file)
@@ -5849,6 +5849,131 @@ frame_parm_handler x_frame_parm_handlers[] =
   x_set_sticky,
 };
 
+#ifdef USE_GTK
+DEFUN ("tab-new", Ftab_new,
+       Stab_new, 0, 2, "",
+       doc: /* Create a new tab with name NAME in frame FRAME.
+If NAME is nil, use a standard name (Page <n>).
+FRAME nil means use the selected frame.
+
+Returns the key for the tab, which can be passed to `tab-delete'.  */)
+     (name, frame)
+     Lisp_Object name, frame;
+{
+  FRAME_PTR f = check_x_frame (frame);
+  const char *key;
+
+  if (!NILP (name) && !STRINGP (name))
+    error ("Name is not string or nil");
+
+  BLOCK_INPUT;
+  key = xg_add_tab (f, NILP (name) ? NULL : SDATA (name));
+  UNBLOCK_INPUT;
+
+  return make_string (key, strlen (key));
+}
+
+DEFUN ("tab-delete", Ftab_delete,
+       Stab_delete, 0, 2, "",
+       doc: /* Remove tab KEY from frame FRAME.
+KEY is what `tab-new' returned or nil, which means the current tab.
+FRAME nil means use the selected frame.  */)
+     (name, frame)
+     Lisp_Object name, frame;
+{
+  FRAME_PTR f = check_x_frame (frame);
+  if (!NILP (name) && !STRINGP (name))
+    error ("Name is not string or nil");
+
+  BLOCK_INPUT;
+  xg_delete_tab (f, NILP (name) ? NULL : SDATA (name));
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+
+DEFUN ("tab-delete-other", Ftab_delete_other,
+       Stab_delete_other, 0, 1, "",
+       doc: /* Remove all tabs from frame FRAME except the current one.
+FRAME nil means use the selected frame.  */)
+     (frame)
+     Lisp_Object frame;
+{
+  FRAME_PTR f = check_x_frame (frame);
+  BLOCK_INPUT;
+  xg_delete_all_tabs (f);
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+
+DEFUN ("tab-next", Ftab_next,
+       Stab_next, 0, 1, "",
+       doc: /* Go to the next tab on frame FRAME.
+Wrap around to the beginning if current tab is last.
+FRAME nil means use the selected frame.  */)
+     (frame)
+     Lisp_Object frame;
+{
+  FRAME_PTR f = check_x_frame (frame);
+  BLOCK_INPUT;
+  xg_tab_next (f);
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+
+DEFUN ("tab-previous", Ftab_previous,
+       Stab_previous, 0, 1, "",
+       doc: /* Go to the previous tab on frame FRAME.
+Wrap around to the end if current tab is first.
+FRAME nil means use the selected frame.  */)
+     (frame)
+     Lisp_Object frame;
+{
+  FRAME_PTR f = check_x_frame (frame);
+  BLOCK_INPUT;
+  xg_tab_previous (f);
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+
+DEFUN ("tab-set-label", Ftab_set_label,
+       Stab_set_label, 1, 2, 0,
+       doc: /* Set label for the current tab in frame FRAME to LABEL.
+LABEL nil means use current buffer name.
+FRAME nil means use the selected frame.  */)
+     (label, frame)
+     Lisp_Object label, frame;
+{
+  FRAME_PTR f;
+
+  if (NILP (frame))
+    frame = selected_frame;
+  CHECK_LIVE_FRAME (frame);
+  f = XFRAME (frame);
+  if (! FRAME_X_P (f)) return;
+
+
+  if (NILP (label))
+    {
+      if (!NILP (Fminibufferp (Qnil))) return;
+      label = Fbuffer_name (Qnil);
+    }
+
+  if (!STRINGP (label))
+    error ("label is not a string");
+
+  BLOCK_INPUT;
+  xg_set_tab_label (f, SDATA (label));
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+
+#endif
+
 void
 syms_of_xfns ()
 {
@@ -5997,6 +6122,7 @@ the tool bar buttons.  */);
      accepts --with-x-toolkit=gtk.  */
   Fprovide (intern_c_string ("x-toolkit"), Qnil);
   Fprovide (intern_c_string ("gtk"), Qnil);
+  Fprovide (intern_c_string ("tabs"), Qnil);
 
   DEFVAR_LISP ("gtk-version-string", &Vgtk_version_string,
                doc: /* Version info for GTK+.  */);
@@ -6006,6 +6132,14 @@ the tool bar buttons.  */);
                 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
     Vgtk_version_string = make_pure_string (gtk_version, strlen (gtk_version), strlen (gtk_version), 0);
   }
+
+  defsubr (&Stab_new);
+  defsubr (&Stab_delete);
+  defsubr (&Stab_delete_other);
+  defsubr (&Stab_next);
+  defsubr (&Stab_previous);
+  defsubr (&Stab_set_label);
+
 #endif /* USE_GTK */
 
   /* X window properties.  */
index 5d4581be4e94b37ae6f74762186fcc92a724a611..e03b78471f8e1bbb96515539bbc880581e2e8da8 100644 (file)
@@ -672,6 +672,13 @@ xftfont_draw (s, from, to, x, y, with_background)
   int len = to - from;
   int i;
 
+  if (XftDrawDrawable (xft_draw) != s->window)
+    {
+      x_catch_errors (s->display);
+      XftDrawChange (xft_draw, s->window);
+      x_uncatch_errors ();
+    }
+
   if (s->font == face->font)
     xftface_info = (struct xftface_info *) face->extra;
   xftfont_get_colors (f, face, s->gc, xftface_info,
index fd09d6d4815b6585cf3cdbbf4d5801d8dbc7f7f8..b43e6d11d64b1f00131b505060a898fed82ed842 100644 (file)
@@ -6177,9 +6177,9 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit)
               f->output_data.x->has_been_visible = 1;
               SET_FRAME_GARBAGED (f);
             }
-          else
+          else 
             expose_frame (f,
-                         event.xexpose.x, event.xexpose.y,
+                          event.xexpose.x, event.xexpose.y,
                           event.xexpose.width, event.xexpose.height);
         }
       else
@@ -7225,6 +7225,9 @@ XTread_socket (terminal, expected, hold_quit)
       if (current_finish == X_EVENT_GOTO_OUT)
         break;
     }
+
+  Ftab_set_label (Qnil, Qnil);
+
 #endif /* USE_GTK */
 
  out:;
index a766f863c4dd5dde90483d9c8a3dc8d2747b5805..846c6df4a11fb18c9b748c87a434c5653dfa277c 100644 (file)
@@ -474,6 +474,8 @@ struct x_output
   GtkWidget *edit_widget;
   /* The widget used for laying out widgets vertically.  */
   GtkWidget *vbox_widget;
+  /* The notebook (i.e. tab) widget.  */
+  GtkWidget *notebook_widget;
   /* The menubar in this frame.  */
   GtkWidget *menubar_widget;
   /* The tool bar in this frame  */