[gtk+] Support scale marks in builder markup



commit dc8f36e2549021a9f51291267d916b73f8d909df
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat May 2 21:02:12 2009 -0400

    Support scale marks in builder markup
---
 NEWS                                    |    4 +
 docs/reference/gtk/tmpl/gtkbuilder.sgml |    3 +-
 docs/reference/gtk/tmpl/gtkscale.sgml   |   24 ++-
 gtk/gtkscale.c                          |  256 ++++++++++++++++++++++++++++++-
 4 files changed, 277 insertions(+), 10 deletions(-)

diff --git a/NEWS b/NEWS
index 5037f3e..c47eb47 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,10 @@
 Overview of Changes from GTK+ 2.16.x to 2.17.0
 ==============================================
 
+* GtkBuilder:
+  - Scale marks can now be specified in builder markup
+  - GtkAssistant action widgets can be added in builder markup
+
 * Changes that are relevant for theme authors
  - GtkEntry now has a ::invisible-char style property that allows
    themes to set the preferred invisible character
diff --git a/docs/reference/gtk/tmpl/gtkbuilder.sgml b/docs/reference/gtk/tmpl/gtkbuilder.sgml
index fe10530..8dc1eb5 100644
--- a/docs/reference/gtk/tmpl/gtkbuilder.sgml
+++ b/docs/reference/gtk/tmpl/gtkbuilder.sgml
@@ -229,7 +229,8 @@ respective objects, see
 <link linkend="GtkUIManager-BUILDER-UI">GtkUIManager</link>,
 <link linkend="GtkActionGroup-BUILDER-UI">GtkActionGroup</link>.
 <link linkend="GtkMenuItem-BUILDER-UI">GtkMenuItem</link>,
-<link linkend="GtkAssistant-BUILDER-UI">GtkAssistant</link>.
+<link linkend="GtkAssistant-BUILDER-UI">GtkAssistant</link>,
+<link linkend="GtkScale-BUILDER-UI">GtkScale</link>.
 </para>
 </refsect2>
 
diff --git a/docs/reference/gtk/tmpl/gtkscale.sgml b/docs/reference/gtk/tmpl/gtkscale.sgml
index 3955e9f..2ac9a18 100644
--- a/docs/reference/gtk/tmpl/gtkscale.sgml
+++ b/docs/reference/gtk/tmpl/gtkscale.sgml
@@ -7,18 +7,28 @@ Base class for GtkHScale and GtkVScale
 <!-- ##### SECTION Long_Description ##### -->
 <para>
 A #GtkScale is a slider control used to select a numeric value.
-To use it, you'll probably want to investigate the methods on 
+To use it, you'll probably want to investigate the methods on
 its base class, #GtkRange, in addition to the methods for #GtkScale itself.
-To set the value of a scale, you would normally use gtk_range_set_value(). 
-To detect changes to the value, you would normally use the "value_changed" 
+To set the value of a scale, you would normally use gtk_range_set_value().
+To detect changes to the value, you would normally use the "value_changed"
 signal.
 </para>
 <para>
 The #GtkScale widget is an abstract class, used only for deriving the
-subclasses #GtkHScale and #GtkVScale. To create a scale widget, 
+subclasses #GtkHScale and #GtkVScale. To create a scale widget,
 call gtk_hscale_new_with_range() or gtk_vscale_new_with_range().
 </para>
 
+<refsect2 id="GtkScale-BUILDER-UI"><title>GtkScale as GtkBuildable</title>
+<para>
+GtkScale supports a custom &lt;marks&gt; element, which
+can contain multiple &lt;mark&gt; elements. The "value" and "position"
+attributes have the same meaning as gtk_scale_add_mark() parameters of the
+same name. If the element is not empty, its content is taken as the markup
+to show at the mark. It can be translated with the usual "translatable and
+"context" attributes.
+</para>
+
 <!-- ##### SECTION See_Also ##### -->
 <para>
 
@@ -39,9 +49,9 @@ the accessor functions.
 
 </para>
 
- scale: 
- value: 
- Returns: 
+ scale:
+ value:
+ Returns:
 
 <!-- ##### ARG GtkScale:digits ##### -->
 <para>
diff --git a/gtk/gtkscale.c b/gtk/gtkscale.c
index b738d6d..87aa469 100644
--- a/gtk/gtkscale.c
+++ b/gtk/gtkscale.c
@@ -38,6 +38,8 @@
 #include "gtkbindings.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
+#include "gtkbuildable.h"
+#include "gtkbuilderprivate.h"
 #include "gtkalias.h"
 
 
@@ -56,7 +58,7 @@ typedef struct _GtkScaleMark GtkScaleMark;
 struct _GtkScaleMark
 {
   gdouble          value;
-  const gchar     *markup;
+  gchar           *markup;
   GtkPositionType  position;
 };
 
@@ -110,8 +112,24 @@ static gboolean gtk_scale_expose                  (GtkWidget      *widget,
 static void     gtk_scale_real_get_layout_offsets (GtkScale       *scale,
                                                    gint           *x,
                                                    gint           *y);
+static void     gtk_scale_buildable_interface_init   (GtkBuildableIface *iface);
+static gboolean gtk_scale_buildable_custom_tag_start (GtkBuildable  *buildable,
+                                                      GtkBuilder    *builder,
+                                                      GObject       *child,
+                                                      const gchar   *tagname,
+                                                      GMarkupParser *parser,
+                                                      gpointer      *data);
+static void     gtk_scale_buildable_custom_finished  (GtkBuildable  *buildable,
+                                                      GtkBuilder    *builder,
+                                                      GObject       *child,
+                                                      const gchar   *tagname,
+                                                      gpointer       user_data);
+
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkScale, gtk_scale, GTK_TYPE_RANGE,
+                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+                                                         gtk_scale_buildable_interface_init))
 
-G_DEFINE_ABSTRACT_TYPE (GtkScale, gtk_scale, GTK_TYPE_RANGE)
 
 static gboolean
 single_string_accumulator (GSignalInvocationHint *ihint,
@@ -1355,6 +1373,240 @@ gtk_scale_add_mark (GtkScale        *scale,
   gtk_widget_queue_resize (GTK_WIDGET (scale));
 }
 
+static GtkBuildableIface *parent_buildable_iface;
+
+static void
+gtk_scale_buildable_interface_init (GtkBuildableIface *iface)
+{
+  parent_buildable_iface = g_type_interface_peek_parent (iface);
+  iface->custom_tag_start = gtk_scale_buildable_custom_tag_start;
+  iface->custom_finished = gtk_scale_buildable_custom_finished;
+}
+
+typedef struct
+{
+  GtkScale *scale;
+  GtkBuilder *builder;
+  GSList *marks;
+} MarksSubparserData;
+
+typedef struct
+{
+  gdouble value;
+  GtkPositionType position;
+  GString *markup;
+  gchar *context;
+  gboolean translatable;
+} MarkData;
+
+static void
+mark_data_free (MarkData *data)
+{
+  g_string_free (data->markup, TRUE);
+  g_free (data->context);
+  g_slice_free (MarkData, data);
+}
+
+static void
+marks_start_element (GMarkupParseContext *context,
+                     const gchar         *element_name,
+                     const gchar        **names,
+                     const gchar        **values,
+                     gpointer             user_data,
+                     GError             **error)
+{
+  MarksSubparserData *parser_data = (MarksSubparserData*)user_data;
+  guint i;
+  gint line_number, char_number;
+
+  if (strcmp (element_name, "marks") == 0)
+   ;
+  else if (strcmp (element_name, "mark") == 0)
+    {
+      gdouble value;
+      gboolean has_value = FALSE;
+      GtkPositionType position = GTK_POS_BOTTOM;
+      const gchar *msg_context = NULL;
+      gboolean translatable = FALSE;
+      MarkData *mark;
+
+      for (i = 0; names[i]; i++)
+        {
+          if (strcmp (names[i], "translatable") == 0)
+            {
+              if (!_gtk_builder_boolean_from_string (values[i], &translatable, error))
+                return;
+            }
+          else if (strcmp (names[i], "comments") == 0)
+            {
+              /* do nothing, comments are for translators */
+            }
+          else if (strcmp (names[i], "context") == 0)
+            msg_context = values[i];
+          else if (strcmp (names[i], "value") == 0)
+            {
+              GValue gvalue = { 0, };
+
+              if (!gtk_builder_value_from_string_type (parser_data->builder, G_TYPE_DOUBLE, values[i], &gvalue, error))
+                return;
+
+              value = g_value_get_double (&gvalue);
+              has_value = TRUE;
+            }
+          else if (strcmp (names[i], "position") == 0)
+            {
+              GValue gvalue = { 0, };
+
+              if (!gtk_builder_value_from_string_type (parser_data->builder, GTK_TYPE_POSITION_TYPE, values[i], &gvalue, error))
+                return;
+
+              position = g_value_get_enum (&gvalue);
+            }
+          else
+            {
+              g_markup_parse_context_get_position (context,
+                                                   &line_number,
+                                                   &char_number);
+              g_set_error (error,
+                           GTK_BUILDER_ERROR,
+                           GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
+                           "%s:%d:%d '%s' is not a valid attribute of <%s>",
+                           "<input>",
+                           line_number, char_number, names[i], "mark");
+              return;
+            }
+        }
+
+      if (!has_value)
+        {
+          g_markup_parse_context_get_position (context,
+                                               &line_number,
+                                               &char_number);
+          g_set_error (error,
+                       GTK_BUILDER_ERROR,
+                       GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
+                       "%s:%d:%d <%s> requires attribute \"%s\"",
+                       "<input>",
+                       line_number, char_number, "mark",
+                       "value");
+          return;
+        }
+
+      mark = g_slice_new (MarkData);
+      mark->value = value;
+      mark->position = position;
+      mark->markup = g_string_new ("");
+      mark->context = g_strdup (msg_context);
+      mark->translatable = translatable;
+
+      parser_data->marks = g_slist_prepend (parser_data->marks, mark);
+    }
+  else
+    {
+      g_markup_parse_context_get_position (context,
+                                           &line_number,
+                                           &char_number);
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
+                   "%s:%d:%d unsupported tag for GtkScale: \"%s\"",
+                   "<input>",
+                   line_number, char_number, element_name);
+      return;
+    }
+}
+
+static void
+marks_text (GMarkupParseContext  *context,
+            const gchar          *text,
+            gsize                 text_len,
+            gpointer              user_data,
+            GError              **error)
+{
+  MarksSubparserData *data = (MarksSubparserData*)user_data;
+
+  if (strcmp (g_markup_parse_context_get_element (context), "mark") == 0)
+    {
+      MarkData *mark = data->marks->data;
+
+      g_string_append_len (mark->markup, text, text_len);
+    }
+}
+
+static const GMarkupParser marks_parser =
+  {
+    marks_start_element,
+    NULL,
+    marks_text,
+  };
+
+
+static gboolean
+gtk_scale_buildable_custom_tag_start (GtkBuildable  *buildable,
+                                      GtkBuilder    *builder,
+                                      GObject       *child,
+                                      const gchar   *tagname,
+                                      GMarkupParser *parser,
+                                      gpointer      *data)
+{
+  MarksSubparserData *parser_data;
+
+  if (child)
+    return FALSE;
+
+  if (strcmp (tagname, "marks") == 0)
+    {
+      parser_data = g_slice_new0 (MarksSubparserData);
+      parser_data->scale = GTK_SCALE (buildable);
+      parser_data->marks = NULL;
+
+      *parser = marks_parser;
+      *data = parser_data;
+      return TRUE;
+    }
+
+  return parent_buildable_iface->custom_tag_start (buildable, builder, child,
+                                                   tagname, parser, data);
+}
+
+static void
+gtk_scale_buildable_custom_finished (GtkBuildable *buildable,
+                                     GtkBuilder   *builder,
+                                     GObject      *child,
+                                     const gchar  *tagname,
+                                     gpointer      user_data)
+{
+  GtkScale *scale = GTK_SCALE (buildable);
+  MarksSubparserData *marks_data;
+  GtkWidget *toplevel;
+
+  if (strcmp (tagname, "marks") == 0)
+    {
+      GSList *m;
+      gchar *markup;
+
+      marks_data = (MarksSubparserData *)user_data;
+
+      for (m = marks_data->marks; m; m = m->next)
+        {
+          MarkData *mdata = m->data;
+
+          if (mdata->translatable && mdata->markup->len)
+            markup = _gtk_builder_parser_translate (gtk_builder_get_translation_domain (builder),
+                                                    mdata->context,
+                                                    mdata->markup->str);
+          else
+            markup = mdata->markup->str;
+
+          gtk_scale_add_mark (scale, mdata->value, mdata->position, markup);
+
+          mark_data_free (mdata);
+        }
+
+      g_slist_free (marks_data->marks);
+      g_slice_free (MarksSubparserData, marks_data);
+    }
+}
 
 #define __GTK_SCALE_C__
 #include "gtkaliasdef.c"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]