/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * pikacontext.c * Copyright (C) 1999-2010 Michael Natterer * * 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 . */ #include "config.h" #include #include #include #include "libpikabase/pikabase.h" #include "libpikacolor/pikacolor.h" #include "libpikaconfig/pikaconfig.h" #include "core-types.h" #include "config/pikacoreconfig.h" #include "pika.h" #include "pika-memsize.h" #include "pikabrush.h" #include "pikabuffer.h" #include "pikacontainer.h" #include "pikacontext.h" #include "pikadatafactory.h" #include "pikadisplay.h" #include "pikadynamics.h" #include "pikaimagefile.h" #include "pikagradient.h" #include "pikaimage.h" #include "pikalineart.h" #include "pikamybrush.h" #include "pikapaintinfo.h" #include "pikapalette.h" #include "pikapattern.h" #include "pikatemplate.h" #include "pikatoolinfo.h" #include "pikatoolpreset.h" #include "text/pikafont.h" #include "pika-intl.h" #define RGBA_EPSILON 1e-10 typedef void (* PikaContextCopyPropFunc) (PikaContext *src, PikaContext *dest); #define context_find_defined(context, prop) \ while (!(((context)->defined_props) & (1 << (prop))) && (context)->parent) \ (context) = (context)->parent #define COPY_NAME(src, dest, member) \ g_free (dest->member); \ dest->member = g_strdup (src->member) /* local function prototypes */ static void pika_context_config_iface_init (PikaConfigInterface *iface); static void pika_context_constructed (GObject *object); static void pika_context_dispose (GObject *object); static void pika_context_finalize (GObject *object); static void pika_context_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_context_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gint64 pika_context_get_memsize (PikaObject *object, gint64 *gui_size); static gboolean pika_context_serialize (PikaConfig *config, PikaConfigWriter *writer, gpointer data); static gboolean pika_context_deserialize (PikaConfig *config, GScanner *scanner, gint nest_level, gpointer data); static gboolean pika_context_serialize_property (PikaConfig *config, guint property_id, const GValue *value, GParamSpec *pspec, PikaConfigWriter *writer); static gboolean pika_context_deserialize_property (PikaConfig *config, guint property_id, GValue *value, GParamSpec *pspec, GScanner *scanner, GTokenType *expected); static PikaConfig * pika_context_duplicate (PikaConfig *config); static gboolean pika_context_copy (PikaConfig *src, PikaConfig *dest, GParamFlags flags); /* image */ static void pika_context_image_removed (PikaContainer *container, PikaImage *image, PikaContext *context); static void pika_context_real_set_image (PikaContext *context, PikaImage *image); /* display */ static void pika_context_display_removed (PikaContainer *container, PikaDisplay *display, PikaContext *context); static void pika_context_real_set_display (PikaContext *context, PikaDisplay *display); /* tool */ static void pika_context_tool_dirty (PikaToolInfo *tool_info, PikaContext *context); static void pika_context_tool_removed (PikaContainer *container, PikaToolInfo *tool_info, PikaContext *context); static void pika_context_tool_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_tool (PikaContext *context, PikaToolInfo *tool_info); /* paint info */ static void pika_context_paint_info_dirty (PikaPaintInfo *paint_info, PikaContext *context); static void pika_context_paint_info_removed (PikaContainer *container, PikaPaintInfo *paint_info, PikaContext *context); static void pika_context_paint_info_list_thaw(PikaContainer *container, PikaContext *context); static void pika_context_real_set_paint_info (PikaContext *context, PikaPaintInfo *paint_info); /* foreground */ static void pika_context_real_set_foreground (PikaContext *context, const PikaRGB *color); /* background */ static void pika_context_real_set_background (PikaContext *context, const PikaRGB *color); /* opacity */ static void pika_context_real_set_opacity (PikaContext *context, gdouble opacity); /* paint mode */ static void pika_context_real_set_paint_mode (PikaContext *context, PikaLayerMode paint_mode); /* brush */ static void pika_context_brush_dirty (PikaBrush *brush, PikaContext *context); static void pika_context_brush_removed (PikaContainer *brush_list, PikaBrush *brush, PikaContext *context); static void pika_context_brush_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_brush (PikaContext *context, PikaBrush *brush); /* dynamics */ static void pika_context_dynamics_dirty (PikaDynamics *dynamics, PikaContext *context); static void pika_context_dynamics_removed (PikaContainer *container, PikaDynamics *dynamics, PikaContext *context); static void pika_context_dynamics_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_dynamics (PikaContext *context, PikaDynamics *dynamics); /* mybrush */ static void pika_context_mybrush_dirty (PikaMybrush *brush, PikaContext *context); static void pika_context_mybrush_removed (PikaContainer *brush_list, PikaMybrush *brush, PikaContext *context); static void pika_context_mybrush_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_mybrush (PikaContext *context, PikaMybrush *brush); /* pattern */ static void pika_context_pattern_dirty (PikaPattern *pattern, PikaContext *context); static void pika_context_pattern_removed (PikaContainer *container, PikaPattern *pattern, PikaContext *context); static void pika_context_pattern_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_pattern (PikaContext *context, PikaPattern *pattern); /* gradient */ static void pika_context_gradient_dirty (PikaGradient *gradient, PikaContext *context); static void pika_context_gradient_removed (PikaContainer *container, PikaGradient *gradient, PikaContext *context); static void pika_context_gradient_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_gradient (PikaContext *context, PikaGradient *gradient); /* palette */ static void pika_context_palette_dirty (PikaPalette *palette, PikaContext *context); static void pika_context_palette_removed (PikaContainer *container, PikaPalette *palette, PikaContext *context); static void pika_context_palette_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_palette (PikaContext *context, PikaPalette *palette); /* font */ static void pika_context_font_dirty (PikaFont *font, PikaContext *context); static void pika_context_font_removed (PikaContainer *container, PikaFont *font, PikaContext *context); static void pika_context_font_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_font (PikaContext *context, PikaFont *font); /* tool preset */ static void pika_context_tool_preset_dirty (PikaToolPreset *tool_preset, PikaContext *context); static void pika_context_tool_preset_removed (PikaContainer *container, PikaToolPreset *tool_preset, PikaContext *context); static void pika_context_tool_preset_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_tool_preset (PikaContext *context, PikaToolPreset *tool_preset); /* buffer */ static void pika_context_buffer_dirty (PikaBuffer *buffer, PikaContext *context); static void pika_context_buffer_removed (PikaContainer *container, PikaBuffer *buffer, PikaContext *context); static void pika_context_buffer_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_buffer (PikaContext *context, PikaBuffer *buffer); /* imagefile */ static void pika_context_imagefile_dirty (PikaImagefile *imagefile, PikaContext *context); static void pika_context_imagefile_removed (PikaContainer *container, PikaImagefile *imagefile, PikaContext *context); static void pika_context_imagefile_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_imagefile (PikaContext *context, PikaImagefile *imagefile); /* template */ static void pika_context_template_dirty (PikaTemplate *template, PikaContext *context); static void pika_context_template_removed (PikaContainer *container, PikaTemplate *template, PikaContext *context); static void pika_context_template_list_thaw (PikaContainer *container, PikaContext *context); static void pika_context_real_set_template (PikaContext *context, PikaTemplate *template); /* line art */ static gboolean pika_context_free_line_art (PikaContext *context); /* utilities */ static gpointer pika_context_find_object (PikaContext *context, PikaContainer *container, const gchar *object_name, gpointer standard_object); /* properties & signals */ enum { PIKA_CONTEXT_PROP_0, PIKA_CONTEXT_PROP_PIKA /* remaining values are in core-enums.h (PikaContextPropType) */ }; enum { DUMMY_0, DUMMY_1, IMAGE_CHANGED, DISPLAY_CHANGED, TOOL_CHANGED, PAINT_INFO_CHANGED, FOREGROUND_CHANGED, BACKGROUND_CHANGED, OPACITY_CHANGED, PAINT_MODE_CHANGED, BRUSH_CHANGED, DYNAMICS_CHANGED, MYBRUSH_CHANGED, PATTERN_CHANGED, GRADIENT_CHANGED, PALETTE_CHANGED, FONT_CHANGED, TOOL_PRESET_CHANGED, BUFFER_CHANGED, IMAGEFILE_CHANGED, TEMPLATE_CHANGED, PROP_NAME_CHANGED, LAST_SIGNAL }; static const gchar * const pika_context_prop_names[] = { NULL, /* PROP_0 */ "pika", "image", "display", "tool", "paint-info", "foreground", "background", "opacity", "paint-mode", "brush", "dynamics", "mybrush", "pattern", "gradient", "palette", "font", "tool-preset", "buffer", "imagefile", "template" }; static GType pika_context_prop_types[] = { G_TYPE_NONE, /* PROP_0 */ G_TYPE_NONE, /* PROP_PIKA */ 0, G_TYPE_NONE, 0, 0, G_TYPE_NONE, G_TYPE_NONE, G_TYPE_NONE, G_TYPE_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; G_DEFINE_TYPE_WITH_CODE (PikaContext, pika_context, PIKA_TYPE_VIEWABLE, G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG, pika_context_config_iface_init)) #define parent_class pika_context_parent_class static PikaConfigInterface *parent_config_iface = NULL; static guint pika_context_signals[LAST_SIGNAL] = { 0 }; static void pika_context_class_init (PikaContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass); PikaRGB black; PikaRGB white; pika_rgba_set (&black, 0.0, 0.0, 0.0, PIKA_OPACITY_OPAQUE); pika_rgba_set (&white, 1.0, 1.0, 1.0, PIKA_OPACITY_OPAQUE); pika_context_signals[IMAGE_CHANGED] = g_signal_new ("image-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, image_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_IMAGE); pika_context_signals[DISPLAY_CHANGED] = g_signal_new ("display-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, display_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_DISPLAY); pika_context_signals[TOOL_CHANGED] = g_signal_new ("tool-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, tool_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_TOOL_INFO); pika_context_signals[PAINT_INFO_CHANGED] = g_signal_new ("paint-info-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, paint_info_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_PAINT_INFO); pika_context_signals[FOREGROUND_CHANGED] = g_signal_new ("foreground-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, foreground_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_RGB | G_SIGNAL_TYPE_STATIC_SCOPE); pika_context_signals[BACKGROUND_CHANGED] = g_signal_new ("background-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, background_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_RGB | G_SIGNAL_TYPE_STATIC_SCOPE); pika_context_signals[OPACITY_CHANGED] = g_signal_new ("opacity-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, opacity_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_DOUBLE); pika_context_signals[PAINT_MODE_CHANGED] = g_signal_new ("paint-mode-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, paint_mode_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_LAYER_MODE); pika_context_signals[BRUSH_CHANGED] = g_signal_new ("brush-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, brush_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_BRUSH); pika_context_signals[DYNAMICS_CHANGED] = g_signal_new ("dynamics-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, dynamics_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_DYNAMICS); pika_context_signals[MYBRUSH_CHANGED] = g_signal_new ("mybrush-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, mybrush_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_MYBRUSH); pika_context_signals[PATTERN_CHANGED] = g_signal_new ("pattern-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, pattern_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_PATTERN); pika_context_signals[GRADIENT_CHANGED] = g_signal_new ("gradient-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, gradient_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_GRADIENT); pika_context_signals[PALETTE_CHANGED] = g_signal_new ("palette-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, palette_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_PALETTE); pika_context_signals[FONT_CHANGED] = g_signal_new ("font-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, font_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_FONT); pika_context_signals[TOOL_PRESET_CHANGED] = g_signal_new ("tool-preset-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, tool_preset_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_TOOL_PRESET); pika_context_signals[BUFFER_CHANGED] = g_signal_new ("buffer-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, buffer_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_BUFFER); pika_context_signals[IMAGEFILE_CHANGED] = g_signal_new ("imagefile-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, imagefile_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_IMAGEFILE); pika_context_signals[TEMPLATE_CHANGED] = g_signal_new ("template-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, template_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_TEMPLATE); pika_context_signals[PROP_NAME_CHANGED] = g_signal_new ("prop-name-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaContextClass, prop_name_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT); object_class->constructed = pika_context_constructed; object_class->set_property = pika_context_set_property; object_class->get_property = pika_context_get_property; object_class->dispose = pika_context_dispose; object_class->finalize = pika_context_finalize; pika_object_class->get_memsize = pika_context_get_memsize; klass->image_changed = NULL; klass->display_changed = NULL; klass->tool_changed = NULL; klass->paint_info_changed = NULL; klass->foreground_changed = NULL; klass->background_changed = NULL; klass->opacity_changed = NULL; klass->paint_mode_changed = NULL; klass->brush_changed = NULL; klass->dynamics_changed = NULL; klass->mybrush_changed = NULL; klass->pattern_changed = NULL; klass->gradient_changed = NULL; klass->palette_changed = NULL; klass->font_changed = NULL; klass->tool_preset_changed = NULL; klass->buffer_changed = NULL; klass->imagefile_changed = NULL; klass->template_changed = NULL; klass->prop_name_changed = NULL; pika_context_prop_types[PIKA_CONTEXT_PROP_IMAGE] = PIKA_TYPE_IMAGE; pika_context_prop_types[PIKA_CONTEXT_PROP_TOOL] = PIKA_TYPE_TOOL_INFO; pika_context_prop_types[PIKA_CONTEXT_PROP_PAINT_INFO] = PIKA_TYPE_PAINT_INFO; pika_context_prop_types[PIKA_CONTEXT_PROP_BRUSH] = PIKA_TYPE_BRUSH; pika_context_prop_types[PIKA_CONTEXT_PROP_DYNAMICS] = PIKA_TYPE_DYNAMICS; pika_context_prop_types[PIKA_CONTEXT_PROP_MYBRUSH] = PIKA_TYPE_MYBRUSH; pika_context_prop_types[PIKA_CONTEXT_PROP_PATTERN] = PIKA_TYPE_PATTERN; pika_context_prop_types[PIKA_CONTEXT_PROP_GRADIENT] = PIKA_TYPE_GRADIENT; pika_context_prop_types[PIKA_CONTEXT_PROP_PALETTE] = PIKA_TYPE_PALETTE; pika_context_prop_types[PIKA_CONTEXT_PROP_FONT] = PIKA_TYPE_FONT; pika_context_prop_types[PIKA_CONTEXT_PROP_TOOL_PRESET] = PIKA_TYPE_TOOL_PRESET; pika_context_prop_types[PIKA_CONTEXT_PROP_BUFFER] = PIKA_TYPE_BUFFER; pika_context_prop_types[PIKA_CONTEXT_PROP_IMAGEFILE] = PIKA_TYPE_IMAGEFILE; pika_context_prop_types[PIKA_CONTEXT_PROP_TEMPLATE] = PIKA_TYPE_TEMPLATE; g_object_class_install_property (object_class, PIKA_CONTEXT_PROP_PIKA, g_param_spec_object ("pika", NULL, NULL, PIKA_TYPE_PIKA, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PIKA_CONTEXT_PROP_IMAGE, g_param_spec_object (pika_context_prop_names[PIKA_CONTEXT_PROP_IMAGE], NULL, NULL, PIKA_TYPE_IMAGE, PIKA_PARAM_READWRITE)); g_object_class_install_property (object_class, PIKA_CONTEXT_PROP_DISPLAY, g_param_spec_object (pika_context_prop_names[PIKA_CONTEXT_PROP_DISPLAY], NULL, NULL, PIKA_TYPE_DISPLAY, PIKA_PARAM_READWRITE)); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_TOOL, pika_context_prop_names[PIKA_CONTEXT_PROP_TOOL], NULL, NULL, PIKA_TYPE_TOOL_INFO, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_PAINT_INFO, pika_context_prop_names[PIKA_CONTEXT_PROP_PAINT_INFO], NULL, NULL, PIKA_TYPE_PAINT_INFO, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_RGB (object_class, PIKA_CONTEXT_PROP_FOREGROUND, pika_context_prop_names[PIKA_CONTEXT_PROP_FOREGROUND], _("Foreground"), _("Foreground color"), FALSE, &black, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_RGB (object_class, PIKA_CONTEXT_PROP_BACKGROUND, pika_context_prop_names[PIKA_CONTEXT_PROP_BACKGROUND], _("Background"), _("Background color"), FALSE, &white, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PIKA_CONTEXT_PROP_OPACITY, pika_context_prop_names[PIKA_CONTEXT_PROP_OPACITY], _("Opacity"), _("Opacity"), PIKA_OPACITY_TRANSPARENT, PIKA_OPACITY_OPAQUE, PIKA_OPACITY_OPAQUE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PIKA_CONTEXT_PROP_PAINT_MODE, pika_context_prop_names[PIKA_CONTEXT_PROP_PAINT_MODE], _("Paint Mode"), _("Paint Mode"), PIKA_TYPE_LAYER_MODE, PIKA_LAYER_MODE_NORMAL, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_BRUSH, pika_context_prop_names[PIKA_CONTEXT_PROP_BRUSH], _("Brush"), _("Brush"), PIKA_TYPE_BRUSH, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_DYNAMICS, pika_context_prop_names[PIKA_CONTEXT_PROP_DYNAMICS], _("Dynamics"), _("Paint dynamics"), PIKA_TYPE_DYNAMICS, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_MYBRUSH, pika_context_prop_names[PIKA_CONTEXT_PROP_MYBRUSH], _("MyPaint Brush"), _("MyPaint Brush"), PIKA_TYPE_MYBRUSH, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_PATTERN, pika_context_prop_names[PIKA_CONTEXT_PROP_PATTERN], _("Pattern"), _("Pattern"), PIKA_TYPE_PATTERN, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_GRADIENT, pika_context_prop_names[PIKA_CONTEXT_PROP_GRADIENT], _("Gradient"), _("Gradient"), PIKA_TYPE_GRADIENT, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_PALETTE, pika_context_prop_names[PIKA_CONTEXT_PROP_PALETTE], _("Palette"), _("Palette"), PIKA_TYPE_PALETTE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_FONT, pika_context_prop_names[PIKA_CONTEXT_PROP_FONT], _("Font"), _("Font"), PIKA_TYPE_FONT, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PIKA_CONTEXT_PROP_TOOL_PRESET, pika_context_prop_names[PIKA_CONTEXT_PROP_TOOL_PRESET], _("Tool Preset"), _("Tool Preset"), PIKA_TYPE_TOOL_PRESET, PIKA_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PIKA_CONTEXT_PROP_BUFFER, g_param_spec_object (pika_context_prop_names[PIKA_CONTEXT_PROP_BUFFER], NULL, NULL, PIKA_TYPE_BUFFER, PIKA_PARAM_READWRITE)); g_object_class_install_property (object_class, PIKA_CONTEXT_PROP_IMAGEFILE, g_param_spec_object (pika_context_prop_names[PIKA_CONTEXT_PROP_IMAGEFILE], NULL, NULL, PIKA_TYPE_IMAGEFILE, PIKA_PARAM_READWRITE)); g_object_class_install_property (object_class, PIKA_CONTEXT_PROP_TEMPLATE, g_param_spec_object (pika_context_prop_names[PIKA_CONTEXT_PROP_TEMPLATE], NULL, NULL, PIKA_TYPE_TEMPLATE, PIKA_PARAM_READWRITE)); } static void pika_context_init (PikaContext *context) { context->pika = NULL; context->parent = NULL; context->defined_props = PIKA_CONTEXT_PROP_MASK_ALL; context->serialize_props = PIKA_CONTEXT_PROP_MASK_ALL; context->image = NULL; context->display = NULL; context->tool_info = NULL; context->tool_name = NULL; context->paint_info = NULL; context->paint_name = NULL; context->brush = NULL; context->brush_name = NULL; context->dynamics = NULL; context->dynamics_name = NULL; context->mybrush = NULL; context->mybrush_name = NULL; context->pattern = NULL; context->pattern_name = NULL; context->gradient = NULL; context->gradient_name = NULL; context->palette = NULL; context->palette_name = NULL; context->font = NULL; context->font_name = NULL; context->tool_preset = NULL; context->tool_preset_name = NULL; context->buffer = NULL; context->buffer_name = NULL; context->imagefile = NULL; context->imagefile_name = NULL; context->template = NULL; context->template_name = NULL; context->line_art = NULL; context->line_art_timeout_id = 0; } static void pika_context_config_iface_init (PikaConfigInterface *iface) { parent_config_iface = g_type_interface_peek_parent (iface); if (! parent_config_iface) parent_config_iface = g_type_default_interface_peek (PIKA_TYPE_CONFIG); iface->serialize = pika_context_serialize; iface->deserialize = pika_context_deserialize; iface->serialize_property = pika_context_serialize_property; iface->deserialize_property = pika_context_deserialize_property; iface->duplicate = pika_context_duplicate; iface->copy = pika_context_copy; } static void pika_context_constructed (GObject *object) { Pika *pika; PikaContainer *container; G_OBJECT_CLASS (parent_class)->constructed (object); pika = PIKA_CONTEXT (object)->pika; pika_assert (PIKA_IS_PIKA (pika)); pika->context_list = g_list_prepend (pika->context_list, object); g_signal_connect_object (pika->images, "remove", G_CALLBACK (pika_context_image_removed), object, 0); g_signal_connect_object (pika->displays, "remove", G_CALLBACK (pika_context_display_removed), object, 0); g_signal_connect_object (pika->tool_info_list, "remove", G_CALLBACK (pika_context_tool_removed), object, 0); g_signal_connect_object (pika->tool_info_list, "thaw", G_CALLBACK (pika_context_tool_list_thaw), object, 0); g_signal_connect_object (pika->paint_info_list, "remove", G_CALLBACK (pika_context_paint_info_removed), object, 0); g_signal_connect_object (pika->paint_info_list, "thaw", G_CALLBACK (pika_context_paint_info_list_thaw), object, 0); container = pika_data_factory_get_container (pika->brush_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_brush_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_brush_list_thaw), object, 0); container = pika_data_factory_get_container (pika->dynamics_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_dynamics_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_dynamics_list_thaw), object, 0); container = pika_data_factory_get_container (pika->mybrush_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_mybrush_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_mybrush_list_thaw), object, 0); container = pika_data_factory_get_container (pika->pattern_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_pattern_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_pattern_list_thaw), object, 0); container = pika_data_factory_get_container (pika->gradient_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_gradient_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_gradient_list_thaw), object, 0); container = pika_data_factory_get_container (pika->palette_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_palette_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_palette_list_thaw), object, 0); container = pika_data_factory_get_container (pika->font_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_font_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_font_list_thaw), object, 0); container = pika_data_factory_get_container (pika->tool_preset_factory); g_signal_connect_object (container, "remove", G_CALLBACK (pika_context_tool_preset_removed), object, 0); g_signal_connect_object (container, "thaw", G_CALLBACK (pika_context_tool_preset_list_thaw), object, 0); g_signal_connect_object (pika->named_buffers, "remove", G_CALLBACK (pika_context_buffer_removed), object, 0); g_signal_connect_object (pika->named_buffers, "thaw", G_CALLBACK (pika_context_buffer_list_thaw), object, 0); g_signal_connect_object (pika->documents, "remove", G_CALLBACK (pika_context_imagefile_removed), object, 0); g_signal_connect_object (pika->documents, "thaw", G_CALLBACK (pika_context_imagefile_list_thaw), object, 0); g_signal_connect_object (pika->templates, "remove", G_CALLBACK (pika_context_template_removed), object, 0); g_signal_connect_object (pika->templates, "thaw", G_CALLBACK (pika_context_template_list_thaw), object, 0); pika_context_set_paint_info (PIKA_CONTEXT (object), pika_paint_info_get_standard (pika)); } static void pika_context_dispose (GObject *object) { PikaContext *context = PIKA_CONTEXT (object); pika_context_set_parent (context, NULL); if (context->pika) { context->pika->context_list = g_list_remove (context->pika->context_list, context); context->pika = NULL; } g_clear_object (&context->tool_info); g_clear_object (&context->paint_info); g_clear_object (&context->brush); g_clear_object (&context->dynamics); g_clear_object (&context->mybrush); g_clear_object (&context->pattern); g_clear_object (&context->gradient); g_clear_object (&context->palette); g_clear_object (&context->font); g_clear_object (&context->tool_preset); g_clear_object (&context->buffer); g_clear_object (&context->imagefile); g_clear_object (&context->template); G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_context_finalize (GObject *object) { PikaContext *context = PIKA_CONTEXT (object); context->parent = NULL; context->image = NULL; context->display = NULL; g_clear_pointer (&context->tool_name, g_free); g_clear_pointer (&context->paint_name, g_free); g_clear_pointer (&context->brush_name, g_free); g_clear_pointer (&context->dynamics_name, g_free); g_clear_pointer (&context->mybrush_name, g_free); g_clear_pointer (&context->pattern_name, g_free); g_clear_pointer (&context->gradient_name, g_free); g_clear_pointer (&context->palette_name, g_free); g_clear_pointer (&context->font_name, g_free); g_clear_pointer (&context->tool_preset_name, g_free); g_clear_pointer (&context->buffer_name, g_free); g_clear_pointer (&context->imagefile_name, g_free); g_clear_pointer (&context->template_name, g_free); g_clear_object (&context->line_art); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_context_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaContext *context = PIKA_CONTEXT (object); switch (property_id) { case PIKA_CONTEXT_PROP_PIKA: context->pika = g_value_get_object (value); break; case PIKA_CONTEXT_PROP_IMAGE: pika_context_set_image (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_DISPLAY: pika_context_set_display (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_TOOL: pika_context_set_tool (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_PAINT_INFO: pika_context_set_paint_info (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_FOREGROUND: pika_context_set_foreground (context, g_value_get_boxed (value)); break; case PIKA_CONTEXT_PROP_BACKGROUND: pika_context_set_background (context, g_value_get_boxed (value)); break; case PIKA_CONTEXT_PROP_OPACITY: pika_context_set_opacity (context, g_value_get_double (value)); break; case PIKA_CONTEXT_PROP_PAINT_MODE: pika_context_set_paint_mode (context, g_value_get_enum (value)); break; case PIKA_CONTEXT_PROP_BRUSH: pika_context_set_brush (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_DYNAMICS: pika_context_set_dynamics (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_MYBRUSH: pika_context_set_mybrush (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_PATTERN: pika_context_set_pattern (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_GRADIENT: pika_context_set_gradient (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_PALETTE: pika_context_set_palette (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_FONT: pika_context_set_font (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_TOOL_PRESET: pika_context_set_tool_preset (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_BUFFER: pika_context_set_buffer (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_IMAGEFILE: pika_context_set_imagefile (context, g_value_get_object (value)); break; case PIKA_CONTEXT_PROP_TEMPLATE: pika_context_set_template (context, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_context_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaContext *context = PIKA_CONTEXT (object); switch (property_id) { case PIKA_CONTEXT_PROP_PIKA: g_value_set_object (value, context->pika); break; case PIKA_CONTEXT_PROP_IMAGE: g_value_set_object (value, pika_context_get_image (context)); break; case PIKA_CONTEXT_PROP_DISPLAY: g_value_set_object (value, pika_context_get_display (context)); break; case PIKA_CONTEXT_PROP_TOOL: g_value_set_object (value, pika_context_get_tool (context)); break; case PIKA_CONTEXT_PROP_PAINT_INFO: g_value_set_object (value, pika_context_get_paint_info (context)); break; case PIKA_CONTEXT_PROP_FOREGROUND: { PikaRGB color; pika_context_get_foreground (context, &color); g_value_set_boxed (value, &color); } break; case PIKA_CONTEXT_PROP_BACKGROUND: { PikaRGB color; pika_context_get_background (context, &color); g_value_set_boxed (value, &color); } break; case PIKA_CONTEXT_PROP_OPACITY: g_value_set_double (value, pika_context_get_opacity (context)); break; case PIKA_CONTEXT_PROP_PAINT_MODE: g_value_set_enum (value, pika_context_get_paint_mode (context)); break; case PIKA_CONTEXT_PROP_BRUSH: g_value_set_object (value, pika_context_get_brush (context)); break; case PIKA_CONTEXT_PROP_DYNAMICS: g_value_set_object (value, pika_context_get_dynamics (context)); break; case PIKA_CONTEXT_PROP_MYBRUSH: g_value_set_object (value, pika_context_get_mybrush (context)); break; case PIKA_CONTEXT_PROP_PATTERN: g_value_set_object (value, pika_context_get_pattern (context)); break; case PIKA_CONTEXT_PROP_GRADIENT: g_value_set_object (value, pika_context_get_gradient (context)); break; case PIKA_CONTEXT_PROP_PALETTE: g_value_set_object (value, pika_context_get_palette (context)); break; case PIKA_CONTEXT_PROP_FONT: g_value_set_object (value, pika_context_get_font (context)); break; case PIKA_CONTEXT_PROP_TOOL_PRESET: g_value_set_object (value, pika_context_get_tool_preset (context)); break; case PIKA_CONTEXT_PROP_BUFFER: g_value_set_object (value, pika_context_get_buffer (context)); break; case PIKA_CONTEXT_PROP_IMAGEFILE: g_value_set_object (value, pika_context_get_imagefile (context)); break; case PIKA_CONTEXT_PROP_TEMPLATE: g_value_set_object (value, pika_context_get_template (context)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gint64 pika_context_get_memsize (PikaObject *object, gint64 *gui_size) { PikaContext *context = PIKA_CONTEXT (object); gint64 memsize = 0; memsize += pika_string_get_memsize (context->tool_name); memsize += pika_string_get_memsize (context->paint_name); memsize += pika_string_get_memsize (context->brush_name); memsize += pika_string_get_memsize (context->dynamics_name); memsize += pika_string_get_memsize (context->mybrush_name); memsize += pika_string_get_memsize (context->pattern_name); memsize += pika_string_get_memsize (context->palette_name); memsize += pika_string_get_memsize (context->font_name); memsize += pika_string_get_memsize (context->tool_preset_name); memsize += pika_string_get_memsize (context->buffer_name); memsize += pika_string_get_memsize (context->imagefile_name); memsize += pika_string_get_memsize (context->template_name); return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size); } static gboolean pika_context_serialize (PikaConfig *config, PikaConfigWriter *writer, gpointer data) { return pika_config_serialize_changed_properties (config, writer); } static gboolean pika_context_deserialize (PikaConfig *config, GScanner *scanner, gint nest_level, gpointer data) { PikaContext *context = PIKA_CONTEXT (config); PikaLayerMode old_paint_mode = context->paint_mode; gboolean success; success = pika_config_deserialize_properties (config, scanner, nest_level); if (context->paint_mode != old_paint_mode) { if (context->paint_mode == PIKA_LAYER_MODE_OVERLAY_LEGACY) g_object_set (context, "paint-mode", PIKA_LAYER_MODE_SOFTLIGHT_LEGACY, NULL); } return success; } static gboolean pika_context_serialize_property (PikaConfig *config, guint property_id, const GValue *value, GParamSpec *pspec, PikaConfigWriter *writer) { PikaContext *context = PIKA_CONTEXT (config); PikaObject *serialize_obj; /* serialize nothing if the property is not in serialize_props */ if (! ((1 << property_id) & context->serialize_props)) return TRUE; switch (property_id) { case PIKA_CONTEXT_PROP_TOOL: case PIKA_CONTEXT_PROP_PAINT_INFO: case PIKA_CONTEXT_PROP_BRUSH: case PIKA_CONTEXT_PROP_DYNAMICS: case PIKA_CONTEXT_PROP_MYBRUSH: case PIKA_CONTEXT_PROP_PATTERN: case PIKA_CONTEXT_PROP_GRADIENT: case PIKA_CONTEXT_PROP_PALETTE: case PIKA_CONTEXT_PROP_FONT: case PIKA_CONTEXT_PROP_TOOL_PRESET: serialize_obj = g_value_get_object (value); break; default: return FALSE; } pika_config_writer_open (writer, pspec->name); if (serialize_obj) pika_config_writer_string (writer, pika_object_get_name (serialize_obj)); else pika_config_writer_print (writer, "NULL", 4); pika_config_writer_close (writer); return TRUE; } static gboolean pika_context_deserialize_property (PikaConfig *object, guint property_id, GValue *value, GParamSpec *pspec, GScanner *scanner, GTokenType *expected) { PikaContext *context = PIKA_CONTEXT (object); PikaContainer *container; gpointer standard; gchar **name_loc; gchar *object_name; switch (property_id) { case PIKA_CONTEXT_PROP_TOOL: container = context->pika->tool_info_list; standard = pika_tool_info_get_standard (context->pika); name_loc = &context->tool_name; break; case PIKA_CONTEXT_PROP_PAINT_INFO: container = context->pika->paint_info_list; standard = pika_paint_info_get_standard (context->pika); name_loc = &context->paint_name; break; case PIKA_CONTEXT_PROP_BRUSH: container = pika_data_factory_get_container (context->pika->brush_factory); standard = pika_brush_get_standard (context); name_loc = &context->brush_name; break; case PIKA_CONTEXT_PROP_DYNAMICS: container = pika_data_factory_get_container (context->pika->dynamics_factory); standard = pika_dynamics_get_standard (context); name_loc = &context->dynamics_name; break; case PIKA_CONTEXT_PROP_MYBRUSH: container = pika_data_factory_get_container (context->pika->mybrush_factory); standard = pika_mybrush_get_standard (context); name_loc = &context->mybrush_name; break; case PIKA_CONTEXT_PROP_PATTERN: container = pika_data_factory_get_container (context->pika->pattern_factory); standard = pika_pattern_get_standard (context); name_loc = &context->pattern_name; break; case PIKA_CONTEXT_PROP_GRADIENT: container = pika_data_factory_get_container (context->pika->gradient_factory); standard = pika_gradient_get_standard (context); name_loc = &context->gradient_name; break; case PIKA_CONTEXT_PROP_PALETTE: container = pika_data_factory_get_container (context->pika->palette_factory); standard = pika_palette_get_standard (context); name_loc = &context->palette_name; break; case PIKA_CONTEXT_PROP_FONT: container = pika_data_factory_get_container (context->pika->font_factory); standard = pika_font_get_standard (); name_loc = &context->font_name; break; case PIKA_CONTEXT_PROP_TOOL_PRESET: container = pika_data_factory_get_container (context->pika->tool_preset_factory); standard = NULL; name_loc = &context->tool_preset_name; break; default: return FALSE; } if (pika_scanner_parse_identifier (scanner, "NULL")) { g_value_set_object (value, NULL); } else if (pika_scanner_parse_string (scanner, &object_name)) { PikaObject *deserialize_obj; if (! object_name) object_name = g_strdup (""); deserialize_obj = pika_container_get_child_by_name (container, object_name); if (! deserialize_obj) { g_value_set_object (value, standard); g_free (*name_loc); *name_loc = g_strdup (object_name); } else { g_value_set_object (value, deserialize_obj); } g_free (object_name); } else { *expected = G_TOKEN_STRING; } return TRUE; } static PikaConfig * pika_context_duplicate (PikaConfig *config) { PikaContext *context = PIKA_CONTEXT (config); PikaContext *new; new = PIKA_CONTEXT (parent_config_iface->duplicate (config)); COPY_NAME (context, new, tool_name); COPY_NAME (context, new, paint_name); COPY_NAME (context, new, brush_name); COPY_NAME (context, new, dynamics_name); COPY_NAME (context, new, mybrush_name); COPY_NAME (context, new, pattern_name); COPY_NAME (context, new, gradient_name); COPY_NAME (context, new, palette_name); COPY_NAME (context, new, font_name); COPY_NAME (context, new, tool_preset_name); COPY_NAME (context, new, buffer_name); COPY_NAME (context, new, imagefile_name); COPY_NAME (context, new, template_name); return PIKA_CONFIG (new); } static gboolean pika_context_copy (PikaConfig *src, PikaConfig *dest, GParamFlags flags) { PikaContext *src_context = PIKA_CONTEXT (src); PikaContext *dest_context = PIKA_CONTEXT (dest); gboolean success = parent_config_iface->copy (src, dest, flags); COPY_NAME (src_context, dest_context, tool_name); COPY_NAME (src_context, dest_context, paint_name); COPY_NAME (src_context, dest_context, brush_name); COPY_NAME (src_context, dest_context, dynamics_name); COPY_NAME (src_context, dest_context, mybrush_name); COPY_NAME (src_context, dest_context, pattern_name); COPY_NAME (src_context, dest_context, gradient_name); COPY_NAME (src_context, dest_context, palette_name); COPY_NAME (src_context, dest_context, font_name); COPY_NAME (src_context, dest_context, tool_preset_name); COPY_NAME (src_context, dest_context, buffer_name); COPY_NAME (src_context, dest_context, imagefile_name); COPY_NAME (src_context, dest_context, template_name); return success; } /*****************************************************************************/ /* public functions ********************************************************/ PikaContext * pika_context_new (Pika *pika, const gchar *name, PikaContext *template) { PikaContext *context; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (template == NULL || PIKA_IS_CONTEXT (template), NULL); context = g_object_new (PIKA_TYPE_CONTEXT, "name", name, "pika", pika, NULL); if (template) { context->defined_props = template->defined_props; pika_context_copy_properties (template, context, PIKA_CONTEXT_PROP_MASK_ALL); } return context; } PikaContext * pika_context_get_parent (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->parent; } static void pika_context_parent_notify (PikaContext *parent, GParamSpec *pspec, PikaContext *context) { if (pspec->owner_type == PIKA_TYPE_CONTEXT) { PikaContextPropType prop = pspec->param_id; /* copy from parent if the changed property is undefined; * ignore properties that are not context properties, for * example notifications on the context's "pika" property */ if ((prop >= PIKA_CONTEXT_PROP_FIRST) && (prop <= PIKA_CONTEXT_PROP_LAST) && ! ((1 << prop) & context->defined_props)) { pika_context_copy_property (parent, context, prop); } } } void pika_context_set_parent (PikaContext *context, PikaContext *parent) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (parent == NULL || PIKA_IS_CONTEXT (parent)); g_return_if_fail (parent == NULL || parent->parent != context); g_return_if_fail (context != parent); if (context->parent == parent) return; if (context->parent) { g_signal_handlers_disconnect_by_func (context->parent, pika_context_parent_notify, context); } g_set_weak_pointer (&context->parent, parent); if (parent) { /* copy all undefined properties from the new parent */ pika_context_copy_properties (parent, context, ~context->defined_props & PIKA_CONTEXT_PROP_MASK_ALL); g_signal_connect_object (parent, "notify", G_CALLBACK (pika_context_parent_notify), context, 0); } } /* define / undefinine context properties */ void pika_context_define_property (PikaContext *context, PikaContextPropType prop, gboolean defined) { PikaContextPropMask mask; g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail ((prop >= PIKA_CONTEXT_PROP_FIRST) && (prop <= PIKA_CONTEXT_PROP_LAST)); mask = (1 << prop); if (defined) { if (! (context->defined_props & mask)) { context->defined_props |= mask; } } else { if (context->defined_props & mask) { context->defined_props &= ~mask; if (context->parent) pika_context_copy_property (context->parent, context, prop); } } } gboolean pika_context_property_defined (PikaContext *context, PikaContextPropType prop) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), FALSE); return (context->defined_props & (1 << prop)) ? TRUE : FALSE; } void pika_context_define_properties (PikaContext *context, PikaContextPropMask prop_mask, gboolean defined) { PikaContextPropType prop; g_return_if_fail (PIKA_IS_CONTEXT (context)); for (prop = PIKA_CONTEXT_PROP_FIRST; prop <= PIKA_CONTEXT_PROP_LAST; prop++) if ((1 << prop) & prop_mask) pika_context_define_property (context, prop, defined); } /* specify which context properties will be serialized */ void pika_context_set_serialize_properties (PikaContext *context, PikaContextPropMask props_mask) { g_return_if_fail (PIKA_IS_CONTEXT (context)); context->serialize_props = props_mask; } PikaContextPropMask pika_context_get_serialize_properties (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), 0); return context->serialize_props; } /* copying context properties */ void pika_context_copy_property (PikaContext *src, PikaContext *dest, PikaContextPropType prop) { g_return_if_fail (PIKA_IS_CONTEXT (src)); g_return_if_fail (PIKA_IS_CONTEXT (dest)); g_return_if_fail ((prop >= PIKA_CONTEXT_PROP_FIRST) && (prop <= PIKA_CONTEXT_PROP_LAST)); switch (prop) { case PIKA_CONTEXT_PROP_IMAGE: pika_context_real_set_image (dest, src->image); break; case PIKA_CONTEXT_PROP_DISPLAY: pika_context_real_set_display (dest, src->display); break; case PIKA_CONTEXT_PROP_TOOL: pika_context_real_set_tool (dest, src->tool_info); COPY_NAME (src, dest, tool_name); break; case PIKA_CONTEXT_PROP_PAINT_INFO: pika_context_real_set_paint_info (dest, src->paint_info); COPY_NAME (src, dest, paint_name); break; case PIKA_CONTEXT_PROP_FOREGROUND: pika_context_real_set_foreground (dest, &src->foreground); break; case PIKA_CONTEXT_PROP_BACKGROUND: pika_context_real_set_background (dest, &src->background); break; case PIKA_CONTEXT_PROP_OPACITY: pika_context_real_set_opacity (dest, src->opacity); break; case PIKA_CONTEXT_PROP_PAINT_MODE: pika_context_real_set_paint_mode (dest, src->paint_mode); break; case PIKA_CONTEXT_PROP_BRUSH: pika_context_real_set_brush (dest, src->brush); COPY_NAME (src, dest, brush_name); break; case PIKA_CONTEXT_PROP_DYNAMICS: pika_context_real_set_dynamics (dest, src->dynamics); COPY_NAME (src, dest, dynamics_name); break; case PIKA_CONTEXT_PROP_MYBRUSH: pika_context_real_set_mybrush (dest, src->mybrush); COPY_NAME (src, dest, mybrush_name); break; case PIKA_CONTEXT_PROP_PATTERN: pika_context_real_set_pattern (dest, src->pattern); COPY_NAME (src, dest, pattern_name); break; case PIKA_CONTEXT_PROP_GRADIENT: pika_context_real_set_gradient (dest, src->gradient); COPY_NAME (src, dest, gradient_name); break; case PIKA_CONTEXT_PROP_PALETTE: pika_context_real_set_palette (dest, src->palette); COPY_NAME (src, dest, palette_name); break; case PIKA_CONTEXT_PROP_FONT: pika_context_real_set_font (dest, src->font); COPY_NAME (src, dest, font_name); break; case PIKA_CONTEXT_PROP_TOOL_PRESET: pika_context_real_set_tool_preset (dest, src->tool_preset); COPY_NAME (src, dest, tool_preset_name); break; case PIKA_CONTEXT_PROP_BUFFER: pika_context_real_set_buffer (dest, src->buffer); COPY_NAME (src, dest, buffer_name); break; case PIKA_CONTEXT_PROP_IMAGEFILE: pika_context_real_set_imagefile (dest, src->imagefile); COPY_NAME (src, dest, imagefile_name); break; case PIKA_CONTEXT_PROP_TEMPLATE: pika_context_real_set_template (dest, src->template); COPY_NAME (src, dest, template_name); break; default: break; } } void pika_context_copy_properties (PikaContext *src, PikaContext *dest, PikaContextPropMask prop_mask) { PikaContextPropType prop; g_return_if_fail (PIKA_IS_CONTEXT (src)); g_return_if_fail (PIKA_IS_CONTEXT (dest)); for (prop = PIKA_CONTEXT_PROP_FIRST; prop <= PIKA_CONTEXT_PROP_LAST; prop++) if ((1 << prop) & prop_mask) pika_context_copy_property (src, dest, prop); } /* attribute access functions */ /*****************************************************************************/ /* manipulate by GType *****************************************************/ PikaContextPropType pika_context_type_to_property (GType type) { PikaContextPropType prop; for (prop = PIKA_CONTEXT_PROP_FIRST; prop <= PIKA_CONTEXT_PROP_LAST; prop++) { if (g_type_is_a (type, pika_context_prop_types[prop])) return prop; } return -1; } const gchar * pika_context_type_to_prop_name (GType type) { PikaContextPropType prop; for (prop = PIKA_CONTEXT_PROP_FIRST; prop <= PIKA_CONTEXT_PROP_LAST; prop++) { if (g_type_is_a (type, pika_context_prop_types[prop])) return pika_context_prop_names[prop]; } return NULL; } const gchar * pika_context_type_to_signal_name (GType type) { PikaContextPropType prop; for (prop = PIKA_CONTEXT_PROP_FIRST; prop <= PIKA_CONTEXT_PROP_LAST; prop++) { if (g_type_is_a (type, pika_context_prop_types[prop])) return g_signal_name (pika_context_signals[prop]); } return NULL; } PikaObject * pika_context_get_by_type (PikaContext *context, GType type) { PikaContextPropType prop; PikaObject *object = NULL; g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); prop = pika_context_type_to_property (type); g_return_val_if_fail (prop != -1, NULL); g_object_get (context, pika_context_prop_names[prop], &object, NULL); /* g_object_get() refs the object, this function however is a getter, * which usually doesn't ref it's return value */ if (object) g_object_unref (object); return object; } void pika_context_set_by_type (PikaContext *context, GType type, PikaObject *object) { PikaContextPropType prop; GParamSpec *pspec; GValue value = G_VALUE_INIT; g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (object == NULL || G_IS_OBJECT (object)); prop = pika_context_type_to_property (type); g_return_if_fail (prop != -1); pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (context), pika_context_prop_names[prop]); g_return_if_fail (pspec != NULL); g_value_init (&value, pspec->value_type); g_value_set_object (&value, object); /* we use pika_context_set_property() (which in turn only calls * pika_context_set_foo() functions) instead of the much more obvious * g_object_set(); this avoids g_object_freeze_notify()/thaw_notify() * around the g_object_set() and makes PikaContext callbacks being * called in a much more predictable order. See bug #731279. */ pika_context_set_property (G_OBJECT (context), pspec->param_id, (const GValue *) &value, pspec); g_value_unset (&value); } void pika_context_changed_by_type (PikaContext *context, GType type) { PikaContextPropType prop; PikaObject *object; g_return_if_fail (PIKA_IS_CONTEXT (context)); prop = pika_context_type_to_property (type); g_return_if_fail (prop != -1); object = pika_context_get_by_type (context, type); g_signal_emit (context, pika_context_signals[prop], 0, object); } /*****************************************************************************/ /* image *******************************************************************/ PikaImage * pika_context_get_image (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->image; } void pika_context_set_image (PikaContext *context, PikaImage *image) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (image == NULL || PIKA_IS_IMAGE (image)); context_find_defined (context, PIKA_CONTEXT_PROP_IMAGE); pika_context_real_set_image (context, image); } void pika_context_image_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[IMAGE_CHANGED], 0, context->image); } static void pika_context_image_removed (PikaContainer *container, PikaImage *image, PikaContext *context) { if (context->image == image) pika_context_real_set_image (context, NULL); } static void pika_context_real_set_image (PikaContext *context, PikaImage *image) { if (context->image == image) return; context->image = image; g_object_notify (G_OBJECT (context), "image"); pika_context_image_changed (context); } /*****************************************************************************/ /* display *****************************************************************/ PikaDisplay * pika_context_get_display (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->display; } void pika_context_set_display (PikaContext *context, PikaDisplay *display) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (display == NULL || PIKA_IS_DISPLAY (display)); context_find_defined (context, PIKA_CONTEXT_PROP_DISPLAY); pika_context_real_set_display (context, display); } void pika_context_display_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[DISPLAY_CHANGED], 0, context->display); } static void pika_context_display_removed (PikaContainer *container, PikaDisplay *display, PikaContext *context) { if (context->display == display) pika_context_real_set_display (context, NULL); } static void pika_context_real_set_display (PikaContext *context, PikaDisplay *display) { PikaDisplay *old_display; if (context->display == display) { /* make sure that setting a display *always* sets the image * to that display's image, even if the display already * matches */ if (display) { PikaImage *image; g_object_get (display, "image", &image, NULL); pika_context_real_set_image (context, image); if (image) g_object_unref (image); } return; } old_display = context->display; context->display = display; if (context->display) { PikaImage *image; g_object_get (display, "image", &image, NULL); pika_context_real_set_image (context, image); if (image) g_object_unref (image); } else if (old_display) { pika_context_real_set_image (context, NULL); } g_object_notify (G_OBJECT (context), "display"); pika_context_display_changed (context); } /*****************************************************************************/ /* tool ********************************************************************/ PikaToolInfo * pika_context_get_tool (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->tool_info; } void pika_context_set_tool (PikaContext *context, PikaToolInfo *tool_info) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (tool_info == NULL || PIKA_IS_TOOL_INFO (tool_info)); context_find_defined (context, PIKA_CONTEXT_PROP_TOOL); pika_context_real_set_tool (context, tool_info); } void pika_context_tool_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[TOOL_CHANGED], 0, context->tool_info); } static void pika_context_tool_dirty (PikaToolInfo *tool_info, PikaContext *context) { g_free (context->tool_name); context->tool_name = g_strdup (pika_object_get_name (tool_info)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_TOOL); } static void pika_context_tool_list_thaw (PikaContainer *container, PikaContext *context) { PikaToolInfo *tool_info; if (! context->tool_name) context->tool_name = g_strdup ("pika-paintbrush-tool"); tool_info = pika_context_find_object (context, container, context->tool_name, pika_tool_info_get_standard (context->pika)); pika_context_real_set_tool (context, tool_info); } static void pika_context_tool_removed (PikaContainer *container, PikaToolInfo *tool_info, PikaContext *context) { if (tool_info == context->tool_info) { g_signal_handlers_disconnect_by_func (context->tool_info, pika_context_tool_dirty, context); g_clear_object (&context->tool_info); if (! pika_container_frozen (container)) pika_context_tool_list_thaw (container, context); } } static void pika_context_real_set_tool (PikaContext *context, PikaToolInfo *tool_info) { if (context->tool_info == tool_info) return; if (context->tool_name && tool_info != pika_tool_info_get_standard (context->pika)) { g_clear_pointer (&context->tool_name, g_free); } if (context->tool_info) g_signal_handlers_disconnect_by_func (context->tool_info, pika_context_tool_dirty, context); g_set_object (&context->tool_info, tool_info); if (tool_info) { g_signal_connect_object (tool_info, "name-changed", G_CALLBACK (pika_context_tool_dirty), context, 0); if (tool_info != pika_tool_info_get_standard (context->pika)) context->tool_name = g_strdup (pika_object_get_name (tool_info)); if (tool_info->paint_info) pika_context_real_set_paint_info (context, tool_info->paint_info); } g_object_notify (G_OBJECT (context), "tool"); pika_context_tool_changed (context); } /*****************************************************************************/ /* paint info **************************************************************/ PikaPaintInfo * pika_context_get_paint_info (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->paint_info; } void pika_context_set_paint_info (PikaContext *context, PikaPaintInfo *paint_info) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (paint_info == NULL || PIKA_IS_PAINT_INFO (paint_info)); context_find_defined (context, PIKA_CONTEXT_PROP_PAINT_INFO); pika_context_real_set_paint_info (context, paint_info); } void pika_context_paint_info_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[PAINT_INFO_CHANGED], 0, context->paint_info); } static void pika_context_paint_info_dirty (PikaPaintInfo *paint_info, PikaContext *context) { g_free (context->paint_name); context->paint_name = g_strdup (pika_object_get_name (paint_info)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_PAINT_INFO); } /* the global paint info list is there again after refresh */ static void pika_context_paint_info_list_thaw (PikaContainer *container, PikaContext *context) { PikaPaintInfo *paint_info; if (! context->paint_name) context->paint_name = g_strdup ("pika-paintbrush"); paint_info = pika_context_find_object (context, container, context->paint_name, pika_paint_info_get_standard (context->pika)); pika_context_real_set_paint_info (context, paint_info); } static void pika_context_paint_info_removed (PikaContainer *container, PikaPaintInfo *paint_info, PikaContext *context) { if (paint_info == context->paint_info) { g_signal_handlers_disconnect_by_func (context->paint_info, pika_context_paint_info_dirty, context); g_clear_object (&context->paint_info); if (! pika_container_frozen (container)) pika_context_paint_info_list_thaw (container, context); } } static void pika_context_real_set_paint_info (PikaContext *context, PikaPaintInfo *paint_info) { if (context->paint_info == paint_info) return; if (context->paint_name && paint_info != pika_paint_info_get_standard (context->pika)) { g_clear_pointer (&context->paint_name, g_free); } if (context->paint_info) g_signal_handlers_disconnect_by_func (context->paint_info, pika_context_paint_info_dirty, context); g_set_object (&context->paint_info, paint_info); if (paint_info) { g_signal_connect_object (paint_info, "name-changed", G_CALLBACK (pika_context_paint_info_dirty), context, 0); if (paint_info != pika_paint_info_get_standard (context->pika)) context->paint_name = g_strdup (pika_object_get_name (paint_info)); } g_object_notify (G_OBJECT (context), "paint-info"); pika_context_paint_info_changed (context); } /*****************************************************************************/ /* foreground color ********************************************************/ void pika_context_get_foreground (PikaContext *context, PikaRGB *color) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (color != NULL); *color = context->foreground; } void pika_context_set_foreground (PikaContext *context, const PikaRGB *color) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (color != NULL); context_find_defined (context, PIKA_CONTEXT_PROP_FOREGROUND); pika_context_real_set_foreground (context, color); } void pika_context_foreground_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[FOREGROUND_CHANGED], 0, &context->foreground); } static void pika_context_real_set_foreground (PikaContext *context, const PikaRGB *color) { if (pika_rgba_distance (&context->foreground, color) < RGBA_EPSILON) return; context->foreground = *color; pika_rgb_set_alpha (&context->foreground, PIKA_OPACITY_OPAQUE); g_object_notify (G_OBJECT (context), "foreground"); pika_context_foreground_changed (context); } /*****************************************************************************/ /* background color ********************************************************/ void pika_context_get_background (PikaContext *context, PikaRGB *color) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (color != NULL); *color = context->background; } void pika_context_set_background (PikaContext *context, const PikaRGB *color) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (color != NULL); context_find_defined (context, PIKA_CONTEXT_PROP_BACKGROUND); pika_context_real_set_background (context, color); } void pika_context_background_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[BACKGROUND_CHANGED], 0, &context->background); } static void pika_context_real_set_background (PikaContext *context, const PikaRGB *color) { if (pika_rgba_distance (&context->background, color) < RGBA_EPSILON) return; context->background = *color; pika_rgb_set_alpha (&context->background, PIKA_OPACITY_OPAQUE); g_object_notify (G_OBJECT (context), "background"); pika_context_background_changed (context); } /*****************************************************************************/ /* color utility functions *************************************************/ void pika_context_set_default_colors (PikaContext *context) { PikaContext *bg_context; PikaRGB fg; PikaRGB bg; g_return_if_fail (PIKA_IS_CONTEXT (context)); bg_context = context; context_find_defined (context, PIKA_CONTEXT_PROP_FOREGROUND); context_find_defined (bg_context, PIKA_CONTEXT_PROP_BACKGROUND); pika_rgba_set (&fg, 0.0, 0.0, 0.0, PIKA_OPACITY_OPAQUE); pika_rgba_set (&bg, 1.0, 1.0, 1.0, PIKA_OPACITY_OPAQUE); pika_context_real_set_foreground (context, &fg); pika_context_real_set_background (bg_context, &bg); } void pika_context_swap_colors (PikaContext *context) { PikaContext *bg_context; PikaRGB fg; PikaRGB bg; g_return_if_fail (PIKA_IS_CONTEXT (context)); bg_context = context; context_find_defined (context, PIKA_CONTEXT_PROP_FOREGROUND); context_find_defined (bg_context, PIKA_CONTEXT_PROP_BACKGROUND); pika_context_get_foreground (context, &fg); pika_context_get_background (bg_context, &bg); pika_context_real_set_foreground (context, &bg); pika_context_real_set_background (bg_context, &fg); } /*****************************************************************************/ /* opacity *****************************************************************/ gdouble pika_context_get_opacity (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), PIKA_OPACITY_OPAQUE); return context->opacity; } void pika_context_set_opacity (PikaContext *context, gdouble opacity) { g_return_if_fail (PIKA_IS_CONTEXT (context)); context_find_defined (context, PIKA_CONTEXT_PROP_OPACITY); pika_context_real_set_opacity (context, opacity); } void pika_context_opacity_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[OPACITY_CHANGED], 0, context->opacity); } static void pika_context_real_set_opacity (PikaContext *context, gdouble opacity) { if (context->opacity == opacity) return; context->opacity = opacity; g_object_notify (G_OBJECT (context), "opacity"); pika_context_opacity_changed (context); } /*****************************************************************************/ /* paint mode **************************************************************/ PikaLayerMode pika_context_get_paint_mode (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), PIKA_LAYER_MODE_NORMAL); return context->paint_mode; } void pika_context_set_paint_mode (PikaContext *context, PikaLayerMode paint_mode) { g_return_if_fail (PIKA_IS_CONTEXT (context)); context_find_defined (context, PIKA_CONTEXT_PROP_PAINT_MODE); pika_context_real_set_paint_mode (context, paint_mode); } void pika_context_paint_mode_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[PAINT_MODE_CHANGED], 0, context->paint_mode); } static void pika_context_real_set_paint_mode (PikaContext *context, PikaLayerMode paint_mode) { if (context->paint_mode == paint_mode) return; context->paint_mode = paint_mode; g_object_notify (G_OBJECT (context), "paint-mode"); pika_context_paint_mode_changed (context); } /*****************************************************************************/ /* brush *******************************************************************/ PikaBrush * pika_context_get_brush (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->brush; } void pika_context_set_brush (PikaContext *context, PikaBrush *brush) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (brush == NULL || PIKA_IS_BRUSH (brush)); context_find_defined (context, PIKA_CONTEXT_PROP_BRUSH); pika_context_real_set_brush (context, brush); } void pika_context_brush_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[BRUSH_CHANGED], 0, context->brush); } static void pika_context_brush_dirty (PikaBrush *brush, PikaContext *context) { g_free (context->brush_name); context->brush_name = g_strdup (pika_object_get_name (brush)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_BRUSH); } static void pika_context_brush_list_thaw (PikaContainer *container, PikaContext *context) { PikaBrush *brush; if (! context->brush_name) context->brush_name = g_strdup (context->pika->config->default_brush); brush = pika_context_find_object (context, container, context->brush_name, pika_brush_get_standard (context)); pika_context_real_set_brush (context, brush); } /* the active brush disappeared */ static void pika_context_brush_removed (PikaContainer *container, PikaBrush *brush, PikaContext *context) { if (brush == context->brush) { g_signal_handlers_disconnect_by_func (context->brush, pika_context_brush_dirty, context); g_clear_object (&context->brush); if (! pika_container_frozen (container)) pika_context_brush_list_thaw (container, context); } } static void pika_context_real_set_brush (PikaContext *context, PikaBrush *brush) { if (context->brush == brush) return; if (context->brush_name && brush != PIKA_BRUSH (pika_brush_get_standard (context))) { g_clear_pointer (&context->brush_name, g_free); } if (context->brush) g_signal_handlers_disconnect_by_func (context->brush, pika_context_brush_dirty, context); g_set_object (&context->brush, brush); if (brush) { g_signal_connect_object (brush, "name-changed", G_CALLBACK (pika_context_brush_dirty), context, 0); if (brush != PIKA_BRUSH (pika_brush_get_standard (context))) context->brush_name = g_strdup (pika_object_get_name (brush)); } g_object_notify (G_OBJECT (context), "brush"); pika_context_brush_changed (context); } /*****************************************************************************/ /* dynamics *****************************************************************/ PikaDynamics * pika_context_get_dynamics (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->dynamics; } void pika_context_set_dynamics (PikaContext *context, PikaDynamics *dynamics) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (dynamics == NULL || PIKA_IS_DYNAMICS (dynamics)); context_find_defined (context, PIKA_CONTEXT_PROP_DYNAMICS); pika_context_real_set_dynamics (context, dynamics); } void pika_context_dynamics_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[DYNAMICS_CHANGED], 0, context->dynamics); } static void pika_context_dynamics_dirty (PikaDynamics *dynamics, PikaContext *context) { g_free (context->dynamics_name); context->dynamics_name = g_strdup (pika_object_get_name (dynamics)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_DYNAMICS); } static void pika_context_dynamics_removed (PikaContainer *container, PikaDynamics *dynamics, PikaContext *context) { if (dynamics == context->dynamics) { g_signal_handlers_disconnect_by_func (context->dynamics, pika_context_dynamics_dirty, context); g_clear_object (&context->dynamics); if (! pika_container_frozen (container)) pika_context_dynamics_list_thaw (container, context); } } static void pika_context_dynamics_list_thaw (PikaContainer *container, PikaContext *context) { PikaDynamics *dynamics; if (! context->dynamics_name) context->dynamics_name = g_strdup (context->pika->config->default_dynamics); dynamics = pika_context_find_object (context, container, context->dynamics_name, pika_dynamics_get_standard (context)); pika_context_real_set_dynamics (context, dynamics); } static void pika_context_real_set_dynamics (PikaContext *context, PikaDynamics *dynamics) { if (context->dynamics == dynamics) return; if (context->dynamics_name && dynamics != PIKA_DYNAMICS (pika_dynamics_get_standard (context))) { g_clear_pointer (&context->dynamics_name, g_free); } if (context->dynamics) g_signal_handlers_disconnect_by_func (context->dynamics, pika_context_dynamics_dirty, context); g_set_object (&context->dynamics, dynamics); if (dynamics) { g_signal_connect_object (dynamics, "name-changed", G_CALLBACK (pika_context_dynamics_dirty), context, 0); if (dynamics != PIKA_DYNAMICS (pika_dynamics_get_standard (context))) context->dynamics_name = g_strdup (pika_object_get_name (dynamics)); } g_object_notify (G_OBJECT (context), "dynamics"); pika_context_dynamics_changed (context); } /*****************************************************************************/ /* mybrush *****************************************************************/ PikaMybrush * pika_context_get_mybrush (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->mybrush; } void pika_context_set_mybrush (PikaContext *context, PikaMybrush *brush) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (brush == NULL || PIKA_IS_MYBRUSH (brush)); context_find_defined (context, PIKA_CONTEXT_PROP_MYBRUSH); pika_context_real_set_mybrush (context, brush); } void pika_context_mybrush_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[MYBRUSH_CHANGED], 0, context->mybrush); } static void pika_context_mybrush_dirty (PikaMybrush *brush, PikaContext *context) { g_free (context->mybrush_name); context->mybrush_name = g_strdup (pika_object_get_name (brush)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_MYBRUSH); } static void pika_context_mybrush_list_thaw (PikaContainer *container, PikaContext *context) { PikaMybrush *brush; if (! context->mybrush_name) context->mybrush_name = g_strdup (context->pika->config->default_mypaint_brush); brush = pika_context_find_object (context, container, context->mybrush_name, pika_mybrush_get_standard (context)); pika_context_real_set_mybrush (context, brush); } static void pika_context_mybrush_removed (PikaContainer *container, PikaMybrush *brush, PikaContext *context) { if (brush == context->mybrush) { g_signal_handlers_disconnect_by_func (context->mybrush, pika_context_mybrush_dirty, context); g_clear_object (&context->mybrush); if (! pika_container_frozen (container)) pika_context_mybrush_list_thaw (container, context); } } static void pika_context_real_set_mybrush (PikaContext *context, PikaMybrush *brush) { if (context->mybrush == brush) return; if (context->mybrush_name && brush != PIKA_MYBRUSH (pika_mybrush_get_standard (context))) { g_clear_pointer (&context->mybrush_name, g_free); } if (context->mybrush) g_signal_handlers_disconnect_by_func (context->mybrush, pika_context_mybrush_dirty, context); g_set_object (&context->mybrush, brush); if (brush) { g_signal_connect_object (brush, "name-changed", G_CALLBACK (pika_context_mybrush_dirty), context, 0); if (brush != PIKA_MYBRUSH (pika_mybrush_get_standard (context))) context->mybrush_name = g_strdup (pika_object_get_name (brush)); } g_object_notify (G_OBJECT (context), "mybrush"); pika_context_mybrush_changed (context); } /*****************************************************************************/ /* pattern *****************************************************************/ PikaPattern * pika_context_get_pattern (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->pattern; } void pika_context_set_pattern (PikaContext *context, PikaPattern *pattern) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (pattern == NULL || PIKA_IS_PATTERN (pattern)); context_find_defined (context, PIKA_CONTEXT_PROP_PATTERN); pika_context_real_set_pattern (context, pattern); } void pika_context_pattern_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[PATTERN_CHANGED], 0, context->pattern); } static void pika_context_pattern_dirty (PikaPattern *pattern, PikaContext *context) { g_free (context->pattern_name); context->pattern_name = g_strdup (pika_object_get_name (pattern)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_PATTERN); } static void pika_context_pattern_list_thaw (PikaContainer *container, PikaContext *context) { PikaPattern *pattern; if (! context->pattern_name) context->pattern_name = g_strdup (context->pika->config->default_pattern); pattern = pika_context_find_object (context, container, context->pattern_name, pika_pattern_get_standard (context)); pika_context_real_set_pattern (context, pattern); } static void pika_context_pattern_removed (PikaContainer *container, PikaPattern *pattern, PikaContext *context) { if (pattern == context->pattern) { g_signal_handlers_disconnect_by_func (context->pattern, pika_context_pattern_dirty, context); g_clear_object (&context->pattern); if (! pika_container_frozen (container)) pika_context_pattern_list_thaw (container, context); } } static void pika_context_real_set_pattern (PikaContext *context, PikaPattern *pattern) { if (context->pattern == pattern) return; if (context->pattern_name && pattern != PIKA_PATTERN (pika_pattern_get_standard (context))) { g_clear_pointer (&context->pattern_name, g_free); } if (context->pattern) g_signal_handlers_disconnect_by_func (context->pattern, pika_context_pattern_dirty, context); g_set_object (&context->pattern, pattern); if (pattern) { g_signal_connect_object (pattern, "name-changed", G_CALLBACK (pika_context_pattern_dirty), context, 0); if (pattern != PIKA_PATTERN (pika_pattern_get_standard (context))) context->pattern_name = g_strdup (pika_object_get_name (pattern)); } g_object_notify (G_OBJECT (context), "pattern"); pika_context_pattern_changed (context); } /*****************************************************************************/ /* gradient ****************************************************************/ PikaGradient * pika_context_get_gradient (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->gradient; } void pika_context_set_gradient (PikaContext *context, PikaGradient *gradient) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (gradient == NULL || PIKA_IS_GRADIENT (gradient)); context_find_defined (context, PIKA_CONTEXT_PROP_GRADIENT); pika_context_real_set_gradient (context, gradient); } void pika_context_gradient_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[GRADIENT_CHANGED], 0, context->gradient); } static void pika_context_gradient_dirty (PikaGradient *gradient, PikaContext *context) { g_free (context->gradient_name); context->gradient_name = g_strdup (pika_object_get_name (gradient)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_GRADIENT); } static void pika_context_gradient_list_thaw (PikaContainer *container, PikaContext *context) { PikaGradient *gradient; if (! context->gradient_name) context->gradient_name = g_strdup (context->pika->config->default_gradient); gradient = pika_context_find_object (context, container, context->gradient_name, pika_gradient_get_standard (context)); pika_context_real_set_gradient (context, gradient); } static void pika_context_gradient_removed (PikaContainer *container, PikaGradient *gradient, PikaContext *context) { if (gradient == context->gradient) { g_signal_handlers_disconnect_by_func (context->gradient, pika_context_gradient_dirty, context); g_clear_object (&context->gradient); if (! pika_container_frozen (container)) pika_context_gradient_list_thaw (container, context); } } static void pika_context_real_set_gradient (PikaContext *context, PikaGradient *gradient) { if (context->gradient == gradient) return; if (context->gradient_name && gradient != PIKA_GRADIENT (pika_gradient_get_standard (context))) { g_clear_pointer (&context->gradient_name, g_free); } if (context->gradient) g_signal_handlers_disconnect_by_func (context->gradient, pika_context_gradient_dirty, context); g_set_object (&context->gradient, gradient); if (gradient) { g_signal_connect_object (gradient, "name-changed", G_CALLBACK (pika_context_gradient_dirty), context, 0); if (gradient != PIKA_GRADIENT (pika_gradient_get_standard (context))) context->gradient_name = g_strdup (pika_object_get_name (gradient)); } g_object_notify (G_OBJECT (context), "gradient"); pika_context_gradient_changed (context); } /*****************************************************************************/ /* palette *****************************************************************/ PikaPalette * pika_context_get_palette (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->palette; } void pika_context_set_palette (PikaContext *context, PikaPalette *palette) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (palette == NULL || PIKA_IS_PALETTE (palette)); context_find_defined (context, PIKA_CONTEXT_PROP_PALETTE); pika_context_real_set_palette (context, palette); } void pika_context_palette_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[PALETTE_CHANGED], 0, context->palette); } static void pika_context_palette_dirty (PikaPalette *palette, PikaContext *context) { g_free (context->palette_name); context->palette_name = g_strdup (pika_object_get_name (palette)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_PALETTE); } static void pika_context_palette_list_thaw (PikaContainer *container, PikaContext *context) { PikaPalette *palette; if (! context->palette_name) context->palette_name = g_strdup (context->pika->config->default_palette); palette = pika_context_find_object (context, container, context->palette_name, pika_palette_get_standard (context)); pika_context_real_set_palette (context, palette); } static void pika_context_palette_removed (PikaContainer *container, PikaPalette *palette, PikaContext *context) { if (palette == context->palette) { g_signal_handlers_disconnect_by_func (context->palette, pika_context_palette_dirty, context); g_clear_object (&context->palette); if (! pika_container_frozen (container)) pika_context_palette_list_thaw (container, context); } } static void pika_context_real_set_palette (PikaContext *context, PikaPalette *palette) { if (context->palette == palette) return; if (context->palette_name && palette != PIKA_PALETTE (pika_palette_get_standard (context))) { g_clear_pointer (&context->palette_name, g_free); } if (context->palette) g_signal_handlers_disconnect_by_func (context->palette, pika_context_palette_dirty, context); g_set_object (&context->palette, palette); if (palette) { g_signal_connect_object (palette, "name-changed", G_CALLBACK (pika_context_palette_dirty), context, 0); if (palette != PIKA_PALETTE (pika_palette_get_standard (context))) context->palette_name = g_strdup (pika_object_get_name (palette)); } g_object_notify (G_OBJECT (context), "palette"); pika_context_palette_changed (context); } /*****************************************************************************/ /* font *****************************************************************/ PikaFont * pika_context_get_font (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->font; } void pika_context_set_font (PikaContext *context, PikaFont *font) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (font == NULL || PIKA_IS_FONT (font)); context_find_defined (context, PIKA_CONTEXT_PROP_FONT); pika_context_real_set_font (context, font); } const gchar * pika_context_get_font_name (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->font_name; } void pika_context_set_font_name (PikaContext *context, const gchar *name) { PikaContainer *container; PikaObject *font; g_return_if_fail (PIKA_IS_CONTEXT (context)); container = pika_data_factory_get_container (context->pika->font_factory); font = pika_container_search (container, (PikaContainerSearchFunc) pika_font_match_by_lookup_name, (gpointer) name); if (font) { pika_context_set_font (context, PIKA_FONT (font)); } else { /* No font with this name exists, use the standard font, but * keep the intended name around */ pika_context_set_font (context, PIKA_FONT (pika_font_get_standard ())); g_free (context->font_name); context->font_name = g_strdup (name); } } void pika_context_font_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[FONT_CHANGED], 0, context->font); } static void pika_context_font_dirty (PikaFont *font, PikaContext *context) { g_free (context->font_name); context->font_name = g_strdup (pika_object_get_name (font)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_FONT); } static void pika_context_font_list_thaw (PikaContainer *container, PikaContext *context) { PikaFont *font; if (! context->font_name) context->font_name = g_strdup (context->pika->config->default_font); font = pika_context_find_object (context, container, context->font_name, pika_font_get_standard ()); pika_context_real_set_font (context, font); } static void pika_context_font_removed (PikaContainer *container, PikaFont *font, PikaContext *context) { if (font == context->font) { g_signal_handlers_disconnect_by_func (context->font, pika_context_font_dirty, context); g_clear_object (&context->font); if (! pika_container_frozen (container)) pika_context_font_list_thaw (container, context); } } static void pika_context_real_set_font (PikaContext *context, PikaFont *font) { if (context->font == font) return; if (context->font_name && font != PIKA_FONT (pika_font_get_standard ())) { g_clear_pointer (&context->font_name, g_free); } if (context->font) g_signal_handlers_disconnect_by_func (context->font, pika_context_font_dirty, context); g_set_object (&context->font, font); if (font) { g_signal_connect_object (font, "name-changed", G_CALLBACK (pika_context_font_dirty), context, 0); if (font != PIKA_FONT (pika_font_get_standard ())) context->font_name = g_strdup (pika_font_get_lookup_name (font)); } g_object_notify (G_OBJECT (context), "font"); pika_context_font_changed (context); } /********************************************************************************/ /* tool preset *****************************************************************/ PikaToolPreset * pika_context_get_tool_preset (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->tool_preset; } void pika_context_set_tool_preset (PikaContext *context, PikaToolPreset *tool_preset) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (tool_preset == NULL || PIKA_IS_TOOL_PRESET (tool_preset)); context_find_defined (context, PIKA_CONTEXT_PROP_TOOL_PRESET); pika_context_real_set_tool_preset (context, tool_preset); } void pika_context_tool_preset_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[TOOL_PRESET_CHANGED], 0, context->tool_preset); } static void pika_context_tool_preset_dirty (PikaToolPreset *tool_preset, PikaContext *context) { g_free (context->tool_preset_name); context->tool_preset_name = g_strdup (pika_object_get_name (tool_preset)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_TOOL_PRESET); } static void pika_context_tool_preset_removed (PikaContainer *container, PikaToolPreset *tool_preset, PikaContext *context) { if (tool_preset == context->tool_preset) { g_signal_handlers_disconnect_by_func (context->tool_preset, pika_context_tool_preset_dirty, context); g_clear_object (&context->tool_preset); if (! pika_container_frozen (container)) pika_context_tool_preset_list_thaw (container, context); } } static void pika_context_tool_preset_list_thaw (PikaContainer *container, PikaContext *context) { PikaToolPreset *tool_preset; tool_preset = pika_context_find_object (context, container, context->tool_preset_name, NULL); pika_context_real_set_tool_preset (context, tool_preset); } static void pika_context_real_set_tool_preset (PikaContext *context, PikaToolPreset *tool_preset) { if (context->tool_preset == tool_preset) return; if (context->tool_preset_name) { g_clear_pointer (&context->tool_preset_name, g_free); } if (context->tool_preset) g_signal_handlers_disconnect_by_func (context->tool_preset, pika_context_tool_preset_dirty, context); g_set_object (&context->tool_preset, tool_preset); if (tool_preset) { g_signal_connect_object (tool_preset, "name-changed", G_CALLBACK (pika_context_tool_preset_dirty), context, 0); context->tool_preset_name = g_strdup (pika_object_get_name (tool_preset)); } g_object_notify (G_OBJECT (context), "tool-preset"); pika_context_tool_preset_changed (context); } /*****************************************************************************/ /* buffer ******************************************************************/ PikaBuffer * pika_context_get_buffer (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->buffer; } void pika_context_set_buffer (PikaContext *context, PikaBuffer *buffer) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (buffer == NULL || PIKA_IS_BUFFER (buffer)); context_find_defined (context, PIKA_CONTEXT_PROP_BUFFER); pika_context_real_set_buffer (context, buffer); } void pika_context_buffer_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[BUFFER_CHANGED], 0, context->buffer); } static void pika_context_buffer_dirty (PikaBuffer *buffer, PikaContext *context) { g_free (context->buffer_name); context->buffer_name = g_strdup (pika_object_get_name (buffer)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_BUFFER); } static void pika_context_buffer_list_thaw (PikaContainer *container, PikaContext *context) { PikaBuffer *buffer; buffer = pika_context_find_object (context, container, context->buffer_name, NULL); if (buffer) { pika_context_real_set_buffer (context, buffer); } else { g_object_notify (G_OBJECT (context), "buffer"); pika_context_buffer_changed (context); } } static void pika_context_buffer_removed (PikaContainer *container, PikaBuffer *buffer, PikaContext *context) { if (buffer == context->buffer) { g_signal_handlers_disconnect_by_func (context->buffer, pika_context_buffer_dirty, context); g_clear_object (&context->buffer); if (! pika_container_frozen (container)) pika_context_buffer_list_thaw (container, context); } } static void pika_context_real_set_buffer (PikaContext *context, PikaBuffer *buffer) { if (context->buffer == buffer) return; if (context->buffer_name) { g_clear_pointer (&context->buffer_name, g_free); } if (context->buffer) g_signal_handlers_disconnect_by_func (context->buffer, pika_context_buffer_dirty, context); g_set_object (&context->buffer, buffer); if (buffer) { g_signal_connect_object (buffer, "name-changed", G_CALLBACK (pika_context_buffer_dirty), context, 0); context->buffer_name = g_strdup (pika_object_get_name (buffer)); } g_object_notify (G_OBJECT (context), "buffer"); pika_context_buffer_changed (context); } /*****************************************************************************/ /* imagefile ***************************************************************/ PikaImagefile * pika_context_get_imagefile (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->imagefile; } void pika_context_set_imagefile (PikaContext *context, PikaImagefile *imagefile) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (imagefile == NULL || PIKA_IS_IMAGEFILE (imagefile)); context_find_defined (context, PIKA_CONTEXT_PROP_IMAGEFILE); pika_context_real_set_imagefile (context, imagefile); } void pika_context_imagefile_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[IMAGEFILE_CHANGED], 0, context->imagefile); } static void pika_context_imagefile_dirty (PikaImagefile *imagefile, PikaContext *context) { g_free (context->imagefile_name); context->imagefile_name = g_strdup (pika_object_get_name (imagefile)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_IMAGEFILE); } static void pika_context_imagefile_list_thaw (PikaContainer *container, PikaContext *context) { PikaImagefile *imagefile; imagefile = pika_context_find_object (context, container, context->imagefile_name, NULL); if (imagefile) { pika_context_real_set_imagefile (context, imagefile); } else { g_object_notify (G_OBJECT (context), "imagefile"); pika_context_imagefile_changed (context); } } static void pika_context_imagefile_removed (PikaContainer *container, PikaImagefile *imagefile, PikaContext *context) { if (imagefile == context->imagefile) { g_signal_handlers_disconnect_by_func (context->imagefile, pika_context_imagefile_dirty, context); g_clear_object (&context->imagefile); if (! pika_container_frozen (container)) pika_context_imagefile_list_thaw (container, context); } } static void pika_context_real_set_imagefile (PikaContext *context, PikaImagefile *imagefile) { if (context->imagefile == imagefile) return; if (context->imagefile_name) { g_clear_pointer (&context->imagefile_name, g_free); } if (context->imagefile) g_signal_handlers_disconnect_by_func (context->imagefile, pika_context_imagefile_dirty, context); g_set_object (&context->imagefile, imagefile); if (imagefile) { g_signal_connect_object (imagefile, "name-changed", G_CALLBACK (pika_context_imagefile_dirty), context, 0); context->imagefile_name = g_strdup (pika_object_get_name (imagefile)); } g_object_notify (G_OBJECT (context), "imagefile"); pika_context_imagefile_changed (context); } /*****************************************************************************/ /* template ***************************************************************/ PikaTemplate * pika_context_get_template (PikaContext *context) { g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return context->template; } void pika_context_set_template (PikaContext *context, PikaTemplate *template) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (template == NULL || PIKA_IS_TEMPLATE (template)); context_find_defined (context, PIKA_CONTEXT_PROP_TEMPLATE); pika_context_real_set_template (context, template); } void pika_context_template_changed (PikaContext *context) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_signal_emit (context, pika_context_signals[TEMPLATE_CHANGED], 0, context->template); } static void pika_context_template_dirty (PikaTemplate *template, PikaContext *context) { g_free (context->template_name); context->template_name = g_strdup (pika_object_get_name (template)); g_signal_emit (context, pika_context_signals[PROP_NAME_CHANGED], 0, PIKA_CONTEXT_PROP_TEMPLATE); } static void pika_context_template_list_thaw (PikaContainer *container, PikaContext *context) { PikaTemplate *template; template = pika_context_find_object (context, container, context->template_name, NULL); if (template) { pika_context_real_set_template (context, template); } else { g_object_notify (G_OBJECT (context), "template"); pika_context_template_changed (context); } } static void pika_context_template_removed (PikaContainer *container, PikaTemplate *template, PikaContext *context) { if (template == context->template) { g_signal_handlers_disconnect_by_func (context->template, pika_context_template_dirty, context); g_clear_object (&context->template); if (! pika_container_frozen (container)) pika_context_template_list_thaw (container, context); } } static void pika_context_real_set_template (PikaContext *context, PikaTemplate *template) { if (context->template == template) return; if (context->template_name) { g_clear_pointer (&context->template_name, g_free); } if (context->template) g_signal_handlers_disconnect_by_func (context->template, pika_context_template_dirty, context); g_set_object (&context->template, template); if (template) { g_signal_connect_object (template, "name-changed", G_CALLBACK (pika_context_template_dirty), context, 0); context->template_name = g_strdup (pika_object_get_name (template)); } g_object_notify (G_OBJECT (context), "template"); pika_context_template_changed (context); } /*****************************************************************************/ /* Line Art ****************************************************************/ PikaLineArt * pika_context_take_line_art (PikaContext *context) { PikaLineArt *line_art; g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); if (context->line_art) { g_source_remove (context->line_art_timeout_id); context->line_art_timeout_id = 0; line_art = context->line_art; context->line_art = NULL; } else { line_art = pika_line_art_new (); } return line_art; } /* * pika_context_store_line_art: * @context: * @line_art: * * The @context takes ownership of @line_art until the next time it is * requested with pika_context_take_line_art() or until 3 minutes have * passed. * This function allows to temporarily store the computed line art data * in case it is needed very soon again, so that not to free and * recompute all the time the data when quickly switching tools. */ void pika_context_store_line_art (PikaContext *context, PikaLineArt *line_art) { g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (PIKA_IS_LINE_ART (line_art)); if (context->line_art) { g_source_remove (context->line_art_timeout_id); context->line_art_timeout_id = 0; } context->line_art = line_art; context->line_art_timeout_id = g_timeout_add (180000, (GSourceFunc) pika_context_free_line_art, context); } static gboolean pika_context_free_line_art (PikaContext *context) { g_clear_object (&context->line_art); context->line_art_timeout_id = 0; return G_SOURCE_REMOVE; } /*****************************************************************************/ /* utility functions *******************************************************/ static gpointer pika_context_find_object (PikaContext *context, PikaContainer *container, const gchar *object_name, gpointer standard_object) { PikaObject *object = NULL; if (object_name) object = pika_container_get_child_by_name (container, object_name); if (! object && ! pika_container_is_empty (container)) object = pika_container_get_child_by_index (container, 0); if (! object) object = standard_object; return object; }