Commit dc8f36e2 authored by Matthias Clasen's avatar Matthias Clasen

Support scale marks in builder markup

parent 6e51533e
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
......
......@@ -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>
......
......@@ -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>
......
......@@ -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"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment