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"
#ifdef HAVE_GTK_MULTIDISPLAY
/* Return the GdkDisplay that corresponds to the X display DPY. */
+
static GdkDisplay *
xg_get_gdk_display (dpy)
Display *dpy;
/* When the GTK widget W is to be created on a display for F that
is not the default display, set the display for W.
W can be a GtkMenu or a GtkWindow widget. */
+
static void
xg_set_screen (w, f)
GtkWidget *w;
/* Make some defines so we can use the GTK 2.2 functions when
compiling with GTK 2.0. */
+
#define xg_set_screen(w, f)
#define gdk_xid_table_lookup_for_display(dpy, w) gdk_xid_table_lookup (w)
#define gdk_pixmap_foreign_new_for_display(dpy, p) gdk_pixmap_foreign_new (p)
Returns non-zero if display could be opened, zero if display could not
be opened, and less than zero if the GTK version doesn't support
multipe displays. */
+
int
xg_display_open (display_name, dpy)
char *display_name;
}
+/* Close display DPY. */
+
void
xg_display_close (Display *dpy)
{
widget_value_free_list or by malloc:ing a new one.
Return a pointer to the allocated structure. */
+
widget_value *
malloc_widget_value ()
{
/* This is analogous to free. It frees only what was allocated
by malloc_widget_value, and no substructures. */
+
void
free_widget_value (wv)
widget_value *wv;
/* Create and return the cursor to be used for popup menus and
scroll bars on display DPY. */
+
GdkCursor *
xg_create_default_cursor (dpy)
Display *dpy;
return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
}
-/* For the image defined in IMG, make and return a GdkPixmap for
- the pixmap in *GPIX, and a GdkBitmap for the mask in *GMASK.
- If IMG has no mask, *GMASK is set to NULL.
- The image is defined on the display where frame F is. */
-static void
-xg_get_gdk_pixmap_and_mask (f, img, gpix, gmask)
+/* 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
+ will look bad. For display with more than 8 planes we just use the
+ pixmap and mask directly. For monochrome displays, GTK doesn't seem
+ able to use external pixmaps, it looks bad whatever we do.
+ The image is defined on the display where frame F is.
+ WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
+ If OLD_WIDGET is NULL, a new widget is constructed and returned.
+ If OLD_WIDGET is not NULL, that widget is modified. */
+
+static GtkWidget *
+xg_get_image_for_pixmap (f, img, widget, old_widget)
FRAME_PTR f;
struct image *img;
- GdkPixmap **gpix;
- GdkBitmap **gmask;
+ GtkWidget *widget;
+ GtkImage *old_widget;
{
- GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+ GdkPixmap *gpix;
+ GdkPixmap *gmask;
+ GdkDisplay *gdpy;
+
+ /* If we are on a one bit display, 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);
+
+ 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);
- *gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap);
- *gmask = img->mask ?
- (GdkBitmap*) gdk_pixmap_foreign_new_for_display (gdpy, img->mask)
- : 0;
+ if (! old_widget)
+ old_widget = GTK_IMAGE (gtk_image_new_from_file (SDATA (file)));
+ else
+ gtk_image_set_from_file (old_widget, SDATA (file));
+
+ UNGCPRO;
+ return GTK_WIDGET (old_widget);
+ }
+ }
+
+ 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);
+ }
+ 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;
+
+ 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 (gpix));
+ if (gmask) g_object_unref (G_OBJECT (gmask));
+
+ return GTK_WIDGET (old_widget);
}
/* Set CURSOR on W and all widgets W contain. We must do like this
for scroll bars and menu because they create widgets internally,
and it is those widgets that are visible. */
+
static void
xg_set_cursor (w, cursor)
GtkWidget *w;
has expired by calling the GTK event loop.
Also, when a menu is active, it has a small timeout before it
pops down the sub menu under it. */
+
static void
xg_process_timeouts (timer)
struct atimer *timer;
/* Start the xg_timer with an interval of 0.1 seconds, if not already started.
xg_process_timeouts is called when the timer expires. The timer
started is continuous, i.e. runs until xg_stop_timer is called. */
+
static void
xg_start_timer ()
{
}
/* Stop the xg_timer if started. */
+
static void
xg_stop_timer ()
{
}
/* Insert NODE into linked LIST. */
+
static void
xg_list_insert (xg_list_node *list, xg_list_node *node)
{
}
/* Remove NODE from linked LIST. */
+
static void
xg_list_remove (xg_list_node *list, xg_list_node *node)
{
utf8 or NULL, just return STR.
If not, a new string is allocated and the caller must free the result
with g_free. */
+
static char *
get_utf8_string (str)
char *str;
only way to get geometry position right if the user explicitly
asked for a position when starting Emacs.
F is the frame we shall set geometry for. */
+
static void
xg_set_geometry (f)
FRAME_PTR f;
/* Resize the outer window of frame F after chainging the height.
This happend when the menu bar or the tool bar is added or removed.
COLUMNS/ROWS is the size the edit area shall have after the resize. */
+
static void
xg_resize_outer_widget (f, columns, rows)
FRAME_PTR f;
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)))
gdk_window_process_all_updates ();
}
-/* This gets called after the frame F has been cleared. Since that is
- done with X calls, we need to redraw GTK widget (scroll bars). */
-void
-xg_frame_cleared (f)
- FRAME_PTR f;
-{
- GtkWidget *w = f->output_data.x->widget;
-
- if (w)
- {
- gtk_container_set_reallocate_redraws (GTK_CONTAINER (w), TRUE);
- gtk_container_foreach (GTK_CONTAINER (w),
- (GtkCallback) gtk_widget_queue_draw,
- 0);
- gdk_window_process_all_updates ();
- }
-}
-
/* Function to handle resize of our widgets. Since Emacs has some layouts
that does not fit well with GTK standard containers, we do most layout
manually.
F is the frame to resize.
PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
+
void
xg_resize_widgets (f, pixelwidth, pixelheight)
FRAME_PTR 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) || rows != FRAME_LINES (f)
- || pixelwidth != FRAME_PIXEL_WIDTH (f) || pixelheight != FRAME_PIXEL_HEIGHT (f)))
+ && (columns != FRAME_COLS (f)
+ || rows != FRAME_LINES (f)
+ || pixelwidth != FRAME_PIXEL_WIDTH (f)
+ || pixelheight != FRAME_PIXEL_HEIGHT (f)))
{
struct x_output *x = f->output_data.x;
GtkAllocation all;
/* Update our widget size to be COLS/ROWS characters for frame F. */
+
void
xg_frame_set_char_size (f, cols, rows)
FRAME_PTR f;
X Window that aren't accessible.
Return 0 if no widget match WDESC. */
+
GtkWidget *
xg_win_to_widget (dpy, wdesc)
Display *dpy;
/* Fill in the GdkColor C so that it represents PIXEL.
W is the widget that color will be used for. Used to find colormap. */
+
static void
xg_pix_to_gcolor (w, pixel, c)
GtkWidget *w;
gdk_colormap_query_color (map, pixel, c);
}
-/* Turning off double buffering for our GtkFixed widget has the side
- effect of turning it off also for its children (scroll bars).
- But we want those to be double buffered to not flicker so handle
- expose manually here.
- WIDGET is the GtkFixed widget that gets exposed.
- EVENT is the expose event.
- USER_DATA is unused.
-
- Return TRUE to tell GTK that this expose event has been fully handeled
- and that GTK shall do nothing more with it. */
-static gboolean
-xg_fixed_handle_expose (GtkWidget *widget,
- GdkEventExpose *event,
- gpointer user_data)
-{
- GList *iter;
-
- for (iter = GTK_FIXED (widget)->children; iter; iter = g_list_next (iter))
- {
- GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
- GtkWidget *child = child_data->widget;
- GdkWindow *window = child->window;
- GdkRegion *region = gtk_widget_region_intersect (child, event->region);
-
- if (! gdk_region_empty (region))
- {
- GdkEvent child_event;
- child_event.expose = *event;
- child_event.expose.region = region;
-
- /* Turn on double buffering, i.e. draw to an off screen area. */
- gdk_window_begin_paint_region (window, region);
-
- /* Tell child to redraw itself. */
- gdk_region_get_clipbox (region, &child_event.expose.area);
- gtk_widget_send_expose (child, &child_event);
- gdk_window_process_updates (window, TRUE);
-
- /* Copy off screen area to the window. */
- gdk_window_end_paint (window);
- }
-
- gdk_region_destroy (region);
- }
-
- return TRUE;
-}
-
/* Create and set up the GTK widgets for frame F.
Return 0 if creation failed, non-zero otherwise. */
+
int
xg_create_frame_widgets (f)
FRAME_PTR f;
if (wvbox) gtk_widget_destroy (wvbox);
if (wfixed) gtk_widget_destroy (wfixed);
+ UNBLOCK_INPUT;
return 0;
}
up in the wrong place as tool bar height has not been taken into account.
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) && FRAME_TOOLBAR_HEIGHT (f) == 0)
- FRAME_TOOLBAR_HEIGHT (f) = 34;
+ if (FRAME_EXTERNAL_TOOL_BAR (f) && f->n_tool_bar_items == 0)
+ FRAME_TOOLBAR_HEIGHT (f) = 38;
/* We don't want this widget double buffered, because we draw on it
a lot, so we turn off double buffering. */
gtk_widget_set_double_buffered (wfixed, FALSE);
- /* Turning off double buffering above has the side effect of turning
- it off also for its children (scroll bars). But we want those
- to be double buffered to not flicker so handle expose manually. */
- g_signal_connect (G_OBJECT (wfixed), "expose-event",
- G_CALLBACK (xg_fixed_handle_expose), 0);
-
/* GTK documents says use gtk_window_set_resizable. But then a user
can't shrink the window from its starting size. */
gtk_window_set_policy (GTK_WINDOW (wtop), TRUE, TRUE, TRUE);
that the window now has.
If USER_POSITION is nonzero, we set the User Position
flag (this is useful when FLAGS is 0). */
+
void
x_wm_set_size_hint (f, flags, user_position)
FRAME_PTR f;
keep the GTK and X colors in sync.
F is the frame to change,
BG is the pixel value to change to. */
+
void
xg_set_background_color (f, bg)
FRAME_PTR f;
***********************************************************************/
/* Return the dialog title to use for a dialog of type KEY.
This is the encoding used by lwlib. We use the same for GTK. */
+
static char *
get_dialog_title (char key)
{
user_data is NULL (not used).
Returns TRUE to end propagation of event. */
+
static gboolean
dialog_delete_callback (w, event, user_data)
GtkWidget *w;
DEACTIVATE_CB is the callback to use when the dialog pops down.
Returns the GTK dialog widget. */
+
static GtkWidget *
create_dialog (wv, select_cb, deactivate_cb)
widget_value *wv;
}
-enum
-{
- XG_FILE_NOT_DONE,
- XG_FILE_OK,
- XG_FILE_CANCEL,
- XG_FILE_DESTROYED,
-};
+\f
+/***********************************************************************
+ File dialog functions
+ ***********************************************************************/
+/* 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). */
-/* Callback function invoked when the Ok button is pressed in
- a file dialog.
- W is the file dialog widget,
- ARG points to an integer where we record what has happend. */
static void
-xg_file_sel_ok (w, arg)
- GtkWidget *w;
- gpointer arg;
+xg_file_response_cb (w,
+ response,
+ user_data)
+ GtkDialog *w;
+ gint response;
+ gpointer user_data;
{
- *(int*)arg = XG_FILE_OK;
+ int *ptr = (int *) user_data;
+ *ptr = response;
}
-/* Callback function invoked when the Cancel button is pressed in
- a file dialog.
- W is the file dialog widget,
- ARG points to an integer where we record what has happend. */
-static void
-xg_file_sel_cancel (w, arg)
- GtkWidget *w;
- gpointer arg;
+
+/* Destroy the dialog. This makes it pop down. */
+
+static Lisp_Object
+pop_down_file_dialog (arg)
+ Lisp_Object arg;
{
- *(int*)arg = XG_FILE_CANCEL;
+ struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+ BLOCK_INPUT;
+ gtk_widget_destroy (GTK_WIDGET (p->pointer));
+ UNBLOCK_INPUT;
+ return Qnil;
}
-/* Callback function invoked when the file dialog is destroyed (i.e.
- popped down). We must keep track of this, because if this
- happens, GTK destroys the widget. But if for example, Ok is pressed,
- the dialog is popped down, but the dialog widget is not destroyed.
- W is the file dialog widget,
- ARG points to an integer where we record what has happend. */
-static void
-xg_file_sel_destroy (w, arg)
+typedef char * (*xg_get_file_func) P_ ((GtkWidget *));
+
+#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
+
+/* Return the selected file for file chooser dialog W.
+ The returned string must be free:d. */
+
+static char *
+xg_get_file_name_from_chooser (w)
GtkWidget *w;
- gpointer arg;
{
- *(int*)arg = XG_FILE_DESTROYED;
+ return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
}
-/* Read a file name from the user using a file dialog.
+/* 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.
DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
If MUSTMATCH_P is non-zero, the returned file name must be an existing
- file.
+ file. *FUNC is set to a function that can be used to retrieve the
+ selected file name from the returned widget.
- Returns a file name or NULL if no file was selected.
- The returned string must be freed by the caller. */
-char *
-xg_get_file_name (f, prompt, default_filename, mustmatch_p)
+ Returns the created widget. */
+
+static GtkWidget *
+xg_get_file_with_chooser (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, func)
FRAME_PTR f;
char *prompt;
char *default_filename;
- int mustmatch_p;
+ int mustmatch_p, only_dir_p;
+ xg_get_file_func *func;
{
GtkWidget *filewin;
- GtkFileSelection *filesel;
- int filesel_done = XG_FILE_NOT_DONE;
- char *fn = 0;
+ GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
+ GtkFileChooserAction action = (mustmatch_p ?
+ GTK_FILE_CHOOSER_ACTION_OPEN :
+ GTK_FILE_CHOOSER_ACTION_SAVE);
+
+ if (only_dir_p)
+ action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
+
+ filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ (mustmatch_p || only_dir_p ?
+ GTK_STOCK_OPEN : GTK_STOCK_OK),
+ GTK_RESPONSE_OK,
+ NULL);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
- filewin = gtk_file_selection_new (prompt);
- filesel = GTK_FILE_SELECTION (filewin);
+ if (default_filename)
+ {
+ Lisp_Object file;
+ struct gcpro gcpro1;
+ GCPRO1 (file);
- xg_set_screen (filewin, f);
+ file = build_string (default_filename);
- gtk_widget_set_name (filewin, "emacs-filedialog");
+ /* 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);
- gtk_window_set_transient_for (GTK_WINDOW (filewin),
- GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
- gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE);
+ default_filename = SDATA (file);
+ if (Ffile_directory_p (file))
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
+ default_filename);
+ else
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
+ default_filename);
- g_signal_connect (G_OBJECT (filesel->ok_button),
- "clicked",
- G_CALLBACK (xg_file_sel_ok),
- &filesel_done);
- g_signal_connect (G_OBJECT (filesel->cancel_button),
- "clicked",
- G_CALLBACK (xg_file_sel_cancel),
- &filesel_done);
- g_signal_connect (G_OBJECT (filesel),
- "destroy",
- G_CALLBACK (xg_file_sel_destroy),
- &filesel_done);
+ UNGCPRO;
+ }
+
+ *func = xg_get_file_name_from_chooser;
+ return filewin;
+}
+#endif /* HAVE_GTK_FILE_CHOOSER_DIALOG_NEW */
+
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
+
+/* Return the selected file for file selector dialog W.
+ The returned string must be free:d. */
+
+static char *
+xg_get_file_name_from_selector (w)
+ GtkWidget *w;
+{
+ GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
+ return xstrdup ((char*) gtk_file_selection_get_filename (filesel));
+}
+
+/* Create a file selection dialog.
+ F is the current frame.
+ PROMPT is a prompt to show to the user. May not be NULL.
+ DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
+ If MUSTMATCH_P is non-zero, the returned file name must be an existing
+ file. *FUNC is set to a function that can be used to retrieve the
+ selected file name from the returned widget.
+
+ Returns the created widget. */
+
+static GtkWidget *
+xg_get_file_with_selection (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, func)
+ FRAME_PTR f;
+ char *prompt;
+ char *default_filename;
+ int mustmatch_p, only_dir_p;
+ xg_get_file_func *func;
+{
+ GtkWidget *filewin;
+ GtkFileSelection *filesel;
+
+ filewin = gtk_file_selection_new (prompt);
+ filesel = GTK_FILE_SELECTION (filewin);
if (default_filename)
gtk_file_selection_set_filename (filesel, default_filename);
gtk_file_selection_hide_fileop_buttons (filesel);
}
+ *func = xg_get_file_name_from_selector;
- gtk_widget_show_all (filewin);
+ return filewin;
+}
+#endif /* HAVE_GTK_FILE_SELECTION_NEW */
- while (filesel_done == XG_FILE_NOT_DONE)
- gtk_main_iteration ();
+/* Read a file name from the user using a file dialog, either the old
+ file selection dialog, or the new file chooser dialog. Which to use
+ depends on what the GTK version used has, and what the value of
+ gtk-use-old-file-dialog.
+ F is the current frame.
+ PROMPT is a prompt to show to the user. May not be NULL.
+ DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
+ If MUSTMATCH_P is non-zero, the returned file name must be an existing
+ file.
+
+ Returns a file name or NULL if no file was selected.
+ The returned string must be freed by the caller. */
+
+char *
+xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
+ FRAME_PTR f;
+ char *prompt;
+ char *default_filename;
+ int mustmatch_p, only_dir_p;
+{
+ GtkWidget *w = 0;
+ int count = SPECPDL_INDEX ();
+ char *fn = 0;
+ int filesel_done = 0;
+ xg_get_file_func func;
+ extern int x_use_old_gtk_file_dialog;
+
+#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 (x_use_old_gtk_file_dialog)
+ w = xg_get_file_with_selection (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+ else
+ w = xg_get_file_with_chooser (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+
+#else /* not HAVE_GTK_FILE_BOTH */
+
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
+ w = xg_get_file_with_selection (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+#endif
+#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
+ w = xg_get_file_with_chooser (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+#endif
+
+#endif /* HAVE_GTK_FILE_BOTH */
+
+ xg_set_screen (w, f);
+ gtk_widget_set_name (w, "emacs-filedialog");
+ gtk_window_set_transient_for (GTK_WINDOW (w),
+ GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
+ gtk_window_set_modal (GTK_WINDOW (w), TRUE);
+
+ g_signal_connect (G_OBJECT (w),
+ "response",
+ G_CALLBACK (xg_file_response_cb),
+ &filesel_done);
+
+ /* Don't destroy the widget if closed by the window manager close button. */
+ g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
+
+ gtk_widget_show (w);
+
+ record_unwind_protect (pop_down_file_dialog, make_save_value (w, 0));
+ while (! filesel_done)
+ {
+ x_menu_wait_for_event (0);
+ gtk_main_iteration ();
+ }
- if (filesel_done == XG_FILE_OK)
- fn = xstrdup ((char*) gtk_file_selection_get_filename (filesel));
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+ sigunblock (sigmask (__SIGRTMIN));
+#endif
- if (filesel_done != XG_FILE_DESTROYED)
- gtk_widget_destroy (filewin);
+ if (filesel_done == GTK_RESPONSE_OK)
+ fn = (*func) (w);
+
+ unbind_to (count, Qnil);
return fn;
}
Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
allocated xg_menu_cb_data if CL_DATA is NULL. */
+
static xg_menu_cb_data *
make_cl_data (cl_data, f, highlight_cb)
xg_menu_cb_data *cl_data;
HIGHLIGHT_CB could change, there is no check that the same
function is given when modifying a menu bar as was given when
creating the menu bar. */
+
static void
update_cl_data (cl_data, f, highlight_cb)
xg_menu_cb_data *cl_data;
/* Decrease reference count for CL_DATA.
If reference count is zero, free CL_DATA. */
+
static void
unref_cl_data (cl_data)
xg_menu_cb_data *cl_data;
}
/* Function that marks all lisp data during GC. */
+
void
xg_mark_data ()
{
/* Callback called when a menu item is destroyed. Used to free data.
W is the widget that is being destroyed (not used).
CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
+
static void
menuitem_destroy_callback (w, client_data)
GtkWidget *w;
CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.
Returns FALSE to tell GTK to keep processing this event. */
+
static gboolean
menuitem_highlight_callback (w, event, client_data)
GtkWidget *w;
/* Callback called when a menu is destroyed. Used to free data.
W is the widget that is being destroyed (not used).
CLIENT_DATA points to the xg_menu_cb_data associated with W. */
+
static void
menu_destroy_callback (w, client_data)
GtkWidget *w;
W is the widget that does the grab (not used).
UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
CLIENT_DATA is NULL (not used). */
+
static void
menu_grab_callback (GtkWidget *widget,
gboolean ungrab_p,
must be non-NULL) and can be inserted into a menu item.
Returns the GtkHBox. */
+
static GtkWidget *
make_widget_for_menu_item (utf8_label, utf8_key)
char *utf8_label;
Unfortunately, keys don't line up as nicely as in Motif,
but the MacOS X version doesn't either, so I guess that is OK. */
+
static GtkWidget *
make_menu_item (utf8_label, utf8_key, item, group)
char *utf8_label;
/* Return non-zero if LABEL specifies a separator (GTK only has one
separator type) */
+
static int
xg_separator_p (char *label)
{
static int xg_detached_menus;
/* Returns non-zero if there are detached menus. */
+
int
xg_have_tear_offs ()
{
decrease the xg_detached_menus count.
WIDGET is the top level window that is removed (the parent of the menu).
CLIENT_DATA is not used. */
+
static void
tearoff_remove (widget, client_data)
GtkWidget *widget;
xg_detached_menus count.
WIDGET is the GtkTearoffMenuItem.
CLIENT_DATA is not used. */
+
static void
tearoff_activate (widget, client_data)
GtkWidget *widget;
in the group. On exit, *GROUP contains the radio item group.
Returns the created GtkWidget. */
+
static GtkWidget *
xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
widget_value *item;
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);
HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
Returns the widget created. */
+
GtkWidget *
xg_create_widget (type, name, f, val,
select_cb, deactivate_cb, highlight_cb)
GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
gtk_widget_set_name (w, "emacs-dialog");
+ gtk_window_set_modal (GTK_WINDOW (w), TRUE);
}
else if (menu_bar_p || pop_up_p)
{
}
/* Return the label for menu item WITEM. */
+
static const char *
xg_get_menu_item_label (witem)
GtkMenuItem *witem;
}
/* Return non-zero if the menu item WITEM has the text LABEL. */
+
static int
xg_item_label_same_p (witem, label)
GtkMenuItem *witem;
return is_same;
}
-/* Remove widgets in LIST from container WCONT. */
+/* Destroy widgets in LIST. */
+
static void
-remove_from_container (wcont, list)
- GtkWidget *wcont;
+xg_destroy_widgets (list)
GList *list;
{
GList *iter;
{
GtkWidget *w = GTK_WIDGET (iter->data);
- /* Add a ref to w so we can explicitly destroy it later. */
- gtk_widget_ref (w);
- gtk_container_remove (GTK_CONTAINER (wcont), w);
-
- /* If there is a menu under this widget that has been detached,
- there is a reference to it, and just removing w from the
- container does not destroy the submenu. By explicitly
- destroying w we make sure the submenu is destroyed, thus
- removing the detached window also if there was one. */
+ /* Destroying the widget will remove it from the container it is in. */
gtk_widget_destroy (w);
}
}
CL_DATA points to the callback data to be used for this menu bar.
This function calls itself to walk through the menu bar names. */
+
static void
xg_update_menubar (menubar, f, list, iter, pos, val,
select_cb, highlight_cb, cl_data)
else if (iter && ! val)
{
/* Item(s) have been removed. Remove all remaining items. */
- remove_from_container (menubar, iter);
+ xg_destroy_widgets (iter);
/* All updated. */
val = 0;
SELECT_CB is the callback to use when a menu item is selected.
HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
CL_DATA is the data to set in the widget for menu invokation. */
+
static void
xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
widget_value *val;
}
/* Update the toggle menu item W so it corresponds to VAL. */
+
static void
xg_update_toggle_item (val, w)
widget_value *val;
}
/* Update the radio menu item W so it corresponds to VAL. */
+
static void
xg_update_radio_item (val, w)
widget_value *val;
{
/* If we are adding new menu items below, we must remove from
first radio button so that radio groups become correct. */
- if (cur && first_radio) remove_from_container (submenu, first_radio);
- else remove_from_container (submenu, iter);
+ if (cur && first_radio) xg_destroy_widgets (first_radio);
+ else xg_destroy_widgets (iter);
}
if (cur)
SELECT_CB is the callback to use when a menu item is selected.
DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
+
void
xg_modify_menubar_widgets (menubar, f, val, deep_p,
select_cb, deactivate_cb, highlight_cb)
/* Setting scroll bar values invokes the callback. Use this variable
to indicate that callback should do nothing. */
+
int xg_ignore_gtk_scrollbar;
/* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
32 bits. But we want to store pointers, and they may be larger
than 32 bits. Keep a mapping from integer index to widget pointers
to get around the 32 bit limitation. */
+
static struct
{
GtkWidget **widgets;
} id_to_widget;
/* Grow this much every time we need to allocate more */
+
#define ID_TO_WIDGET_INCR 32
/* Store the widget pointer W in id_to_widget and return the integer index. */
+
static int
xg_store_widget_in_map (w)
GtkWidget *w;
/* Remove pointer at IDX from id_to_widget.
Called when scroll bar is destroyed. */
+
static void
xg_remove_widget_from_map (idx)
int idx;
}
/* Get the widget pointer at IDX from id_to_widget. */
+
static GtkWidget *
xg_get_widget_from_map (idx)
int idx;
/* Return the scrollbar id for X Window WID on display DPY.
Return -1 if WID not in id_to_widget. */
+
int
xg_get_scroll_id_for_window (dpy, wid)
Display *dpy;
/* Callback invoked when scroll bar WIDGET is destroyed.
DATA is the index into id_to_widget for WIDGET.
We free pointer to last scroll bar values here and remove the index. */
+
static void
xg_gtk_scroll_destroy (widget, data)
GtkWidget *widget;
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);
Returns FALSE to tell GTK that it shall continue propagate the event
to widgets. */
+
static gboolean
scroll_bar_button_cb (widget, event, user_data)
GtkWidget *widget;
if (xg_timer) xg_stop_timer ();
bar->dragging = Qnil;
}
-
+
return FALSE;
}
bar changes.
SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
to set resources for the widget. */
+
void
xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
FRAME_PTR f;
char *scroll_bar_name;
{
GtkWidget *wscroll;
+ GtkWidget *webox;
GtkObject *vadj;
int scroll_id;
0.1, 0.1, 0.1);
wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
+ webox = gtk_event_box_new ();
gtk_widget_set_name (wscroll, scroll_bar_name);
gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
"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. */
G_CALLBACK (scroll_bar_button_cb),
(gpointer) bar);
- gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget),
- wscroll, -1, -1);
+ /* The scroll bar widget does not draw on a window of its own. Instead
+ it draws on the parent window, in this case the edit widget. So
+ whenever the edit widget is cleared, the scroll bar needs to redraw
+ also, which causes flicker. Put an event box between the edit widget
+ and the scroll bar, so the scroll bar instead draws itself on the
+ 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 (wscroll, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
+ xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
}
/* Make the scroll bar represented by SCROLLBAR_ID visible. */
+
void
xg_show_scroll_bar (scrollbar_id)
int scrollbar_id;
{
GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
if (w)
- gtk_widget_show (w);
+ gtk_widget_show_all (gtk_widget_get_parent (w));
}
/* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
+
void
xg_remove_scroll_bar (f, scrollbar_id)
FRAME_PTR f;
GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
if (w)
{
+ GtkWidget *wparent = gtk_widget_get_parent (w);
gtk_widget_destroy (w);
+ gtk_widget_destroy (wparent);
SET_FRAME_GARBAGED (f);
}
}
-/* Find left/top for widget W in GtkFixed widget WFIXED. */
-static void
-xg_find_top_left_in_fixed (w, wfixed, left, top)
- GtkWidget *w, *wfixed;
- int *left, *top;
-{
- GList *iter;
-
- for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter))
- {
- GtkFixedChild *child = (GtkFixedChild *) iter->data;
-
- if (child->widget == w)
- {
- *left = child->x;
- *top = child->y;
- return;
- }
- }
-
- /* Shall never end up here. */
- abort ();
-}
-
/* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
in frame F.
TOP/LEFT are the new pixel positions where the bar shall appear.
WIDTH, HEIGHT is the size in pixels the bar shall have. */
+
void
-xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height,
- real_left, canon_width)
+xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
FRAME_PTR f;
int scrollbar_id;
int top;
if (wscroll)
{
GtkWidget *wfixed = f->output_data.x->edit_widget;
- int winextra = canon_width > width ? (canon_width - width) / 2 : 0;
- int bottom = top + height;
-
- gint slider_width;
- int oldtop, oldleft, oldbottom;
- GtkRequisition req;
-
- /* Get old values. */
- xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop);
- gtk_widget_size_request (wscroll, &req);
- oldbottom = oldtop + req.height;
-
- /* Scroll bars in GTK has a fixed width, so if we say width 16, it
- will only be its fixed width (14 is default) anyway, the rest is
- blank. We are drawing the mode line across scroll bars when
- the frame is split:
- |bar| |fringe|
- ----------------
- mode line
- ----------------
- |bar| |fringe|
-
- When we "unsplit" the frame:
-
- |bar| |fringe|
- -| |-| |
- m¦ |i| |
- -| |-| |
- | | | |
-
-
- the remains of the mode line can be seen in these blank spaces.
- So we must clear them explicitly.
- GTK scroll bars should do that, but they don't.
- Also, the canonical width may be wider than the width for the
- scroll bar so that there is some space (typically 1 pixel) between
- the scroll bar and the edge of the window and between the scroll
- bar and the fringe. */
-
- if (oldtop != -1 && oldleft != -1)
- {
- int gtkextral, gtkextrah;
- int xl, xr, wbl, wbr;
- int bottomdiff, topdiff;
-
- gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL);
- gtkextral = width > slider_width ? (width - slider_width) / 2 : 0;
- gtkextrah = gtkextral ? (width - slider_width - gtkextral) : 0;
-
- xl = real_left;
- wbl = gtkextral + winextra;
- wbr = gtkextrah + winextra;
- xr = left + gtkextral + slider_width;
- bottomdiff = abs (oldbottom - bottom);
- topdiff = abs (oldtop - top);
-
- if (oldleft != left)
- {
- gdk_window_clear_area (wfixed->window, xl, top, wbl, height);
- gdk_window_clear_area (wfixed->window, xr, top, wbr, height);
- }
-
- if (oldtop > top)
- {
- gdk_window_clear_area (wfixed->window, xl, top, wbl, topdiff);
- gdk_window_clear_area (wfixed->window, xr, top, wbr, topdiff);
- }
- else if (oldtop < top)
- {
- gdk_window_clear_area (wfixed->window, xl, oldtop, wbl, topdiff);
- gdk_window_clear_area (wfixed->window, xr, oldtop, wbr, topdiff);
- }
-
- if (oldbottom > bottom)
- {
- gdk_window_clear_area (wfixed->window, xl, bottom, wbl,
- bottomdiff);
- gdk_window_clear_area (wfixed->window, xr, bottom, wbr,
- bottomdiff);
- }
- else if (oldbottom < bottom)
- {
- gdk_window_clear_area (wfixed->window, xl, oldbottom, wbl,
- bottomdiff);
- gdk_window_clear_area (wfixed->window, xr, oldbottom, wbr,
- bottomdiff);
- }
- }
+ GtkWidget *wparent = gtk_widget_get_parent (wscroll);
/* Move and resize to new values. */
- gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top);
+ gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
gtk_widget_set_size_request (wscroll, width, height);
-
- /* Must force out update so changed scroll bars gets redrawn. */
+ 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);
}
/* Set the thumb size and position of scroll bar BAR. We are currently
displaying PORTION out of a whole WHOLE, and our position POSITION. */
+
void
xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
struct scroll_bar *bar;
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 void
xg_tool_bar_callback (w, client_data)
GtkWidget *w;
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 key, frame;
struct input_event event;
WBOX is the handle box widget that enables detach/attach of the tool bar.
W is the tool bar widget.
CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
+
static void
xg_tool_bar_detach_callback (wbox, w, client_data)
GtkHandleBox *wbox;
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
WBOX is the handle box widget that enables detach/attach of the tool bar.
W is the tool bar widget.
CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
+
static void
xg_tool_bar_attach_callback (wbox, w, client_data)
GtkHandleBox *wbox;
{
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));
}
}
tool bar. 0 is the first button.
Returns FALSE to tell GTK to keep processing this event. */
+
static gboolean
xg_tool_bar_help_callback (w, event, client_data)
GtkWidget *w;
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;
CLIENT_DATA is unused.
Returns FALSE to tell GTK to keep processing this event. */
+
static gboolean
xg_tool_bar_item_expose_callback (w, event, client_data)
GtkWidget *w;
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;
}
CLIENT_DATA is pointing to the frame for this tool bar.
Returns FALSE to tell GTK to keep processing this event. */
+
static gboolean
xg_tool_bar_expose_callback (w, event, client_data)
GtkWidget *w;
return FALSE;
}
+/* Create a tool bar for frame F. */
+
static void
xg_create_tool_bar (f)
FRAME_PTR 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);
SET_FRAME_GARBAGED (f);
}
+/* Update the tool bar for frame F. Add new buttons and remove old. */
+
void
update_frame_tool_bar (f)
FRAME_PTR f;
GList *icon_list;
GList *iter;
struct x_output *x = f->output_data.x;
+ int hmargin, vmargin;
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);
if (! wicon)
{
- GdkPixmap *gpix;
- GdkBitmap *gmask;
- GtkWidget *w;
+ GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
- xg_get_gdk_pixmap_and_mask (f, img, &gpix, &gmask);
- w = gtk_image_new_from_pixmap (gpix, gmask);
+ gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
+
+ /* The EMACS_INT cast avoids a warning. */
gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
0, 0, 0,
w,
GTK_SIGNAL_FUNC (xg_tool_bar_callback),
- (gpointer)i);
+ (gpointer) (EMACS_INT) i);
/* Save the image so we can see if an update is needed when
this function is called again. */
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 (w),
"enter-notify-event",
G_CALLBACK (xg_tool_bar_help_callback),
- (gpointer)i);
+ (gpointer) (EMACS_INT) i);
g_signal_connect (G_OBJECT (w),
"leave-notify-event",
G_CALLBACK (xg_tool_bar_help_callback),
- (gpointer)i);
+ (gpointer) (EMACS_INT) i);
}
}
else
XG_TOOL_BAR_IMAGE_DATA);
g_list_free (chlist);
- if (old_img != img->pixmap)
- {
- GdkPixmap *gpix;
- GdkBitmap *gmask;
+ gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin);
- xg_get_gdk_pixmap_and_mask (f, img, &gpix, &gmask);
- gtk_image_set_from_pixmap (wimage, gpix, gmask);
- }
+ 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_IMAGE_DATA,
(gpointer)img->pixmap);
}
gtk_widget_size_request (x->toolbar_widget, &new_req);
- if (old_req.height != new_req.height)
+ 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));
UNBLOCK_INPUT;
}
+/* Deallocate all resources for the tool bar on frame F.
+ Remove the tool bar. */
+
void
free_frame_tool_bar (f)
FRAME_PTR f;
void
xg_initialize ()
{
+ GtkBindingSet *binding_set;
+
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 */