/* Functions for creating and updating GTK widgets.
- Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
+ 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"
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. */
-
-static void
-xg_resize_outer_widget (f, columns, rows)
- 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 ();
-}
-
-/* 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 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
if (FRAME_GTK_WIDGET (f)
|| pixelwidth != FRAME_PIXEL_WIDTH (f)
|| pixelheight != FRAME_PIXEL_HEIGHT (f)))
{
- struct x_output *x = f->output_data.x;
- GtkAllocation all;
-
- all.y = mbheight + tbheight;
- all.x = 0;
+ FRAME_PIXEL_WIDTH (f) = pixelwidth;
+ FRAME_PIXEL_HEIGHT (f) = pixelheight;
- all.width = pixelwidth;
- all.height = pixelheight - mbheight - tbheight;
+ if (rows != FRAME_LINES (f) || columns != FRAME_COLS (f)
+ || (f->new_text_lines != 0 && f->new_text_lines != rows)
+ || (f->new_text_cols != 0 && f->new_text_cols != columns))
+ {
+ change_frame_size (f, rows, columns, 0, 1, 0);
+ SET_FRAME_GARBAGED (f);
+ cancel_mouse_face (f);
+ }
+ }
+}
- gtk_widget_size_allocate (x->edit_widget, &all);
+/* Process all pending events on the display for frame F. */
- change_frame_size (f, rows, columns, 0, 1, 0);
- SET_FRAME_GARBAGED (f);
- cancel_mouse_face (f);
+static void
+flush_and_sync (f)
+ FRAME_PTR f;
+{
+ gdk_window_process_all_updates ();
+ x_sync (f);
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ gdk_window_process_all_updates ();
+ x_sync (f);
}
}
+/* Turn wm hints for resize off on frame F */
-/* Update our widget size to be COLS/ROWS characters for frame F. */
+static void
+x_wm_size_hint_off (f)
+ FRAME_PTR f;
+{
+ GdkGeometry size_hints;
+ gint hint_flags = 0;
+ memset (&size_hints, 0, sizeof (size_hints));
+ hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
+ size_hints.width_inc = 1;
+ size_hints.height_inc = 1;
+ hint_flags |= GDK_HINT_BASE_SIZE;
+ size_hints.base_width = 1;
+ size_hints.base_height = 1;
+ size_hints.min_width = 1;
+ size_hints.min_height = 1;
+ gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ NULL,
+ &size_hints,
+ hint_flags);
+ /* Make sure these get set again in next call to x_wm_set_size_hint. */
+ f->output_data.x->hint_flags &= ~hint_flags;
+ flush_and_sync (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. */
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
pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
/* Must resize our top level widget. Font size may have changed,
- but not rows/cols. */
+ but not rows/cols.
+ Turn wm hints (min/max size and size increments) of temporarly.
+ It interferes too much, when for example adding or removing the
+ menu/tool bar. */
+ x_wm_size_hint_off (f);
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
pixelwidth, pixelheight);
- xg_resize_widgets (f, pixelwidth, pixelheight);
+ flush_and_sync (f);
x_wm_set_size_hint (f, 0, 0);
- SET_FRAME_GARBAGED (f);
- cancel_mouse_face (f);
}
/* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
BLOCK_INPUT;
- wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ if (FRAME_X_EMBEDDED_P (f))
+ wtop = gtk_plug_new (f->output_data.x->parent_desc);
+ else
+ wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
xg_set_screen (wtop, f);
wvbox = gtk_vbox_new (FALSE, 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
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;
hint_flags |= GDK_HINT_USER_POS;
}
- BLOCK_INPUT;
+ 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)),
- FRAME_GTK_OUTER_WIDGET (f),
- &size_hints,
- hint_flags);
+ 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;
+ f->output_data.x->size_hints = size_hints;
+ f->output_data.x->hint_flags = hint_flags;
+ UNBLOCK_INPUT;
+ }
}
}
}
-/* Function that is called when the file dialog pops down.
+/* 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 (pointer to int). */
static void
-xg_file_response_cb (w,
+xg_dialog_response_cb (w,
response,
user_data)
GtkDialog *w;
/* Destroy the dialog. This makes it pop down. */
static Lisp_Object
-pop_down_file_dialog (arg)
+pop_down_dialog (arg)
Lisp_Object arg;
{
struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
g_signal_connect (G_OBJECT (w),
"response",
- G_CALLBACK (xg_file_response_cb),
+ G_CALLBACK (xg_dialog_response_cb),
&filesel_done);
/* Don't destroy the widget if closed by the window manager close button. */
gtk_widget_show (w);
- record_unwind_protect (pop_down_file_dialog, make_save_value (w, 0));
+ record_unwind_protect (pop_down_dialog, make_save_value (w, 0));
while (! filesel_done)
{
x_menu_wait_for_event (0);
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 = 0;
+ int count = SPECPDL_INDEX ();
+ 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)
+ gtk_font_selection_dialog_set_font_name (w, default_name);
+
+ xg_set_screen (w, f);
+ gtk_widget_set_name (w, "emacs-fontdialog");
+ 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_dialog_response_cb), &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_dialog, make_save_value (w, 0));
+ while (!done)
+ {
+ x_menu_wait_for_event (0);
+ gtk_main_iteration ();
+ }
+
+#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
+ ((GtkFontSelectionDialog *) w);
+
+ unbind_to (count, Qnil);
+
+ return fontname;
+}
+#endif /* HAVE_FREETYPE */
+
+
\f
/***********************************************************************
Menu functions.
/* 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));
+ xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
SET_FRAME_GARBAGED (f);
UNBLOCK_INPUT;
/* 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));
+ xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
SET_FRAME_GARBAGED (f);
UNBLOCK_INPUT;
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. */
+/* Xlib's `Window' 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
{
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);
+ xfree (p);
xg_remove_widget_from_map (id);
}
/* Set the cursor to an arrow. */
xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
- SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
+ bar->x_window = scroll_id;
}
/* Make the scroll bar represented by SCROLLBAR_ID visible. */
struct scroll_bar *bar;
int portion, position, whole;
{
- GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar));
+ GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
GtkImage *wimage = GTK_IMAGE (gtk_bin_get_child (GTK_BIN (wbutton)));
GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton));
GtkImageType store_type = gtk_image_get_storage_type (wimage);
+
if (store_type == GTK_IMAGE_STOCK)
{
gchar *stock_id;
wmenuimage = gtk_image_new_from_pixbuf (dest_pixbuf);
}
+ else
+ {
+ fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
+ abort ();
+ }
+ }
+ else if (store_type == GTK_IMAGE_ICON_NAME)
+ {
+ const gchar *icon_name;
+ GtkIconSize icon_size;
+
+ gtk_image_get_icon_name (wimage, &icon_name, &icon_size);
+ wmenuimage = gtk_image_new_from_icon_name (icon_name,
+ GTK_ICON_SIZE_MENU);
+ }
+ else
+ {
+ fprintf (stderr, "internal error: store_type is %d\n", store_type);
+ abort ();
}
}
if (wmenuimage)
/* 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));
+ xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
}
}
/* 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_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (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;
+ GtkRequisition req;
+
+ 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. */
GtkToolbar *wtoolbar;
GtkToolItem *ti;
GtkTextDirection dir;
+ int pack_tool_bar = x->handlebox_widget == NULL;
if (! FRAME_GTK_WIDGET (f))
return;
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);
continue;
}
- if (EQ (Qt, Ffboundp (func)))
- stock = call1 (func, file_for_image (image));
+ specified_file = file_for_image (image);
+ if (!NILP (specified_file) && EQ (Qt, Ffboundp (func)))
+ stock = call1 (func, specified_file);
if (! NILP (stock) && STRINGP (stock))
{
if (ti) gtk_widget_hide_all (GTK_WIDGET (ti));
} while (ti != NULL);
+ new_req.height = 0;
gtk_widget_size_request (GTK_WIDGET (wtoolbar), &new_req);
- if (old_req.height != new_req.height
+
+ if (pack_tool_bar && f->n_tool_bar_items != 0)
+ xg_pack_tool_bar (f);
+
+ if (new_req.height != 0
+ && f->n_tool_bar_items != 0
+ && 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_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (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;
/* 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);
+ xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
UNBLOCK_INPUT;
}
}