gimp/app/dialogs/layer-options-dialog.c

574 lines
23 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (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
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "dialogs-types.h"
#include "operations/layer-modes/gimp-layer-modes.h"
#include "core/gimpcontext.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpimage.h"
#include "core/gimplayer.h"
#include "text/gimptext.h"
#include "text/gimptextlayer.h"
#include "widgets/gimpcontainertreeview.h"
#include "widgets/gimplayermodebox.h"
#include "widgets/gimpviewabledialog.h"
#include "item-options-dialog.h"
#include "layer-options-dialog.h"
#include "gimp-intl.h"
typedef struct _LayerOptionsDialog LayerOptionsDialog;
struct _LayerOptionsDialog
{
GimpLayer *layer;
GimpLayerMode mode;
GimpLayerColorSpace blend_space;
GimpLayerColorSpace composite_space;
GimpLayerCompositeMode composite_mode;
gdouble opacity;
GimpFillType fill_type;
gboolean lock_alpha;
gboolean rename_text_layers;
GimpLayerOptionsCallback callback;
gpointer user_data;
GtkWidget *mode_box;
GtkWidget *blend_space_combo;
GtkWidget *composite_space_combo;
GtkWidget *composite_mode_combo;
GtkWidget *size_se;
GtkWidget *offset_se;
};
/* local function prototypes */
static void layer_options_dialog_free (LayerOptionsDialog *private);
static void layer_options_dialog_callback (GtkWidget *dialog,
GimpImage *image,
GimpItem *item,
GimpContext *context,
const gchar *item_name,
gboolean item_visible,
GimpColorTag item_color_tag,
gboolean item_lock_content,
gboolean item_lock_position,
gboolean item_lock_visibility,
gpointer user_data);
static void
layer_options_dialog_update_mode_sensitivity (LayerOptionsDialog *private);
static void layer_options_dialog_mode_notify (GtkWidget *widget,
const GParamSpec *pspec,
LayerOptionsDialog *private);
static void layer_options_dialog_rename_toggled (GtkWidget *widget,
LayerOptionsDialog *private);
/* public functions */
GtkWidget *
layer_options_dialog_new (GimpImage *image,
GimpLayer *layer,
GimpContext *context,
GtkWidget *parent,
const gchar *title,
const gchar *role,
const gchar *icon_name,
const gchar *desc,
const gchar *help_id,
const gchar *layer_name,
GimpLayerMode layer_mode,
GimpLayerColorSpace layer_blend_space,
GimpLayerColorSpace layer_composite_space,
GimpLayerCompositeMode layer_composite_mode,
gdouble layer_opacity,
GimpFillType layer_fill_type,
gboolean layer_visible,
GimpColorTag layer_color_tag,
gboolean layer_lock_content,
gboolean layer_lock_position,
gboolean layer_lock_visibility,
gboolean layer_lock_alpha,
GimpLayerOptionsCallback callback,
gpointer user_data)
{
LayerOptionsDialog *private;
GtkWidget *dialog;
GtkWidget *grid;
GtkListStore *space_model;
GtkWidget *combo;
GtkWidget *scale;
GtkWidget *label;
GtkAdjustment *adjustment;
GtkWidget *spinbutton;
GtkWidget *button;
GimpLayerModeContext mode_context;
gdouble xres;
gdouble yres;
gint row = 0;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), NULL);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL);
private = g_slice_new0 (LayerOptionsDialog);
private->layer = layer;
private->mode = layer_mode;
private->blend_space = layer_blend_space;
private->composite_space = layer_composite_space;
private->composite_mode = layer_composite_mode;
private->opacity = layer_opacity * 100.0;
private->fill_type = layer_fill_type;
private->lock_alpha = layer_lock_alpha;
private->rename_text_layers = FALSE;
private->callback = callback;
private->user_data = user_data;
if (layer && gimp_item_is_text_layer (GIMP_ITEM (layer)))
private->rename_text_layers = GIMP_TEXT_LAYER (layer)->auto_rename;
dialog = item_options_dialog_new (image, GIMP_ITEM (layer), context,
parent, title, role,
icon_name, desc, help_id,
_("Layer _name:"),
GIMP_ICON_LOCK_CONTENT,
_("Lock _pixels"),
_("Lock position and _size"),
_("Lock visibility"),
layer_name,
layer_visible,
layer_color_tag,
layer_lock_content,
layer_lock_position,
layer_lock_visibility,
layer_options_dialog_callback,
private);
g_object_weak_ref (G_OBJECT (dialog),
(GWeakNotify) layer_options_dialog_free, private);
if (! layer || gimp_viewable_get_children (GIMP_VIEWABLE (layer)) == NULL)
mode_context = GIMP_LAYER_MODE_CONTEXT_LAYER;
else
mode_context = GIMP_LAYER_MODE_CONTEXT_GROUP;
private->mode_box = gimp_layer_mode_box_new (mode_context);
item_options_dialog_add_widget (dialog, _("_Mode:"), private->mode_box);
gimp_layer_mode_box_set_mode (GIMP_LAYER_MODE_BOX (private->mode_box),
private->mode);
g_signal_connect (private->mode_box, "notify::layer-mode",
G_CALLBACK (layer_options_dialog_mode_notify),
private);
space_model =
gimp_enum_store_new_with_range (GIMP_TYPE_LAYER_COLOR_SPACE,
GIMP_LAYER_COLOR_SPACE_AUTO,
GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL);
private->blend_space_combo = combo =
gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (space_model));
item_options_dialog_add_widget (dialog, _("_Blend space:"), combo);
gimp_enum_combo_box_set_icon_prefix (GIMP_ENUM_COMBO_BOX (combo),
"gimp-layer-color-space");
gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
private->blend_space,
G_CALLBACK (gimp_int_combo_box_get_active),
&private->blend_space, NULL);
private->composite_space_combo = combo =
gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (space_model));
item_options_dialog_add_widget (dialog, _("Compos_ite space:"), combo);
gimp_enum_combo_box_set_icon_prefix (GIMP_ENUM_COMBO_BOX (combo),
"gimp-layer-color-space");
gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
private->composite_space,
G_CALLBACK (gimp_int_combo_box_get_active),
&private->composite_space, NULL);
g_object_unref (space_model);
private->composite_mode_combo = combo =
gimp_enum_combo_box_new (GIMP_TYPE_LAYER_COMPOSITE_MODE);
item_options_dialog_add_widget (dialog, _("Composite mo_de:"), combo);
gimp_enum_combo_box_set_icon_prefix (GIMP_ENUM_COMBO_BOX (combo),
"gimp-layer-composite");
gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
private->composite_mode,
G_CALLBACK (gimp_int_combo_box_get_active),
&private->composite_mode, NULL);
/* set the sensitivity of above 3 menus */
layer_options_dialog_update_mode_sensitivity (private);
adjustment = gtk_adjustment_new (private->opacity, 0.0, 100.0,
1.0, 10.0, 0.0);
scale = gimp_spin_scale_new (adjustment, NULL, 1);
item_options_dialog_add_widget (dialog, _("_Opacity:"), scale);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (gimp_double_adjustment_update),
&private->opacity);
grid = item_options_dialog_get_grid (dialog, &row);
gimp_image_get_resolution (image, &xres, &yres);
if (! layer)
{
/* The size labels */
label = gtk_label_new (_("Width:"));
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
label = gtk_label_new (_("Height:"));
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_grid_attach (GTK_GRID (grid), label, 0, row + 1, 1, 1);
gtk_widget_show (label);
/* The size sizeentry */
adjustment = gtk_adjustment_new (1, 1, 1, 1, 10, 0);
spinbutton = gimp_spin_button_new (adjustment, 1.0, 2);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
private->size_se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
TRUE, TRUE, FALSE, 10,
GIMP_SIZE_ENTRY_UPDATE_SIZE);
gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->size_se),
GTK_SPIN_BUTTON (spinbutton), NULL);
gtk_grid_attach (GTK_GRID (private->size_se), spinbutton, 1, 0, 1, 1);
gtk_widget_show (spinbutton);
gtk_grid_attach (GTK_GRID (grid), private->size_se, 1, row, 1, 2);
gtk_widget_show (private->size_se);
gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->size_se),
GIMP_UNIT_PIXEL);
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se), 0,
xres, FALSE);
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se), 1,
yres, FALSE);
gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->size_se), 0,
GIMP_MIN_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE);
gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->size_se), 1,
GIMP_MIN_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->size_se), 0,
0, gimp_image_get_width (image));
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->size_se), 1,
0, gimp_image_get_height (image));
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se), 0,
gimp_image_get_width (image));
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se), 1,
gimp_image_get_height (image));
row += 2;
}
/* The offset labels */
label = gtk_label_new (_("Offset X:"));
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
label = gtk_label_new (_("Offset Y:"));
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_grid_attach (GTK_GRID (grid), label, 0, row + 1, 1, 1);
gtk_widget_show (label);
/* The offset sizeentry */
adjustment = gtk_adjustment_new (0, 1, 1, 1, 10, 0);
spinbutton = gimp_spin_button_new (adjustment, 1.0, 2);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
private->offset_se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
TRUE, TRUE, FALSE, 10,
GIMP_SIZE_ENTRY_UPDATE_SIZE);
gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->offset_se),
GTK_SPIN_BUTTON (spinbutton), NULL);
gtk_grid_attach (GTK_GRID (private->offset_se), spinbutton, 1, 0, 1, 1);
gtk_widget_show (spinbutton);
gtk_grid_attach (GTK_GRID (grid), private->offset_se, 1, row, 1, 2);
gtk_widget_show (private->offset_se);
gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->offset_se),
GIMP_UNIT_PIXEL);
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->offset_se), 0,
xres, FALSE);
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->offset_se), 1,
yres, FALSE);
gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->offset_se), 0,
-GIMP_MAX_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE);
gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->offset_se), 1,
-GIMP_MAX_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->offset_se), 0,
0, gimp_image_get_width (image));
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->offset_se), 1,
0, gimp_image_get_height (image));
if (layer)
{
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 0,
gimp_item_get_offset_x (GIMP_ITEM (layer)));
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 1,
gimp_item_get_offset_y (GIMP_ITEM (layer)));
}
else
{
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 0, 0);
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 1, 0);
}
row += 2;
if (! layer)
{
/* The fill type */
combo = gimp_enum_combo_box_new (GIMP_TYPE_FILL_TYPE);
gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
_("_Fill with:"), 0.0, 0.5,
combo, 1);
gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
private->fill_type,
G_CALLBACK (gimp_int_combo_box_get_active),
&private->fill_type, NULL);
}
if (layer)
{
GtkWidget *left_vbox = item_options_dialog_get_vbox (dialog);
GtkWidget *frame;
GimpContainer *filters;
GtkWidget *view;
frame = gimp_frame_new (_("Active Filters"));
gtk_box_pack_start (GTK_BOX (left_vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (layer));
view = gimp_container_tree_view_new (filters, context,
GIMP_VIEW_SIZE_SMALL, 0);
gtk_container_add (GTK_CONTAINER (frame), view);
gtk_widget_show (view);
}
button = item_options_dialog_get_lock_position (dialog);
if (private->size_se)
g_object_bind_property (G_OBJECT (button), "active",
G_OBJECT (private->size_se), "sensitive",
G_BINDING_SYNC_CREATE |
G_BINDING_INVERT_BOOLEAN);
g_object_bind_property (G_OBJECT (button), "active",
G_OBJECT (private->offset_se), "sensitive",
G_BINDING_SYNC_CREATE |
G_BINDING_INVERT_BOOLEAN);
button = item_options_dialog_add_switch (dialog,
GIMP_ICON_LOCK_ALPHA,
_("Lock _alpha"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
private->lock_alpha);
g_signal_connect (button, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&private->lock_alpha);
/* For text layers add a toggle to control "auto-rename" */
if (layer && gimp_item_is_text_layer (GIMP_ITEM (layer)))
{
button = item_options_dialog_add_switch (dialog,
GIMP_ICON_TOOL_TEXT,
_("Set name from _text"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
private->rename_text_layers);
g_signal_connect (button, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&private->rename_text_layers);
g_signal_connect (button, "toggled",
G_CALLBACK (layer_options_dialog_rename_toggled),
private);
}
return dialog;
}
/* private functions */
static void
layer_options_dialog_free (LayerOptionsDialog *private)
{
g_slice_free (LayerOptionsDialog, private);
}
static void
layer_options_dialog_callback (GtkWidget *dialog,
GimpImage *image,
GimpItem *item,
GimpContext *context,
const gchar *item_name,
gboolean item_visible,
GimpColorTag item_color_tag,
gboolean item_lock_content,
gboolean item_lock_position,
gboolean item_lock_visibility,
gpointer user_data)
{
LayerOptionsDialog *private = user_data;
gint width = 0;
gint height = 0;
gint offset_x;
gint offset_y;
if (private->size_se)
{
width =
RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se),
0));
height =
RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se),
1));
}
offset_x =
RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->offset_se),
0));
offset_y =
RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->offset_se),
1));
private->callback (dialog,
image,
GIMP_LAYER (item),
context,
item_name,
private->mode,
private->blend_space,
private->composite_space,
private->composite_mode,
private->opacity / 100.0,
private->fill_type,
width,
height,
offset_x,
offset_y,
item_visible,
item_color_tag,
item_lock_content,
item_lock_position,
item_lock_visibility,
private->lock_alpha,
private->rename_text_layers,
private->user_data);
}
static void
layer_options_dialog_update_mode_sensitivity (LayerOptionsDialog *private)
{
gboolean mutable;
mutable = gimp_layer_mode_is_blend_space_mutable (private->mode);
gtk_widget_set_sensitive (private->blend_space_combo, mutable);
mutable = gimp_layer_mode_is_composite_space_mutable (private->mode);
gtk_widget_set_sensitive (private->composite_space_combo, mutable);
mutable = gimp_layer_mode_is_composite_mode_mutable (private->mode);
gtk_widget_set_sensitive (private->composite_mode_combo, mutable);
}
static void
layer_options_dialog_mode_notify (GtkWidget *widget,
const GParamSpec *pspec,
LayerOptionsDialog *private)
{
private->mode = gimp_layer_mode_box_get_mode (GIMP_LAYER_MODE_BOX (widget));
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->blend_space_combo),
GIMP_LAYER_COLOR_SPACE_AUTO);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->composite_space_combo),
GIMP_LAYER_COLOR_SPACE_AUTO);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->composite_mode_combo),
GIMP_LAYER_COMPOSITE_AUTO);
layer_options_dialog_update_mode_sensitivity (private);
}
static void
layer_options_dialog_rename_toggled (GtkWidget *widget,
LayerOptionsDialog *private)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) &&
gimp_item_is_text_layer (GIMP_ITEM (private->layer)))
{
GimpTextLayer *text_layer = GIMP_TEXT_LAYER (private->layer);
GimpText *text = gimp_text_layer_get_text (text_layer);
if (text && text->text)
{
GtkWidget *dialog;
GtkWidget *name_entry;
gchar *name = gimp_utf8_strtrim (text->text, 30);
dialog = gtk_widget_get_toplevel (widget);
name_entry = item_options_dialog_get_name_entry (dialog);
gtk_entry_set_text (GTK_ENTRY (name_entry), name);
g_free (name);
}
}
}