/* Functions for creating and updating GTK widgets.
- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GNU Emacs.
-GNU Emacs is free software; you can redistribute it and/or modify
+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 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
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., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
-#include "config.h"
+#include <config.h>
#ifdef USE_GTK
#include <string.h>
#include <signal.h>
#include <stdio.h>
+#include <setjmp.h>
#include "lisp.h"
#include "xterm.h"
#include "blockinput.h"
#include "syssignal.h"
#include "window.h"
-#include "atimer.h"
#include "gtkutil.h"
#include "termhooks.h"
#include "keyboard.h"
#include "coding.h"
#include <gdk/gdkkeysyms.h>
+#ifdef HAVE_XFT
+#include <X11/Xft/Xft.h>
+#endif
#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
(FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
#ifdef HAVE_GTK_MULTIDISPLAY
-/* Gtk does not work well without any display open. Emacs may close
- all its displays. In that case, keep a display around just for
- the purpose of having one. */
+/* Keep track of the default display, or NULL if there is none. Emacs
+ may close all its displays. */
static GdkDisplay *gdpy_def;
-
-/* Return the GdkDisplay that corresponds to the X display DPY. */
-
-static GdkDisplay *
-xg_get_gdk_display (dpy)
- Display *dpy;
-{
- return gdk_x11_lookup_xdisplay (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. */
GdkDisplay *gdpy;
gdpy = gdk_display_open (display_name);
- *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
+ if (!gdpy_def && gdpy)
+ {
+ gdpy_def = gdpy;
+ gdk_display_manager_set_default_display (gdk_display_manager_get (),
+ gdpy);
+ }
+ *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
return gdpy != NULL;
#else /* not HAVE_GTK_MULTIDISPLAY */
#ifdef HAVE_GTK_MULTIDISPLAY
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
- /* If this is the default display, we must change it before calling
- dispose, otherwise it will crash on some Gtk+ versions. */
+ /* If this is the default display, try to change it before closing.
+ If there is no other display to use, gdpy_def is set to NULL, and
+ the next call to xg_display_open resets the default display. */
if (gdk_display_get_default () == gdpy)
{
struct x_display_info *dpyinfo;
- Display *new_dpy = 0;
- GdkDisplay *gdpy_new;
+ GdkDisplay *gdpy_new = NULL;
/* Find another display. */
for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
if (dpyinfo->display != dpy)
{
- new_dpy = dpyinfo->display;
+ gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
+ gdk_display_manager_set_default_display (gdk_display_manager_get (),
+ gdpy_new);
break;
}
-
- if (new_dpy)
- gdpy_new = gdk_x11_lookup_xdisplay (new_dpy);
- else
- {
- if (!gdpy_def)
- gdpy_def = gdk_display_open (gdk_display_get_name (gdpy));
- gdpy_new = gdpy_def;
- }
-
- gdk_display_manager_set_default_display (gdk_display_manager_get (),
- gdpy_new);
+ gdpy_def = gdpy_new;
}
- /* 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
+ /* 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. */
g_object_run_dispose (G_OBJECT (gdpy));
#else
/* This seems to be fixed in GTK 2.10. */
/***********************************************************************
Utility functions
***********************************************************************/
-/* The timer for scroll bar repetition and menu bar timeouts.
- NULL if no timer is started. */
-static struct atimer *xg_timer;
-
-
/* The next two variables and functions are taken from lwlib. */
static widget_value *widget_value_free_list;
static int malloc_cpt;
{
/* When the number of already allocated cells is too big,
We free it. */
- free (wv);
+ xfree (wv);
malloc_cpt--;
}
else
GdkPixmap *gmask;
GdkColormap *cmap;
{
- int x, y, width, height, rowstride, mask_rowstride;
+ int width, height;
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,
}
static Lisp_Object
-file_for_image(image)
+file_for_image (image)
Lisp_Object image;
{
Lisp_Object specified_file = Qnil;
gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
}
-/* Timer function called when a timeout occurs for xg_timer.
- This function processes all GTK events in a recursive event loop.
- This is done because GTK timer events are not seen by Emacs event
- detection, Emacs only looks for X events. When a scroll bar has the
- pointer (detected by button press/release events below) an Emacs
- timer is started, and this function can then check if the GTK timer
- 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;
-{
- BLOCK_INPUT;
- /* Ideally we would like to just handle timer events, like the Xt version
- of this does in xterm.c, but there is no such feature in GTK. */
- while (gtk_events_pending ())
- gtk_main_iteration ();
- UNBLOCK_INPUT;
-}
-
-/* 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 ()
-{
- if (! xg_timer)
- {
- EMACS_TIME interval;
- EMACS_SET_SECS_USECS (interval, 0, 100000);
- xg_timer = start_atimer (ATIMER_CONTINUOUS,
- interval,
- xg_process_timeouts,
- 0);
- }
-}
-
-/* Stop the xg_timer if started. */
-
-static void
-xg_stop_timer ()
-{
- if (xg_timer)
- {
- cancel_atimer (xg_timer);
- xg_timer = 0;
- }
-}
-
/* Insert NODE into linked LIST. */
static void
if (!g_utf8_validate (str, -1, NULL))
utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
- if (!utf8_str)
+ if (!utf8_str)
{
/* Probably some control characters in str. Escape them. */
size_t nr_bad = 0;
error = NULL;
}
- if (error)
+ if (error)
{
g_error_free (error);
error = NULL;
error = NULL;
}
- if (cp)
+ if (cp)
{
strcat (utf8_str, cp);
g_free (cp);
}
- if (error)
+ if (error)
{
g_error_free (error);
error = NULL;
FRAME_PTR f;
{
if (f->size_hint_flags & USPosition)
- {
- int left = f->left_pos;
- int xneg = f->size_hint_flags & XNegative;
- int top = f->top_pos;
- int yneg = f->size_hint_flags & YNegative;
- char geom_str[32];
-
- if (xneg)
- left = -left;
- if (yneg)
- top = -top;
-
- sprintf (geom_str, "=%dx%d%c%d%c%d",
- FRAME_PIXEL_WIDTH (f),
- FRAME_TOTAL_PIXEL_HEIGHT (f),
- (xneg ? '-' : '+'), left,
- (yneg ? '-' : '+'), top);
-
- 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) {
+ {
+ int left = f->left_pos;
+ int xneg = f->size_hint_flags & XNegative;
+ int top = f->top_pos;
+ int yneg = f->size_hint_flags & YNegative;
+ char geom_str[32];
+
+ if (xneg)
+ left = -left;
+ if (yneg)
+ top = -top;
+
+ sprintf (geom_str, "=%dx%d%c%d%c%d",
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_TOTAL_PIXEL_HEIGHT (f),
+ (xneg ? '-' : '+'), left,
+ (yneg ? '-' : '+'), top);
+
+ 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);
- }
}
-
-/* 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. */
+/* Clear under internal border if any. As we use a mix of Gtk+ and X calls
+ and use a GtkFixed widget, this doesn't happen automatically. */
static void
-xg_resize_outer_widget (f, columns, rows)
+xg_clear_under_internal_border (f)
FRAME_PTR f;
- int columns;
- int rows;
{
- /* 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)))
- xg_set_geometry (f);
-
- xg_frame_set_char_size (f, columns, rows);
- gdk_window_process_all_updates ();
+ if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
+ {
+ GtkWidget *wfixed = f->output_data.x->edit_widget;
+ gtk_widget_queue_draw (wfixed);
+ gdk_window_process_all_updates ();
+ x_clear_area (FRAME_X_DISPLAY (f),
+ FRAME_X_WINDOW (f),
+ 0, 0,
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_INTERNAL_BORDER_WIDTH (f), 0);
+ x_clear_area (FRAME_X_DISPLAY (f),
+ FRAME_X_WINDOW (f),
+ 0, 0,
+ FRAME_INTERNAL_BORDER_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f), 0);
+ x_clear_area (FRAME_X_DISPLAY (f),
+ FRAME_X_WINDOW (f),
+ 0, FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_INTERNAL_BORDER_WIDTH (f), 0);
+ x_clear_area (FRAME_X_DISPLAY (f),
+ FRAME_X_WINDOW (f),
+ FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
+ 0,
+ FRAME_INTERNAL_BORDER_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f), 0);
+ }
}
-/* 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.
+/* Function to handle resize of our frame. As we have a Gtk+ tool bar
+ and a Gtk+ menu bar, we get resize events for the edit part of the
+ frame only. We let Gtk+ deal with the Gtk+ parts.
F is the frame to resize.
PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
void
-xg_resize_widgets (f, pixelwidth, pixelheight)
+xg_frame_resized (f, pixelwidth, pixelheight)
FRAME_PTR f;
int pixelwidth, pixelheight;
{
- int mbheight = FRAME_MENUBAR_HEIGHT (f);
- int tbheight = FRAME_TOOLBAR_HEIGHT (f);
- 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)))
- {
- struct x_output *x = f->output_data.x;
- GtkAllocation all;
+ int rows, columns;
- all.y = mbheight + tbheight;
- all.x = 0;
+ if (pixelwidth == -1 && pixelheight == -1)
+ {
+ if (FRAME_GTK_WIDGET (f) && GTK_WIDGET_MAPPED (FRAME_GTK_WIDGET (f)))
+ gdk_window_get_geometry (FRAME_GTK_WIDGET (f)->window, 0, 0,
+ &pixelwidth, &pixelheight, 0);
+ else return;
+ }
+
- all.width = pixelwidth;
- all.height = pixelheight - mbheight - tbheight;
+ rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
+ columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
- gtk_widget_size_allocate (x->edit_widget, &all);
+ if (columns != FRAME_COLS (f)
+ || rows != FRAME_LINES (f)
+ || pixelwidth != FRAME_PIXEL_WIDTH (f)
+ || pixelheight != FRAME_PIXEL_HEIGHT (f))
+ {
+ FRAME_PIXEL_WIDTH (f) = pixelwidth;
+ FRAME_PIXEL_HEIGHT (f) = pixelheight;
+ xg_clear_under_internal_border (f);
change_frame_size (f, rows, columns, 0, 1, 0);
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
}
}
-
-/* Update our widget size to be COLS/ROWS characters for frame F. */
+/* Resize the outer window of frame F after chainging the height.
+ COLUMNS/ROWS is the size the edit area shall have after the resize. */
void
xg_frame_set_char_size (f, cols, rows)
+ FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
int pixelwidth;
+ if (FRAME_PIXEL_HEIGHT (f) == 0)
+ return;
+
/* Take into account the size of the scroll bar. Always use the
number of columns occupied by the scroll bar here otherwise we
might end up with a frame width that is not a multiple of the
after calculating that value. */
pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
+
+ /* Do this before resize, as we don't know yet if we will be resized. */
+ xg_clear_under_internal_border (f);
+
/* Must resize our top level widget. Font size may have changed,
but not rows/cols. */
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
pixelwidth, pixelheight);
- xg_resize_widgets (f, pixelwidth, pixelheight);
x_wm_set_size_hint (f, 0, 0);
+
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
+
+ /* We can not call change_frame_size for a mapped frame,
+ we can not set pixel width/height either. The window manager may
+ override our resize request, XMonad does this all the time.
+ The best we can do is try to sync, so lisp code sees the updated
+ size as fast as possible.
+ For unmapped windows, we can set rows/cols. When
+ the frame is mapped again we will (hopefully) get the correct size. */
+ if (f->async_visible)
+ {
+ /* Must call this to flush out events */
+ (void)gtk_events_pending ();
+ gdk_flush ();
+ x_wait_for_event (f, ConfigureNotify);
+ }
+ else
+ {
+ change_frame_size (f, rows, cols, 0, 1, 0);
+ FRAME_PIXEL_WIDTH (f) = pixelwidth;
+ FRAME_PIXEL_HEIGHT (f) = pixelheight;
+ }
+}
+
+/* Handle height changes (i.e. add/remove menu/toolbar).
+ The policy is to keep the number of editable lines. */
+
+static void
+xg_height_changed (f)
+ FRAME_PTR f;
+{
+ gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
+ f->output_data.x->hint_flags = 0;
+ x_wm_set_size_hint (f, 0, 0);
}
/* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
GtkWidget *wfixed;
GdkColor bg;
GtkRcStyle *style;
- int i;
char *title = 0;
BLOCK_INPUT;
gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
- gtk_widget_set_size_request (wfixed, FRAME_PIXEL_WIDTH (f),
- FRAME_PIXEL_HEIGHT (f));
-
gtk_container_add (GTK_CONTAINER (wtop), wvbox);
gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
if (FRAME_EXTERNAL_TOOL_BAR (f))
update_frame_tool_bar (f);
- /* The tool bar is created but first there are no items in it.
- This causes it to be zero height. Later items are added, but then
- the frame is already mapped, so there is a "jumping" resize.
- This makes geometry handling difficult, for example -0-0 will end
- 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) && f->n_tool_bar_items == 0)
- FRAME_TOOLBAR_HEIGHT (f) = 38;
-
-
/* We don't want this widget double buffered, because we draw on it
with regular X drawing primitives, so from a GTK/GDK point of
view, the widget is totally blank. When an expose comes, this
a lot, so we turn off double buffering. */
gtk_widget_set_double_buffered (wfixed, FALSE);
- /* 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);
gtk_window_set_wmclass (GTK_WINDOW (wtop),
SSDATA (Vx_resource_name),
SSDATA (Vx_resource_class));
and specify it.
GTK will itself handle calculating the real position this way. */
xg_set_geometry (f);
+ f->win_gravity
+ = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
gtk_widget_add_events (wfixed,
GDK_POINTER_MOTION_MASK
gtk_widget_modify_style (wfixed, style);
/* GTK does not set any border, and they look bad with GTK. */
+ /* That they look bad is no excuse for imposing this here. --Stef
+ It should be done by providing the proper default in Fx_create_Frame.
f->border_width = 0;
- f->internal_border_width = 0;
+ f->internal_border_width = 0; */
UNBLOCK_INPUT;
long flags;
int user_position;
{
- if (FRAME_GTK_OUTER_WIDGET (f))
- {
- /* Must use GTK routines here, otherwise GTK resets the size hints
- to its own defaults. */
- GdkGeometry size_hints;
- gint hint_flags = 0;
- int base_width, base_height;
- int min_rows = 0, min_cols = 0;
- int win_gravity = f->win_gravity;
-
- if (flags)
- {
- memset (&size_hints, 0, sizeof (size_hints));
- f->output_data.x->size_hints = size_hints;
- f->output_data.x->hint_flags = hint_flags;
- }
- else
- flags = f->size_hint_flags;
-
- size_hints = f->output_data.x->size_hints;
- hint_flags = f->output_data.x->hint_flags;
-
- hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
- size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
- size_hints.height_inc = FRAME_LINE_HEIGHT (f);
-
- hint_flags |= GDK_HINT_BASE_SIZE;
- base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
- base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
- + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
-
- check_frame_size (f, &min_rows, &min_cols);
-
- size_hints.base_width = base_width;
- size_hints.base_height = base_height;
- size_hints.min_width = base_width + min_cols * size_hints.width_inc;
- size_hints.min_height = base_height + min_rows * size_hints.height_inc;
-
-
- /* These currently have a one to one mapping with the X values, but I
- don't think we should rely on that. */
- hint_flags |= GDK_HINT_WIN_GRAVITY;
- size_hints.win_gravity = 0;
- if (win_gravity == NorthWestGravity)
- size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
- else if (win_gravity == NorthGravity)
- size_hints.win_gravity = GDK_GRAVITY_NORTH;
- else if (win_gravity == NorthEastGravity)
- size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
- else if (win_gravity == WestGravity)
- size_hints.win_gravity = GDK_GRAVITY_WEST;
- else if (win_gravity == CenterGravity)
- size_hints.win_gravity = GDK_GRAVITY_CENTER;
- else if (win_gravity == EastGravity)
- size_hints.win_gravity = GDK_GRAVITY_EAST;
- else if (win_gravity == SouthWestGravity)
- size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
- else if (win_gravity == SouthGravity)
- size_hints.win_gravity = GDK_GRAVITY_SOUTH;
- else if (win_gravity == SouthEastGravity)
- size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
- else if (win_gravity == StaticGravity)
- size_hints.win_gravity = GDK_GRAVITY_STATIC;
-
- if (flags & PPosition) hint_flags |= GDK_HINT_POS;
- if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
- if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
-
- if (user_position)
- {
- hint_flags &= ~GDK_HINT_POS;
- hint_flags |= GDK_HINT_USER_POS;
- }
+ /* Must use GTK routines here, otherwise GTK resets the size hints
+ to its own defaults. */
+ GdkGeometry size_hints;
+ gint hint_flags = 0;
+ int base_width, base_height;
+ int min_rows = 0, min_cols = 0;
+ int win_gravity = f->win_gravity;
+
+ /* Don't set size hints during initialization; that apparently leads
+ to a race condition. See the thread at
+ http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */
+ if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
+ return;
+
+ if (flags)
+ {
+ memset (&size_hints, 0, sizeof (size_hints));
+ f->output_data.x->size_hints = size_hints;
+ f->output_data.x->hint_flags = hint_flags;
+ }
+ else
+ flags = f->size_hint_flags;
- BLOCK_INPUT;
+ size_hints = f->output_data.x->size_hints;
+ hint_flags = f->output_data.x->hint_flags;
- gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
- FRAME_GTK_OUTER_WIDGET (f),
- &size_hints,
- hint_flags);
+ hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
+ size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
+ size_hints.height_inc = FRAME_LINE_HEIGHT (f);
- f->output_data.x->size_hints = size_hints;
- f->output_data.x->hint_flags = hint_flags;
- UNBLOCK_INPUT;
- }
+ hint_flags |= GDK_HINT_BASE_SIZE;
+ base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
+ base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
+ + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
+
+ check_frame_size (f, &min_rows, &min_cols);
+
+ size_hints.base_width = base_width;
+ size_hints.base_height = base_height;
+ size_hints.min_width = base_width + min_cols * size_hints.width_inc;
+ size_hints.min_height = base_height + min_rows * size_hints.height_inc;
+
+ /* These currently have a one to one mapping with the X values, but I
+ don't think we should rely on that. */
+ hint_flags |= GDK_HINT_WIN_GRAVITY;
+ size_hints.win_gravity = 0;
+ if (win_gravity == NorthWestGravity)
+ size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
+ else if (win_gravity == NorthGravity)
+ size_hints.win_gravity = GDK_GRAVITY_NORTH;
+ else if (win_gravity == NorthEastGravity)
+ size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
+ else if (win_gravity == WestGravity)
+ size_hints.win_gravity = GDK_GRAVITY_WEST;
+ else if (win_gravity == CenterGravity)
+ size_hints.win_gravity = GDK_GRAVITY_CENTER;
+ else if (win_gravity == EastGravity)
+ size_hints.win_gravity = GDK_GRAVITY_EAST;
+ else if (win_gravity == SouthWestGravity)
+ size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
+ else if (win_gravity == SouthGravity)
+ size_hints.win_gravity = GDK_GRAVITY_SOUTH;
+ else if (win_gravity == SouthEastGravity)
+ size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
+ else if (win_gravity == StaticGravity)
+ size_hints.win_gravity = GDK_GRAVITY_STATIC;
+
+ if (flags & PPosition) hint_flags |= GDK_HINT_POS;
+ if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
+ if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
+
+ if (user_position)
+ {
+ hint_flags &= ~GDK_HINT_POS;
+ hint_flags |= GDK_HINT_USER_POS;
+ }
+
+ if (hint_flags != f->output_data.x->hint_flags
+ || memcmp (&size_hints,
+ &f->output_data.x->size_hints,
+ sizeof (size_hints)) != 0)
+ {
+ BLOCK_INPUT;
+ gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ NULL, &size_hints, hint_flags);
+ f->output_data.x->size_hints = size_hints;
+ f->output_data.x->hint_flags = hint_flags;
+ UNBLOCK_INPUT;
+ }
}
/* Change background color of a frame.
- Since GTK uses the background colour to clear the window, we must
+ Since GTK uses the background color to clear the window, we must
keep the GTK and X colors in sync.
F is the frame to change,
BG is the pixel value to change to. */
return wdialog;
}
+struct xg_dialog_data
+{
+ GMainLoop *loop;
+ int response;
+ GtkWidget *w;
+ guint timerid;
+};
+
+/* Function that is called when the file or font dialogs pop down.
+ W is the dialog widget, RESPONSE is the response code.
+ USER_DATA is what we passed in to g_signal_connect. */
+
+static void
+xg_dialog_response_cb (w,
+ response,
+ user_data)
+ GtkDialog *w;
+ gint response;
+ gpointer user_data;
+{
+ struct xg_dialog_data *dd = (struct xg_dialog_data *)user_data;
+ dd->response = response;
+ g_main_loop_quit (dd->loop);
+}
+
+
+/* Destroy the dialog. This makes it pop down. */
+
+static Lisp_Object
+pop_down_dialog (arg)
+ Lisp_Object arg;
+{
+ struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+ struct xg_dialog_data *dd = (struct xg_dialog_data *) p->pointer;
+
+ BLOCK_INPUT;
+ if (dd->w) gtk_widget_destroy (dd->w);
+ if (dd->timerid != 0) g_source_remove (dd->timerid);
+
+ g_main_loop_quit (dd->loop);
+ g_main_loop_unref (dd->loop);
+
+ UNBLOCK_INPUT;
+
+ return Qnil;
+}
+
+/* If there are any emacs timers pending, add a timeout to main loop in DATA.
+ We pass in DATA as gpointer* so we can use this as a callback. */
+
+static gboolean
+xg_maybe_add_timer (data)
+ gpointer data;
+{
+ struct xg_dialog_data *dd = (struct xg_dialog_data *) data;
+ EMACS_TIME next_time = timer_check (1);
+ long secs = EMACS_SECS (next_time);
+ long usecs = EMACS_USECS (next_time);
+
+ dd->timerid = 0;
+
+ if (secs >= 0 && usecs >= 0 && secs < ((guint)-1)/1000)
+ {
+ dd->timerid = g_timeout_add (secs * 1000 + usecs/1000,
+ xg_maybe_add_timer,
+ dd);
+ }
+ return FALSE;
+}
+
+
+/* Pops up a modal dialog W and waits for response.
+ We don't use gtk_dialog_run because we want to process emacs timers.
+ The dialog W is not destroyed when this function returns. */
+
+static int
+xg_dialog_run (f, w)
+ FRAME_PTR f;
+ GtkWidget *w;
+
+{
+ int count = SPECPDL_INDEX ();
+ struct xg_dialog_data dd;
+
+ xg_set_screen (w, f);
+ 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);
+
+ dd.loop = g_main_loop_new (NULL, FALSE);
+ dd.response = GTK_RESPONSE_CANCEL;
+ dd.w = w;
+ dd.timerid = 0;
+
+ g_signal_connect (G_OBJECT (w),
+ "response",
+ G_CALLBACK (xg_dialog_response_cb),
+ &dd);
+ /* 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_dialog, make_save_value (&dd, 0));
+
+ (void) xg_maybe_add_timer (&dd);
+ g_main_loop_run (dd.loop);
+
+ dd.w = 0;
+ unbind_to (count, Qnil);
+
+ return dd.response;
+}
\f
/***********************************************************************
}
-/* 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). */
-
-static void
-xg_file_response_cb (w,
- response,
- user_data)
- GtkDialog *w;
- gint response;
- gpointer user_data;
-{
- int *ptr = (int *) user_data;
- *ptr = response;
-}
-
-
-/* Destroy the dialog. This makes it pop down. */
-
-static Lisp_Object
-pop_down_file_dialog (arg)
- Lisp_Object arg;
-{
- struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
- BLOCK_INPUT;
- gtk_widget_destroy (GTK_WIDGET (p->pointer));
- UNBLOCK_INPUT;
- return Qnil;
-}
-
typedef char * (*xg_get_file_func) P_ ((GtkWidget *));
#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
if (strcmp (arg1->name, "show-hidden") == 0)
{
- GtkFileChooser *dialog = GTK_FILE_CHOOSER (gobject);
GtkWidget *wtoggle = GTK_WIDGET (user_data);
gboolean visible, toggle_on;
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)
+
+ 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);
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);
}
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),
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;
#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 ();
- }
+ filesel_done = xg_dialog_run (f, w);
#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
sigunblock (sigmask (__SIGRTMIN));
if (filesel_done == GTK_RESPONSE_OK)
fn = (*func) (w);
- unbind_to (count, Qnil);
-
+ gtk_widget_destroy (w);
return fn;
}
+#ifdef HAVE_FREETYPE
+/* Pop up a GTK font selector and return the name of the font the user
+ selects, as a C string. The returned font name follows GTK's own
+ format:
+
+ `FAMILY [VALUE1 VALUE2] SIZE'
+
+ This can be parsed using font_parse_fcname in font.c.
+ DEFAULT_NAME, if non-zero, is the default font name. */
+
+char *
+xg_get_font_name (f, default_name)
+ FRAME_PTR f;
+ char *default_name;
+{
+ GtkWidget *w;
+ char *fontname = NULL;
+ int done = 0;
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+ sigblock (sigmask (__SIGRTMIN));
+#endif /* HAVE_GTK_AND_PTHREAD */
+
+ w = gtk_font_selection_dialog_new ("Pick a font");
+ if (!default_name)
+ default_name = "Monospace 10";
+ gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (w),
+ default_name);
+
+ gtk_widget_set_name (w, "emacs-fontdialog");
+
+ done = xg_dialog_run (f, w);
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+ sigunblock (sigmask (__SIGRTMIN));
+#endif
+
+ if (done == GTK_RESPONSE_OK)
+ fontname = gtk_font_selection_dialog_get_font_name
+ (GTK_FONT_SELECTION_DIALOG (w));
+
+ gtk_widget_destroy (w);
+ return fontname;
+}
+#endif /* HAVE_FREETYPE */
+
+
\f
/***********************************************************************
Menu functions.
***********************************************************************/
-/* The name of menu items that can be used for citomization. Since GTK
+/* The name of menu items that can be used for customization. Since GTK
RC files are very crude and primitive, we have to set this on all
- menu item names so a user can easily cutomize menu items. */
+ menu item names so a user can easily customize menu items. */
#define MENU_ITEM_NAME "emacs-menuitem"
unref_cl_data ((xg_menu_cb_data*) client_data);
}
-/* Callback called when a menu does a grab or ungrab. That means the
- menu has been activated or deactivated.
- Used to start a timer so the small timeout the menus in GTK uses before
- popping down a menu is seen by Emacs (see xg_process_timeouts above).
- 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). */
-
-/* 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)
-{
- if (ungrab_p) menu_grab_callback_cnt--;
- else menu_grab_callback_cnt++;
-
- 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
must be non-NULL) and can be inserted into a menu item.
/* Return non-zero if LABEL specifies a separator (GTK only has one
separator type) */
-static char* separator_names[] = {
+static const char* separator_names[] = {
"space",
"no-line",
"single-line",
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)
- {
- GtkMenuShell *w = GTK_MENU_SHELL (FRAME_X_OUTPUT (f)->menubar_widget);
- Display *dpy = FRAME_X_DISPLAY (f);
-
- 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 *));
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);
+ else
+ {
+ wmenu = gtk_menu_bar_new ();
+ /* Set width of menu bar to a small value so it doesn't enlarge
+ a small initial frame size. The width will be set to the
+ width of the frame later on when it is added to a container.
+ height -1: Natural height. */
+ gtk_widget_set_size_request (wmenu, 1, -1);
+ }
/* Put cl_data on the top menu for easier access. */
cl_data = make_cl_data (cl_data, f, highlight_cb);
if (deactivate_cb)
g_signal_connect (G_OBJECT (wmenu),
"selection-done", deactivate_cb, 0);
-
- g_signal_connect (G_OBJECT (wmenu),
- "grab-notify", G_CALLBACK (menu_grab_callback), 0);
}
if (! menu_bar_p && add_tearoff_p)
static void
xg_update_menubar (menubar, f, list, iter, pos, val,
- select_cb, highlight_cb, cl_data)
+ select_cb, deactivate_cb, highlight_cb, cl_data)
GtkWidget *menubar;
FRAME_PTR f;
GList **list;
int pos;
widget_value *val;
GCallback select_cb;
+ GCallback deactivate_cb;
GCallback highlight_cb;
xg_menu_cb_data *cl_data;
{
/* Item(s) have been removed. Remove all remaining items. */
xg_destroy_widgets (iter);
+ /* Add a blank entry so the menubar doesn't collapse to nothing. */
+ gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
+ gtk_menu_item_new_with_label (""),
+ 0);
/* All updated. */
val = 0;
iter = 0;
else if (! iter && val)
{
/* Item(s) added. Add all new items in one call. */
- create_menus (val, f, select_cb, 0, highlight_cb,
+ create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
0, 1, 0, menubar, cl_data, 0);
/* All updated. */
/* 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,
+ select_cb, deactivate_cb,
+ 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);
/* Update the rest of the menu bar. */
xg_update_menubar (menubar, f, list, iter, pos, val,
- select_cb, highlight_cb, cl_data);
+ select_cb, deactivate_cb, highlight_cb, cl_data);
}
/* Update the menu item W so it corresponds to VAL.
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. */
+ CL_DATA is the data to set in the widget for menu invocation. */
static void
xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
XG_FRAME_DATA);
xg_update_menubar (menubar, f, &list, list, 0, val->contents,
- select_cb, highlight_cb, cl_data);
+ select_cb, deactivate_cb, highlight_cb, cl_data);
if (deep_p)
{
if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
return 0;
+ if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
+ return 0; /* Already done this, happens for frames created invisible. */
+
BLOCK_INPUT;
gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
gtk_widget_show_all (x->menubar_widget);
gtk_widget_size_request (x->menubar_widget, &req);
-
FRAME_MENUBAR_HEIGHT (f) = req.height;
-
- /* The height has changed, resize outer widget and set columns
- rows to what we had before adding the menu bar. */
- xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
-
- SET_FRAME_GARBAGED (f);
+ xg_height_changed (f);
UNBLOCK_INPUT;
return 1;
the container. */
x->menubar_widget = 0;
FRAME_MENUBAR_HEIGHT (f) = 0;
-
- /* The height has changed, resize outer widget and set columns
- rows to what we had before removing the menu bar. */
- xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
-
- SET_FRAME_GARBAGED (f);
+ xg_height_changed (f);
UNBLOCK_INPUT;
}
}
GtkWidget *widget;
gpointer data;
{
- gpointer p;
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);
xg_remove_widget_from_map (id);
}
-/* Callback for button press/release events. Used to start timer so that
- the scroll bar repetition timer in GTK gets handeled.
- Also, sets bar->dragging to Qnil when dragging (button release) is done.
- WIDGET is the scroll bar widget the event is for (not used).
- EVENT contains the event.
- USER_DATA points to the struct scrollbar structure.
-
- 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;
- GdkEventButton *event;
- gpointer user_data;
-{
- if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
- xg_start_timer ();
- else if (event->type == GDK_BUTTON_RELEASE)
- {
- struct scroll_bar *bar = (struct scroll_bar *) user_data;
- if (xg_timer) xg_stop_timer ();
- bar->dragging = Qnil;
- }
-
- return FALSE;
-}
-
/* Create a scroll bar widget for frame F. Store the scroll bar
in BAR.
SCROLL_CALLBACK is the callback to invoke when the value of the
bar changes.
+ END_CALLBACK is the callback to invoke when scrolling ends.
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)
+xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name)
FRAME_PTR f;
struct scroll_bar *bar;
- GCallback scroll_callback;
+ GCallback scroll_callback, end_callback;
char *scroll_bar_name;
{
GtkWidget *wscroll;
webox = gtk_event_box_new ();
gtk_widget_set_name (wscroll, scroll_bar_name);
gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
+ g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
scroll_id = xg_store_widget_in_map (wscroll);
- g_signal_connect (G_OBJECT (wscroll),
- "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) (EMACS_INT) scroll_id);
-
- /* Connect to button press and button release to detect if any scroll bar
- has the pointer. */
g_signal_connect (G_OBJECT (wscroll),
- "button-press-event",
- G_CALLBACK (scroll_bar_button_cb),
+ "change-value",
+ scroll_callback,
(gpointer) bar);
g_signal_connect (G_OBJECT (wscroll),
"button-release-event",
- G_CALLBACK (scroll_bar_button_cb),
+ end_callback,
(gpointer) bar);
-
+
/* 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
{
GtkWidget *wfixed = f->output_data.x->edit_widget;
GtkWidget *wparent = gtk_widget_get_parent (wscroll);
+ GtkFixed *wf = GTK_FIXED (wfixed);
+
+ /* Clear out old position. */
+ GList *iter;
+ int oldx = -1, oldy = -1, oldw, oldh;
+ for (iter = wf->children; iter; iter = iter->next)
+ if (((GtkFixedChild *)iter->data)->widget == wparent)
+ {
+ GtkFixedChild *ch = (GtkFixedChild *)iter->data;
+ if (ch->x != left || ch->y != top)
+ {
+ oldx = ch->x;
+ oldy = ch->y;
+ gtk_widget_get_size_request (wscroll, &oldw, &oldh);
+ }
+ break;
+ }
/* Move and resize to new values. */
gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
gtk_widget_set_size_request (wscroll, width, height);
- gtk_widget_queue_draw (wparent);
+ gtk_widget_queue_draw (wfixed);
gdk_window_process_all_updates ();
+ if (oldx != -1)
+ {
+ /* Clear under old scroll bar position. This must be done after
+ the gtk_widget_queue_draw and gdk_window_process_all_updates
+ above. */
+ x_clear_area (FRAME_X_DISPLAY (f),
+ FRAME_X_WINDOW (f),
+ oldx, oldy, oldw, oldh, 0);
+ }
+
/* 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);
if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
{
- GtkWidget *wfixed = f->output_data.x->edit_widget;
-
BLOCK_INPUT;
/* gtk_range_set_value invokes the callback. Set
}
}
+/* Return non-zero if EVENT is for a scroll bar in frame F.
+ When the same X window is used for several Gtk+ widgets, we cannot
+ say for sure based on the X window alone if an event is for the
+ frame. This function does additional checks.
+
+ Return non-zero if the event is for a scroll bar, zero otherwise. */
+
+int
+xg_event_is_for_scrollbar (f, event)
+ FRAME_PTR f;
+ XEvent *event;
+{
+ int retval = 0;
+
+ if (f && event->type == ButtonPress && event->xbutton.button < 4)
+ {
+ /* Check if press occurred outside the edit widget. */
+ GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+ retval = gdk_display_get_window_at_pointer (gdpy, NULL, NULL)
+ != f->output_data.x->edit_widget->window;
+ }
+ else if (f
+ && ((event->type == ButtonRelease && event->xbutton.button < 4)
+ || event->type == MotionNotify))
+ {
+ /* If we are releasing or moving the scroll bar, it has the grab. */
+ retval = gtk_grab_get_current () != 0
+ && gtk_grab_get_current () != f->output_data.x->edit_widget;
+ }
+
+ return retval;
+}
+
+
\f
/***********************************************************************
Tool bar functions
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.arg = frame;
+ kbd_buffer_store_event (&event);
+
+ event.kind = TOOL_BAR_EVENT;
event.frame_or_window = frame;
event.arg = key;
/* 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
+ Emacs uses. This assumes GDK and 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);
+
+ /* Return focus to the frame after we have clicked on a detached
+ tool bar button. */
+ Fx_focus_frame (frame);
}
/* Callback function invoked when a tool bar item is pressed in a detached
xg_tool_bar_callback (wbutton, client_data);
}
+
+static gboolean
+xg_tool_bar_help_callback P_ ((GtkWidget *w,
+ GdkEventCrossing *event,
+ gpointer client_data));
+
+/* This callback is called when a help is to be shown for an item in
+ the detached tool bar when the detached tool bar it is not expanded. */
+
+static gboolean
+xg_tool_bar_proxy_help_callback (w, event, client_data)
+ GtkWidget *w;
+ GdkEventCrossing *event;
+ gpointer client_data;
+{
+ GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
+ XG_TOOL_BAR_PROXY_BUTTON));
+
+ return xg_tool_bar_help_callback (wbutton, event, 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
{
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 *wmenuitem = gtk_image_menu_item_new_with_label ("");
GtkWidget *wmenuimage;
- if (gtk_button_get_use_stock (wbutton))
+ if (gtk_button_get_use_stock (wbutton))
wmenuimage = gtk_image_new_from_stock (gtk_button_get_label (wbutton),
GTK_ICON_SIZE_MENU);
else
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))
abort ();
}
}
- else if (store_type == GTK_IMAGE_ICON_NAME)
+ else if (store_type == GTK_IMAGE_ICON_NAME)
{
const gchar *icon_name;
GtkIconSize icon_size;
g_signal_connect (G_OBJECT (wmenuitem),
"activate",
- GTK_SIGNAL_FUNC (xg_tool_bar_proxy_callback),
+ G_CALLBACK (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);
+ gtk_widget_set_sensitive (wmenuitem, GTK_WIDGET_SENSITIVE (wbutton));
+
+ /* 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 (wmenuitem),
+ "enter-notify-event",
+ G_CALLBACK (xg_tool_bar_proxy_help_callback),
+ user_data);
+ g_signal_connect (G_OBJECT (wmenuitem),
+ "leave-notify-event",
+ G_CALLBACK (xg_tool_bar_proxy_help_callback),
+ user_data);
return TRUE;
}
/* 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. */
- FRAME_TOOLBAR_HEIGHT (f) = 2;
-
- /* The height has changed, resize outer widget and set columns
- rows to what we had before detaching the tool bar. */
- xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
+ FRAME_TOOLBAR_HEIGHT (f) = 4;
+ xg_height_changed (f);
}
}
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 attaching the tool bar. */
- xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
+ xg_height_changed (f);
}
}
return FALSE;
}
-#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
-
-
-/* Create a tool bar for frame F. */
+/* Attach a tool bar to frame F. */
static void
-xg_create_tool_bar (f)
+xg_pack_tool_bar (f)
FRAME_PTR f;
{
struct x_output *x = f->output_data.x;
- GtkRequisition req;
int vbox_pos = x->menubar_widget ? 1 : 0;
- x->toolbar_widget = gtk_toolbar_new ();
x->handlebox_widget = gtk_handle_box_new ();
- x->toolbar_detached = 0;
+ g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
+ 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);
gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
x->toolbar_widget);
gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget,
vbox_pos);
+ gtk_widget_show_all (x->handlebox_widget);
+}
+
+/* Create a tool bar for frame F. */
+
+static void
+xg_create_tool_bar (f)
+ FRAME_PTR f;
+{
+ struct x_output *x = f->output_data.x;
+
+ x->toolbar_widget = gtk_toolbar_new ();
+ x->toolbar_detached = 0;
gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
gtk_toolbar_set_orientation (GTK_TOOLBAR (x->toolbar_widget),
GTK_ORIENTATION_HORIZONTAL);
+}
- g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
- 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);
-
- gtk_widget_show_all (x->handlebox_widget);
-
- gtk_widget_size_request (x->toolbar_widget, &req);
- FRAME_TOOLBAR_HEIGHT (f) = req.height;
-
- /* The height has changed, resize outer widget and set columns
- rows to what we had before adding the tool bar. */
- xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
- SET_FRAME_GARBAGED (f);
-}
+#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
/* Find the right-to-left image named by RTL in the tool bar images for F.
Returns IMAGE if RTL is not found. */
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)))
+ if (!NILP (file = file_for_image (rtl_image)))
{
file = call1 (intern ("file-name-sans-extension"),
Ffile_name_nondirectory (file));
/* Update the tool bar for frame F. Add new buttons and remove old. */
+extern Lisp_Object Qx_gtk_map_stock;
+
void
update_frame_tool_bar (f)
FRAME_PTR f;
GtkToolbar *wtoolbar;
GtkToolItem *ti;
GtkTextDirection dir;
+ int pack_tool_bar = x->handlebox_widget == NULL;
if (! FRAME_GTK_WIDGET (f))
return;
for (i = 0; i < f->n_tool_bar_items; ++i)
{
-
int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
int idx;
int icon_size = 0;
struct image *img = NULL;
Lisp_Object image;
- Lisp_Object stock;
+ Lisp_Object stock = Qnil;
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");
Lisp_Object specified_file;
ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i);
}
specified_file = file_for_image (image);
- if (!NILP (specified_file) && EQ (Qt, Ffboundp (func)))
- stock = call1 (func, specified_file);
+ if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
+ stock = call1 (Qx_gtk_map_stock, specified_file);
- if (! NILP (stock) && STRINGP (stock))
+ if (STRINGP (stock))
{
stock_name = SSDATA (stock);
if (stock_name[0] == 'n' && stock_name[1] == ':')
}
else if (gtk_stock_lookup (SSDATA (stock), &stock_item))
icon_size = gtk_toolbar_get_icon_size (wtoolbar);
- else
+ else
{
stock = Qnil;
stock_name = NULL;
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)
(gpointer) xstrdup (stock_name),
(GDestroyNotify) xfree);
}
- else if (icon_name)
+ 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,
/* The EMACS_INT cast avoids a warning. */
g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
- GTK_SIGNAL_FUNC (xg_tool_bar_menu_proxy),
+ G_CALLBACK (xg_tool_bar_menu_proxy),
(gpointer) (EMACS_INT) i);
g_signal_connect (G_OBJECT (wbutton), "clicked",
- GTK_SIGNAL_FUNC (xg_tool_bar_callback),
+ G_CALLBACK (xg_tool_bar_callback),
(gpointer) (EMACS_INT) i);
gtk_widget_show_all (GTK_WIDGET (ti));
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),
+ G_CALLBACK (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
if (ti) gtk_widget_hide_all (GTK_WIDGET (ti));
} while (ti != NULL);
- gtk_widget_size_request (GTK_WIDGET (wtoolbar), &new_req);
+ new_req.height = 0;
+ if (pack_tool_bar && f->n_tool_bar_items != 0)
+ xg_pack_tool_bar (f);
+
+
+ gtk_widget_size_request (GTK_WIDGET (x->toolbar_widget), &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));
+ xg_height_changed (f);
}
-
UNBLOCK_INPUT;
}
if (x->toolbar_widget)
{
+ int is_packed = x->handlebox_widget != 0;
BLOCK_INPUT;
- gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
- x->handlebox_widget);
+ /* We may have created the toolbar_widget in xg_create_tool_bar, but
+ not the x->handlebox_widget which is created in xg_pack_tool_bar. */
+ if (is_packed)
+ gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
+ x->handlebox_widget);
+ else
+ gtk_widget_destroy (x->toolbar_widget);
+
x->toolbar_widget = 0;
x->handlebox_widget = 0;
FRAME_TOOLBAR_HEIGHT (f) = 0;
+ xg_height_changed (f);
- /* The height has changed, resize outer widget and set columns
- rows to what we had before removing the tool bar. */
- xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
-
- SET_FRAME_GARBAGED (f);
UNBLOCK_INPUT;
}
}
/* 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));
+ binding_set = gtk_binding_set_by_class (g_type_class_ref (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));
+ binding_set = gtk_binding_set_by_class (g_type_class_ref
+ (GTK_TYPE_MENU_SHELL));
gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK,
"cancel", 0);
}