Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  paintable-editor.c

  Sprache: C
 

/*
 * 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 (2880)
  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 (00, 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, 100100);

  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






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik