2006-12-09 21:33:38 +00:00
|
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1999-09-27 17:58:10 +00:00
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
|
*
|
2003-03-13 13:08:37 +00:00
|
|
|
|
* gimpwidgets-utils.c
|
|
|
|
|
* Copyright (C) 1999-2003 Michael Natterer <mitch@gimp.org>
|
1999-09-27 17:58:10 +00:00
|
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1999-09-27 17:58:10 +00:00
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-17 22:28:01 +00:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1999-09-27 17:58:10 +00:00
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2018-07-11 23:27:07 +02:00
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1999-09-27 17:58:10 +00:00
|
|
|
|
*/
|
2000-12-16 21:37:03 +00:00
|
|
|
|
|
1999-12-28 18:30:01 +00:00
|
|
|
|
#include "config.h"
|
2000-02-02 01:21:36 +00:00
|
|
|
|
|
2001-02-04 04:51:17 +00:00
|
|
|
|
#include <string.h>
|
2000-02-02 01:21:36 +00:00
|
|
|
|
|
2012-03-30 15:08:54 +02:00
|
|
|
|
#include <gegl.h>
|
2000-12-16 21:37:03 +00:00
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
2005-09-09 18:07:31 +00:00
|
|
|
|
#ifdef GDK_WINDOWING_WIN32
|
2023-10-16 16:43:17 +00:00
|
|
|
|
#include <dwmapi.h>
|
2005-09-09 18:07:31 +00:00
|
|
|
|
#include <gdk/gdkwin32.h>
|
2023-10-16 16:43:17 +00:00
|
|
|
|
|
|
|
|
|
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
|
|
|
|
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
|
|
|
|
#endif
|
2005-09-09 18:07:31 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
|
|
|
#include <gdk/gdkx.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-12-30 12:22:42 +01:00
|
|
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
|
|
|
#include <gdk/gdkwayland.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-06-25 01:31:09 +02:00
|
|
|
|
#ifdef PLATFORM_OSX
|
2018-10-24 10:45:07 +02:00
|
|
|
|
#include <ApplicationServices/ApplicationServices.h>
|
2018-06-25 01:31:09 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2003-05-29 11:34:30 +00:00
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
2011-05-31 17:40:10 +02:00
|
|
|
|
#include "libgimpconfig/gimpconfig.h"
|
2007-03-09 13:00:01 +00:00
|
|
|
|
#include "libgimpmath/gimpmath.h"
|
2003-06-10 16:44:44 +00:00
|
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
2001-01-24 22:36:18 +00:00
|
|
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
|
|
2001-05-25 16:04:54 +00:00
|
|
|
|
#include "widgets-types.h"
|
2000-12-29 15:22:01 +00:00
|
|
|
|
|
2016-05-22 01:19:18 +02:00
|
|
|
|
#include "gegl/gimp-babl.h"
|
|
|
|
|
|
2023-10-16 16:43:17 +00:00
|
|
|
|
#include "config/gimpguiconfig.h"
|
|
|
|
|
|
2022-03-05 14:32:34 +01:00
|
|
|
|
#include "core/gimp.h"
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
#include "core/gimpprogress.h"
|
2022-03-07 11:47:21 +01:00
|
|
|
|
#include "core/gimptoolinfo.h"
|
2022-03-05 14:32:34 +01:00
|
|
|
|
|
2023-02-22 15:04:18 +01:00
|
|
|
|
#include "gimpaccellabel.h"
|
2019-07-02 03:54:38 +02:00
|
|
|
|
#include "gimpaction.h"
|
2010-02-20 16:55:35 +01:00
|
|
|
|
#include "gimpdialogfactory.h"
|
|
|
|
|
#include "gimpdock.h"
|
2011-05-13 18:56:03 +02:00
|
|
|
|
#include "gimpdockcontainer.h"
|
2010-02-20 16:55:35 +01:00
|
|
|
|
#include "gimpdockwindow.h"
|
2004-08-25 17:58:52 +00:00
|
|
|
|
#include "gimperrordialog.h"
|
2011-05-31 17:40:10 +02:00
|
|
|
|
#include "gimpsessioninfo.h"
|
2022-03-07 11:47:21 +01:00
|
|
|
|
#include "gimptoolbutton.h"
|
2022-03-06 13:34:12 +01:00
|
|
|
|
#include "gimpuimanager.h"
|
2001-05-25 16:04:54 +00:00
|
|
|
|
#include "gimpwidgets-utils.h"
|
2022-03-05 14:32:34 +01:00
|
|
|
|
#include "gimpwindowstrategy.h"
|
1999-09-27 17:58:10 +00:00
|
|
|
|
|
2003-03-25 16:38:19 +00:00
|
|
|
|
#include "gimp-intl.h"
|
1999-09-27 17:58:10 +00:00
|
|
|
|
|
2000-12-16 21:37:03 +00:00
|
|
|
|
|
2018-09-27 02:31:39 -04:00
|
|
|
|
#define GIMP_TOOL_OPTIONS_GUI_KEY "gimp-tool-options-gui"
|
|
|
|
|
#define GIMP_TOOL_OPTIONS_GUI_FUNC_KEY "gimp-tool-options-gui-func"
|
2010-10-31 13:05:15 +01:00
|
|
|
|
|
|
|
|
|
|
2022-03-05 17:29:44 +01:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GList **blink_script;
|
|
|
|
|
const gchar *widget_identifier;
|
2022-03-06 14:31:27 +01:00
|
|
|
|
const gchar *settings_value;
|
|
|
|
|
} BlinkSearch;
|
2022-03-05 17:29:44 +01:00
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *widget;
|
|
|
|
|
gchar *settings_value;
|
|
|
|
|
} BlinkStep;
|
2022-03-05 17:29:44 +01:00
|
|
|
|
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
static void gimp_widget_blink_after (GtkWidget *widget,
|
|
|
|
|
gint ms_timeout);
|
|
|
|
|
static void gimp_search_widget_rec (GtkWidget *widget,
|
|
|
|
|
BlinkSearch *data);
|
|
|
|
|
static void gimp_blink_free_script (GList *blink_scenario);
|
|
|
|
|
|
|
|
|
|
static gboolean gimp_window_transient_on_mapped (GtkWidget *widget,
|
|
|
|
|
GdkEventAny *event,
|
|
|
|
|
GimpProgress *progress);
|
app, libgimp, pdb: add a parent_window parameter to gimp_*_popup() PDB calls.
Brush, font, gradient, palette and pattern choices are currently chosen through
a dialog created by the core, which then returns the user choice to the calling
plug-in. This has the unfortunate consequence of having a pile of likely at
least 3 windows (main GIMP window by core process, plug-in window by plug-in
process, then the choice popup by the core process) shared in 2 processes, which
often end up under each other and that's messy. Even more as the choice popup is
kinda expected to be like a sub-part of the plug-in dialog.
So anyway, now the plug-in can send its window handle to the core so that the
resource choice dialog ends up always above the plug-in dialog.
Of course, it will always work only on platforms where we have working
inter-process transient support.
2023-08-15 00:12:16 +02:00
|
|
|
|
static void gimp_window_set_transient_cb (GtkWidget *window,
|
|
|
|
|
GdkEventAny *event,
|
|
|
|
|
GBytes *handle);
|
2022-03-05 14:32:34 +01:00
|
|
|
|
|
|
|
|
|
|
2018-05-02 14:50:22 +02:00
|
|
|
|
GtkWidget *
|
|
|
|
|
gimp_menu_item_get_image (GtkMenuItem *item)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (GTK_IS_MENU_ITEM (item), NULL);
|
|
|
|
|
|
|
|
|
|
return g_object_get_data (G_OBJECT (item), "gimp-menu-item-image");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_menu_item_set_image (GtkMenuItem *item,
|
2023-02-22 15:04:18 +01:00
|
|
|
|
GtkWidget *image,
|
|
|
|
|
GimpAction *action)
|
2018-05-02 14:50:22 +02:00
|
|
|
|
{
|
|
|
|
|
GtkWidget *hbox;
|
|
|
|
|
GtkWidget *label;
|
2023-02-22 15:04:18 +01:00
|
|
|
|
GtkWidget *accel_label;
|
2018-05-02 14:50:22 +02:00
|
|
|
|
GtkWidget *old_image;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_MENU_ITEM (item));
|
|
|
|
|
g_return_if_fail (image == NULL || GTK_IS_WIDGET (image));
|
|
|
|
|
|
|
|
|
|
hbox = g_object_get_data (G_OBJECT (item), "gimp-menu-item-hbox");
|
|
|
|
|
|
|
|
|
|
if (! hbox)
|
|
|
|
|
{
|
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
2018-05-23 22:21:52 +02:00
|
|
|
|
g_object_set_data (G_OBJECT (item), "gimp-menu-item-hbox", hbox);
|
2018-05-02 14:50:22 +02:00
|
|
|
|
|
|
|
|
|
label = gtk_bin_get_child (GTK_BIN (item));
|
2018-05-23 22:21:52 +02:00
|
|
|
|
g_object_set_data (G_OBJECT (item), "gimp-menu-item-label", label);
|
2023-02-22 15:04:18 +01:00
|
|
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
2018-05-02 14:50:22 +02:00
|
|
|
|
|
|
|
|
|
g_object_ref (label);
|
|
|
|
|
gtk_container_remove (GTK_CONTAINER (item), label);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (hbox), label);
|
|
|
|
|
g_object_unref (label);
|
|
|
|
|
|
2023-04-17 16:13:59 +02:00
|
|
|
|
if (action)
|
|
|
|
|
{
|
|
|
|
|
accel_label = gimp_accel_label_new (action);
|
|
|
|
|
g_object_set_data (G_OBJECT (item), "gimp-menu-item-accel", accel_label);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (hbox), accel_label);
|
|
|
|
|
gtk_widget_set_hexpand (GTK_WIDGET (accel_label), TRUE);
|
|
|
|
|
gtk_label_set_xalign (GTK_LABEL (accel_label), 1.0);
|
|
|
|
|
gtk_widget_show (accel_label);
|
|
|
|
|
}
|
2023-02-22 15:04:18 +01:00
|
|
|
|
|
2018-05-02 14:50:22 +02:00
|
|
|
|
gtk_container_add (GTK_CONTAINER (item), hbox);
|
|
|
|
|
gtk_widget_show (hbox);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
old_image = g_object_get_data (G_OBJECT (item), "gimp-menu-item-image");
|
|
|
|
|
|
|
|
|
|
if (old_image != image)
|
|
|
|
|
{
|
|
|
|
|
if (old_image)
|
|
|
|
|
{
|
|
|
|
|
gtk_widget_destroy (old_image);
|
|
|
|
|
g_object_set_data (G_OBJECT (item), "gimp-menu-item-image", NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (image)
|
|
|
|
|
{
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (hbox), image);
|
|
|
|
|
gtk_box_reorder_child (GTK_BOX (hbox), image, 0);
|
|
|
|
|
g_object_set_data (G_OBJECT (item), "gimp-menu-item-image", image);
|
|
|
|
|
gtk_widget_show (image);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-11-19 18:08:15 +00:00
|
|
|
|
/**
|
|
|
|
|
* gimp_menu_position:
|
|
|
|
|
* @menu: a #GtkMenu widget
|
|
|
|
|
* @x: pointer to horizontal position
|
|
|
|
|
* @y: pointer to vertical position
|
|
|
|
|
*
|
|
|
|
|
* Positions a #GtkMenu so that it pops up on screen. This function
|
|
|
|
|
* takes care of the preferred popup direction (taken from the widget
|
|
|
|
|
* render direction) and it handles multiple monitors representing a
|
|
|
|
|
* single #GdkScreen (Xinerama).
|
|
|
|
|
*
|
|
|
|
|
* You should call this function with @x and @y initialized to the
|
|
|
|
|
* origin of the menu. This is typically the center of the widget the
|
|
|
|
|
* menu is popped up from. gimp_menu_position() will then decide if
|
|
|
|
|
* and how these initial values need to be changed.
|
|
|
|
|
**/
|
2001-04-20 16:27:44 +00:00
|
|
|
|
void
|
2003-11-19 18:08:15 +00:00
|
|
|
|
gimp_menu_position (GtkMenu *menu,
|
2006-04-12 12:49:29 +00:00
|
|
|
|
gint *x,
|
|
|
|
|
gint *y)
|
2001-04-20 16:27:44 +00:00
|
|
|
|
{
|
2003-11-19 18:08:15 +00:00
|
|
|
|
GtkWidget *widget;
|
|
|
|
|
GtkRequisition requisition;
|
2018-05-04 13:20:46 +02:00
|
|
|
|
GdkRectangle workarea;
|
2001-08-04 20:38:54 +00:00
|
|
|
|
|
2001-11-30 14:41:56 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_MENU (menu));
|
2001-08-04 20:38:54 +00:00
|
|
|
|
g_return_if_fail (x != NULL);
|
|
|
|
|
g_return_if_fail (y != NULL);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
2003-11-19 18:08:15 +00:00
|
|
|
|
widget = GTK_WIDGET (menu);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
2018-05-04 13:20:46 +02:00
|
|
|
|
gdk_monitor_get_workarea (gimp_widget_get_monitor (widget), &workarea);
|
2000-03-28 23:02:05 +00:00
|
|
|
|
|
2018-05-04 13:20:46 +02:00
|
|
|
|
gtk_menu_set_screen (menu, gtk_widget_get_screen (widget));
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
2010-10-19 12:11:59 +02:00
|
|
|
|
gtk_widget_get_preferred_size (widget, &requisition, NULL);
|
2003-09-24 14:23:32 +00:00
|
|
|
|
|
2003-11-19 18:08:15 +00:00
|
|
|
|
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
|
|
|
|
{
|
|
|
|
|
*x -= requisition.width;
|
2018-05-04 13:20:46 +02:00
|
|
|
|
if (*x < workarea.x)
|
2003-11-19 18:08:15 +00:00
|
|
|
|
*x += requisition.width;
|
2003-09-24 14:23:32 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-05-04 13:20:46 +02:00
|
|
|
|
if (*x + requisition.width > workarea.x + workarea.width)
|
2003-11-19 18:08:15 +00:00
|
|
|
|
*x -= requisition.width;
|
2003-09-24 14:23:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-04 13:20:46 +02:00
|
|
|
|
if (*x < workarea.x)
|
|
|
|
|
*x = workarea.x;
|
2001-08-04 20:38:54 +00:00
|
|
|
|
|
2018-05-04 13:20:46 +02:00
|
|
|
|
if (*y + requisition.height > workarea.y + workarea.height)
|
2003-11-19 18:08:15 +00:00
|
|
|
|
*y -= requisition.height;
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
2018-05-04 13:20:46 +02:00
|
|
|
|
if (*y < workarea.y)
|
|
|
|
|
*y = workarea.y;
|
2001-08-04 20:38:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-09 15:42:38 +00:00
|
|
|
|
void
|
2018-04-29 18:40:43 +02:00
|
|
|
|
gimp_grid_attach_icon (GtkGrid *grid,
|
|
|
|
|
gint row,
|
|
|
|
|
const gchar *icon_name,
|
|
|
|
|
GtkWidget *widget,
|
|
|
|
|
gint columns)
|
2002-10-09 15:42:38 +00:00
|
|
|
|
{
|
2003-02-05 22:15:39 +00:00
|
|
|
|
GtkWidget *image;
|
2002-10-09 15:42:38 +00:00
|
|
|
|
|
2018-04-29 18:40:43 +02:00
|
|
|
|
g_return_if_fail (GTK_IS_GRID (grid));
|
2014-05-07 15:30:38 +02:00
|
|
|
|
g_return_if_fail (icon_name != NULL);
|
2005-09-02 17:22:29 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
2003-02-05 18:23:58 +00:00
|
|
|
|
|
2014-05-07 15:30:38 +02:00
|
|
|
|
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
|
2018-04-29 01:51:36 +02:00
|
|
|
|
gtk_widget_set_halign (image, GTK_ALIGN_END);
|
2018-04-29 18:40:43 +02:00
|
|
|
|
gtk_grid_attach (grid, image, 0, row, 1, 1);
|
2005-09-02 17:22:29 +00:00
|
|
|
|
gtk_widget_show (image);
|
2003-09-09 11:35:27 +00:00
|
|
|
|
|
2018-04-29 18:40:43 +02:00
|
|
|
|
gtk_grid_attach (grid, widget, 1, row, columns, 1);
|
2005-09-02 17:22:29 +00:00
|
|
|
|
gtk_widget_show (widget);
|
2002-10-09 15:42:38 +00:00
|
|
|
|
}
|
2002-12-19 16:33:29 +00:00
|
|
|
|
|
2004-10-12 12:06:50 +00:00
|
|
|
|
void
|
2008-10-24 22:34:24 +00:00
|
|
|
|
gimp_enum_radio_box_add (GtkBox *box,
|
|
|
|
|
GtkWidget *widget,
|
2008-10-28 17:40:32 +00:00
|
|
|
|
gint enum_value,
|
|
|
|
|
gboolean below)
|
2004-10-12 12:06:50 +00:00
|
|
|
|
{
|
2008-10-24 22:34:24 +00:00
|
|
|
|
GList *children;
|
|
|
|
|
GList *list;
|
|
|
|
|
gint pos;
|
2004-10-12 12:06:50 +00:00
|
|
|
|
|
2008-10-24 22:34:24 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_BOX (box));
|
2004-10-12 12:06:50 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
2008-10-24 22:34:24 +00:00
|
|
|
|
children = gtk_container_get_children (GTK_CONTAINER (box));
|
2004-10-12 12:06:50 +00:00
|
|
|
|
|
2004-10-14 13:44:06 +00:00
|
|
|
|
for (list = children, pos = 1;
|
2004-10-12 12:06:50 +00:00
|
|
|
|
list;
|
2004-10-14 13:44:06 +00:00
|
|
|
|
list = g_list_next (list), pos++)
|
2004-10-12 12:06:50 +00:00
|
|
|
|
{
|
2004-10-14 13:44:06 +00:00
|
|
|
|
if (GTK_IS_RADIO_BUTTON (list->data) &&
|
|
|
|
|
GPOINTER_TO_INT (g_object_get_data (list->data, "gimp-item-data")) ==
|
2004-10-12 12:06:50 +00:00
|
|
|
|
enum_value)
|
|
|
|
|
{
|
2004-10-14 13:44:06 +00:00
|
|
|
|
GtkWidget *radio = list->data;
|
|
|
|
|
GtkWidget *hbox;
|
2008-06-28 15:50:27 +00:00
|
|
|
|
|
2011-09-30 11:29:11 +02:00
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
2008-10-28 17:40:32 +00:00
|
|
|
|
gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
|
|
|
|
|
gtk_box_reorder_child (GTK_BOX (box), hbox, pos);
|
2004-10-14 13:44:06 +00:00
|
|
|
|
|
2008-10-28 17:40:32 +00:00
|
|
|
|
if (below)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *spacer;
|
|
|
|
|
gint indicator_size;
|
|
|
|
|
gint indicator_spacing;
|
|
|
|
|
gint focus_width;
|
|
|
|
|
gint focus_padding;
|
|
|
|
|
gint border_width;
|
|
|
|
|
|
|
|
|
|
gtk_widget_style_get (radio,
|
|
|
|
|
"indicator-size", &indicator_size,
|
|
|
|
|
"indicator-spacing", &indicator_spacing,
|
|
|
|
|
"focus-line-width", &focus_width,
|
|
|
|
|
"focus-padding", &focus_padding,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
border_width = gtk_container_get_border_width (GTK_CONTAINER (radio));
|
|
|
|
|
|
2011-09-30 11:29:11 +02:00
|
|
|
|
spacer = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
2008-10-28 17:40:32 +00:00
|
|
|
|
gtk_widget_set_size_request (spacer,
|
|
|
|
|
indicator_size +
|
|
|
|
|
3 * indicator_spacing +
|
|
|
|
|
focus_width +
|
|
|
|
|
focus_padding +
|
|
|
|
|
border_width,
|
|
|
|
|
-1);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), spacer, FALSE, FALSE, 0);
|
|
|
|
|
gtk_widget_show (spacer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GtkSizeGroup *size_group;
|
|
|
|
|
|
|
|
|
|
size_group = g_object_get_data (G_OBJECT (box), "size-group");
|
|
|
|
|
|
|
|
|
|
if (! size_group)
|
|
|
|
|
{
|
|
|
|
|
size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
|
|
|
|
g_object_set_data (G_OBJECT (box), "size-group", size_group);
|
|
|
|
|
|
|
|
|
|
gtk_size_group_add_widget (size_group, radio);
|
|
|
|
|
g_object_unref (size_group);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gtk_size_group_add_widget (size_group, radio);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_box_set_spacing (GTK_BOX (hbox), 4);
|
|
|
|
|
|
|
|
|
|
g_object_ref (radio);
|
|
|
|
|
gtk_container_remove (GTK_CONTAINER (box), radio);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), radio, FALSE, FALSE, 0);
|
|
|
|
|
g_object_unref (radio);
|
|
|
|
|
}
|
2004-10-14 13:44:06 +00:00
|
|
|
|
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
|
|
|
|
|
gtk_widget_show (widget);
|
|
|
|
|
|
2011-05-11 11:43:19 +02:00
|
|
|
|
g_object_bind_property (radio, "active",
|
|
|
|
|
widget, "sensitive",
|
|
|
|
|
G_BINDING_SYNC_CREATE);
|
2004-10-12 12:06:50 +00:00
|
|
|
|
|
2004-10-14 13:44:06 +00:00
|
|
|
|
gtk_widget_show (hbox);
|
|
|
|
|
|
2004-10-12 12:06:50 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-14 13:44:06 +00:00
|
|
|
|
g_list_free (children);
|
2004-10-12 12:06:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-24 22:34:24 +00:00
|
|
|
|
void
|
|
|
|
|
gimp_enum_radio_frame_add (GtkFrame *frame,
|
|
|
|
|
GtkWidget *widget,
|
2008-10-28 17:40:32 +00:00
|
|
|
|
gint enum_value,
|
|
|
|
|
gboolean below)
|
2008-10-24 22:34:24 +00:00
|
|
|
|
{
|
2011-07-27 20:21:58 +02:00
|
|
|
|
GtkWidget *box;
|
2008-10-24 22:34:24 +00:00
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_FRAME (frame));
|
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
2011-07-27 20:21:58 +02:00
|
|
|
|
box = gtk_bin_get_child (GTK_BIN (frame));
|
2008-10-24 22:34:24 +00:00
|
|
|
|
|
2011-07-27 20:21:58 +02:00
|
|
|
|
g_return_if_fail (GTK_IS_BOX (box));
|
2008-10-24 22:34:24 +00:00
|
|
|
|
|
2011-07-27 20:21:58 +02:00
|
|
|
|
gimp_enum_radio_box_add (GTK_BOX (box), widget, enum_value, below);
|
2008-10-24 22:34:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-24 17:11:24 +02:00
|
|
|
|
/**
|
|
|
|
|
* gimp_widget_load_icon:
|
|
|
|
|
* @widget: parent widget (to determine icon theme and
|
|
|
|
|
* style)
|
|
|
|
|
* @icon_name: icon name
|
|
|
|
|
* @size: requested pixel size
|
|
|
|
|
*
|
|
|
|
|
* Loads an icon into a pixbuf with size as close as possible to @size.
|
|
|
|
|
* If icon does not exist or fail to load, the function will fallback to
|
2018-06-24 18:21:24 +02:00
|
|
|
|
* "gimp-wilber-eek" instead to prevent NULL pixbuf. As a last resort,
|
|
|
|
|
* if even the fallback failed to load, a magenta @size square will be
|
|
|
|
|
* returned, so this function is guaranteed to always return a
|
|
|
|
|
* #GdkPixbuf.
|
2018-06-24 17:11:24 +02:00
|
|
|
|
*
|
2019-08-03 00:10:14 +02:00
|
|
|
|
* Returns: a newly allocated #GdkPixbuf containing @icon_name at
|
2018-06-24 18:21:24 +02:00
|
|
|
|
* size @size or a fallback icon/size.
|
2018-06-24 17:11:24 +02:00
|
|
|
|
**/
|
2014-05-08 09:11:31 +02:00
|
|
|
|
GdkPixbuf *
|
|
|
|
|
gimp_widget_load_icon (GtkWidget *widget,
|
|
|
|
|
const gchar *icon_name,
|
|
|
|
|
gint size)
|
|
|
|
|
{
|
2018-06-25 15:32:24 +02:00
|
|
|
|
GdkPixbuf *pixbuf = NULL;
|
2014-05-08 09:11:31 +02:00
|
|
|
|
GtkIconTheme *icon_theme;
|
2018-05-30 09:39:04 +02:00
|
|
|
|
GtkIconInfo *icon_info;
|
|
|
|
|
gchar *name;
|
|
|
|
|
gint scale_factor;
|
2014-05-08 09:11:31 +02:00
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
|
|
|
|
g_return_val_if_fail (icon_name != NULL, NULL);
|
|
|
|
|
|
|
|
|
|
icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
|
2018-05-30 09:39:04 +02:00
|
|
|
|
scale_factor = gtk_widget_get_scale_factor (widget);
|
|
|
|
|
name = g_strdup_printf ("%s-symbolic", icon_name);
|
|
|
|
|
/* This will find the symbolic icon and fallback to non-symbolic
|
|
|
|
|
* depending on icon theme.
|
|
|
|
|
*/
|
2018-12-31 18:44:35 +01:00
|
|
|
|
icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, name,
|
|
|
|
|
size, scale_factor,
|
|
|
|
|
GTK_ICON_LOOKUP_GENERIC_FALLBACK);
|
2018-05-30 09:39:04 +02:00
|
|
|
|
g_free (name);
|
|
|
|
|
|
2018-06-25 15:32:24 +02:00
|
|
|
|
if (icon_info)
|
|
|
|
|
{
|
|
|
|
|
pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info,
|
|
|
|
|
gtk_widget_get_style_context (widget),
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
g_object_unref (icon_info);
|
|
|
|
|
if (! pixbuf)
|
|
|
|
|
/* The icon was seemingly present in the current icon theme, yet
|
|
|
|
|
* it failed to load. Maybe the file is broken?
|
|
|
|
|
* As last resort, try to load "gimp-wilber-eek" as fallback.
|
|
|
|
|
* Note that we are not making more checks, so if the fallback
|
|
|
|
|
* icon fails to load as well, the function may still return NULL.
|
|
|
|
|
*/
|
|
|
|
|
g_printerr ("WARNING: icon '%s' failed to load. Check the files "
|
|
|
|
|
"in your icon theme.\n", icon_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
g_printerr ("WARNING: icon theme has no icon '%s'.\n", icon_name);
|
|
|
|
|
|
|
|
|
|
/* First fallback: gimp-wilber-eek */
|
|
|
|
|
if (! pixbuf)
|
2014-05-08 09:11:31 +02:00
|
|
|
|
{
|
2018-05-30 09:39:04 +02:00
|
|
|
|
icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme,
|
|
|
|
|
GIMP_ICON_WILBER_EEK "-symbolic",
|
|
|
|
|
size, scale_factor,
|
|
|
|
|
GTK_ICON_LOOKUP_GENERIC_FALLBACK);
|
2018-06-24 17:27:36 +02:00
|
|
|
|
if (icon_info)
|
2018-06-24 16:59:39 +02:00
|
|
|
|
{
|
2018-06-25 15:32:24 +02:00
|
|
|
|
pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info,
|
|
|
|
|
gtk_widget_get_style_context (widget),
|
|
|
|
|
NULL, NULL);
|
2018-06-24 16:59:39 +02:00
|
|
|
|
g_object_unref (icon_info);
|
2018-06-24 18:21:24 +02:00
|
|
|
|
if (! pixbuf)
|
2018-06-25 15:32:24 +02:00
|
|
|
|
g_printerr ("WARNING: icon '%s' failed to load. Check the files "
|
|
|
|
|
"in your icon theme.\n", GIMP_ICON_WILBER_EEK);
|
|
|
|
|
}
|
|
|
|
|
else
|
2018-12-31 18:44:35 +01:00
|
|
|
|
{
|
|
|
|
|
g_printerr ("WARNING: icon theme has no icon '%s'.\n",
|
|
|
|
|
GIMP_ICON_WILBER_EEK);
|
|
|
|
|
}
|
2018-06-25 15:32:24 +02:00
|
|
|
|
}
|
2018-06-24 18:21:24 +02:00
|
|
|
|
|
2018-06-25 15:32:24 +02:00
|
|
|
|
/* Last fallback: just a magenta square. */
|
|
|
|
|
if (! pixbuf)
|
|
|
|
|
{
|
|
|
|
|
/* As last resort, just draw an ugly magenta square. */
|
|
|
|
|
guchar *data;
|
2018-12-31 18:44:35 +01:00
|
|
|
|
gint rowstride = 3 * size * scale_factor;
|
2018-06-25 15:32:24 +02:00
|
|
|
|
gint i, j;
|
2018-06-24 18:21:24 +02:00
|
|
|
|
|
2018-06-25 15:32:24 +02:00
|
|
|
|
data = g_new (guchar, rowstride * size);
|
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
|
{
|
2018-12-31 18:44:35 +01:00
|
|
|
|
for (j = 0; j < size * scale_factor; j++)
|
2018-06-25 15:32:24 +02:00
|
|
|
|
{
|
|
|
|
|
data[i * rowstride + j * 3] = 255;
|
|
|
|
|
data[i * rowstride + j * 3 + 1] = 0;
|
|
|
|
|
data[i * rowstride + j * 3 + 2] = 255;
|
2018-06-24 18:21:24 +02:00
|
|
|
|
}
|
2018-06-24 16:59:39 +02:00
|
|
|
|
}
|
2018-06-25 15:32:24 +02:00
|
|
|
|
pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, FALSE,
|
2018-12-31 18:44:35 +01:00
|
|
|
|
8,
|
|
|
|
|
size * scale_factor,
|
|
|
|
|
size * scale_factor, rowstride,
|
2018-06-25 15:32:24 +02:00
|
|
|
|
(GdkPixbufDestroyNotify) g_free,
|
|
|
|
|
NULL);
|
2018-06-24 16:59:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 16:15:25 +02:00
|
|
|
|
/* Small assertion test to get a warning if we ever get NULL return
|
|
|
|
|
* value, as this is never supposed to happen.
|
|
|
|
|
*/
|
|
|
|
|
g_return_val_if_fail (pixbuf != NULL, NULL);
|
|
|
|
|
|
2018-06-24 16:59:39 +02:00
|
|
|
|
return pixbuf;
|
2014-05-08 09:11:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-06-11 22:38:34 +02:00
|
|
|
|
GimpTabStyle
|
|
|
|
|
gimp_preview_tab_style_to_icon (GimpTabStyle tab_style)
|
|
|
|
|
{
|
|
|
|
|
switch (tab_style)
|
|
|
|
|
{
|
|
|
|
|
case GIMP_TAB_STYLE_PREVIEW:
|
|
|
|
|
tab_style = GIMP_TAB_STYLE_ICON;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GIMP_TAB_STYLE_PREVIEW_NAME:
|
|
|
|
|
tab_style = GIMP_TAB_STYLE_ICON_NAME;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GIMP_TAB_STYLE_PREVIEW_BLURB:
|
|
|
|
|
tab_style = GIMP_TAB_STYLE_ICON_BLURB;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tab_style;
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-28 23:30:57 +00:00
|
|
|
|
const gchar *
|
|
|
|
|
gimp_get_mod_string (GdkModifierType modifiers)
|
|
|
|
|
{
|
2011-10-05 01:28:45 +02:00
|
|
|
|
static GHashTable *mod_labels;
|
2011-10-05 18:47:37 +02:00
|
|
|
|
gchar *label;
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-05 01:28:45 +02:00
|
|
|
|
if (! modifiers)
|
|
|
|
|
return NULL;
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-05 01:28:45 +02:00
|
|
|
|
if (G_UNLIKELY (! mod_labels))
|
|
|
|
|
mod_labels = g_hash_table_new (g_int_hash, g_int_equal);
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-06 22:26:43 +02:00
|
|
|
|
modifiers = gimp_replace_virtual_modifiers (modifiers);
|
|
|
|
|
|
2011-10-05 01:28:45 +02:00
|
|
|
|
label = g_hash_table_lookup (mod_labels, &modifiers);
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-05 18:47:37 +02:00
|
|
|
|
if (! label)
|
|
|
|
|
{
|
|
|
|
|
GtkAccelLabelClass *accel_label_class;
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-05 18:47:37 +02:00
|
|
|
|
label = gtk_accelerator_get_label (0, modifiers);
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-05 18:47:37 +02:00
|
|
|
|
accel_label_class = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
|
2011-10-05 01:28:45 +02:00
|
|
|
|
|
2011-10-05 18:47:37 +02:00
|
|
|
|
if (accel_label_class->mod_separator &&
|
|
|
|
|
*accel_label_class->mod_separator)
|
|
|
|
|
{
|
|
|
|
|
gchar *sep = g_strrstr (label, accel_label_class->mod_separator);
|
2011-10-05 01:28:45 +02:00
|
|
|
|
|
2011-10-05 18:47:37 +02:00
|
|
|
|
if (sep - label ==
|
|
|
|
|
strlen (label) - strlen (accel_label_class->mod_separator))
|
|
|
|
|
*sep = '\0';
|
|
|
|
|
}
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-05 18:47:37 +02:00
|
|
|
|
g_type_class_unref (accel_label_class);
|
2011-10-05 01:28:45 +02:00
|
|
|
|
|
2011-10-05 18:47:37 +02:00
|
|
|
|
g_hash_table_insert (mod_labels,
|
2021-08-26 17:18:32 +02:00
|
|
|
|
g_memdup2 (&modifiers, sizeof (GdkModifierType)),
|
2011-10-05 18:47:37 +02:00
|
|
|
|
label);
|
|
|
|
|
}
|
2011-10-05 01:28:45 +02:00
|
|
|
|
|
|
|
|
|
return label;
|
2004-07-20 18:50:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-08-01 23:42:12 +00:00
|
|
|
|
#define BUF_SIZE 100
|
|
|
|
|
/**
|
|
|
|
|
* gimp_suggest_modifiers:
|
2015-10-17 15:31:08 +02:00
|
|
|
|
* @message: initial text for the message
|
|
|
|
|
* @modifiers: bit mask of modifiers that should be suggested
|
|
|
|
|
* @extend_selection_format: optional format string for the
|
|
|
|
|
* "Extend selection" modifier
|
|
|
|
|
* @toggle_behavior_format: optional format string for the
|
|
|
|
|
* "Toggle behavior" modifier
|
|
|
|
|
* @alt_format: optional format string for the Alt modifier
|
2006-08-01 23:42:12 +00:00
|
|
|
|
*
|
|
|
|
|
* Utility function to build a message suggesting to use some
|
|
|
|
|
* modifiers for performing different actions (only Shift, Ctrl and
|
|
|
|
|
* Alt are currently supported). If some of these modifiers are
|
|
|
|
|
* already active, they will not be suggested. The optional format
|
2015-10-17 15:31:08 +02:00
|
|
|
|
* strings #extend_selection_format, #toggle_behavior_format and
|
|
|
|
|
* #alt_format may be used to describe what the modifier will do.
|
|
|
|
|
* They must contain a single '%%s' which will be replaced by the name
|
|
|
|
|
* of the modifier. They can also be %NULL if the modifier name
|
|
|
|
|
* should be left alone.
|
2006-08-01 23:42:12 +00:00
|
|
|
|
*
|
2019-08-03 00:10:14 +02:00
|
|
|
|
* Returns: a newly allocated string containing the message.
|
2006-08-01 23:42:12 +00:00
|
|
|
|
**/
|
|
|
|
|
gchar *
|
|
|
|
|
gimp_suggest_modifiers (const gchar *message,
|
|
|
|
|
GdkModifierType modifiers,
|
2015-10-17 15:31:08 +02:00
|
|
|
|
const gchar *extend_selection_format,
|
|
|
|
|
const gchar *toggle_behavior_format,
|
2006-08-01 23:42:12 +00:00
|
|
|
|
const gchar *alt_format)
|
|
|
|
|
{
|
2015-10-17 15:31:08 +02:00
|
|
|
|
GdkModifierType extend_mask = gimp_get_extend_selection_mask ();
|
|
|
|
|
GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask ();
|
|
|
|
|
gchar msg_buf[3][BUF_SIZE];
|
|
|
|
|
gint num_msgs = 0;
|
|
|
|
|
gboolean try = FALSE;
|
2006-08-01 23:42:12 +00:00
|
|
|
|
|
2015-10-17 15:31:08 +02:00
|
|
|
|
if (modifiers & extend_mask)
|
2006-08-01 23:42:12 +00:00
|
|
|
|
{
|
2015-10-17 15:31:08 +02:00
|
|
|
|
if (extend_selection_format && *extend_selection_format)
|
2006-09-12 06:37:54 +00:00
|
|
|
|
{
|
2015-10-17 15:31:08 +02:00
|
|
|
|
g_snprintf (msg_buf[num_msgs], BUF_SIZE, extend_selection_format,
|
|
|
|
|
gimp_get_mod_string (extend_mask));
|
2006-09-12 06:37:54 +00:00
|
|
|
|
}
|
2006-08-01 23:42:12 +00:00
|
|
|
|
else
|
2006-09-12 06:37:54 +00:00
|
|
|
|
{
|
2011-10-06 22:53:42 +02:00
|
|
|
|
g_strlcpy (msg_buf[num_msgs],
|
2015-10-17 15:31:08 +02:00
|
|
|
|
gimp_get_mod_string (extend_mask), BUF_SIZE);
|
2006-09-12 06:37:54 +00:00
|
|
|
|
try = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-01 23:42:12 +00:00
|
|
|
|
num_msgs++;
|
|
|
|
|
}
|
2006-09-12 06:37:54 +00:00
|
|
|
|
|
2015-10-17 15:31:08 +02:00
|
|
|
|
if (modifiers & toggle_mask)
|
2006-08-01 23:42:12 +00:00
|
|
|
|
{
|
2015-10-17 15:31:08 +02:00
|
|
|
|
if (toggle_behavior_format && *toggle_behavior_format)
|
2006-09-12 06:37:54 +00:00
|
|
|
|
{
|
2015-10-17 15:31:08 +02:00
|
|
|
|
g_snprintf (msg_buf[num_msgs], BUF_SIZE, toggle_behavior_format,
|
|
|
|
|
gimp_get_mod_string (toggle_mask));
|
2006-09-12 06:37:54 +00:00
|
|
|
|
}
|
2006-08-01 23:42:12 +00:00
|
|
|
|
else
|
2006-09-12 06:37:54 +00:00
|
|
|
|
{
|
2011-10-06 22:53:42 +02:00
|
|
|
|
g_strlcpy (msg_buf[num_msgs],
|
2015-10-17 15:31:08 +02:00
|
|
|
|
gimp_get_mod_string (toggle_mask), BUF_SIZE);
|
2006-09-12 06:37:54 +00:00
|
|
|
|
try = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-01 23:42:12 +00:00
|
|
|
|
num_msgs++;
|
|
|
|
|
}
|
2006-09-12 06:37:54 +00:00
|
|
|
|
|
2006-08-01 23:42:12 +00:00
|
|
|
|
if (modifiers & GDK_MOD1_MASK)
|
|
|
|
|
{
|
|
|
|
|
if (alt_format && *alt_format)
|
2006-09-12 06:37:54 +00:00
|
|
|
|
{
|
|
|
|
|
g_snprintf (msg_buf[num_msgs], BUF_SIZE, alt_format,
|
2011-10-05 01:28:45 +02:00
|
|
|
|
gimp_get_mod_string (GDK_MOD1_MASK));
|
2006-09-12 06:37:54 +00:00
|
|
|
|
}
|
2006-08-01 23:42:12 +00:00
|
|
|
|
else
|
2006-09-12 06:37:54 +00:00
|
|
|
|
{
|
2011-10-06 22:53:42 +02:00
|
|
|
|
g_strlcpy (msg_buf[num_msgs],
|
|
|
|
|
gimp_get_mod_string (GDK_MOD1_MASK), BUF_SIZE);
|
2006-09-12 06:37:54 +00:00
|
|
|
|
try = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-01 23:42:12 +00:00
|
|
|
|
num_msgs++;
|
|
|
|
|
}
|
2006-09-12 06:37:54 +00:00
|
|
|
|
|
2006-08-01 23:42:12 +00:00
|
|
|
|
/* This convoluted way to build the message using multiple format strings
|
|
|
|
|
* tries to make the messages easier to translate to other languages.
|
|
|
|
|
*/
|
2006-09-12 06:37:54 +00:00
|
|
|
|
|
|
|
|
|
switch (num_msgs)
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
return g_strdup_printf (try ? _("%s (try %s)") : _("%s (%s)"),
|
|
|
|
|
message, msg_buf[0]);
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
return g_strdup_printf (_("%s (try %s, %s)"),
|
|
|
|
|
message, msg_buf[0], msg_buf[1]);
|
|
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
return g_strdup_printf (_("%s (try %s, %s, %s)"),
|
|
|
|
|
message, msg_buf[0], msg_buf[1], msg_buf[2]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_strdup (message);
|
2006-08-01 23:42:12 +00:00
|
|
|
|
}
|
|
|
|
|
#undef BUF_SIZE
|
2004-06-28 23:30:57 +00:00
|
|
|
|
|
2011-10-05 19:15:17 +02:00
|
|
|
|
GimpChannelOps
|
2011-10-06 10:24:49 +02:00
|
|
|
|
gimp_modifiers_to_channel_op (GdkModifierType modifiers)
|
2011-10-05 19:15:17 +02:00
|
|
|
|
{
|
2011-10-06 10:24:49 +02:00
|
|
|
|
GdkModifierType extend_mask = gimp_get_extend_selection_mask ();
|
|
|
|
|
GdkModifierType modify_mask = gimp_get_modify_selection_mask ();
|
2011-10-05 19:15:17 +02:00
|
|
|
|
|
|
|
|
|
if (modifiers & extend_mask)
|
|
|
|
|
{
|
|
|
|
|
if (modifiers & modify_mask)
|
|
|
|
|
{
|
|
|
|
|
return GIMP_CHANNEL_OP_INTERSECT;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return GIMP_CHANNEL_OP_ADD;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (modifiers & modify_mask)
|
|
|
|
|
{
|
|
|
|
|
return GIMP_CHANNEL_OP_SUBTRACT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GIMP_CHANNEL_OP_REPLACE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GdkModifierType
|
|
|
|
|
gimp_replace_virtual_modifiers (GdkModifierType modifiers)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
|
|
|
GdkModifierType result = 0;
|
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
GdkModifierType real = 1 << i;
|
|
|
|
|
|
|
|
|
|
if (modifiers & real)
|
|
|
|
|
{
|
|
|
|
|
GdkModifierType virtual = real;
|
|
|
|
|
|
|
|
|
|
gdk_keymap_add_virtual_modifiers (gdk_keymap_get_for_display (display),
|
|
|
|
|
&virtual);
|
|
|
|
|
|
|
|
|
|
if (virtual == real)
|
|
|
|
|
result |= virtual;
|
|
|
|
|
else
|
|
|
|
|
result |= virtual & ~real;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-08 10:00:20 +01:00
|
|
|
|
GdkModifierType
|
|
|
|
|
gimp_get_primary_accelerator_mask (void)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
|
|
|
|
|
|
|
|
return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-06 10:20:23 +02:00
|
|
|
|
GdkModifierType
|
|
|
|
|
gimp_get_extend_selection_mask (void)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
|
|
|
|
|
|
|
|
return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_EXTEND_SELECTION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GdkModifierType
|
|
|
|
|
gimp_get_modify_selection_mask (void)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
|
|
|
|
|
|
|
|
return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GdkModifierType
|
|
|
|
|
gimp_get_toggle_behavior_mask (void)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
|
|
|
|
|
|
|
|
/* use the modify selection modifier */
|
|
|
|
|
return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-06 16:42:42 +02:00
|
|
|
|
GdkModifierType
|
|
|
|
|
gimp_get_constrain_behavior_mask (void)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
|
|
|
|
|
|
|
|
/* use the modify selection modifier */
|
|
|
|
|
return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-10 00:17:20 +02:00
|
|
|
|
GdkModifierType
|
|
|
|
|
gimp_get_all_modifiers_mask (void)
|
|
|
|
|
{
|
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
|
|
|
|
|
|
|
|
return (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK |
|
|
|
|
|
gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR) |
|
|
|
|
|
gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_EXTEND_SELECTION) |
|
|
|
|
|
gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
|
|
|
|
|
GDK_MODIFIER_INTENT_MODIFY_SELECTION));
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-10 16:44:44 +00:00
|
|
|
|
/**
|
2014-05-02 23:56:16 +02:00
|
|
|
|
* gimp_get_monitor_resolution:
|
|
|
|
|
* @screen: a #GdkScreen
|
|
|
|
|
* @monitor: a monitor number
|
|
|
|
|
* @xres: returns the horizontal monitor resolution (in dpi)
|
|
|
|
|
* @yres: returns the vertical monitor resolution (in dpi)
|
2003-09-09 11:35:27 +00:00
|
|
|
|
*
|
2014-05-02 23:56:16 +02:00
|
|
|
|
* Retrieves the monitor's resolution from GDK.
|
2003-06-10 16:44:44 +00:00
|
|
|
|
**/
|
2003-05-29 11:34:30 +00:00
|
|
|
|
void
|
2018-04-29 17:27:47 +02:00
|
|
|
|
gimp_get_monitor_resolution (GdkMonitor *monitor,
|
|
|
|
|
gdouble *xres,
|
|
|
|
|
gdouble *yres)
|
2003-05-29 11:34:30 +00:00
|
|
|
|
{
|
2014-05-02 23:56:16 +02:00
|
|
|
|
GdkRectangle size_pixels;
|
|
|
|
|
gint width_mm, height_mm;
|
|
|
|
|
gdouble x = 0.0;
|
|
|
|
|
gdouble y = 0.0;
|
2018-06-25 01:31:09 +02:00
|
|
|
|
#ifdef PLATFORM_OSX
|
|
|
|
|
CGSize size;
|
|
|
|
|
#endif
|
2003-05-29 11:34:30 +00:00
|
|
|
|
|
2018-04-29 17:27:47 +02:00
|
|
|
|
g_return_if_fail (GDK_IS_MONITOR (monitor));
|
2003-05-29 11:34:30 +00:00
|
|
|
|
g_return_if_fail (xres != NULL);
|
|
|
|
|
g_return_if_fail (yres != NULL);
|
|
|
|
|
|
2018-06-25 01:31:09 +02:00
|
|
|
|
#ifndef PLATFORM_OSX
|
2018-04-29 17:27:47 +02:00
|
|
|
|
gdk_monitor_get_geometry (monitor, &size_pixels);
|
2003-05-29 11:34:30 +00:00
|
|
|
|
|
2018-04-29 17:27:47 +02:00
|
|
|
|
width_mm = gdk_monitor_get_width_mm (monitor);
|
|
|
|
|
height_mm = gdk_monitor_get_height_mm (monitor);
|
2018-06-25 01:31:09 +02:00
|
|
|
|
#else
|
|
|
|
|
width_mm = 0;
|
|
|
|
|
height_mm = 0;
|
|
|
|
|
size = CGDisplayScreenSize (kCGDirectMainDisplay);
|
|
|
|
|
if (!CGSizeEqualToSize (size, CGSizeZero))
|
|
|
|
|
{
|
|
|
|
|
width_mm = size.width;
|
|
|
|
|
height_mm = size.height;
|
|
|
|
|
}
|
|
|
|
|
size_pixels.width = CGDisplayPixelsWide (kCGDirectMainDisplay);
|
|
|
|
|
size_pixels.height = CGDisplayPixelsHigh (kCGDirectMainDisplay);
|
|
|
|
|
#endif
|
2003-05-29 11:34:30 +00:00
|
|
|
|
/*
|
|
|
|
|
* From xdpyinfo.c:
|
|
|
|
|
*
|
|
|
|
|
* there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
|
|
|
|
|
*
|
|
|
|
|
* dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
|
|
|
|
|
* = N pixels / (M inch / 25.4)
|
|
|
|
|
* = N * 25.4 pixels / M inch
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (width_mm > 0 && height_mm > 0)
|
|
|
|
|
{
|
2014-05-02 23:56:16 +02:00
|
|
|
|
x = (size_pixels.width * 25.4) / (gdouble) width_mm;
|
|
|
|
|
y = (size_pixels.height * 25.4) / (gdouble) height_mm;
|
2003-05-29 11:34:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (x < GIMP_MIN_RESOLUTION || x > GIMP_MAX_RESOLUTION ||
|
|
|
|
|
y < GIMP_MIN_RESOLUTION || y > GIMP_MAX_RESOLUTION)
|
|
|
|
|
{
|
2014-05-03 17:17:46 +02:00
|
|
|
|
g_printerr ("gimp_get_monitor_resolution(): GDK returned bogus "
|
2014-05-08 12:12:19 +02:00
|
|
|
|
"values for the monitor resolution, using 96 dpi instead.\n");
|
2003-05-29 11:34:30 +00:00
|
|
|
|
|
2007-02-01 10:44:01 +00:00
|
|
|
|
x = 96.0;
|
|
|
|
|
y = 96.0;
|
2003-05-29 11:34:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* round the value to full integers to give more pleasant results */
|
|
|
|
|
*xres = ROUND (x);
|
|
|
|
|
*yres = ROUND (y);
|
|
|
|
|
}
|
2003-06-10 16:44:44 +00:00
|
|
|
|
|
2011-02-07 22:16:05 +01:00
|
|
|
|
gboolean
|
|
|
|
|
gimp_get_style_color (GtkWidget *widget,
|
|
|
|
|
const gchar *property_name,
|
|
|
|
|
GdkRGBA *color)
|
|
|
|
|
{
|
|
|
|
|
GdkRGBA *c = NULL;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
|
|
|
|
|
g_return_val_if_fail (property_name != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (color != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
gtk_widget_style_get (widget,
|
|
|
|
|
property_name, &c,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
if (c)
|
|
|
|
|
{
|
|
|
|
|
*color = *c;
|
|
|
|
|
gdk_rgba_free (c);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* return ugly magenta to indicate that something is wrong */
|
|
|
|
|
color->red = 1.0;
|
|
|
|
|
color->green = 1.0;
|
|
|
|
|
color->blue = 0.0;
|
|
|
|
|
color->alpha = 1.0;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-16 23:18:23 +00:00
|
|
|
|
void
|
|
|
|
|
gimp_window_set_hint (GtkWindow *window,
|
|
|
|
|
GimpWindowHint hint)
|
2003-11-20 20:36:55 +00:00
|
|
|
|
{
|
2004-01-16 23:18:23 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_WINDOW (window));
|
|
|
|
|
|
2003-11-20 20:36:55 +00:00
|
|
|
|
switch (hint)
|
|
|
|
|
{
|
2004-01-16 23:18:23 +00:00
|
|
|
|
case GIMP_WINDOW_HINT_NORMAL:
|
|
|
|
|
gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_NORMAL);
|
|
|
|
|
break;
|
2003-11-20 20:36:55 +00:00
|
|
|
|
|
2004-01-16 23:18:23 +00:00
|
|
|
|
case GIMP_WINDOW_HINT_UTILITY:
|
|
|
|
|
gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_UTILITY);
|
|
|
|
|
break;
|
2004-05-04 13:31:57 +00:00
|
|
|
|
|
|
|
|
|
case GIMP_WINDOW_HINT_KEEP_ABOVE:
|
|
|
|
|
gtk_window_set_keep_above (window, TRUE);
|
|
|
|
|
break;
|
2003-11-20 20:36:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2004-08-10 22:21:56 +00:00
|
|
|
|
|
2006-09-22 09:24:41 +00:00
|
|
|
|
/* similar to what we have in libgimp/gimpui.c */
|
2011-02-16 22:51:02 +01:00
|
|
|
|
static GdkWindow *
|
2023-08-14 18:10:41 +02:00
|
|
|
|
gimp_get_foreign_window (gpointer window)
|
2011-02-16 22:51:02 +01:00
|
|
|
|
{
|
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
2020-06-21 12:54:13 +02:00
|
|
|
|
if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
|
|
|
|
|
return gdk_x11_window_foreign_new_for_display (gdk_display_get_default (),
|
2023-08-14 18:10:41 +02:00
|
|
|
|
(Window) window);
|
2011-02-16 22:51:02 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef GDK_WINDOWING_WIN32
|
|
|
|
|
return gdk_win32_window_foreign_new_for_display (gdk_display_get_default (),
|
2023-08-14 18:10:41 +02:00
|
|
|
|
(HWND) window);
|
2011-02-16 22:51:02 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-22 09:24:41 +00:00
|
|
|
|
void
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
gimp_window_set_transient_for (GtkWindow *window,
|
|
|
|
|
GimpProgress *parent)
|
2006-09-22 09:24:41 +00:00
|
|
|
|
{
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
g_signal_connect_after (window, "map-event",
|
|
|
|
|
G_CALLBACK (gimp_window_transient_on_mapped),
|
|
|
|
|
parent);
|
2006-09-22 09:24:41 +00:00
|
|
|
|
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
if (gtk_widget_get_mapped (GTK_WIDGET (window)))
|
|
|
|
|
gimp_window_transient_on_mapped (GTK_WIDGET (window), NULL, parent);
|
2006-09-22 09:24:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
app, libgimp, pdb: add a parent_window parameter to gimp_*_popup() PDB calls.
Brush, font, gradient, palette and pattern choices are currently chosen through
a dialog created by the core, which then returns the user choice to the calling
plug-in. This has the unfortunate consequence of having a pile of likely at
least 3 windows (main GIMP window by core process, plug-in window by plug-in
process, then the choice popup by the core process) shared in 2 processes, which
often end up under each other and that's messy. Even more as the choice popup is
kinda expected to be like a sub-part of the plug-in dialog.
So anyway, now the plug-in can send its window handle to the core so that the
resource choice dialog ends up always above the plug-in dialog.
Of course, it will always work only on platforms where we have working
inter-process transient support.
2023-08-15 00:12:16 +02:00
|
|
|
|
void
|
|
|
|
|
gimp_window_set_transient_for_handle (GtkWindow *window,
|
|
|
|
|
GBytes *handle)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GTK_IS_WINDOW (window));
|
|
|
|
|
g_return_if_fail (handle != NULL);
|
|
|
|
|
|
|
|
|
|
g_signal_connect_data (window, "map-event",
|
|
|
|
|
G_CALLBACK (gimp_window_set_transient_cb),
|
|
|
|
|
g_bytes_ref (handle),
|
|
|
|
|
(GClosureNotify) g_bytes_unref,
|
|
|
|
|
G_CONNECT_AFTER);
|
|
|
|
|
|
|
|
|
|
if (gtk_widget_get_mapped (GTK_WIDGET (window)))
|
|
|
|
|
gimp_window_set_transient_cb (GTK_WIDGET (window), NULL, handle);
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-16 00:46:21 +00:00
|
|
|
|
static void
|
2023-02-05 22:55:37 +01:00
|
|
|
|
gimp_widget_accels_changed (GimpAction *action,
|
|
|
|
|
const gchar **accels,
|
|
|
|
|
GtkWidget *widget)
|
2005-09-16 00:46:21 +00:00
|
|
|
|
{
|
2023-02-05 22:55:37 +01:00
|
|
|
|
const gchar *tooltip;
|
|
|
|
|
const gchar *help_id;
|
2005-09-16 00:46:21 +00:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
tooltip = gimp_action_get_tooltip (action);
|
|
|
|
|
help_id = gimp_action_get_help_id (action);
|
2005-09-16 00:46:21 +00:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
if (accels && accels[0])
|
2005-09-16 00:46:21 +00:00
|
|
|
|
{
|
2023-02-05 22:55:37 +01:00
|
|
|
|
gchar *escaped = g_markup_escape_text (tooltip, -1);
|
|
|
|
|
gchar *tmp = g_strdup_printf ("%s <b>%s</b>", escaped, accels[0]);
|
2005-09-16 00:46:21 +00:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
g_free (escaped);
|
2005-09-16 00:46:21 +00:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
gimp_help_set_help_data_with_markup (widget, tmp, help_id);
|
|
|
|
|
g_free (tmp);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gimp_help_set_help_data (widget, tooltip, help_id);
|
2005-09-16 00:46:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-09 23:28:37 +02:00
|
|
|
|
static void gimp_accel_help_widget_weak_notify (gpointer accel_group,
|
|
|
|
|
GObject *where_widget_was);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gimp_accel_help_accel_group_weak_notify (gpointer widget,
|
2023-02-05 22:55:37 +01:00
|
|
|
|
GObject *where_action_was)
|
2016-09-09 23:28:37 +02:00
|
|
|
|
{
|
|
|
|
|
g_object_weak_unref (widget,
|
|
|
|
|
gimp_accel_help_widget_weak_notify,
|
2023-02-05 22:55:37 +01:00
|
|
|
|
where_action_was);
|
2016-09-09 23:28:37 +02:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
g_object_set_data (widget, "gimp-accel-help-action", NULL);
|
2016-09-09 23:28:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2023-02-05 22:55:37 +01:00
|
|
|
|
gimp_accel_help_widget_weak_notify (gpointer action,
|
2016-09-09 23:28:37 +02:00
|
|
|
|
GObject *where_widget_was)
|
|
|
|
|
{
|
2023-02-05 22:55:37 +01:00
|
|
|
|
g_object_weak_unref (action,
|
2016-09-09 23:28:37 +02:00
|
|
|
|
gimp_accel_help_accel_group_weak_notify,
|
|
|
|
|
where_widget_was);
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-16 00:46:21 +00:00
|
|
|
|
void
|
2019-07-02 03:54:38 +02:00
|
|
|
|
gimp_widget_set_accel_help (GtkWidget *widget,
|
|
|
|
|
GimpAction *action)
|
2005-09-16 00:46:21 +00:00
|
|
|
|
{
|
2023-02-05 22:55:37 +01:00
|
|
|
|
GimpAction *prev_action;
|
2016-09-09 23:28:37 +02:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
prev_action = g_object_get_data (G_OBJECT (widget), "gimp-accel-help-action");
|
2016-09-09 23:28:37 +02:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
if (prev_action)
|
2016-09-09 23:28:37 +02:00
|
|
|
|
{
|
2023-02-05 22:55:37 +01:00
|
|
|
|
g_signal_handlers_disconnect_by_func (prev_action,
|
|
|
|
|
gimp_widget_accels_changed,
|
2016-09-09 23:28:37 +02:00
|
|
|
|
widget);
|
2023-02-05 22:55:37 +01:00
|
|
|
|
g_object_weak_unref (G_OBJECT (action),
|
2016-09-09 23:28:37 +02:00
|
|
|
|
gimp_accel_help_accel_group_weak_notify,
|
|
|
|
|
widget);
|
|
|
|
|
g_object_weak_unref (G_OBJECT (widget),
|
|
|
|
|
gimp_accel_help_widget_weak_notify,
|
2023-02-05 22:55:37 +01:00
|
|
|
|
action);
|
|
|
|
|
g_object_set_data (G_OBJECT (widget), "gimp-accel-help-action", NULL);
|
2016-09-09 23:28:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
if (action)
|
2005-09-16 00:46:21 +00:00
|
|
|
|
{
|
2023-02-05 22:55:37 +01:00
|
|
|
|
gchar **accels;
|
2016-09-09 23:28:37 +02:00
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
g_object_set_data (G_OBJECT (widget), "gimp-accel-help-action",
|
|
|
|
|
action);
|
|
|
|
|
g_object_weak_ref (G_OBJECT (action),
|
2016-09-09 23:28:37 +02:00
|
|
|
|
gimp_accel_help_accel_group_weak_notify,
|
|
|
|
|
widget);
|
|
|
|
|
g_object_weak_ref (G_OBJECT (widget),
|
|
|
|
|
gimp_accel_help_widget_weak_notify,
|
2023-02-05 22:55:37 +01:00
|
|
|
|
action);
|
2005-09-16 00:46:21 +00:00
|
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (widget), "gimp-accel-action",
|
|
|
|
|
action);
|
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
g_signal_connect_object (action, "accels-changed",
|
|
|
|
|
G_CALLBACK (gimp_widget_accels_changed),
|
2005-09-16 00:46:21 +00:00
|
|
|
|
widget, 0);
|
|
|
|
|
|
2023-02-05 22:55:37 +01:00
|
|
|
|
accels = gimp_action_get_display_accels (action);
|
|
|
|
|
gimp_widget_accels_changed (action, (const gchar **) accels, widget);
|
|
|
|
|
g_strfreev (accels);
|
2005-09-16 00:46:21 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-07-02 03:54:38 +02:00
|
|
|
|
gimp_help_set_help_data (widget,
|
|
|
|
|
gimp_action_get_tooltip (action),
|
|
|
|
|
gimp_action_get_help_id (action));
|
2005-09-16 00:46:21 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-11 12:56:26 +00:00
|
|
|
|
|
2006-10-09 08:17:22 +00:00
|
|
|
|
const gchar *
|
2014-05-07 15:30:38 +02:00
|
|
|
|
gimp_get_message_icon_name (GimpMessageSeverity severity)
|
2006-08-11 12:56:26 +00:00
|
|
|
|
{
|
2006-10-09 08:17:22 +00:00
|
|
|
|
switch (severity)
|
|
|
|
|
{
|
|
|
|
|
case GIMP_MESSAGE_INFO:
|
2017-03-05 16:01:59 +01:00
|
|
|
|
return GIMP_ICON_DIALOG_INFORMATION;
|
2006-08-11 12:56:26 +00:00
|
|
|
|
|
2006-10-09 08:17:22 +00:00
|
|
|
|
case GIMP_MESSAGE_WARNING:
|
2017-03-05 16:01:59 +01:00
|
|
|
|
return GIMP_ICON_DIALOG_WARNING;
|
2006-08-11 12:56:26 +00:00
|
|
|
|
|
2006-10-09 08:17:22 +00:00
|
|
|
|
case GIMP_MESSAGE_ERROR:
|
2017-03-05 16:01:59 +01:00
|
|
|
|
return GIMP_ICON_DIALOG_ERROR;
|
2018-02-12 17:20:53 +01:00
|
|
|
|
|
|
|
|
|
case GIMP_MESSAGE_BUG_WARNING:
|
|
|
|
|
case GIMP_MESSAGE_BUG_CRITICAL:
|
|
|
|
|
return GIMP_ICON_WILBER_EEK;
|
2006-10-09 08:17:22 +00:00
|
|
|
|
}
|
2006-08-11 12:56:26 +00:00
|
|
|
|
|
2017-03-05 16:01:59 +01:00
|
|
|
|
g_return_val_if_reached (GIMP_ICON_DIALOG_WARNING);
|
2006-08-11 12:56:26 +00:00
|
|
|
|
}
|
2008-03-19 09:29:30 +00:00
|
|
|
|
|
2016-10-29 16:50:13 +02:00
|
|
|
|
gboolean
|
|
|
|
|
gimp_get_color_tag_color (GimpColorTag color_tag,
|
2017-12-07 16:42:54 -05:00
|
|
|
|
GimpRGB *color,
|
|
|
|
|
gboolean inherited)
|
2016-10-29 16:50:13 +02:00
|
|
|
|
{
|
|
|
|
|
static const struct
|
|
|
|
|
{
|
|
|
|
|
guchar r;
|
|
|
|
|
guchar g;
|
|
|
|
|
guchar b;
|
|
|
|
|
}
|
|
|
|
|
colors[] =
|
|
|
|
|
{
|
2017-02-19 14:06:43 +03:00
|
|
|
|
{ 0, 0, 0 }, /* none */
|
|
|
|
|
{ 84, 102, 159 }, /* blue */
|
|
|
|
|
{ 111, 143, 48 }, /* green */
|
|
|
|
|
{ 210, 182, 45 }, /* yellow */
|
|
|
|
|
{ 217, 122, 38 }, /* orange */
|
|
|
|
|
{ 87, 53, 25 }, /* brown */
|
|
|
|
|
{ 170, 42, 47 }, /* red */
|
|
|
|
|
{ 99, 66, 174 }, /* violet */
|
|
|
|
|
{ 87, 87, 87 } /* gray */
|
2016-10-29 16:50:13 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (color != NULL, FALSE);
|
2017-12-07 16:42:54 -05:00
|
|
|
|
g_return_val_if_fail (color_tag < G_N_ELEMENTS (colors), FALSE);
|
2016-10-29 16:50:13 +02:00
|
|
|
|
|
|
|
|
|
if (color_tag > GIMP_COLOR_TAG_NONE)
|
|
|
|
|
{
|
|
|
|
|
gimp_rgba_set_uchar (color,
|
|
|
|
|
colors[color_tag].r,
|
|
|
|
|
colors[color_tag].g,
|
|
|
|
|
colors[color_tag].b,
|
|
|
|
|
255);
|
|
|
|
|
|
2017-12-07 16:42:54 -05:00
|
|
|
|
if (inherited)
|
|
|
|
|
{
|
|
|
|
|
gimp_rgb_composite (color, &(GimpRGB) {1.0, 1.0, 1.0, 0.2},
|
|
|
|
|
GIMP_RGB_COMPOSITE_NORMAL);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-29 16:50:13 +02:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-30 11:16:28 +00:00
|
|
|
|
void
|
|
|
|
|
gimp_pango_layout_set_scale (PangoLayout *layout,
|
|
|
|
|
gdouble scale)
|
|
|
|
|
{
|
|
|
|
|
PangoAttrList *attrs;
|
|
|
|
|
PangoAttribute *attr;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (PANGO_IS_LAYOUT (layout));
|
|
|
|
|
|
|
|
|
|
attrs = pango_attr_list_new ();
|
|
|
|
|
|
|
|
|
|
attr = pango_attr_scale_new (scale);
|
|
|
|
|
attr->start_index = 0;
|
|
|
|
|
attr->end_index = -1;
|
|
|
|
|
pango_attr_list_insert (attrs, attr);
|
|
|
|
|
|
|
|
|
|
pango_layout_set_attributes (layout, attrs);
|
|
|
|
|
pango_attr_list_unref (attrs);
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-19 09:29:30 +00:00
|
|
|
|
void
|
|
|
|
|
gimp_pango_layout_set_weight (PangoLayout *layout,
|
|
|
|
|
PangoWeight weight)
|
|
|
|
|
{
|
|
|
|
|
PangoAttrList *attrs;
|
|
|
|
|
PangoAttribute *attr;
|
|
|
|
|
|
2008-09-30 11:16:28 +00:00
|
|
|
|
g_return_if_fail (PANGO_IS_LAYOUT (layout));
|
|
|
|
|
|
2008-03-19 09:29:30 +00:00
|
|
|
|
attrs = pango_attr_list_new ();
|
|
|
|
|
|
2008-09-30 11:09:06 +00:00
|
|
|
|
attr = pango_attr_weight_new (weight);
|
2008-03-19 09:29:30 +00:00
|
|
|
|
attr->start_index = 0;
|
|
|
|
|
attr->end_index = -1;
|
|
|
|
|
pango_attr_list_insert (attrs, attr);
|
|
|
|
|
|
|
|
|
|
pango_layout_set_attributes (layout, attrs);
|
|
|
|
|
pango_attr_list_unref (attrs);
|
|
|
|
|
}
|
2009-11-28 19:12:39 +01:00
|
|
|
|
|
2017-10-28 09:30:42 -04:00
|
|
|
|
static gboolean
|
2018-02-11 17:25:33 +01:00
|
|
|
|
gimp_highlight_widget_draw (GtkWidget *widget,
|
|
|
|
|
cairo_t *cr,
|
|
|
|
|
gpointer data)
|
2017-10-28 09:30:42 -04:00
|
|
|
|
{
|
2021-02-09 11:50:23 +01:00
|
|
|
|
GdkRectangle *rect = (GdkRectangle *) data;
|
|
|
|
|
|
2018-05-13 15:11:13 +02:00
|
|
|
|
/* this code is a straight copy of draw_flash() from gtk-inspector's
|
|
|
|
|
* inspect-button.c
|
2017-10-28 09:30:42 -04:00
|
|
|
|
*/
|
|
|
|
|
|
2018-05-13 15:11:13 +02:00
|
|
|
|
GtkAllocation alloc;
|
2017-10-28 09:30:42 -04:00
|
|
|
|
|
2021-02-09 11:50:23 +01:00
|
|
|
|
if (rect)
|
|
|
|
|
{
|
|
|
|
|
alloc.x = rect->x;
|
|
|
|
|
alloc.y = rect->y;
|
|
|
|
|
alloc.width = rect->width;
|
|
|
|
|
alloc.height = rect->height;
|
|
|
|
|
}
|
|
|
|
|
else if (GTK_IS_WINDOW (widget))
|
2018-05-13 15:11:13 +02:00
|
|
|
|
{
|
|
|
|
|
GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
|
/* We don't want to draw the drag highlight around the
|
|
|
|
|
* CSD window decorations
|
|
|
|
|
*/
|
|
|
|
|
if (child == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
gtk_widget_get_allocation (child, &alloc);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
alloc.x = 0;
|
|
|
|
|
alloc.y = 0;
|
|
|
|
|
alloc.width = gtk_widget_get_allocated_width (widget);
|
|
|
|
|
alloc.height = gtk_widget_get_allocated_height (widget);
|
|
|
|
|
}
|
2017-10-28 09:30:42 -04:00
|
|
|
|
|
2018-05-13 15:11:13 +02:00
|
|
|
|
cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.2);
|
2018-02-11 17:25:33 +01:00
|
|
|
|
cairo_rectangle (cr,
|
2018-05-13 15:11:13 +02:00
|
|
|
|
alloc.x + 0.5, alloc.y + 0.5,
|
|
|
|
|
alloc.width - 1, alloc.height - 1);
|
|
|
|
|
cairo_fill (cr);
|
2017-10-28 09:30:42 -04:00
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-28 19:12:39 +01:00
|
|
|
|
/**
|
|
|
|
|
* gimp_highlight_widget:
|
|
|
|
|
* @widget:
|
|
|
|
|
* @highlight:
|
2021-02-09 11:50:23 +01:00
|
|
|
|
* @rect:
|
2009-11-28 19:12:39 +01:00
|
|
|
|
*
|
2017-10-28 09:30:42 -04:00
|
|
|
|
* Turns highlighting for @widget on or off according to
|
2017-10-29 09:48:00 -04:00
|
|
|
|
* @highlight, in a similar fashion to gtk_drag_highlight()
|
|
|
|
|
* and gtk_drag_unhighlight().
|
2021-02-09 11:50:23 +01:00
|
|
|
|
*
|
|
|
|
|
* If @rect is %NULL, highlight the full widget, otherwise highlight the
|
|
|
|
|
* specific rectangle in widget coordinates.
|
|
|
|
|
* When unhighlighting (i.e. @highlight is %FALSE), the value of @rect
|
|
|
|
|
* doesn't matter, as the previously used rectangle will be reused.
|
2009-11-28 19:12:39 +01:00
|
|
|
|
**/
|
|
|
|
|
void
|
2021-02-09 11:50:23 +01:00
|
|
|
|
gimp_highlight_widget (GtkWidget *widget,
|
|
|
|
|
gboolean highlight,
|
|
|
|
|
GdkRectangle *rect)
|
2009-11-28 19:12:39 +01:00
|
|
|
|
{
|
2021-02-09 11:50:23 +01:00
|
|
|
|
GdkRectangle *old_rect;
|
|
|
|
|
gboolean old_highlight;
|
2018-05-23 19:45:03 +02:00
|
|
|
|
|
2009-11-28 19:12:39 +01:00
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
2018-05-23 19:45:03 +02:00
|
|
|
|
highlight = highlight ? TRUE : FALSE;
|
|
|
|
|
|
|
|
|
|
old_highlight = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-highlight"));
|
2021-02-09 11:50:23 +01:00
|
|
|
|
old_rect = g_object_get_data (G_OBJECT (widget), "gimp-widget-highlight-rect");
|
|
|
|
|
|
2021-02-25 19:24:54 +01:00
|
|
|
|
if (highlight && old_highlight &&
|
|
|
|
|
rect && old_rect && ! gdk_rectangle_equal (rect, old_rect))
|
2021-02-09 11:50:23 +01:00
|
|
|
|
{
|
|
|
|
|
/* Highlight area changed. */
|
|
|
|
|
gimp_highlight_widget (widget, FALSE, NULL);
|
|
|
|
|
old_highlight = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-highlight"));
|
|
|
|
|
old_rect = g_object_get_data (G_OBJECT (widget), "gimp-widget-highlight-rect");
|
|
|
|
|
}
|
2018-05-23 19:45:03 +02:00
|
|
|
|
|
|
|
|
|
if (highlight != old_highlight)
|
2017-10-28 09:30:42 -04:00
|
|
|
|
{
|
2018-05-23 19:45:03 +02:00
|
|
|
|
if (highlight)
|
|
|
|
|
{
|
2021-02-09 11:50:23 +01:00
|
|
|
|
GdkRectangle *new_rect = NULL;
|
|
|
|
|
|
|
|
|
|
if (rect)
|
|
|
|
|
{
|
|
|
|
|
new_rect = g_new0 (GdkRectangle, 1);
|
|
|
|
|
*new_rect = *rect;
|
|
|
|
|
g_object_set_data_full (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-highlight-rect",
|
|
|
|
|
new_rect,
|
|
|
|
|
(GDestroyNotify) g_free);
|
|
|
|
|
}
|
2018-05-23 19:45:03 +02:00
|
|
|
|
g_signal_connect_after (widget, "draw",
|
|
|
|
|
G_CALLBACK (gimp_highlight_widget_draw),
|
2021-02-09 11:50:23 +01:00
|
|
|
|
new_rect);
|
2018-05-23 19:45:03 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-02-09 11:50:23 +01:00
|
|
|
|
if (old_rect)
|
|
|
|
|
{
|
|
|
|
|
g_signal_handlers_disconnect_by_func (widget,
|
|
|
|
|
gimp_highlight_widget_draw,
|
|
|
|
|
old_rect);
|
|
|
|
|
g_object_set_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-highlight-rect",
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 19:45:03 +02:00
|
|
|
|
g_signal_handlers_disconnect_by_func (widget,
|
|
|
|
|
gimp_highlight_widget_draw,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2017-10-28 09:30:42 -04:00
|
|
|
|
|
2018-05-23 19:45:03 +02:00
|
|
|
|
g_object_set_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-highlight",
|
|
|
|
|
GINT_TO_POINTER (highlight));
|
|
|
|
|
|
|
|
|
|
gtk_widget_queue_draw (widget);
|
|
|
|
|
}
|
2009-11-28 19:12:39 +01:00
|
|
|
|
}
|
2010-02-20 16:55:35 +01:00
|
|
|
|
|
2017-10-29 09:48:00 -04:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
2021-02-09 11:50:23 +01:00
|
|
|
|
gint timeout_id;
|
|
|
|
|
gint counter;
|
|
|
|
|
GdkRectangle *rect;
|
2017-10-29 09:48:00 -04:00
|
|
|
|
} WidgetBlink;
|
|
|
|
|
|
|
|
|
|
static WidgetBlink *
|
|
|
|
|
widget_blink_new (void)
|
|
|
|
|
{
|
|
|
|
|
WidgetBlink *blink;
|
|
|
|
|
|
|
|
|
|
blink = g_slice_new (WidgetBlink);
|
|
|
|
|
|
|
|
|
|
blink->timeout_id = 0;
|
|
|
|
|
blink->counter = 0;
|
2021-02-09 11:50:23 +01:00
|
|
|
|
blink->rect = NULL;
|
2017-10-29 09:48:00 -04:00
|
|
|
|
|
|
|
|
|
return blink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
widget_blink_free (WidgetBlink *blink)
|
|
|
|
|
{
|
|
|
|
|
if (blink->timeout_id)
|
|
|
|
|
{
|
|
|
|
|
g_source_remove (blink->timeout_id);
|
|
|
|
|
blink->timeout_id = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 11:50:23 +01:00
|
|
|
|
if (blink->rect)
|
|
|
|
|
g_slice_free (GdkRectangle, blink->rect);
|
|
|
|
|
|
2017-10-29 09:48:00 -04:00
|
|
|
|
g_slice_free (WidgetBlink, blink);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
static gboolean
|
|
|
|
|
gimp_widget_blink_start_timeout (GtkWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
WidgetBlink *blink;
|
|
|
|
|
|
|
|
|
|
blink = g_object_get_data (G_OBJECT (widget), "gimp-widget-blink");
|
|
|
|
|
if (blink)
|
|
|
|
|
{
|
|
|
|
|
blink->timeout_id = 0;
|
|
|
|
|
gimp_widget_blink (widget);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If the data is not here anymore, our blink has been canceled
|
|
|
|
|
* already. Also delete the script, if any.
|
|
|
|
|
*/
|
|
|
|
|
g_object_set_data (G_OBJECT (widget), "gimp-widget-blink-script", NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 11:47:21 +01:00
|
|
|
|
static gboolean
|
|
|
|
|
gimp_widget_blink_popover_remove (GtkWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
gtk_widget_destroy (widget);
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-29 09:48:00 -04:00
|
|
|
|
static gboolean
|
|
|
|
|
gimp_widget_blink_timeout (GtkWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
WidgetBlink *blink;
|
2022-03-05 17:29:44 +01:00
|
|
|
|
GList *script;
|
2017-10-29 09:48:00 -04:00
|
|
|
|
|
2022-03-05 17:29:44 +01:00
|
|
|
|
blink = g_object_get_data (G_OBJECT (widget), "gimp-widget-blink");
|
|
|
|
|
script = g_object_get_data (G_OBJECT (widget), "gimp-widget-blink-script");
|
2017-10-29 09:48:00 -04:00
|
|
|
|
|
2021-02-09 11:50:23 +01:00
|
|
|
|
gimp_highlight_widget (widget, blink->counter % 2 == 1, blink->rect);
|
2017-10-29 09:48:00 -04:00
|
|
|
|
blink->counter++;
|
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
if (blink->counter == 1)
|
|
|
|
|
{
|
|
|
|
|
if (script)
|
|
|
|
|
{
|
2022-03-07 11:47:21 +01:00
|
|
|
|
BlinkStep *step = script->data;
|
|
|
|
|
gchar *popover_text = NULL;
|
2022-03-06 14:31:27 +01:00
|
|
|
|
|
|
|
|
|
if (step->settings_value)
|
|
|
|
|
{
|
|
|
|
|
const gchar *prop_name;
|
|
|
|
|
GObject *config;
|
|
|
|
|
|
|
|
|
|
prop_name = g_object_get_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-property-name");
|
|
|
|
|
config = g_object_get_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-property-config");
|
|
|
|
|
|
|
|
|
|
if (config && G_IS_OBJECT (config) && prop_name)
|
|
|
|
|
{
|
2022-03-07 11:47:21 +01:00
|
|
|
|
GParamSpec *param_spec;
|
|
|
|
|
const gchar *nick;
|
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
|
|
|
|
|
prop_name);
|
|
|
|
|
if (! param_spec)
|
|
|
|
|
{
|
|
|
|
|
g_printerr ("%s: %s has no property named '%s'.\n",
|
|
|
|
|
G_STRFUNC,
|
|
|
|
|
g_type_name (G_TYPE_FROM_INSTANCE (config)),
|
|
|
|
|
prop_name);
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
if (! (param_spec->flags & G_PARAM_WRITABLE))
|
|
|
|
|
{
|
|
|
|
|
g_printerr ("%s: property '%s' of %s is not writable.\n",
|
|
|
|
|
G_STRFUNC,
|
|
|
|
|
param_spec->name,
|
|
|
|
|
g_type_name (param_spec->owner_type));
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
2022-03-07 11:47:21 +01:00
|
|
|
|
|
|
|
|
|
nick = g_param_spec_get_nick (param_spec);
|
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
if (g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_ENUM) ||
|
|
|
|
|
g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_INT) ||
|
|
|
|
|
g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_BOOLEAN))
|
|
|
|
|
{
|
|
|
|
|
gchar *endptr;
|
|
|
|
|
gint64 enum_value;
|
|
|
|
|
|
|
|
|
|
enum_value = g_ascii_strtoll (step->settings_value, &endptr, 10);
|
|
|
|
|
if (enum_value == 0 && endptr == step->settings_value)
|
|
|
|
|
{
|
|
|
|
|
g_printerr ("%s: settings value '%s' cannot properly be converted to int.\n",
|
|
|
|
|
G_STRFUNC, step->settings_value);
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_object_set (config,
|
|
|
|
|
prop_name, enum_value,
|
|
|
|
|
NULL);
|
2022-03-07 11:47:21 +01:00
|
|
|
|
|
|
|
|
|
if (nick)
|
|
|
|
|
{
|
|
|
|
|
if (g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_BOOLEAN))
|
|
|
|
|
{
|
|
|
|
|
if ((gboolean) enum_value)
|
|
|
|
|
/* TRANSLATORS: the %s will be replaced
|
|
|
|
|
* by the localized label of a boolean settings
|
|
|
|
|
* (e.g. a checkbox settings) displayed
|
|
|
|
|
* in some dockable GUI.
|
|
|
|
|
*/
|
|
|
|
|
popover_text = g_strdup_printf (_("Switch \"%s\" ON"), nick);
|
|
|
|
|
else
|
|
|
|
|
popover_text = g_strdup_printf (_("Switch \"%s\" OFF"), nick);
|
|
|
|
|
}
|
|
|
|
|
else if (g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_ENUM))
|
|
|
|
|
{
|
|
|
|
|
GParamSpecEnum *pspec_enum = (GParamSpecEnum *) param_spec;
|
|
|
|
|
const GEnumValue *genum_value;
|
|
|
|
|
|
|
|
|
|
genum_value = g_enum_get_value (pspec_enum->enum_class, enum_value);
|
|
|
|
|
if (genum_value)
|
|
|
|
|
{
|
|
|
|
|
const gchar *enum_desc;
|
|
|
|
|
|
|
|
|
|
enum_desc = gimp_enum_value_get_desc (pspec_enum->enum_class, genum_value);
|
|
|
|
|
if (enum_desc)
|
|
|
|
|
/* TRANSLATORS: the %s will be replaced
|
|
|
|
|
* by the localized label of a
|
|
|
|
|
* multi-choice settings displayed
|
|
|
|
|
* in some dockable GUI.
|
|
|
|
|
*/
|
|
|
|
|
popover_text = g_strdup_printf (_("Select \"%s\""), enum_desc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-06 14:31:27 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_printerr ("%s: currently unsupported type '%s' for property %s of %s.\n",
|
|
|
|
|
G_STRFUNC,
|
|
|
|
|
g_type_name (G_TYPE_FROM_INSTANCE (param_spec)),
|
|
|
|
|
param_spec->name,
|
|
|
|
|
g_type_name (param_spec->owner_type));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-07 11:47:21 +01:00
|
|
|
|
else if (GIMP_IS_TOOL_BUTTON (widget))
|
|
|
|
|
{
|
|
|
|
|
GimpToolInfo *info;
|
|
|
|
|
|
|
|
|
|
info = gimp_tool_button_get_tool_info (GIMP_TOOL_BUTTON (widget));
|
|
|
|
|
/* TRANSLATORS: %s will be a tool name, so we'll get a
|
|
|
|
|
* final string looking like 'Activate the "Bucket fill" tool'.
|
|
|
|
|
*/
|
|
|
|
|
popover_text = g_strdup_printf (_("Activate the \"%s\" tool"),
|
|
|
|
|
info->label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (popover_text != NULL)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *popover = gtk_popover_new (widget);
|
|
|
|
|
GtkWidget *label = gtk_label_new (popover_text);
|
|
|
|
|
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (popover), label);
|
|
|
|
|
gtk_widget_show (label);
|
|
|
|
|
gtk_widget_show (popover);
|
|
|
|
|
|
|
|
|
|
g_timeout_add (1200,
|
|
|
|
|
(GSourceFunc) gimp_widget_blink_popover_remove,
|
|
|
|
|
popover);
|
|
|
|
|
g_free (popover_text);
|
|
|
|
|
}
|
2022-03-06 14:31:27 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (blink->counter == 3)
|
2017-10-29 09:48:00 -04:00
|
|
|
|
{
|
|
|
|
|
blink->timeout_id = 0;
|
|
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (widget), "gimp-widget-blink", NULL);
|
|
|
|
|
|
2022-03-05 17:29:44 +01:00
|
|
|
|
if (script)
|
|
|
|
|
{
|
|
|
|
|
if (script->next)
|
2022-03-06 14:31:27 +01:00
|
|
|
|
{
|
|
|
|
|
BlinkStep *next_step = script->next->data;
|
|
|
|
|
GtkWidget *next_widget = next_step->widget;
|
2022-03-05 17:29:44 +01:00
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
g_object_set_data_full (G_OBJECT (next_widget), "gimp-widget-blink-script",
|
|
|
|
|
script->next,
|
|
|
|
|
(GDestroyNotify) gimp_blink_free_script);
|
|
|
|
|
script->next->prev = NULL;
|
|
|
|
|
script->next = NULL;
|
|
|
|
|
|
2022-03-07 11:47:21 +01:00
|
|
|
|
gimp_widget_blink_after (next_widget, 800);
|
2022-03-06 14:31:27 +01:00
|
|
|
|
}
|
2022-03-05 17:29:44 +01:00
|
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (widget), "gimp-widget-blink-script", NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-29 09:48:00 -04:00
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_widget_blink (GtkWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
WidgetBlink *blink;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
2018-05-23 19:45:03 +02:00
|
|
|
|
gimp_widget_blink_cancel (widget);
|
|
|
|
|
|
2017-10-29 09:48:00 -04:00
|
|
|
|
blink = widget_blink_new ();
|
|
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (widget), "gimp-widget-blink", blink,
|
|
|
|
|
(GDestroyNotify) widget_blink_free);
|
|
|
|
|
|
|
|
|
|
blink->timeout_id = g_timeout_add (150,
|
|
|
|
|
(GSourceFunc) gimp_widget_blink_timeout,
|
|
|
|
|
widget);
|
|
|
|
|
|
2021-02-09 11:50:23 +01:00
|
|
|
|
gimp_highlight_widget (widget, TRUE, NULL);
|
|
|
|
|
|
|
|
|
|
while ((widget = gtk_widget_get_parent (widget)))
|
|
|
|
|
gimp_widget_blink_cancel (widget);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-05 17:29:44 +01:00
|
|
|
|
void
|
2022-03-06 14:31:27 +01:00
|
|
|
|
gimp_widget_script_blink (GtkWidget *widget,
|
|
|
|
|
const gchar *settings_value,
|
|
|
|
|
GList **blink_scenario)
|
2022-03-05 17:29:44 +01:00
|
|
|
|
{
|
2022-03-06 14:31:27 +01:00
|
|
|
|
BlinkStep *step;
|
|
|
|
|
|
|
|
|
|
step = g_slice_new (BlinkStep);
|
|
|
|
|
step->widget = widget;
|
|
|
|
|
step->settings_value = g_strdup (settings_value);
|
|
|
|
|
|
|
|
|
|
*blink_scenario = g_list_append (*blink_scenario, step);
|
2022-03-05 17:29:44 +01:00
|
|
|
|
|
|
|
|
|
while ((widget = gtk_widget_get_parent (widget)))
|
|
|
|
|
gimp_widget_blink_cancel (widget);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
/* gimp_blink_play_script:
|
|
|
|
|
* @blink_scenario:
|
|
|
|
|
*
|
|
|
|
|
* This function will play the @blink_scenario and free the associated
|
|
|
|
|
* data once done.
|
|
|
|
|
*/
|
2022-03-05 17:29:44 +01:00
|
|
|
|
void
|
|
|
|
|
gimp_blink_play_script (GList *blink_scenario)
|
|
|
|
|
{
|
2022-03-06 14:31:27 +01:00
|
|
|
|
BlinkStep *step;
|
|
|
|
|
|
2022-03-05 17:29:44 +01:00
|
|
|
|
g_return_if_fail (g_list_length (blink_scenario) > 0);
|
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
step = blink_scenario->data;
|
|
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (step->widget),
|
2022-03-05 17:29:44 +01:00
|
|
|
|
"gimp-widget-blink-script",
|
2022-03-06 14:31:27 +01:00
|
|
|
|
blink_scenario,
|
|
|
|
|
(GDestroyNotify) gimp_blink_free_script);
|
|
|
|
|
gimp_widget_blink (step->widget);
|
2022-03-05 17:29:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 11:50:23 +01:00
|
|
|
|
void
|
|
|
|
|
gimp_widget_blink_rect (GtkWidget *widget,
|
|
|
|
|
GdkRectangle *rect)
|
|
|
|
|
{
|
|
|
|
|
WidgetBlink *blink;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
|
|
|
|
gimp_widget_blink_cancel (widget);
|
|
|
|
|
|
|
|
|
|
blink = widget_blink_new ();
|
|
|
|
|
blink->rect = g_slice_new (GdkRectangle);
|
|
|
|
|
*(blink->rect) = *rect;
|
|
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (widget), "gimp-widget-blink", blink,
|
|
|
|
|
(GDestroyNotify) widget_blink_free);
|
|
|
|
|
|
|
|
|
|
blink->timeout_id = g_timeout_add (150,
|
|
|
|
|
(GSourceFunc) gimp_widget_blink_timeout,
|
|
|
|
|
widget);
|
|
|
|
|
|
|
|
|
|
gimp_highlight_widget (widget, TRUE, blink->rect);
|
2018-12-10 07:57:02 -05:00
|
|
|
|
|
|
|
|
|
while ((widget = gtk_widget_get_parent (widget)))
|
|
|
|
|
gimp_widget_blink_cancel (widget);
|
2017-10-29 09:48:00 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 19:45:03 +02:00
|
|
|
|
void
|
|
|
|
|
gimp_widget_blink_cancel (GtkWidget *widget)
|
2017-10-29 09:48:00 -04:00
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
2018-12-10 14:14:34 -05:00
|
|
|
|
if (g_object_get_data (G_OBJECT (widget), "gimp-widget-blink"))
|
|
|
|
|
{
|
2021-02-09 11:50:23 +01:00
|
|
|
|
gimp_highlight_widget (widget, FALSE, NULL);
|
2017-10-29 09:48:00 -04:00
|
|
|
|
|
2018-12-10 14:14:34 -05:00
|
|
|
|
g_object_set_data (G_OBJECT (widget), "gimp-widget-blink", NULL);
|
|
|
|
|
}
|
2017-10-29 09:48:00 -04:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:34:12 +01:00
|
|
|
|
/**
|
|
|
|
|
* gimp_blink_toolbox:
|
|
|
|
|
* @gimp:
|
|
|
|
|
* @action_name:
|
|
|
|
|
* @blink_scenario:
|
|
|
|
|
*
|
|
|
|
|
* This is similar to gimp_blink_dockable() for the toolbox
|
|
|
|
|
* specifically. What it will do, additionally to blink the tool button,
|
|
|
|
|
* is first to activate it.
|
|
|
|
|
*
|
|
|
|
|
* Also the identifier is easy as it is simply the action name.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
gimp_blink_toolbox (Gimp *gimp,
|
|
|
|
|
const gchar *action_name,
|
|
|
|
|
GList **blink_scenario)
|
|
|
|
|
{
|
|
|
|
|
GimpUIManager *ui_manager;
|
|
|
|
|
GtkWidget *toolbox;
|
|
|
|
|
|
|
|
|
|
/* As a special case, for the toolbox, we don't just raise it,
|
|
|
|
|
* we also select the tool if one was requested.
|
|
|
|
|
*/
|
|
|
|
|
toolbox = gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)),
|
|
|
|
|
gimp,
|
|
|
|
|
gimp_dialog_factory_get_singleton (),
|
|
|
|
|
gimp_get_monitor_at_pointer (),
|
|
|
|
|
"gimp-toolbox");
|
|
|
|
|
/* Find and activate the tool. */
|
|
|
|
|
if (toolbox && action_name != NULL &&
|
|
|
|
|
(ui_manager = gimp_dock_get_ui_manager (GIMP_DOCK (toolbox))))
|
|
|
|
|
{
|
|
|
|
|
GimpAction *action;
|
|
|
|
|
|
|
|
|
|
action = gimp_ui_manager_find_action (ui_manager, "tools", action_name);
|
|
|
|
|
gimp_action_activate (GIMP_ACTION (action));
|
|
|
|
|
}
|
2022-03-06 14:31:27 +01:00
|
|
|
|
gimp_blink_dockable (gimp, "gimp-toolbox", action_name, NULL, blink_scenario);
|
2022-03-06 13:34:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-05 14:32:34 +01:00
|
|
|
|
/**
|
|
|
|
|
* gimp_blink_dockable:
|
|
|
|
|
* @gimp:
|
|
|
|
|
* @dockable_identifier:
|
|
|
|
|
* @widget_identifier:
|
2022-03-06 14:31:27 +01:00
|
|
|
|
* @settings_value:
|
2022-03-05 17:29:44 +01:00
|
|
|
|
* @blink_scenario:
|
2022-03-05 14:32:34 +01:00
|
|
|
|
*
|
|
|
|
|
* This function will raise the dockable identified by
|
|
|
|
|
* @dockable_identifier. The list of dockable identifiers can be found
|
|
|
|
|
* in `app/dialogs/dialogs.c`.
|
|
|
|
|
*
|
|
|
|
|
* Then it will find the widget identified by @widget_identifier. Note
|
|
|
|
|
* that for propwidgets, this is usually the associated property name.
|
2022-03-05 17:29:44 +01:00
|
|
|
|
*
|
2022-03-06 14:31:27 +01:00
|
|
|
|
* If @settings_value is set, then it will try to change the widget
|
|
|
|
|
* value, depending the type of widgets. Note that for now, only
|
|
|
|
|
* property widgets of enum, int or boolean types can be set (so the
|
|
|
|
|
* @settings_value string must represent an int value).
|
|
|
|
|
*
|
2022-03-05 17:29:44 +01:00
|
|
|
|
* Finally it will either blink this widget immediately to raise
|
|
|
|
|
* attention, or add it to the @blink_scenario if not %NULL. The blink
|
|
|
|
|
* scenario must be explicitly started with gimp_blink_play_script()
|
2022-03-06 14:31:27 +01:00
|
|
|
|
* when ready. @blink_scenario should be considered as opaque data, so
|
|
|
|
|
* you should not free it directly and let gimp_blink_play_script() do
|
|
|
|
|
* so for you.
|
2022-03-05 14:32:34 +01:00
|
|
|
|
*/
|
|
|
|
|
void
|
2022-03-05 17:29:44 +01:00
|
|
|
|
gimp_blink_dockable (Gimp *gimp,
|
|
|
|
|
const gchar *dockable_identifier,
|
|
|
|
|
const gchar *widget_identifier,
|
2022-03-06 14:31:27 +01:00
|
|
|
|
const gchar *settings_value,
|
2022-03-05 17:29:44 +01:00
|
|
|
|
GList **blink_scenario)
|
2022-03-05 14:32:34 +01:00
|
|
|
|
{
|
2022-03-06 14:31:27 +01:00
|
|
|
|
GtkWidget *dockable;
|
|
|
|
|
GdkMonitor *monitor;
|
|
|
|
|
BlinkSearch *data;
|
2022-03-05 14:32:34 +01:00
|
|
|
|
|
|
|
|
|
g_return_if_fail (GIMP_IS_GIMP (gimp));
|
|
|
|
|
|
|
|
|
|
monitor = gimp_get_monitor_at_pointer ();
|
|
|
|
|
|
|
|
|
|
dockable = gimp_window_strategy_show_dockable_dialog (
|
|
|
|
|
GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)),
|
|
|
|
|
gimp,
|
|
|
|
|
gimp_dialog_factory_get_singleton (),
|
|
|
|
|
monitor,
|
|
|
|
|
dockable_identifier);
|
|
|
|
|
|
|
|
|
|
if (! dockable)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-03-05 22:59:55 +01:00
|
|
|
|
if (widget_identifier)
|
|
|
|
|
{
|
2022-03-06 14:31:27 +01:00
|
|
|
|
data = g_slice_new (BlinkSearch);
|
2022-03-05 22:59:55 +01:00
|
|
|
|
data->blink_script = blink_scenario;
|
|
|
|
|
data->widget_identifier = widget_identifier;
|
2022-03-06 14:31:27 +01:00
|
|
|
|
data->settings_value = settings_value;
|
2022-03-05 22:59:55 +01:00
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (dockable),
|
|
|
|
|
(GtkCallback) gimp_search_widget_rec,
|
|
|
|
|
(gpointer) data);
|
2022-03-06 14:31:27 +01:00
|
|
|
|
g_slice_free (BlinkSearch, data);
|
2022-03-05 22:59:55 +01:00
|
|
|
|
}
|
2022-03-05 14:32:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-02-20 16:55:35 +01:00
|
|
|
|
/**
|
|
|
|
|
* gimp_dock_with_window_new:
|
|
|
|
|
* @factory: a #GimpDialogFacotry
|
2018-04-29 17:27:47 +02:00
|
|
|
|
* @monitor: the #GdkMonitor the dock window should appear on
|
2010-02-20 16:55:35 +01:00
|
|
|
|
* @toolbox: if %TRUE; gives a "gimp-toolbox-window" with a
|
|
|
|
|
* "gimp-toolbox", "gimp-dock-window"+"gimp-dock"
|
|
|
|
|
* otherwise
|
|
|
|
|
*
|
|
|
|
|
* Returns: the newly created #GimpDock with the #GimpDockWindow
|
|
|
|
|
**/
|
|
|
|
|
GtkWidget *
|
|
|
|
|
gimp_dock_with_window_new (GimpDialogFactory *factory,
|
2018-04-29 17:27:47 +02:00
|
|
|
|
GdkMonitor *monitor,
|
2010-02-20 16:55:35 +01:00
|
|
|
|
gboolean toolbox)
|
|
|
|
|
{
|
2011-05-13 18:56:03 +02:00
|
|
|
|
GtkWidget *dock_window;
|
|
|
|
|
GimpDockContainer *dock_container;
|
|
|
|
|
GtkWidget *dock;
|
|
|
|
|
GimpUIManager *ui_manager;
|
2010-02-20 16:55:35 +01:00
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
|
2018-04-29 17:27:47 +02:00
|
|
|
|
g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
|
2010-02-20 16:55:35 +01:00
|
|
|
|
|
|
|
|
|
/* Create a dock window to put the dock in. We need to create the
|
|
|
|
|
* dock window before the dock because the dock has a dependency to
|
|
|
|
|
* the ui manager in the dock window
|
|
|
|
|
*/
|
2018-04-29 17:27:47 +02:00
|
|
|
|
dock_window = gimp_dialog_factory_dialog_new (factory, monitor,
|
2011-02-22 16:27:18 +01:00
|
|
|
|
NULL /*ui_manager*/,
|
2018-05-03 00:55:44 +02:00
|
|
|
|
NULL,
|
2011-02-22 16:27:18 +01:00
|
|
|
|
(toolbox ?
|
|
|
|
|
"gimp-toolbox-window" :
|
|
|
|
|
"gimp-dock-window"),
|
|
|
|
|
-1 /*view_size*/,
|
|
|
|
|
FALSE /*present*/);
|
|
|
|
|
|
2011-05-13 18:56:03 +02:00
|
|
|
|
dock_container = GIMP_DOCK_CONTAINER (dock_window);
|
|
|
|
|
ui_manager = gimp_dock_container_get_ui_manager (dock_container);
|
2018-04-29 17:27:47 +02:00
|
|
|
|
dock = gimp_dialog_factory_dialog_new (factory, monitor,
|
2011-05-13 18:56:03 +02:00
|
|
|
|
ui_manager,
|
2018-05-03 00:55:44 +02:00
|
|
|
|
dock_window,
|
2011-05-13 18:56:03 +02:00
|
|
|
|
(toolbox ?
|
|
|
|
|
"gimp-toolbox" :
|
|
|
|
|
"gimp-dock"),
|
|
|
|
|
-1 /*view_size*/,
|
|
|
|
|
FALSE /*present*/);
|
2011-02-22 16:27:18 +01:00
|
|
|
|
|
2010-02-20 16:55:35 +01:00
|
|
|
|
if (dock)
|
2011-02-22 16:27:18 +01:00
|
|
|
|
gimp_dock_window_add_dock (GIMP_DOCK_WINDOW (dock_window),
|
|
|
|
|
GIMP_DOCK (dock),
|
|
|
|
|
-1);
|
2010-02-20 16:55:35 +01:00
|
|
|
|
|
|
|
|
|
return dock;
|
|
|
|
|
}
|
2010-10-31 13:05:15 +01:00
|
|
|
|
|
|
|
|
|
GtkWidget *
|
|
|
|
|
gimp_tools_get_tool_options_gui (GimpToolOptions *tool_options)
|
|
|
|
|
{
|
2018-09-27 02:31:39 -04:00
|
|
|
|
GtkWidget *widget;
|
|
|
|
|
|
|
|
|
|
widget = g_object_get_data (G_OBJECT (tool_options),
|
|
|
|
|
GIMP_TOOL_OPTIONS_GUI_KEY);
|
|
|
|
|
|
|
|
|
|
if (! widget)
|
|
|
|
|
{
|
|
|
|
|
GimpToolOptionsGUIFunc func;
|
|
|
|
|
|
|
|
|
|
func = g_object_get_data (G_OBJECT (tool_options),
|
|
|
|
|
GIMP_TOOL_OPTIONS_GUI_FUNC_KEY);
|
|
|
|
|
|
|
|
|
|
if (func)
|
|
|
|
|
{
|
|
|
|
|
widget = func (tool_options);
|
|
|
|
|
|
|
|
|
|
gimp_tools_set_tool_options_gui (tool_options, widget);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return widget;
|
2010-10-31 13:05:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2018-09-27 02:31:39 -04:00
|
|
|
|
gimp_tools_set_tool_options_gui (GimpToolOptions *tool_options,
|
|
|
|
|
GtkWidget *widget)
|
2010-10-31 13:05:15 +01:00
|
|
|
|
{
|
2018-09-27 02:31:39 -04:00
|
|
|
|
GtkWidget *prev_widget;
|
|
|
|
|
|
|
|
|
|
prev_widget = g_object_get_data (G_OBJECT (tool_options),
|
|
|
|
|
GIMP_TOOL_OPTIONS_GUI_KEY);
|
|
|
|
|
|
|
|
|
|
if (widget == prev_widget)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (prev_widget)
|
|
|
|
|
gtk_widget_destroy (prev_widget);
|
|
|
|
|
|
2010-11-10 00:13:07 +01:00
|
|
|
|
g_object_set_data_full (G_OBJECT (tool_options),
|
|
|
|
|
GIMP_TOOL_OPTIONS_GUI_KEY,
|
2018-09-27 02:31:39 -04:00
|
|
|
|
widget ? g_object_ref_sink (widget) : NULL,
|
2010-11-10 00:13:07 +01:00
|
|
|
|
widget ? (GDestroyNotify) g_object_unref : NULL);
|
2010-10-31 13:05:15 +01:00
|
|
|
|
}
|
2010-11-09 19:33:15 +01:00
|
|
|
|
|
2018-09-27 02:31:39 -04:00
|
|
|
|
void
|
|
|
|
|
gimp_tools_set_tool_options_gui_func (GimpToolOptions *tool_options,
|
|
|
|
|
GimpToolOptionsGUIFunc func)
|
|
|
|
|
{
|
|
|
|
|
g_object_set_data (G_OBJECT (tool_options),
|
|
|
|
|
GIMP_TOOL_OPTIONS_GUI_FUNC_KEY,
|
|
|
|
|
func);
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-27 21:41:32 +01:00
|
|
|
|
gboolean
|
|
|
|
|
gimp_widget_get_fully_opaque (GtkWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
|
|
|
|
|
|
|
|
|
|
return g_object_get_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-fully-opaque") != NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_widget_set_fully_opaque (GtkWidget *widget,
|
|
|
|
|
gboolean fully_opaque)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
|
|
|
|
return g_object_set_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-fully-opaque",
|
|
|
|
|
GINT_TO_POINTER (fully_opaque));
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 14:14:26 -04:00
|
|
|
|
static void
|
|
|
|
|
gimp_gtk_container_clear_callback (GtkWidget *widget,
|
|
|
|
|
GtkContainer *container)
|
|
|
|
|
{
|
|
|
|
|
gtk_container_remove (container, widget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_gtk_container_clear (GtkContainer *container)
|
|
|
|
|
{
|
|
|
|
|
gtk_container_foreach (container,
|
|
|
|
|
(GtkCallback) gimp_gtk_container_clear_callback,
|
|
|
|
|
container);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 12:52:13 +02:00
|
|
|
|
void
|
|
|
|
|
gimp_button_set_suggested (GtkWidget *button,
|
|
|
|
|
gboolean suggested,
|
|
|
|
|
GtkReliefStyle default_relief)
|
|
|
|
|
{
|
|
|
|
|
GtkStyleContext *style;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_BUTTON (button));
|
|
|
|
|
|
|
|
|
|
style = gtk_widget_get_style_context (button);
|
|
|
|
|
|
|
|
|
|
if (suggested)
|
|
|
|
|
{
|
|
|
|
|
gtk_style_context_add_class (style, "suggested-action");
|
|
|
|
|
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gtk_style_context_remove_class (style, "suggested-action");
|
|
|
|
|
gtk_button_set_relief (GTK_BUTTON (button), default_relief);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_button_set_destructive (GtkWidget *button,
|
|
|
|
|
gboolean destructive,
|
|
|
|
|
GtkReliefStyle default_relief)
|
|
|
|
|
{
|
|
|
|
|
GtkStyleContext *style;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_BUTTON (button));
|
|
|
|
|
|
|
|
|
|
style = gtk_widget_get_style_context (button);
|
|
|
|
|
|
|
|
|
|
if (destructive)
|
|
|
|
|
{
|
|
|
|
|
gtk_style_context_add_class (style, "destructive-action");
|
|
|
|
|
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gtk_style_context_remove_class (style, "destructive-action");
|
|
|
|
|
gtk_button_set_relief (GTK_BUTTON (button), default_relief);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-01 23:21:21 +03:00
|
|
|
|
void
|
|
|
|
|
gimp_gtk_adjustment_chain (GtkAdjustment *adjustment1,
|
|
|
|
|
GtkAdjustment *adjustment2)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment1));
|
|
|
|
|
g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment2));
|
|
|
|
|
|
|
|
|
|
g_object_bind_property (adjustment1, "value",
|
|
|
|
|
adjustment2, "lower",
|
|
|
|
|
G_BINDING_SYNC_CREATE);
|
|
|
|
|
g_object_bind_property (adjustment2, "value",
|
|
|
|
|
adjustment1, "upper",
|
|
|
|
|
G_BINDING_SYNC_CREATE);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-24 12:34:57 +01:00
|
|
|
|
static gboolean
|
|
|
|
|
gimp_print_event_free (gpointer data)
|
|
|
|
|
{
|
|
|
|
|
g_free (data);
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const gchar *
|
|
|
|
|
gimp_print_event (const GdkEvent *event)
|
|
|
|
|
{
|
|
|
|
|
gchar *str;
|
2011-02-27 15:59:07 +01:00
|
|
|
|
gchar *tmp;
|
2011-02-24 12:34:57 +01:00
|
|
|
|
|
|
|
|
|
switch (event->type)
|
|
|
|
|
{
|
|
|
|
|
case GDK_ENTER_NOTIFY:
|
2011-02-27 15:59:07 +01:00
|
|
|
|
str = g_strdup_printf ("ENTER_NOTIFY (mode %d)",
|
|
|
|
|
event->crossing.mode);
|
2011-02-24 12:34:57 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_LEAVE_NOTIFY:
|
2011-02-27 15:59:07 +01:00
|
|
|
|
str = g_strdup_printf ("LEAVE_NOTIFY (mode %d)",
|
|
|
|
|
event->crossing.mode);
|
2011-02-24 12:34:57 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_PROXIMITY_IN:
|
|
|
|
|
str = g_strdup ("PROXIMITY_IN");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_PROXIMITY_OUT:
|
|
|
|
|
str = g_strdup ("PROXIMITY_OUT");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_FOCUS_CHANGE:
|
|
|
|
|
if (event->focus_change.in)
|
|
|
|
|
str = g_strdup ("FOCUS_IN");
|
|
|
|
|
else
|
|
|
|
|
str = g_strdup ("FOCUS_OUT");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_BUTTON_PRESS:
|
|
|
|
|
str = g_strdup_printf ("BUTTON_PRESS (%d @ %0.0f:%0.0f)",
|
|
|
|
|
event->button.button,
|
|
|
|
|
event->button.x,
|
|
|
|
|
event->button.y);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_2BUTTON_PRESS:
|
|
|
|
|
str = g_strdup_printf ("2BUTTON_PRESS (%d @ %0.0f:%0.0f)",
|
|
|
|
|
event->button.button,
|
|
|
|
|
event->button.x,
|
|
|
|
|
event->button.y);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_3BUTTON_PRESS:
|
|
|
|
|
str = g_strdup_printf ("3BUTTON_PRESS (%d @ %0.0f:%0.0f)",
|
|
|
|
|
event->button.button,
|
|
|
|
|
event->button.x,
|
|
|
|
|
event->button.y);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_BUTTON_RELEASE:
|
|
|
|
|
str = g_strdup_printf ("BUTTON_RELEASE (%d @ %0.0f:%0.0f)",
|
|
|
|
|
event->button.button,
|
|
|
|
|
event->button.x,
|
|
|
|
|
event->button.y);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_SCROLL:
|
|
|
|
|
str = g_strdup_printf ("SCROLL (%d)",
|
|
|
|
|
event->scroll.direction);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_MOTION_NOTIFY:
|
|
|
|
|
str = g_strdup_printf ("MOTION_NOTIFY (%0.0f:%0.0f %d)",
|
|
|
|
|
event->motion.x,
|
|
|
|
|
event->motion.y,
|
|
|
|
|
event->motion.time);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_KEY_PRESS:
|
|
|
|
|
str = g_strdup_printf ("KEY_PRESS (%d, %s)",
|
|
|
|
|
event->key.keyval,
|
|
|
|
|
gdk_keyval_name (event->key.keyval) ?
|
|
|
|
|
gdk_keyval_name (event->key.keyval) : "<none>");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GDK_KEY_RELEASE:
|
|
|
|
|
str = g_strdup_printf ("KEY_RELEASE (%d, %s)",
|
|
|
|
|
event->key.keyval,
|
|
|
|
|
gdk_keyval_name (event->key.keyval) ?
|
|
|
|
|
gdk_keyval_name (event->key.keyval) : "<none>");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
str = g_strdup_printf ("UNHANDLED (type %d)",
|
|
|
|
|
event->type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-27 15:59:07 +01:00
|
|
|
|
tmp = g_strdup_printf ("%s (device '%s', source device '%s')",
|
|
|
|
|
str,
|
|
|
|
|
gdk_device_get_name (gdk_event_get_device (event)),
|
|
|
|
|
gdk_device_get_name (gdk_event_get_source_device (event)));
|
|
|
|
|
g_free (str);
|
|
|
|
|
str = tmp;
|
|
|
|
|
|
2011-02-24 12:34:57 +01:00
|
|
|
|
g_idle_add (gimp_print_event_free, str);
|
|
|
|
|
|
|
|
|
|
return str;
|
|
|
|
|
}
|
2016-05-22 01:19:18 +02:00
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
gimp_color_profile_store_add_defaults (GimpColorProfileStore *store,
|
|
|
|
|
GimpColorConfig *config,
|
|
|
|
|
GimpImageBaseType base_type,
|
|
|
|
|
GimpPrecision precision,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GimpColorProfile *profile;
|
|
|
|
|
gchar *label;
|
2016-05-22 13:24:20 +02:00
|
|
|
|
GError *my_error = NULL;
|
2016-05-22 01:19:18 +02:00
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store), FALSE);
|
|
|
|
|
g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
|
|
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
|
|
Initial space invasion commit in GIMP
All babl formats now have a space equivalent to a color profile,
determining the format's primaries and TRCs. This commit makes GIMP
aware of this.
libgimp:
- enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA
as deprecated aliases, add PERCEPTUAL values so we now have LINEAR,
NON_LINEAR and PERCPTUAL for each encoding, matching the babl
encoding variants RGB, R'G'B' and R~G~B~.
- gimp_color_transform_can_gegl_copy() now returns TRUE if both
profiles can return a babl space, increasing the amount of fast babl
color conversions significantly.
- TODO: no solution yet for getting libgimp drawable proxy buffers in
the right format with space.
plug-ins:
- follow the GimpPrecision change.
- TODO: everything else unchanged and partly broken or sub-optimal,
like setting a new image's color profile too late.
app:
- add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as
replacement for all "linear" booleans.
- change gimp-babl functions to take babl spaces and GimpTRCType
parameters and support all sorts of new perceptual ~ formats.
- a lot of places changed in the early days of goat invasion didn't
take advantage of gimp-babl utility functions and constructed
formats manually. They all needed revisiting and many now use much
simpler code calling gimp-babl API.
- change gimp_babl_format_get_color_profile() to really extract a
newly allocated color profile from the format, and add
gimp_babl_get_builtin_color_profile() which does the same as
gimp_babl_format_get_color_profile() did before. Visited all callers
to decide whether they are looking for the format's actual profile,
or for one of the builtin profiles, simplifying code that only needs
builtin profiles.
- drawables have a new get_space_api(), get_linear() is now get_trc().
- images now have a "layer space" and an API to get it,
gimp_image_get_layer_format() returns formats in that space.
- an image's layer space is created from the image's color profile,
change gimpimage-color-profile to deal with that correctly
- change many babl_format() calls to babl_format_with_space() and take
the space from passed formats or drawables
- add function gimp_layer_fix_format_space() which replaces the
layer's buffer with one that has the image's layer format, but
doesn't change pixel values
- use gimp_layer_fix_format_space() to make sure layers loaded from
XCF and created by plug-ins have the right space when added to the
image, because it's impossible to always assign the right space upon
layer creation
- "assign color profile" and "discard color profile" now require use
of gimp_layer_fix_format_space() too because the profile is now
embedded in all formats via the space. Add
gimp_image_assign_color_profile() which does all that and call it
instead of a simple gimp_image_set_color_profile(), also from the
PDB set-color-profile functions, which are essentially "assign" and
"discard" calls.
- generally, make sure a new image's color profile is set before
adding layers to it, gimp_image_set_color_profile() is more than
before considered know-what-you-are-doing API.
- take special precaution in all places that call
gimp_drawable_convert_type(), we now must pass a new_profile from
all callers that convert layers within the same image (such as
image_convert_type, image_convert_precision), because the layer's
new space can't be determined from the image's layer format during
the call.
- change all "linear" properties to "trc", in all config objects like
for levels and curves, in the histogram, in the widgets. This results
in some GUI that now has three choices instead of two.
TODO: we might want to reduce that back to two later.
- keep "linear" boolean properties around as compat if needed for file
pasring, but always convert the parsed parsed boolean to
GimpTRCType.
- TODO: the image's "enable color management" switch is currently
broken, will fix that in another commit.
2018-07-21 14:23:01 +02:00
|
|
|
|
profile = gimp_babl_get_builtin_color_profile (base_type,
|
|
|
|
|
gimp_babl_trc (precision));
|
2016-05-22 01:19:18 +02:00
|
|
|
|
|
|
|
|
|
if (base_type == GIMP_GRAY)
|
|
|
|
|
{
|
|
|
|
|
label = g_strdup_printf (_("Built-in grayscale (%s)"),
|
|
|
|
|
gimp_color_profile_get_label (profile));
|
|
|
|
|
|
2016-05-22 13:24:20 +02:00
|
|
|
|
profile = gimp_color_config_get_gray_color_profile (config, &my_error);
|
2016-05-22 01:19:18 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
label = g_strdup_printf (_("Built-in RGB (%s)"),
|
|
|
|
|
gimp_color_profile_get_label (profile));
|
|
|
|
|
|
2016-05-22 13:24:20 +02:00
|
|
|
|
profile = gimp_color_config_get_rgb_color_profile (config, &my_error);
|
2016-05-22 01:19:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gimp_color_profile_store_add_file (store, NULL, label);
|
|
|
|
|
g_free (label);
|
|
|
|
|
|
|
|
|
|
if (profile)
|
|
|
|
|
{
|
2018-05-05 20:07:27 +02:00
|
|
|
|
gchar *path;
|
2016-05-22 01:19:18 +02:00
|
|
|
|
GFile *file;
|
|
|
|
|
|
|
|
|
|
if (base_type == GIMP_GRAY)
|
|
|
|
|
{
|
2018-05-05 20:07:27 +02:00
|
|
|
|
g_object_get (config, "gray-profile", &path, NULL);
|
|
|
|
|
file = gimp_file_new_for_config_path (path, NULL);
|
|
|
|
|
g_free (path);
|
2016-05-22 01:19:18 +02:00
|
|
|
|
|
|
|
|
|
label = g_strdup_printf (_("Preferred grayscale (%s)"),
|
|
|
|
|
gimp_color_profile_get_label (profile));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-05-05 20:07:27 +02:00
|
|
|
|
g_object_get (config, "rgb-profile", &path, NULL);
|
|
|
|
|
file = gimp_file_new_for_config_path (path, NULL);
|
|
|
|
|
g_free (path);
|
2016-05-22 01:19:18 +02:00
|
|
|
|
|
|
|
|
|
label = g_strdup_printf (_("Preferred RGB (%s)"),
|
|
|
|
|
gimp_color_profile_get_label (profile));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_object_unref (profile);
|
|
|
|
|
|
|
|
|
|
gimp_color_profile_store_add_file (store, file, label);
|
|
|
|
|
|
|
|
|
|
g_object_unref (file);
|
|
|
|
|
g_free (label);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2016-05-22 13:24:20 +02:00
|
|
|
|
else if (my_error)
|
|
|
|
|
{
|
|
|
|
|
g_propagate_error (error, my_error);
|
2016-05-22 01:19:18 +02:00
|
|
|
|
|
2016-05-22 13:24:20 +02:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
2016-05-22 01:19:18 +02:00
|
|
|
|
}
|
2018-01-02 23:09:00 +01:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
connect_path_show (GimpColorProfileChooserDialog *dialog)
|
|
|
|
|
{
|
|
|
|
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
|
|
|
|
|
GFile *file = gtk_file_chooser_get_file (chooser);
|
|
|
|
|
|
|
|
|
|
if (file)
|
|
|
|
|
{
|
|
|
|
|
/* if something is already selected in this dialog,
|
|
|
|
|
* leave it alone
|
|
|
|
|
*/
|
|
|
|
|
g_object_unref (file);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GObject *config;
|
|
|
|
|
const gchar *property;
|
|
|
|
|
gchar *path = NULL;
|
|
|
|
|
|
|
|
|
|
config = g_object_get_data (G_OBJECT (dialog), "profile-path-config");
|
|
|
|
|
property = g_object_get_data (G_OBJECT (dialog), "profile-path-property");
|
|
|
|
|
|
|
|
|
|
g_object_get (config, property, &path, NULL);
|
|
|
|
|
|
|
|
|
|
if (path)
|
|
|
|
|
{
|
|
|
|
|
GFile *folder = gimp_file_new_for_config_path (path, NULL);
|
|
|
|
|
|
|
|
|
|
if (folder)
|
|
|
|
|
{
|
|
|
|
|
gtk_file_chooser_set_current_folder_file (chooser, folder, NULL);
|
|
|
|
|
g_object_unref (folder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
connect_path_response (GimpColorProfileChooserDialog *dialog,
|
|
|
|
|
gint response)
|
|
|
|
|
{
|
|
|
|
|
if (response == GTK_RESPONSE_ACCEPT)
|
|
|
|
|
{
|
|
|
|
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
|
|
|
|
|
GFile *file = gtk_file_chooser_get_file (chooser);
|
|
|
|
|
|
|
|
|
|
if (file)
|
|
|
|
|
{
|
|
|
|
|
GFile *folder = gtk_file_chooser_get_current_folder_file (chooser);
|
|
|
|
|
|
|
|
|
|
if (folder)
|
|
|
|
|
{
|
|
|
|
|
GObject *config;
|
|
|
|
|
const gchar *property;
|
|
|
|
|
gchar *path = NULL;
|
|
|
|
|
|
|
|
|
|
config = g_object_get_data (G_OBJECT (dialog),
|
|
|
|
|
"profile-path-config");
|
|
|
|
|
property = g_object_get_data (G_OBJECT (dialog),
|
|
|
|
|
"profile-path-property");
|
|
|
|
|
|
|
|
|
|
path = gimp_file_get_config_path (folder, NULL);
|
|
|
|
|
|
|
|
|
|
g_object_set (config, property, path, NULL);
|
|
|
|
|
|
|
|
|
|
if (path)
|
|
|
|
|
g_free (path);
|
|
|
|
|
|
|
|
|
|
g_object_unref (folder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_object_unref (file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_color_profile_chooser_dialog_connect_path (GtkWidget *dialog,
|
|
|
|
|
GObject *config,
|
|
|
|
|
const gchar *property_name)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG (dialog));
|
|
|
|
|
g_return_if_fail (G_IS_OBJECT (config));
|
|
|
|
|
g_return_if_fail (property_name != NULL);
|
|
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (dialog), "profile-path-config",
|
|
|
|
|
g_object_ref (config),
|
|
|
|
|
(GDestroyNotify) g_object_unref);
|
|
|
|
|
g_object_set_data_full (G_OBJECT (dialog), "profile-path-property",
|
|
|
|
|
g_strdup (property_name),
|
|
|
|
|
(GDestroyNotify) g_free);
|
|
|
|
|
|
|
|
|
|
g_signal_connect (dialog, "show",
|
|
|
|
|
G_CALLBACK (connect_path_show),
|
|
|
|
|
NULL);
|
|
|
|
|
g_signal_connect (dialog, "response",
|
|
|
|
|
G_CALLBACK (connect_path_response),
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2019-02-19 15:31:50 +01:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_widget_flush_expose (void)
|
|
|
|
|
{
|
|
|
|
|
while (g_main_context_pending (NULL))
|
|
|
|
|
g_main_context_iteration (NULL, FALSE);
|
|
|
|
|
}
|
2022-03-05 14:32:34 +01:00
|
|
|
|
|
2023-02-05 19:29:41 +01:00
|
|
|
|
void
|
|
|
|
|
gimp_make_valid_action_name (gchar *action_name)
|
|
|
|
|
{
|
|
|
|
|
/* g_action_name_is_valid() says: "action_name is valid if it consists only of
|
|
|
|
|
* alphanumeric characters, plus ‘-‘ and ‘.’. The empty string is not a valid
|
|
|
|
|
* action name."
|
|
|
|
|
*/
|
|
|
|
|
for (gint i = 0; action_name[i] != '\0'; i++)
|
|
|
|
|
{
|
|
|
|
|
if (action_name[i] == '-' || action_name[i] == '.' ||
|
|
|
|
|
(action_name[i] >= 'a' && action_name[i] <= 'z') ||
|
|
|
|
|
(action_name[i] >= 'A' && action_name[i] <= 'Z') ||
|
|
|
|
|
(action_name[i] >= '0' && action_name[i] <= '9'))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
action_name[i] = '-';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-05 14:32:34 +01:00
|
|
|
|
|
|
|
|
|
/* private functions */
|
|
|
|
|
|
|
|
|
|
static void
|
2022-03-06 14:31:27 +01:00
|
|
|
|
gimp_widget_blink_after (GtkWidget *widget,
|
|
|
|
|
gint ms_timeout)
|
|
|
|
|
{
|
|
|
|
|
WidgetBlink *blink;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
|
|
|
|
|
|
|
|
blink = widget_blink_new ();
|
|
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (widget), "gimp-widget-blink", blink,
|
|
|
|
|
(GDestroyNotify) widget_blink_free);
|
|
|
|
|
|
|
|
|
|
blink->timeout_id = g_timeout_add (ms_timeout,
|
|
|
|
|
(GSourceFunc) gimp_widget_blink_start_timeout,
|
|
|
|
|
widget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gimp_search_widget_rec (GtkWidget *widget,
|
|
|
|
|
BlinkSearch *data)
|
2022-03-05 14:32:34 +01:00
|
|
|
|
{
|
2022-03-06 14:31:27 +01:00
|
|
|
|
GList **blink_script = data->blink_script;
|
|
|
|
|
const gchar *searched_id = data->widget_identifier;
|
|
|
|
|
const gchar *settings_value = data->settings_value;
|
|
|
|
|
const gchar *id;
|
2022-03-05 17:29:44 +01:00
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
id = g_object_get_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-identifier");
|
|
|
|
|
|
|
|
|
|
if (id == NULL)
|
|
|
|
|
/* Using propwidgets identifiers as fallback. */
|
|
|
|
|
id = g_object_get_data (G_OBJECT (widget),
|
|
|
|
|
"gimp-widget-property-name");
|
|
|
|
|
|
|
|
|
|
if (id && g_strcmp0 (id, searched_id) == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Giving focus to help scrolling the dockable so that the
|
|
|
|
|
* widget is visible. Note that it seems to work fine if the
|
|
|
|
|
* dockable was already present, not if it was just created.
|
|
|
|
|
*
|
|
|
|
|
* TODO: this should be fixed so that we always make the
|
|
|
|
|
* widget visible before blinking, otherwise it's a bit
|
|
|
|
|
* useless when this happens.
|
|
|
|
|
*/
|
|
|
|
|
gtk_widget_grab_focus (widget);
|
|
|
|
|
if (blink_script)
|
|
|
|
|
gimp_widget_script_blink (widget, settings_value, blink_script);
|
|
|
|
|
else if (gtk_widget_is_visible (widget))
|
|
|
|
|
gimp_widget_blink (widget);
|
|
|
|
|
}
|
|
|
|
|
else if (GTK_IS_CONTAINER (widget))
|
2022-03-05 14:32:34 +01:00
|
|
|
|
{
|
2022-03-06 14:31:27 +01:00
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (widget),
|
|
|
|
|
(GtkCallback) gimp_search_widget_rec,
|
|
|
|
|
(gpointer) data);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-05 14:32:34 +01:00
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
static void
|
|
|
|
|
gimp_blink_free_script (GList *blink_scenario)
|
|
|
|
|
{
|
|
|
|
|
GList *iter;
|
2022-03-05 14:32:34 +01:00
|
|
|
|
|
2022-03-06 14:31:27 +01:00
|
|
|
|
for (iter = blink_scenario; iter; iter = iter->next)
|
|
|
|
|
{
|
|
|
|
|
BlinkStep *step = iter->data;
|
|
|
|
|
|
|
|
|
|
g_free (step->settings_value);
|
|
|
|
|
g_slice_free (BlinkStep, step);
|
2022-03-05 14:32:34 +01:00
|
|
|
|
}
|
2022-03-06 14:31:27 +01:00
|
|
|
|
g_list_free (blink_scenario);
|
2022-03-05 14:32:34 +01:00
|
|
|
|
}
|
2023-03-30 14:53:01 +02:00
|
|
|
|
|
|
|
|
|
gchar *
|
|
|
|
|
gimp_utils_make_canonical_menu_label (const gchar *path)
|
|
|
|
|
{
|
|
|
|
|
gchar **split_path;
|
|
|
|
|
gchar *canon_path;
|
|
|
|
|
|
|
|
|
|
/* The first underscore of each path item is a mnemonic. */
|
|
|
|
|
split_path = g_strsplit (path, "_", 2);
|
|
|
|
|
canon_path = g_strjoinv ("", split_path);
|
|
|
|
|
g_strfreev (split_path);
|
|
|
|
|
|
|
|
|
|
return canon_path;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-13 15:29:55 +02:00
|
|
|
|
/**
|
|
|
|
|
* gimp_utils_break_menu_path:
|
|
|
|
|
* @path:
|
|
|
|
|
* @section_name:
|
|
|
|
|
*
|
|
|
|
|
* Break @path which is in the form "/_some/p_ath" into a GStrv containing "some"
|
|
|
|
|
* and "path", in a canonical way:
|
|
|
|
|
* - Multiple slashes are folded to one (e.g. "//some///path/" and "/some/path"
|
|
|
|
|
* are equivalent).
|
|
|
|
|
* - End slash or none are equivalent.
|
|
|
|
|
* - Individual path components are canonicalized with
|
|
|
|
|
* gimp_utils_make_canonical_menu_label(), in particular with mnemonic
|
|
|
|
|
* underscore removed.
|
|
|
|
|
*
|
|
|
|
|
* Moreover if @section_name is not %NULL, then a path component of the form
|
|
|
|
|
* "[section]" at the end of the path will be removed and "section" will be
|
|
|
|
|
* stored inside @section_name. Furthermore, "[[label]]" or "[[label]" will both
|
|
|
|
|
* be transformed into a component path "[label]", without being removed, as a
|
|
|
|
|
* way to create menu paths containing square brackets.
|
|
|
|
|
*/
|
2023-03-30 14:53:01 +02:00
|
|
|
|
gchar **
|
2023-07-13 15:29:55 +02:00
|
|
|
|
gimp_utils_break_menu_path (const gchar *path,
|
2023-07-14 16:13:39 +02:00
|
|
|
|
gchar **mnemonic_path1,
|
2023-07-13 15:29:55 +02:00
|
|
|
|
gchar **section_name)
|
2023-03-30 14:53:01 +02:00
|
|
|
|
{
|
2023-07-14 16:13:39 +02:00
|
|
|
|
GRegex *path_regex;
|
|
|
|
|
gchar **paths;
|
|
|
|
|
GString *mnemonic_string = NULL;
|
|
|
|
|
gint start = 0;
|
2023-03-30 14:53:01 +02:00
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
|
|
|
|
|
|
|
|
path_regex = g_regex_new ("/+", 0, 0, NULL);
|
|
|
|
|
|
2023-07-14 16:13:39 +02:00
|
|
|
|
if (mnemonic_path1 != NULL)
|
|
|
|
|
mnemonic_string = g_string_new (NULL);
|
|
|
|
|
|
2023-03-30 14:53:01 +02:00
|
|
|
|
/* Get rid of leading slashes. */
|
|
|
|
|
while (path[start] == '/' && path[start] != '\0')
|
|
|
|
|
start++;
|
|
|
|
|
|
|
|
|
|
/* Get rid of empty last item because of trailing slashes. */
|
|
|
|
|
paths = g_regex_split (path_regex, path + start, 0);
|
|
|
|
|
if (g_strv_length (paths) > 0 &&
|
|
|
|
|
strlen (paths[g_strv_length (paths) - 1]) == 0)
|
|
|
|
|
{
|
|
|
|
|
g_free (paths[g_strv_length (paths) - 1]);
|
|
|
|
|
paths[g_strv_length (paths) - 1] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; paths[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
gchar *canon_path;
|
|
|
|
|
|
2023-07-13 15:29:55 +02:00
|
|
|
|
if (section_name && paths[i + 1] == NULL)
|
|
|
|
|
{
|
|
|
|
|
gint path_len;
|
|
|
|
|
|
|
|
|
|
path_len = strlen (paths[i]);
|
|
|
|
|
if (path_len > 2 && paths[i][0] == '[' && paths[i][path_len - 1] == ']')
|
|
|
|
|
{
|
|
|
|
|
if (paths[i][1] == '[')
|
|
|
|
|
{
|
|
|
|
|
if (paths[i][path_len - 2] == ']')
|
|
|
|
|
paths[i][path_len - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
canon_path = g_strdup (paths[i] + 1);
|
|
|
|
|
g_free (paths[i]);
|
|
|
|
|
paths[i] = canon_path;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
paths[i][path_len - 1] = '\0';
|
|
|
|
|
*section_name = g_strdup (paths[i] + 1);
|
|
|
|
|
|
|
|
|
|
g_free (paths[i]);
|
|
|
|
|
paths[i] = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-14 16:13:39 +02:00
|
|
|
|
|
|
|
|
|
if (mnemonic_string != NULL)
|
|
|
|
|
g_string_append_printf (mnemonic_string, "/%s", paths[i]);
|
|
|
|
|
|
2023-03-30 14:53:01 +02:00
|
|
|
|
canon_path = gimp_utils_make_canonical_menu_label (paths[i]);
|
|
|
|
|
g_free (paths[i]);
|
|
|
|
|
paths[i] = canon_path;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-14 16:13:39 +02:00
|
|
|
|
if (mnemonic_path1 != NULL)
|
|
|
|
|
*mnemonic_path1 = g_string_free (mnemonic_string, FALSE);
|
|
|
|
|
|
2023-03-30 14:53:01 +02:00
|
|
|
|
g_regex_unref (path_regex);
|
|
|
|
|
|
|
|
|
|
return paths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
2023-07-13 15:29:55 +02:00
|
|
|
|
gimp_utils_are_menu_path_identical (const gchar *path1,
|
|
|
|
|
const gchar *path2,
|
2023-07-14 16:13:39 +02:00
|
|
|
|
gchar **canonical_path1,
|
|
|
|
|
gchar **mnemonic_path1,
|
2023-07-13 15:29:55 +02:00
|
|
|
|
gchar **path1_section_name)
|
2023-03-30 14:53:01 +02:00
|
|
|
|
{
|
|
|
|
|
gchar **paths1;
|
|
|
|
|
gchar **paths2;
|
|
|
|
|
gboolean identical = TRUE;
|
|
|
|
|
|
|
|
|
|
if (path1 == NULL)
|
|
|
|
|
path1 = "/";
|
|
|
|
|
if (path2 == NULL)
|
|
|
|
|
path2 = "/";
|
|
|
|
|
|
2023-07-14 16:13:39 +02:00
|
|
|
|
paths1 = gimp_utils_break_menu_path (path1, mnemonic_path1, path1_section_name);
|
|
|
|
|
paths2 = gimp_utils_break_menu_path (path2, NULL, NULL);
|
2023-03-30 14:53:01 +02:00
|
|
|
|
if (g_strv_length (paths1) != g_strv_length (paths2))
|
|
|
|
|
{
|
|
|
|
|
identical = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; paths1[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
if (g_strcmp0 (paths1[i], paths2[i]) != 0)
|
|
|
|
|
{
|
|
|
|
|
identical = FALSE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-14 16:13:39 +02:00
|
|
|
|
|
|
|
|
|
if (canonical_path1)
|
|
|
|
|
{
|
|
|
|
|
gchar *joined = g_strjoinv ("/", paths1);
|
|
|
|
|
|
|
|
|
|
*canonical_path1 = g_strdup_printf ("/%s", joined);
|
|
|
|
|
|
|
|
|
|
g_free (joined);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-30 14:53:01 +02:00
|
|
|
|
g_strfreev (paths1);
|
|
|
|
|
g_strfreev (paths2);
|
|
|
|
|
|
|
|
|
|
return identical;
|
|
|
|
|
}
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
gimp_window_transient_on_mapped (GtkWidget *window,
|
app, libgimp, pdb: add a parent_window parameter to gimp_*_popup() PDB calls.
Brush, font, gradient, palette and pattern choices are currently chosen through
a dialog created by the core, which then returns the user choice to the calling
plug-in. This has the unfortunate consequence of having a pile of likely at
least 3 windows (main GIMP window by core process, plug-in window by plug-in
process, then the choice popup by the core process) shared in 2 processes, which
often end up under each other and that's messy. Even more as the choice popup is
kinda expected to be like a sub-part of the plug-in dialog.
So anyway, now the plug-in can send its window handle to the core so that the
resource choice dialog ends up always above the plug-in dialog.
Of course, it will always work only on platforms where we have working
inter-process transient support.
2023-08-15 00:12:16 +02:00
|
|
|
|
GdkEventAny *event G_GNUC_UNUSED,
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
GimpProgress *progress)
|
|
|
|
|
{
|
app, libgimp, pdb: add a parent_window parameter to gimp_*_popup() PDB calls.
Brush, font, gradient, palette and pattern choices are currently chosen through
a dialog created by the core, which then returns the user choice to the calling
plug-in. This has the unfortunate consequence of having a pile of likely at
least 3 windows (main GIMP window by core process, plug-in window by plug-in
process, then the choice popup by the core process) shared in 2 processes, which
often end up under each other and that's messy. Even more as the choice popup is
kinda expected to be like a sub-part of the plug-in dialog.
So anyway, now the plug-in can send its window handle to the core so that the
resource choice dialog ends up always above the plug-in dialog.
Of course, it will always work only on platforms where we have working
inter-process transient support.
2023-08-15 00:12:16 +02:00
|
|
|
|
GBytes *handle;
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
|
|
|
|
|
handle = gimp_progress_get_window_id (progress);
|
|
|
|
|
|
|
|
|
|
if (handle == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
app, libgimp, pdb: add a parent_window parameter to gimp_*_popup() PDB calls.
Brush, font, gradient, palette and pattern choices are currently chosen through
a dialog created by the core, which then returns the user choice to the calling
plug-in. This has the unfortunate consequence of having a pile of likely at
least 3 windows (main GIMP window by core process, plug-in window by plug-in
process, then the choice popup by the core process) shared in 2 processes, which
often end up under each other and that's messy. Even more as the choice popup is
kinda expected to be like a sub-part of the plug-in dialog.
So anyway, now the plug-in can send its window handle to the core so that the
resource choice dialog ends up always above the plug-in dialog.
Of course, it will always work only on platforms where we have working
inter-process transient support.
2023-08-15 00:12:16 +02:00
|
|
|
|
gimp_window_set_transient_cb (window, NULL, handle);
|
|
|
|
|
|
|
|
|
|
g_bytes_unref (handle);
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gimp_window_set_transient_cb (GtkWidget *window,
|
|
|
|
|
GdkEventAny *event G_GNUC_UNUSED,
|
|
|
|
|
GBytes *handle)
|
|
|
|
|
{
|
|
|
|
|
gboolean transient_set = FALSE;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (handle != NULL);
|
|
|
|
|
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
|
|
|
if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()))
|
|
|
|
|
{
|
|
|
|
|
char *wayland_handle;
|
|
|
|
|
|
|
|
|
|
wayland_handle = (char *) g_bytes_get_data (handle, NULL);
|
|
|
|
|
gdk_wayland_window_set_transient_for_exported (gtk_widget_get_window (window),
|
|
|
|
|
wayland_handle);
|
|
|
|
|
transient_set = TRUE;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2023-08-14 18:10:41 +02:00
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
|
|
|
if (! transient_set && GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
|
|
|
|
|
{
|
|
|
|
|
GdkWindow *parent;
|
|
|
|
|
Window *handle_data;
|
|
|
|
|
Window parent_ID;
|
|
|
|
|
gsize handle_size;
|
|
|
|
|
|
|
|
|
|
handle_data = (Window *) g_bytes_get_data (handle, &handle_size);
|
app, libgimp, pdb: add a parent_window parameter to gimp_*_popup() PDB calls.
Brush, font, gradient, palette and pattern choices are currently chosen through
a dialog created by the core, which then returns the user choice to the calling
plug-in. This has the unfortunate consequence of having a pile of likely at
least 3 windows (main GIMP window by core process, plug-in window by plug-in
process, then the choice popup by the core process) shared in 2 processes, which
often end up under each other and that's messy. Even more as the choice popup is
kinda expected to be like a sub-part of the plug-in dialog.
So anyway, now the plug-in can send its window handle to the core so that the
resource choice dialog ends up always above the plug-in dialog.
Of course, it will always work only on platforms where we have working
inter-process transient support.
2023-08-15 00:12:16 +02:00
|
|
|
|
g_return_if_fail (handle_size == sizeof (Window));
|
2023-08-14 18:10:41 +02:00
|
|
|
|
parent_ID = *handle_data;
|
|
|
|
|
|
|
|
|
|
parent = gimp_get_foreign_window ((gpointer) parent_ID);
|
|
|
|
|
|
|
|
|
|
if (parent)
|
|
|
|
|
gdk_window_set_transient_for (gtk_widget_get_window (window), parent);
|
|
|
|
|
|
|
|
|
|
transient_set = TRUE;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2023-08-30 20:31:48 +02:00
|
|
|
|
/* Cross-process transient-for is broken in gdk/win32. It causes hangs of the
|
|
|
|
|
* main process and we still don't know why.
|
|
|
|
|
* If it eventually is fixed to actually work, change this to a run-time check
|
|
|
|
|
* of GTK+ version. Remember to change also gimp_window_transient_on_mapped()
|
|
|
|
|
* in libgimp/gimpui.c
|
|
|
|
|
*
|
|
|
|
|
* Note: this hanging bug is still happening with GTK+3 as of mid-2023 with
|
|
|
|
|
* steps described in comment 4 in:
|
|
|
|
|
* https://bugzilla.gnome.org/show_bug.cgi?id=359538
|
|
|
|
|
*/
|
|
|
|
|
#if 0 && defined (GDK_WINDOWING_WIN32)
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
if (! transient_set)
|
|
|
|
|
{
|
|
|
|
|
GdkWindow *parent;
|
2023-08-14 18:10:41 +02:00
|
|
|
|
HANDLE *handle_data;
|
|
|
|
|
HANDLE parent_ID;
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
gsize handle_size;
|
|
|
|
|
|
2023-08-14 18:10:41 +02:00
|
|
|
|
handle_data = (HANDLE *) g_bytes_get_data (handle, &handle_size);
|
app, libgimp, pdb: add a parent_window parameter to gimp_*_popup() PDB calls.
Brush, font, gradient, palette and pattern choices are currently chosen through
a dialog created by the core, which then returns the user choice to the calling
plug-in. This has the unfortunate consequence of having a pile of likely at
least 3 windows (main GIMP window by core process, plug-in window by plug-in
process, then the choice popup by the core process) shared in 2 processes, which
often end up under each other and that's messy. Even more as the choice popup is
kinda expected to be like a sub-part of the plug-in dialog.
So anyway, now the plug-in can send its window handle to the core so that the
resource choice dialog ends up always above the plug-in dialog.
Of course, it will always work only on platforms where we have working
inter-process transient support.
2023-08-15 00:12:16 +02:00
|
|
|
|
g_return_if_fail (handle_size == sizeof (HANDLE));
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
parent_ID = *handle_data;
|
|
|
|
|
|
2023-08-14 18:10:41 +02:00
|
|
|
|
parent = gimp_get_foreign_window ((gpointer) parent_ID);
|
app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window.
Having windows ID as guint32 is a mistake. Different systems have
different protocols. In Wayland in particular, Windows handles are
exchanged as strings. What this commit does is the following:
In core:
- get_window_id() virtual function in core GimpProgress is changed to
return a GBytes, as a generic "data" to represent a window differently
on different systems.
- All implementations of get_window_id() in various classes implementing
this interface are updated accordingly:
* GimpSubProgress
* GimpDisplay returns the handle of its shell.
* GimpDisplayShell now creates its window handle at construction with
libgimpwidget's gimp_widget_set_native_handle() and simply return
this handle every time it's requested.
* GimpFileDialog also creates its window handle at construction with
gimp_widget_set_native_handle().
- gimp_window_set_transient_for() in core is changed to take a
GimpProgress as argument (instead of a guint32 ID), requests and
process the ID itself, according to the running platform. In
particular, the following were improved:
* Unlike old code, it will work even if the window is not visible yet.
In such a case, the function simply adds a signal handler to set
transient at mapping. It makes it easier to use it at construction
in a reliable way.
* It now works for Wayland too, additionally to X11.
- GimpPdbProgress now exchanges a GBytes too with the command
GIMP_PROGRESS_COMMAND_GET_WINDOW.
- display_get_window_id() in gimp-gui.h also returns a GBytes now.
PDB/libgimp:
- gimp_display_get_window_handle() and gimp_progress_get_window_handle()
now return a GBytes to represent a window handle in an opaque way
(depending on the running platform).
In libgimp:
- GimpProgress's get_window() virtual function changed to return a
GBytes and renamed get_window_handle().
- In particular GimpProgressBar is the only implementation of
get_window_handle(). It creates its handle at object construction with
libgimpwidget's gimp_widget_set_native_handle() and the virtual
method's implementation simply returns the GBytes.
In libgimpUi:
- gimp_ui_get_display_window() and gimp_ui_get_progress_window() were
removed. We should not assume anymore that it is possible to create a
GdkWindow to be used. For instance this is not possible with Wayland
which has its own way to set a window transient with a string handle.
- gimp_window_set_transient_for_display() and
gimp_window_set_transient() now use an internal implementation similar
to core gimp_window_set_transient_for(), with the same improvements
(works even at construction when the window is not visible yet + works
for Wayland too).
In libgimpwidgets:
- New gimp_widget_set_native_handle() is a helper function used both in
core and libgimp* libraries for widgets which we want to be usable as
possible parents. It takes care of getting the relevant window handle
(depending on the running platform) and stores it in a given pointer,
either immediately or after a callback once the widget is mapped. So
it can be used at construction. Also it sets a handle for X11 or
Wayland.
In plug-ins:
- Screenshot uses the new gimp_progress_get_window_handle() directly now
in its X11 code path and creates out of it a GdkWindows itself with
gdk_x11_window_foreign_new_for_display().
Our inter-process transient implementation only worked for X11, and with
this commit, it works for Wayland too.
There is code for Windows but it is currently disabled as it apparently
hangs (there is a comment in-code which links to this old report:
https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested
yesterday with re-enabling the code and said they experienced a freeze.
;-(
Finally there is no infrastructure yet to make this work on macOS and
apparently there is no implementation of window handle in GDK for macOS
that I could find. I'm not sure if macOS doesn't have this concept of
setting transient on another processus's window or GDK is simply lacking
the implementation.
2023-08-14 14:23:06 +02:00
|
|
|
|
|
|
|
|
|
if (parent)
|
|
|
|
|
gdk_window_set_transient_for (gtk_widget_get_window (window), parent);
|
|
|
|
|
|
|
|
|
|
transient_set = TRUE;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2023-10-16 16:43:17 +00:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gimp_window_set_title_bar_theme (Gimp *gimp,
|
|
|
|
|
GtkWidget *dialog,
|
|
|
|
|
gboolean is_main_window)
|
|
|
|
|
{
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
HWND hwnd;
|
|
|
|
|
GdkWindow *window = NULL;
|
|
|
|
|
gboolean use_dark_mode = FALSE;
|
|
|
|
|
|
|
|
|
|
window = gtk_widget_get_window (GTK_WIDGET (dialog));
|
|
|
|
|
if (window)
|
|
|
|
|
{
|
|
|
|
|
if (gimp)
|
|
|
|
|
{
|
|
|
|
|
GimpGuiConfig *config;
|
|
|
|
|
|
|
|
|
|
config = GIMP_GUI_CONFIG (gimp->config);
|
|
|
|
|
use_dark_mode = config->prefer_dark_theme;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GtkStyleContext *style;
|
|
|
|
|
GdkRGBA *color = NULL;
|
|
|
|
|
|
|
|
|
|
/* Workaround if we don't have access to GimpGuiConfig.
|
|
|
|
|
* If the background color is below the threshold, then we're
|
|
|
|
|
* likely in dark mode.
|
|
|
|
|
*/
|
|
|
|
|
style = gtk_widget_get_style_context (dialog);
|
|
|
|
|
gtk_style_context_get (style, gtk_style_context_get_state (style),
|
|
|
|
|
GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &color,
|
|
|
|
|
NULL);
|
|
|
|
|
if (color)
|
|
|
|
|
{
|
|
|
|
|
if (color->red < 0.5 && color->green < 0.5 && color->blue < 0.5)
|
|
|
|
|
use_dark_mode = TRUE;
|
|
|
|
|
|
|
|
|
|
gdk_rgba_free (color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hwnd = (HWND) gdk_win32_window_get_handle (window);
|
|
|
|
|
DwmSetWindowAttribute (hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE,
|
|
|
|
|
&use_dark_mode, sizeof (use_dark_mode));
|
|
|
|
|
|
|
|
|
|
if (! is_main_window)
|
|
|
|
|
{
|
|
|
|
|
/* Toggle the window's visibility so the title bar change appears */
|
|
|
|
|
gdk_window_hide (window);
|
|
|
|
|
gdk_window_show (window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|