gimpui.c 14.1 KB
Newer Older
1
2
3
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
4
 * This library is free software: you can redistribute it and/or
5
6
 * modify it under the terms of the GNU Lesser General Public
 * 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
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
13
14
 * Lesser General Public License for more details.
 *
 * 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
 */

Sven Neumann's avatar
Sven Neumann committed
19
20
#include "config.h"

21
22
23
#ifdef GDK_DISABLE_DEPRECATED
#undef GDK_DISABLE_DEPRECATED
#endif
24
25
#include <gtk/gtk.h>

26
27
28
29
30
31
32
33
#ifdef GDK_WINDOWING_WIN32
#include <gdk/gdkwin32.h>
#endif

#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif

34
35
36
37
#ifdef GDK_WINDOWING_QUARTZ
#include <Cocoa/Cocoa.h>
#endif

38
39
40
#include "gimp.h"
#include "gimpui.h"

41
42
#include "libgimpmodule/gimpmodule.h"

43
#include "libgimpwidgets/gimpwidgets.h"
44
#include "libgimpwidgets/gimpwidgets-private.h"
45

46

47
48
49
50
51
/**
 * SECTION: gimpui
 * @title: gimpui
 * @short_description: Common user interface functions. This header includes
 *                     all other GIMP User Interface Library headers.
52
 * @see_also: gtk_init(), gdk_set_use_xshm(), gdk_rgb_get_visual(),
53
54
55
56
57
58
59
60
 *            gdk_rgb_get_cmap(), gtk_widget_set_default_visual(),
 *            gtk_widget_set_default_colormap(), gtk_preview_set_gamma().
 *
 * Common user interface functions. This header includes all other
 * GIMP User Interface Library headers.
 **/


61
62
/*  local function prototypes  */

63
64
static void      gimp_ui_help_func              (const gchar   *help_id,
                                                 gpointer       help_data);
65
static void      gimp_ensure_modules            (void);
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
static void      gimp_window_transient_realized (GtkWidget     *window,
                                                 GdkWindow     *parent);
static gboolean  gimp_window_set_transient_for  (GtkWindow     *window,
                                                 GdkWindow     *parent);

static void      gimp_ui_theme_changed          (void);
static void      gimp_ui_fix_pixbuf_style       (void);
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);
83
84
85
#ifdef GDK_WINDOWING_QUARTZ
static void      gimp_osx_focus_window           (void);
#endif
Sven Neumann's avatar
Sven Neumann committed
86
87

static gboolean gimp_ui_initialized = FALSE;
88
89
90
91


/*  public functions  */

92
93
94
95
/**
 * gimp_ui_init:
 * @prog_name: The name of the plug-in which will be passed as argv[0] to
 *             gtk_init(). It's a convention to use the name of the
96
 *             executable and _not_ the PDB procedure name.
97
98
 * @preview:   This parameter is unused and exists for historical
 *             reasons only.
99
 *
100
101
 * This function initializes GTK+ with gtk_init() and initializes GDK's
 * image rendering subsystem (GdkRGB) to follow the GIMP main program's
102
 * colormap allocation/installation policy.
103
 *
104
105
106
107
108
 * It also sets up various other things so that the plug-in user looks
 * and behaves like the GIMP core. This includes selecting the GTK+
 * theme and setting up the help system as chosen in the GIMP
 * preferences. Any plug-in that provides a user interface should call
 * this function.
109
 **/
110
void
111
gimp_ui_init (const gchar *prog_name,
Sven Neumann's avatar
Sven Neumann committed
112
              gboolean     preview)
113
{
114
115
116
117
118
  GdkScreen    *screen;
  const gchar  *display_name;
  gchar        *themerc;
  GFileMonitor *rc_monitor;
  GFile        *file;
119

120
121
  g_return_if_fail (prog_name != NULL);

Sven Neumann's avatar
Sven Neumann committed
122
  if (gimp_ui_initialized)
123
124
    return;

125
126
  g_set_prgname (prog_name);

127
128
129
130
131
  display_name = gimp_display_name ();

  if (display_name)
    {
#if defined (GDK_WINDOWING_X11)
Michael Natterer's avatar
Michael Natterer committed
132
      g_setenv ("DISPLAY", display_name, TRUE);
133
#else
Michael Natterer's avatar
Michael Natterer committed
134
      g_setenv ("GDK_DISPLAY", display_name, TRUE);
135
136
137
#endif
    }

138
139
140
141
142
143
144
145
146
147
148
  if (gimp_user_time ())
    {
      /* Construct a fake startup ID as we only want to pass the
       * interaction timestamp, see _gdk_windowing_set_default_display().
       */
      gchar *startup_id = g_strdup_printf ("_TIME%u", gimp_user_time ());

      g_setenv ("DESKTOP_STARTUP_ID", startup_id, TRUE);
      g_free (startup_id);
    }

149
  gtk_init (NULL, NULL);
150

151
  themerc = gimp_personal_rc_file ("themerc");
152
153
154
  gtk_rc_parse (themerc);

  file = g_file_new_for_path (themerc);
155
  g_free (themerc);
156

157
158
159
160
  rc_monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL);
  g_object_unref (file);

  g_signal_connect (rc_monitor, "changed",
161
                    G_CALLBACK (gimp_ui_theme_changed),
162
163
                    NULL);

164
165
  gdk_set_program_class (gimp_wm_class ());

166
167
  screen = gdk_screen_get_default ();
  gtk_widget_set_default_colormap (gdk_screen_get_rgb_colormap (screen));
168

169
170
171
172
173
174
  if (gimp_icon_theme_dir ())
    {
      file = g_file_new_for_path (gimp_icon_theme_dir ());
      gimp_icons_set_icon_theme (file);
      g_object_unref (file);
    }
175

176
  gimp_widgets_init (gimp_ui_help_func,
177
178
                     gimp_context_get_foreground,
                     gimp_context_get_background,
179
                     gimp_ensure_modules);
180

181
182
183
  if (! gimp_show_tool_tips ())
    gimp_help_disable_tooltips ();

184
185
  gimp_dialogs_show_help_button (gimp_show_help_button ());

186
#ifdef GDK_WINDOWING_QUARTZ
187
  g_idle_add ((GSourceFunc) gimp_osx_focus_window, NULL);
188
189
#endif

190
  gimp_ui_fix_pixbuf_style ();
Sven Neumann's avatar
Sven Neumann committed
191
192
193
  gimp_ui_initialized = TRUE;
}

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
static GdkWindow *
gimp_ui_get_foreign_window (guint32 window)
{
#ifdef GDK_WINDOWING_X11
  return gdk_x11_window_foreign_new_for_display (gdk_display_get_default (),
                                                 window);
#endif

#ifdef GDK_WINDOWING_WIN32
  return gdk_win32_window_foreign_new_for_display (gdk_display_get_default (),
                                                   window);
#endif

  return NULL;
}

Sven Neumann's avatar
Sven Neumann committed
210
211
/**
 * gimp_ui_get_display_window:
212
 * @gdisp_ID: a #GimpDisplay ID.
Sven Neumann's avatar
Sven Neumann committed
213
214
215
216
217
218
219
220
221
222
223
224
 *
 * Returns the #GdkWindow of a display window. The purpose is to allow
 * to make plug-in dialogs transient to the image display as explained
 * with gdk_window_set_transient_for().
 *
 * You shouldn't have to call this function directly. Use
 * gimp_window_set_transient_for_display() instead.
 *
 * Return value: A reference to a #GdkWindow or %NULL. You should
 *               unref the window using g_object_unref() as soon as
 *               you don't need it any longer.
 *
225
 * Since: 2.4
Sven Neumann's avatar
Sven Neumann committed
226
227
228
229
 */
GdkWindow *
gimp_ui_get_display_window (guint32 gdisp_ID)
{
230
  guint32 window;
Sven Neumann's avatar
Sven Neumann committed
231
232
233
234
235

  g_return_val_if_fail (gimp_ui_initialized, NULL);

  window = gimp_display_get_window_handle (gdisp_ID);
  if (window)
236
    return gimp_ui_get_foreign_window (window);
Sven Neumann's avatar
Sven Neumann committed
237
238
239
240

  return NULL;
}

241
242
243
244
245
246
247
248
249
250
251
252
253
254
/**
 * gimp_ui_get_progress_window:
 *
 * Returns the #GdkWindow of the window this plug-in's progress bar is
 * shown in. Use it to make plug-in dialogs transient to this window
 * as explained with gdk_window_set_transient_for().
 *
 * You shouldn't have to call this function directly. Use
 * gimp_window_set_transient() instead.
 *
 * Return value: A reference to a #GdkWindow or %NULL. You should
 *               unref the window using g_object_unref() as soon as
 *               you don't need it any longer.
 *
255
 * Since: 2.4
256
257
258
259
 */
GdkWindow *
gimp_ui_get_progress_window (void)
{
260
  guint32  window;
261
262
263
264
265

  g_return_val_if_fail (gimp_ui_initialized, NULL);

  window = gimp_progress_get_window_handle ();
  if (window)
266
     return gimp_ui_get_foreign_window (window);
267
268
269
270

  return NULL;
}

271
272
273
274
275
276
277
278
279
280
281
#ifdef GDK_WINDOWING_QUARTZ
static void
gimp_window_transient_show (GtkWidget *window)
{
  g_signal_handlers_disconnect_by_func (window,
                                        gimp_window_transient_show,
                                        NULL);
  [NSApp arrangeInFront: nil];
}
#endif

Sven Neumann's avatar
Sven Neumann committed
282
283
284
285
286
287
288
289
290
291
/**
 * gimp_window_set_transient_for_display:
 * @window:   the #GtkWindow that should become transient
 * @gdisp_ID: display ID of the image window that should become the parent
 *
 * Indicates to the window manager that @window is a transient dialog
 * associated with the GIMP image window that is identified by it's
 * display ID.  See gdk_window_set_transient_for () for more information.
 *
 * Most of the time you will want to use the convenience function
292
 * gimp_window_set_transient().
Sven Neumann's avatar
Sven Neumann committed
293
 *
294
 * Since: 2.4
Sven Neumann's avatar
Sven Neumann committed
295
296
297
298
299
300
301
302
 */
void
gimp_window_set_transient_for_display (GtkWindow *window,
                                       guint32    gdisp_ID)
{
  g_return_if_fail (gimp_ui_initialized);
  g_return_if_fail (GTK_IS_WINDOW (window));

303
304
305
306
307
308
309
310
  if (! gimp_window_set_transient_for (window,
                                       gimp_ui_get_display_window (gdisp_ID)))
    {
      /*  if setting the window transient failed, at least set
       *  WIN_POS_CENTER, which will center the window on the screen
       *  where the mouse is (see bug #684003).
       */
      gtk_window_set_position (window, GTK_WIN_POS_CENTER);
311
312

#ifdef GDK_WINDOWING_QUARTZ
313
314
315
      g_signal_connect (window, "show",
                        G_CALLBACK (gimp_window_transient_show),
                        NULL);
316
#endif
317
    }
Sven Neumann's avatar
Sven Neumann committed
318
319
320
}

/**
321
 * gimp_window_set_transient:
Sven Neumann's avatar
Sven Neumann committed
322
323
324
 * @window: the #GtkWindow that should become transient
 *
 * Indicates to the window manager that @window is a transient dialog
325
 * associated with the GIMP window that the plug-in has been
Sven Neumann's avatar
Sven Neumann committed
326
327
 * started from. See also gimp_window_set_transient_for_display().
 *
328
 * Since: 2.4
Sven Neumann's avatar
Sven Neumann committed
329
330
 */
void
331
gimp_window_set_transient (GtkWindow *window)
Sven Neumann's avatar
Sven Neumann committed
332
333
334
335
{
  g_return_if_fail (gimp_ui_initialized);
  g_return_if_fail (GTK_IS_WINDOW (window));

336
337
338
339
  if (! gimp_window_set_transient_for (window, gimp_ui_get_progress_window ()))
    {
      /*  see above  */
      gtk_window_set_position (window, GTK_WIN_POS_CENTER);
340
341

#ifdef GDK_WINDOWING_QUARTZ
342
343
344
      g_signal_connect (window, "show",
                        G_CALLBACK (gimp_window_transient_show),
                        NULL);
345
#endif
346
    }
347
}
348
349
350
351
352


/*  private functions  */

static void
353
354
gimp_ui_help_func (const gchar *help_id,
                   gpointer     help_data)
355
{
356
  gimp_help (NULL, help_id);
357
}
358
359
360
361
362
363
364
365

static void
gimp_ensure_modules (void)
{
  static GimpModuleDB *module_db = NULL;

  if (! module_db)
    {
Sven Neumann's avatar
Sven Neumann committed
366
367
      gchar *load_inhibit = gimp_get_module_load_inhibit ();
      gchar *module_path  = gimp_gimprc_query ("module-path");
368
369

      module_db = gimp_module_db_new (FALSE);
Sven Neumann's avatar
Sven Neumann committed
370

371
372
373
374
      gimp_module_db_set_load_inhibit (module_db, load_inhibit);
      gimp_module_db_load (module_db, module_path);

      g_free (module_path);
Sven Neumann's avatar
Sven Neumann committed
375
      g_free (load_inhibit);
376
377
    }
}
Sven Neumann's avatar
Sven Neumann committed
378
379
380
381
382

static void
gimp_window_transient_realized (GtkWidget *window,
                                GdkWindow *parent)
{
383
  if (gtk_widget_get_realized (window))
384
    gdk_window_set_transient_for (gtk_widget_get_window (window), parent);
Sven Neumann's avatar
Sven Neumann committed
385
}
386

387
static gboolean
388
389
390
391
392
gimp_window_set_transient_for (GtkWindow *window,
                               GdkWindow *parent)
{
  gtk_window_set_transient_for (window, NULL);

393
#ifndef GDK_WINDOWING_WIN32
394
395
396
397
398
399
  g_signal_handlers_disconnect_matched (window, G_SIGNAL_MATCH_FUNC,
                                        0, 0, NULL,
                                        gimp_window_transient_realized,
                                        NULL);

  if (! parent)
400
    return FALSE;
401

402
  if (gtk_widget_get_realized (GTK_WIDGET (window)))
403
404
    gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (window)),
                                  parent);
405
406
407
408
409

  g_signal_connect_object (window, "realize",
                           G_CALLBACK (gimp_window_transient_realized),
                           parent, 0);
  g_object_unref (parent);
410
411

  return TRUE;
412
#endif
413
414

  return FALSE;
415
}
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496

static void
gimp_ui_theme_changed (void)
{
  gtk_rc_reparse_all ();

  gimp_ui_fix_pixbuf_style ();
}

static void
gimp_ui_fix_pixbuf_style (void)
{
  /*  Same hack as in app/gui/themes.c, to be removed for GTK+ 3.x  */

  static GtkStyleClass *pixbuf_style_class = NULL;

  if (! pixbuf_style_class)
    {
      GType type = g_type_from_name ("PixbufStyle");

      if (type)
        {
          pixbuf_style_class = g_type_class_ref (type);

          if (pixbuf_style_class)
            pixbuf_style_class->draw_layout = gimp_ui_draw_pixbuf_layout;
        }
    }
}

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)
{
  GdkGC *gc;

  gc = use_text ? style->text_gc[state_type] : style->fg_gc[state_type];

  if (area)
    gdk_gc_set_clip_rectangle (gc, area);

  if (state_type == GTK_STATE_INSENSITIVE)
    {
      GdkGC       *copy = gdk_gc_new (window);
      GdkGCValues  orig;
      GdkColor     fore;
      guint16      r, g, b;

      gdk_gc_copy (copy, gc);
      gdk_gc_get_values (gc, &orig);

      r = 0x40 + (((orig.foreground.pixel >> 16) & 0xff) >> 1);
      g = 0x40 + (((orig.foreground.pixel >>  8) & 0xff) >> 1);
      b = 0x40 + (((orig.foreground.pixel >>  0) & 0xff) >> 1);

      fore.pixel = (r << 16) | (g << 8) | b;
      fore.red   = r * 257;
      fore.green = g * 257;
      fore.blue  = b * 257;

      gdk_gc_set_foreground (copy, &fore);
      gdk_draw_layout (window, copy, x, y, layout);

      g_object_unref (copy);
    }
  else
    {
      gdk_draw_layout (window, gc, x, y, layout);
    }

  if (area)
    gdk_gc_set_clip_rectangle (gc, NULL);
}
497
498
499
500
501
502
503
504

#ifdef GDK_WINDOWING_QUARTZ
static void
gimp_osx_focus_window (void)
{
  [NSApp activateIgnoringOtherApps:YES];
}
#endif