X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/810f2256ba03de93679ae73c02512f0da0fd1c7b..8d892d7fef218001fa8ef828db4a5a864448f950:/src/gtkutil.c diff --git a/src/gtkutil.c b/src/gtkutil.c index 4fc3351ab7..3b590e0fae 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1,6 +1,5 @@ /* Functions for creating and updating GTK widgets. - Copyright (C) 2003 - Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -16,17 +15,19 @@ 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. */ +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ #include "config.h" #ifdef USE_GTK #include +#include #include #include "lisp.h" #include "xterm.h" #include "blockinput.h" +#include "syssignal.h" #include "window.h" #include "atimer.h" #include "gtkutil.h" @@ -48,6 +49,7 @@ Boston, MA 02111-1307, USA. */ #ifdef HAVE_GTK_MULTIDISPLAY /* Return the GdkDisplay that corresponds to the X display DPY. */ + static GdkDisplay * xg_get_gdk_display (dpy) Display *dpy; @@ -58,6 +60,7 @@ xg_get_gdk_display (dpy) /* When the GTK widget W is to be created on a display for F that is not the default display, set the display for W. W can be a GtkMenu or a GtkWindow widget. */ + static void xg_set_screen (w, f) GtkWidget *w; @@ -80,6 +83,7 @@ xg_set_screen (w, f) /* Make some defines so we can use the GTK 2.2 functions when compiling with GTK 2.0. */ + #define xg_set_screen(w, f) #define gdk_xid_table_lookup_for_display(dpy, w) gdk_xid_table_lookup (w) #define gdk_pixmap_foreign_new_for_display(dpy, p) gdk_pixmap_foreign_new (p) @@ -95,6 +99,7 @@ xg_set_screen (w, f) Returns non-zero if display could be opened, zero if display could not be opened, and less than zero if the GTK version doesn't support multipe displays. */ + int xg_display_open (display_name, dpy) char *display_name; @@ -115,6 +120,8 @@ xg_display_open (display_name, dpy) } +/* Close display DPY. */ + void xg_display_close (Display *dpy) { @@ -176,6 +183,7 @@ static int malloc_cpt; widget_value_free_list or by malloc:ing a new one. Return a pointer to the allocated structure. */ + widget_value * malloc_widget_value () { @@ -197,6 +205,7 @@ malloc_widget_value () /* This is analogous to free. It frees only what was allocated by malloc_widget_value, and no substructures. */ + void free_widget_value (wv) widget_value *wv; @@ -221,6 +230,7 @@ free_widget_value (wv) /* Create and return the cursor to be used for popup menus and scroll bars on display DPY. */ + GdkCursor * xg_create_default_cursor (dpy) Display *dpy; @@ -229,29 +239,165 @@ xg_create_default_cursor (dpy) return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR); } -/* For the image defined in IMG, make and return a GdkPixmap for - the pixmap in *GPIX, and a GdkBitmap for the mask in *GMASK. - If IMG has no mask, *GMASK is set to NULL. - The image is defined on the display where frame F is. */ -static void -xg_get_gdk_pixmap_and_mask (f, img, gpix, gmask) +/* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */ + +static GdkPixbuf * +xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap) + GdkPixmap *gpix; + GdkPixmap *gmask; + GdkColormap *cmap; +{ + int x, y, width, height, rowstride, mask_rowstride; + GdkPixbuf *icon_buf, *tmp_buf; + guchar *pixels; + guchar *mask_pixels; + + gdk_drawable_get_size (gpix, &width, &height); + tmp_buf = gdk_pixbuf_get_from_drawable (NULL, gpix, cmap, + 0, 0, 0, 0, width, height); + icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0); + g_object_unref (G_OBJECT (tmp_buf)); + + if (gmask) + { + GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL, + gmask, + NULL, + 0, 0, 0, 0, + width, height); + guchar *pixels = gdk_pixbuf_get_pixels (icon_buf); + guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf); + int rowstride = gdk_pixbuf_get_rowstride (icon_buf); + int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf); + int y; + + for (y = 0; y < height; ++y) + { + guchar *iconptr, *maskptr; + int x; + + iconptr = pixels + y * rowstride; + maskptr = mask_pixels + y * mask_rowstride; + + for (x = 0; x < width; ++x) + { + /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking + just R is sufficient. */ + if (maskptr[0] == 0) + iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */ + + iconptr += rowstride/width; + maskptr += mask_rowstride/width; + } + } + + g_object_unref (G_OBJECT (mask_buf)); + } + + return icon_buf; +} + +/* For the image defined in IMG, make and return a GtkImage. For displays with + 8 planes or less we must make a GdkPixbuf and apply the mask manually. + Otherwise the highlightning and dimming the tool bar code in GTK does + will look bad. For display with more than 8 planes we just use the + pixmap and mask directly. For monochrome displays, GTK doesn't seem + able to use external pixmaps, it looks bad whatever we do. + The image is defined on the display where frame F is. + WIDGET is used to find the GdkColormap to use for the GdkPixbuf. + If OLD_WIDGET is NULL, a new widget is constructed and returned. + If OLD_WIDGET is not NULL, that widget is modified. */ + +static GtkWidget * +xg_get_image_for_pixmap (f, img, widget, old_widget) FRAME_PTR f; struct image *img; - GdkPixmap **gpix; - GdkBitmap **gmask; + GtkWidget *widget; + GtkImage *old_widget; { - GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); + GdkPixmap *gpix; + GdkPixmap *gmask; + GdkDisplay *gdpy; + + /* If we are on a one bit display, let GTK do all the image handling. + This seems to be the only way to make insensitive and activated icons + look good. */ + if (x_screen_planes (f) == 1) + { + Lisp_Object specified_file = Qnil; + Lisp_Object tail; + extern Lisp_Object QCfile; + + for (tail = XCDR (img->spec); + NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail)); + tail = XCDR (XCDR (tail))) + if (EQ (XCAR (tail), QCfile)) + specified_file = XCAR (XCDR (tail)); + + if (STRINGP (specified_file)) + { + + Lisp_Object file = Qnil; + struct gcpro gcpro1; + GCPRO1 (file); + + file = x_find_image_file (specified_file); + /* We already loaded the image once before calling this + function, so this should not fail. */ + xassert (STRINGP (file) != 0); + + if (! old_widget) + old_widget = GTK_IMAGE (gtk_image_new_from_file (SDATA (file))); + else + gtk_image_set_from_file (old_widget, SDATA (file)); + + UNGCPRO; + return GTK_WIDGET (old_widget); + } + } - *gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap); - *gmask = img->mask ? - (GdkBitmap*) gdk_pixmap_foreign_new_for_display (gdpy, img->mask) - : 0; + gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); + gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap); + gmask = img->mask ? gdk_pixmap_foreign_new_for_display (gdpy, img->mask) : 0; + + if (x_screen_planes (f) > 8 || x_screen_planes (f) == 1) + { + if (! old_widget) + old_widget = GTK_IMAGE (gtk_image_new_from_pixmap (gpix, gmask)); + else + gtk_image_set_from_pixmap (old_widget, gpix, gmask); + } + else + { + + /* This is a workaround to make icons look good on pseudo color + displays. Apparently GTK expects the images to have an alpha + channel. If they don't, insensitive and activated icons will + look bad. This workaround does not work on monochrome displays, + and is not needed on true color/static color displays (i.e. + 16 bits and higher). */ + GdkColormap *cmap = gtk_widget_get_colormap (widget); + GdkPixbuf *icon_buf = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap); + + if (! old_widget) + old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf)); + else + gtk_image_set_from_pixbuf (old_widget, icon_buf); + + g_object_unref (G_OBJECT (icon_buf)); + } + + g_object_unref (G_OBJECT (gpix)); + if (gmask) g_object_unref (G_OBJECT (gmask)); + + return GTK_WIDGET (old_widget); } /* Set CURSOR on W and all widgets W contain. We must do like this for scroll bars and menu because they create widgets internally, and it is those widgets that are visible. */ + static void xg_set_cursor (w, cursor) GtkWidget *w; @@ -279,6 +425,7 @@ xg_set_cursor (w, cursor) has expired by calling the GTK event loop. Also, when a menu is active, it has a small timeout before it pops down the sub menu under it. */ + static void xg_process_timeouts (timer) struct atimer *timer; @@ -294,6 +441,7 @@ xg_process_timeouts (timer) /* Start the xg_timer with an interval of 0.1 seconds, if not already started. xg_process_timeouts is called when the timer expires. The timer started is continuous, i.e. runs until xg_stop_timer is called. */ + static void xg_start_timer () { @@ -309,6 +457,7 @@ xg_start_timer () } /* Stop the xg_timer if started. */ + static void xg_stop_timer () { @@ -320,6 +469,7 @@ xg_stop_timer () } /* Insert NODE into linked LIST. */ + static void xg_list_insert (xg_list_node *list, xg_list_node *node) { @@ -332,6 +482,7 @@ xg_list_insert (xg_list_node *list, xg_list_node *node) } /* Remove NODE from linked LIST. */ + static void xg_list_remove (xg_list_node *list, xg_list_node *node) { @@ -352,6 +503,7 @@ xg_list_remove (xg_list_node *list, xg_list_node *node) utf8 or NULL, just return STR. If not, a new string is allocated and the caller must free the result with g_free. */ + static char * get_utf8_string (str) char *str; @@ -375,6 +527,7 @@ get_utf8_string (str) only way to get geometry position right if the user explicitly asked for a position when starting Emacs. F is the frame we shall set geometry for. */ + static void xg_set_geometry (f) FRAME_PTR f; @@ -408,18 +561,13 @@ xg_set_geometry (f) /* Resize the outer window of frame F after chainging the height. This happend when the menu bar or the tool bar is added or removed. COLUMNS/ROWS is the size the edit area shall have after the resize. */ + static void xg_resize_outer_widget (f, columns, rows) FRAME_PTR f; int columns; int rows; { - gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), - FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f)); - - /* base_height is now changed. */ - x_wm_set_size_hint (f, 0, 0); - /* If we are not mapped yet, set geometry once again, as window height now have changed. */ if (! GTK_WIDGET_MAPPED (FRAME_GTK_OUTER_WIDGET (f))) @@ -429,29 +577,12 @@ xg_resize_outer_widget (f, columns, rows) gdk_window_process_all_updates (); } -/* This gets called after the frame F has been cleared. Since that is - done with X calls, we need to redraw GTK widget (scroll bars). */ -void -xg_frame_cleared (f) - FRAME_PTR f; -{ - GtkWidget *w = f->output_data.x->widget; - - if (w) - { - gtk_container_set_reallocate_redraws (GTK_CONTAINER (w), TRUE); - gtk_container_foreach (GTK_CONTAINER (w), - (GtkCallback) gtk_widget_queue_draw, - 0); - gdk_window_process_all_updates (); - } -} - /* Function to handle resize of our widgets. Since Emacs has some layouts that does not fit well with GTK standard containers, we do most layout manually. F is the frame to resize. PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */ + void xg_resize_widgets (f, pixelwidth, pixelheight) FRAME_PTR f; @@ -459,13 +590,15 @@ xg_resize_widgets (f, pixelwidth, pixelheight) { int mbheight = FRAME_MENUBAR_HEIGHT (f); int tbheight = FRAME_TOOLBAR_HEIGHT (f); - int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight + int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight - mbheight - tbheight)); int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth); if (FRAME_GTK_WIDGET (f) - && (columns != FRAME_COLS (f) || rows != FRAME_LINES (f) - || pixelwidth != FRAME_PIXEL_WIDTH (f) || pixelheight != FRAME_PIXEL_HEIGHT (f))) + && (columns != FRAME_COLS (f) + || rows != FRAME_LINES (f) + || pixelwidth != FRAME_PIXEL_WIDTH (f) + || pixelheight != FRAME_PIXEL_HEIGHT (f))) { struct x_output *x = f->output_data.x; GtkAllocation all; @@ -486,6 +619,7 @@ xg_resize_widgets (f, pixelwidth, pixelheight) /* Update our widget size to be COLS/ROWS characters for frame F. */ + void xg_frame_set_char_size (f, cols, rows) FRAME_PTR f; @@ -515,7 +649,7 @@ xg_frame_set_char_size (f, cols, rows) gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), pixelwidth, pixelheight); xg_resize_widgets (f, pixelwidth, pixelheight); - + x_wm_set_size_hint (f, 0, 0); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); } @@ -525,6 +659,7 @@ xg_frame_set_char_size (f, cols, rows) X Window that aren't accessible. Return 0 if no widget match WDESC. */ + GtkWidget * xg_win_to_widget (dpy, wdesc) Display *dpy; @@ -550,6 +685,7 @@ xg_win_to_widget (dpy, wdesc) /* Fill in the GdkColor C so that it represents PIXEL. W is the widget that color will be used for. Used to find colormap. */ + static void xg_pix_to_gcolor (w, pixel, c) GtkWidget *w; @@ -560,56 +696,9 @@ xg_pix_to_gcolor (w, pixel, c) gdk_colormap_query_color (map, pixel, c); } -/* Turning off double buffering for our GtkFixed widget has the side - effect of turning it off also for its children (scroll bars). - But we want those to be double buffered to not flicker so handle - expose manually here. - WIDGET is the GtkFixed widget that gets exposed. - EVENT is the expose event. - USER_DATA is unused. - - Return TRUE to tell GTK that this expose event has been fully handeled - and that GTK shall do nothing more with it. */ -static gboolean -xg_fixed_handle_expose (GtkWidget *widget, - GdkEventExpose *event, - gpointer user_data) -{ - GList *iter; - - for (iter = GTK_FIXED (widget)->children; iter; iter = g_list_next (iter)) - { - GtkFixedChild *child_data = (GtkFixedChild *) iter->data; - GtkWidget *child = child_data->widget; - GdkWindow *window = child->window; - GdkRegion *region = gtk_widget_region_intersect (child, event->region); - - if (! gdk_region_empty (region)) - { - GdkEvent child_event; - child_event.expose = *event; - child_event.expose.region = region; - - /* Turn on double buffering, i.e. draw to an off screen area. */ - gdk_window_begin_paint_region (window, region); - - /* Tell child to redraw itself. */ - gdk_region_get_clipbox (region, &child_event.expose.area); - gtk_widget_send_expose (child, &child_event); - gdk_window_process_updates (window, TRUE); - - /* Copy off screen area to the window. */ - gdk_window_end_paint (window); - } - - gdk_region_destroy (region); - } - - return TRUE; -} - /* Create and set up the GTK widgets for frame F. Return 0 if creation failed, non-zero otherwise. */ + int xg_create_frame_widgets (f) FRAME_PTR f; @@ -636,6 +725,7 @@ xg_create_frame_widgets (f) if (wvbox) gtk_widget_destroy (wvbox); if (wfixed) gtk_widget_destroy (wfixed); + UNBLOCK_INPUT; return 0; } @@ -672,8 +762,8 @@ xg_create_frame_widgets (f) up in the wrong place as tool bar height has not been taken into account. So we cheat a bit by setting a height that is what it will have later on when tool bar items are added. */ - if (FRAME_EXTERNAL_TOOL_BAR (f) && FRAME_TOOLBAR_HEIGHT (f) == 0) - FRAME_TOOLBAR_HEIGHT (f) = 34; + if (FRAME_EXTERNAL_TOOL_BAR (f) && f->n_tool_bar_items == 0) + FRAME_TOOLBAR_HEIGHT (f) = 38; /* We don't want this widget double buffered, because we draw on it @@ -683,12 +773,6 @@ xg_create_frame_widgets (f) a lot, so we turn off double buffering. */ gtk_widget_set_double_buffered (wfixed, FALSE); - /* Turning off double buffering above has the side effect of turning - it off also for its children (scroll bars). But we want those - to be double buffered to not flicker so handle expose manually. */ - g_signal_connect (G_OBJECT (wfixed), "expose-event", - G_CALLBACK (xg_fixed_handle_expose), 0); - /* GTK documents says use gtk_window_set_resizable. But then a user can't shrink the window from its starting size. */ gtk_window_set_policy (GTK_WINDOW (wtop), TRUE, TRUE, TRUE); @@ -751,6 +835,7 @@ xg_create_frame_widgets (f) that the window now has. If USER_POSITION is nonzero, we set the User Position flag (this is useful when FLAGS is 0). */ + void x_wm_set_size_hint (f, flags, user_position) FRAME_PTR f; @@ -849,6 +934,7 @@ x_wm_set_size_hint (f, flags, user_position) keep the GTK and X colors in sync. F is the frame to change, BG is the pixel value to change to. */ + void xg_set_background_color (f, bg) FRAME_PTR f; @@ -866,12 +952,31 @@ xg_set_background_color (f, bg) } +/* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK + functions so GTK does not overwrite the icon. */ + +void +xg_set_frame_icon (f, icon_pixmap, icon_mask) + FRAME_PTR f; + Pixmap icon_pixmap; + Pixmap icon_mask; +{ + GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); + GdkPixmap *gpix = gdk_pixmap_foreign_new_for_display (gdpy, icon_pixmap); + GdkPixmap *gmask = gdk_pixmap_foreign_new_for_display (gdpy, icon_mask); + GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, NULL); + + gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp); +} + + /*********************************************************************** Dialog functions ***********************************************************************/ /* Return the dialog title to use for a dialog of type KEY. This is the encoding used by lwlib. We use the same for GTK. */ + static char * get_dialog_title (char key) { @@ -912,6 +1017,7 @@ get_dialog_title (char key) user_data is NULL (not used). Returns TRUE to end propagation of event. */ + static gboolean dialog_delete_callback (w, event, user_data) GtkWidget *w; @@ -928,6 +1034,7 @@ dialog_delete_callback (w, event, user_data) DEACTIVATE_CB is the callback to use when the dialog pops down. Returns the GTK dialog widget. */ + static GtkWidget * create_dialog (wv, select_cb, deactivate_cb) widget_value *wv; @@ -1038,96 +1145,156 @@ create_dialog (wv, select_cb, deactivate_cb) } -enum -{ - XG_FILE_NOT_DONE, - XG_FILE_OK, - XG_FILE_CANCEL, - XG_FILE_DESTROYED, -}; + +/*********************************************************************** + File dialog functions + ***********************************************************************/ +/* Function that is called when the file dialog pops down. + W is the dialog widget, RESPONSE is the response code. + USER_DATA is what we passed in to g_signal_connect (pointer to int). */ -/* Callback function invoked when the Ok button is pressed in - a file dialog. - W is the file dialog widget, - ARG points to an integer where we record what has happend. */ static void -xg_file_sel_ok (w, arg) - GtkWidget *w; - gpointer arg; +xg_file_response_cb (w, + response, + user_data) + GtkDialog *w; + gint response; + gpointer user_data; { - *(int*)arg = XG_FILE_OK; + int *ptr = (int *) user_data; + *ptr = response; } -/* Callback function invoked when the Cancel button is pressed in - a file dialog. - W is the file dialog widget, - ARG points to an integer where we record what has happend. */ -static void -xg_file_sel_cancel (w, arg) - GtkWidget *w; - gpointer arg; + +/* Destroy the dialog. This makes it pop down. */ + +static Lisp_Object +pop_down_file_dialog (arg) + Lisp_Object arg; { - *(int*)arg = XG_FILE_CANCEL; + struct Lisp_Save_Value *p = XSAVE_VALUE (arg); + BLOCK_INPUT; + gtk_widget_destroy (GTK_WIDGET (p->pointer)); + UNBLOCK_INPUT; + return Qnil; } -/* Callback function invoked when the file dialog is destroyed (i.e. - popped down). We must keep track of this, because if this - happens, GTK destroys the widget. But if for example, Ok is pressed, - the dialog is popped down, but the dialog widget is not destroyed. - W is the file dialog widget, - ARG points to an integer where we record what has happend. */ -static void -xg_file_sel_destroy (w, arg) +typedef char * (*xg_get_file_func) P_ ((GtkWidget *)); + +#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW + +/* Return the selected file for file chooser dialog W. + The returned string must be free:d. */ + +static char * +xg_get_file_name_from_chooser (w) GtkWidget *w; - gpointer arg; { - *(int*)arg = XG_FILE_DESTROYED; + return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w)); } -/* Read a file name from the user using a file dialog. +/* Read a file name from the user using a file chooser dialog. F is the current frame. PROMPT is a prompt to show to the user. May not be NULL. DEFAULT_FILENAME is a default selection to be displayed. May be NULL. If MUSTMATCH_P is non-zero, the returned file name must be an existing - file. + file. *FUNC is set to a function that can be used to retrieve the + selected file name from the returned widget. - Returns a file name or NULL if no file was selected. - The returned string must be freed by the caller. */ -char * -xg_get_file_name (f, prompt, default_filename, mustmatch_p) + Returns the created widget. */ + +static GtkWidget * +xg_get_file_with_chooser (f, prompt, default_filename, + mustmatch_p, only_dir_p, func) FRAME_PTR f; char *prompt; char *default_filename; - int mustmatch_p; + int mustmatch_p, only_dir_p; + xg_get_file_func *func; { GtkWidget *filewin; - GtkFileSelection *filesel; - int filesel_done = XG_FILE_NOT_DONE; - char *fn = 0; + GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)); + GtkFileChooserAction action = (mustmatch_p ? + GTK_FILE_CHOOSER_ACTION_OPEN : + GTK_FILE_CHOOSER_ACTION_SAVE); + + if (only_dir_p) + action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + + filewin = gtk_file_chooser_dialog_new (prompt, gwin, action, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + (mustmatch_p || only_dir_p ? + GTK_STOCK_OPEN : GTK_STOCK_OK), + GTK_RESPONSE_OK, + NULL); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE); - filewin = gtk_file_selection_new (prompt); - filesel = GTK_FILE_SELECTION (filewin); + if (default_filename) + { + Lisp_Object file; + struct gcpro gcpro1; + GCPRO1 (file); - xg_set_screen (filewin, f); + file = build_string (default_filename); - gtk_widget_set_name (filewin, "emacs-filedialog"); + /* File chooser does not understand ~/... in the file name. It must be + an absolute name starting with /. */ + if (default_filename[0] != '/') + file = Fexpand_file_name (file, Qnil); - gtk_window_set_transient_for (GTK_WINDOW (filewin), - GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f))); - gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE); + default_filename = SDATA (file); + if (Ffile_directory_p (file)) + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin), + default_filename); + else + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin), + default_filename); - g_signal_connect (G_OBJECT (filesel->ok_button), - "clicked", - G_CALLBACK (xg_file_sel_ok), - &filesel_done); - g_signal_connect (G_OBJECT (filesel->cancel_button), - "clicked", - G_CALLBACK (xg_file_sel_cancel), - &filesel_done); - g_signal_connect (G_OBJECT (filesel), - "destroy", - G_CALLBACK (xg_file_sel_destroy), - &filesel_done); + UNGCPRO; + } + + *func = xg_get_file_name_from_chooser; + return filewin; +} +#endif /* HAVE_GTK_FILE_CHOOSER_DIALOG_NEW */ + +#ifdef HAVE_GTK_FILE_SELECTION_NEW + +/* Return the selected file for file selector dialog W. + The returned string must be free:d. */ + +static char * +xg_get_file_name_from_selector (w) + GtkWidget *w; +{ + GtkFileSelection *filesel = GTK_FILE_SELECTION (w); + return xstrdup ((char*) gtk_file_selection_get_filename (filesel)); +} + +/* Create a file selection dialog. + F is the current frame. + PROMPT is a prompt to show to the user. May not be NULL. + DEFAULT_FILENAME is a default selection to be displayed. May be NULL. + If MUSTMATCH_P is non-zero, the returned file name must be an existing + file. *FUNC is set to a function that can be used to retrieve the + selected file name from the returned widget. + + Returns the created widget. */ + +static GtkWidget * +xg_get_file_with_selection (f, prompt, default_filename, + mustmatch_p, only_dir_p, func) + FRAME_PTR f; + char *prompt; + char *default_filename; + int mustmatch_p, only_dir_p; + xg_get_file_func *func; +{ + GtkWidget *filewin; + GtkFileSelection *filesel; + + filewin = gtk_file_selection_new (prompt); + filesel = GTK_FILE_SELECTION (filewin); if (default_filename) gtk_file_selection_set_filename (filesel, default_filename); @@ -1139,17 +1306,100 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p) gtk_file_selection_hide_fileop_buttons (filesel); } + *func = xg_get_file_name_from_selector; - gtk_widget_show_all (filewin); + return filewin; +} +#endif /* HAVE_GTK_FILE_SELECTION_NEW */ - while (filesel_done == XG_FILE_NOT_DONE) - gtk_main_iteration (); +/* Read a file name from the user using a file dialog, either the old + file selection dialog, or the new file chooser dialog. Which to use + depends on what the GTK version used has, and what the value of + gtk-use-old-file-dialog. + F is the current frame. + PROMPT is a prompt to show to the user. May not be NULL. + DEFAULT_FILENAME is a default selection to be displayed. May be NULL. + If MUSTMATCH_P is non-zero, the returned file name must be an existing + file. + + Returns a file name or NULL if no file was selected. + The returned string must be freed by the caller. */ - if (filesel_done == XG_FILE_OK) - fn = xstrdup ((char*) gtk_file_selection_get_filename (filesel)); +char * +xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) + FRAME_PTR f; + char *prompt; + char *default_filename; + int mustmatch_p, only_dir_p; +{ + GtkWidget *w = 0; + int count = SPECPDL_INDEX (); + char *fn = 0; + int filesel_done = 0; + xg_get_file_func func; + extern int x_use_old_gtk_file_dialog; + +#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN) + /* I really don't know why this is needed, but without this the GLIBC add on + library linuxthreads hangs when the Gnome file chooser backend creates + threads. */ + sigblock (sigmask (__SIGRTMIN)); +#endif /* HAVE_GTK_AND_PTHREAD */ + +#ifdef HAVE_GTK_FILE_BOTH + + if (x_use_old_gtk_file_dialog) + w = xg_get_file_with_selection (f, prompt, default_filename, + mustmatch_p, only_dir_p, &func); + else + w = xg_get_file_with_chooser (f, prompt, default_filename, + mustmatch_p, only_dir_p, &func); - if (filesel_done != XG_FILE_DESTROYED) - gtk_widget_destroy (filewin); +#else /* not HAVE_GTK_FILE_BOTH */ + +#ifdef HAVE_GTK_FILE_SELECTION_NEW + w = xg_get_file_with_selection (f, prompt, default_filename, + mustmatch_p, only_dir_p, &func); +#endif +#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW + w = xg_get_file_with_chooser (f, prompt, default_filename, + mustmatch_p, only_dir_p, &func); +#endif + +#endif /* HAVE_GTK_FILE_BOTH */ + + xg_set_screen (w, f); + gtk_widget_set_name (w, "emacs-filedialog"); + gtk_window_set_transient_for (GTK_WINDOW (w), + GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f))); + gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE); + gtk_window_set_modal (GTK_WINDOW (w), TRUE); + + g_signal_connect (G_OBJECT (w), + "response", + G_CALLBACK (xg_file_response_cb), + &filesel_done); + + /* Don't destroy the widget if closed by the window manager close button. */ + g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL); + + gtk_widget_show (w); + + record_unwind_protect (pop_down_file_dialog, make_save_value (w, 0)); + while (! filesel_done) + { + x_menu_wait_for_event (0); + gtk_main_iteration (); + } + +#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN) + sigunblock (sigmask (__SIGRTMIN)); +#endif + + if (filesel_done == GTK_RESPONSE_OK) + fn = (*func) (w); + + unbind_to (count, Qnil); return fn; } @@ -1183,6 +1433,7 @@ static xg_list_node xg_menu_item_cb_list; Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly allocated xg_menu_cb_data if CL_DATA is NULL. */ + static xg_menu_cb_data * make_cl_data (cl_data, f, highlight_cb) xg_menu_cb_data *cl_data; @@ -1216,6 +1467,7 @@ make_cl_data (cl_data, f, highlight_cb) HIGHLIGHT_CB could change, there is no check that the same function is given when modifying a menu bar as was given when creating the menu bar. */ + static void update_cl_data (cl_data, f, highlight_cb) xg_menu_cb_data *cl_data; @@ -1233,6 +1485,7 @@ update_cl_data (cl_data, f, highlight_cb) /* Decrease reference count for CL_DATA. If reference count is zero, free CL_DATA. */ + static void unref_cl_data (cl_data) xg_menu_cb_data *cl_data; @@ -1249,6 +1502,7 @@ unref_cl_data (cl_data) } /* Function that marks all lisp data during GC. */ + void xg_mark_data () { @@ -1270,6 +1524,7 @@ xg_mark_data () /* Callback called when a menu item is destroyed. Used to free data. W is the widget that is being destroyed (not used). CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */ + static void menuitem_destroy_callback (w, client_data) GtkWidget *w; @@ -1289,6 +1544,7 @@ menuitem_destroy_callback (w, client_data) CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. Returns FALSE to tell GTK to keep processing this event. */ + static gboolean menuitem_highlight_callback (w, event, client_data) GtkWidget *w; @@ -1313,6 +1569,7 @@ menuitem_highlight_callback (w, event, client_data) /* Callback called when a menu is destroyed. Used to free data. W is the widget that is being destroyed (not used). CLIENT_DATA points to the xg_menu_cb_data associated with W. */ + static void menu_destroy_callback (w, client_data) GtkWidget *w; @@ -1328,6 +1585,7 @@ menu_destroy_callback (w, client_data) W is the widget that does the grab (not used). UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab. CLIENT_DATA is NULL (not used). */ + static void menu_grab_callback (GtkWidget *widget, gboolean ungrab_p, @@ -1347,6 +1605,7 @@ menu_grab_callback (GtkWidget *widget, must be non-NULL) and can be inserted into a menu item. Returns the GtkHBox. */ + static GtkWidget * make_widget_for_menu_item (utf8_label, utf8_key) char *utf8_label; @@ -1386,6 +1645,7 @@ make_widget_for_menu_item (utf8_label, utf8_key) 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; @@ -1435,6 +1695,7 @@ make_menu_item (utf8_label, utf8_key, item, group) /* Return non-zero if LABEL specifies a separator (GTK only has one separator type) */ + static int xg_separator_p (char *label) { @@ -1483,6 +1744,7 @@ xg_separator_p (char *label) static int xg_detached_menus; /* Returns non-zero if there are detached menus. */ + int xg_have_tear_offs () { @@ -1493,6 +1755,7 @@ xg_have_tear_offs () decrease the xg_detached_menus count. WIDGET is the top level window that is removed (the parent of the menu). CLIENT_DATA is not used. */ + static void tearoff_remove (widget, client_data) GtkWidget *widget; @@ -1505,6 +1768,7 @@ tearoff_remove (widget, client_data) xg_detached_menus count. WIDGET is the GtkTearoffMenuItem. CLIENT_DATA is not used. */ + static void tearoff_activate (widget, client_data) GtkWidget *widget; @@ -1534,6 +1798,7 @@ tearoff_activate (widget, client_data) in the group. On exit, *GROUP contains the radio item group. Returns the created GtkWidget. */ + static GtkWidget * xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group) widget_value *item; @@ -1665,7 +1930,7 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb, if (deactivate_cb) g_signal_connect (G_OBJECT (wmenu), - "deactivate", deactivate_cb, 0); + "selection-done", deactivate_cb, 0); g_signal_connect (G_OBJECT (wmenu), "grab-notify", G_CALLBACK (menu_grab_callback), 0); @@ -1751,6 +2016,7 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb, HIGHLIGHT_CB is the callback to call when entering/leaving menu items. Returns the widget created. */ + GtkWidget * xg_create_widget (type, name, f, val, select_cb, deactivate_cb, highlight_cb) @@ -1774,6 +2040,7 @@ xg_create_widget (type, name, f, val, GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f))); gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE); gtk_widget_set_name (w, "emacs-dialog"); + gtk_window_set_modal (GTK_WINDOW (w), TRUE); } else if (menu_bar_p || pop_up_p) { @@ -1808,6 +2075,7 @@ xg_create_widget (type, name, f, val, } /* Return the label for menu item WITEM. */ + static const char * xg_get_menu_item_label (witem) GtkMenuItem *witem; @@ -1817,6 +2085,7 @@ xg_get_menu_item_label (witem) } /* Return non-zero if the menu item WITEM has the text LABEL. */ + static int xg_item_label_same_p (witem, label) GtkMenuItem *witem; @@ -1836,10 +2105,10 @@ xg_item_label_same_p (witem, label) return is_same; } -/* Remove widgets in LIST from container WCONT. */ +/* Destroy widgets in LIST. */ + static void -remove_from_container (wcont, list) - GtkWidget *wcont; +xg_destroy_widgets (list) GList *list; { GList *iter; @@ -1848,15 +2117,7 @@ remove_from_container (wcont, list) { GtkWidget *w = GTK_WIDGET (iter->data); - /* Add a ref to w so we can explicitly destroy it later. */ - gtk_widget_ref (w); - gtk_container_remove (GTK_CONTAINER (wcont), w); - - /* If there is a menu under this widget that has been detached, - there is a reference to it, and just removing w from the - container does not destroy the submenu. By explicitly - destroying w we make sure the submenu is destroyed, thus - removing the detached window also if there was one. */ + /* Destroying the widget will remove it from the container it is in. */ gtk_widget_destroy (w); } } @@ -1872,6 +2133,7 @@ remove_from_container (wcont, list) CL_DATA points to the callback data to be used for this menu bar. This function calls itself to walk through the menu bar names. */ + static void xg_update_menubar (menubar, f, list, iter, pos, val, select_cb, highlight_cb, cl_data) @@ -1890,7 +2152,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val, else if (iter && ! val) { /* Item(s) have been removed. Remove all remaining items. */ - remove_from_container (menubar, iter); + xg_destroy_widgets (iter); /* All updated. */ val = 0; @@ -2044,6 +2306,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val, SELECT_CB is the callback to use when a menu item is selected. HIGHLIGHT_CB is the callback to call when entering/leaving menu items. CL_DATA is the data to set in the widget for menu invokation. */ + static void xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data) widget_value *val; @@ -2178,6 +2441,7 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data) } /* Update the toggle menu item W so it corresponds to VAL. */ + static void xg_update_toggle_item (val, w) widget_value *val; @@ -2187,6 +2451,7 @@ xg_update_toggle_item (val, w) } /* Update the radio menu item W so it corresponds to VAL. */ + static void xg_update_radio_item (val, w) widget_value *val; @@ -2316,8 +2581,8 @@ xg_update_submenu (submenu, f, val, { /* If we are adding new menu items below, we must remove from first radio button so that radio groups become correct. */ - if (cur && first_radio) remove_from_container (submenu, first_radio); - else remove_from_container (submenu, iter); + if (cur && first_radio) xg_destroy_widgets (first_radio); + else xg_destroy_widgets (iter); } if (cur) @@ -2349,6 +2614,7 @@ xg_update_submenu (submenu, f, val, SELECT_CB is the callback to use when a menu item is selected. DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore. HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */ + void xg_modify_menubar_widgets (menubar, f, val, deep_p, select_cb, deactivate_cb, highlight_cb) @@ -2371,7 +2637,7 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p, xg_update_menubar (menubar, f, &list, list, 0, val->contents, select_cb, highlight_cb, cl_data); - if (deep_p); + if (deep_p) { widget_value *cur; @@ -2493,12 +2759,14 @@ free_frame_menubar (f) /* Setting scroll bar values invokes the callback. Use this variable to indicate that callback should do nothing. */ + int xg_ignore_gtk_scrollbar; /* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in 32 bits. But we want to store pointers, and they may be larger than 32 bits. Keep a mapping from integer index to widget pointers to get around the 32 bit limitation. */ + static struct { GtkWidget **widgets; @@ -2507,9 +2775,11 @@ static struct } id_to_widget; /* Grow this much every time we need to allocate more */ + #define ID_TO_WIDGET_INCR 32 /* Store the widget pointer W in id_to_widget and return the integer index. */ + static int xg_store_widget_in_map (w) GtkWidget *w; @@ -2548,6 +2818,7 @@ xg_store_widget_in_map (w) /* Remove pointer at IDX from id_to_widget. Called when scroll bar is destroyed. */ + static void xg_remove_widget_from_map (idx) int idx; @@ -2560,6 +2831,7 @@ xg_remove_widget_from_map (idx) } /* Get the widget pointer at IDX from id_to_widget. */ + static GtkWidget * xg_get_widget_from_map (idx) int idx; @@ -2572,6 +2844,7 @@ xg_get_widget_from_map (idx) /* Return the scrollbar id for X Window WID on display DPY. Return -1 if WID not in id_to_widget. */ + int xg_get_scroll_id_for_window (dpy, wid) Display *dpy; @@ -2595,13 +2868,14 @@ xg_get_scroll_id_for_window (dpy, wid) /* Callback invoked when scroll bar WIDGET is destroyed. DATA is the index into id_to_widget for WIDGET. We free pointer to last scroll bar values here and remove the index. */ + static void xg_gtk_scroll_destroy (widget, data) GtkWidget *widget; gpointer data; { gpointer p; - int id = (int)data; + int id = (int) (EMACS_INT) data; /* The EMACS_INT cast avoids a warning. */ p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA); if (p) xfree (p); @@ -2617,6 +2891,7 @@ xg_gtk_scroll_destroy (widget, data) Returns FALSE to tell GTK that it shall continue propagate the event to widgets. */ + static gboolean scroll_bar_button_cb (widget, event, user_data) GtkWidget *widget; @@ -2631,7 +2906,7 @@ scroll_bar_button_cb (widget, event, user_data) if (xg_timer) xg_stop_timer (); bar->dragging = Qnil; } - + return FALSE; } @@ -2641,6 +2916,7 @@ scroll_bar_button_cb (widget, event, user_data) bar changes. SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used to set resources for the widget. */ + void xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name) FRAME_PTR f; @@ -2649,6 +2925,7 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name) char *scroll_bar_name; { GtkWidget *wscroll; + GtkWidget *webox; GtkObject *vadj; int scroll_id; @@ -2658,6 +2935,7 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name) 0.1, 0.1, 0.1); wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj)); + webox = gtk_event_box_new (); gtk_widget_set_name (wscroll, scroll_bar_name); gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS); @@ -2667,10 +2945,11 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name) "value-changed", scroll_callback, (gpointer) bar); + /* The EMACS_INT cast avoids a warning. */ g_signal_connect (G_OBJECT (wscroll), "destroy", G_CALLBACK (xg_gtk_scroll_destroy), - (gpointer) scroll_id); + (gpointer) (EMACS_INT) scroll_id); /* Connect to button press and button release to detect if any scroll bar has the pointer. */ @@ -2683,26 +2962,35 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name) G_CALLBACK (scroll_bar_button_cb), (gpointer) bar); - gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), - wscroll, -1, -1); + /* The scroll bar widget does not draw on a window of its own. Instead + it draws on the parent window, in this case the edit widget. So + whenever the edit widget is cleared, the scroll bar needs to redraw + also, which causes flicker. Put an event box between the edit widget + and the scroll bar, so the scroll bar instead draws itself on the + event box window. */ + gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1); + gtk_container_add (GTK_CONTAINER (webox), wscroll); + /* Set the cursor to an arrow. */ - xg_set_cursor (wscroll, FRAME_X_DISPLAY_INFO (f)->xg_cursor); + xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor); SET_SCROLL_BAR_X_WINDOW (bar, scroll_id); } /* Make the scroll bar represented by SCROLLBAR_ID visible. */ + void xg_show_scroll_bar (scrollbar_id) int scrollbar_id; { GtkWidget *w = xg_get_widget_from_map (scrollbar_id); if (w) - gtk_widget_show (w); + gtk_widget_show_all (gtk_widget_get_parent (w)); } /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */ + void xg_remove_scroll_bar (f, scrollbar_id) FRAME_PTR f; @@ -2711,42 +2999,20 @@ xg_remove_scroll_bar (f, scrollbar_id) GtkWidget *w = xg_get_widget_from_map (scrollbar_id); if (w) { + GtkWidget *wparent = gtk_widget_get_parent (w); gtk_widget_destroy (w); + gtk_widget_destroy (wparent); SET_FRAME_GARBAGED (f); } } -/* Find left/top for widget W in GtkFixed widget WFIXED. */ -static void -xg_find_top_left_in_fixed (w, wfixed, left, top) - GtkWidget *w, *wfixed; - int *left, *top; -{ - GList *iter; - - for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter)) - { - GtkFixedChild *child = (GtkFixedChild *) iter->data; - - if (child->widget == w) - { - *left = child->x; - *top = child->y; - return; - } - } - - /* Shall never end up here. */ - abort (); -} - /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID in frame F. TOP/LEFT are the new pixel positions where the bar shall appear. WIDTH, HEIGHT is the size in pixels the bar shall have. */ + void -xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height, - real_left, canon_width) +xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height) FRAME_PTR f; int scrollbar_id; int top; @@ -2760,102 +3026,17 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height, if (wscroll) { GtkWidget *wfixed = f->output_data.x->edit_widget; - int winextra = canon_width > width ? (canon_width - width) / 2 : 0; - int bottom = top + height; - - gint slider_width; - int oldtop, oldleft, oldbottom; - GtkRequisition req; - - /* Get old values. */ - xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop); - gtk_widget_size_request (wscroll, &req); - oldbottom = oldtop + req.height; - - /* Scroll bars in GTK has a fixed width, so if we say width 16, it - will only be its fixed width (14 is default) anyway, the rest is - blank. We are drawing the mode line across scroll bars when - the frame is split: - |bar| |fringe| - ---------------- - mode line - ---------------- - |bar| |fringe| - - When we "unsplit" the frame: - - |bar| |fringe| - -| |-| | - m¦ |i| | - -| |-| | - | | | | - - - the remains of the mode line can be seen in these blank spaces. - So we must clear them explicitly. - GTK scroll bars should do that, but they don't. - Also, the canonical width may be wider than the width for the - scroll bar so that there is some space (typically 1 pixel) between - the scroll bar and the edge of the window and between the scroll - bar and the fringe. */ - - if (oldtop != -1 && oldleft != -1) - { - int gtkextral, gtkextrah; - int xl, xr, wbl, wbr; - int bottomdiff, topdiff; - - gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL); - gtkextral = width > slider_width ? (width - slider_width) / 2 : 0; - gtkextrah = gtkextral ? (width - slider_width - gtkextral) : 0; - - xl = real_left; - wbl = gtkextral + winextra; - wbr = gtkextrah + winextra; - xr = left + gtkextral + slider_width; - bottomdiff = abs (oldbottom - bottom); - topdiff = abs (oldtop - top); - - if (oldleft != left) - { - gdk_window_clear_area (wfixed->window, xl, top, wbl, height); - gdk_window_clear_area (wfixed->window, xr, top, wbr, height); - } - - if (oldtop > top) - { - gdk_window_clear_area (wfixed->window, xl, top, wbl, topdiff); - gdk_window_clear_area (wfixed->window, xr, top, wbr, topdiff); - } - else if (oldtop < top) - { - gdk_window_clear_area (wfixed->window, xl, oldtop, wbl, topdiff); - gdk_window_clear_area (wfixed->window, xr, oldtop, wbr, topdiff); - } - - if (oldbottom > bottom) - { - gdk_window_clear_area (wfixed->window, xl, bottom, wbl, - bottomdiff); - gdk_window_clear_area (wfixed->window, xr, bottom, wbr, - bottomdiff); - } - else if (oldbottom < bottom) - { - gdk_window_clear_area (wfixed->window, xl, oldbottom, wbl, - bottomdiff); - gdk_window_clear_area (wfixed->window, xr, oldbottom, wbr, - bottomdiff); - } - } + GtkWidget *wparent = gtk_widget_get_parent (wscroll); /* Move and resize to new values. */ - gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top); + gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top); gtk_widget_set_size_request (wscroll, width, height); - - /* Must force out update so changed scroll bars gets redrawn. */ + gtk_widget_queue_draw (wparent); gdk_window_process_all_updates (); - + /* GTK does not redraw until the main loop is entered again, but + if there are no X events pending we will not enter it. So we sync + here to get some events. */ + x_sync (f); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); } @@ -2863,6 +3044,7 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height, /* 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; @@ -2955,12 +3137,14 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) W is the button widget in the tool bar that got pressed, CLIENT_DATA is an integer that is the index of the button in the tool bar. 0 is the first button. */ + static void xg_tool_bar_callback (w, client_data) GtkWidget *w; gpointer client_data; { - int idx = (int)client_data; + /* The EMACS_INT cast avoids a warning. */ + int idx = (int) (EMACS_INT) client_data; FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA); Lisp_Object key, frame; struct input_event event; @@ -2991,6 +3175,7 @@ xg_tool_bar_callback (w, client_data) WBOX is the handle box widget that enables detach/attach of the tool bar. W is the tool bar widget. CLIENT_DATA is a pointer to the frame the tool bar belongs to. */ + static void xg_tool_bar_detach_callback (wbox, w, client_data) GtkHandleBox *wbox; @@ -3001,10 +3186,11 @@ xg_tool_bar_detach_callback (wbox, w, client_data) if (f) { + FRAME_X_OUTPUT (f)->toolbar_detached = 1; + /* When detaching a tool bar, not everything dissapear. There are a few pixels left that are used to drop the tool bar back into place. */ - int bw = gtk_container_get_border_width (GTK_CONTAINER (wbox)); FRAME_TOOLBAR_HEIGHT (f) = 2; /* The height has changed, resize outer widget and set columns @@ -3019,6 +3205,7 @@ xg_tool_bar_detach_callback (wbox, w, client_data) WBOX is the handle box widget that enables detach/attach of the tool bar. W is the tool bar widget. CLIENT_DATA is a pointer to the frame the tool bar belongs to. */ + static void xg_tool_bar_attach_callback (wbox, w, client_data) GtkHandleBox *wbox; @@ -3031,11 +3218,13 @@ xg_tool_bar_attach_callback (wbox, w, client_data) { GtkRequisition req; + FRAME_X_OUTPUT (f)->toolbar_detached = 0; + gtk_widget_size_request (w, &req); FRAME_TOOLBAR_HEIGHT (f) = req.height; /* The height has changed, resize outer widget and set columns - rows to what we had before detaching the tool bar. */ + rows to what we had before attaching the tool bar. */ xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f)); } } @@ -3048,13 +3237,15 @@ xg_tool_bar_attach_callback (wbox, w, client_data) tool bar. 0 is the first button. Returns FALSE to tell GTK to keep processing this event. */ + static gboolean xg_tool_bar_help_callback (w, event, client_data) GtkWidget *w; GdkEventCrossing *event; gpointer client_data; { - int idx = (int)client_data; + /* The EMACS_INT cast avoids a warning. */ + int idx = (int) (EMACS_INT) client_data; FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA); Lisp_Object help, frame; @@ -3093,6 +3284,7 @@ xg_tool_bar_help_callback (w, event, client_data) 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; @@ -3108,10 +3300,10 @@ xg_tool_bar_item_expose_callback (w, event, client_data) event->area.x = max (0, event->area.x); event->area.y = max (0, event->area.y); - + event->area.width = max (width, event->area.width); event->area.height = max (height, event->area.height); - + return FALSE; } @@ -3123,6 +3315,7 @@ xg_tool_bar_item_expose_callback (w, event, client_data) 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; @@ -3133,6 +3326,8 @@ xg_tool_bar_expose_callback (w, event, client_data) return FALSE; } +/* Create a tool bar for frame F. */ + static void xg_create_tool_bar (f) FRAME_PTR f; @@ -3143,6 +3338,8 @@ xg_create_tool_bar (f) x->toolbar_widget = gtk_toolbar_new (); x->handlebox_widget = gtk_handle_box_new (); + x->toolbar_detached = 0; + gtk_container_add (GTK_CONTAINER (x->handlebox_widget), x->toolbar_widget); @@ -3186,6 +3383,8 @@ xg_create_tool_bar (f) SET_FRAME_GARBAGED (f); } +/* Update the tool bar for frame F. Add new buttons and remove old. */ + void update_frame_tool_bar (f) FRAME_PTR f; @@ -3195,12 +3394,37 @@ update_frame_tool_bar (f) GList *icon_list; GList *iter; struct x_output *x = f->output_data.x; + int hmargin, vmargin; if (! FRAME_GTK_WIDGET (f)) return; BLOCK_INPUT; + if (INTEGERP (Vtool_bar_button_margin) + && XINT (Vtool_bar_button_margin) > 0) + { + hmargin = XFASTINT (Vtool_bar_button_margin); + vmargin = XFASTINT (Vtool_bar_button_margin); + } + else if (CONSP (Vtool_bar_button_margin)) + { + if (INTEGERP (XCAR (Vtool_bar_button_margin)) + && XINT (XCAR (Vtool_bar_button_margin)) > 0) + hmargin = XFASTINT (XCAR (Vtool_bar_button_margin)); + + if (INTEGERP (XCDR (Vtool_bar_button_margin)) + && XINT (XCDR (Vtool_bar_button_margin)) > 0) + vmargin = XFASTINT (XCDR (Vtool_bar_button_margin)); + } + + /* The natural size (i.e. when GTK uses 0 as margin) looks best, + so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK", + i.e. zero. This means that margins less than + DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */ + hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN); + vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN); + if (! x->toolbar_widget) xg_create_tool_bar (f); @@ -3262,17 +3486,16 @@ update_frame_tool_bar (f) if (! wicon) { - GdkPixmap *gpix; - GdkBitmap *gmask; - GtkWidget *w; + GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL); - xg_get_gdk_pixmap_and_mask (f, img, &gpix, &gmask); - w = gtk_image_new_from_pixmap (gpix, gmask); + gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin); + + /* The EMACS_INT cast avoids a warning. */ gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget), 0, 0, 0, w, GTK_SIGNAL_FUNC (xg_tool_bar_callback), - (gpointer)i); + (gpointer) (EMACS_INT) i); /* Save the image so we can see if an update is needed when this function is called again. */ @@ -3302,14 +3525,15 @@ update_frame_tool_bar (f) rather than the GtkButton specific signals "enter" and "leave", so we can have only one callback. The event will tell us what kind of event it is. */ + /* The EMACS_INT cast avoids a warning. */ g_signal_connect (G_OBJECT (w), "enter-notify-event", G_CALLBACK (xg_tool_bar_help_callback), - (gpointer)i); + (gpointer) (EMACS_INT) i); g_signal_connect (G_OBJECT (w), "leave-notify-event", G_CALLBACK (xg_tool_bar_help_callback), - (gpointer)i); + (gpointer) (EMACS_INT) i); } } else @@ -3323,14 +3547,10 @@ update_frame_tool_bar (f) XG_TOOL_BAR_IMAGE_DATA); g_list_free (chlist); - if (old_img != img->pixmap) - { - GdkPixmap *gpix; - GdkBitmap *gmask; + gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin); - xg_get_gdk_pixmap_and_mask (f, img, &gpix, &gmask); - gtk_image_set_from_pixmap (wimage, gpix, gmask); - } + if (old_img != img->pixmap) + (void) xg_get_image_for_pixmap (f, img, x->widget, wimage); g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA, (gpointer)img->pixmap); @@ -3352,7 +3572,8 @@ update_frame_tool_bar (f) } gtk_widget_size_request (x->toolbar_widget, &new_req); - if (old_req.height != new_req.height) + if (old_req.height != new_req.height + && ! FRAME_X_OUTPUT (f)->toolbar_detached) { FRAME_TOOLBAR_HEIGHT (f) = new_req.height; xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f)); @@ -3363,6 +3584,9 @@ update_frame_tool_bar (f) UNBLOCK_INPUT; } +/* Deallocate all resources for the tool bar on frame F. + Remove the tool bar. */ + void free_frame_tool_bar (f) FRAME_PTR f; @@ -3395,6 +3619,8 @@ free_frame_tool_bar (f) void xg_initialize () { + GtkBindingSet *binding_set; + xg_ignore_gtk_scrollbar = 0; xg_detached_menus = 0; xg_menu_cb_list.prev = xg_menu_cb_list.next = @@ -3417,6 +3643,17 @@ xg_initialize () "gtk-key-theme-name", "Emacs", EMACS_CLASS); + + /* Make dialogs close on C-g. Since file dialog inherits from + dialog, this works for them also. */ + binding_set = gtk_binding_set_by_class (gtk_type_class (GTK_TYPE_DIALOG)); + gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK, + "close", 0); + + /* Make menus close on C-g. */ + binding_set = gtk_binding_set_by_class (gtk_type_class (GTK_TYPE_MENU_SHELL)); + gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK, + "cancel", 0); } #endif /* USE_GTK */