From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: "Jan D." Newsgroups: gmane.emacs.devel Subject: GTK patches part 2 Date: Sun, 8 Dec 2002 23:33:53 +0100 (CET) Sender: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Message-ID: <200212082229.gB8MT416004254@stubby.bodenonline.com> NNTP-Posting-Host: main.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary=ELM737119599-199-0_ Content-Transfer-Encoding: 8bit X-Trace: main.gmane.org 1039387057 25264 80.91.224.249 (8 Dec 2002 22:37:37 GMT) X-Complaints-To: usenet@main.gmane.org NNTP-Posting-Date: Sun, 8 Dec 2002 22:37:37 +0000 (UTC) Return-path: Original-Received: from quimby.gnus.org ([80.91.224.244]) by main.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 18LA3G-0006Yt-00 for ; Sun, 08 Dec 2002 23:37:30 +0100 Original-Received: from monty-python.gnu.org ([199.232.76.173]) by quimby.gnus.org with esmtp (Exim 3.12 #1 (Debian)) id 18LAD2-000542-00 for ; Sun, 08 Dec 2002 23:47:36 +0100 Original-Received: from localhost ([127.0.0.1] helo=monty-python.gnu.org) by monty-python.gnu.org with esmtp (Exim 4.10.13) id 18LA1G-0005EB-00 for emacs-devel@quimby.gnus.org; Sun, 08 Dec 2002 17:35:26 -0500 Original-Received: from list by monty-python.gnu.org with tmda-scanned (Exim 4.10.13) id 18LA0K-0005Bx-00 for emacs-devel@gnu.org; Sun, 08 Dec 2002 17:34:28 -0500 Original-Received: from mail by monty-python.gnu.org with spam-scanned (Exim 4.10.13) id 18LA0D-00059u-00 for emacs-devel@gnu.org; Sun, 08 Dec 2002 17:34:26 -0500 Original-Received: from stubby.bodenonline.com ([193.201.16.94]) by monty-python.gnu.org with esmtp (Exim 4.10.13) id 18LA0B-00058y-00 for emacs-devel@gnu.org; Sun, 08 Dec 2002 17:34:19 -0500 Original-Received: from accessno42.bodenonline.com (IDENT:root@accessno42.bodenonline.com [193.201.16.44])gB8MT416004254 for ; Sun, 8 Dec 2002 23:29:04 +0100 Original-To: emacs-devel@gnu.org X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1b5 Precedence: list List-Id: Emacs development discussions. List-Help: List-Post: List-Subscribe: , List-Archive: List-Unsubscribe: , Errors-To: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Xref: main.gmane.org gmane.emacs.devel:9978 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:9978 --ELM737119599-199-0_ Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII Hello. These are the new files in the GTK port. Most of the GTK specific code is here. Jan D. --ELM737119599-199-0_ Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=ISO-8859-1 Content-Disposition: attachment; filename=gtkutil.h /* Definitions and headers for GTK widgets. Copyright (C) 2002 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef GTKUTIL_H #define GTKUTIL_H #ifdef USE_GTK #include #include "frame.h" /* Minimum and maximum values used for GTK scroll bars */ #define GTK_SB_MIN 0 #define GTK_SB_MAX 10000000 #define GTK_SB_RANGE (GTK_SB_MAX - GTK_SB_MIN) /* Key for data that is the frame pointer */ #define G_FRAME_DATA "emacs_frame" /* Key for data that is the last scrollbar value */ #define G_LAST_SB_DATA "emacs_last_sb_value" /* Button types in menus. */ enum button_type { BUTTON_TYPE_NONE, BUTTON_TYPE_TOGGLE, BUTTON_TYPE_RADIO }; /* This structure is the callback data for menus. We need to keep it separate from the frame structure due to detachable menus. The data in the frame structure is only valid while the menu is popped up. */ struct menu_gtk_data { FRAME_PTR f; Lisp_Object menu_bar_vector; int menu_bar_items_used; int ref_count; struct menu_gtk_data *next; struct menu_gtk_data *prev; }; /* Used to specify menus and dialogs. This is an adaption from lwlib for Gtk so we can use more of the same code as lwlib in xmenu.c. */ typedef struct _widget_value { /* name of widget */ char* name; /* value (meaning depend on widget type) */ char* value; /* keyboard equivalent. no implications for XtTranslations */ char* key; /* Help string or nil if none. GC finds this string through the frame's menu_bar_vector or through menu_items. */ Lisp_Object help; /* true if enabled */ gint enabled; /* true if selected */ gint selected; /* The type of a button. */ enum button_type button_type; /* Contents of the sub-widgets, also selected slot for checkbox */ struct _widget_value* contents; /* data passed to callback */ gpointer call_data; /* next one in the list */ struct _widget_value* next; /* we resource the widget_value structures; this points to the next one on the free list if this one has been deallocated. */ struct _widget_value *free_list; } widget_value; extern widget_value *malloc_widget_value (); extern void free_widget_value __P ((widget_value *)); extern GtkWidget *xg_create_widget __P ((char* type, char* name, FRAME_PTR f, widget_value* val, int pop_up_p, GCallback activate_cb, GCallback select_cb, GCallback deactivate_cb, GCallback help_cb)); extern void xg_modify_menubar_widgets __P ((GtkWidget *menubar, FRAME_PTR f, widget_value* val, int deep_p, GCallback activate_cb, GCallback select_cb, GCallback deactivate_cb, GCallback help_cb)); extern int xg_update_frame_menubar __P ((FRAME_PTR f)); extern void xg_free_frame_menubar __P ((FRAME_PTR f)); extern void xg_resize_widgets __P ((FRAME_PTR f, int pixelwidth, int pixelheight)); extern void xg_frame_set_char_size __P ((FRAME_PTR f, int cols, int rows)); extern GtkWidget* xg_win_to_widget __P ((Window)); extern int xg_create_frame_widgets __P ((FRAME_PTR f)); extern void xg_set_wm_size_hints __P ((FRAME_PTR f, long flags, int user_position)); extern void xg_create_scroll_bar __P ((FRAME_PTR f, struct scroll_bar *bar, GCallback scroll_callback, char *scroll_bar_name)); extern void xg_show_scroll_bar __P ((int scrollbar_id)); extern void xg_remove_scroll_bar __P ((FRAME_PTR f, int scrollbar_id)); extern void xg_update_scrollbar_pos __P ((FRAME_PTR f, int scrollbar_id, int top, int left, int width, int height)); extern void xg_set_toolkit_scroll_bar_thumb __P ((struct scroll_bar *bar, int portion, int position, int whole)); /* Set this to non-zero to skip Emacs event processing. For dialogs and popups. */ extern int xg_pass_through_events; /* Setting scrollbar values invokes the callback. Use this variable to indicate that the callback should do nothing. */ extern 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. */ extern int xg_ignore_next_thumb; /* Mark all callback data that are Lisp_object:s during GC. */ extern void xg_mark_data (); #endif /* USE_GTK */ #endif /* GTKUTIL_H */ --ELM737119599-199-0_ Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=ISO-8859-1 Content-Disposition: attachment; filename=gtkutil.c /* Functions for creating and updating GTK widgets. Copyright (C) 2002 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #ifdef USE_GTK #include "lisp.h" #include "xterm.h" #include "blockinput.h" #include "gtkutil.h" #include /*********************************************************************** Menus and dialog functions. ***********************************************************************/ /* Linked list of all allocated struct menu_gtk_data. Used for marking during GC. */ static struct menu_gtk_data *menu_gtk_data_list = 0; /* The next two variables and functions are taken from lwlib. */ static widget_value *widget_value_free_list = 0; static int malloc_cpt = 0; widget_value * malloc_widget_value () { widget_value *wv; if (widget_value_free_list) { wv = widget_value_free_list; widget_value_free_list = wv->free_list; wv->free_list = 0; } else { wv = (widget_value *) malloc (sizeof (widget_value)); malloc_cpt++; } memset (wv, 0, sizeof (widget_value)); return wv; } /* this is analogous to free(). It frees only what was allocated by malloc_widget_value(), and no substructures. */ void free_widget_value (wv) widget_value *wv; { if (wv->free_list) abort (); if (malloc_cpt > 25) { /* When the number of already allocated cells is too big, We free it. */ free (wv); malloc_cpt--; } else { wv->free_list = widget_value_free_list; widget_value_free_list = wv; } } /* Allocate and return a utf8 version of STR. If STR is already utf8 or NULL, just return STR. If not, a new string is allocated and the caller must free the result with g_free(). */ static char* get_utf8_string (str) char *str; { 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); return utf8_str; } /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count */ struct menu_gtk_data* make_cl_data (cl_data, f) struct menu_gtk_data *cl_data; FRAME_PTR f; { if (! cl_data) { cl_data = (struct menu_gtk_data*) xmalloc (sizeof (*cl_data)); cl_data->f = f; cl_data->menu_bar_vector = f->menu_bar_vector; cl_data->menu_bar_items_used = f->menu_bar_items_used; cl_data->ref_count = 0; cl_data->next = menu_gtk_data_list; if (menu_gtk_data_list) menu_gtk_data_list->prev = cl_data; cl_data->prev = 0; menu_gtk_data_list = cl_data; } cl_data->ref_count++; return cl_data; } /* Function that marks all lisp data during GC. */ void xg_mark_data () { struct menu_gtk_data *iter = menu_gtk_data_list; while (iter) { mark_object (&iter->menu_bar_vector); iter = iter->next; } } /* Callback called when a menu item is destroyed. Used to free data. */ static void menuitem_destroy_callback (w, client_data) GtkWidget *w; gpointer client_data; { struct menu_gtk_data *data = (struct menu_gtk_data*) client_data; if (data && data->ref_count > 0) { data->ref_count--; if (data->ref_count == 0) { if (data == menu_gtk_data_list) { menu_gtk_data_list = data->next; if (menu_gtk_data_list) menu_gtk_data_list->prev = 0; } else { data->prev->next = data->next; if (data->next) data->next->prev = data->prev; } xfree (data); } } } /* Make and return a menu item with the key to the right. Unfortunately, keys don't line up as nicely as in Motif, but the MacOS X version doesn't either, so I guess that is OK. */ static GtkWidget* make_menu_item (utf8_label, utf8_key, item, group) char *utf8_label; char *utf8_key; widget_value* item; GSList **group; { GtkWidget *w; GtkWidget *wtoadd = 0; if (utf8_key) { GtkWidget *wlbl; GtkWidget *wkey; wtoadd = 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 (wtoadd), wlbl, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (wtoadd), wkey, FALSE, FALSE, 0); gtk_widget_show (wlbl); gtk_widget_show (wkey); gtk_widget_show (wtoadd); } if (item->button_type == BUTTON_TYPE_TOGGLE) { *group = NULL; if (utf8_key) w = gtk_check_menu_item_new (); else w = gtk_check_menu_item_new_with_mnemonic (utf8_label); gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected); } else if (item->button_type == BUTTON_TYPE_RADIO) { if (utf8_key) w = gtk_radio_menu_item_new (*group); else w = gtk_radio_menu_item_new_with_mnemonic (*group, utf8_label); *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w)); if (item->selected) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE); } else { *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); return w; } /* Return non-zero if NAME specifies a separator (GTK only has one separator type) */ static int xg_separator_p (char *name) { return strcmp (name, "--") == 0 || strcmp (name, "--:") == 0 || strcmp (name, "---") == 0; } /* Create a full menu tree specified by DATA. */ static GtkWidget* createMenus (data, f, activate_cb, select_cb, deactivate_cb, help_cb, isPopup, isMenubar, topmenu, cl_data) widget_value* data; FRAME_PTR f; GCallback activate_cb; GCallback select_cb; GCallback deactivate_cb; int isMenubar; GtkWidget *topmenu; struct menu_gtk_data *cl_data; { widget_value* item; GtkWidget* wmenu = topmenu; GSList* group = NULL; if (! topmenu) { if (! isMenubar) wmenu = gtk_menu_new (); else wmenu = gtk_menu_bar_new (); if (deactivate_cb) g_signal_connect(wmenu, "deactivate", deactivate_cb, 0); } if (! isMenubar) { GtkWidget *tearoff = gtk_tearoff_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff); gtk_widget_show (tearoff); } for (item = data; item; item = item->next) { GtkWidget *w; if (isPopup && !item->contents && !item->call_data && !xg_separator_p (item->name)) { char *utf8_label; /* A title for a popup. We do the same as GTK does when creating titles, but it is very ugly looking. */ group = NULL; utf8_label = get_utf8_string (item->name); gtk_menu_set_title(GTK_MENU(wmenu), utf8_label); w = gtk_menu_item_new_with_mnemonic (utf8_label); gtk_widget_set_sensitive (w, FALSE); if (utf8_label && utf8_label != item->name) g_free (utf8_label); } else if (xg_separator_p (item->name)) { group = NULL; /* GTK only have one separator type. */ w = gtk_separator_menu_item_new (); } else { char *utf8_label; char *utf8_key; utf8_label = get_utf8_string (item->name); utf8_key = get_utf8_string (item->key); w = make_menu_item (utf8_label, utf8_key, item, &group); if (utf8_label && utf8_label != item->name) g_free (utf8_label); if (utf8_key && utf8_key != item->key) g_free (utf8_key); /* Assume "Help" is the last menu in the menubar. */ if (isMenubar && ! item->next) gtk_menu_item_right_justify (GTK_MENU_ITEM (w)); /* final item, not a submenu */ if (item->call_data && ! item->contents) { if (select_cb) { cl_data = make_cl_data (cl_data, f); g_signal_connect(w, "activate", select_cb, item->call_data); g_signal_connect(w, "destroy", menuitem_destroy_callback, cl_data); /* Put cl_data in widget, so select_cb can get it */ g_object_set_data (G_OBJECT (w), G_FRAME_DATA, (gpointer)cl_data); } /* help_cb NYI */ } if (item->contents) { GtkWidget *submenu = createMenus (item->contents, f, activate_cb, select_cb, deactivate_cb, help_cb, 0, 0, 0, cl_data); gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu); if (activate_cb) g_signal_connect(w, "activate", activate_cb, 0); gtk_widget_show (submenu); } } gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w); gtk_widget_show (w); } return wmenu; } static char* get_dialog_title (char key) { char *title = ""; switch (key) { case 'E': case 'e': title = "Error"; break; case 'I': case 'i': title = "Information"; break; case 'L': case 'l': title = "Prompt"; break; case 'P': case 'p': title = "Prompt"; break; case 'Q': case 'q': title = "Question"; break; } return title; } static GtkWidget* createDialog (wv, select_cb, deactivate_cb) widget_value* wv; GCallback select_cb; GCallback deactivate_cb; { char *title = get_dialog_title (wv->name[0]); int total_buttons = wv->name[1] - '0'; int right_buttons = wv->name[4] - '0'; int left_buttons; int button_nr = 0; int button_spacing = 10; GtkWidget *wdialog = gtk_dialog_new (); widget_value* item; if (right_buttons == 0) right_buttons = total_buttons/2; left_buttons = total_buttons - right_buttons; gtk_window_set_title (GTK_WINDOW (wdialog), title); if (deactivate_cb) { g_signal_connect(wdialog, "close", deactivate_cb, 0); g_signal_connect(wdialog, "response", deactivate_cb, 0); } for (item = wv->contents; item; item = item->next) { char *utf8_label = get_utf8_string (item->value); GtkWidget *w; GtkRequisition req; if (strcmp (item->name, "message") == 0) { w = gtk_label_new (utf8_label); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), gtk_label_new (""), FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), w, TRUE, TRUE, 0); gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5); gtk_widget_realize (w); /* Try to make dialog look better. */ gtk_widget_size_request (w, &req); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (wdialog)->vbox), req.height); if (strlen(item->value) > 0) button_spacing = 2*req.width/strlen(item->value); } else { w = gtk_button_new_with_mnemonic (utf8_label); if (! item->enabled) gtk_widget_set_sensitive (w, FALSE); if (select_cb) g_signal_connect(w, "clicked", select_cb, item->call_data); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->action_area), w, TRUE, TRUE, button_spacing); if (++button_nr == left_buttons) gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->action_area), gtk_label_new (""), TRUE, TRUE, button_spacing); } if (utf8_label && utf8_label != item->value) g_free (utf8_label); gtk_widget_show (w); } return wdialog; } /* Create a menubar, popup menu or dialog, depending on the TYPE argument. */ GtkWidget* xg_create_widget (type, name, f, val, pop_up_p, activate_cb, select_cb, deactivate_cb, help_cb) char* type; char* name; FRAME_PTR f; widget_value* val; int pop_up_p; GCallback activate_cb; GCallback select_cb; GCallback deactivate_cb; GCallback help_cb; { GtkWidget *w = 0; if (strcmp (type, "dialog") == 0) { w = createDialog (val, select_cb, deactivate_cb); gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f))); } else if (strcmp (type, "menubar") == 0 || strcmp (type, "popup") == 0) { w = createMenus (val->contents, f, activate_cb, select_cb, deactivate_cb, help_cb, pop_up_p, strcmp (type, "menubar") == 0, 0, 0); } else { fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n", type); } return w; } /* Remove all children in a GTK container. */ static void remove_all_children (w) GtkWidget *w; { GList *list = gtk_container_get_children (GTK_CONTAINER (w)); GList *iter; if (! list) return; for (iter = g_list_copy (list); iter; iter = g_list_next (iter)) { GtkWidget *witem = GTK_WIDGET (iter->data); gtk_container_remove (GTK_CONTAINER (w), witem); } g_list_free (iter); } /* Update the MENUBAR. If DEEP_P is non-zero, rebuild all but the top level menu names in the MENUBAR. If DEEP_P is zero, just rebuild everything. */ void xg_modify_menubar_widgets (menubar, f, val, deep_p, activate_cb, select_cb, deactivate_cb, help_cb) GtkWidget *menubar; FRAME_PTR f; widget_value* val; int deep_p; GCallback activate_cb; GCallback select_cb; GCallback deactivate_cb; GCallback help_cb; { GList *list = gtk_container_get_children (GTK_CONTAINER (menubar)); GList *iter; if (! list) return; if (! deep_p) { widget_value* cur = val->contents; int is_changed = 0; /* Check if the change is actually no change. */ for (iter = g_list_first (list); iter && cur && ! is_changed; cur = cur->next, iter = g_list_next (iter)) { GtkMenuItem *witem = GTK_MENU_ITEM (iter->data); GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem))); char *utf8_label = get_utf8_string (cur->name); is_changed = strcmp (utf8_label, gtk_label_get_label (wlabel)); } is_changed = ! (cur == 0 && iter == 0); if (! is_changed) { return; } /* Keep menubar, but change everything else. This is simple, but not efficient. Ok for now. */ remove_all_children (menubar); (void)createMenus (val->contents, f, activate_cb, select_cb, deactivate_cb, help_cb, 0, 1, menubar, 0); } else { /* Keep the top level submenu names intact, but change everything else. This is simple, but not efficient. We must keep the submenu names (GTK menu item widgets) since the X Window in the XEvent that activates the menu are those widgets. */ widget_value* cur; for (cur = val->contents; cur; cur = cur->next) { for (iter = list ; iter; iter = g_list_next (iter)) { GtkMenuItem *witem = GTK_MENU_ITEM (iter->data); GtkWidget *wlbl = gtk_bin_get_child (GTK_BIN (witem)); const char *text = gtk_label_get_text (GTK_LABEL (wlbl)); GtkWidget *sub = gtk_menu_item_get_submenu (witem); GtkWidget *newsub; if (strcmp (text, cur->name)) continue; if (sub) gtk_menu_item_remove_submenu (witem); newsub = createMenus (cur->contents, f, activate_cb, select_cb, deactivate_cb, help_cb, 0, 0, 0, 0); gtk_menu_item_set_submenu (witem, newsub); break; } } } } /* Recompute all the widgets of frame F, when the menu bar has been changed. Value is non-zero if widgets were updated. */ int xg_update_frame_menubar (f) FRAME_PTR f; { struct x_output *x = f->output_data.x; GtkRequisition req; if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget)) return 0; BLOCK_INPUT; gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget, FALSE, FALSE, 0); gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0); gtk_widget_show (x->menubar_widget); gtk_widget_size_request (x->menubar_widget, &req); FRAME_MENUBAR_HEIGHT (f) = req.height; xg_set_wm_size_hints (f, 0, 0); gtk_window_resize (GTK_WINDOW (x->widget), PIXEL_WIDTH (f), req.height + PIXEL_HEIGHT (f)); gdk_window_process_all_updates (); SET_FRAME_GARBAGED (f); UNBLOCK_INPUT; } /* Get rid of the menu bar of frame F, and free its storage. This is used when deleting a frame, and when turning off the menu bar. */ void xg_free_frame_menubar (f) FRAME_PTR f; { struct x_output *x = f->output_data.x; if (x->menubar_widget) { int rows = f->height; int columns = f->width; int menubar_height = FRAME_MENUBAR_HEIGHT (f); BLOCK_INPUT; gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget); /* The menubar and its children shall be deleted when removed from the container. */ x->menubar_widget = 0; FRAME_MENUBAR_HEIGHT (f) = 0; /* base_height is now changed. */ xg_set_wm_size_hints (f, 0, 0); SET_FRAME_GARBAGED (f); UNBLOCK_INPUT; } } /*********************************************************************** General functions for creating widgets, resizing, events, e.t.c. ***********************************************************************/ /* Set this to non-zero to skip Emacs event processing. For dialogs and popups. */ int xg_pass_through_events = FALSE; /* 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. */ void xg_resize_widgets (f, pixelwidth, pixelheight) FRAME_PTR f; int pixelwidth, pixelheight; { int mbheight = FRAME_MENUBAR_HEIGHT (f); int rows = PIXEL_TO_CHAR_HEIGHT (f, pixelheight - mbheight); 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))) { struct x_output *x = f->output_data.x; GtkAllocation all; all.y = mbheight; all.x = 0; all.width = pixelwidth; all.height = pixelheight - mbheight; gtk_widget_size_allocate (x->edit_widget, &all); gdk_window_process_all_updates (); 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 */ void xg_frame_set_char_size (f, cols, rows) FRAME_PTR f; int cols; int rows; { int pixelheight = CHAR_TO_PIXEL_HEIGHT (f, rows) + FRAME_MENUBAR_HEIGHT (f); int pixelwidth = CHAR_TO_PIXEL_WIDTH (f, cols); xg_resize_widgets (f, pixelwidth, pixelheight); } /* Convert an X Window to its corresponding GtkWidget. Must be done like this, because GtkWidget:s can have "hidden" X Window that aren't accessible. Return 0 if no widget match WDESC. */ GtkWidget* xg_win_to_widget (wdesc) Window wdesc; { BLOCK_INPUT; gpointer gdkwin = gdk_xid_table_lookup (wdesc); GtkWidget *gwdesc = 0; if (gdkwin) { GdkEvent event; event.any.window = gdkwin; gwdesc = gtk_get_event_widget (&event); } UNBLOCK_INPUT; return gwdesc; } static void xg_pix2Gcolor (c, pixel) GdkColor *c; unsigned long pixel; { c->red = c->green = c->blue = 0; c->pixel = pixel; /* I've looked at the Gdk source, this works for X */ } int xg_create_frame_widgets (f) FRAME_PTR f; { GtkWidget *wtop; GtkWidget *wvbox; GtkWidget *wfixed; GdkColor bg; int i; BLOCK_INPUT; wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL); wvbox = gtk_vbox_new (FALSE, 0); wfixed = gtk_fixed_new (); /* Must have this to place scrollbars */ if (! wtop || ! wvbox || ! wfixed) { if (wtop) gtk_widget_destroy (wtop); if (wvbox) gtk_widget_destroy (wvbox); if (wfixed) gtk_widget_destroy (wfixed); return 0; } FRAME_GTK_OUTER_WIDGET (f) = wtop; FRAME_GTK_WIDGET (f) = wfixed; f->output_data.x->vbox_widget = wvbox; gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE); 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); 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_window_set_wmclass (GTK_WINDOW (wtop), SDATA (Vx_resource_name), SDATA (Vx_resource_class)); /* Convert our geometry parameters into a geometry string and specify it. GTK will itself handle calculating the real position this way. */ if (f->output_data.x->size_hint_flags & USPosition) { int left = f->output_data.x->left_pos; int xneg = f->output_data.x->size_hint_flags & XNegative; 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) top = -top; sprintf (geom_str, "=%dx%d%c%d%c%d", PIXEL_WIDTH (f), PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f), (xneg ? '-' : '+'), left, (yneg ? '-' : '+'), top); if (!gtk_window_parse_geometry (GTK_WINDOW (wtop), geom_str)) fprintf (stderr, "Failed to parse: '%s'\n", geom_str); } gtk_widget_add_events (wfixed, GDK_POINTER_MOTION_MASK | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_VISIBILITY_NOTIFY_MASK); /* Must realize the windows so the X window gets created. It is used by callers of this function. */ gtk_widget_realize (wfixed); FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed); /* Since GTK clears its window by filling with the background color, we must keep X and GTK background in sync. */ xg_pix2Gcolor (&bg, (f)->output_data.x->background_pixel); gdk_window_set_background (wfixed->window, &bg); /* 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; UNBLOCK_INPUT; return 1; } /* Set the normal size hints for the window manager, for frame F. FLAGS is the flags word to use--or 0 meaning preserve the flags that the window now has. If USER_POSITION is nonzero, we set the User Position flag (this is useful when FLAGS is 0). */ void xg_set_wm_size_hints (f, flags, user_position) FRAME_PTR f; 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->output_data.x->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->output_data.x->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 = FONT_WIDTH (f->output_data.x->font); size_hints.height_inc = f->output_data.x->line_height; hint_flags |= GDK_HINT_BASE_SIZE; base_width = CHAR_TO_PIXEL_WIDTH (f, 0); base_height = CHAR_TO_PIXEL_HEIGHT (f, 0) + FRAME_MENUBAR_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; if (win_gravity == NorthGravity) size_hints.win_gravity = GDK_GRAVITY_NORTH; if (win_gravity == NorthEastGravity) size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST; if (win_gravity == WestGravity) size_hints.win_gravity = GDK_GRAVITY_WEST; if (win_gravity == CenterGravity) size_hints.win_gravity = GDK_GRAVITY_CENTER; if (win_gravity == EastGravity) size_hints.win_gravity = GDK_GRAVITY_EAST; if (win_gravity == SouthWestGravity) size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST; if (win_gravity == SouthGravity) size_hints.win_gravity = GDK_GRAVITY_SOUTH; if (win_gravity == SouthEastGravity) size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST; 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; } BLOCK_INPUT; gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), FRAME_GTK_OUTER_WIDGET (f), &size_hints, hint_flags); f->output_data.x->size_hints = size_hints; f->output_data.x->hint_flags = hint_flags; UNBLOCK_INPUT; } } /*********************************************************************** Scrollbar functions ***********************************************************************/ /* Setting scrollbar values invokes the callback. Use this variable to indicate that callback should do nothing. */ int xg_ignore_gtk_scrollbar = 0; /* 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 = 0; /* 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 int to pointers to get around the 32 bit limitation. */ static struct { GtkWidget **widgets; int max_size; int used; } id_to_widget = { 0, 0, 0 }; /* Grow this much every time we need to allocate more */ #define ID_TO_WIDGET_INCR 32 /* Store a pointer in id_to_widget and return the int. */ static int xg_store_widget_in_map (w) GtkWidget *w; { int i; if (id_to_widget.max_size == id_to_widget.used) { int new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR; id_to_widget.widgets = xrealloc (id_to_widget.widgets, sizeof (GtkWidget *)*new_size); for (i = id_to_widget.max_size; i < new_size; ++i) id_to_widget.widgets[i] = 0; id_to_widget.max_size = new_size; } /* Just loop over the array and find a free place. After all, how many scrollbars are we creating? Should be a small number. The check above guarantees we will find a free place. */ for (i = 0; i < id_to_widget.max_size; ++i) { if (! id_to_widget.widgets[i]) { id_to_widget.widgets[i] = w; ++id_to_widget.used; return i; } } /* Should never end up here */ abort (); } /* Remove a pointer from id_to_widget. Called when scrollbar is destroyed. */ static void xg_remove_widget_from_map (idx) int idx; { if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0) { id_to_widget.widgets[idx] = 0; --id_to_widget.used; } } /* Get widget pointer from id_to_widget. */ static GtkWidget* xg_get_widget_from_map (idx) int idx; { if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0) return id_to_widget.widgets[idx]; return 0; } /* Callback invoked when scrollbars are destroyed. We free pointer to last scrollbar value here and remove the id. */ static void xg_gtk_scroll_destroy (widget, data) GtkWidget *widget; gpointer data; { gpointer p; int id = (int)data; p = g_object_get_data (G_OBJECT (widget), G_LAST_SB_DATA); if (p) xfree (p); xg_remove_widget_from_map (id); } void xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name) FRAME_PTR f; struct scroll_bar *bar; GCallback scroll_callback; char *scroll_bar_name; { 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 (GTK_SB_MIN, GTK_SB_MIN, GTK_SB_MAX, 0.1, 0.1, 0.1); /* KOKO, set fg & bg, cursor? */ 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 (vadj, "value-changed", scroll_callback, (gpointer)bar); g_signal_connect (wscroll, "destroy", G_CALLBACK(xg_gtk_scroll_destroy), (gpointer)scroll_id); gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), wscroll, 0, 0); SET_SCROLL_BAR_X_WINDOW (bar, scroll_id); } void xg_show_scroll_bar (scrollbar_id) int scrollbar_id; { GtkWidget *w = xg_get_widget_from_map (scrollbar_id); if (w) gtk_widget_show (w); } void xg_remove_scroll_bar (f, scrollbar_id) FRAME_PTR f; int scrollbar_id; { GtkWidget *w = xg_get_widget_from_map (scrollbar_id); if (w) { gtk_widget_destroy (w); SET_FRAME_GARBAGED (f); } } /* Update the position of the vertical scrollbar wscroll in frame F. */ void xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height) FRAME_PTR f; int scrollbar_id; int top; int left; int width; int height; { int gtop = top; int gheight = max (height, 1); GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id); if (wscroll) { gtk_fixed_move (GTK_FIXED (f->output_data.x->edit_widget), wscroll, left, gtop); gtk_widget_set_size_request (wscroll, width, gheight); gtk_widget_queue_draw (wscroll); /* 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 (); } } /* Set the thumb size and position of scroll bar BAR. We are currently displaying PORTION out of a whole WHOLE, and our position POSITION. */ void xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) struct scroll_bar *bar; int portion, position, whole; { GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar)); if (wscroll && whole > 0 && ! xg_ignore_next_thumb) { GtkAdjustment* adj = gtk_range_get_adjustment (GTK_RANGE (wscroll)); gdouble top = GTK_SB_RANGE * ((gdouble) position / whole); gdouble shown = GTK_SB_RANGE * ((gdouble) portion / whole); adj->page_size = (int)shown; /* Assume a page increment is about 95% of the page size */ adj->page_increment = (int) (0.95*adj->page_size); /* Average 30 chars per line */ adj->step_increment = (int) (adj->page_size/30.0); /* 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), top); xg_ignore_gtk_scrollbar = 0; } /* Make sure the scrollbar is redrawn with new thumb */ gtk_widget_queue_draw (wscroll); gdk_window_process_all_updates (); /* See comment in gtkutil.c */ xg_ignore_next_thumb = 0; } #endif /* USE_GTK */ --ELM737119599-199-0_ Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel --ELM737119599-199-0_--