1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3 *
4 * This library is free software: you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #ifdef GDK_DISABLE_DEPRECATED
22 #undef GDK_DISABLE_DEPRECATED
23 #endif
24 #include <gtk/gtk.h>
25
26 #ifdef GDK_WINDOWING_WIN32
27 #include <gdk/gdkwin32.h>
28 #endif
29
30 #ifdef GDK_WINDOWING_X11
31 #include <gdk/gdkx.h>
32 #endif
33
34 #ifdef GDK_WINDOWING_QUARTZ
35 #include <Cocoa/Cocoa.h>
36 #endif
37
38 #include "gimp.h"
39 #include "gimpui.h"
40
41 #include "libgimpmodule/gimpmodule.h"
42
43 #include "libgimpwidgets/gimpwidgets.h"
44 #include "libgimpwidgets/gimpwidgets-private.h"
45
46
47 /**
48 * SECTION: gimpui
49 * @title: gimpui
50 * @short_description: Common user interface functions. This header includes
51 * all other GIMP User Interface Library headers.
52 * @see_also: gtk_init(), gdk_set_use_xshm(), gdk_rgb_get_visual(),
53 * gdk_rgb_get_cmap(), gtk_widget_set_default_visual(),
54 * gtk_widget_set_default_colormap(), gtk_preview_set_gamma().
55 *
56 * Common user interface functions. This header includes all other
57 * GIMP User Interface Library headers.
58 **/
59
60
61 /* local function prototypes */
62
63 static void gimp_ui_help_func (const gchar *help_id,
64 gpointer help_data);
65 static void gimp_ensure_modules (void);
66 static void gimp_window_transient_realized (GtkWidget *window,
67 GdkWindow *parent);
68 static gboolean gimp_window_set_transient_for (GtkWindow *window,
69 GdkWindow *parent);
70
71 static void gimp_ui_theme_changed (void);
72 static void gimp_ui_fix_pixbuf_style (void);
73 static void gimp_ui_draw_pixbuf_layout (GtkStyle *style,
74 GdkWindow *window,
75 GtkStateType state_type,
76 gboolean use_text,
77 GdkRectangle *area,
78 GtkWidget *widget,
79 const gchar *detail,
80 gint x,
81 gint y,
82 PangoLayout *layout);
83 #ifdef GDK_WINDOWING_QUARTZ
84 static gboolean gimp_osx_focus_window (gpointer);
85 #endif
86
87 static gboolean gimp_ui_initialized = FALSE;
88
89
90 /* public functions */
91
92 /**
93 * gimp_ui_init:
94 * @prog_name: The name of the plug-in which will be passed as argv[0] to
95 * gtk_init(). It's a convention to use the name of the
96 * executable and _not_ the PDB procedure name.
97 * @preview: This parameter is unused and exists for historical
98 * reasons only.
99 *
100 * This function initializes GTK+ with gtk_init() and initializes GDK's
101 * image rendering subsystem (GdkRGB) to follow the GIMP main program's
102 * colormap allocation/installation policy.
103 *
104 * It also sets up various other things so that the plug-in user looks
105 * and behaves like the GIMP core. This includes selecting the GTK+
106 * theme and setting up the help system as chosen in the GIMP
107 * preferences. Any plug-in that provides a user interface should call
108 * this function.
109 **/
110 void
gimp_ui_init(const gchar * prog_name,gboolean preview)111 gimp_ui_init (const gchar *prog_name,
112 gboolean preview)
113 {
114 GdkScreen *screen;
115 const gchar *display_name;
116 gchar *themerc;
117 GFileMonitor *rc_monitor;
118 GFile *file;
119
120 g_return_if_fail (prog_name != NULL);
121
122 if (gimp_ui_initialized)
123 return;
124
125 g_set_prgname (prog_name);
126
127 display_name = gimp_display_name ();
128
129 if (display_name)
130 {
131 #if defined (GDK_WINDOWING_X11)
132 g_setenv ("DISPLAY", display_name, TRUE);
133 #else
134 g_setenv ("GDK_DISPLAY", display_name, TRUE);
135 #endif
136 }
137
138 if (gimp_user_time ())
139 {
140 /* Construct a fake startup ID as we only want to pass the
141 * interaction timestamp, see _gdk_windowing_set_default_display().
142 */
143 gchar *startup_id = g_strdup_printf ("_TIME%u", gimp_user_time ());
144
145 g_setenv ("DESKTOP_STARTUP_ID", startup_id, TRUE);
146 g_free (startup_id);
147 }
148
149 gtk_init (NULL, NULL);
150
151 themerc = gimp_personal_rc_file ("themerc");
152 gtk_rc_parse (themerc);
153
154 file = g_file_new_for_path (themerc);
155 g_free (themerc);
156
157 rc_monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL);
158 g_object_unref (file);
159
160 g_signal_connect (rc_monitor, "changed",
161 G_CALLBACK (gimp_ui_theme_changed),
162 NULL);
163
164 gdk_set_program_class (gimp_wm_class ());
165
166 screen = gdk_screen_get_default ();
167 gtk_widget_set_default_colormap (gdk_screen_get_rgb_colormap (screen));
168
169 if (gimp_icon_theme_dir ())
170 {
171 file = g_file_new_for_path (gimp_icon_theme_dir ());
172 gimp_icons_set_icon_theme (file);
173 g_object_unref (file);
174 }
175
176 gimp_widgets_init (gimp_ui_help_func,
177 gimp_context_get_foreground,
178 gimp_context_get_background,
179 gimp_ensure_modules);
180
181 if (! gimp_show_tool_tips ())
182 gimp_help_disable_tooltips ();
183
184 gimp_dialogs_show_help_button (gimp_show_help_button ());
185
186 #ifdef GDK_WINDOWING_QUARTZ
187 g_idle_add (gimp_osx_focus_window, NULL);
188 #endif
189
190 gimp_ui_fix_pixbuf_style ();
191 gimp_ui_initialized = TRUE;
192 }
193
194 static GdkWindow *
gimp_ui_get_foreign_window(guint32 window)195 gimp_ui_get_foreign_window (guint32 window)
196 {
197 #ifdef GDK_WINDOWING_X11
198 return gdk_x11_window_foreign_new_for_display (gdk_display_get_default (),
199 window);
200 #endif
201
202 #ifdef GDK_WINDOWING_WIN32
203 return gdk_win32_window_foreign_new_for_display (gdk_display_get_default (),
204 (HWND) (uintptr_t) window);
205 #endif
206
207 return NULL;
208 }
209
210 /**
211 * gimp_ui_get_display_window:
212 * @gdisp_ID: a GimpDisplay ID.
213 *
214 * Returns the #GdkWindow of a display window. The purpose is to allow
215 * to make plug-in dialogs transient to the image display as explained
216 * with gdk_window_set_transient_for().
217 *
218 * You shouldn't have to call this function directly. Use
219 * gimp_window_set_transient_for_display() instead.
220 *
221 * Return value: A reference to a #GdkWindow or %NULL. You should
222 * unref the window using g_object_unref() as soon as
223 * you don't need it any longer.
224 *
225 * Since: 2.4
226 */
227 GdkWindow *
gimp_ui_get_display_window(guint32 gdisp_ID)228 gimp_ui_get_display_window (guint32 gdisp_ID)
229 {
230 guint32 window;
231
232 g_return_val_if_fail (gimp_ui_initialized, NULL);
233
234 window = gimp_display_get_window_handle (gdisp_ID);
235 if (window)
236 return gimp_ui_get_foreign_window (window);
237
238 return NULL;
239 }
240
241 /**
242 * gimp_ui_get_progress_window:
243 *
244 * Returns the #GdkWindow of the window this plug-in's progress bar is
245 * shown in. Use it to make plug-in dialogs transient to this window
246 * as explained with gdk_window_set_transient_for().
247 *
248 * You shouldn't have to call this function directly. Use
249 * gimp_window_set_transient() instead.
250 *
251 * Return value: A reference to a #GdkWindow or %NULL. You should
252 * unref the window using g_object_unref() as soon as
253 * you don't need it any longer.
254 *
255 * Since: 2.4
256 */
257 GdkWindow *
gimp_ui_get_progress_window(void)258 gimp_ui_get_progress_window (void)
259 {
260 guint32 window;
261
262 g_return_val_if_fail (gimp_ui_initialized, NULL);
263
264 window = gimp_progress_get_window_handle ();
265 if (window)
266 return gimp_ui_get_foreign_window (window);
267
268 return NULL;
269 }
270
271 #ifdef GDK_WINDOWING_QUARTZ
272 static void
gimp_window_transient_show(GtkWidget * window)273 gimp_window_transient_show (GtkWidget *window)
274 {
275 g_signal_handlers_disconnect_by_func (window,
276 gimp_window_transient_show,
277 NULL);
278 [NSApp arrangeInFront: nil];
279 }
280 #endif
281
282 /**
283 * gimp_window_set_transient_for_display:
284 * @window: the #GtkWindow that should become transient
285 * @gdisp_ID: display ID of the image window that should become the parent
286 *
287 * Indicates to the window manager that @window is a transient dialog
288 * associated with the GIMP image window that is identified by it's
289 * display ID. See gdk_window_set_transient_for () for more information.
290 *
291 * Most of the time you will want to use the convenience function
292 * gimp_window_set_transient().
293 *
294 * Since: 2.4
295 */
296 void
gimp_window_set_transient_for_display(GtkWindow * window,guint32 gdisp_ID)297 gimp_window_set_transient_for_display (GtkWindow *window,
298 guint32 gdisp_ID)
299 {
300 g_return_if_fail (gimp_ui_initialized);
301 g_return_if_fail (GTK_IS_WINDOW (window));
302
303 if (! gimp_window_set_transient_for (window,
304 gimp_ui_get_display_window (gdisp_ID)))
305 {
306 /* if setting the window transient failed, at least set
307 * WIN_POS_CENTER, which will center the window on the screen
308 * where the mouse is (see bug #684003).
309 */
310 gtk_window_set_position (window, GTK_WIN_POS_CENTER);
311
312 #ifdef GDK_WINDOWING_QUARTZ
313 g_signal_connect (window, "show",
314 G_CALLBACK (gimp_window_transient_show),
315 NULL);
316 #endif
317 }
318 }
319
320 /**
321 * gimp_window_set_transient:
322 * @window: the #GtkWindow that should become transient
323 *
324 * Indicates to the window manager that @window is a transient dialog
325 * associated with the GIMP window that the plug-in has been
326 * started from. See also gimp_window_set_transient_for_display().
327 *
328 * Since: 2.4
329 */
330 void
gimp_window_set_transient(GtkWindow * window)331 gimp_window_set_transient (GtkWindow *window)
332 {
333 g_return_if_fail (gimp_ui_initialized);
334 g_return_if_fail (GTK_IS_WINDOW (window));
335
336 if (! gimp_window_set_transient_for (window, gimp_ui_get_progress_window ()))
337 {
338 /* see above */
339 gtk_window_set_position (window, GTK_WIN_POS_CENTER);
340
341 #ifdef GDK_WINDOWING_QUARTZ
342 g_signal_connect (window, "show",
343 G_CALLBACK (gimp_window_transient_show),
344 NULL);
345 #endif
346 }
347 }
348
349
350 /* private functions */
351
352 static void
gimp_ui_help_func(const gchar * help_id,gpointer help_data)353 gimp_ui_help_func (const gchar *help_id,
354 gpointer help_data)
355 {
356 gimp_help (NULL, help_id);
357 }
358
359 static void
gimp_ensure_modules(void)360 gimp_ensure_modules (void)
361 {
362 static GimpModuleDB *module_db = NULL;
363
364 if (! module_db)
365 {
366 gchar *load_inhibit = gimp_get_module_load_inhibit ();
367 gchar *module_path = gimp_gimprc_query ("module-path");
368
369 module_db = gimp_module_db_new (FALSE);
370
371 gimp_module_db_set_load_inhibit (module_db, load_inhibit);
372 gimp_module_db_load (module_db, module_path);
373
374 g_free (module_path);
375 g_free (load_inhibit);
376 }
377 }
378
379 static void
gimp_window_transient_realized(GtkWidget * window,GdkWindow * parent)380 gimp_window_transient_realized (GtkWidget *window,
381 GdkWindow *parent)
382 {
383 if (gtk_widget_get_realized (window))
384 gdk_window_set_transient_for (gtk_widget_get_window (window), parent);
385 }
386
387 static gboolean
gimp_window_set_transient_for(GtkWindow * window,GdkWindow * parent)388 gimp_window_set_transient_for (GtkWindow *window,
389 GdkWindow *parent)
390 {
391 gtk_window_set_transient_for (window, NULL);
392
393 #ifndef GDK_WINDOWING_WIN32
394 g_signal_handlers_disconnect_matched (window, G_SIGNAL_MATCH_FUNC,
395 0, 0, NULL,
396 gimp_window_transient_realized,
397 NULL);
398
399 if (! parent)
400 return FALSE;
401
402 if (gtk_widget_get_realized (GTK_WIDGET (window)))
403 gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (window)),
404 parent);
405
406 g_signal_connect_object (window, "realize",
407 G_CALLBACK (gimp_window_transient_realized),
408 parent, 0);
409 g_object_unref (parent);
410
411 return TRUE;
412 #endif
413
414 return FALSE;
415 }
416
417 static void
gimp_ui_theme_changed(void)418 gimp_ui_theme_changed (void)
419 {
420 gtk_rc_reparse_all ();
421
422 gimp_ui_fix_pixbuf_style ();
423 }
424
425 static void
gimp_ui_fix_pixbuf_style(void)426 gimp_ui_fix_pixbuf_style (void)
427 {
428 /* Same hack as in app/gui/themes.c, to be removed for GTK+ 3.x */
429
430 static GtkStyleClass *pixbuf_style_class = NULL;
431
432 if (! pixbuf_style_class)
433 {
434 GType type = g_type_from_name ("PixbufStyle");
435
436 if (type)
437 {
438 pixbuf_style_class = g_type_class_ref (type);
439
440 if (pixbuf_style_class)
441 pixbuf_style_class->draw_layout = gimp_ui_draw_pixbuf_layout;
442 }
443 }
444 }
445
446 static void
gimp_ui_draw_pixbuf_layout(GtkStyle * style,GdkWindow * window,GtkStateType state_type,gboolean use_text,GdkRectangle * area,GtkWidget * widget,const gchar * detail,gint x,gint y,PangoLayout * layout)447 gimp_ui_draw_pixbuf_layout (GtkStyle *style,
448 GdkWindow *window,
449 GtkStateType state_type,
450 gboolean use_text,
451 GdkRectangle *area,
452 GtkWidget *widget,
453 const gchar *detail,
454 gint x,
455 gint y,
456 PangoLayout *layout)
457 {
458 GdkGC *gc;
459
460 gc = use_text ? style->text_gc[state_type] : style->fg_gc[state_type];
461
462 if (area)
463 gdk_gc_set_clip_rectangle (gc, area);
464
465 if (state_type == GTK_STATE_INSENSITIVE)
466 {
467 GdkGC *copy = gdk_gc_new (window);
468 GdkGCValues orig;
469 GdkColor fore;
470 guint16 r, g, b;
471
472 gdk_gc_copy (copy, gc);
473 gdk_gc_get_values (gc, &orig);
474
475 r = 0x40 + (((orig.foreground.pixel >> 16) & 0xff) >> 1);
476 g = 0x40 + (((orig.foreground.pixel >> 8) & 0xff) >> 1);
477 b = 0x40 + (((orig.foreground.pixel >> 0) & 0xff) >> 1);
478
479 fore.pixel = (r << 16) | (g << 8) | b;
480 fore.red = r * 257;
481 fore.green = g * 257;
482 fore.blue = b * 257;
483
484 gdk_gc_set_foreground (copy, &fore);
485 gdk_draw_layout (window, copy, x, y, layout);
486
487 g_object_unref (copy);
488 }
489 else
490 {
491 gdk_draw_layout (window, gc, x, y, layout);
492 }
493
494 if (area)
495 gdk_gc_set_clip_rectangle (gc, NULL);
496 }
497
498 #ifdef GDK_WINDOWING_QUARTZ
499 static gboolean
gimp_osx_focus_window(gpointer user_data)500 gimp_osx_focus_window (gpointer user_data)
501 {
502 [NSApp activateIgnoringOtherApps:YES];
503 return FALSE;
504 }
505 #endif
506