/* Functions for creating and updating GTK widgets.
- Copyright (C) 2003
- Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Emacs is distributed in the hope that it will be useful,
You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING. If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include "config.h"
#ifdef USE_GTK
#include <string.h>
+#include <signal.h>
#include <stdio.h>
#include "lisp.h"
#include "xterm.h"
#include "blockinput.h"
+#include "syssignal.h"
#include "window.h"
#include "atimer.h"
#include "gtkutil.h"
#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
(FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
+/* Avoid "differ in sign" warnings */
+#define SSDATA(x) ((char *) SDATA (x))
+
\f
/***********************************************************************
Display handling functions
#ifdef HAVE_GTK_MULTIDISPLAY
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
- /* GTK 2.2 has a bug that makes gdk_display_close crash (bug
- http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way
- we can continue running, but there will be memory leaks. */
-
-#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4
-
/* If this is the default display, we must change it before calling
- dispose, otherwise it will crash. */
+ dispose, otherwise it will crash on some Gtk+ versions. */
if (gdk_display_get_default () == gdpy)
{
struct x_display_info *dpyinfo;
gdpy_new);
}
- g_object_run_dispose (G_OBJECT (gdpy));
+ /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
+ http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way
+ we can continue running, but there will be memory leaks. */
+#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 10
+ g_object_run_dispose (G_OBJECT (gdpy));
#else
- /* I hope this will be fixed in GTK 2.4. It is what bug 85715 says. */
+ /* This seems to be fixed in GTK 2.10. */
gdk_display_close (gdpy);
#endif
#endif /* HAVE_GTK_MULTIDISPLAY */
}
else
{
- wv = (widget_value *) malloc (sizeof (widget_value));
+ wv = (widget_value *) xmalloc (sizeof (widget_value));
malloc_cpt++;
}
memset (wv, 0, sizeof (widget_value));
return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
}
+/* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
+
+static GdkPixbuf *
+xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap)
+ GdkPixmap *gpix;
+ GdkPixmap *gmask;
+ GdkColormap *cmap;
+{
+ int x, y, width, height, rowstride, mask_rowstride;
+ GdkPixbuf *icon_buf, *tmp_buf;
+ guchar *pixels;
+ guchar *mask_pixels;
+
+ gdk_drawable_get_size (gpix, &width, &height);
+ tmp_buf = gdk_pixbuf_get_from_drawable (NULL, gpix, cmap,
+ 0, 0, 0, 0, width, height);
+ icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
+ g_object_unref (G_OBJECT (tmp_buf));
+
+ if (gmask)
+ {
+ GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL,
+ gmask,
+ NULL,
+ 0, 0, 0, 0,
+ width, height);
+ guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
+ guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
+ int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
+ int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
+ int y;
+
+ for (y = 0; y < height; ++y)
+ {
+ guchar *iconptr, *maskptr;
+ int x;
+
+ iconptr = pixels + y * rowstride;
+ maskptr = mask_pixels + y * mask_rowstride;
+
+ for (x = 0; x < width; ++x)
+ {
+ /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking
+ just R is sufficient. */
+ if (maskptr[0] == 0)
+ iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */
+
+ iconptr += rowstride/width;
+ maskptr += mask_rowstride/width;
+ }
+ }
+
+ g_object_unref (G_OBJECT (mask_buf));
+ }
+
+ return icon_buf;
+}
+
+static Lisp_Object
+file_for_image(image)
+ Lisp_Object image;
+{
+ Lisp_Object specified_file = Qnil;
+ Lisp_Object tail;
+ extern Lisp_Object QCfile;
+
+ for (tail = XCDR (image);
+ NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
+ tail = XCDR (XCDR (tail)))
+ if (EQ (XCAR (tail), QCfile))
+ specified_file = XCAR (XCDR (tail));
+
+ return specified_file;
+}
+
/* For the image defined in IMG, make and return a GtkImage. For displays with
8 planes or less we must make a GdkPixbuf and apply the mask manually.
Otherwise the highlightning and dimming the tool bar code in GTK does
GdkPixmap *gpix;
GdkPixmap *gmask;
GdkDisplay *gdpy;
+ GdkColormap *cmap;
+ GdkPixbuf *icon_buf;
- /* If we are on a one bit display, let GTK do all the image handling.
+ /* If we have a file, let GTK do all the image handling.
This seems to be the only way to make insensitive and activated icons
- look good. */
- if (x_screen_planes (f) == 1)
- {
- Lisp_Object specified_file = Qnil;
- Lisp_Object tail;
- extern Lisp_Object QCfile;
-
- for (tail = XCDR (img->spec);
- NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
- tail = XCDR (XCDR (tail)))
- if (EQ (XCAR (tail), QCfile))
- specified_file = XCAR (XCDR (tail));
-
- if (STRINGP (specified_file))
- {
-
- Lisp_Object file = Qnil;
- struct gcpro gcpro1;
- GCPRO1 (file);
+ look good in all cases. */
+ Lisp_Object specified_file = file_for_image (img->spec);
+ Lisp_Object file;
- file = x_find_image_file (specified_file);
- /* We already loaded the image once before calling this
- function, so this should not fail. */
- xassert (STRINGP (file) != 0);
+ /* We already loaded the image once before calling this
+ function, so this only fails if the image file has been removed.
+ In that case, use the pixmap already loaded. */
- if (! old_widget)
- old_widget = GTK_IMAGE (gtk_image_new_from_file (SDATA (file)));
- else
- gtk_image_set_from_file (old_widget, SDATA (file));
+ if (STRINGP (specified_file)
+ && STRINGP (file = x_find_image_file (specified_file)))
+ {
+ if (! old_widget)
+ old_widget = GTK_IMAGE (gtk_image_new_from_file (SSDATA (file)));
+ else
+ gtk_image_set_from_file (old_widget, SSDATA (file));
- UNGCPRO;
- return GTK_WIDGET (old_widget);
- }
+ return GTK_WIDGET (old_widget);
}
+ /* No file, do the image handling ourselves. This will look very bad
+ on a monochrome display, and sometimes bad on all displays with
+ certain themes. */
+
gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap);
gmask = img->mask ? gdk_pixmap_foreign_new_for_display (gdpy, img->mask) : 0;
- if (x_screen_planes (f) > 8 || x_screen_planes (f) == 1)
- {
- if (! old_widget)
- old_widget = GTK_IMAGE (gtk_image_new_from_pixmap (gpix, gmask));
- else
- gtk_image_set_from_pixmap (old_widget, gpix, gmask);
- }
+ /* This is a workaround to make icons look good on pseudo color
+ displays. Apparently GTK expects the images to have an alpha
+ channel. If they don't, insensitive and activated icons will
+ look bad. This workaround does not work on monochrome displays,
+ and is strictly not needed on true color/static color displays (i.e.
+ 16 bits and higher). But we do it anyway so we get a pixbuf that is
+ not associated with the img->pixmap. The img->pixmap may be removed
+ by clearing the image cache and then the tool bar redraw fails, since
+ Gtk+ assumes the pixmap is always there. */
+ cmap = gtk_widget_get_colormap (widget);
+ icon_buf = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap);
+
+ if (! old_widget)
+ old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
else
- {
- /* This is a workaround to make icons look good on pseudo color
- displays. Apparently GTK expects the images to have an alpha
- channel. If they don't, insensitive and activated icons will
- look bad. This workaround does not work on monochrome displays,
- and is not needed on true color/static color displays (i.e.
- 16 bits and higher). */
- int x, y, width, height, rowstride, mask_rowstride;
- GdkPixbuf *icon_buf, *tmp_buf;
- guchar *pixels;
- guchar *mask_pixels;
-
- gdk_drawable_get_size (gpix, &width, &height);
- tmp_buf = gdk_pixbuf_get_from_drawable (NULL,
- gpix,
- gtk_widget_get_colormap (widget),
- 0, 0, 0, 0, width, height);
- icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
- g_object_unref (G_OBJECT (tmp_buf));
-
- if (gmask)
- {
- GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL,
- gmask,
- NULL,
- 0, 0, 0, 0,
- width, height);
- guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
- guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
- int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
- int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
- int y;
-
- for (y = 0; y < height; ++y)
- {
- guchar *iconptr, *maskptr;
- int x;
+ gtk_image_set_from_pixbuf (old_widget, icon_buf);
- iconptr = pixels + y * rowstride;
- maskptr = mask_pixels + y * mask_rowstride;
-
- for (x = 0; x < width; ++x)
- {
- /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking
- just R is sufficient. */
- if (maskptr[0] == 0)
- iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */
-
- iconptr += rowstride/width;
- maskptr += mask_rowstride/width;
- }
- }
-
- g_object_unref (G_OBJECT (mask_buf));
- }
-
- if (! old_widget)
- old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
- else
- gtk_image_set_from_pixbuf (old_widget, icon_buf);
-
- g_object_unref (G_OBJECT (icon_buf));
- }
+ g_object_unref (G_OBJECT (icon_buf));
g_object_unref (G_OBJECT (gpix));
if (gmask) g_object_unref (G_OBJECT (gmask));
{
char *utf8_str = str;
+ if (!str) return NULL;
+
/* If not UTF-8, try current locale. */
- if (str && !g_utf8_validate (str, -1, NULL))
+ if (!g_utf8_validate (str, -1, NULL))
utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
+ if (!utf8_str)
+ {
+ /* Probably some control characters in str. Escape them. */
+ size_t nr_bad = 0;
+ gsize bytes_read;
+ gsize bytes_written;
+ unsigned char *p = (unsigned char *)str;
+ char *cp, *up;
+ GError *error = NULL;
+
+ while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
+ &bytes_written, &error))
+ && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
+ {
+ ++nr_bad;
+ p += bytes_written+1;
+ g_error_free (error);
+ error = NULL;
+ }
+
+ if (error)
+ {
+ g_error_free (error);
+ error = NULL;
+ }
+ if (cp) g_free (cp);
+
+ up = utf8_str = xmalloc (strlen (str) + nr_bad * 4 + 1);
+ p = (unsigned char *)str;
+
+ while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
+ &bytes_written, &error))
+ && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
+ {
+ strncpy (up, (char *)p, bytes_written);
+ sprintf (up + bytes_written, "\\%03o", p[bytes_written]);
+ up[bytes_written+4] = '\0';
+ up += bytes_written+4;
+ p += bytes_written+1;
+ g_error_free (error);
+ error = NULL;
+ }
+
+ if (cp)
+ {
+ strcat (utf8_str, cp);
+ g_free (cp);
+ }
+ if (error)
+ {
+ g_error_free (error);
+ error = NULL;
+ }
+ }
return utf8_str;
}
if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
geom_str))
fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
+ } else if (f->size_hint_flags & PPosition) {
+ gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ f->left_pos, f->top_pos);
}
}
int columns;
int rows;
{
- gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
- FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
-
- /* base_height is now changed. */
- x_wm_set_size_hint (f, 0, 0);
-
/* If we are not mapped yet, set geometry once again, as window
height now have changed. */
if (! GTK_WIDGET_MAPPED (FRAME_GTK_OUTER_WIDGET (f)))
{
int mbheight = FRAME_MENUBAR_HEIGHT (f);
int tbheight = FRAME_TOOLBAR_HEIGHT (f);
- int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight
+ int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight
- mbheight - tbheight));
int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
if (FRAME_GTK_WIDGET (f)
- && (columns != FRAME_COLS (f)
+ && (columns != FRAME_COLS (f)
|| rows != FRAME_LINES (f)
- || pixelwidth != FRAME_PIXEL_WIDTH (f)
+ || pixelwidth != FRAME_PIXEL_WIDTH (f)
|| pixelheight != FRAME_PIXEL_HEIGHT (f)))
{
struct x_output *x = f->output_data.x;
if (wvbox) gtk_widget_destroy (wvbox);
if (wfixed) gtk_widget_destroy (wfixed);
+ UNBLOCK_INPUT;
return 0;
}
/* 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, SDATA (Vx_resource_name));
+ 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 = SDATA (ENCODE_UTF_8 (f->title));
- else if (! NILP (f->name)) title = SDATA (ENCODE_UTF_8 (f->name));
+ if (! NILP (f->title)) title = SSDATA (ENCODE_UTF_8 (f->title));
+ else if (! NILP (f->name)) title = SSDATA (ENCODE_UTF_8 (f->name));
if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
So we cheat a bit by setting a height that is what it will have
later on when tool bar items are added. */
if (FRAME_EXTERNAL_TOOL_BAR (f) && f->n_tool_bar_items == 0)
- FRAME_TOOLBAR_HEIGHT (f) = 34;
+ FRAME_TOOLBAR_HEIGHT (f) = 38;
/* We don't want this widget double buffered, because we draw on it
can't shrink the window from its starting size. */
gtk_window_set_policy (GTK_WINDOW (wtop), TRUE, TRUE, TRUE);
gtk_window_set_wmclass (GTK_WINDOW (wtop),
- SDATA (Vx_resource_name),
- SDATA (Vx_resource_class));
+ SSDATA (Vx_resource_name),
+ SSDATA (Vx_resource_class));
/* Add callback to do nothing on WM_DELETE_WINDOW. The default in
GTK is to destroy the widget. We want Emacs to do that instead. */
/* 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, f->output_data.x->background_pixel, &bg);
+ 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
}
+/* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
+ functions so GTK does not overwrite the icon. */
+
+void
+xg_set_frame_icon (f, icon_pixmap, icon_mask)
+ FRAME_PTR f;
+ Pixmap icon_pixmap;
+ Pixmap icon_mask;
+{
+ GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+ GdkPixmap *gpix = gdk_pixmap_foreign_new_for_display (gdpy, icon_pixmap);
+ GdkPixmap *gmask = gdk_pixmap_foreign_new_for_display (gdpy, icon_mask);
+ GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, NULL);
+
+ gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
+}
+
+
\f
/***********************************************************************
Dialog functions
/***********************************************************************
File dialog functions
***********************************************************************/
+/* Return non-zero if the old file selection dialog is being used.
+ Return zero if not. */
+
+int
+xg_uses_old_file_dialog ()
+{
#ifdef HAVE_GTK_FILE_BOTH
-int use_old_gtk_file_dialog;
+ extern int x_gtk_use_old_file_dialog;
+ return x_gtk_use_old_file_dialog;
+#else /* ! HAVE_GTK_FILE_BOTH */
+
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
+ return 1;
+#else
+ return 0;
#endif
+#endif /* ! HAVE_GTK_FILE_BOTH */
+}
+
+
/* Function that is called when the file dialog pops down.
W is the dialog widget, RESPONSE is the response code.
USER_DATA is what we passed in to g_signal_connect (pointer to int). */
return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
}
+/* Callback called when the "Show hidden files" toggle is pressed.
+ WIDGET is the toggle widget, DATA is the file chooser dialog. */
+
+static void
+xg_toggle_visibility_cb (widget, data)
+ GtkWidget *widget;
+ gpointer data;
+{
+ GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
+ gboolean visible;
+ g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
+ g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
+}
+
+
+/* Callback called when a property changes in a file chooser.
+ GOBJECT is the file chooser dialog, ARG1 describes the property.
+ USER_DATA is the toggle widget in the file chooser dialog.
+ We use this to update the "Show hidden files" toggle when the user
+ changes that property by right clicking in the file list. */
+
+static void
+xg_toggle_notify_cb (gobject, arg1, user_data)
+ GObject *gobject;
+ GParamSpec *arg1;
+ gpointer user_data;
+{
+ extern int x_gtk_show_hidden_files;
+
+ if (strcmp (arg1->name, "show-hidden") == 0)
+ {
+ GtkFileChooser *dialog = GTK_FILE_CHOOSER (gobject);
+ GtkWidget *wtoggle = GTK_WIDGET (user_data);
+ gboolean visible, toggle_on;
+
+ g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
+ toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
+
+ if (!!visible != !!toggle_on)
+ {
+ g_signal_handlers_block_by_func (G_OBJECT (wtoggle),
+ G_CALLBACK (xg_toggle_visibility_cb),
+ gobject);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
+ g_signal_handlers_unblock_by_func
+ (G_OBJECT (wtoggle),
+ G_CALLBACK (xg_toggle_visibility_cb),
+ gobject);
+ }
+ x_gtk_show_hidden_files = visible;
+ }
+}
+
/* Read a file name from the user using a file chooser dialog.
F is the current frame.
PROMPT is a prompt to show to the user. May not be NULL.
int mustmatch_p, only_dir_p;
xg_get_file_func *func;
{
- GtkWidget *filewin;
+ char message[1024];
+
+ GtkWidget *filewin, *wtoggle, *wbox, *wmessage;
GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
GtkFileChooserAction action = (mustmatch_p ?
GTK_FILE_CHOOSER_ACTION_OPEN :
GTK_FILE_CHOOSER_ACTION_SAVE);
+ extern int x_gtk_show_hidden_files;
+ extern int x_gtk_file_dialog_help_text;
+
if (only_dir_p)
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
GTK_STOCK_OPEN : GTK_STOCK_OK),
GTK_RESPONSE_OK,
NULL);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
+
+ wbox = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (wbox);
+ wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
+
+ if (x_gtk_show_hidden_files)
+ {
+ g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
+ }
+ gtk_widget_show (wtoggle);
+ g_signal_connect (G_OBJECT (wtoggle), "clicked",
+ G_CALLBACK (xg_toggle_visibility_cb), filewin);
+ g_signal_connect (G_OBJECT (filewin), "notify",
+ G_CALLBACK (xg_toggle_notify_cb), wtoggle);
+
+ if (x_gtk_file_dialog_help_text)
+ {
+ message[0] = '\0';
+ /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
+ Show the C-l help text only for versions < 2.10. */
+ if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
+ strcat (message, "\nType C-l to display a file name text entry box.\n");
+ strcat (message, "\nIf you don't like this file selector, use the "
+ "corresponding\nkey binding or customize "
+ "use-file-dialog to turn it off.");
+
+ wmessage = gtk_label_new (message);
+ gtk_widget_show (wmessage);
+ }
+
+ gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
+ if (x_gtk_file_dialog_help_text)
+ gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
if (default_filename)
{
Lisp_Object file;
struct gcpro gcpro1;
+ char *utf8_filename;
GCPRO1 (file);
+ file = build_string (default_filename);
+
/* File chooser does not understand ~/... in the file name. It must be
an absolute name starting with /. */
if (default_filename[0] != '/')
+ file = Fexpand_file_name (file, Qnil);
+
+ utf8_filename = SSDATA (ENCODE_UTF_8 (file));
+ if (! NILP (Ffile_directory_p (file)))
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
+ utf8_filename);
+ else
{
- file = Fexpand_file_name (build_string (default_filename), Qnil);
- default_filename = SDATA (file);
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
+ utf8_filename);
+ if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ {
+ char *cp = strrchr (utf8_filename, '/');
+ if (cp) ++cp;
+ else cp = utf8_filename;
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
+ }
}
- gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
- default_filename);
-
UNGCPRO;
}
char *default_filename;
int mustmatch_p, only_dir_p;
{
- GtkWidget *w;
+ GtkWidget *w = 0;
int count = SPECPDL_INDEX ();
char *fn = 0;
int filesel_done = 0;
xg_get_file_func func;
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+ /* I really don't know why this is needed, but without this the GLIBC add on
+ library linuxthreads hangs when the Gnome file chooser backend creates
+ threads. */
+ sigblock (sigmask (__SIGRTMIN));
+#endif /* HAVE_GTK_AND_PTHREAD */
+
#ifdef HAVE_GTK_FILE_BOTH
- if (use_old_gtk_file_dialog)
+
+ if (xg_uses_old_file_dialog ())
w = xg_get_file_with_selection (f, prompt, default_filename,
mustmatch_p, only_dir_p, &func);
else
#else /* not HAVE_GTK_FILE_BOTH */
-#ifdef HAVE_GTK_FILE_SELECTION_DIALOG_NEW
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
w = xg_get_file_with_selection (f, prompt, default_filename,
mustmatch_p, only_dir_p, &func);
#endif
gtk_main_iteration ();
}
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+ sigunblock (sigmask (__SIGRTMIN));
+#endif
+
if (filesel_done == GTK_RESPONSE_OK)
fn = (*func) (w);
}
/* Callback called when the pointer enters/leaves a menu item.
- W is the menu item.
+ W is the parent of the menu item.
EVENT is either an enter event or leave event.
- CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.
+ CLIENT_DATA is not used.
Returns FALSE to tell GTK to keep processing this event. */
GdkEventCrossing *event;
gpointer client_data;
{
- if (client_data)
+ GdkEvent ev;
+ GtkWidget *subwidget;
+ xg_menu_item_cb_data *data;
+
+ ev.crossing = *event;
+ subwidget = gtk_get_event_widget (&ev);
+ data = (xg_menu_item_cb_data *) g_object_get_data (G_OBJECT (subwidget),
+ XG_ITEM_DATA);
+ if (data)
{
- xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
- gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : client_data;
-
if (! NILP (data->help) && data->cl_data->highlight_cb)
{
+ gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
- (*func) (w, call_data);
+ (*func) (subwidget, call_data);
}
}
UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
CLIENT_DATA is NULL (not used). */
+/* Keep track of total number of grabs. */
+static int menu_grab_callback_cnt;
+
static void
menu_grab_callback (GtkWidget *widget,
gboolean ungrab_p,
gpointer client_data)
{
- /* Keep track of total number of grabs. */
- static int cnt;
+ if (ungrab_p) menu_grab_callback_cnt--;
+ else menu_grab_callback_cnt++;
- if (ungrab_p) cnt--;
- else cnt++;
-
- if (cnt > 0 && ! xg_timer) xg_start_timer ();
- else if (cnt == 0 && xg_timer) xg_stop_timer ();
+ if (menu_grab_callback_cnt > 0 && ! xg_timer) xg_start_timer ();
+ else if (menu_grab_callback_cnt == 0 && xg_timer) xg_stop_timer ();
}
/* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
/* Return non-zero if LABEL specifies a separator (GTK only has one
separator type) */
+static char* separator_names[] = {
+ "space",
+ "no-line",
+ "single-line",
+ "double-line",
+ "single-dashed-line",
+ "double-dashed-line",
+ "shadow-etched-in",
+ "shadow-etched-out",
+ "shadow-etched-in-dash",
+ "shadow-etched-out-dash",
+ "shadow-double-etched-in",
+ "shadow-double-etched-out",
+ "shadow-double-etched-in-dash",
+ "shadow-double-etched-out-dash",
+ 0,
+};
+
static int
xg_separator_p (char *label)
{
&& strncmp (label, "--", 2) == 0
&& label[2] != '-')
{
- static char* separator_names[] = {
- "space",
- "no-line",
- "single-line",
- "double-line",
- "single-dashed-line",
- "double-dashed-line",
- "shadow-etched-in",
- "shadow-etched-out",
- "shadow-etched-in-dash",
- "shadow-etched-out-dash",
- "shadow-double-etched-in",
- "shadow-double-etched-out",
- "shadow-double-etched-in-dash",
- "shadow-double-etched-out-dash",
- 0,
- };
-
int i;
label += 2;
xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
- cb_data->unhighlight_id = cb_data->highlight_id = cb_data->select_id = 0;
+ cb_data->select_id = 0;
cb_data->help = item->help;
cb_data->cl_data = cl_data;
cb_data->call_data = item->call_data;
= g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
}
- if (! NILP (item->help) && highlight_cb)
+ return w;
+}
+
+/* Callback called when keyboard traversal (started by x-menu-bar-open) ends.
+ WMENU is the menu for which traversal has been done. DATA points to the
+ frame for WMENU. We must release grabs, some bad interaction between GTK
+ and Emacs makes the menus keep the grabs. */
+
+static void
+menu_nav_ended (wmenu, data)
+ GtkMenuShell *wmenu;
+ gpointer data;
+{
+ FRAME_PTR f = (FRAME_PTR) data;
+
+ if (FRAME_X_OUTPUT (f)->menubar_widget)
{
- /* We use enter/leave notify instead of select/deselect because
- select/deselect doesn't go well with detached menus. */
- cb_data->highlight_id
- = g_signal_connect (G_OBJECT (w),
- "enter-notify-event",
- G_CALLBACK (menuitem_highlight_callback),
- cb_data);
- cb_data->unhighlight_id
- = g_signal_connect (G_OBJECT (w),
- "leave-notify-event",
- G_CALLBACK (menuitem_highlight_callback),
- cb_data);
- }
+ GtkMenuShell *w = GTK_MENU_SHELL (FRAME_X_OUTPUT (f)->menubar_widget);
+ Display *dpy = FRAME_X_DISPLAY (f);
- return w;
+ BLOCK_INPUT;
+ gtk_menu_shell_deactivate (w);
+ gtk_menu_shell_deselect (w);
+
+ XUngrabKeyboard (dpy, CurrentTime);
+ XUngrabPointer (dpy, CurrentTime);
+ UNBLOCK_INPUT;
+ }
}
+
static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
GCallback, GCallback, int, int, int,
GtkWidget *, xg_menu_cb_data *, char *));
{
wmenu = gtk_menu_new ();
xg_set_screen (wmenu, f);
+ /* Connect this to the menu instead of items so we get enter/leave for
+ disabled items also. TODO: Still does not get enter/leave for
+ disabled items in detached menus. */
+ g_signal_connect (G_OBJECT (wmenu),
+ "enter-notify-event",
+ G_CALLBACK (menuitem_highlight_callback),
+ NULL);
+ g_signal_connect (G_OBJECT (wmenu),
+ "leave-notify-event",
+ G_CALLBACK (menuitem_highlight_callback),
+ NULL);
}
else wmenu = gtk_menu_bar_new ();
+ /* Fix up grabs after keyboard traversal ends. */
+ g_signal_connect (G_OBJECT (wmenu),
+ "selection-done",
+ G_CALLBACK (menu_nav_ended),
+ f);
+
/* Put cl_data on the top menu for easier access. */
cl_data = make_cl_data (cl_data, f, highlight_cb);
g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
if (deactivate_cb)
g_signal_connect (G_OBJECT (wmenu),
- "deactivate", deactivate_cb, 0);
+ "selection-done", deactivate_cb, 0);
g_signal_connect (G_OBJECT (wmenu),
"grab-notify", G_CALLBACK (menu_grab_callback), 0);
cl_data,
&group);
- if (item->contents)
+ /* Create a possibly empty submenu for menu bar items, since some
+ themes don't highlight items correctly without it. */
+ if (item->contents || menu_bar_p)
{
GtkWidget *submenu = create_menus (item->contents,
f,
cl_data,
&group);
+ /* Create a possibly empty submenu for menu bar items, since some
+ themes don't highlight items correctly without it. */
+ GtkWidget *submenu = create_menus (NULL, f,
+ select_cb, NULL, highlight_cb,
+ 0, 0, 0, 0, cl_data, 0);
gtk_widget_set_name (w, MENU_ITEM_NAME);
gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
g_list_free (*list);
*list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
g_list_free (*list);
*list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
while (nr-- > 0) iter = g_list_next (iter);
+ if (iter) iter = g_list_next (iter);
val = val->next;
++pos;
}
g_signal_handler_disconnect (w, cb_data->select_id);
cb_data->select_id = 0;
}
-
- if (NILP (cb_data->help))
- {
- /* Shall not have help. Remove if any existed previously. */
- if (cb_data->highlight_id)
- {
- g_signal_handler_disconnect (G_OBJECT (w),
- cb_data->highlight_id);
- cb_data->highlight_id = 0;
- }
- if (cb_data->unhighlight_id)
- {
- g_signal_handler_disconnect (G_OBJECT (w),
- cb_data->unhighlight_id);
- cb_data->unhighlight_id = 0;
- }
- }
- else if (! cb_data->highlight_id && highlight_cb)
- {
- /* Have help now, but didn't previously. Add callback. */
- cb_data->highlight_id
- = g_signal_connect (G_OBJECT (w),
- "enter-notify-event",
- G_CALLBACK (menuitem_highlight_callback),
- cb_data);
- cb_data->unhighlight_id
- = g_signal_connect (G_OBJECT (w),
- "leave-notify-event",
- G_CALLBACK (menuitem_highlight_callback),
- cb_data);
- }
}
}
xg_update_menubar (menubar, f, &list, list, 0, val->contents,
select_cb, highlight_cb, cl_data);
- if (deep_p);
+ if (deep_p)
{
widget_value *cur;
gpointer data;
{
gpointer p;
- int id = (int)data;
+ int id = (int) (EMACS_INT) data; /* The EMACS_INT cast avoids a warning. */
p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA);
if (p) xfree (p);
if (xg_timer) xg_stop_timer ();
bar->dragging = Qnil;
}
-
+
return FALSE;
}
"value-changed",
scroll_callback,
(gpointer) bar);
+ /* The EMACS_INT cast avoids a warning. */
g_signal_connect (G_OBJECT (wscroll),
"destroy",
G_CALLBACK (xg_gtk_scroll_destroy),
- (gpointer) scroll_id);
+ (gpointer) (EMACS_INT) scroll_id);
/* Connect to button press and button release to detect if any scroll bar
has the pointer. */
event box window. */
gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
gtk_container_add (GTK_CONTAINER (webox), wscroll);
-
+
/* Set the cursor to an arrow. */
xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
GtkWidget *wparent = gtk_widget_get_parent (wscroll);
/* Move and resize to new values. */
- gtk_widget_set_size_request (wscroll, width, height);
gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
-
+ gtk_widget_set_size_request (wscroll, width, height);
+ gtk_widget_queue_draw (wparent);
+ gdk_window_process_all_updates ();
+ /* GTK does not redraw until the main loop is entered again, but
+ if there are no X events pending we will not enter it. So we sync
+ here to get some events. */
+ x_sync (f);
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
}
the GtkImage with a new image. */
#define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
+/* The key for storing the latest modifiers so the activate callback can
+ get them. */
+#define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
+
+/* The key for storing the button widget in its proxy menu item. */
+#define XG_TOOL_BAR_PROXY_BUTTON "emacs-tool-bar-proxy-button"
+
+/* The key for the data we put in the GtkImage widgets. The data is
+ the stock name used by Emacs. We use this to see if we need to update
+ the GtkImage with a new image. */
+#define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
+
+/* As above, but this is used for named theme widgets, as opposed to
+ stock items. */
+#define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
+
+/* Callback function invoked when a tool bar item is pressed.
+ W is the button widget in the tool bar that got pressed,
+ CLIENT_DATA is an integer that is the index of the button in the
+ tool bar. 0 is the first button. */
+
+static gboolean
+xg_tool_bar_button_cb (widget, event, user_data)
+ GtkWidget *widget;
+ GdkEventButton *event;
+ gpointer user_data;
+{
+ /* Casts to avoid warnings when gpointer is 64 bits and int is 32 bits */
+ gpointer ptr = (gpointer) (EMACS_INT) event->state;
+ g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
+ return FALSE;
+}
+
+
/* Callback function invoked when a tool bar item is pressed.
W is the button widget in the tool bar that got pressed,
CLIENT_DATA is an integer that is the index of the button in the
GtkWidget *w;
gpointer client_data;
{
- int idx = (int)client_data;
+ /* The EMACS_INT cast avoids a warning. */
+ int idx = (int) (EMACS_INT) client_data;
+ int mod = (int) (EMACS_INT) g_object_get_data (G_OBJECT (w),
+ XG_TOOL_BAR_LAST_MODIFIER);
+
FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
Lisp_Object key, frame;
struct input_event event;
key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
XSETFRAME (frame, f);
- event.kind = TOOL_BAR_EVENT;
- event.frame_or_window = frame;
- event.arg = frame;
- kbd_buffer_store_event (&event);
+ /* We generate two events here. The first one is to set the prefix
+ to `(tool_bar)', see keyboard.c. */
event.kind = TOOL_BAR_EVENT;
event.frame_or_window = frame;
+ event.arg = frame;
+ kbd_buffer_store_event (&event);
+
+ event.kind = TOOL_BAR_EVENT;
+ event.frame_or_window = frame;
event.arg = key;
- event.modifiers = 0; /* These are not available. */
+ /* Convert between the modifier bits GDK uses and the modifier bits
+ Emacs uses. This assumes GDK an X masks are the same, which they are when
+ this is written. */
+ event.modifiers = x_x_to_emacs_modifiers (FRAME_X_DISPLAY_INFO (f), mod);
kbd_buffer_store_event (&event);
}
+/* Callback function invoked when a tool bar item is pressed in a detached
+ tool bar or the overflow drop down menu.
+ We just call xg_tool_bar_callback.
+ W is the menu item widget that got pressed,
+ CLIENT_DATA is an integer that is the index of the button in the
+ tool bar. 0 is the first button. */
+
+static void
+xg_tool_bar_proxy_callback (w, client_data)
+ GtkWidget *w;
+ gpointer client_data;
+{
+ GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
+ XG_TOOL_BAR_PROXY_BUTTON));
+ xg_tool_bar_callback (wbutton, client_data);
+}
+
+/* This callback is called when a tool item should create a proxy item,
+ such as for the overflow menu. Also called when the tool bar is detached.
+ If we don't create a proxy menu item, the detached tool bar will be
+ blank. */
+
+static gboolean
+xg_tool_bar_menu_proxy (toolitem, user_data)
+ GtkToolItem *toolitem;
+ gpointer user_data;
+{
+ GtkWidget *weventbox = gtk_bin_get_child (GTK_BIN (toolitem));
+ GtkButton *wbutton = GTK_BUTTON (gtk_bin_get_child (GTK_BIN (weventbox)));
+ GtkWidget *wmenuitem = gtk_image_menu_item_new ();
+ GtkWidget *wmenuimage;
+
+ if (gtk_button_get_use_stock (wbutton))
+ wmenuimage = gtk_image_new_from_stock (gtk_button_get_label (wbutton),
+ GTK_ICON_SIZE_MENU);
+ else
+ {
+ GtkImage *wimage = GTK_IMAGE (gtk_bin_get_child (GTK_BIN (wbutton)));
+ GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton));
+ GtkImageType store_type = gtk_image_get_storage_type (wimage);
+ if (store_type == GTK_IMAGE_STOCK)
+ {
+ gchar *stock_id;
+ gtk_image_get_stock (wimage, &stock_id, NULL);
+ wmenuimage = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
+ }
+ else if (store_type == GTK_IMAGE_ICON_SET)
+ {
+ GtkIconSet *icon_set;
+ gtk_image_get_icon_set (wimage, &icon_set, NULL);
+ wmenuimage = gtk_image_new_from_icon_set (icon_set,
+ GTK_ICON_SIZE_MENU);
+ }
+ else if (store_type == GTK_IMAGE_PIXBUF)
+ {
+ gint width, height;
+
+ if (settings &&
+ gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
+ &width, &height))
+ {
+ GdkPixbuf *src_pixbuf, *dest_pixbuf;
+
+ src_pixbuf = gtk_image_get_pixbuf (wimage);
+ dest_pixbuf = gdk_pixbuf_scale_simple (src_pixbuf, width, height,
+ GDK_INTERP_BILINEAR);
+
+ wmenuimage = gtk_image_new_from_pixbuf (dest_pixbuf);
+ }
+ else
+ {
+ fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
+ abort ();
+ }
+ }
+ else if (store_type == GTK_IMAGE_ICON_NAME)
+ {
+ const gchar *icon_name;
+ GtkIconSize icon_size;
+
+ gtk_image_get_icon_name (wimage, &icon_name, &icon_size);
+ wmenuimage = gtk_image_new_from_icon_name (icon_name,
+ GTK_ICON_SIZE_MENU);
+ }
+ else
+ {
+ fprintf (stderr, "internal error: store_type is %d\n", store_type);
+ abort ();
+ }
+ }
+ if (wmenuimage)
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (wmenuitem), wmenuimage);
+
+ g_signal_connect (G_OBJECT (wmenuitem),
+ "activate",
+ GTK_SIGNAL_FUNC (xg_tool_bar_proxy_callback),
+ user_data);
+
+ g_object_set_data (G_OBJECT (wmenuitem), XG_TOOL_BAR_PROXY_BUTTON,
+ (gpointer) wbutton);
+ gtk_tool_item_set_proxy_menu_item (toolitem, "Emacs toolbar item", wmenuitem);
+
+ return TRUE;
+}
+
/* This callback is called when a tool bar is detached. We must set
the height of the tool bar to zero when this happens so frame sizes
are correctly calculated.
gpointer client_data;
{
FRAME_PTR f = (FRAME_PTR) client_data;
+ extern int x_gtk_whole_detached_tool_bar;
+
+ g_object_set (G_OBJECT (w), "show-arrow", !x_gtk_whole_detached_tool_bar,
+ NULL);
if (f)
{
+ FRAME_X_OUTPUT (f)->toolbar_detached = 1;
+
/* When detaching a tool bar, not everything dissapear. There are
a few pixels left that are used to drop the tool bar back into
place. */
- int bw = gtk_container_get_border_width (GTK_CONTAINER (wbox));
FRAME_TOOLBAR_HEIGHT (f) = 2;
/* The height has changed, resize outer widget and set columns
gpointer client_data;
{
FRAME_PTR f = (FRAME_PTR) client_data;
+ g_object_set (G_OBJECT (w), "show-arrow", TRUE, NULL);
if (f)
{
GtkRequisition req;
+ FRAME_X_OUTPUT (f)->toolbar_detached = 0;
+
gtk_widget_size_request (w, &req);
FRAME_TOOLBAR_HEIGHT (f) = req.height;
/* The height has changed, resize outer widget and set columns
- rows to what we had before detaching the tool bar. */
+ rows to what we had before attaching the tool bar. */
xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
}
}
GdkEventCrossing *event;
gpointer client_data;
{
- int idx = (int)client_data;
+ /* The EMACS_INT cast avoids a warning. */
+ int idx = (int) (EMACS_INT) client_data;
FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
Lisp_Object help, frame;
- if (! GTK_IS_BUTTON (w))
- {
- return FALSE;
- }
-
if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
return FALSE;
event->area.x = max (0, event->area.x);
event->area.y = max (0, event->area.y);
-
+
event->area.width = max (width, event->area.width);
event->area.height = max (height, event->area.height);
-
+
return FALSE;
}
-/* This callback is called when a tool bar shall be redrawn.
- We need to update the tool bar from here in case the image cache
- has deleted the pixmaps used in the tool bar.
- W is the GtkToolbar to be redrawn.
- EVENT is the expose event for W.
- CLIENT_DATA is pointing to the frame for this tool bar.
-
- Returns FALSE to tell GTK to keep processing this event. */
+#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
-static gboolean
-xg_tool_bar_expose_callback (w, event, client_data)
- GtkWidget *w;
- GdkEventExpose *event;
- gpointer client_data;
-{
- update_frame_tool_bar ((FRAME_PTR) client_data);
- return FALSE;
-}
/* Create a tool bar for frame F. */
x->toolbar_widget = gtk_toolbar_new ();
x->handlebox_widget = gtk_handle_box_new ();
+ x->toolbar_detached = 0;
+
gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
x->toolbar_widget);
G_CALLBACK (xg_tool_bar_detach_callback), f);
g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
G_CALLBACK (xg_tool_bar_attach_callback), f);
- g_signal_connect (G_OBJECT (x->toolbar_widget),
- "expose-event",
- G_CALLBACK (xg_tool_bar_expose_callback),
- f);
gtk_widget_show_all (x->handlebox_widget);
SET_FRAME_GARBAGED (f);
}
+/* Find the right-to-left image named by RTL in the tool bar images for F.
+ Returns IMAGE if RTL is not found. */
+
+static Lisp_Object
+find_rtl_image (f, image, rtl)
+ FRAME_PTR f;
+ Lisp_Object image;
+ Lisp_Object rtl;
+{
+ int i;
+ Lisp_Object file, rtl_name;
+ struct gcpro gcpro1, gcpro2;
+ GCPRO2 (file, rtl_name);
+
+ rtl_name = Ffile_name_nondirectory (rtl);
+
+ for (i = 0; i < f->n_tool_bar_items; ++i)
+ {
+ Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
+ if (!NILP (file = file_for_image (rtl_image)))
+ {
+ file = call1 (intern ("file-name-sans-extension"),
+ Ffile_name_nondirectory (file));
+ if (EQ (Fequal (file, rtl_name), Qt))
+ {
+ image = rtl_image;
+ break;
+ }
+ }
+ }
+
+ return image;
+}
+
/* Update the tool bar for frame F. Add new buttons and remove old. */
void
{
int i;
GtkRequisition old_req, new_req;
- GList *icon_list;
- GList *iter;
struct x_output *x = f->output_data.x;
+ int hmargin = 0, vmargin = 0;
+ GtkToolbar *wtoolbar;
+ GtkToolItem *ti;
+ GtkTextDirection dir;
if (! FRAME_GTK_WIDGET (f))
return;
BLOCK_INPUT;
+ if (INTEGERP (Vtool_bar_button_margin)
+ && XINT (Vtool_bar_button_margin) > 0)
+ {
+ hmargin = XFASTINT (Vtool_bar_button_margin);
+ vmargin = XFASTINT (Vtool_bar_button_margin);
+ }
+ else if (CONSP (Vtool_bar_button_margin))
+ {
+ if (INTEGERP (XCAR (Vtool_bar_button_margin))
+ && XINT (XCAR (Vtool_bar_button_margin)) > 0)
+ hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
+
+ if (INTEGERP (XCDR (Vtool_bar_button_margin))
+ && XINT (XCDR (Vtool_bar_button_margin)) > 0)
+ vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
+ }
+
+ /* The natural size (i.e. when GTK uses 0 as margin) looks best,
+ so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
+ i.e. zero. This means that margins less than
+ DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
+ hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
+ vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
+
if (! x->toolbar_widget)
xg_create_tool_bar (f);
- gtk_widget_size_request (x->toolbar_widget, &old_req);
-
- icon_list = gtk_container_get_children (GTK_CONTAINER (x->toolbar_widget));
- iter = icon_list;
+ wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
+ gtk_widget_size_request (GTK_WIDGET (wtoolbar), &old_req);
+ dir = gtk_widget_get_direction (x->toolbar_widget);
for (i = 0; i < f->n_tool_bar_items; ++i)
{
-#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
int idx;
int img_id;
- struct image *img;
+ int icon_size = 0;
+ struct image *img = NULL;
Lisp_Object image;
- GtkWidget *wicon = iter ? GTK_WIDGET (iter->data) : 0;
-
- if (iter) iter = g_list_next (iter);
+ Lisp_Object stock;
+ GtkStockItem stock_item;
+ char *stock_name = NULL;
+ char *icon_name = NULL;
+ Lisp_Object rtl;
+ GtkWidget *wbutton = NULL;
+ GtkWidget *weventbox;
+ Lisp_Object func = intern ("x-gtk-map-stock");
+
+ ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i);
+
+ if (ti)
+ {
+ weventbox = gtk_bin_get_child (GTK_BIN (ti));
+ wbutton = gtk_bin_get_child (GTK_BIN (weventbox));
+ }
- /* If image is a vector, choose the image according to the
- button state. */
image = PROP (TOOL_BAR_ITEM_IMAGES);
- if (VECTORP (image))
- {
- if (enabled_p)
- idx = (selected_p
- ? TOOL_BAR_IMAGE_ENABLED_SELECTED
- : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
- else
- idx = (selected_p
- ? TOOL_BAR_IMAGE_DISABLED_SELECTED
- : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
-
- xassert (ASIZE (image) >= idx);
- image = AREF (image, idx);
- }
- else
- idx = -1;
/* Ignore invalid image specifications. */
if (!valid_image_p (image))
{
- if (wicon) gtk_widget_hide (wicon);
+ if (wbutton) gtk_widget_hide (wbutton);
continue;
}
- img_id = lookup_image (f, image);
- img = IMAGE_FROM_ID (f, img_id);
- prepare_image_for_display (f, img);
+ if (EQ (Qt, Ffboundp (func)))
+ stock = call1 (func, file_for_image (image));
- if (img->load_failed_p || img->pixmap == None)
+ if (! NILP (stock) && STRINGP (stock))
{
- if (wicon) gtk_widget_hide (wicon);
- continue;
+ stock_name = SSDATA (stock);
+ if (stock_name[0] == 'n' && stock_name[1] == ':')
+ {
+ GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
+ GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (screen);
+
+ icon_name = stock_name + 2;
+ stock_name = NULL;
+ stock = Qnil;
+
+ if (! gtk_icon_theme_has_icon (icon_theme, icon_name))
+ icon_name = NULL;
+ else
+ icon_size = gtk_toolbar_get_icon_size (wtoolbar);
+ }
+ else if (gtk_stock_lookup (SSDATA (stock), &stock_item))
+ icon_size = gtk_toolbar_get_icon_size (wtoolbar);
+ else
+ {
+ stock = Qnil;
+ stock_name = NULL;
+ }
}
- if (! wicon)
+ if (stock_name == NULL && icon_name == NULL)
{
- GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
+ /* No stock image, or stock item not known. Try regular image. */
- gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
- 0, 0, 0,
- w,
- GTK_SIGNAL_FUNC (xg_tool_bar_callback),
- (gpointer)i);
+ /* If image is a vector, choose the image according to the
+ button state. */
+ if (dir == GTK_TEXT_DIR_RTL
+ && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
+ && STRINGP (rtl))
+ {
+ image = find_rtl_image (f, image, rtl);
+ }
- /* Save the image so we can see if an update is needed when
- this function is called again. */
- g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
- (gpointer)img->pixmap);
+ if (VECTORP (image))
+ {
+ if (enabled_p)
+ idx = (selected_p
+ ? TOOL_BAR_IMAGE_ENABLED_SELECTED
+ : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
+ else
+ idx = (selected_p
+ ? TOOL_BAR_IMAGE_DISABLED_SELECTED
+ : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
+
+ xassert (ASIZE (image) >= idx);
+ image = AREF (image, idx);
+ }
+ else
+ idx = -1;
+
+ img_id = lookup_image (f, image);
+ img = IMAGE_FROM_ID (f, img_id);
+ prepare_image_for_display (f, img);
+
+ if (img->load_failed_p || img->pixmap == None)
+ {
+ if (ti)
+ gtk_widget_hide_all (GTK_WIDGET (ti));
+ else
+ {
+ /* Insert an empty (non-image) button */
+ weventbox = gtk_event_box_new ();
+ wbutton = gtk_button_new ();
+ gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE);
+ gtk_button_set_relief (GTK_BUTTON (wbutton),
+ GTK_RELIEF_NONE);
+ gtk_container_add (GTK_CONTAINER (weventbox), wbutton);
+ ti = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (ti), weventbox);
+ gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1);
+ }
+ continue;
+ }
+ }
+
+ if (ti == NULL)
+ {
+ GtkWidget *w;
+ if (stock_name)
+ {
+ w = gtk_image_new_from_stock (stock_name, icon_size);
+ g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
+ (gpointer) xstrdup (stock_name),
+ (GDestroyNotify) xfree);
+ }
+ else if (icon_name)
+ {
+ w = gtk_image_new_from_icon_name (icon_name, icon_size);
+ g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
+ (gpointer) xstrdup (icon_name),
+ (GDestroyNotify) xfree);
+ }
+ else
+ {
+ w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
+ /* Save the image so we can see if an update is needed when
+ this function is called again. */
+ g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
+ (gpointer)img->pixmap);
+ }
+
+ gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
+ wbutton = gtk_button_new ();
+ gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE);
+ gtk_button_set_relief (GTK_BUTTON (wbutton), GTK_RELIEF_NONE);
+ gtk_container_add (GTK_CONTAINER (wbutton), w);
+ weventbox = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER (weventbox), wbutton);
+ ti = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (ti), weventbox);
+ gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1);
+
+
+ /* The EMACS_INT cast avoids a warning. */
+ g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
+ GTK_SIGNAL_FUNC (xg_tool_bar_menu_proxy),
+ (gpointer) (EMACS_INT) i);
+
+ g_signal_connect (G_OBJECT (wbutton), "clicked",
+ GTK_SIGNAL_FUNC (xg_tool_bar_callback),
+ (gpointer) (EMACS_INT) i);
+
+ gtk_widget_show_all (GTK_WIDGET (ti));
+
+
+ g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
/* Catch expose events to overcome an annoying redraw bug, see
comment for xg_tool_bar_item_expose_callback. */
- g_signal_connect (G_OBJECT (w),
+ g_signal_connect (G_OBJECT (ti),
"expose-event",
G_CALLBACK (xg_tool_bar_item_expose_callback),
0);
- /* We must set sensitive on the button that is the parent
- of the GtkImage parent. Go upwards until we find the button. */
- while (! GTK_IS_BUTTON (w))
- w = gtk_widget_get_parent (w);
-
- if (w)
- {
- /* Save the frame in the button so the xg_tool_bar_callback
- can get at it. */
- g_object_set_data (G_OBJECT (w), XG_FRAME_DATA, (gpointer)f);
- gtk_widget_set_sensitive (w, enabled_p);
-
- /* Use enter/leave notify to show help. We use the events
- rather than the GtkButton specific signals "enter" and
- "leave", so we can have only one callback. The event
- will tell us what kind of event it is. */
- g_signal_connect (G_OBJECT (w),
- "enter-notify-event",
- G_CALLBACK (xg_tool_bar_help_callback),
- (gpointer)i);
- g_signal_connect (G_OBJECT (w),
- "leave-notify-event",
- G_CALLBACK (xg_tool_bar_help_callback),
- (gpointer)i);
- }
+ gtk_widget_set_sensitive (wbutton, enabled_p);
+ gtk_tool_item_set_homogeneous (ti, FALSE);
+
+ /* Callback to save modifyer mask (Shift/Control, etc). GTK makes
+ no distinction based on modifiers in the activate callback,
+ so we have to do it ourselves. */
+ g_signal_connect (wbutton, "button-release-event",
+ GTK_SIGNAL_FUNC (xg_tool_bar_button_cb),
+ NULL);
+
+ g_object_set_data (G_OBJECT (wbutton), XG_FRAME_DATA, (gpointer)f);
+
+ /* Use enter/leave notify to show help. We use the events
+ rather than the GtkButton specific signals "enter" and
+ "leave", so we can have only one callback. The event
+ will tell us what kind of event it is. */
+ /* The EMACS_INT cast avoids a warning. */
+ g_signal_connect (G_OBJECT (weventbox),
+ "enter-notify-event",
+ G_CALLBACK (xg_tool_bar_help_callback),
+ (gpointer) (EMACS_INT) i);
+ g_signal_connect (G_OBJECT (weventbox),
+ "leave-notify-event",
+ G_CALLBACK (xg_tool_bar_help_callback),
+ (gpointer) (EMACS_INT) i);
}
else
{
- /* The child of the tool bar is a button. Inside that button
- is a vbox. Inside that vbox is the GtkImage. */
- GtkWidget *wvbox = gtk_bin_get_child (GTK_BIN (wicon));
- GList *chlist = gtk_container_get_children (GTK_CONTAINER (wvbox));
- GtkImage *wimage = GTK_IMAGE (chlist->data);
+ GtkWidget *wimage = gtk_bin_get_child (GTK_BIN (wbutton));
Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage),
XG_TOOL_BAR_IMAGE_DATA);
- g_list_free (chlist);
+ gpointer old_stock_name = g_object_get_data (G_OBJECT (wimage),
+ XG_TOOL_BAR_STOCK_NAME);
+ gpointer old_icon_name = g_object_get_data (G_OBJECT (wimage),
+ XG_TOOL_BAR_ICON_NAME);
+ if (stock_name &&
+ (! old_stock_name || strcmp (old_stock_name, stock_name) != 0))
+ {
+ gtk_image_set_from_stock (GTK_IMAGE (wimage),
+ stock_name, icon_size);
+ g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
+ (gpointer) xstrdup (stock_name),
+ (GDestroyNotify) xfree);
+ g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
+ NULL);
+ g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL);
+ }
+ else if (icon_name &&
+ (! old_icon_name || strcmp (old_icon_name, icon_name) != 0))
+ {
+ gtk_image_set_from_icon_name (GTK_IMAGE (wimage),
+ icon_name, icon_size);
+ g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME,
+ (gpointer) xstrdup (icon_name),
+ (GDestroyNotify) xfree);
+ g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
+ NULL);
+ g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
+ NULL);
+ }
+ else if (img && old_img != img->pixmap)
+ {
+ (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
+ g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
+ (gpointer)img->pixmap);
- if (old_img != img->pixmap)
- (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
+ g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
+ NULL);
+ g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL);
+ }
- g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
- (gpointer)img->pixmap);
+ gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin);
- gtk_widget_set_sensitive (wicon, enabled_p);
- gtk_widget_show (wicon);
- }
+ gtk_widget_set_sensitive (wbutton, enabled_p);
+ gtk_widget_show_all (GTK_WIDGET (ti));
+ }
#undef PROP
}
/* Remove buttons not longer needed. We just hide them so they
can be reused later on. */
- while (iter)
+ do
{
- GtkWidget *w = GTK_WIDGET (iter->data);
- gtk_widget_hide (w);
- iter = g_list_next (iter);
- }
+ ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i++);
+ if (ti) gtk_widget_hide_all (GTK_WIDGET (ti));
+ } while (ti != NULL);
- gtk_widget_size_request (x->toolbar_widget, &new_req);
- if (old_req.height != new_req.height)
+ gtk_widget_size_request (GTK_WIDGET (wtoolbar), &new_req);
+ if (old_req.height != new_req.height
+ && ! FRAME_X_OUTPUT (f)->toolbar_detached)
{
FRAME_TOOLBAR_HEIGHT (f) = new_req.height;
xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
}
- if (icon_list) g_list_free (icon_list);
-
UNBLOCK_INPUT;
}
void
xg_initialize ()
{
+ GtkBindingSet *binding_set;
+
+#if HAVE_XFT
+ /* Work around a bug with corrupted data if libXft gets unloaded. This way
+ we keep it permanently linked in. */
+ XftInit (0);
+#endif
xg_ignore_gtk_scrollbar = 0;
xg_detached_menus = 0;
xg_menu_cb_list.prev = xg_menu_cb_list.next =
"gtk-key-theme-name",
"Emacs",
EMACS_CLASS);
+
+ /* Make dialogs close on C-g. Since file dialog inherits from
+ dialog, this works for them also. */
+ binding_set = gtk_binding_set_by_class (gtk_type_class (GTK_TYPE_DIALOG));
+ gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK,
+ "close", 0);
+
+ /* Make menus close on C-g. */
+ binding_set = gtk_binding_set_by_class (gtk_type_class (GTK_TYPE_MENU_SHELL));
+ gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK,
+ "cancel", 0);
}
#endif /* USE_GTK */