/*
* Copyright © 2025 Red Hat , Inc
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 . 1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see < http : //www.gnu.org/licenses/>.
*
* SPDX - License - Identifier : LGPL - 2 . 1 - or - later
*
* Authors : Matthias Clasen < mclasen @ redhat . com >
*/
#include "paintable-editor.h"
#include "shape-editor.h"
#include "style-editor.h"
#include "path-paintable.h"
#include "gtk/svg/gtksvgnumberprivate.h"
#include "gtk/svg/gtksvgviewboxprivate.h"
#include "gtk/svg/gtksvgelementprivate.h"
#include "gtk/svg/gtksvgpathprivate.h"
static void size_changed (PaintableEditor *self);
static void paintable_editor_set_compat_classes (PaintableEditor *self,
gboolean compat_classes);
struct _PaintableEditor
{
GtkWidget parent_instance;
PathPaintable *paintable;
gboolean compat_classes;
GtkScrolledWindow *swin;
GtkEntry *author;
GtkEntry *license;
GtkEntry *description;
GtkEntry *keywords;
GtkEntry *width;
GtkEntry *height;
GtkEntry *viewbox_x;
GtkEntry *viewbox_y;
GtkEntry *viewbox_w;
GtkEntry *viewbox_h;
GtkLabel *compat;
GtkLabel *summary1;
GtkLabel *summary2;
GtkImage *icon_image;
GtkCheckButton *compat_check;
GtkStack *stack;
GtkTextView *xml_view;
GtkTextBuffer *xml_buffer;
guint timeout;
GList *errors;
GtkBox *elements;
StyleEditor *stylesheet;
gulong style_handler;
};
struct _PaintableEditorClass
{
GtkWidgetClass parent_class;
};
enum
{
PROP_PAINTABLE = 1 ,
PROP_COMPAT_CLASSES,
NUM_PROPERTIES,
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (PaintableEditor, paintable_editor, GTK_TYPE_WIDGET)
/* {{{ Utilities, callbacks */
static void
clear_shape_editors (PaintableEditor *self)
{
GtkWidget *child;
while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->elements))) != NULL)
gtk_box_remove (self->elements, child);
}
static void
append_shape_editor (PaintableEditor *self,
SvgElement *shape)
{
ShapeEditor *pe;
pe = shape_editor_new (self->paintable, shape);
if (pe)
{
gtk_box_append (self->elements, GTK_WIDGET (pe));
gtk_box_append (self->elements, gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
}
}
static void
create_shape_editors (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
gtk_box_append (self->elements, gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
for (unsigned int i = 0 ; i < svg_element_get_n_children (svg->content); i++)
{
SvgElement *shape = svg_element_get_child (svg->content, i);
append_shape_editor (self, shape);
}
}
static void
append_styles (SvgElement *element,
gpointer data)
{
GString *s = data;
for (unsigned int i = 0 ; i < svg_element_get_n_styles (element); i++)
{
if (s->len > 0 )
g_string_append (s, "\n" );
if (svg_element_get_style_media (element, i))
{
g_string_append_printf (s, "@media (%s) {\n" , svg_element_get_style_media (element, i));
g_string_append (s, svg_element_get_style (element, i));
g_string_append (s, "\n}\n" );
}
else
g_string_append (s, svg_element_get_style (element, i));
}
svg_element_clear_style (element);
}
static void
populate_style_editor (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
g_autoptr (GString) s = g_string_new ("" );
g_signal_handler_block (self->stylesheet, self->style_handler);
svg_element_foreach (svg->content, append_styles, s);
style_editor_set_style (self->stylesheet, s->str);
g_signal_handler_unblock (self->stylesheet, self->style_handler);
}
static void
update_size (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
g_autofree char *text = NULL;
text = g_strdup_printf ("%g" , svg->width);
gtk_editable_set_text (GTK_EDITABLE (self->width), text);
g_set_str (&text, g_strdup_printf ("%g" , svg->height));
gtk_editable_set_text (GTK_EDITABLE (self->height), text);
}
static void
update_viewbox (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
SvgValue *value;
graphene_rect_t rect;
value = ref_value (svg->content, SVG_PROPERTY_VIEW_BOX);
if (svg_view_box_get (value, &rect))
{
g_autofree char *text = g_strdup_printf ("%g" , rect.origin.x);
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_x), text);
g_set_str (&text, g_strdup_printf ("%g" , rect.origin.y));
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_y), text);
g_set_str (&text, g_strdup_printf ("%g" , rect.size.width));
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_w), text);
g_set_str (&text, g_strdup_printf ("%g" , rect.size.height));
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_h), text);
}
else
{
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_x), "" );
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_y), "" );
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_w), "" );
gtk_editable_set_text (GTK_EDITABLE (self->viewbox_h), "" );
}
svg_value_unref (value);
}
typedef struct
{
unsigned int all;
unsigned int graphical;
unsigned int current;
unsigned int state;
} ShapeCountData;
static void
count_shapes (SvgElement *shape,
gpointer data)
{
ShapeCountData *d = data;
d->all++;
if (!svg_element_type_is_graphical (svg_element_get_type (shape)))
return ;
d->graphical++;
if ((svg_element_get_states (shape) & (G_GUINT64_CONSTANT (1 ) << d->state)) == 0 )
return ;
d->current++;
}
static void
update_metadata (PaintableEditor *self)
{
if (self->paintable)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
gtk_editable_set_text (GTK_EDITABLE (self->author), svg->author ? svg->author : "" );
gtk_editable_set_text (GTK_EDITABLE (self->license), svg->license ? svg->license : "" );
gtk_editable_set_text (GTK_EDITABLE (self->description), svg->description ? svg->description : "" );
gtk_editable_set_text (GTK_EDITABLE (self->keywords), svg->keywords ? svg->keywords : "" );
}
}
static void
update_summary (PaintableEditor *self)
{
if (self->paintable)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
unsigned int state, n_names;
const char **names;
g_autofree char *summary1 = NULL;
g_autofree char *summary2 = NULL;
ShapeCountData counts;
state = gtk_svg_get_state (svg);
names = gtk_svg_get_state_names (svg, &n_names);
counts.state = state;
counts.all = counts.graphical = counts.current = 0 ;
svg_element_foreach (svg->content, count_shapes, &counts);
if (state < n_names)
summary1 = g_strdup_printf ("Current state: %u (%s)" , state, names[state]);
else
summary1 = g_strdup_printf ("Current state: %u" , state);
summary2 = g_strdup_printf ("%u graphical shapes, %u in current state" , counts.graphical, counts.current);
gtk_label_set_label (self->summary1, summary1);
gtk_label_set_label (self->summary2, summary2);
}
else
{
gtk_label_set_label (self->summary1, "" );
gtk_label_set_label (self->summary2, "" );
}
}
static void
update_compat (PaintableEditor *self)
{
switch (path_paintable_get_compatibility (self->paintable))
{
case GTK_4_0:
gtk_label_set_label (self->compat, "GTK 4.0" );
break ;
case GTK_4_20:
gtk_label_set_label (self->compat, "GTK 4.20" );
break ;
case GTK_4_22:
gtk_label_set_label (self->compat, "GTK 4.22" );
break ;
default :
g_assert_not_reached ();
}
}
static void
update_icon_paintable (PaintableEditor *self)
{
GdkPaintable *paintable = GDK_PAINTABLE (path_paintable_get_icon_paintable (self->paintable));
gtk_image_set_from_paintable (self->icon_image, paintable);
g_object_unref (paintable);
}
typedef struct
{
GError *error;
GtkTextIter start;
GtkTextIter end;
} SvgError;
static void
svg_error_free (gpointer data)
{
SvgError *error = data;
g_error_free (error->error);
g_free (error);
}
typedef struct
{
PaintableEditor *self;
const char *text;
} ErrorData;
static void
error_cb (GtkSvg *svg,
GError *error,
gpointer data)
{
/* Without GLib 2.88, we don't get usable location
* information from GMarkup , so don ' t try to highlight
* errors
*/
#if GLIB_CHECK_VERSION (2 , 88 , 0 )
ErrorData *d = data;
PaintableEditor *self = d->self;
SvgError *svg_error;
size_t offset;
const GtkSvgLocation *start, *end;
const char *tag;
if (error->domain != GTK_SVG_ERROR)
return ;
start = gtk_svg_error_get_start (error);
end = gtk_svg_error_get_end (error);
svg_error = g_new (SvgError, 1 );
svg_error->error = g_error_copy (error);
offset = g_utf8_pointer_to_offset (d->text, d->text + start->bytes);
gtk_text_buffer_get_iter_at_offset (self->xml_buffer, &svg_error->start, offset);
offset = g_utf8_pointer_to_offset (d->text, d->text + end->bytes);
gtk_text_buffer_get_iter_at_offset (self->xml_buffer, &svg_error->end, offset);
self->errors = g_list_append (self->errors, svg_error);
if (gtk_text_iter_equal (&svg_error->start, &svg_error->end))
gtk_text_iter_forward_chars (&svg_error->end, 1 );
if (error->code == GTK_SVG_ERROR_IGNORED_ELEMENT)
tag = "ignored" ;
else if (error->code == GTK_SVG_ERROR_NOT_IMPLEMENTED)
tag = "unimplemented" ;
else
tag = "error" ;
gtk_text_buffer_apply_tag_by_name (self->xml_buffer,
tag,
&svg_error->start,
&svg_error->end);
#endif
}
static void
update_timeout (gpointer data)
{
PaintableEditor *self = data;
GtkTextIter start, end;
char *text;
g_autoptr (GBytes) bytes = NULL;
g_autoptr (GtkSvg) svg = NULL;
gulong handler;
ErrorData d;
gtk_text_buffer_get_bounds (self->xml_buffer, &start, &end);
gtk_text_buffer_remove_all_tags (self->xml_buffer, &start, &end);
text = gtk_text_buffer_get_text (self->xml_buffer, &start, &end, FALSE );
bytes = g_bytes_new_take (text, strlen (text));
svg = gtk_svg_new ();
g_list_free_full (self->errors, svg_error_free);
self->errors = NULL;
d.self = self;
d.text = text;
handler = g_signal_connect (svg, "error" , G_CALLBACK (error_cb), &d);
gtk_svg_load_from_bytes (svg, bytes);
g_signal_handler_disconnect (svg, handler);
path_paintable_set_svg (self->paintable, svg);
self->timeout = 0 ;
}
static void
xml_changed (PaintableEditor *self)
{
if (self->timeout != 0 )
g_source_remove (self->timeout);
self->timeout = g_timeout_add_once (100 , update_timeout, self);
}
static void
style_changed (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
const char *style = style_editor_get_style (self->stylesheet);
svg_element_clear_style (svg->content);
svg_element_set_style (svg->content, 0 , style);
path_paintable_changed (self->paintable);
}
/* }}} */
static void
update_xml (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
g_autoptr (GBytes) xml = gtk_svg_serialize (svg);
g_signal_handlers_block_by_func (self->xml_buffer, xml_changed, self);
gtk_text_buffer_set_text (self->xml_buffer, g_bytes_get_data (xml, NULL), g_bytes_get_size (xml));
//update_timeout (self);
g_signal_handlers_unblock_by_func (self->xml_buffer, xml_changed, self);
}
static void
paths_changed (PaintableEditor *self)
{
clear_shape_editors (self);
create_shape_editors (self);
update_summary (self);
update_icon_paintable (self);
}
static void
changed (PaintableEditor *self)
{
update_compat (self);
update_size (self);
update_viewbox (self);
update_metadata (self);
update_summary (self);
update_icon_paintable (self);
}
static void
set_size (PaintableEditor *self,
double width,
double height)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
SvgValue *value;
graphene_rect_t rect;
svg->width = width;
svg->height = height;
svg_element_take_specified_value (svg->content, SVG_PROPERTY_WIDTH, svg_number_new (width));
svg_element_take_specified_value (svg->content, SVG_PROPERTY_HEIGHT, svg_number_new (height));
value = ref_value (svg->content, SVG_PROPERTY_VIEW_BOX);
if (!svg_view_box_get (value, &rect))
svg_element_take_specified_value (svg->content, SVG_PROPERTY_VIEW_BOX,
svg_view_box_new (&GRAPHENE_RECT_INIT (0 , 0 , width, height)));
svg_value_unref (value);
path_paintable_changed (self->paintable);
gdk_paintable_invalidate_size (GDK_PAINTABLE (self->paintable));
}
static void
size_changed (PaintableEditor *self)
{
const char *text;
double width, height;
int res;
text = gtk_editable_get_text (GTK_EDITABLE (self->width));
res = sscanf (text, "%lf" , &width);
text = gtk_editable_get_text (GTK_EDITABLE (self->height));
res += sscanf (text, "%lf" , &height);
if (res == 2 && width > 0 && height > 0 )
set_size (self, width, height);
}
static void
viewbox_changed (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
const char *text;
double x, y, w, h;
int res;
text = gtk_editable_get_text (GTK_EDITABLE (self->viewbox_x));
res = sscanf (text, "%lf" , &x);
text = gtk_editable_get_text (GTK_EDITABLE (self->viewbox_y));
res += sscanf (text, "%lf" , &y);
text = gtk_editable_get_text (GTK_EDITABLE (self->viewbox_w));
res += sscanf (text, "%lf" , &w);
text = gtk_editable_get_text (GTK_EDITABLE (self->viewbox_h));
res += sscanf (text, "%lf" , &h);
if (res == 4 && w > 0 && h > 0 )
{
svg_element_take_specified_value (svg->content, SVG_PROPERTY_VIEW_BOX,
svg_view_box_new (&GRAPHENE_RECT_INIT (x, y, w, h)));
path_paintable_changed (self->paintable);
gdk_paintable_invalidate_size (GDK_PAINTABLE (self->paintable));
}
}
static void
author_changed (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
const char *text = gtk_editable_get_text (GTK_EDITABLE (self->author));
if (g_set_str (&svg->author, text))
path_paintable_changed (self->paintable);
}
static void
license_changed (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
const char *text = gtk_editable_get_text (GTK_EDITABLE (self->license));
if (g_set_str (&svg->license, text))
path_paintable_changed (self->paintable);
}
static void
description_changed (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
const char *text = gtk_editable_get_text (GTK_EDITABLE (self->description));
if (g_set_str (&svg->description, text))
path_paintable_changed (self->paintable);
}
static void
keywords_changed (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
const char *text = gtk_editable_get_text (GTK_EDITABLE (self->keywords));
if (g_set_str (&svg->keywords, text))
path_paintable_changed (self->paintable);
}
void
paintable_editor_set_show_xml (PaintableEditor *self,
gboolean xml)
{
if (xml)
{
update_xml (self);
gtk_stack_set_visible_child_name (self->stack, "xml" );
}
else
{
gtk_stack_set_visible_child_name (self->stack, "controls" );
}
}
static gboolean
query_tooltip_cb (GtkWidget *widget,
int x,
int y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
PaintableEditor *self)
{
GtkTextIter iter;
if (strcmp (gtk_stack_get_visible_child_name (self->stack), "controls" ) == 0 )
return FALSE ;
if (keyboard_tip)
{
int offset;
g_object_get (self->xml_view, "cursor-position" , &offset, NULL);
gtk_text_buffer_get_iter_at_offset (self->xml_buffer, &iter, offset);
}
else
{
int bx, by, trailing;
gtk_text_view_window_to_buffer_coords (self->xml_view,
GTK_TEXT_WINDOW_TEXT,
x, y, &bx, &by);
gtk_text_view_get_iter_at_position (self->xml_view, &iter, &trailing, bx, by);
}
for (GList *l = self->errors; l; l = l->next)
{
SvgError *error = l->data;
if (gtk_text_iter_in_range (&iter, &error->start, &error->end))
{
gtk_tooltip_set_text (tooltip, error->error->message);
return TRUE ;
}
}
return FALSE ;
}
/* {{{ GObject boilerplate */
static void
paintable_editor_init (PaintableEditor *self)
{
self->compat_classes = TRUE ;
gtk_widget_init_template (GTK_WIDGET (self));
self->style_handler = g_signal_connect_swapped (self->stylesheet, "notify::style" ,
G_CALLBACK (style_changed), self);
}
static void
paintable_editor_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
PaintableEditor *self = PAINTABLE_EDITOR (object);
switch (prop_id)
{
case PROP_PAINTABLE:
paintable_editor_set_paintable (self, g_value_get_object (value));
break ;
case PROP_COMPAT_CLASSES:
paintable_editor_set_compat_classes (self, g_value_get_boolean (value));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break ;
}
}
static void
paintable_editor_get_property (GObject *object,
unsigned int prop_id,
GValue *value,
GParamSpec *pspec)
{
PaintableEditor *self = PAINTABLE_EDITOR (object);
switch (prop_id)
{
case PROP_PAINTABLE:
g_value_set_object (value, self->paintable);
break ;
case PROP_COMPAT_CLASSES:
g_value_set_boolean (value, self->compat_classes);
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break ;
}
}
static void
paintable_editor_dispose (GObject *object)
{
PaintableEditor *self = PAINTABLE_EDITOR (object);
g_clear_signal_handler (&self->style_handler, self->stylesheet);
g_list_free_full (self->errors, svg_error_free);
self->errors = NULL;
g_clear_handle_id (&self->timeout, g_source_remove);
if (self->paintable)
{
g_signal_handlers_disconnect_by_func (self->paintable, paths_changed, self);
g_signal_handlers_disconnect_by_func (self->paintable, changed, self);
g_signal_handlers_disconnect_by_func (self->paintable, update_summary, self);
}
g_clear_object (&self->paintable);
clear_shape_editors (self);
gtk_widget_dispose_template (GTK_WIDGET (object), PAINTABLE_EDITOR_TYPE);
G_OBJECT_CLASS (paintable_editor_parent_class)->dispose (object);
}
static void
paintable_editor_finalize (GObject *object)
{
G_OBJECT_CLASS (paintable_editor_parent_class)->finalize (object);
}
static void
paintable_editor_class_init (PaintableEditorClass *class )
{
GObjectClass *object_class = G_OBJECT_CLASS (class );
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class );
g_type_ensure (SHAPE_EDITOR_TYPE);
g_type_ensure (STYLE_EDITOR_TYPE);
object_class->dispose = paintable_editor_dispose;
object_class->finalize = paintable_editor_finalize;
object_class->set_property = paintable_editor_set_property;
object_class->get_property = paintable_editor_get_property;
properties[PROP_PAINTABLE] =
g_param_spec_object ("paintable" , NULL, NULL,
PATH_PAINTABLE_TYPE,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
properties[PROP_COMPAT_CLASSES] =
g_param_spec_boolean ("compat-classes" , NULL, NULL,
TRUE ,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gtk/Shaper/paintable-editor.ui" );
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, swin);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, author);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, license);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, description);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, keywords);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, width);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, height);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, viewbox_x);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, viewbox_y);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, viewbox_w);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, viewbox_h);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, compat);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, summary1);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, summary2);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, icon_image);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, elements);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, stylesheet);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, compat_check);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, stack);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, xml_view);
gtk_widget_class_bind_template_child (widget_class, PaintableEditor, xml_buffer);
gtk_widget_class_bind_template_callback (widget_class, size_changed);
gtk_widget_class_bind_template_callback (widget_class, viewbox_changed);
gtk_widget_class_bind_template_callback (widget_class, author_changed);
gtk_widget_class_bind_template_callback (widget_class, license_changed);
gtk_widget_class_bind_template_callback (widget_class, description_changed);
gtk_widget_class_bind_template_callback (widget_class, keywords_changed);
gtk_widget_class_bind_template_callback (widget_class, xml_changed);
gtk_widget_class_bind_template_callback (widget_class, query_tooltip_cb);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, "PaintableEditor" );
}
/* }}} */
/* {{{ Public API */
PaintableEditor *
paintable_editor_new (void )
{
return g_object_new (PAINTABLE_EDITOR_TYPE, NULL);
}
PathPaintable *
paintable_editor_get_paintable (PaintableEditor *self)
{
g_return_val_if_fail (PAINTABLE_IS_EDITOR (self), NULL);
return self->paintable;
}
void
paintable_editor_set_paintable (PaintableEditor *self,
PathPaintable *paintable)
{
g_return_if_fail (PAINTABLE_IS_EDITOR (self));
if (self->paintable == paintable)
return ;
if (self->paintable)
{
g_signal_handlers_disconnect_by_func (self->paintable, paths_changed, self);
g_signal_handlers_disconnect_by_func (self->paintable, changed, self);
g_signal_handlers_disconnect_by_func (self->paintable, update_summary, self);
}
clear_shape_editors (self);
g_set_object (&self->paintable, paintable);
if (paintable)
{
g_signal_connect_swapped (paintable, "paths-changed" ,
G_CALLBACK (paths_changed), self);
g_signal_connect_swapped (paintable, "changed" ,
G_CALLBACK (changed), self);
g_signal_connect_swapped (paintable, "notify::state" ,
G_CALLBACK (update_summary), self);
create_shape_editors (self);
populate_style_editor (self);
update_summary (self);
update_metadata (self);
update_size (self);
update_viewbox (self);
update_compat (self);
update_icon_paintable (self);
update_xml (self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PAINTABLE]);
}
static void
paintable_editor_set_compat_classes (PaintableEditor *self,
gboolean compat_classes)
{
g_return_if_fail (PAINTABLE_IS_EDITOR (self));
if (self->compat_classes == compat_classes)
return ;
self->compat_classes = compat_classes;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COMPAT_CLASSES]);
}
void
paintable_editor_add_element (PaintableEditor *self)
{
GtkSvg *svg = path_paintable_get_svg (self->paintable);
GskPathBuilder *builder;
g_autoptr (GskPath) path = NULL;
SvgElement *shape;
SvgValue *value;
graphene_rect_t rect;
char *id;
if (svg_element_get_n_children (svg->content) == 0 && svg->width == 0 && svg->height == 0 )
set_size (self, 100 , 100 );
value = ref_value (svg->content, SVG_PROPERTY_VIEW_BOX);
svg_view_box_get (value, &rect);
svg_value_unref (value);
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, rect.origin.x, rect.origin.y);
gsk_path_builder_rel_line_to (builder, rect.size.width, rect.size.height);
path = gsk_path_builder_free_to_path (builder);
g_signal_handlers_block_by_func (self->paintable, paths_changed, self);
shape = svg_element_new (svg->content, SVG_ELEMENT_PATH);
svg_element_add_child (svg->content, shape);
shape_set_default_attrs (shape);
svg_element_take_specified_value (shape, SVG_PROPERTY_PATH, svg_path_new (path));
id = path_paintable_find_unused_id (self->paintable, "path" );
svg_element_set_id (shape, id);
g_free (id);
append_shape_editor (self, svg_element_get_child (svg->content, svg_element_get_n_children (svg->content) - 1 ));
g_signal_handlers_unblock_by_func (self->paintable, paths_changed, self);
}
/* }}} */
/* vim:set foldmethod=marker: */
Messung V0.5 in Prozent C=99 H=94 G=96
¤ Dauer der Verarbeitung: 0.8 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland