| /* gtkcelllayout.c |
| * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 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 |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /** |
| * SECTION:gtkcelllayout |
| * @Short_Description: An interface for packing cells |
| * @Title: GtkCellLayout |
| * |
| * #GtkCellLayout is an interface to be implemented by all objects which |
| * want to provide a #GtkTreeViewColumn like API for packing cells, |
| * setting attributes and data funcs. |
| * |
| * One of the notable features provided by implementations of |
| * GtkCellLayout are attributes. Attributes let you set the properties |
| * in flexible ways. They can just be set to constant values like regular |
| * properties. But they can also be mapped to a column of the underlying |
| * tree model with gtk_cell_layout_set_attributes(), which means that the value |
| * of the attribute can change from cell to cell as they are rendered by |
| * the cell renderer. Finally, it is possible to specify a function with |
| * gtk_cell_layout_set_cell_data_func() that is called to determine the |
| * value of the attribute for each cell that is rendered. |
| * |
| * # GtkCellLayouts as GtkBuildable |
| * |
| * Implementations of GtkCellLayout which also implement the GtkBuildable |
| * interface (#GtkCellView, #GtkIconView, #GtkComboBox, |
| * #GtkEntryCompletion, #GtkTreeViewColumn) accept GtkCellRenderer objects |
| * as <child> elements in UI definitions. They support a custom <attributes> |
| * element for their children, which can contain multiple <attribute> |
| * elements. Each <attribute> element has a name attribute which specifies |
| * a property of the cell renderer; the content of the element is the |
| * attribute value. |
| * |
| * This is an example of a UI definition fragment specifying attributes: |
| * |[ |
| * <object class="GtkCellView"> |
| * <child> |
| * <object class="GtkCellRendererText"/> |
| * <attributes> |
| * <attribute name="text">0</attribute> |
| * </attributes> |
| * </child>" |
| * </object> |
| * ]| |
| * |
| * Furthermore for implementations of GtkCellLayout that use a #GtkCellArea |
| * to lay out cells (all GtkCellLayouts in GTK+ use a GtkCellArea) |
| * [cell properties][cell-properties] can also be defined in the format by |
| * specifying the custom <cell-packing> attribute which can contain multiple |
| * <property> elements defined in the normal way. |
| * |
| * Here is a UI definition fragment specifying cell properties: |
| * |
| * |[ |
| * <object class="GtkTreeViewColumn"> |
| * <child> |
| * <object class="GtkCellRendererText"/> |
| * <cell-packing> |
| * <property name="align">True</property> |
| * <property name="expand">False</property> |
| * </cell-packing> |
| * </child>" |
| * </object> |
| * ]| |
| * |
| * # Subclassing GtkCellLayout implementations |
| * |
| * When subclassing a widget that implements #GtkCellLayout like |
| * #GtkIconView or #GtkComboBox, there are some considerations related |
| * to the fact that these widgets internally use a #GtkCellArea. |
| * The cell area is exposed as a construct-only property by these |
| * widgets. This means that it is possible to e.g. do |
| * |
| * |[<!-- language="C" --> |
| * combo = g_object_new (GTK_TYPE_COMBO_BOX, "cell-area", my_cell_area, NULL); |
| * ]| |
| * |
| * to use a custom cell area with a combo box. But construct properties |
| * are only initialized after instance init() |
| * functions have run, which means that using functions which rely on |
| * the existence of the cell area in your subclass’ init() function will |
| * cause the default cell area to be instantiated. In this case, a provided |
| * construct property value will be ignored (with a warning, to alert |
| * you to the problem). |
| * |
| * |[<!-- language="C" --> |
| * static void |
| * my_combo_box_init (MyComboBox *b) |
| * { |
| * GtkCellRenderer *cell; |
| * |
| * cell = gtk_cell_renderer_pixbuf_new (); |
| * // The following call causes the default cell area for combo boxes, |
| * // a GtkCellAreaBox, to be instantiated |
| * gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (b), cell, FALSE); |
| * ... |
| * } |
| * |
| * GtkWidget * |
| * my_combo_box_new (GtkCellArea *area) |
| * { |
| * // This call is going to cause a warning about area being ignored |
| * return g_object_new (MY_TYPE_COMBO_BOX, "cell-area", area, NULL); |
| * } |
| * ]| |
| * |
| * If supporting alternative cell areas with your derived widget is |
| * not important, then this does not have to concern you. If you want |
| * to support alternative cell areas, you can do so by moving the |
| * problematic calls out of init() and into a constructor() |
| * for your class. |
| */ |
| |
| #include "config.h" |
| #include <string.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include "gtkcelllayout.h" |
| #include "gtkbuilderprivate.h" |
| #include "gtkintl.h" |
| |
| #define warn_no_cell_area(func) \ |
| g_critical ("%s: Called but no GtkCellArea is available yet", func) |
| |
| typedef GtkCellLayoutIface GtkCellLayoutInterface; |
| G_DEFINE_INTERFACE (GtkCellLayout, gtk_cell_layout, G_TYPE_OBJECT); |
| |
| static void gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gboolean expand); |
| static void gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gboolean expand); |
| static void gtk_cell_layout_default_clear (GtkCellLayout *cell_layout); |
| static void gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| const gchar *attribute, |
| gint column); |
| static void gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| GtkCellLayoutDataFunc func, |
| gpointer func_data, |
| GDestroyNotify destroy); |
| static void gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell); |
| static void gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gint position); |
| static GList *gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout); |
| |
| |
| static void |
| gtk_cell_layout_default_init (GtkCellLayoutIface *iface) |
| { |
| iface->pack_start = gtk_cell_layout_default_pack_start; |
| iface->pack_end = gtk_cell_layout_default_pack_end; |
| iface->clear = gtk_cell_layout_default_clear; |
| iface->add_attribute = gtk_cell_layout_default_add_attribute; |
| iface->set_cell_data_func = gtk_cell_layout_default_set_cell_data_func; |
| iface->clear_attributes = gtk_cell_layout_default_clear_attributes; |
| iface->reorder = gtk_cell_layout_default_reorder; |
| iface->get_cells = gtk_cell_layout_default_get_cells; |
| } |
| |
| /* Default implementation is to fall back on an underlying cell area */ |
| static void |
| gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gboolean expand) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (area), cell, expand); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->pack_start()"); |
| } |
| } |
| |
| static void |
| gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gboolean expand) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (area), cell, expand); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->pack_end()"); |
| } |
| } |
| |
| static void |
| gtk_cell_layout_default_clear (GtkCellLayout *cell_layout) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| gtk_cell_layout_clear (GTK_CELL_LAYOUT (area)); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->clear()"); |
| } |
| } |
| |
| static void |
| gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| const gchar *attribute, |
| gint column) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (area), cell, attribute, column); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->add_attribute()"); |
| } |
| } |
| |
| static void |
| gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| GtkCellLayoutDataFunc func, |
| gpointer func_data, |
| GDestroyNotify destroy) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| _gtk_cell_area_set_cell_data_func_with_proxy (area, cell, |
| (GFunc)func, func_data, destroy, |
| cell_layout); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->set_cell_data_func()"); |
| } |
| } |
| |
| static void |
| gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (area), cell); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->clear_attributes()"); |
| } |
| } |
| |
| static void |
| gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gint position) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| gtk_cell_layout_reorder (GTK_CELL_LAYOUT (area), cell, position); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->reorder()"); |
| } |
| } |
| |
| static GList * |
| gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout) |
| { |
| GtkCellLayoutIface *iface; |
| GtkCellArea *area; |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| |
| if (iface->get_area) |
| { |
| area = iface->get_area (cell_layout); |
| |
| if (area) |
| return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); |
| else |
| warn_no_cell_area ("GtkCellLayoutIface->get_cells()"); |
| } |
| return NULL; |
| } |
| |
| |
| /** |
| * gtk_cell_layout_pack_start: |
| * @cell_layout: a #GtkCellLayout |
| * @cell: a #GtkCellRenderer |
| * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout |
| * |
| * Packs the @cell into the beginning of @cell_layout. If @expand is %FALSE, |
| * then the @cell is allocated no more space than it needs. Any unused space |
| * is divided evenly between cells for which @expand is %TRUE. |
| * |
| * Note that reusing the same cell renderer is not supported. |
| */ |
| void |
| gtk_cell_layout_pack_start (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gboolean expand) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); |
| |
| GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_start (cell_layout, cell, expand); |
| } |
| |
| /** |
| * gtk_cell_layout_pack_end: |
| * @cell_layout: a #GtkCellLayout |
| * @cell: a #GtkCellRenderer |
| * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout |
| * |
| * Adds the @cell to the end of @cell_layout. If @expand is %FALSE, then the |
| * @cell is allocated no more space than it needs. Any unused space is |
| * divided evenly between cells for which @expand is %TRUE. |
| * |
| * Note that reusing the same cell renderer is not supported. |
| */ |
| void |
| gtk_cell_layout_pack_end (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gboolean expand) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); |
| |
| GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_end (cell_layout, cell, expand); |
| } |
| |
| /** |
| * gtk_cell_layout_clear: |
| * @cell_layout: a #GtkCellLayout |
| * |
| * Unsets all the mappings on all renderers on @cell_layout and |
| * removes all renderers from @cell_layout. |
| */ |
| void |
| gtk_cell_layout_clear (GtkCellLayout *cell_layout) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| |
| GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear (cell_layout); |
| } |
| |
| static void |
| gtk_cell_layout_set_attributesv (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| va_list args) |
| { |
| gchar *attribute; |
| gint column; |
| |
| attribute = va_arg (args, gchar *); |
| |
| gtk_cell_layout_clear_attributes (cell_layout, cell); |
| |
| while (attribute != NULL) |
| { |
| column = va_arg (args, gint); |
| |
| gtk_cell_layout_add_attribute (cell_layout, cell, attribute, column); |
| |
| attribute = va_arg (args, gchar *); |
| } |
| } |
| |
| /** |
| * gtk_cell_layout_set_attributes: |
| * @cell_layout: a #GtkCellLayout |
| * @cell: a #GtkCellRenderer |
| * @...: a %NULL-terminated list of attributes |
| * |
| * Sets the attributes in list as the attributes of @cell_layout. |
| * |
| * The attributes should be in attribute/column order, as in |
| * gtk_cell_layout_add_attribute(). All existing attributes are |
| * removed, and replaced with the new attributes. |
| */ |
| void |
| gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| ...) |
| { |
| va_list args; |
| |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); |
| |
| va_start (args, cell); |
| gtk_cell_layout_set_attributesv (cell_layout, cell, args); |
| va_end (args); |
| } |
| |
| /** |
| * gtk_cell_layout_add_attribute: |
| * @cell_layout: a #GtkCellLayout |
| * @cell: a #GtkCellRenderer |
| * @attribute: an attribute on the renderer |
| * @column: the column position on the model to get the attribute from |
| * |
| * Adds an attribute mapping to the list in @cell_layout. |
| * |
| * The @column is the column of the model to get a value from, and the |
| * @attribute is the parameter on @cell to be set from the value. So for |
| * example if column 2 of the model contains strings, you could have the |
| * “text” attribute of a #GtkCellRendererText get its values from column 2. |
| */ |
| void |
| gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| const gchar *attribute, |
| gint column) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); |
| g_return_if_fail (attribute != NULL); |
| g_return_if_fail (column >= 0); |
| |
| GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->add_attribute (cell_layout, cell, attribute, column); |
| } |
| |
| /** |
| * gtk_cell_layout_set_cell_data_func: |
| * @cell_layout: a #GtkCellLayout |
| * @cell: a #GtkCellRenderer |
| * @func: (allow-none): the #GtkCellLayoutDataFunc to use, or %NULL |
| * @func_data: (closure): user data for @func |
| * @destroy: destroy notify for @func_data |
| * |
| * Sets the #GtkCellLayoutDataFunc to use for @cell_layout. |
| * |
| * This function is used instead of the standard attributes mapping |
| * for setting the column value, and should set the value of @cell_layout’s |
| * cell renderer(s) as appropriate. |
| * |
| * @func may be %NULL to remove a previously set function. |
| */ |
| void |
| gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| GtkCellLayoutDataFunc func, |
| gpointer func_data, |
| GDestroyNotify destroy) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); |
| |
| GTK_CELL_LAYOUT_GET_IFACE |
| (cell_layout)->set_cell_data_func (cell_layout, cell, func, func_data, destroy); |
| } |
| |
| /** |
| * gtk_cell_layout_clear_attributes: |
| * @cell_layout: a #GtkCellLayout |
| * @cell: a #GtkCellRenderer to clear the attribute mapping on |
| * |
| * Clears all existing attributes previously set with |
| * gtk_cell_layout_set_attributes(). |
| */ |
| void |
| gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); |
| |
| GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear_attributes (cell_layout, cell); |
| } |
| |
| /** |
| * gtk_cell_layout_reorder: |
| * @cell_layout: a #GtkCellLayout |
| * @cell: a #GtkCellRenderer to reorder |
| * @position: new position to insert @cell at |
| * |
| * Re-inserts @cell at @position. |
| * |
| * Note that @cell has already to be packed into @cell_layout |
| * for this to function properly. |
| */ |
| void |
| gtk_cell_layout_reorder (GtkCellLayout *cell_layout, |
| GtkCellRenderer *cell, |
| gint position) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); |
| |
| GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->reorder (cell_layout, cell, position); |
| } |
| |
| /** |
| * gtk_cell_layout_get_cells: |
| * @cell_layout: a #GtkCellLayout |
| * |
| * Returns the cell renderers which have been added to @cell_layout. |
| * |
| * Returns: (element-type GtkCellRenderer) (transfer container): |
| * a list of cell renderers. The list, but not the renderers has |
| * been newly allocated and should be freed with g_list_free() |
| * when no longer needed. |
| */ |
| GList * |
| gtk_cell_layout_get_cells (GtkCellLayout *cell_layout) |
| { |
| g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL); |
| |
| return GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->get_cells (cell_layout); |
| } |
| |
| /** |
| * gtk_cell_layout_get_area: |
| * @cell_layout: a #GtkCellLayout |
| * |
| * Returns the underlying #GtkCellArea which might be @cell_layout |
| * if called on a #GtkCellArea or might be %NULL if no #GtkCellArea |
| * is used by @cell_layout. |
| * |
| * Returns: (transfer none) (nullable): the cell area used by @cell_layout, |
| * or %NULL in case no cell area is used. |
| */ |
| GtkCellArea * |
| gtk_cell_layout_get_area (GtkCellLayout *cell_layout) |
| { |
| GtkCellLayoutIface *iface; |
| |
| g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL); |
| |
| iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); |
| if (iface->get_area) |
| return iface->get_area (cell_layout); |
| |
| return NULL; |
| } |
| |
| /* Attribute parsing */ |
| typedef struct { |
| GtkCellLayout *cell_layout; |
| GtkCellRenderer *renderer; |
| GtkBuilder *builder; |
| gchar *attr_name; |
| GString *string; |
| } AttributesSubParserData; |
| |
| static void |
| attributes_start_element (GMarkupParseContext *context, |
| const gchar *element_name, |
| const gchar **names, |
| const gchar **values, |
| gpointer user_data, |
| GError **error) |
| { |
| AttributesSubParserData *data = (AttributesSubParserData*)user_data; |
| |
| if (strcmp (element_name, "attribute") == 0) |
| { |
| const gchar *name; |
| |
| if (!_gtk_builder_check_parent (data->builder, context, "attributes", error)) |
| return; |
| |
| if (!g_markup_collect_attributes (element_name, names, values, error, |
| G_MARKUP_COLLECT_STRING, "name", &name, |
| G_MARKUP_COLLECT_INVALID)) |
| { |
| _gtk_builder_prefix_error (data->builder, context, error); |
| return; |
| } |
| |
| data->attr_name = g_strdup (name); |
| } |
| else if (strcmp (element_name, "attributes") == 0) |
| { |
| if (!_gtk_builder_check_parent (data->builder, context, "child", error)) |
| return; |
| |
| if (!g_markup_collect_attributes (element_name, names, values, error, |
| G_MARKUP_COLLECT_INVALID, NULL, NULL, |
| G_MARKUP_COLLECT_INVALID)) |
| _gtk_builder_prefix_error (data->builder, context, error); |
| } |
| else |
| { |
| _gtk_builder_error_unhandled_tag (data->builder, context, |
| "GtkCellLayout", element_name, |
| error); |
| } |
| } |
| |
| static void |
| attributes_text_element (GMarkupParseContext *context, |
| const gchar *text, |
| gsize text_len, |
| gpointer user_data, |
| GError **error) |
| { |
| AttributesSubParserData *data = (AttributesSubParserData*)user_data; |
| |
| if (data->attr_name) |
| g_string_append_len (data->string, text, text_len); |
| } |
| |
| static void |
| attributes_end_element (GMarkupParseContext *context, |
| const gchar *element_name, |
| gpointer user_data, |
| GError **error) |
| { |
| AttributesSubParserData *data = (AttributesSubParserData*)user_data; |
| GValue val = G_VALUE_INIT; |
| |
| if (!data->attr_name) |
| return; |
| |
| if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, data->string->str, &val, error)) |
| { |
| _gtk_builder_prefix_error (data->builder, context, error); |
| return; |
| } |
| |
| gtk_cell_layout_add_attribute (data->cell_layout, |
| data->renderer, |
| data->attr_name, |
| g_value_get_int (&val)); |
| |
| g_free (data->attr_name); |
| data->attr_name = NULL; |
| |
| g_string_set_size (data->string, 0); |
| } |
| |
| static const GMarkupParser attributes_parser = |
| { |
| attributes_start_element, |
| attributes_end_element, |
| attributes_text_element |
| }; |
| |
| |
| /* Cell packing parsing */ |
| static void |
| gtk_cell_layout_buildable_set_cell_property (GtkCellArea *area, |
| GtkBuilder *builder, |
| GtkCellRenderer *cell, |
| gchar *name, |
| const gchar *value) |
| { |
| GParamSpec *pspec; |
| GValue gvalue = G_VALUE_INIT; |
| GError *error = NULL; |
| |
| pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_GET_CLASS (area), name); |
| if (!pspec) |
| { |
| g_warning ("%s does not have a property called %s", |
| g_type_name (G_OBJECT_TYPE (area)), name); |
| return; |
| } |
| |
| if (!gtk_builder_value_from_string (builder, pspec, value, &gvalue, &error)) |
| { |
| g_warning ("Could not read property %s:%s with value %s of type %s: %s", |
| g_type_name (G_OBJECT_TYPE (area)), |
| name, |
| value, |
| g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), |
| error->message); |
| g_error_free (error); |
| return; |
| } |
| |
| gtk_cell_area_cell_set_property (area, cell, name, &gvalue); |
| g_value_unset (&gvalue); |
| } |
| |
| typedef struct { |
| GtkBuilder *builder; |
| GtkCellLayout *cell_layout; |
| GtkCellRenderer *renderer; |
| GString *string; |
| gchar *cell_prop_name; |
| gchar *context; |
| gboolean translatable; |
| } CellPackingSubParserData; |
| |
| static void |
| cell_packing_start_element (GMarkupParseContext *context, |
| const gchar *element_name, |
| const gchar **names, |
| const gchar **values, |
| gpointer user_data, |
| GError **error) |
| { |
| CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; |
| |
| if (strcmp (element_name, "property") == 0) |
| { |
| const gchar *name; |
| gboolean translatable = FALSE; |
| const gchar *ctx = NULL; |
| |
| if (!_gtk_builder_check_parent (data->builder, context, "cell-packing", error)) |
| return; |
| |
| if (!g_markup_collect_attributes (element_name, names, values, error, |
| G_MARKUP_COLLECT_STRING, "name", &name, |
| G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, |
| G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, |
| G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &ctx, |
| G_MARKUP_COLLECT_INVALID)) |
| { |
| _gtk_builder_prefix_error (data->builder, context, error); |
| return; |
| } |
| |
| data->cell_prop_name = g_strdup (name); |
| data->translatable = translatable; |
| data->context = g_strdup (ctx); |
| } |
| else if (strcmp (element_name, "cell-packing") == 0) |
| { |
| if (!_gtk_builder_check_parent (data->builder, context, "child", error)) |
| return; |
| |
| if (!g_markup_collect_attributes (element_name, names, values, error, |
| G_MARKUP_COLLECT_INVALID, NULL, NULL, |
| G_MARKUP_COLLECT_INVALID)) |
| _gtk_builder_prefix_error (data->builder, context, error); |
| } |
| else |
| { |
| _gtk_builder_error_unhandled_tag (data->builder, context, |
| "GtkCellLayout", element_name, |
| error); |
| } |
| } |
| |
| static void |
| cell_packing_text_element (GMarkupParseContext *context, |
| const gchar *text, |
| gsize text_len, |
| gpointer user_data, |
| GError **error) |
| { |
| CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; |
| |
| if (data->cell_prop_name) |
| g_string_append_len (data->string, text, text_len); |
| } |
| |
| static void |
| cell_packing_end_element (GMarkupParseContext *context, |
| const gchar *element_name, |
| gpointer user_data, |
| GError **error) |
| { |
| CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; |
| GtkCellArea *area; |
| |
| area = gtk_cell_layout_get_area (data->cell_layout); |
| |
| if (area) |
| { |
| /* translate the string */ |
| if (data->string->len && data->translatable) |
| { |
| const gchar *translated; |
| const gchar* domain; |
| |
| domain = gtk_builder_get_translation_domain (data->builder); |
| |
| translated = _gtk_builder_parser_translate (domain, |
| data->context, |
| data->string->str); |
| g_string_assign (data->string, translated); |
| } |
| |
| if (data->cell_prop_name) |
| gtk_cell_layout_buildable_set_cell_property (area, |
| data->builder, |
| data->renderer, |
| data->cell_prop_name, |
| data->string->str); |
| } |
| else |
| g_warning ("%s does not have an internal GtkCellArea class and cannot apply child cell properties", |
| g_type_name (G_OBJECT_TYPE (data->cell_layout))); |
| |
| g_string_set_size (data->string, 0); |
| g_free (data->cell_prop_name); |
| g_free (data->context); |
| data->cell_prop_name = NULL; |
| data->context = NULL; |
| data->translatable = FALSE; |
| } |
| |
| |
| static const GMarkupParser cell_packing_parser = |
| { |
| cell_packing_start_element, |
| cell_packing_end_element, |
| cell_packing_text_element |
| }; |
| |
| gboolean |
| _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, |
| GtkBuilder *builder, |
| GObject *child, |
| const gchar *tagname, |
| GMarkupParser *parser, |
| gpointer *data) |
| { |
| AttributesSubParserData *attr_data; |
| CellPackingSubParserData *packing_data; |
| |
| if (!child) |
| return FALSE; |
| |
| if (strcmp (tagname, "attributes") == 0) |
| { |
| attr_data = g_slice_new0 (AttributesSubParserData); |
| attr_data->cell_layout = GTK_CELL_LAYOUT (buildable); |
| attr_data->renderer = GTK_CELL_RENDERER (child); |
| attr_data->builder = builder; |
| attr_data->attr_name = NULL; |
| attr_data->string = g_string_new (""); |
| |
| *parser = attributes_parser; |
| *data = attr_data; |
| |
| return TRUE; |
| } |
| else if (strcmp (tagname, "cell-packing") == 0) |
| { |
| packing_data = g_slice_new0 (CellPackingSubParserData); |
| packing_data->string = g_string_new (""); |
| packing_data->builder = builder; |
| packing_data->cell_layout = GTK_CELL_LAYOUT (buildable); |
| packing_data->renderer = GTK_CELL_RENDERER (child); |
| |
| *parser = cell_packing_parser; |
| *data = packing_data; |
| |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| gboolean |
| _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, |
| GtkBuilder *builder, |
| GObject *child, |
| const gchar *tagname, |
| gpointer *data) |
| { |
| AttributesSubParserData *attr_data; |
| CellPackingSubParserData *packing_data; |
| |
| if (strcmp (tagname, "attributes") == 0) |
| { |
| attr_data = (AttributesSubParserData*)data; |
| g_assert (!attr_data->attr_name); |
| g_string_free (attr_data->string, TRUE); |
| g_slice_free (AttributesSubParserData, attr_data); |
| |
| return TRUE; |
| } |
| else if (strcmp (tagname, "cell-packing") == 0) |
| { |
| packing_data = (CellPackingSubParserData *)data; |
| g_string_free (packing_data->string, TRUE); |
| g_slice_free (CellPackingSubParserData, packing_data); |
| |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| void |
| _gtk_cell_layout_buildable_add_child (GtkBuildable *buildable, |
| GtkBuilder *builder, |
| GObject *child, |
| const gchar *type) |
| { |
| g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable)); |
| g_return_if_fail (GTK_IS_CELL_RENDERER (child)); |
| |
| gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE); |
| } |