#include "config.h"
#ifdef USE_GTK
+#include <string.h>
+#include <stdio.h>
#include "lisp.h"
#include "xterm.h"
#include "blockinput.h"
#include "atimer.h"
#include "gtkutil.h"
#include "termhooks.h"
-#include <string.h>
-#include <stdio.h>
#include <gdk/gdkkeysyms.h>
#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
(PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
+
+
\f
/***********************************************************************
Utility functions
xg_list_insert (xg_list_node *list, xg_list_node *node)
{
xg_list_node *list_start = list->next;
-
+
if (list_start) list_start->prev = node;
node->next = list_start;
node->prev = 0;
char *str;
{
char *utf8_str = str;
-
+
/* If not UTF-8, try current locale. */
if (str && !g_utf8_validate (str, -1, NULL))
utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
int top = f->output_data.x->top_pos;
int yneg = f->output_data.x->size_hint_flags & YNegative;
char geom_str[32];
-
+
if (xneg)
left = -left;
if (yneg)
}
}
-
+
/* 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. */
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.
int tbheight = FRAME_TOOLBAR_HEIGHT (f);
int rows = PIXEL_TO_CHAR_HEIGHT (f, pixelheight - mbheight - tbheight);
int columns = PIXEL_TO_CHAR_WIDTH (f, pixelwidth);
-
+
if (FRAME_GTK_WIDGET (f)
&& (columns != FRAME_WIDTH (f) || rows != FRAME_HEIGHT (f)
|| pixelwidth != PIXEL_WIDTH (f) || pixelheight != PIXEL_HEIGHT (f)))
all.height = pixelheight - mbheight - tbheight;
gtk_widget_size_allocate (x->edit_widget, &all);
- gdk_window_process_all_updates ();
+
+ xg_frame_cleared (f);
change_frame_size (f, rows, columns, 0, 1, 0);
SET_FRAME_GARBAGED (f);
int pixelheight = CHAR_TO_PIXEL_HEIGHT (f, rows)
+ FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
int pixelwidth = CHAR_TO_PIXEL_WIDTH (f, cols);
-
+
/* 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
: (FRAME_SCROLL_BAR_COLS (f)
* FONT_WIDTH (f->output_data.x->font)));
- x_compute_fringe_widths (f, 0);
+ compute_fringe_widths (f, 0);
/* Must resize our top level widget. Font size may have changed,
but not rows/cols. */
event.any.window = gdkwin;
gwdesc = gtk_get_event_widget (&event);
}
-
+
UNBLOCK_INPUT;
return gwdesc;
}
GtkRcStyle *style;
int i;
char *title = 0;
-
+
BLOCK_INPUT;
wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
wvbox = gtk_vbox_new (FALSE, 0);
wfixed = gtk_fixed_new (); /* Must have this to place scroll bars */
-
+
if (! wtop || ! wvbox || ! wfixed)
{
if (wtop) gtk_widget_destroy (wtop);
else if (! NILP (f->name)) title = SDATA (f->name);
if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
-
+
FRAME_GTK_OUTER_WIDGET (f) = wtop;
FRAME_GTK_WIDGET (f) = wfixed;
f->output_data.x->vbox_widget = wvbox;
gtk_widget_set_size_request (wfixed,
PIXEL_WIDTH (f),
PIXEL_HEIGHT (f));
-
+
gtk_container_add (GTK_CONTAINER (wtop), wvbox);
gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
later on when tool bar items are added. */
if (FRAME_EXTERNAL_TOOL_BAR (f) && FRAME_TOOLBAR_HEIGHT (f) == 0)
FRAME_TOOLBAR_HEIGHT (f) = 34;
-
+
gtk_widget_set_double_buffered (wvbox, FALSE);
gtk_widget_set_double_buffered (wfixed, FALSE);
gtk_widget_set_double_buffered (wtop, 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 is to destroy the widget. We want Emacs to do that instead. */
g_signal_connect (G_OBJECT (wtop), "delete-event",
G_CALLBACK (gtk_true), 0);
-
+
/* Convert our geometry parameters into a geometry string
and specify it.
GTK will itself handle calculating the real position this way. */
/* Must use g_strdup because gtk_widget_modify_style does g_free. */
style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
gtk_widget_modify_style (wfixed, style);
-
+
/* GTK does not set any border, and they look bad with GTK. */
f->output_data.x->border_width = 0;
f->output_data.x->internal_border_width = 0;
int base_width, base_height;
int min_rows = 0, min_cols = 0;
int win_gravity = f->output_data.x->win_gravity;
-
+
if (flags)
{
memset (&size_hints, 0, sizeof (size_hints));
}
else
flags = f->output_data.x->size_hint_flags;
-
+
size_hints = f->output_data.x->size_hints;
hint_flags = f->output_data.x->hint_flags;
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;
get_dialog_title (char key)
{
char *title = "";
-
+
switch (key) {
case 'E': case 'e':
title = "Error";
g_signal_connect (G_OBJECT (wdialog), "delete-event",
G_CALLBACK (dialog_delete_callback), 0);
-
+
if (deactivate_cb)
{
g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
GtkWidget *w;
GtkRequisition req;
- if (strcmp (item->name, "message") == 0)
+ if (item->name && strcmp (item->name, "message") == 0)
{
/* This is the text part of the dialog. */
w = gtk_label_new (utf8_label);
gtk_widget_size_request (w, &req);
gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
req.height);
- if (strlen (item->value) > 0)
+ if (item->value && strlen (item->value) > 0)
button_spacing = 2*req.width/strlen (item->value);
}
else
GtkFileSelection *filesel;
int filesel_done = XG_FILE_NOT_DONE;
char *fn = 0;
-
+
filewin = gtk_file_selection_new (prompt);
filesel = GTK_FILE_SELECTION (filewin);
gtk_file_selection_hide_fileop_buttons (filesel);
}
-
+
gtk_widget_show_all (filewin);
-
+
while (filesel_done == XG_FILE_NOT_DONE)
gtk_main_iteration ();
The menu bar and all sub menus under the menu bar in a frame
share the same structure, hence the reference count.
-
+
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 *
{
xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : client_data;
-
+
if (! NILP (data->help) && data->cl_data->highlight_cb)
{
GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
GtkWidget *wlbl;
GtkWidget *wkey;
GtkWidget *wbox;
-
+
wbox = gtk_hbox_new (FALSE, 0);
wlbl = gtk_label_new_with_mnemonic (utf8_label);
wkey = gtk_label_new (utf8_key);
gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
-
+
gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
UTF8_KEY is the text representing the key binding.
ITEM is the widget_value describing the menu item.
-
+
GROUP is an in/out parameter. If the menu item to be created is not
part of any radio menu group, *GROUP contains NULL on entry and exit.
If the menu item to be created is part of a radio menu group, on entry
{
GtkWidget *w;
GtkWidget *wtoadd = 0;
-
+
if (utf8_key)
wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
-
+
if (item->button_type == BUTTON_TYPE_TOGGLE)
{
*group = NULL;
if (utf8_key) w = gtk_menu_item_new ();
else w = gtk_menu_item_new_with_mnemonic (utf8_label);
}
-
+
if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
static int
xg_separator_p (char *name)
{
+ if (! name) return 0;
+
return strcmp (name, "--") == 0
- || strcmp (name, "--:") == 0
+ || strncmp (name, "--:", 3) == 0
|| strcmp (name, "---") == 0;
}
/* Callback invoked when a menu is detached. It sets the xg_did_tearoff
variable.
WIDGET is the GtkTearoffMenuItem.
- CLIENT_DATA is not used. */
+ CLIENT_DATA is not used. */
static void
tearoff_activate (widget, client_data)
GtkWidget *widget;
/* Find the top widget for the detached menu. */
p = gtk_widget_get_toplevel (submenu);
-
+
/* Delay destroying the menu until the detached menu is removed. */
g_signal_connect (G_OBJECT (p), "unmap_event",
G_CALLBACK (tearoff_remove), menu);
cb_data->help = item->help;
cb_data->cl_data = cl_data;
cb_data->call_data = item->call_data;
-
+
g_signal_connect (G_OBJECT (w),
"destroy",
G_CALLBACK (menuitem_destroy_callback),
return w;
}
+static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
+ GCallback, GCallback, int, int, int,
+ GtkWidget *, xg_menu_cb_data *, char *));
+
/* Create a full menu tree specified by DATA.
F is the frame the created menu belongs to.
SELECT_CB is the callback to use when a menu item is selected.
g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
g_signal_connect (G_OBJECT (wmenu), "destroy",
G_CALLBACK (menu_destroy_callback), cl_data);
-
+
if (name)
gtk_widget_set_name (wmenu, name);
g_signal_connect (G_OBJECT (wmenu),
"grab-notify", G_CALLBACK (menu_grab_callback), 0);
}
-
+
if (! menu_bar_p && add_tearoff_p)
{
GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
for (item = data; item; item = item->next)
{
GtkWidget *w;
-
+
if (pop_up_p && !item->contents && !item->call_data
&& !xg_separator_p (item->name))
{
0);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
}
-
- /* Assume "Help" is the last menu in the menubar. */
- if (menu_bar_p && ! item->next)
- gtk_menu_item_set_right_justified (GTK_MENU_ITEM (w), TRUE);
}
gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
return w;
}
+/* Return the label for menu item WITEM. */
static const char *
xg_get_menu_item_label (witem)
GtkMenuItem *witem;
return gtk_label_get_label (wlabel);
}
+/* Return non-zero if the menu item WITEM has the text LABEL. */
static int
xg_item_label_same_p (witem, label)
GtkMenuItem *witem;
char *label;
{
- int is_same;
+ int is_same = 0;
char *utf8_label = get_utf8_string (label);
-
- is_same = strcmp (utf8_label, xg_get_menu_item_label (witem)) == 0;
- if (utf8_label != label) g_free (utf8_label);
+ const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
+
+ if (! old_label && ! utf8_label)
+ is_same = 1;
+ else if (old_label && utf8_label)
+ is_same = strcmp (utf8_label, old_label) == 0;
+
+ if (utf8_label && utf8_label != label) g_free (utf8_label);
return is_same;
}
GtkWidget *wcont;
GList *list;
{
- /* We must copy list because gtk_container_remove changes it. */
- GList *clist = g_list_copy (list);
GList *iter;
- for (iter = clist; iter; iter = g_list_next (iter))
+ for (iter = list; iter; iter = g_list_next (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
removing the detached window also if there was one. */
gtk_widget_destroy (w);
}
- g_list_free (clist);
}
/* Update the top level names in MENUBAR (i.e. not submenus).
F is the frame the menu bar belongs to.
- LIST is a list with the current menu bar names (menu item widgets).
+ *LIST is a list with the current menu bar names (menu item widgets).
+ ITER is the item within *LIST that shall be updated.
+ POS is the numerical position, starting at 0, of ITER in *LIST.
VAL describes what the menu bar shall look like after the update.
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 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, val, select_cb, highlight_cb, cl_data)
+xg_update_menubar (menubar, f, list, iter, pos, val,
+ select_cb, highlight_cb, cl_data)
GtkWidget *menubar;
FRAME_PTR f;
- GList *list;
+ GList **list;
+ GList *iter;
+ int pos;
widget_value *val;
GCallback select_cb;
GCallback highlight_cb;
xg_menu_cb_data *cl_data;
{
- if (! list && ! val)
+ if (! iter && ! val)
return;
- else if (list && ! val)
+ else if (iter && ! val)
{
- /* Item(s) have been removed. Remove all remaining items from list. */
- remove_from_container (menubar, list);
+ /* Item(s) have been removed. Remove all remaining items. */
+ remove_from_container (menubar, iter);
/* All updated. */
val = 0;
- list = 0;
+ iter = 0;
}
- else if (! list && val)
+ else if (! iter && val)
{
/* Item(s) added. Add all new items in one call. */
create_menus (val, f, select_cb, 0, highlight_cb,
/* All updated. */
val = 0;
- list = 0;
+ iter = 0;
}
- /* Below this neither list or val is NULL */
- else if (xg_item_label_same_p (GTK_MENU_ITEM (list->data), val->name))
+ /* Below this neither iter or val is NULL */
+ else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
{
/* This item is still the same, check next item. */
val = val->next;
- list = g_list_next (list);
+ iter = g_list_next (iter);
+ ++pos;
}
else /* This item is changed. */
{
- GtkMenuItem *witem = GTK_MENU_ITEM (list->data);
+ GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
GtkMenuItem *witem2 = 0;
- int pos = 0;
int val_in_menubar = 0;
- int list_in_new_menubar = 0;
- GList *list2;
- GList *iter;
+ int iter_in_new_menubar = 0;
+ GList *iter2;
widget_value *cur;
-
- /* Get position number for witem. */
- list2 = gtk_container_get_children (GTK_CONTAINER (menubar));
- for (iter = list2; iter; iter = g_list_next (iter))
- {
- if (list->data == iter->data) break;
- ++pos;
- }
-
/* See if the changed entry (val) is present later in the menu bar */
- for (iter = g_list_next (list);
- iter && ! val_in_menubar;
- iter = g_list_next (iter))
+ for (iter2 = iter;
+ iter2 && ! val_in_menubar;
+ iter2 = g_list_next (iter2))
{
- witem2 = GTK_MENU_ITEM (iter->data);
+ witem2 = GTK_MENU_ITEM (iter2->data);
val_in_menubar = xg_item_label_same_p (witem2, val->name);
}
- /* See if the current entry (list) is present later in the
+ /* See if the current entry (iter) is present later in the
specification for the new menu bar. */
- for (cur = val; cur && ! list_in_new_menubar; cur = cur->next)
- list_in_new_menubar = xg_item_label_same_p (witem, cur->name);
+ for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
+ iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
- if (val_in_menubar && ! list_in_new_menubar)
+ if (val_in_menubar && ! iter_in_new_menubar)
{
+ int nr = pos;
+
/* This corresponds to:
Current: A B C
New: A C
Remove B. */
-
+
gtk_widget_ref (GTK_WIDGET (witem));
gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
gtk_widget_destroy (GTK_WIDGET (witem));
/* Must get new list since the old changed. */
- list = gtk_container_get_children (GTK_CONTAINER (menubar));
- while (pos-- > 0) list = g_list_next (list);
+ g_list_free (*list);
+ *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
+ while (nr-- > 0) iter = g_list_next (iter);
}
- else if (! val_in_menubar && ! list_in_new_menubar)
+ else if (! val_in_menubar && ! iter_in_new_menubar)
{
/* This corresponds to:
Current: A B C
is up to date when leaving the minibuffer. */
GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
char *utf8_label = get_utf8_string (val->name);
-
+
gtk_label_set_text_with_mnemonic (wlabel, utf8_label);
- list = g_list_next (list);
+ iter = g_list_next (iter);
val = val->next;
+ ++pos;
}
- else if (! val_in_menubar && list_in_new_menubar)
+ else if (! val_in_menubar && iter_in_new_menubar)
{
/* This corresponds to:
Current: A B C
New: A X B C
Insert X. */
+ int nr = pos;
GList *group = 0;
GtkWidget *w = xg_create_one_menuitem (val,
f,
gtk_widget_set_name (w, MENU_ITEM_NAME);
gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
- list = gtk_container_get_children (GTK_CONTAINER (menubar));
- while (pos-- > 0) list = g_list_next (list);
- list = g_list_next (list);
+ g_list_free (*list);
+ *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
+ while (nr-- > 0) iter = g_list_next (iter);
+ iter = g_list_next (iter);
val = val->next;
+ ++pos;
}
- else /* if (val_in_menubar && list_in_new_menubar) */
+ else /* if (val_in_menubar && iter_in_new_menubar) */
{
+ int nr = pos;
/* This corresponds to:
Current: A B C
New: A C B
GTK_WIDGET (witem2), pos);
gtk_widget_unref (GTK_WIDGET (witem2));
+ g_list_free (*list);
+ *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
+ while (nr-- > 0) iter = g_list_next (iter);
val = val->next;
- list = gtk_container_get_children (GTK_CONTAINER (menubar));
- while (pos-- > 0) list = g_list_next (list);
- list = g_list_next (list);
+ ++pos;
}
-
}
/* Update the rest of the menu bar. */
- xg_update_menubar (menubar, f, list, val, select_cb, highlight_cb, cl_data);
+ xg_update_menubar (menubar, f, list, iter, pos, val,
+ select_cb, highlight_cb, cl_data);
}
/* Update the menu item W so it corresponds to VAL.
GtkLabel *wkey = 0;
char *utf8_label;
char *utf8_key;
+ const char *old_label = 0;
+ const char *old_key = 0;
xg_menu_item_cb_data *cb_data;
-
- wchild = gtk_bin_get_child (GTK_BIN (w));
+
+ wchild = gtk_bin_get_child (GTK_BIN (w));
utf8_label = get_utf8_string (val->name);
utf8_key = get_utf8_string (val->key);
wlbl = GTK_LABEL (list->data);
wkey = GTK_LABEL (list->next->data);
+ g_list_free (list);
+
if (! utf8_key)
{
/* Remove the key and keep just the label. */
gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
wkey = 0;
}
+
}
else /* Just a label. */
{
{
GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
+
wlbl = GTK_LABEL (list->data);
wkey = GTK_LABEL (list->next->data);
+ g_list_free (list);
gtk_container_remove (GTK_CONTAINER (w), wchild);
gtk_container_add (GTK_CONTAINER (w), wtoadd);
}
}
- if (utf8_key && strcmp (utf8_key, gtk_label_get_label (wkey)) != 0)
+
+ if (wkey) old_key = gtk_label_get_label (wkey);
+ if (wlbl) old_label = gtk_label_get_label (wlbl);
+
+ if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
gtk_label_set_text (wkey, utf8_key);
- if (strcmp (utf8_label, gtk_label_get_label (wlbl)) != 0)
+ if (! old_label || strcmp (utf8_label, old_label) != 0)
gtk_label_set_text_with_mnemonic (wlbl, utf8_label);
- if (utf8_key != val->key) g_free (utf8_key);
- if (utf8_label != val->name) g_free (utf8_label);
-
+ if (utf8_key && utf8_key != val->key) g_free (utf8_key);
+ if (utf8_label && utf8_label != val->name) g_free (utf8_label);
+
if (! val->enabled && GTK_WIDGET_SENSITIVE (w))
gtk_widget_set_sensitive (w, FALSE);
else if (val->enabled && ! GTK_WIDGET_SENSITIVE (w))
cb_data->call_data = val->call_data;
cb_data->help = val->help;
cb_data->cl_data = cl_data;
-
+
/* We assume the callback functions don't change. */
if (val->call_data && ! val->contents)
{
widget_value *cur;
int has_tearoff_p = 0;
GList *first_radio = 0;
-
+
if (submenu)
list = gtk_container_get_children (GTK_CONTAINER (submenu));
-
+
for (cur = val, iter = list;
cur && iter;
iter = g_list_next (iter), cur = cur->next)
if (cur && first_radio) remove_from_container (submenu, first_radio);
else remove_from_container (submenu, iter);
}
-
+
if (cur)
{
/* More items added. Create them. */
cl_data,
0);
}
-
+
+ if (list) g_list_free (list);
+
return newsub;
}
{
xg_menu_cb_data *cl_data;
GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
- GList *iter;
if (! list) return;
-
+
cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
XG_FRAME_DATA);
if (! deep_p)
{
widget_value *cur = val->contents;
- xg_update_menubar (menubar, f, list, cur,
+ xg_update_menubar (menubar, f, &list, list, 0, cur,
select_cb, highlight_cb, cl_data);
}
else
for (cur = val->contents; cur; cur = cur->next)
{
+ GList *iter;
GtkWidget *sub = 0;
GtkWidget *newsub;
GtkMenuItem *witem;
break;
}
}
-
+
newsub = xg_update_submenu (sub,
f,
cur->contents,
}
}
+ g_list_free (list);
gtk_widget_show_all (menubar);
}
{
struct x_output *x = f->output_data.x;
GtkRequisition req;
-
+
if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
return 0;
/* 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_WIDTH (f), FRAME_HEIGHT (f));
-
+
SET_FRAME_GARBAGED (f);
UNBLOCK_INPUT;
+
+ return 1;
}
/* Get rid of the menu bar of frame F, and free its storage.
to indicate that callback should do nothing. */
int xg_ignore_gtk_scrollbar;
-/* After we send a scroll bar event, x_set_toolkit_scroll_bar_thumb will
- be called. For some reason that needs to be debugged, it gets called
- with bad values. Thus, we set this variable to ignore those calls. */
-int xg_ignore_next_thumb;
-
/* 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
GtkWidget **widgets;
int max_size;
int used;
-} id_to_widget = { 0, 0, 0 };
+} id_to_widget;
/* Grow this much every time we need to allocate more */
#define ID_TO_WIDGET_INCR 32
return 0;
}
+/* Return the scrollbar id for X Window WID.
+ Return -1 if WID not in id_to_widget. */
+int
+xg_get_scroll_id_for_window (wid)
+ Window wid;
+{
+ int idx;
+ GtkWidget *w;
+
+ w = xg_win_to_widget (wid);
+
+ if (w)
+ {
+ for (idx = 0; idx < id_to_widget.max_size; ++idx)
+ if (id_to_widget.widgets[idx] == w)
+ return idx;
+ }
+
+ return -1;
+}
+
/* 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 value here and remove the index. */
+ We free pointer to last scroll bar values here and remove the index. */
static void
xg_gtk_scroll_destroy (widget, data)
GtkWidget *widget;
{
gpointer p;
int id = (int)data;
-
+
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 is 0 (not used).
+ USER_DATA points to the struct scrollbar structure.
Returns FALSE to tell GTK that it shall continue propagate the event
to widgets. */
{
if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
xg_start_timer ();
- else if (event->type == GDK_BUTTON_RELEASE && xg_timer)
- xg_stop_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;
}
GtkWidget *wscroll;
GtkObject *vadj;
int scroll_id;
-
+
/* Page, step increment values are not so important here, they
will be corrected in x_set_toolkit_scroll_bar_thumb. */
vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
gtk_widget_set_name (wscroll, scroll_bar_name);
gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
-
+
scroll_id = xg_store_widget_in_map (wscroll);
-
+
g_signal_connect (G_OBJECT (vadj),
"value-changed",
scroll_callback,
g_signal_connect (G_OBJECT (wscroll),
"button-press-event",
G_CALLBACK (scroll_bar_button_cb),
- (gpointer)1);
+ (gpointer)bar);
g_signal_connect (G_OBJECT (wscroll),
"button-release-event",
G_CALLBACK (scroll_bar_button_cb),
- 0);
-
+ (gpointer)bar);
+
gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget),
- wscroll, 0, 0);
+ wscroll, -1, -1);
/* Set the cursor to an arrow. */
xg_set_cursor (wscroll, &xg_left_ptr_cursor);
}
}
+/* 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)
+xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
FRAME_PTR f;
int scrollbar_id;
int top;
int width;
int height;
{
- GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
-
- if (wscroll)
- {
- int gheight = max (height, 1);
- gtk_fixed_move (GTK_FIXED (f->output_data.x->edit_widget),
- wscroll, left, top);
+ GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
+
+ if (wscroll)
+ {
+ GtkWidget *wfixed = f->output_data.x->edit_widget;
+ int gheight = max (height, 1);
+ int canon_width = FRAME_SCROLL_BAR_COLS (f) * CANON_X_UNIT (f);
+ int winextra = canon_width > width ? (canon_width - width) / 2 : 0;
+ int bottom = top + gheight;
+
+ 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 scroll bar canonical width may be wider than the width
+ passed in here. */
+
+ if (oldtop != -1 && oldleft != -1)
+ {
+ int gtkextra;
+ int xl, xr, wblank;
+ int bottomdiff, topdiff;
+
+ gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL);
+ gtkextra = width > slider_width ? (width - slider_width) / 2 : 0;
+
+ xl = left - winextra;
+ wblank = gtkextra + winextra;
+ xr = left + gtkextra + slider_width;
+ bottomdiff = abs (oldbottom - bottom);
+ topdiff = abs (oldtop - top);
+
+ if (oldtop > top)
+ {
+ gdk_window_clear_area (wfixed->window, xl, top, wblank, topdiff);
+ gdk_window_clear_area (wfixed->window, xr, top, wblank, topdiff);
+ }
+ else if (oldtop < top)
+ {
+ gdk_window_clear_area (wfixed->window, xl, oldtop, wblank,
+ topdiff);
+ gdk_window_clear_area (wfixed->window, xr, oldtop, wblank,
+ topdiff);
+ }
- gtk_widget_set_size_request (wscroll, width, gheight);
+ if (oldbottom > bottom)
+ {
+ gdk_window_clear_area (wfixed->window, xl, bottom, wblank,
+ bottomdiff);
+ gdk_window_clear_area (wfixed->window, xr, bottom, wblank,
+ bottomdiff);
+ }
+ else if (oldbottom < bottom)
+ {
+ gdk_window_clear_area (wfixed->window, xl, oldbottom, wblank,
+ bottomdiff);
+ gdk_window_clear_area (wfixed->window, xr, oldbottom, wblank,
+ bottomdiff);
+ }
+ }
- /* Must force out update so wscroll gets the resize.
- Otherwise, the gdk_window_clear clears the old window size. */
- gdk_window_process_all_updates ();
+ /* Move and resize to new values. */
+ gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top);
+ gtk_widget_set_size_request (wscroll, width, gheight);
- /* The scroll bar doesn't explicitly redraw the whole window
- when a resize occurs. Since the scroll bar seems to be fixed
- in width it doesn't fill the space reserved, so we must clear
- the whole window. */
- gdk_window_clear (wscroll->window);
+ gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE);
- /* Since we are not using a pure gtk event loop, we must force out
- pending update events with this call. */
- gdk_window_process_all_updates ();
+ /* Make GTK draw the new sizes. We are not using a pure GTK event
+ loop so we need to do this. */
+ gdk_window_process_all_updates ();
- SET_FRAME_GARBAGED (f);
- cancel_mouse_face (f);
- }
+ SET_FRAME_GARBAGED (f);
+ cancel_mouse_face (f);
+ }
}
/* Set the thumb size and position of scroll bar BAR. We are currently
FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
BLOCK_INPUT;
- if (wscroll && ! xg_ignore_next_thumb)
+
+ if (wscroll && NILP (bar->dragging))
{
GtkAdjustment *adj;
gdouble shown;
gdouble top;
int size, value;
-
+ int new_upper, new_step;
+
adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
+ /* We do the same as for MOTIF in xterm.c, assume 30 chars per line
+ rather than the real portion value. This makes the thumb less likely
+ to resize and that looks better. */
+ portion = XFASTINT (XWINDOW (bar->window)->height) * 30;
+ /* When the thumb is at the bottom, position == whole.
+ So we need to increase `whole' to make space for the thumb. */
+ whole += portion;
+
if (whole <= 0)
top = 0, shown = 1;
else
value = min (value, whole - size);
value = max (value, XG_SB_MIN);
- adj->upper = max (whole, size);
- adj->page_size = (int)size;
-
- /* Assume a page increment is about 95% of the page size */
- adj->page_increment = (int) (0.95*adj->page_size);
-
- /* Assume all lines are equal. */
- adj->step_increment = portion / max (1, FRAME_HEIGHT (f));
-
/* gtk_range_set_value invokes the callback. Set
ignore_gtk_scrollbar to make the callback do nothing */
xg_ignore_gtk_scrollbar = 1;
- gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
+
+ new_upper = max (whole, size);
+ new_step = portion / max (1, FRAME_HEIGHT (f));
+
+ if ((int) adj->page_size != size
+ || (int) adj->upper != new_upper
+ || (int) adj->step_increment != new_step)
+ {
+ adj->page_size = (int) size;
+
+ gtk_range_set_range (GTK_RANGE (wscroll), adj->lower,
+ (gdouble) new_upper);
+
+ /* Assume all lines are of equal size. */
+ /* Assume a page increment is about 95% of the page size */
+ gtk_range_set_increments (GTK_RANGE (wscroll),
+ portion / max (1, FRAME_HEIGHT (f)),
+ (int) (0.95*adj->page_size));
+
+ }
+
+ if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
+ gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
+
xg_ignore_gtk_scrollbar = 0;
+
+ /* Make GTK draw the new thumb. We are not using a pure GTK event
+ loop so we need to do this. */
+ gdk_window_process_all_updates ();
}
- /* Make sure the scroll bar is redrawn with new thumb */
- gtk_widget_queue_draw (wscroll);
- gdk_window_process_all_updates ();
- xg_ignore_next_thumb = 0;
UNBLOCK_INPUT;
}
return;
idx *= TOOL_BAR_ITEM_NSLOTS;
-
+
key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
XSETFRAME (frame, f);
event.kind = TOOL_BAR_EVENT;
}
if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
- return;
+ return FALSE;
if (event->type == GDK_ENTER_NOTIFY)
{
}
+/* This callback is called when a tool bar item shall be redrawn.
+ It modifies the expose event so that the GtkImage widget redraws the
+ whole image. This to overcome a bug that makes GtkImage draw the image
+ in the wrong place when it tries to redraw just a part of the image.
+ W is the GtkImage to be redrawn.
+ EVENT is the expose event for W.
+ 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;
+ GdkEventExpose *event;
+ gpointer client_data;
+{
+ event->area.x = event->area.y = 0;
+ event->area.width = event->area.height = 1000;
+ return FALSE;
+}
+
+/* This callback is called when a tool bar shall be redrawn.
+ We need to update the tool bar from here in case the image cache
+ has deleted the pixmaps used in the tool bar.
+ W is the GtkToolbar to be redrawn.
+ EVENT is the expose event for W.
+ CLIENT_DATA is pointing to the frame for this tool bar.
+
+ Returns FALSE to tell GTK to keep processing this event. */
+static gboolean
+xg_tool_bar_expose_callback (w, event, client_data)
+ GtkWidget *w;
+ GdkEventExpose *event;
+ gpointer client_data;
+{
+ update_frame_tool_bar((FRAME_PTR)client_data);
+ return FALSE;
+}
+
static void
xg_create_tool_bar (f)
FRAME_PTR f;
x->handlebox_widget = gtk_handle_box_new ();
gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
x->toolbar_widget);
-
+
gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget,
FALSE, FALSE, 0);
-
+
gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget,
vbox_pos);
+ gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
+
+ /* We only have icons, so override any user setting. We could
+ use the caption property of the toolbar item (see update_frame_tool_bar
+ below), but some of those strings are long, making the toolbar so
+ long it does not fit on the screen. The GtkToolbar widget makes every
+ item equal size, so the longest caption determine the size of every
+ tool bar item. I think the creators of the GtkToolbar widget
+ counted on 4 or 5 character long strings. */
+ 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);
+ g_signal_connect (G_OBJECT (x->toolbar_widget),
+ "expose-event",
+ G_CALLBACK (xg_tool_bar_expose_callback),
+ f);
gtk_widget_show_all (x->handlebox_widget);
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_WIDTH (f), FRAME_HEIGHT (f));
-
+
SET_FRAME_GARBAGED (f);
}
int i;
GtkRequisition old_req, new_req;
GList *icon_list;
+ GList *iter;
struct x_output *x = f->output_data.x;
if (! FRAME_GTK_WIDGET (f))
return;
BLOCK_INPUT;
-
+
if (! x->toolbar_widget)
xg_create_tool_bar (f);
gtk_widget_size_request (x->toolbar_widget, &old_req);
icon_list = gtk_container_get_children (GTK_CONTAINER (x->toolbar_widget));
+ iter = icon_list;
for (i = 0; i < f->n_tool_bar_items; ++i)
{
int img_id;
struct image *img;
Lisp_Object image;
- GtkWidget *wicon = icon_list ? GTK_WIDGET (icon_list->data) : 0;
+ GtkWidget *wicon = iter ? GTK_WIDGET (iter->data) : 0;
- if (icon_list) icon_list = g_list_next (icon_list);
+ if (iter) iter = g_list_next (iter);
/* If image is a vector, choose the image according to the
button state. */
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 (wicon) gtk_widget_hide (wicon);
+ continue;
+ }
if (! wicon)
{
w,
GTK_SIGNAL_FUNC (xg_tool_bar_callback),
(gpointer)i);
-
+
/* Save the image so we can see if an update is needed when
this function is called again. */
g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
- (gpointer)img);
+ (gpointer)img->pixmap);
+
+ /* Catch expose events to overcome an annoying redraw bug, see
+ comment for xg_tool_bar_item_expose_callback. */
+ g_signal_connect (G_OBJECT (w),
+ "expose-event",
+ G_CALLBACK (xg_tool_bar_item_expose_callback),
+ 0);
/* We must set sensitive on the button that is the parent
of the GtkImage parent. Go upwards until we find the button. */
while (! GTK_IS_BUTTON (w))
w = gtk_widget_get_parent (w);
-
+
if (w)
{
/* Save the frame in the button so the xg_tool_bar_callback
/* The child of the tool bar is a button. Inside that button
is a vbox. Inside that vbox is the GtkImage. */
GtkWidget *wvbox = gtk_bin_get_child (GTK_BIN (wicon));
- GList *ch = gtk_container_get_children (GTK_CONTAINER (wvbox));
- GtkImage *wimage = GTK_IMAGE (ch->data);
- struct image *old_img = g_object_get_data (G_OBJECT (wimage),
- XG_TOOL_BAR_IMAGE_DATA);
-
- if (! old_img
- || old_img->pixmap != img->pixmap
- || old_img->mask != img->mask)
+ GList *chlist = gtk_container_get_children (GTK_CONTAINER (wvbox));
+ GtkImage *wimage = GTK_IMAGE (chlist->data);
+ Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage),
+ XG_TOOL_BAR_IMAGE_DATA);
+ g_list_free (chlist);
+
+ if (old_img != img->pixmap)
{
GdkPixmap *gpix = gdk_pixmap_foreign_new (img->pixmap);
GdkBitmap *gmask = img->mask ?
}
g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
- (gpointer)img);
+ (gpointer)img->pixmap);
gtk_widget_set_sensitive (wicon, enabled_p);
gtk_widget_show (wicon);
}
-
+
#undef PROP
}
/* Remove buttons not longer needed. We just hide them so they
can be reused later on. */
- while (icon_list)
+ while (iter)
{
- GtkWidget *w = GTK_WIDGET (icon_list->data);
+ GtkWidget *w = GTK_WIDGET (iter->data);
gtk_widget_hide (w);
- icon_list = g_list_next (icon_list);
+ iter = g_list_next (iter);
}
gtk_widget_size_request (x->toolbar_widget, &new_req);
/* Must force out update so changed images gets redrawn. */
gdk_window_process_all_updates ();
+ if (icon_list) g_list_free (icon_list);
+
UNBLOCK_INPUT;
}
if (x->toolbar_widget)
{
- fprintf(stderr, "Remove toolbar\n");
BLOCK_INPUT;
gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
x->handlebox_widget);
xg_initialize ()
{
xg_ignore_gtk_scrollbar = 0;
- xg_ignore_next_thumb = 0;
xg_left_ptr_cursor = 0;
xg_did_tearoff = 0;
xg_menu_cb_list.prev = xg_menu_cb_list.next =
xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
+ id_to_widget.max_size = id_to_widget.used = 0;
+ id_to_widget.widgets = 0;
+
/* Remove F10 as a menu accelerator, it does not mix well with Emacs key
bindings. It doesn't seem to be any way to remove properties,
so we set it to VoidSymbol which in X means "no key". */
"gtk-menu-bar-accel",
"VoidSymbol",
EMACS_CLASS);
+
+ /* Make GTK text input widgets use Emacs style keybindings. This is
+ Emacs after all. */
+ gtk_settings_set_string_property (gtk_settings_get_default (),
+ "gtk-key-theme-name",
+ "Emacs",
+ EMACS_CLASS);
}
#endif /* USE_GTK */