nautilus-3.6.3/eel/eel-gtk-extensions.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 
  3 /* eel-gtk-extensions.c - implementation of new functions that operate on
  4   			  gtk classes. Perhaps some of these should be
  5   			  rolled into gtk someday.
  6 
  7    Copyright (C) 1999, 2000, 2001 Eazel, Inc.
  8 
  9    The Gnome Library is free software; you can redistribute it and/or
 10    modify it under the terms of the GNU Library General Public License as
 11    published by the Free Software Foundation; either version 2 of the
 12    License, or (at your option) any later version.
 13 
 14    The Gnome Library is distributed in the hope that it will be useful,
 15    but WITHOUT ANY WARRANTY; without even the implied warranty of
 16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17    Library General Public License for more details.
 18 
 19    You should have received a copy of the GNU Library General Public
 20    License along with the Gnome Library; see the file COPYING.LIB.  If not,
 21    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 22    Boston, MA 02111-1307, USA.
 23 
 24    Authors: John Sullivan <sullivan@eazel.com>
 25             Ramiro Estrugo <ramiro@eazel.com>
 26 	    Darin Adler <darin@eazel.com>
 27 */
 28 
 29 #include <config.h>
 30 #include "eel-gtk-extensions.h"
 31 
 32 #include "eel-glib-extensions.h"
 33 #include "eel-gnome-extensions.h"
 34 #include "eel-string.h"
 35 
 36 #include <X11/Xlib.h>
 37 #include <X11/Xatom.h>
 38 #include <gdk/gdk.h>
 39 #include <gdk/gdkprivate.h>
 40 #include <gdk/gdkx.h>
 41 #include <gtk/gtk.h>
 42 #include <glib/gi18n-lib.h>
 43 #include <math.h>
 44 
 45 /* This number is fairly arbitrary. Long enough to show a pretty long
 46  * menu title, but not so long to make a menu grotesquely wide.
 47  */
 48 #define MAXIMUM_MENU_TITLE_LENGTH	48
 49 
 50 /* Used for window position & size sanity-checking. The sizes are big enough to prevent
 51  * at least normal-sized gnome panels from obscuring the window at the screen edges. 
 52  */
 53 #define MINIMUM_ON_SCREEN_WIDTH		100
 54 #define MINIMUM_ON_SCREEN_HEIGHT	100
 55 
 56 
 57 /**
 58  * eel_gtk_window_get_geometry_string:
 59  * @window: a #GtkWindow
 60  * 
 61  * Obtains the geometry string for this window, suitable for
 62  * set_geometry_string(); assumes the window has NorthWest gravity
 63  * 
 64  * Return value: geometry string, must be freed
 65  **/
 66 char*
 67 eel_gtk_window_get_geometry_string (GtkWindow *window)
 68 {
 69 	char *str;
 70 	int w, h, x, y;
 71 	
 72 	g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
 73 	g_return_val_if_fail (gtk_window_get_gravity (window) ==
 74 			      GDK_GRAVITY_NORTH_WEST, NULL);
 75 
 76 	gtk_window_get_position (window, &x, &y);
 77 	gtk_window_get_size (window, &w, &h);
 78 	
 79 	str = g_strdup_printf ("%dx%d+%d+%d", w, h, x, y);
 80 
 81 	return str;
 82 }
 83 
 84 static void
 85 sanity_check_window_position (int *left, int *top)
 86 {
 87 	g_assert (left != NULL);
 88 	g_assert (top != NULL);
 89 
 90 	/* Make sure the top of the window is on screen, for
 91 	 * draggability (might not be necessary with all window managers,
 92 	 * but seems reasonable anyway). Make sure the top of the window
 93 	 * isn't off the bottom of the screen, or so close to the bottom
 94 	 * that it might be obscured by the panel.
 95 	 */
 96 	*top = CLAMP (*top, 0, gdk_screen_height() - MINIMUM_ON_SCREEN_HEIGHT);
 97 	
 98 	/* FIXME bugzilla.eazel.com 669: 
 99 	 * If window has negative left coordinate, set_uposition sends it
100 	 * somewhere else entirely. Not sure what level contains this bug (XWindows?).
101 	 * Hacked around by pinning the left edge to zero, which just means you
102 	 * can't set a window to be partly off the left of the screen using
103 	 * this routine.
104 	 */
105 	/* Make sure the left edge of the window isn't off the right edge of
106 	 * the screen, or so close to the right edge that it might be
107 	 * obscured by the panel.
108 	 */
109 	*left = CLAMP (*left, 0, gdk_screen_width() - MINIMUM_ON_SCREEN_WIDTH);
110 }
111 
112 static void
113 sanity_check_window_dimensions (guint *width, guint *height)
114 {
115 	g_assert (width != NULL);
116 	g_assert (height != NULL);
117 
118 	/* Pin the size of the window to the screen, so we don't end up in
119 	 * a state where the window is so big essential parts of it can't
120 	 * be reached (might not be necessary with all window managers,
121 	 * but seems reasonable anyway).
122 	 */
123 	*width = MIN (*width, gdk_screen_width());
124 	*height = MIN (*height, gdk_screen_height());
125 }
126 
127 /**
128  * eel_gtk_window_set_initial_geometry:
129  * 
130  * Sets the position and size of a GtkWindow before the
131  * GtkWindow is shown. It is an error to call this on a window that
132  * is already on-screen. Takes into account screen size, and does
133  * some sanity-checking on the passed-in values.
134  * 
135  * @window: A non-visible GtkWindow
136  * @geometry_flags: A EelGdkGeometryFlags value defining which of
137  * the following parameters have defined values
138  * @left: pixel coordinate for left of window
139  * @top: pixel coordinate for top of window
140  * @width: width of window in pixels
141  * @height: height of window in pixels
142  */
143 static void
144 eel_gtk_window_set_initial_geometry (GtkWindow *window, 
145 					  EelGdkGeometryFlags geometry_flags,
146 					  int left,
147 					  int top,
148 					  guint width,
149 					  guint height)
150 {
151 	GdkScreen *screen;
152 	int real_left, real_top;
153 	int screen_width, screen_height;
154 
155 	g_return_if_fail (GTK_IS_WINDOW (window));
156 
157 	/* Setting the default size doesn't work when the window is already showing.
158 	 * Someday we could make this move an already-showing window, but we don't
159 	 * need that functionality yet. 
160 	 */
161 	g_return_if_fail (!gtk_widget_get_visible (GTK_WIDGET (window)));
162 
163 	if ((geometry_flags & EEL_GDK_X_VALUE) && (geometry_flags & EEL_GDK_Y_VALUE)) {
164 		real_left = left;
165 		real_top = top;
166 
167 		screen = gtk_window_get_screen (window);
168 		screen_width  = gdk_screen_get_width  (screen);
169 		screen_height = gdk_screen_get_height (screen);
170 
171 		/* This is sub-optimal. GDK doesn't allow us to set win_gravity
172 		 * to South/East types, which should be done if using negative
173 		 * positions (so that the right or bottom edge of the window
174 		 * appears at the specified position, not the left or top).
175 		 * However it does seem to be consistent with other GNOME apps.
176 		 */
177 		if (geometry_flags & EEL_GDK_X_NEGATIVE) {
178 			real_left = screen_width - real_left;
179 		}
180 		if (geometry_flags & EEL_GDK_Y_NEGATIVE) {
181 			real_top = screen_height - real_top;
182 		}
183 
184 		sanity_check_window_position (&real_left, &real_top);
185 		gtk_window_move (window, real_left, real_top);
186 	}
187 
188 	if ((geometry_flags & EEL_GDK_WIDTH_VALUE) && (geometry_flags & EEL_GDK_HEIGHT_VALUE)) {
189 		sanity_check_window_dimensions (&width, &height);
190 		gtk_window_set_default_size (GTK_WINDOW (window), (int)width, (int)height);
191 	}
192 }
193 
194 /**
195  * eel_gtk_window_set_initial_geometry_from_string:
196  * 
197  * Sets the position and size of a GtkWindow before the
198  * GtkWindow is shown. The geometry is passed in as a string. 
199  * It is an error to call this on a window that
200  * is already on-screen. Takes into account screen size, and does
201  * some sanity-checking on the passed-in values.
202  * 
203  * @window: A non-visible GtkWindow
204  * @geometry_string: A string suitable for use with eel_gdk_parse_geometry
205  * @minimum_width: If the width from the string is smaller than this,
206  * use this for the width.
207  * @minimum_height: If the height from the string is smaller than this,
208  * use this for the height.
209  * @ignore_position: If true position data from string will be ignored.
210  */
211 void
212 eel_gtk_window_set_initial_geometry_from_string (GtkWindow *window, 
213 						 const char *geometry_string,
214 						 guint minimum_width,
215 						 guint minimum_height,
216 						 gboolean ignore_position)
217 {
218 	int left, top;
219 	guint width, height;
220 	EelGdkGeometryFlags geometry_flags;
221 
222 	g_return_if_fail (GTK_IS_WINDOW (window));
223 	g_return_if_fail (geometry_string != NULL);
224 
225 	/* Setting the default size doesn't work when the window is already showing.
226 	 * Someday we could make this move an already-showing window, but we don't
227 	 * need that functionality yet. 
228 	 */
229 	g_return_if_fail (!gtk_widget_get_visible (GTK_WIDGET (window)));
230 
231 	geometry_flags = eel_gdk_parse_geometry (geometry_string, &left, &top, &width, &height);
232 
233 	/* Make sure the window isn't smaller than makes sense for this window.
234 	 * Other sanity checks are performed in set_initial_geometry.
235 	 */
236 	if (geometry_flags & EEL_GDK_WIDTH_VALUE) {
237 		width = MAX (width, minimum_width);
238 	}
239 	if (geometry_flags & EEL_GDK_HEIGHT_VALUE) {
240 		height = MAX (height, minimum_height);
241 	}
242 	
243 	/* Ignore saved window position if requested. */
244 	if (ignore_position) {
245 		geometry_flags &= ~(EEL_GDK_X_VALUE | EEL_GDK_Y_VALUE);
246 	}
247 
248 	eel_gtk_window_set_initial_geometry (window, geometry_flags, left, top, width, height);
249 }
250 
251 /**
252  * eel_pop_up_context_menu:
253  * 
254  * Pop up a context menu under the mouse.
255  * The menu is sunk after use, so it will be destroyed unless the 
256  * caller first ref'ed it.
257  * 
258  * This function is more of a helper function than a gtk extension,
259  * so perhaps it belongs in a different file.
260  * 
261  * @menu: The menu to pop up under the mouse.
262  * @offset_x: Ignored.
263  * @offset_y: Ignored.
264  * @event: The event that invoked this popup menu, or #NULL if there
265  * is no event available.  This is used to get the timestamp for the menu's popup.
266  * In case no event is provided, gtk_get_current_event_time() will be used automatically.
267  **/
268 void 
269 eel_pop_up_context_menu (GtkMenu *menu,
270 			 GdkEventButton *event)
271 {
272 	int button;
273 
274 	g_return_if_fail (GTK_IS_MENU (menu));
275 
276 	/* The event button needs to be 0 if we're popping up this menu from
277 	 * a button release, else a 2nd click outside the menu with any button
278 	 * other than the one that invoked the menu will be ignored (instead
279 	 * of dismissing the menu). This is a subtle fragility of the GTK menu code.
280 	 */
281 
282 	if (event) {
283 		button = event->type == GDK_BUTTON_RELEASE
284 			? 0
285 			: event->button;
286 	} else {
287 		button = 0;
288 	}
289 	
290 	gtk_menu_popup (menu,					/* menu */
291 			NULL,					/* parent_menu_shell */
292 			NULL,					/* parent_menu_item */
293 			NULL,					/* popup_position_func */
294 			NULL,					/* popup_position_data */
295 			button,					/* button */
296 			event ? event->time : gtk_get_current_event_time ()); /* activate_time */
297 
298 	g_object_ref_sink (menu);
299 	g_object_unref (menu);
300 }
301 
302 GtkMenuItem *
303 eel_gtk_menu_append_separator (GtkMenu *menu)
304 {
305 	return eel_gtk_menu_insert_separator (menu, -1);
306 }
307 
308 GtkMenuItem *
309 eel_gtk_menu_insert_separator (GtkMenu *menu, int index)
310 {
311 	GtkWidget *menu_item;
312 
313 	menu_item = gtk_separator_menu_item_new ();
314 	gtk_widget_show (menu_item);
315 	gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menu_item, index);
316 
317 	return GTK_MENU_ITEM (menu_item);
318 }
319 
320 static gboolean 
321 tree_view_button_press_callback (GtkWidget *tree_view,
322 				 GdkEventButton *event,
323 				 gpointer data)
324 {
325 	GtkTreePath *path;
326 	GtkTreeViewColumn *column;
327 
328 	if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
329 		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view),
330 						   event->x, event->y,
331 						   &path,
332 						   &column,
333 						   NULL, 
334 						   NULL)) {
335 			gtk_tree_view_row_activated
336 				(GTK_TREE_VIEW (tree_view), path, column);
337 			gtk_tree_path_free (path);
338 		}
339 	}
340 
341 	return FALSE;
342 }
343 
344 void
345 eel_gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view,
346 						gboolean should_activate)
347 {
348 	guint button_press_id;
349 
350 	button_press_id = GPOINTER_TO_UINT 
351 		(g_object_get_data (G_OBJECT (tree_view), 
352 				    "eel-tree-view-activate"));
353 
354 	if (button_press_id && !should_activate) {
355 		g_signal_handler_disconnect (tree_view, button_press_id);
356 		g_object_set_data (G_OBJECT (tree_view), 
357 				   "eel-tree-view-activate", 
358 				   NULL);
359 	} else if (!button_press_id && should_activate) {
360 		button_press_id = g_signal_connect 
361 			(tree_view,
362 			 "button_press_event",
363 			 G_CALLBACK  (tree_view_button_press_callback),
364 			 NULL);
365 		g_object_set_data (G_OBJECT (tree_view), 
366 				   "eel-tree-view-activate", 
367 				   GUINT_TO_POINTER (button_press_id));
368 	}
369 }
370 
371 void
372 eel_gtk_message_dialog_set_details_label (GtkMessageDialog *dialog,
373 				  const gchar *details_text)
374 {
375 	GtkWidget *content_area, *expander, *label;
376 
377 	content_area = gtk_message_dialog_get_message_area (dialog);
378 	expander = gtk_expander_new_with_mnemonic (_("Show more _details"));
379 	gtk_expander_set_spacing (GTK_EXPANDER (expander), 6);
380 
381 	label = gtk_label_new (details_text);
382 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
383 	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
384 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
385 
386 	gtk_container_add (GTK_CONTAINER (expander), label);
387 	gtk_box_pack_start (GTK_BOX (content_area), expander, FALSE, FALSE, 0);
388 
389 	gtk_widget_show (label);
390 	gtk_widget_show (expander);
391 }