summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/reference/gtk/gtk-docs.sgml3
-rw-r--r--docs/reference/gtk/gtk3-sections.txt119
-rw-r--r--docs/reference/gtk/gtk3.types3
-rw-r--r--docs/reference/gtk/tmpl/gtkcelllayout.sgml186
-rw-r--r--gtk/Makefile.am8
-rw-r--r--gtk/gtk.h3
-rw-r--r--gtk/gtk.symbols69
-rw-r--r--gtk/gtkcellarea.c3618
-rw-r--r--gtk/gtkcellarea.h463
-rw-r--r--gtk/gtkcellareabox.c2089
-rw-r--r--gtk/gtkcellareabox.h84
-rw-r--r--gtk/gtkcellareaboxcontext.c853
-rw-r--r--gtk/gtkcellareaboxcontext.h138
-rw-r--r--gtk/gtkcellareacontext.c674
-rw-r--r--gtk/gtkcellareacontext.h133
-rw-r--r--gtk/gtkcelllayout.c437
-rw-r--r--gtk/gtkcelllayout.h18
-rw-r--r--gtk/gtkcellrenderer.c129
-rw-r--r--gtk/gtkcellrenderer.h13
-rw-r--r--gtk/gtkcellrendererspinner.c7
-rw-r--r--gtk/gtkcellrenderertext.c76
-rw-r--r--gtk/gtkcellrenderertoggle.c7
-rw-r--r--gtk/gtkcombobox.c8
-rw-r--r--gtk/gtkentrycompletion.c275
-rw-r--r--gtk/gtkentrycompletion.h2
-rw-r--r--gtk/gtkentryprivate.h1
-rw-r--r--gtk/gtkfilechooserdefault.c6
-rw-r--r--gtk/gtkmarshalers.list4
-rw-r--r--gtk/gtktreeprivate.h451
-rw-r--r--gtk/gtktreeselection.c198
-rw-r--r--gtk/gtktreeview.c1614
-rw-r--r--gtk/gtktreeviewcolumn.c2686
-rw-r--r--gtk/gtktreeviewcolumn.h56
-rw-r--r--gtk/tests/treeview.c80
-rw-r--r--modules/other/gail/gailtreeview.c2
-rw-r--r--tests/Makefile.am11
-rw-r--r--tests/cellareascaffold.c1140
-rw-r--r--tests/cellareascaffold.h79
-rw-r--r--tests/testcellarea.c627
-rw-r--r--tests/testtooltips.c2
-rw-r--r--tests/testtreeedit.c115
-rw-r--r--tests/testtreeview.c5
-rw-r--r--tests/testverticalcells.c378
43 files changed, 13662 insertions, 3208 deletions
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml
index 3671b672e9..39b9624c0a 100644
--- a/docs/reference/gtk/gtk-docs.sgml
+++ b/docs/reference/gtk/gtk-docs.sgml
@@ -151,6 +151,9 @@
<xi:include href="xml/gtktreemodelsort.xml" />
<xi:include href="xml/gtktreemodelfilter.xml" />
<xi:include href="xml/gtkcelllayout.xml" />
+ <xi:include href="xml/gtkcellarea.xml" />
+ <xi:include href="xml/gtkcellareabox.xml" />
+ <xi:include href="xml/gtkcellareacontext.xml" />
<xi:include href="xml/gtkcellrenderer.xml" />
<xi:include href="xml/gtkcelleditable.xml" />
<xi:include href="xml/gtkcellrendereraccel.xml" />
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 88a6d4d90d..6fefb960ad 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -1119,6 +1119,7 @@ gtk_entry_buffer_get_type
GtkEntryCompletion
GtkEntryCompletionMatchFunc
gtk_entry_completion_new
+gtk_entry_completion_new_with_area
gtk_entry_completion_get_entry
gtk_entry_completion_set_model
gtk_entry_completion_get_model
@@ -4145,6 +4146,7 @@ GtkTreeViewColumnSizing
GtkTreeCellDataFunc
GtkTreeViewColumn
gtk_tree_view_column_new
+gtk_tree_view_column_new_with_area
gtk_tree_view_column_new_with_attributes
gtk_tree_view_column_pack_start
gtk_tree_view_column_pack_end
@@ -4364,6 +4366,7 @@ GtkCellLayoutIface
GtkCellLayoutDataFunc
gtk_cell_layout_pack_start
gtk_cell_layout_pack_end
+gtk_cell_layout_get_area
gtk_cell_layout_get_cells
gtk_cell_layout_reorder
gtk_cell_layout_clear
@@ -4381,12 +4384,127 @@ gtk_cell_layout_get_type
</SECTION>
<SECTION>
+<FILE>gtkcellarea</FILE>
+<TITLE>GtkCellArea</TITLE>
+GtkCellArea
+GtkCellAreaClass
+GtkCellCallback
+GtkCellAllocCallback
+GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID
+gtk_cell_area_add
+gtk_cell_area_remove
+gtk_cell_area_has_renderer
+gtk_cell_area_foreach
+gtk_cell_area_foreach_alloc
+gtk_cell_area_event
+gtk_cell_area_render
+gtk_cell_area_set_style_detail
+gtk_cell_area_get_style_detail
+gtk_cell_area_get_cell_allocation
+gtk_cell_area_get_cell_at_position
+gtk_cell_area_create_context
+gtk_cell_area_copy_context
+gtk_cell_area_get_request_mode
+gtk_cell_area_get_preferred_width
+gtk_cell_area_get_preferred_height_for_width
+gtk_cell_area_get_preferred_height
+gtk_cell_area_get_preferred_width_for_height
+gtk_cell_area_get_current_path_string
+gtk_cell_area_apply_attributes
+gtk_cell_area_attribute_connect
+gtk_cell_area_attribute_disconnect
+gtk_cell_area_class_install_cell_property
+gtk_cell_area_class_find_cell_property
+gtk_cell_area_class_list_cell_properties
+gtk_cell_area_add_with_properties
+gtk_cell_area_cell_set
+gtk_cell_area_cell_get
+gtk_cell_area_cell_set_valist
+gtk_cell_area_cell_get_valist
+gtk_cell_area_cell_set_property
+gtk_cell_area_cell_get_property
+gtk_cell_area_is_activatable
+gtk_cell_area_activate
+gtk_cell_area_focus
+gtk_cell_area_set_focus_cell
+gtk_cell_area_get_focus_cell
+gtk_cell_area_add_focus_sibling
+gtk_cell_area_remove_focus_sibling
+gtk_cell_area_is_focus_sibling
+gtk_cell_area_get_focus_siblings
+gtk_cell_area_get_focus_from_sibling
+gtk_cell_area_get_edited_cell
+gtk_cell_area_get_edit_widget
+gtk_cell_area_activate_cell
+gtk_cell_area_stop_editing
+gtk_cell_area_inner_cell_area
+gtk_cell_area_request_renderer
+<SUBSECTION Standard>
+GTK_CELL_AREA
+GTK_IS_CELL_AREA
+GTK_TYPE_CELL_AREA
+gtk_cell_area_get_type
+GTK_CELL_AREA_CLASS
+GTK_IS_CELL_AREA_CLASS
+GTK_CELL_AREA_GET_CLASS
+GtkCellAreaPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gtkcellareacontext</FILE>
+<TITLE>GtkCellAreaContext</TITLE>
+GtkCellAreaContextClass
+GtkCellAreaContext
+gtk_cell_area_context_get_area
+gtk_cell_area_context_allocate
+gtk_cell_area_context_reset
+gtk_cell_area_context_get_preferred_width
+gtk_cell_area_context_get_preferred_height
+gtk_cell_area_context_get_preferred_height_for_width
+gtk_cell_area_context_get_preferred_width_for_height
+gtk_cell_area_context_get_allocation
+gtk_cell_area_context_push_preferred_width
+gtk_cell_area_context_push_preferred_height
+<SUBSECTION Standard>
+GTK_CELL_AREA_CONTEXT
+GTK_IS_CELL_AREA_CONTEXT
+GTK_TYPE_CELL_AREA_CONTEXT
+gtk_cell_area_context_get_type
+GTK_CELL_AREA_CONTEXT_CLASS
+GTK_IS_CELL_AREA_CONTEXT_CLASS
+GTK_CELL_AREA_CONTEXT_GET_CLASS
+GtkCellAreaContextPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gtkcellareabox</FILE>
+<TITLE>GtkCellAreaBox</TITLE>
+GtkCellAreaBox
+GtkCellAreaBoxClass
+gtk_cell_area_box_new
+gtk_cell_area_box_pack_start
+gtk_cell_area_box_pack_end
+gtk_cell_area_box_get_spacing
+gtk_cell_area_box_set_spacing
+<SUBSECTION Standard>
+GTK_CELL_AREA_BOX
+GTK_IS_CELL_AREA_BOX
+GTK_TYPE_CELL_AREA_BOX
+gtk_cell_area_box_get_type
+GTK_CELL_AREA_BOX_CLASS
+GTK_IS_CELL_AREA_BOX_CLASS
+GTK_CELL_AREA_BOX_GET_CLASS
+GtkCellAreaBoxPrivate
+</SECTION>
+
+<SECTION>
<FILE>gtkcellrenderer</FILE>
<TITLE>GtkCellRenderer</TITLE>
GtkCellRendererState
GtkCellRendererMode
GtkCellRenderer
GtkCellRendererClass
+gtk_cell_renderer_get_aligned_area
gtk_cell_renderer_get_size
gtk_cell_renderer_render
gtk_cell_renderer_activate
@@ -4402,6 +4520,7 @@ gtk_cell_renderer_get_alignment
gtk_cell_renderer_set_alignment
gtk_cell_renderer_get_padding
gtk_cell_renderer_set_padding
+gtk_cell_renderer_is_activatable
<SUBSECTION Width-for-height>
gtk_cell_renderer_get_preferred_height
diff --git a/docs/reference/gtk/gtk3.types b/docs/reference/gtk/gtk3.types
index c385a37c48..fe0ff45f6b 100644
--- a/docs/reference/gtk/gtk3.types
+++ b/docs/reference/gtk/gtk3.types
@@ -26,6 +26,9 @@ gtk_buildable_get_type
gtk_button_box_get_type
gtk_button_get_type
gtk_calendar_get_type
+gtk_cell_area_get_type
+gtk_cell_area_box_get_type
+gtk_cell_area_context_get_type
gtk_cell_editable_get_type
gtk_cell_layout_get_type
gtk_cell_renderer_accel_get_type
diff --git a/docs/reference/gtk/tmpl/gtkcelllayout.sgml b/docs/reference/gtk/tmpl/gtkcelllayout.sgml
deleted file mode 100644
index 166d2cb996..0000000000
--- a/docs/reference/gtk/tmpl/gtkcelllayout.sgml
+++ /dev/null
@@ -1,186 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-GtkCellLayout
-
-<!-- ##### SECTION Short_Description ##### -->
-An interface for packing cells
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#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.
-</para>
-
-<para>
-One of the notable features provided by implementations of GtkCellLayout
-are <emphasis>attributes</emphasis>. 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.
-</para>
-
-<refsect2 id="GtkCellLayout-BUILDER-UI">
-<title>GtkCellLayouts as GtkBuildable</title>
-<para>
-Implementations of GtkCellLayout which also implement the GtkBuildable
-interface (#GtkCellView, #GtkIconView, #GtkComboBox, #GtkComboBoxEntry,
-#GtkEntryCompletion, #GtkTreeViewColumn) accept GtkCellRenderer objects
-as &lt;child&gt; elements in UI definitions. They support a custom
-&lt;attributes&gt; element for their children, which can contain
-multiple &lt;attribute&gt; elements. Each &lt;attribute&gt; element has
-a name attribute which specifies a property of the cell renderer; the
-content of the element is the attribute value.
-</para>
-<example>
-<title>A UI definition fragment specifying attributes</title>
-<programlisting><![CDATA[
-<object class="GtkCellView">
- <child>
- <object class="GtkCellRendererText"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>"
-</object>
-]]></programlisting>
-</example>
-</refsect2>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### SECTION Image ##### -->
-
-
-<!-- ##### STRUCT GtkCellLayout ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### STRUCT GtkCellLayoutIface ##### -->
-<para>
-
-</para>
-
-@g_iface:
-@pack_start:
-@pack_end:
-@clear:
-@add_attribute:
-@set_cell_data_func:
-@clear_attributes:
-@reorder:
-@get_cells:
-
-<!-- ##### USER_FUNCTION GtkCellLayoutDataFunc ##### -->
-<para>
-A function which should set the value of @cell_layout's cell renderer(s)
-as appropriate.
-</para>
-
-@cell_layout: a #GtkCellLayout
-@cell: the cell renderer whose value is to be set
-@tree_model: the model
-@iter: a #GtkTreeIter indicating the row to set the value for
-@data: user data passed to gtk_cell_layout_set_cell_data_func()
-
-
-<!-- ##### FUNCTION gtk_cell_layout_pack_start ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@cell:
-@expand:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_pack_end ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@cell:
-@expand:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_get_cells ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@Returns:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_reorder ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@cell:
-@position:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_clear ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_set_attributes ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@cell:
-@Varargs:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_add_attribute ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@cell:
-@attribute:
-@column:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_set_cell_data_func ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@cell:
-@func:
-@func_data:
-@destroy:
-
-
-<!-- ##### FUNCTION gtk_cell_layout_clear_attributes ##### -->
-<para>
-
-</para>
-
-@cell_layout:
-@cell:
-
-
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 9fa5059c27..cbf8b3d89e 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -173,6 +173,10 @@ gtk_public_h_sources = \
gtkbuildable.h \
gtkbutton.h \
gtkcalendar.h \
+ gtkcellarea.h \
+ gtkcellareabox.h \
+ gtkcellareaboxcontext.h \
+ gtkcellareacontext.h \
gtkcelleditable.h \
gtkcelllayout.h \
gtkcellrenderer.h \
@@ -470,6 +474,10 @@ gtk_base_c_sources = \
gtkbuilderparser.c \
gtkbutton.c \
gtkcalendar.c \
+ gtkcellarea.c \
+ gtkcellareabox.c \
+ gtkcellareaboxcontext.c \
+ gtkcellareacontext.c \
gtkcelleditable.c \
gtkcelllayout.c \
gtkcellrenderer.c \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 3b1155d7fb..0d85793a3c 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -57,6 +57,9 @@
#include <gtk/gtkbuilder.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkcalendar.h>
+#include <gtk/gtkcellarea.h>
+#include <gtk/gtkcellareabox.h>
+#include <gtk/gtkcellareacontext.h>
#include <gtk/gtkcelleditable.h>
#include <gtk/gtkcelllayout.h>
#include <gtk/gtkcellrenderer.h>
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index bb206fa36d..d121000676 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -359,6 +359,70 @@ gtk_calendar_set_detail_height_rows
gtk_calendar_set_detail_width_chars
gtk_calendar_set_display_options
gtk_calendar_unmark_day
+gtk_cell_area_activate
+gtk_cell_area_activate_cell
+gtk_cell_area_add
+gtk_cell_area_add_focus_sibling
+gtk_cell_area_add_with_properties
+gtk_cell_area_apply_attributes
+gtk_cell_area_attribute_connect
+gtk_cell_area_attribute_disconnect
+gtk_cell_area_box_get_spacing
+gtk_cell_area_box_get_type G_GNUC_CONST
+gtk_cell_area_box_new
+gtk_cell_area_box_pack_end
+gtk_cell_area_box_pack_start
+gtk_cell_area_box_set_spacing
+gtk_cell_area_cell_get
+gtk_cell_area_cell_get_property
+gtk_cell_area_cell_get_valist
+gtk_cell_area_cell_set
+gtk_cell_area_cell_set_property
+gtk_cell_area_cell_set_valist
+gtk_cell_area_class_find_cell_property
+gtk_cell_area_class_install_cell_property
+gtk_cell_area_class_list_cell_properties
+gtk_cell_area_context_allocate
+gtk_cell_area_context_get_allocation
+gtk_cell_area_context_get_area
+gtk_cell_area_context_get_preferred_height
+gtk_cell_area_context_get_preferred_height_for_width
+gtk_cell_area_context_get_preferred_width
+gtk_cell_area_context_get_preferred_width_for_height
+gtk_cell_area_context_get_type G_GNUC_CONST
+gtk_cell_area_context_push_preferred_width
+gtk_cell_area_context_push_preferred_height
+gtk_cell_area_context_reset
+gtk_cell_area_copy_context
+gtk_cell_area_create_context
+gtk_cell_area_event
+gtk_cell_area_foreach
+gtk_cell_area_focus
+gtk_cell_area_get_edited_cell
+gtk_cell_area_get_edit_widget
+gtk_cell_area_get_focus_cell
+gtk_cell_area_get_focus_from_sibling
+gtk_cell_area_get_focus_siblings
+gtk_cell_area_get_cell_allocation
+gtk_cell_area_get_current_path_string
+gtk_cell_area_get_preferred_height
+gtk_cell_area_get_preferred_height_for_width
+gtk_cell_area_get_preferred_width
+gtk_cell_area_get_preferred_width_for_height
+gtk_cell_area_get_request_mode
+gtk_cell_area_get_style_detail
+gtk_cell_area_get_type G_GNUC_CONST
+gtk_cell_area_has_renderer
+gtk_cell_area_inner_cell_area
+gtk_cell_area_is_activatable
+gtk_cell_area_is_focus_sibling
+gtk_cell_area_remove
+gtk_cell_area_remove_focus_sibling
+gtk_cell_area_render
+gtk_cell_area_request_renderer
+gtk_cell_area_set_focus_cell
+gtk_cell_area_set_style_detail
+gtk_cell_area_stop_editing
gtk_cell_editable_editing_done
gtk_cell_editable_get_type G_GNUC_CONST
gtk_cell_editable_remove_widget
@@ -366,6 +430,7 @@ gtk_cell_editable_start_editing
gtk_cell_layout_add_attribute
gtk_cell_layout_clear
gtk_cell_layout_clear_attributes
+gtk_cell_layout_get_area
gtk_cell_layout_get_cells
gtk_cell_layout_get_type G_GNUC_CONST
gtk_cell_layout_pack_end
@@ -379,6 +444,7 @@ gtk_cell_renderer_accel_new
gtk_cell_renderer_activate
gtk_cell_renderer_combo_get_type G_GNUC_CONST
gtk_cell_renderer_combo_new
+gtk_cell_renderer_get_aligned_area
gtk_cell_renderer_get_alignment
gtk_cell_renderer_get_fixed_size
gtk_cell_renderer_get_padding
@@ -392,6 +458,7 @@ gtk_cell_renderer_get_sensitive
gtk_cell_renderer_get_size
gtk_cell_renderer_get_type G_GNUC_CONST
gtk_cell_renderer_get_visible
+gtk_cell_renderer_is_activatable
gtk_cell_renderer_mode_get_type G_GNUC_CONST
gtk_cell_renderer_pixbuf_get_type G_GNUC_CONST
gtk_cell_renderer_pixbuf_new
@@ -731,6 +798,7 @@ gtk_entry_completion_insert_action_markup
gtk_entry_completion_insert_action_text
gtk_entry_completion_insert_prefix
gtk_entry_completion_new
+gtk_entry_completion_new_with_area
gtk_entry_completion_set_inline_completion
gtk_entry_completion_set_inline_selection
gtk_entry_completion_set_match_func
@@ -3131,6 +3199,7 @@ gtk_tree_view_column_get_visible
gtk_tree_view_column_get_widget
gtk_tree_view_column_get_width
gtk_tree_view_column_new
+gtk_tree_view_column_new_with_area
gtk_tree_view_column_new_with_attributes G_GNUC_NULL_TERMINATED
gtk_tree_view_column_pack_end
gtk_tree_view_column_pack_start
diff --git a/gtk/gtkcellarea.c b/gtk/gtkcellarea.c
new file mode 100644
index 0000000000..fcf550ef82
--- /dev/null
+++ b/gtk/gtkcellarea.c
@@ -0,0 +1,3618 @@
+/* gtkcellarea.c
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gtkcellarea
+ * @Short_Description: An abstract class for laying out #GtkCellRenderers
+ * @Title: GtkCellArea
+ *
+ * The #GtkCellArea is an abstract class for #GtkCellLayout widgets (also referred
+ * to as "layouting widgets") to interface with an arbitrary number of #GtkCellRenderers
+ * and interact with the user for a given #GtkTreeModel row.
+ *
+ * The cell area handles events, focus navigation, drawing and wraps geometrical
+ * size requests and allocations for a given row of data.
+ *
+ * Usually users dont have to interact with the #GtkCellArea directly unless they
+ * are implementing a cell layouting widget themselves.
+ *
+ * <refsect2 id="cell-area-geometry-management">
+ * <title>Requesting area sizes</title>
+ * <para>
+ * As outlined in <link linkend="geometry-management">GtkWidget's
+ * geometry management section</link>, GTK+ uses a height-for-width
+ * geometry managemen system to compute the sizes of widgets and user
+ * interfaces. #GtkCellArea uses the same semantics to calculate the
+ * size of an area for an arbitrary number of #GtkTreeModel rows.
+ *
+ * When requesting the size of a cell area one needs to calculate
+ * the size for a handful of rows, this will be done differently by
+ * different layouting widgets. For instance a #GtkTreeViewColumn
+ * always lines up the areas from top to bottom while a #GtkIconView
+ * on the other hand might enforce that all areas received the same
+ * width and wrap the areas around, requesting height for more cell
+ * areas when allocated less width.
+ *
+ * It's also important for areas to maintain some cell
+ * alignments with areas rendered for adjacent rows (cells can
+ * appear "columnized" inside an area even when the size of
+ * cells are different in each row). For this reason the #GtkCellArea
+ * uses a #GtkCellAreaContext object to store the alignments
+ * and sizes along the way (as well as the overall largest minimum
+ * and natural size for all the rows which have been calculated
+ * with the said context).
+ *
+ * The #GtkCellAreaContext is an opaque object specific to the
+ * #GtkCellArea which created it (see gtk_cell_area_create_context()).
+ * The owning cell layouting widget can create as many contexts as
+ * it wishes to calculate sizes of rows which should receive the
+ * same size in at least one orientation (horizontally or vertically),
+ * however it's important that the same #GtkCellAreaContext which
+ * was used to request the sizes for a given #GtkTreeModel row be
+ * used when rendering or processing events for that row.
+ *
+ * In order to request the width of all the rows at the root level
+ * of a #GtkTreeModel one would do the following:
+ * <example>
+ * <title>Requesting the width of a hand full of GtkTreeModel rows.</title>
+ * <programlisting>
+ * GtkTreeIter iter;
+ * gint minimum_width;
+ * gint natural_width;
+ *
+ * valid = gtk_tree_model_get_iter_first (model, &iter);
+ * while (valid)
+ * {
+ * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
+ * gtk_cell_area_get_preferred_width (area, context, widget, NULL, NULL);
+ *
+ * valid = gtk_tree_model_iter_next (model, &iter);
+ * }
+ * gtk_cell_area_context_get_preferred_width (context, &minimum_width, &natural_width);
+ * </programlisting>
+ * </example>
+ * Note that in this example it's not important to observe the returned minimum and
+ * natural width of the area for each row unless the cell layouting object is actually
+ * interested in the widths of individual rows. The overall width is however stored
+ * in the accompanying #GtkCellAreaContext object and can be consulted at any time.
+ *
+ * This can be useful since #GtkCellLayout widgets usually have to support requesting
+ * and rendering rows in treemodels with an exceedingly large amount of rows. The
+ * #GtkCellLayout widget in that case would calculate the required width of the rows
+ * in an idle or timeout source (see g_timeout_add()) and when the widget is requested
+ * its actual width in #GtkWidgetClass.get_preferred_width() it can simply consult the
+ * width accumulated so far in the #GtkCellAreaContext object.
+ *
+ * A simple example where rows are rendered from top to bottom and take up the full
+ * width of the layouting widget would look like:
+ * <example>
+ * <title>A typical #GtkWidgetClass.get_preferred_width() for a layouting widget.</title>
+ * <programlisting>
+ * static void
+ * foo_get_preferred_width (GtkWidget *widget,
+ * gint *minimum_size,
+ * gint *natural_size)
+ * {
+ * Foo *foo = FOO (widget);
+ * FooPrivate *priv = foo->priv;
+ *
+ * foo_ensure_at_least_one_handfull_of_rows_have_been_requested (foo);
+ *
+ * gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size);
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * In the above example the Foo widget has to make sure that some row sizes have
+ * been calculated (the amount of rows that Foo judged was appropriate to request
+ * space for in a single timeout iteration) before simply returning the amount
+ * of space required by the area via the #GtkCellAreaContext.
+ *
+ * Requesting the height for width (or width for height) of an area is a similar
+ * task except in this case the #GtkCellAreaContext does not store the data (actually
+ * it does not know how much space the layouting widget plans to allocate it for
+ * every row, it's up to the layouting widget to render each row of data with
+ * the appropriate height and width which was requested by the #GtkCellArea).
+ *
+ * In order to request the height for width of all the rows at the root level
+ * of a #GtkTreeModel one would do the following:
+ * <example>
+ * <title>Requesting the height for width of a hand full of GtkTreeModel rows.</title>
+ * <programlisting>
+ * GtkTreeIter iter;
+ * gint minimum_height;
+ * gint natural_height;
+ * gint full_minimum_height = 0;
+ * gint full_natural_height = 0;
+ *
+ * valid = gtk_tree_model_get_iter_first (model, &iter);
+ * while (valid)
+ * {
+ * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
+ * gtk_cell_area_get_preferred_height_for_width (area, context, widget,
+ * width, &minimum_height, &natural_height);
+ *
+ * if (width_is_for_allocation)
+ * cache_row_height (&iter, minimum_height, natural_height);
+ *
+ * full_minimum_height += minimum_height;
+ * full_natural_height += natural_height;
+ *
+ * valid = gtk_tree_model_iter_next (model, &iter);
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * Note that in the above example we would need to cache the heights returned for each
+ * treemodel row so that we would know what sizes to render the areas for each row. However
+ * we would only want to really cache the heights if the request is intended for the
+ * layouting widgets real allocation.
+ *
+ * In some cases the layouting widget is requested the height for an arbitrary for_width,
+ * this is a special case for layouting widgets who need to request size for tens of thousands
+ * of treemodel rows. For this case it's only important that the layouting widget calculate
+ * one reasonably sized chunk of rows and return that height synchronously. The reasoning here
+ * is that any layouting widget is at least capable of synchronously calculating enough
+ * height to fill the screen height (or scrolled window height) in response to a single call to
+ * #GtkWidgetClass.get_preferred_height_for_width(). Returning a perfect height for width that
+ * is larger than the screen area is inconsequential since after the layouting receives an
+ * allocation from a scrolled window it simply continues to drive the the scrollbar
+ * values while more and mode height is required for the row heights that are calculated
+ * in the background.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="cell-area-rendering">
+ * <title>Rendering Areas</title>
+ * <para>
+ * Once area sizes have been aquired at least for the rows in the visible area of the
+ * layouting widget they can be rendered at #GtkWidgetClass.draw() time.
+ *
+ * A crued example of how to render all the rows at the root level runs as follows:
+ * <example>
+ * <title>Requesting the width of a hand full of GtkTreeModel rows.</title>
+ * <programlisting>
+ * GtkAllocation allocation;
+ * GdkRectangle cell_area = { 0, };
+ * GtkTreeIter iter;
+ * gint minimum_width;
+ * gint natural_width;
+ *
+ * gtk_widget_get_allocation (widget, &allocation);
+ * cell_area.width = allocation.width;
+ *
+ * valid = gtk_tree_model_get_iter_first (model, &iter);
+ * while (valid)
+ * {
+ * cell_area.height = get_cached_height_for_row (&iter);
+ *
+ * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
+ * gtk_cell_area_render (area, context, widget, cr,
+ * &cell_area, &cell_area, state_flags, FALSE);
+ *
+ * cell_area.y += cell_area.height;
+ *
+ * valid = gtk_tree_model_iter_next (model, &iter);
+ * }
+ * </programlisting>
+ * </example>
+ * Note that the cached height in this example really depends on how the layouting
+ * widget works. The layouting widget might decide to give every row it's minimum
+ * or natural height or if the model content is expected to fit inside the layouting
+ * widget with no scrolled window it would make sense to calculate the allocation
+ * for each row at #GtkWidget.size_allocate() time using gtk_distribute_natural_allocation().
+ * </para>
+ * </refsect2>
+ * <refsect2 id="cell-area-events-and-focus">
+ * <title>Handling Events and Driving Keyboard Focus</title>
+ * <para>
+ * Passing events to the area is as simple as handling events on any normal
+ * widget and then passing them to the gtk_cell_area_event() api as they come
+ * in. Usually #GtkCellArea is only interested in button events, however some
+ * customized derived areas can be implemented who are interested in handling
+ * other events. Handling an event can trigger the #GtkCellArea::focus-changed
+ * signal to fire as well as #GtkCellArea::add-editable in the case that
+ * an editable cell was clicked and needs to start editing. You can call
+ * gtk_cell_area_stop_editing() at any time to cancel any cell editing
+ * that is currently in progress.
+ *
+ * The #GtkCellArea drives keyboard focus from cell to cell in a way similar
+ * to #GtkWidget. For layouting widgets that support giving focus to cells it's
+ * important to remember to pass %GTK_CELL_RENDERER_FOCUSED to the area functions
+ * for the row that has focus and to tell the area to paint the focus at render
+ * time.
+ *
+ * Layouting widgets that accept focus on cells should implement the #GtkWidgetClass.focus()
+ * virtual method. The layouting widget is always responsible for knowing where
+ * #GtkTreeModel rows are rendered inside the widget, so at #GtkWidgetClass.focus() time
+ * the layouting widget should use the #GtkCellArea methods to navigate focus inside the
+ * area and then observe the GtkDirectionType to pass the focus to adjacent rows and
+ * areas.
+ *
+ * A basic example of how the #GtkWidgetClass.focus() virtual method should be implemented:
+ * <example>
+ * <title>Implementing keyboard focus navigation when displaying rows from top to bottom.</title>
+ * <programlisting>
+ * static gboolean
+ * foo_focus (GtkWidget *widget,
+ * GtkDirectionType direction)
+ * {
+ * Foo *foo = FOO (widget);
+ * FooPrivate *priv = foo->priv;
+ * gint focus_row;
+ * gboolean have_focus = FALSE;
+ *
+ * focus_row = priv->focus_row;
+ *
+ * if (!gtk_widget_has_focus (widget))
+ * gtk_widget_grab_focus (widget);
+ *
+ * valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row);
+ * while (valid)
+ * {
+ * gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+ *
+ * if (gtk_cell_area_focus (priv->area, direction))
+ * {
+ * priv->focus_row = focus_row;
+ * have_focus = TRUE;
+ * break;
+ * }
+ * else
+ * {
+ * if (direction == GTK_DIR_RIGHT ||
+ * direction == GTK_DIR_LEFT)
+ * break;
+ * else if (direction == GTK_DIR_UP ||
+ * direction == GTK_DIR_TAB_BACKWARD)
+ * {
+ * if (focus_row == 0)
+ * break;
+ * else
+ * {
+ * focus_row--;
+ * valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
+ * }
+ * }
+ * else
+ * {
+ * if (focus_row == last_row)
+ * break;
+ * else
+ * {
+ * focus_row++;
+ * valid = gtk_tree_model_iter_next (priv->model, &iter);
+ * }
+ * }
+ * }
+ * }
+ * return have_focus;
+ * }
+ * </programlisting>
+ * </example>
+ * </para>
+ * </refsect2>
+ * <refsect2 id="cell-properties">
+ * <title>Cell Properties</title>
+ * <para>
+ * The #GtkCellArea introduces <emphasis>cell properties</emphasis> for #GtkCellRenderers in very
+ * much the same way that #GtkContainer introduces <link linkend="child-properties">child properties</link>
+ * for #GtkWidgets. This provides some general interfaces for defining the relationship cell areas
+ * have with their cells. For instance in a #GtkCellAreaBox a cell might "expand" and recieve extra
+ * space when the area is allocated more than it's full natural request, or a cell might be configured
+ * to "align" with adjacent rows which were requested and rendered with the same #GtkCellAreaContext.
+ *
+ * Use gtk_cell_area_class_install_cell_property() to install cell properties
+ * for a cell area class and gtk_cell_area_class_find_cell_property() or
+ * gtk_cell_area_class_list_cell_properties() to get information about existing
+ * cell properties.
+ *
+ * To set the value of a cell property, use gtk_cell_area_cell_set_property(),
+ * gtk_cell_area_cell_set() or gtk_cell_area_cell_set_valist().
+ * To obtain the value of a cell property, use
+ * gtk_cell_area_cell_get_property(), gtk_cell_area_cell_get() or
+ * gtk_cell_area_cell_get_valist().
+ * </para>
+ * </refsect2>
+ *
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "gtkintl.h"
+#include "gtkcelllayout.h"
+#include "gtkcellarea.h"
+#include "gtkcellareacontext.h"
+#include "gtkmarshalers.h"
+#include "gtkprivate.h"
+
+#include <gobject/gvaluecollector.h>
+
+
+/* GObjectClass */
+static void gtk_cell_area_dispose (GObject *object);
+static void gtk_cell_area_finalize (GObject *object);
+static void gtk_cell_area_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_cell_area_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* GtkCellAreaClass */
+static gint gtk_cell_area_real_event (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags);
+static void gtk_cell_area_real_render (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ cairo_t *cr,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean paint_focus);
+static void gtk_cell_area_real_apply_attributes (GtkCellArea *area,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gboolean is_expander,
+ gboolean is_expanded);
+static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+static gboolean gtk_cell_area_real_is_activatable (GtkCellArea *area);
+static gboolean gtk_cell_area_real_activate (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean edit_only);
+
+/* GtkCellLayoutIface */
+static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
+static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gboolean expand);
+static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
+static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ const gchar *attribute,
+ gint column);
+static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy);
+static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer);
+static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gint position);
+static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
+static GtkCellArea *gtk_cell_area_get_area (GtkCellLayout *cell_layout);
+
+/* GtkBuildableIface */
+static void gtk_cell_area_buildable_init (GtkBuildableIface *iface);
+static void gtk_cell_area_buildable_custom_tag_end (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer *data);
+
+/* Used in foreach loop to check if a child renderer is present */
+typedef struct {
+ GtkCellRenderer *renderer;
+ gboolean has_renderer;
+} HasRendererCheck;
+
+/* Used in foreach loop to get a cell's allocation */
+typedef struct {
+ GtkCellRenderer *renderer;
+ GdkRectangle allocation;
+} RendererAllocationData;
+
+/* Used in foreach loop to render cells */
+typedef struct {
+ GtkCellArea *area;
+ GtkWidget *widget;
+ cairo_t *cr;
+ GdkRectangle focus_rect;
+ GtkCellRendererState render_flags;
+ guint paint_focus : 1;
+ guint focus_all : 1;
+ guint first_focus : 1;
+} CellRenderData;
+
+/* Used in foreach loop to get a cell by position */
+typedef struct {
+ gint x;
+ gint y;
+ GtkCellRenderer *renderer;
+ GdkRectangle cell_area;
+} CellByPositionData;
+
+/* Attribute/Cell metadata */
+typedef struct {
+ const gchar *attribute;
+ gint column;
+} CellAttribute;
+
+typedef struct {
+ GSList *attributes;
+
+ GtkCellLayoutDataFunc func;
+ gpointer data;
+ GDestroyNotify destroy;
+} CellInfo;
+
+static CellInfo *cell_info_new (GtkCellLayoutDataFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+static void cell_info_free (CellInfo *info);
+static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer,
+ const gchar *attribute,
+ gint column);
+static void cell_attribute_free (CellAttribute *attribute);
+static gint cell_attribute_find (CellAttribute *cell_attribute,
+ const gchar *attribute);
+
+/* Internal functions/signal emissions */
+static void gtk_cell_area_add_editable (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *editable,
+ const GdkRectangle *cell_area);
+static void gtk_cell_area_remove_editable (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *editable);
+static void gtk_cell_area_set_edit_widget (GtkCellArea *area,
+ GtkCellEditable *editable);
+static void gtk_cell_area_set_edited_cell (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+
+
+/* Struct to pass data along while looping over
+ * cell renderers to apply attributes
+ */
+typedef struct {
+ GtkCellArea *area;
+ GtkTreeModel *model;
+ GtkTreeIter *iter;
+ gboolean is_expander;
+ gboolean is_expanded;
+} AttributeData;
+
+struct _GtkCellAreaPrivate
+{
+ /* The GtkCellArea bookkeeps any connected
+ * attributes in this hash table.
+ */
+ GHashTable *cell_info;
+
+ /* Current path is saved as a side-effect
+ * of gtk_cell_area_apply_attributes() */
+ gchar *current_path;
+
+ /* Current cell being edited and editable widget used */
+ GtkCellEditable *edit_widget;
+ GtkCellRenderer *edited_cell;
+
+ /* Signal connections to the editable widget */
+ gulong remove_widget_id;
+
+ /* Currently focused cell */
+ GtkCellRenderer *focus_cell;
+
+ /* Tracking which cells are focus siblings of focusable cells */
+ GHashTable *focus_siblings;
+
+ /* Detail string to pass to gtk_paint_*() functions */
+ gchar *style_detail;
+};
+
+enum {
+ PROP_0,
+ PROP_FOCUS_CELL,
+ PROP_EDITED_CELL,
+ PROP_EDIT_WIDGET
+};
+
+enum {
+ SIGNAL_APPLY_ATTRIBUTES,
+ SIGNAL_ADD_EDITABLE,
+ SIGNAL_REMOVE_EDITABLE,
+ SIGNAL_FOCUS_CHANGED,
+ LAST_SIGNAL
+};
+
+/* Keep the paramspec pool internal, no need to deliver notifications
+ * on cells. at least no percieved need for now */
+static GParamSpecPool *cell_property_pool = NULL;
+static guint cell_area_signals[LAST_SIGNAL] = { 0 };
+
+#define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
+#define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
+ gtk_cell_area_cell_layout_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+ gtk_cell_area_buildable_init))
+
+static void
+gtk_cell_area_init (GtkCellArea *area)
+{
+ GtkCellAreaPrivate *priv;
+
+ area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
+ GTK_TYPE_CELL_AREA,
+ GtkCellAreaPrivate);
+ priv = area->priv;
+
+ priv->cell_info = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)cell_info_free);
+
+ priv->focus_siblings = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)g_list_free);
+
+ priv->focus_cell = NULL;
+ priv->edited_cell = NULL;
+ priv->edit_widget = NULL;
+
+ priv->remove_widget_id = 0;
+}
+
+static void
+gtk_cell_area_class_init (GtkCellAreaClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ /* GObjectClass */
+ object_class->dispose = gtk_cell_area_dispose;
+ object_class->finalize = gtk_cell_area_finalize;
+ object_class->get_property = gtk_cell_area_get_property;
+ object_class->set_property = gtk_cell_area_set_property;
+
+ /* general */
+ class->add = NULL;
+ class->remove = NULL;
+ class->foreach = NULL;
+ class->event = gtk_cell_area_real_event;
+ class->render = gtk_cell_area_real_render;
+ class->apply_attributes = gtk_cell_area_real_apply_attributes;
+
+ /* geometry */
+ class->create_context = NULL;
+ class->get_request_mode = NULL;
+ class->get_preferred_width = NULL;
+ class->get_preferred_height = NULL;
+ class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
+ class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
+
+ /* focus */
+ class->is_activatable = gtk_cell_area_real_is_activatable;
+ class->activate = gtk_cell_area_real_activate;
+ class->focus = NULL;
+
+ /* Signals */
+ /**
+ * GtkCellArea::apply-attributes:
+ * @area: the #GtkCellArea to apply the attributes to
+ * @model: the #GtkTreeModel to apply the attributes from
+ * @iter: the #GtkTreeIter indicating which row to apply the attributes of
+ * @is_expander: whether the view shows children for this row
+ * @is_expanded: whether the view is currently showing the children of this row
+ *
+ * This signal is emitted whenever applying attributes to @area from @model
+ *
+ * Since: 3.0
+ */
+ cell_area_signals[SIGNAL_APPLY_ATTRIBUTES] =
+ g_signal_new (I_("apply-attributes"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkCellAreaClass, apply_attributes),
+ NULL, NULL,
+ _gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEAN,
+ G_TYPE_NONE, 4,
+ GTK_TYPE_TREE_MODEL,
+ GTK_TYPE_TREE_ITER,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+
+ /**
+ * GtkCellArea::add-editable:
+ * @area: the #GtkCellArea where editing started
+ * @renderer: the #GtkCellRenderer that started the edited
+ * @editable: the #GtkCellEditable widget to add
+ * @cell_area: the #GtkWidget relative #GdkRectangle coordinates
+ * where @editable should be added
+ * @path: the #GtkTreePath string this edit was initiated for
+ *
+ * Indicates that editing has started on @renderer and that @editable
+ * should be added to the owning cell layouting widget at @cell_area.
+ *
+ * Since: 3.0
+ */
+ cell_area_signals[SIGNAL_ADD_EDITABLE] =
+ g_signal_new (I_("add-editable"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* No class closure here */
+ NULL, NULL,
+ _gtk_marshal_VOID__OBJECT_OBJECT_BOXED_STRING,
+ G_TYPE_NONE, 4,
+ GTK_TYPE_CELL_RENDERER,
+ GTK_TYPE_CELL_EDITABLE,
+ GDK_TYPE_RECTANGLE,
+ G_TYPE_STRING);
+
+
+ /**
+ * GtkCellArea::remove-editable:
+ * @area: the #GtkCellArea where editing finished
+ * @renderer: the #GtkCellRenderer that finished editeding
+ * @editable: the #GtkCellEditable widget to remove
+ *
+ * Indicates that editing finished on @renderer and that @editable
+ * should be removed from the owning cell layouting widget.
+ *
+ * Since: 3.0
+ */
+ cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
+ g_signal_new (I_("remove-editable"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* No class closure here */
+ NULL, NULL,
+ _gtk_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_CELL_RENDERER,
+ GTK_TYPE_CELL_EDITABLE);
+
+ /**
+ * GtkCellArea::focus-changed:
+ * @area: the #GtkCellArea where focus changed
+ * @renderer: the #GtkCellRenderer that has focus
+ * @path: the current #GtkTreePath string set for @area
+ *
+ * Indicates that focus changed on this @area. This signal
+ * is emitted either as a result of focus handling or event
+ * handling.
+ *
+ * It's possible that the signal is emitted even if the
+ * currently focused renderer did not change, this is
+ * because focus may change to the same renderer in the
+ * same cell area for a different row of data.
+ *
+ * Since: 3.0
+ */
+ cell_area_signals[SIGNAL_FOCUS_CHANGED] =
+ g_signal_new (I_("focus-changed"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* No class closure here */
+ NULL, NULL,
+ _gtk_marshal_VOID__OBJECT_STRING,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_CELL_RENDERER,
+ G_TYPE_STRING);
+
+ /* Properties */
+ /**
+ * GtkCellArea:focus-cell:
+ *
+ * The cell in the area that currently has focus
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_FOCUS_CELL,
+ g_param_spec_object
+ ("focus-cell",
+ P_("Focus Cell"),
+ P_("The cell which currently has focus"),
+ GTK_TYPE_CELL_RENDERER,
+ GTK_PARAM_READWRITE));
+
+ /**
+ * GtkCellArea:edited-cell:
+ *
+ * The cell in the area that is currently edited
+ *
+ * This property is read-only and only changes as
+ * a result of a call gtk_cell_area_activate_cell().
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_EDITED_CELL,
+ g_param_spec_object
+ ("edited-cell",
+ P_("Edited Cell"),
+ P_("The cell which is currently being edited"),
+ GTK_TYPE_CELL_RENDERER,
+ G_PARAM_READABLE));
+
+ /**
+ * GtkCellArea:edit-widget:
+ *
+ * The widget currently editing the edited cell
+ *
+ * This property is read-only and only changes as
+ * a result of a call gtk_cell_area_activate_cell().
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_EDIT_WIDGET,
+ g_param_spec_object
+ ("edit-widget",
+ P_("Edit Widget"),
+ P_("The widget currently editing the edited cell"),
+ GTK_TYPE_CELL_RENDERER,
+ G_PARAM_READABLE));
+
+ /* Pool for Cell Properties */
+ if (!cell_property_pool)
+ cell_property_pool = g_param_spec_pool_new (FALSE);
+
+ g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
+}
+
+/*************************************************************
+ * CellInfo Basics *
+ *************************************************************/
+static CellInfo *
+cell_info_new (GtkCellLayoutDataFunc func,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ CellInfo *info = g_slice_new0 (CellInfo);
+
+ info->func = func;
+ info->data = data;
+ info->destroy = destroy;
+
+ return info;
+}
+
+static void
+cell_info_free (CellInfo *info)
+{
+ if (info->destroy)
+ info->destroy (info->data);
+
+ g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
+ g_slist_free (info->attributes);
+
+ g_slice_free (CellInfo, info);
+}
+
+static CellAttribute *
+cell_attribute_new (GtkCellRenderer *renderer,
+ const gchar *attribute,
+ gint column)
+{
+ GParamSpec *pspec;
+
+ /* Check if the attribute really exists and point to
+ * the property string installed on the cell renderer
+ * class (dont dup the string)
+ */
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
+
+ if (pspec)
+ {
+ CellAttribute *cell_attribute = g_slice_new (CellAttribute);
+
+ cell_attribute->attribute = pspec->name;
+ cell_attribute->column = column;
+
+ return cell_attribute;
+ }
+
+ return NULL;
+}
+
+static void
+cell_attribute_free (CellAttribute *attribute)
+{
+ g_slice_free (CellAttribute, attribute);
+}
+
+/* GCompareFunc for g_slist_find_custom() */
+static gint
+cell_attribute_find (CellAttribute *cell_attribute,
+ const gchar *attribute)
+{
+ return g_strcmp0 (cell_attribute->attribute, attribute);
+}
+
+/*************************************************************
+ * GObjectClass *
+ *************************************************************/
+static void
+gtk_cell_area_finalize (GObject *object)
+{
+ GtkCellArea *area = GTK_CELL_AREA (object);
+ GtkCellAreaPrivate *priv = area->priv;
+
+ /* All cell renderers should already be removed at this point,
+ * just kill our (empty) hash tables here.
+ */
+ g_hash_table_destroy (priv->cell_info);
+ g_hash_table_destroy (priv->focus_siblings);
+
+ g_free (priv->current_path);
+
+ G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
+}
+
+
+static void
+gtk_cell_area_dispose (GObject *object)
+{
+ /* This removes every cell renderer that may be added to the GtkCellArea,
+ * subclasses should be breaking references to the GtkCellRenderers
+ * at this point.
+ */
+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
+
+ /* Remove any ref to a focused/edited cell */
+ gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
+ gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
+ gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
+
+ G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
+}
+
+static void
+gtk_cell_area_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellArea *area = GTK_CELL_AREA (object);
+
+ switch (prop_id)
+ {
+ case PROP_FOCUS_CELL:
+ gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_cell_area_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellArea *area = GTK_CELL_AREA (object);
+ GtkCellAreaPrivate *priv = area->priv;
+
+ switch (prop_id)
+ {
+ case PROP_FOCUS_CELL:
+ g_value_set_object (value, priv->focus_cell);
+ break;
+ case PROP_EDITED_CELL:
+ g_value_set_object (value, priv->edited_cell);
+ break;
+ case PROP_EDIT_WIDGET:
+ g_value_set_object (value, priv->edit_widget);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*************************************************************
+ * GtkCellAreaClass *
+ *************************************************************/
+static gint
+gtk_cell_area_real_event (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ GtkCellAreaPrivate *priv = area->priv;
+ gboolean retval = FALSE;
+
+ if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0)
+ {
+ GdkEventKey *key_event = (GdkEventKey *)event;
+
+ /* Cancel any edits in progress */
+ if (priv->edited_cell && (key_event->keyval == GDK_KEY_Escape))
+ {
+ gtk_cell_area_stop_editing (area, TRUE);
+ retval = TRUE;
+ }
+ }
+ else if (event->type == GDK_BUTTON_PRESS)
+ {
+ GdkEventButton *button_event = (GdkEventButton *)event;
+
+ if (button_event->button == 1)
+ {
+ GtkCellRenderer *renderer = NULL;
+ GtkCellRenderer *focus_renderer;
+ GdkRectangle alloc_area;
+ gint event_x, event_y;
+
+ /* We may need some semantics to tell us the offset of the event
+ * window we are handling events for (i.e. GtkTreeView has a bin_window) */
+ event_x = button_event->x;
+ event_y = button_event->y;
+
+ /* Dont try to search for an event coordinate that is not in the area, that will
+ * trigger a runtime warning.
+ */
+ if (event_x >= cell_area->x && event_x <= cell_area->x + cell_area->width &&
+ event_y >= cell_area->y && event_y <= cell_area->y + cell_area->height)
+ renderer =
+ gtk_cell_area_get_cell_at_position (area, context, widget,
+ cell_area, event_x, event_y,
+ &alloc_area);
+
+ if (renderer)
+ {
+ focus_renderer = gtk_cell_area_get_focus_from_sibling (area, renderer);
+ if (!focus_renderer)
+ focus_renderer = renderer;
+
+ /* If we're already editing, cancel it and set focus */
+ if (gtk_cell_area_get_edited_cell (area))
+ {
+ /* XXX Was it really canceled in this case ? */
+ gtk_cell_area_stop_editing (area, TRUE);
+ gtk_cell_area_set_focus_cell (area, focus_renderer);
+ retval = TRUE;
+ }
+ else
+ {
+ /* If we are activating via a focus sibling,
+ * we need to fetch the right cell area for the real event renderer */
+ if (focus_renderer != renderer)
+ gtk_cell_area_get_cell_allocation (area, context, widget, focus_renderer,
+ cell_area, &alloc_area);
+
+ gtk_cell_area_set_focus_cell (area, focus_renderer);
+ retval = gtk_cell_area_activate_cell (area, widget, focus_renderer,
+ event, &alloc_area, flags);
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+static gboolean
+render_cell (GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *cell_background,
+ CellRenderData *data)
+{
+ GtkCellRenderer *focus_cell;
+ GtkCellRendererState flags;
+ GdkRectangle inner_area;
+
+ focus_cell = gtk_cell_area_get_focus_cell (data->area);
+ flags = data->render_flags;
+
+ gtk_cell_area_inner_cell_area (data->area, data->widget, cell_area, &inner_area);
+
+ if ((flags & GTK_CELL_RENDERER_FOCUSED) &&
+ (data->focus_all ||
+ (focus_cell &&
+ (renderer == focus_cell ||
+ gtk_cell_area_is_focus_sibling (data->area, focus_cell, renderer)))))
+ {
+ gint focus_line_width;
+ GdkRectangle cell_focus;
+
+ gtk_cell_renderer_get_aligned_area (renderer, data->widget, flags, &inner_area, &cell_focus);
+
+ gtk_widget_style_get (data->widget,
+ "focus-line-width", &focus_line_width,
+ NULL);
+
+ /* The focus rectangle is located around the aligned area of the cell */
+ cell_focus.x -= focus_line_width;
+ cell_focus.y -= focus_line_width;
+ cell_focus.width += 2 * focus_line_width;
+ cell_focus.height += 2 * focus_line_width;
+
+ if (data->first_focus)
+ {
+ data->first_focus = FALSE;
+ data->focus_rect = cell_focus;
+ }
+ else
+ {
+ gdk_rectangle_union (&data->focus_rect, &cell_focus, &data->focus_rect);
+ }
+ }
+ else
+ flags &= ~GTK_CELL_RENDERER_FOCUSED;
+
+ gtk_cell_renderer_render (renderer, data->cr, data->widget,
+ cell_background, &inner_area, flags);
+
+ return FALSE;
+}
+
+static void
+gtk_cell_area_real_render (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ cairo_t *cr,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean paint_focus)
+{
+ CellRenderData render_data =
+ {
+ area,
+ widget,
+ cr,
+ { 0, },
+ flags,
+ paint_focus,
+ FALSE, TRUE
+ };
+
+ /* Make sure we dont paint a focus rectangle while there
+ * is an editable widget in play
+ */
+ if (gtk_cell_area_get_edited_cell (area))
+ render_data.paint_focus = FALSE;
+
+ /* If no cell can activate but the caller wants focus painted,
+ * then we paint focus around all cells */
+ if ((flags & GTK_CELL_RENDERER_FOCUSED) != 0 && paint_focus &&
+ !gtk_cell_area_is_activatable (area))
+ render_data.focus_all = TRUE;
+
+ gtk_cell_area_foreach_alloc (area, context, widget, cell_area, background_area,
+ (GtkCellAllocCallback)render_cell, &render_data);
+
+ if (render_data.paint_focus &&
+ render_data.focus_rect.width != 0 &&
+ render_data.focus_rect.height != 0)
+ {
+ GtkStateType renderer_state =
+ flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
+ (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
+ (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
+
+ cairo_save (cr);
+
+ gdk_cairo_rectangle (cr, background_area);
+ cairo_clip (cr);
+
+ gtk_paint_focus (gtk_widget_get_style (widget), cr,
+ renderer_state, widget,
+ gtk_cell_area_get_style_detail (area),
+ render_data.focus_rect.x, render_data.focus_rect.y,
+ render_data.focus_rect.width, render_data.focus_rect.height);
+
+ cairo_restore (cr);
+ }
+}
+
+static void
+apply_cell_attributes (GtkCellRenderer *renderer,
+ CellInfo *info,
+ AttributeData *data)
+{
+ CellAttribute *attribute;
+ GSList *list;
+ GValue value = { 0, };
+ gboolean is_expander;
+ gboolean is_expanded;
+
+ g_object_freeze_notify (G_OBJECT (renderer));
+
+ /* Whether a row expands or is presently expanded can only be
+ * provided by the view (as these states can vary across views
+ * accessing the same model).
+ */
+ g_object_get (renderer, "is-expander", &is_expander, NULL);
+ if (is_expander != data->is_expander)
+ g_object_set (renderer, "is-expander", data->is_expander, NULL);
+
+ g_object_get (renderer, "is-expanded", &is_expanded, NULL);
+ if (is_expanded != data->is_expanded)
+ g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
+
+ /* Apply the attributes directly to the renderer */
+ for (list = info->attributes; list; list = list->next)
+ {
+ attribute = list->data;
+
+ gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
+ g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
+ g_value_unset (&value);
+ }
+
+ /* Call any GtkCellLayoutDataFunc that may have been set by the user
+ */
+ if (info->func)
+ info->func (GTK_CELL_LAYOUT (data->area), renderer,
+ data->model, data->iter, info->data);
+
+ g_object_thaw_notify (G_OBJECT (renderer));
+}
+
+static void
+gtk_cell_area_real_apply_attributes (GtkCellArea *area,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gboolean is_expander,
+ gboolean is_expanded)
+{
+
+ GtkCellAreaPrivate *priv;
+ AttributeData data;
+ GtkTreePath *path;
+
+ priv = area->priv;
+
+ /* Feed in data needed to apply to every renderer */
+ data.area = area;
+ data.model = tree_model;
+ data.iter = iter;
+ data.is_expander = is_expander;
+ data.is_expanded = is_expanded;
+
+ /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
+ * apply the data from the treemodel */
+ g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
+
+ /* Update the currently applied path */
+ g_free (priv->current_path);
+ path = gtk_tree_model_get_path (tree_model, iter);
+ priv->current_path = gtk_tree_path_to_string (path);
+ gtk_tree_path_free (path);
+}
+
+static void
+gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ /* If the area doesnt do height-for-width, fallback on base preferred height */
+ GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, context, widget, minimum_height, natural_height);
+}
+
+static void
+gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ /* If the area doesnt do width-for-height, fallback on base preferred width */
+ GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, context, widget, minimum_width, natural_width);
+}
+
+static gboolean
+get_is_activatable (GtkCellRenderer *renderer,
+ gboolean *activatable)
+{
+
+ if (gtk_cell_renderer_is_activatable (renderer))
+ *activatable = TRUE;
+
+ return *activatable;
+}
+
+static gboolean
+gtk_cell_area_real_is_activatable (GtkCellArea *area)
+{
+ gboolean activatable = FALSE;
+
+ /* Checks if any renderer can focus for the currently applied
+ * attributes.
+ *
+ * Subclasses can override this in the case that they are also
+ * rendering widgets as well as renderers.
+ */
+ gtk_cell_area_foreach (area, (GtkCellCallback)get_is_activatable, &activatable);
+
+ return activatable;
+}
+
+static gboolean
+gtk_cell_area_real_activate (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean edit_only)
+{
+ GtkCellAreaPrivate *priv = area->priv;
+ GdkRectangle renderer_area;
+ GtkCellRenderer *activate_cell = NULL;
+ GtkCellRendererMode mode;
+
+ if (priv->focus_cell)
+ {
+ g_object_get (priv->focus_cell, "mode", &mode, NULL);
+
+ if (gtk_cell_renderer_get_visible (priv->focus_cell) &&
+ (edit_only ? mode == GTK_CELL_RENDERER_MODE_EDITABLE :
+ mode != GTK_CELL_RENDERER_MODE_INERT))
+ activate_cell = priv->focus_cell;
+ }
+ else
+ {
+ GList *cells, *l;
+
+ /* GtkTreeView sometimes wants to activate a cell when no
+ * cells are in focus.
+ */
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
+ for (l = cells; l && !activate_cell; l = l->next)
+ {
+ GtkCellRenderer *renderer = l->data;
+
+ g_object_get (renderer, "mode", &mode, NULL);
+
+ if (gtk_cell_renderer_get_visible (renderer) &&
+ (edit_only ? mode == GTK_CELL_RENDERER_MODE_EDITABLE :
+ mode != GTK_CELL_RENDERER_MODE_INERT))
+ activate_cell = renderer;
+ }
+ g_list_free (cells);
+ }
+
+ if (activate_cell)
+ {
+ /* Get the allocation of the focused cell.
+ */
+ gtk_cell_area_get_cell_allocation (area, context, widget, activate_cell,
+ cell_area, &renderer_area);
+
+ /* Activate or Edit the cell
+ *
+ * Currently just not sending an event, renderers afaics dont use
+ * the event argument anyway, worst case is we can synthesize one.
+ */
+ if (gtk_cell_area_activate_cell (area, widget, activate_cell, NULL,
+ &renderer_area, flags))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*************************************************************
+ * GtkCellLayoutIface *
+ *************************************************************/
+static void
+gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
+{
+ iface->pack_start = gtk_cell_area_pack_default;
+ iface->pack_end = gtk_cell_area_pack_default;
+ iface->clear = gtk_cell_area_clear;
+ iface->add_attribute = gtk_cell_area_add_attribute;
+ iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
+ iface->clear_attributes = gtk_cell_area_clear_attributes;
+ iface->reorder = gtk_cell_area_reorder;
+ iface->get_cells = gtk_cell_area_get_cells;
+ iface->get_area = gtk_cell_area_get_area;
+}
+
+static void
+gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gboolean expand)
+{
+ gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
+}
+
+static void
+gtk_cell_area_clear (GtkCellLayout *cell_layout)
+{
+ GtkCellArea *area = GTK_CELL_AREA (cell_layout);
+ GList *l, *cells =
+ gtk_cell_layout_get_cells (cell_layout);
+
+ for (l = cells; l; l = l->next)
+ {
+ GtkCellRenderer *renderer = l->data;
+ gtk_cell_area_remove (area, renderer);
+ }
+
+ g_list_free (cells);
+}
+
+static void
+gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ const gchar *attribute,
+ gint column)
+{
+ gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
+ renderer, attribute, column);
+}
+
+static void
+gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy)
+{
+ GtkCellArea *area = GTK_CELL_AREA (cell_layout);
+ GtkCellAreaPrivate *priv = area->priv;
+ CellInfo *info;
+
+ info = g_hash_table_lookup (priv->cell_info, renderer);
+
+ if (info)
+ {
+ if (info->destroy && info->data)
+ info->destroy (info->data);
+
+ if (func)
+ {
+ info->func = func;
+ info->data = func_data;
+ info->destroy = destroy;
+ }
+ else
+ {
+ info->func = NULL;
+ info->data = NULL;
+ info->destroy = NULL;
+ }
+ }
+ else
+ {
+ info = cell_info_new (func, func_data, destroy);
+
+ g_hash_table_insert (priv->cell_info, renderer, info);
+ }
+}
+
+static void
+gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer)
+{
+ GtkCellArea *area = GTK_CELL_AREA (cell_layout);
+ GtkCellAreaPrivate *priv = area->priv;
+ CellInfo *info;
+
+ info = g_hash_table_lookup (priv->cell_info, renderer);
+
+ if (info)
+ {
+ g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
+ g_slist_free (info->attributes);
+
+ info->attributes = NULL;
+ }
+}
+
+static void
+gtk_cell_area_reorder (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gint position)
+{
+ g_warning ("GtkCellLayout::reorder not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
+}
+
+static gboolean
+accum_cells (GtkCellRenderer *renderer,
+ GList **accum)
+{
+ *accum = g_list_prepend (*accum, renderer);
+
+ return FALSE;
+}
+
+static GList *
+gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
+{
+ GList *cells = NULL;
+
+ gtk_cell_area_foreach (GTK_CELL_AREA (cell_layout),
+ (GtkCellCallback)accum_cells,
+ &cells);
+
+ return g_list_reverse (cells);
+}
+
+static GtkCellArea *
+gtk_cell_area_get_area (GtkCellLayout *cell_layout)
+{
+ return GTK_CELL_AREA (cell_layout);
+}
+
+/*************************************************************
+ * GtkBuildableIface *
+ *************************************************************/
+static void
+gtk_cell_area_buildable_init (GtkBuildableIface *iface)
+{
+ iface->add_child = _gtk_cell_layout_buildable_add_child;
+ iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
+ iface->custom_tag_end = gtk_cell_area_buildable_custom_tag_end;
+}
+
+static void
+gtk_cell_area_buildable_custom_tag_end (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer *data)
+{
+ /* Just ignore the boolean return from here */
+ _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data);
+}
+
+/*************************************************************
+ * API *
+ *************************************************************/
+
+/**
+ * gtk_cell_area_add:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer to add to @area
+ *
+ * Adds @renderer to @area with the default child cell properties.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_add (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->add)
+ class->add (area, renderer);
+ else
+ g_warning ("GtkCellAreaClass::add not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+/**
+ * gtk_cell_area_remove:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer to remove from @area
+ *
+ * Removes @renderer from @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_remove (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellAreaClass *class;
+ GtkCellAreaPrivate *priv;
+ GList *renderers, *l;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+ priv = area->priv;
+
+ /* Remove any custom attributes and custom cell data func here first */
+ g_hash_table_remove (priv->cell_info, renderer);
+
+ /* Remove focus siblings of this renderer */
+ g_hash_table_remove (priv->focus_siblings, renderer);
+
+ /* Remove this renderer from any focus renderer's sibling list */
+ renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
+
+ for (l = renderers; l; l = l->next)
+ {
+ GtkCellRenderer *focus_renderer = l->data;
+
+ if (gtk_cell_area_is_focus_sibling (area, focus_renderer, renderer))
+ {
+ gtk_cell_area_remove_focus_sibling (area, focus_renderer, renderer);
+ break;
+ }
+ }
+
+ g_list_free (renderers);
+
+ if (class->remove)
+ class->remove (area, renderer);
+ else
+ g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+static gboolean
+get_has_renderer (GtkCellRenderer *renderer,
+ HasRendererCheck *check)
+{
+ if (renderer == check->renderer)
+ check->has_renderer = TRUE;
+
+ return check->has_renderer;
+}
+
+/**
+ * gtk_cell_area_has_renderer:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer to check
+ *
+ * Checks if @area contains @renderer.
+ *
+ * Return value: %TRUE if @renderer is in the @area.
+ *
+ * Since: 3.0
+ */
+gboolean
+gtk_cell_area_has_renderer (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ HasRendererCheck check = { renderer, FALSE };
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
+
+ gtk_cell_area_foreach (area, (GtkCellCallback)get_has_renderer, &check);
+
+ return check.has_renderer;
+}
+
+/**
+ * gtk_cell_area_foreach:
+ * @area: a #GtkCellArea
+ * @callback: the #GtkCellCallback to call
+ * @callback_data: user provided data pointer
+ *
+ * Calls @callback for every #GtkCellRenderer in @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_foreach (GtkCellArea *area,
+ GtkCellCallback callback,
+ gpointer callback_data)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (callback != NULL);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->foreach)
+ class->foreach (area, callback, callback_data);
+ else
+ g_warning ("GtkCellAreaClass::foreach not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+/**
+ * gtk_cell_area_foreach_alloc:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext for this row of data.
+ * @widget: the #GtkWidget that @area is rendering to
+ * @cell_area: the @widget relative coordinates and size for @area
+ * @background_area: the @widget relative coordinates of the background area
+ * @callback: the #GtkCellAllocCallback to call
+ * @callback_data: user provided data pointer
+ *
+ * Calls @callback for every #GtkCellRenderer in @area with the
+ * allocated rectangle inside @cell_area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_foreach_alloc (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *background_area,
+ GtkCellAllocCallback callback,
+ gpointer callback_data)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (cell_area != NULL);
+ g_return_if_fail (callback != NULL);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->foreach_alloc)
+ class->foreach_alloc (area, context, widget, cell_area, background_area, callback, callback_data);
+ else
+ g_warning ("GtkCellAreaClass::foreach_alloc not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+/**
+ * gtk_cell_area_event:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext for this row of data.
+ * @widget: the #GtkWidget that @area is rendering to
+ * @event: the #GdkEvent to handle
+ * @cell_area: the @widget relative coordinates for @area
+ * @flags: the #GtkCellRendererState for @area in this row.
+ *
+ * Delegates event handling to a #GtkCellArea.
+ *
+ * Return value: %TRUE if the event was handled by @area.
+ *
+ * Since: 3.0
+ */
+gint
+gtk_cell_area_event (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ GtkCellAreaClass *class;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
+ g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), 0);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+ g_return_val_if_fail (event != NULL, 0);
+ g_return_val_if_fail (cell_area != NULL, 0);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->event)
+ return class->event (area, context, widget, event, cell_area, flags);
+
+ g_warning ("GtkCellAreaClass::event not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+ return 0;
+}
+
+/**
+ * gtk_cell_area_render:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext for this row of data.
+ * @widget: the #GtkWidget that @area is rendering to
+ * @cr: the #cairo_t to render with
+ * @background_area: the @widget relative coordinates for @area's background
+ * @cell_area: the @widget relative coordinates for @area
+ * @flags: the #GtkCellRendererState for @area in this row.
+ * @paint_focus: whether @area should paint focus on focused cells for focused rows or not.
+ *
+ * Renders @area's cells according to @area's layout onto @widget at
+ * the given coordinates.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_render (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ cairo_t *cr,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean paint_focus)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (cr != NULL);
+ g_return_if_fail (background_area != NULL);
+ g_return_if_fail (cell_area != NULL);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->render)
+ class->render (area, context, widget, cr, background_area, cell_area, flags, paint_focus);
+ else
+ g_warning ("GtkCellAreaClass::render not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+/**
+ * gtk_cell_area_set_style_detail:
+ * @area: a #GtkCellArea
+ * @detail: the #GtkStyle detail string to set
+ *
+ * Sets the detail string used in any gtk_paint_*() functions
+ * used by @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_set_style_detail (GtkCellArea *area,
+ const gchar *detail)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+
+ priv = area->priv;
+
+ if (g_strcmp0 (priv->style_detail, detail) != 0)
+ {
+ g_free (priv->style_detail);
+ priv->style_detail = g_strdup (detail);
+ }
+}
+
+/**
+ * gtk_cell_area_get_style_detail:
+ * @area: a #GtkCellArea
+ *
+ * Gets the detail string used in any gtk_paint_*() functions
+ * used by @area.
+ *
+ * Return value: the detail string, the string belongs to the area and should not be freed.
+ *
+ * Since: 3.0
+ */
+G_CONST_RETURN gchar *
+gtk_cell_area_get_style_detail (GtkCellArea *area)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+
+ priv = area->priv;
+
+ return priv->style_detail;
+}
+
+static gboolean
+get_cell_allocation (GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *cell_background,
+ RendererAllocationData *data)
+{
+ if (data->renderer == renderer)
+ data->allocation = *cell_area;
+
+ return (data->renderer == renderer);
+}
+
+/**
+ * gtk_cell_area_get_cell_allocation:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext used to hold sizes for @area.
+ * @widget: the #GtkWidget that @area is rendering on
+ * @renderer: the #GtkCellRenderer to get the allocation for
+ * @cell_area: the whole allocated area for @area in @widget
+ * for this row
+ * @allocation: (out): where to store the allocation for @renderer
+ *
+ * Derives the allocation of @renderer inside @area if @area
+ * were to be renderered in @cell_area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_get_cell_allocation (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ GdkRectangle *allocation)
+{
+ RendererAllocationData data = { renderer, { 0, } };
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (cell_area != NULL);
+ g_return_if_fail (allocation != NULL);
+
+ gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area,
+ (GtkCellAllocCallback)get_cell_allocation, &data);
+
+ *allocation = data.allocation;
+}
+
+static gboolean
+get_cell_by_position (GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *cell_background,
+ CellByPositionData *data)
+{
+ if (data->x >= cell_area->x && data->x < cell_area->x + cell_area->width &&
+ data->y >= cell_area->y && data->y < cell_area->y + cell_area->height)
+ {
+ data->renderer = renderer;
+ data->cell_area = *cell_area;
+ }
+
+ return (data->renderer != NULL);
+}
+
+/**
+ * gtk_cell_area_get_cell_at_position:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext used to hold sizes for @area.
+ * @widget: the #GtkWidget that @area is rendering on
+ * @cell_area: the whole allocated area for @area in @widget
+ * for this row
+ * @x: the x position
+ * @y: the y position
+ * @alloc_area: (out) (allow-none): where to store the inner allocated area of the
+ * returned cell renderer, or %NULL.
+ *
+ * Gets the #GtkCellRenderer at @x and @y coordinates inside @area and optionally
+ * returns the full cell allocation for it inside @cell_area.
+ *
+ * Return value: the #GtkCellRenderer at @x and @y.
+ *
+ * Since: 3.0
+ */
+GtkCellRenderer *
+gtk_cell_area_get_cell_at_position (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ gint x,
+ gint y,
+ GdkRectangle *alloc_area)
+{
+ CellByPositionData data = { x, y, NULL, { 0, } };
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+ g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+ g_return_val_if_fail (cell_area != NULL, NULL);
+ g_return_val_if_fail (x >= cell_area->x && x <= cell_area->x + cell_area->width, NULL);
+ g_return_val_if_fail (y >= cell_area->y && y <= cell_area->y + cell_area->height, NULL);
+
+ gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area,
+ (GtkCellAllocCallback)get_cell_by_position, &data);
+
+ if (alloc_area)
+ *alloc_area = data.cell_area;
+
+ return data.renderer;
+}
+
+/*************************************************************
+ * API: Geometry *
+ *************************************************************/
+/**
+ * gtk_cell_area_create_context:
+ * @area: a #GtkCellArea
+ *
+ * Creates a #GtkCellAreaContext to be used with @area for
+ * all purposes. #GtkCellAreaContext stores geometry information
+ * for rows for which it was operated on, it is important to use
+ * the same context for the same row of data at all times (i.e.
+ * one should render and handle events with the same #GtkCellAreaContext
+ * which was used to request the size of those rows of data).
+ *
+ * Return value: (transfer full): a newly created #GtkCellAreaContext which can be used with @area.
+ *
+ * Since: 3.0
+ */
+GtkCellAreaContext *
+gtk_cell_area_create_context (GtkCellArea *area)
+{
+ GtkCellAreaClass *class;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->create_context)
+ return class->create_context (area);
+
+ g_warning ("GtkCellAreaClass::create_context not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+
+ return NULL;
+}
+
+/**
+ * gtk_cell_area_copy_context:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext to copy
+ *
+ * This is sometimes needed for cases where rows need to share
+ * alignments in one orientation but may be separately grouped
+ * in the opposing orientation.
+ *
+ * For instance, #GtkIconView creates all icons (rows) to have
+ * the same width and the cells theirin to have the same
+ * horizontal alignments. However each row of icons may have
+ * a separate collective height. #GtkIconView uses this to
+ * request the heights of each row based on a context which
+ * was already used to request all the row widths that are
+ * to be displayed.
+ *
+ * Return value: (transfer full): a newly created #GtkCellAreaContext copy of @context.
+ *
+ * Since: 3.0
+ */
+GtkCellAreaContext *
+gtk_cell_area_copy_context (GtkCellArea *area,
+ GtkCellAreaContext *context)
+{
+ GtkCellAreaClass *class;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+ g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->copy_context)
+ return class->copy_context (area, context);
+
+ g_warning ("GtkCellAreaClass::copy_context not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+
+ return NULL;
+}
+
+/**
+ * gtk_cell_area_get_request_mode:
+ * @area: a #GtkCellArea
+ *
+ * Gets whether the area prefers a height-for-width layout
+ * or a width-for-height layout.
+ *
+ * Return value: The #GtkSizeRequestMode preferred by @area.
+ *
+ * Since: 3.0
+ */
+GtkSizeRequestMode
+gtk_cell_area_get_request_mode (GtkCellArea *area)
+{
+ GtkCellAreaClass *class;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area),
+ GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->get_request_mode)
+ return class->get_request_mode (area);
+
+ g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+
+ return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+}
+
+/**
+ * gtk_cell_area_get_preferred_width:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext to perform this request with
+ * @widget: the #GtkWidget where @area will be rendering
+ * @minimum_width: (out) (allow-none): location to store the minimum width, or %NULL
+ * @natural_width: (out) (allow-none): location to store the natural width, or %NULL
+ *
+ * Retrieves a cell area's initial minimum and natural width.
+ *
+ * @area will store some geometrical information in @context along the way,
+ * when requesting sizes over an arbitrary number of rows, its not important
+ * to check the @minimum_width and @natural_width of this call but rather to
+ * consult gtk_cell_area_context_get_preferred_width() after a series of
+ * requests.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_get_preferred_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->get_preferred_width)
+ class->get_preferred_width (area, context, widget, minimum_width, natural_width);
+ else
+ g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+/**
+ * gtk_cell_area_get_preferred_height_for_width:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext which has already been requested for widths.
+ * @widget: the #GtkWidget where @area will be rendering
+ * @width: the width for which to check the height of this area
+ * @minimum_height: (out) (allow-none): location to store the minimum height, or %NULL
+ * @natural_height: (out) (allow-none): location to store the natural height, or %NULL
+ *
+ * Retrieves a cell area's minimum and natural height if it would be given
+ * the specified @width.
+ *
+ * @area stores some geometrical information in @context along the way
+ * while calling gtk_cell_area_get_preferred_width(), it's important to
+ * perform a series of gtk_cell_area_get_preferred_width() requests with
+ * @context first and then call gtk_cell_area_get_preferred_height_for_width()
+ * on each cell area individually to get the height for width of each
+ * fully requested row.
+ *
+ * If at some point, the width of a single row changes, it should be
+ * requested with gtk_cell_area_get_preferred_width() again and then
+ * the full width of the requested rows checked again with
+ * gtk_cell_area_context_get_preferred_width().
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+ class->get_preferred_height_for_width (area, context, widget, width, minimum_height, natural_height);
+}
+
+
+/**
+ * gtk_cell_area_get_preferred_height:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext to perform this request with
+ * @widget: the #GtkWidget where @area will be rendering
+ * @minimum_height: (out) (allow-none): location to store the minimum height, or %NULL
+ * @natural_height: (out) (allow-none): location to store the natural height, or %NULL
+ *
+ * Retrieves a cell area's initial minimum and natural height.
+ *
+ * @area will store some geometrical information in @context along the way,
+ * when requesting sizes over an arbitrary number of rows, its not important
+ * to check the @minimum_height and @natural_height of this call but rather to
+ * consult gtk_cell_area_context_get_preferred_height() after a series of
+ * requests.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_get_preferred_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->get_preferred_height)
+ class->get_preferred_height (area, context, widget, minimum_height, natural_height);
+ else
+ g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+/**
+ * gtk_cell_area_get_preferred_width_for_height:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext which has already been requested for widths.
+ * @widget: the #GtkWidget where @area will be rendering
+ * @height: the height for which to check the width of this area
+ * @minimum_width: (out) (allow-none): location to store the minimum width, or %NULL
+ * @natural_width: (out) (allow-none): location to store the natural width, or %NULL
+ *
+ * Retrieves a cell area's minimum and natural width if it would be given
+ * the specified @height.
+ *
+ * @area stores some geometrical information in @context along the way
+ * while calling gtk_cell_area_get_preferred_height(), it's important to
+ * perform a series of gtk_cell_area_get_preferred_height() requests with
+ * @context first and then call gtk_cell_area_get_preferred_width_for_height()
+ * on each cell area individually to get the height for width of each
+ * fully requested row.
+ *
+ * If at some point, the height of a single row changes, it should be
+ * requested with gtk_cell_area_get_preferred_height() again and then
+ * the full height of the requested rows checked again with
+ * gtk_cell_area_context_get_preferred_height().
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+ class->get_preferred_width_for_height (area, context, widget, height, minimum_width, natural_width);
+}
+
+/*************************************************************
+ * API: Attributes *
+ *************************************************************/
+
+/**
+ * gtk_cell_area_attribute_connect:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer to connect an attribute for
+ * @attribute: the attribute name
+ * @column: the #GtkTreeModel column to fetch attribute values from
+ *
+ * Connects an @attribute to apply values from @column for the
+ * #GtkTreeModel in use.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_attribute_connect (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *attribute,
+ gint column)
+{
+ GtkCellAreaPrivate *priv;
+ CellInfo *info;
+ CellAttribute *cell_attribute;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (attribute != NULL);
+ g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
+
+ priv = area->priv;
+ info = g_hash_table_lookup (priv->cell_info, renderer);
+
+ if (!info)
+ {
+ info = cell_info_new (NULL, NULL, NULL);
+
+ g_hash_table_insert (priv->cell_info, renderer, info);
+ }
+ else
+ {
+ GSList *node;
+
+ /* Check we are not adding the same attribute twice */
+ if ((node = g_slist_find_custom (info->attributes, attribute,
+ (GCompareFunc)cell_attribute_find)) != NULL)
+ {
+ cell_attribute = node->data;
+
+ g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
+ "since `%s' is already attributed to column %d",
+ attribute,
+ g_type_name (G_TYPE_FROM_INSTANCE (area)),
+ attribute, cell_attribute->column);
+ return;
+ }
+ }
+
+ cell_attribute = cell_attribute_new (renderer, attribute, column);
+
+ if (!cell_attribute)
+ {
+ g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
+ "since attribute does not exist",
+ attribute,
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+ return;
+ }
+
+ info->attributes = g_slist_prepend (info->attributes, cell_attribute);
+}
+
+/**
+ * gtk_cell_area_attribute_disconnect:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer to disconnect an attribute for
+ * @attribute: the attribute name
+ *
+ * Disconnects @attribute for the @renderer in @area so that
+ * attribute will no longer be updated with values from the
+ * model.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_attribute_disconnect (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *attribute)
+{
+ GtkCellAreaPrivate *priv;
+ CellInfo *info;
+ CellAttribute *cell_attribute;
+ GSList *node;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (attribute != NULL);
+ g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
+
+ priv = area->priv;
+ info = g_hash_table_lookup (priv->cell_info, renderer);
+
+ if (info)
+ {
+ node = g_slist_find_custom (info->attributes, attribute,
+ (GCompareFunc)cell_attribute_find);
+ if (node)
+ {
+ cell_attribute = node->data;
+
+ cell_attribute_free (cell_attribute);
+
+ info->attributes = g_slist_delete_link (info->attributes, node);
+ }
+ }
+}
+
+/**
+ * gtk_cell_area_apply_attributes
+ * @area: a #GtkCellArea
+ * @tree_model: the #GtkTreeModel to pull values from
+ * @iter: the #GtkTreeIter in @tree_model to apply values for
+ * @is_expander: whether @iter has children
+ * @is_expanded: whether @iter is expanded in the view and
+ * children are visible
+ *
+ * Applies any connected attributes to the renderers in
+ * @area by pulling the values from @tree_model.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_apply_attributes (GtkCellArea *area,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gboolean is_expander,
+ gboolean is_expanded)
+{
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
+ g_return_if_fail (iter != NULL);
+
+ g_signal_emit (area, cell_area_signals[SIGNAL_APPLY_ATTRIBUTES], 0,
+ tree_model, iter, is_expander, is_expanded);
+}
+
+/**
+ * gtk_cell_area_get_current_path_string:
+ * @area: a #GtkCellArea
+ *
+ * Gets the current #GtkTreePath string for the currently
+ * applied #GtkTreeIter, this is implicitly updated when
+ * gtk_cell_area_apply_attributes() is called and can be
+ * used to interact with renderers from #GtkCellArea
+ * subclasses.
+ *
+ * Return value: The current #GtkTreePath string for the current
+ * attributes applied to @area. This string belongs to the area and
+ * should not be freed.
+ *
+ * Since: 3.0
+ */
+G_CONST_RETURN gchar *
+gtk_cell_area_get_current_path_string (GtkCellArea *area)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+
+ priv = area->priv;
+
+ return priv->current_path;
+}
+
+
+/*************************************************************
+ * API: Cell Properties *
+ *************************************************************/
+/**
+ * gtk_cell_area_class_install_cell_property:
+ * @aclass: a #GtkCellAreaClass
+ * @property_id: the id for the property
+ * @pspec: the #GParamSpec for the property
+ *
+ * Installs a cell property on a cell area class.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
+ guint property_id,
+ GParamSpec *pspec)
+{
+ g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
+ g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+ if (pspec->flags & G_PARAM_WRITABLE)
+ g_return_if_fail (aclass->set_cell_property != NULL);
+ if (pspec->flags & G_PARAM_READABLE)
+ g_return_if_fail (aclass->get_cell_property != NULL);
+ g_return_if_fail (property_id > 0);
+ g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
+ g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
+
+ if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
+ {
+ g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
+ G_OBJECT_CLASS_NAME (aclass), pspec->name);
+ return;
+ }
+ g_param_spec_ref (pspec);
+ g_param_spec_sink (pspec);
+ PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
+ g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
+}
+
+/**
+ * gtk_cell_area_class_find_cell_property:
+ * @aclass: a #GtkCellAreaClass
+ * @property_name: the name of the child property to find
+ *
+ * Finds a cell property of a cell area class by name.
+ *
+ * Return value: (allow-none): the #GParamSpec of the child property or %NULL if @aclass has no
+ * child property with that name.
+ *
+ * Since: 3.0
+ */
+GParamSpec*
+gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
+ const gchar *property_name)
+{
+ g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ return g_param_spec_pool_lookup (cell_property_pool,
+ property_name,
+ G_OBJECT_CLASS_TYPE (aclass),
+ TRUE);
+}
+
+/**
+ * gtk_cell_area_class_list_cell_properties:
+ * @aclass: a #GtkCellAreaClass
+ * @n_properties: location to return the number of cell properties found
+ *
+ * Returns all cell properties of a cell area class.
+ *
+ * Return value: a newly allocated %NULL-terminated array of #GParamSpec*.
+ * The array must be freed with g_free().
+ *
+ * Since: 3.0
+ */
+GParamSpec**
+gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
+ guint *n_properties)
+{
+ GParamSpec **pspecs;
+ guint n;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
+
+ pspecs = g_param_spec_pool_list (cell_property_pool,
+ G_OBJECT_CLASS_TYPE (aclass),
+ &n);
+ if (n_properties)
+ *n_properties = n;
+
+ return pspecs;
+}
+
+/**
+ * gtk_cell_area_add_with_properties:
+ * @area: a #GtkCellArea
+ * @renderer: a #GtkCellRenderer to be placed inside @area
+ * @first_prop_name: the name of the first cell property to set
+ * @Varargs: a %NULL-terminated list of property names and values, starting
+ * with @first_prop_name
+ *
+ * Adds @renderer to @area, setting cell properties at the same time.
+ * See gtk_cell_area_add() and gtk_cell_area_child_set() for more details.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_add_with_properties (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_prop_name,
+ ...)
+{
+ GtkCellAreaClass *class;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->add)
+ {
+ va_list var_args;
+
+ class->add (area, renderer);
+
+ va_start (var_args, first_prop_name);
+ gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
+ va_end (var_args);
+ }
+ else
+ g_warning ("GtkCellAreaClass::add not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+}
+
+/**
+ * gtk_cell_area_cell_set:
+ * @area: a #GtkCellArea
+ * @renderer: a #GtkCellRenderer which is a cell inside @area
+ * @first_prop_name: the name of the first cell property to set
+ * @Varargs: a %NULL-terminated list of property names and values, starting
+ * with @first_prop_name
+ *
+ * Sets one or more cell properties for @cell in @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_cell_set (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_prop_name,
+ ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ va_start (var_args, first_prop_name);
+ gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
+ va_end (var_args);
+}
+
+/**
+ * gtk_cell_area_cell_get:
+ * @area: a #GtkCellArea
+ * @renderer: a #GtkCellRenderer which is inside @area
+ * @first_prop_name: the name of the first cell property to get
+ * @Varargs: return location for the first cell property, followed
+ * optionally by more name/return location pairs, followed by %NULL
+ *
+ * Gets the values of one or more cell properties for @renderer in @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_cell_get (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_prop_name,
+ ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ va_start (var_args, first_prop_name);
+ gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
+ va_end (var_args);
+}
+
+static inline void
+area_get_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
+
+ class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
+}
+
+static inline void
+area_set_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GParamSpec *pspec,
+ const GValue *value)
+{
+ GValue tmp_value = { 0, };
+ GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
+
+ /* provide a copy to work from, convert (if necessary) and validate */
+ g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ if (!g_value_transform (value, &tmp_value))
+ g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
+ pspec->name,
+ g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
+ G_VALUE_TYPE_NAME (value));
+ else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
+ {
+ gchar *contents = g_strdup_value_contents (value);
+
+ g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
+ contents,
+ G_VALUE_TYPE_NAME (value),
+ pspec->name,
+ g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+ g_free (contents);
+ }
+ else
+ {
+ class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
+ }
+ g_value_unset (&tmp_value);
+}
+
+/**
+ * gtk_cell_area_cell_set_valist:
+ * @area: a #GtkCellArea
+ * @renderer: a #GtkCellRenderer which inside @area
+ * @first_property_name: the name of the first cell property to set
+ * @var_args: a %NULL-terminated list of property names and values, starting
+ * with @first_prop_name
+ *
+ * Sets one or more cell properties for @renderer in @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_cell_set_valist (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_property_name,
+ va_list var_args)
+{
+ const gchar *name;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ name = first_property_name;
+ while (name)
+ {
+ GValue value = { 0, };
+ gchar *error = NULL;
+ GParamSpec *pspec =
+ g_param_spec_pool_lookup (cell_property_pool, name,
+ G_OBJECT_TYPE (area), TRUE);
+ if (!pspec)
+ {
+ g_warning ("%s: cell area class `%s' has no cell property named `%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
+ break;
+ }
+ if (!(pspec->flags & G_PARAM_WRITABLE))
+ {
+ g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
+ G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
+ break;
+ }
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ G_VALUE_COLLECT (&value, var_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+
+ /* we purposely leak the value here, it might not be
+ * in a sane state if an error condition occoured
+ */
+ break;
+ }
+ area_set_cell_property (area, renderer, pspec, &value);
+ g_value_unset (&value);
+ name = va_arg (var_args, gchar*);
+ }
+}
+
+/**
+ * gtk_cell_area_cell_get_valist:
+ * @area: a #GtkCellArea
+ * @renderer: a #GtkCellRenderer inside @area
+ * @first_property_name: the name of the first property to get
+ * @var_args: return location for the first property, followed
+ * optionally by more name/return location pairs, followed by %NULL
+ *
+ * Gets the values of one or more cell properties for @renderer in @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_cell_get_valist (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_property_name,
+ va_list var_args)
+{
+ const gchar *name;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ name = first_property_name;
+ while (name)
+ {
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ gchar *error;
+
+ pspec = g_param_spec_pool_lookup (cell_property_pool, name,
+ G_OBJECT_TYPE (area), TRUE);
+ if (!pspec)
+ {
+ g_warning ("%s: cell area class `%s' has no cell property named `%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
+ break;
+ }
+ if (!(pspec->flags & G_PARAM_READABLE))
+ {
+ g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
+ G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
+ break;
+ }
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ area_get_cell_property (area, renderer, pspec, &value);
+ G_VALUE_LCOPY (&value, var_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+ g_value_unset (&value);
+ break;
+ }
+ g_value_unset (&value);
+ name = va_arg (var_args, gchar*);
+ }
+}
+
+/**
+ * gtk_cell_area_cell_set_property:
+ * @area: a #GtkCellArea
+ * @renderer: a #GtkCellRenderer inside @area
+ * @property_name: the name of the cell property to set
+ * @value: the value to set the cell property to
+ *
+ * Sets a cell property for @renderer in @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_cell_set_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *property_name,
+ const GValue *value)
+{
+ GParamSpec *pspec;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (property_name != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+
+ pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
+ G_OBJECT_TYPE (area), TRUE);
+ if (!pspec)
+ g_warning ("%s: cell area class `%s' has no cell property named `%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
+ else if (!(pspec->flags & G_PARAM_WRITABLE))
+ g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
+ G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
+ else
+ {
+ area_set_cell_property (area, renderer, pspec, value);
+ }
+}
+
+/**
+ * gtk_cell_area_cell_get_property:
+ * @area: a #GtkCellArea
+ * @renderer: a #GtkCellRenderer inside @area
+ * @property_name: the name of the property to get
+ * @value: a location to return the value
+ *
+ * Gets the value of a cell property for @renderer in @area.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_cell_get_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *property_name,
+ GValue *value)
+{
+ GParamSpec *pspec;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (property_name != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+
+ pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
+ G_OBJECT_TYPE (area), TRUE);
+ if (!pspec)
+ g_warning ("%s: cell area class `%s' has no cell property named `%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
+ else if (!(pspec->flags & G_PARAM_READABLE))
+ g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
+ G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
+ else
+ {
+ GValue *prop_value, tmp_value = { 0, };
+
+ /* auto-conversion of the callers value type
+ */
+ if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
+ {
+ g_value_reset (value);
+ prop_value = value;
+ }
+ else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
+ {
+ g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
+ pspec->name,
+ g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
+ G_VALUE_TYPE_NAME (value));
+ return;
+ }
+ else
+ {
+ g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ prop_value = &tmp_value;
+ }
+
+ area_get_cell_property (area, renderer, pspec, prop_value);
+
+ if (prop_value != value)
+ {
+ g_value_transform (prop_value, value);
+ g_value_unset (&tmp_value);
+ }
+ }
+}
+
+/*************************************************************
+ * API: Focus *
+ *************************************************************/
+
+/**
+ * gtk_cell_area_is_activatable:
+ * @area: a #GtkCellArea
+ *
+ * Returns whether the area can do anything when activated,
+ * after applying new attributes to @area.
+ *
+ * Return value: whether @area can do anything when activated.
+ *
+ * Since: 3.0
+ */
+gboolean
+gtk_cell_area_is_activatable (GtkCellArea *area)
+{
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+
+ return GTK_CELL_AREA_GET_CLASS (area)->is_activatable (area);
+}
+
+/**
+ * gtk_cell_area_focus:
+ * @area: a #GtkCellArea
+ * @direction: the #GtkDirectionType
+ *
+ * This should be called by the @area's owning layout widget
+ * when focus is to be passed to @area, or moved within @area
+ * for a given @direction and row data.
+ *
+ * Implementing #GtkCellArea classes should implement this
+ * method to receive and navigate focus in it's own way particular
+ * to how it lays out cells.
+ *
+ * Return value: %TRUE if focus remains inside @area as a result of this call.
+ *
+ * Since: 3.0
+ */
+gboolean
+gtk_cell_area_focus (GtkCellArea *area,
+ GtkDirectionType direction)
+{
+ GtkCellAreaClass *class;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+
+ class = GTK_CELL_AREA_GET_CLASS (area);
+
+ if (class->focus)
+ return class->focus (area, direction);
+
+ g_warning ("GtkCellAreaClass::focus not implemented for `%s'",
+ g_type_name (G_TYPE_FROM_INSTANCE (area)));
+
+ return FALSE;
+}
+
+/**
+ * gtk_cell_area_activate:
+ * @area: a #GtkCellArea
+ * @context: the #GtkCellAreaContext in context with the current row data
+ * @widget: the #GtkWidget that @area is rendering on
+ * @cell_area: the size and location of @area relative to @widget's allocation
+ * @flags: the #GtkCellRendererState flags for @area for this row of data.
+ * @edit_only: if %TRUE then only cell renderers that are %GTK_CELL_RENDERER_MODE_EDITABLE
+ * will be activated.
+ *
+ * Activates @area, usually by activating the currently focused
+ * cell, however some subclasses which embed widgets in the area
+ * can also activate a widget if it currently has the focus.
+ *
+ * Return value: Whether @area was successfully activated.
+ *
+ * Since: 3.0
+ */
+gboolean
+gtk_cell_area_activate (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean edit_only)
+{
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+
+ return GTK_CELL_AREA_GET_CLASS (area)->activate (area, context, widget, cell_area, flags, edit_only);
+}
+
+
+/**
+ * gtk_cell_area_set_focus_cell:
+ * @area: a #GtkCellArea
+ * @focus_cell: the #GtkCellRenderer to give focus to
+ *
+ * This is generally called from #GtkCellArea implementations
+ * either gtk_cell_area_grab_focus() or gtk_cell_area_update_focus()
+ * is called. It's also up to the #GtkCellArea implementation
+ * to update the focused cell when receiving events from
+ * gtk_cell_area_event() appropriately.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_set_focus_cell (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
+
+ priv = area->priv;
+
+ if (priv->focus_cell != renderer)
+ {
+ if (priv->focus_cell)
+ g_object_unref (priv->focus_cell);
+
+ priv->focus_cell = renderer;
+
+ if (priv->focus_cell)
+ g_object_ref (priv->focus_cell);
+
+ g_object_notify (G_OBJECT (area), "focus-cell");
+ }
+
+ /* Signal that the current focus renderer for this path changed
+ * (it may be that the focus cell did not change, but the row
+ * may have changed so we need to signal it) */
+ g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_CHANGED], 0,
+ priv->focus_cell, priv->current_path);
+
+}
+
+/**
+ * gtk_cell_area_get_focus_cell:
+ * @area: a #GtkCellArea
+ *
+ * Retrieves the currently focused cell for @area
+ *
+ * Return value: the currently focused cell in @area.
+ *
+ * Since: 3.0
+ */
+GtkCellRenderer *
+gtk_cell_area_get_focus_cell (GtkCellArea *area)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+
+ priv = area->priv;
+
+ return priv->focus_cell;
+}
+
+
+/*************************************************************
+ * API: Focus Siblings *
+ *************************************************************/
+
+/**
+ * gtk_cell_area_add_focus_sibling:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer expected to have focus
+ * @sibling: the #GtkCellRenderer to add to @renderer's focus area
+ *
+ * Adds @sibling to @renderer's focusable area, focus will be drawn
+ * around @renderer and all of it's siblings if @renderer can
+ * focus for a given row.
+ *
+ * Events handled by focus siblings can also activate the given
+ * focusable @renderer.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_add_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling)
+{
+ GtkCellAreaPrivate *priv;
+ GList *siblings;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
+ g_return_if_fail (renderer != sibling);
+ g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
+ g_return_if_fail (gtk_cell_area_has_renderer (area, sibling));
+ g_return_if_fail (!gtk_cell_area_is_focus_sibling (area, renderer, sibling));
+
+ /* XXX We should also check that sibling is not in any other renderer's sibling
+ * list already, a renderer can be sibling of only one focusable renderer
+ * at a time.
+ */
+
+ priv = area->priv;
+
+ siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
+
+ if (siblings)
+ siblings = g_list_append (siblings, sibling);
+ else
+ {
+ siblings = g_list_append (siblings, sibling);
+ g_hash_table_insert (priv->focus_siblings, renderer, siblings);
+ }
+}
+
+/**
+ * gtk_cell_area_remove_focus_sibling:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer expected to have focus
+ * @sibling: the #GtkCellRenderer to remove from @renderer's focus area
+ *
+ * Removes @sibling from @renderer's focus sibling list
+ * (see gtk_cell_area_add_focus_sibling()).
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_remove_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling)
+{
+ GtkCellAreaPrivate *priv;
+ GList *siblings;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
+ g_return_if_fail (gtk_cell_area_is_focus_sibling (area, renderer, sibling));
+
+ priv = area->priv;
+
+ siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
+
+ siblings = g_list_copy (siblings);
+ siblings = g_list_remove (siblings, sibling);
+
+ if (!siblings)
+ g_hash_table_remove (priv->focus_siblings, renderer);
+ else
+ g_hash_table_insert (priv->focus_siblings, renderer, siblings);
+}
+
+/**
+ * gtk_cell_area_is_focus_sibling:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer expected to have focus
+ * @sibling: the #GtkCellRenderer to check against @renderer's sibling list
+ *
+ * Returns %TRUE if @sibling is one of @renderer's focus siblings
+ * (see gtk_cell_area_add_focus_sibling()).
+ *
+ * Since: 3.0
+ */
+gboolean
+gtk_cell_area_is_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling)
+{
+ GtkCellAreaPrivate *priv;
+ GList *siblings, *l;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (sibling), FALSE);
+
+ priv = area->priv;
+
+ siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
+
+ for (l = siblings; l; l = l->next)
+ {
+ GtkCellRenderer *a_sibling = l->data;
+
+ if (a_sibling == sibling)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_cell_area_get_focus_siblings:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer expected to have focus
+ *
+ * Gets the focus sibling cell renderers for @renderer.
+ *
+ * Return value: (element-type GtkCellRenderer) (transfer none): A #GList of #GtkCellRenderers.
+ * The returned list is internal and should not be freed.
+ *
+ * Since: 3.0
+ */
+const GList *
+gtk_cell_area_get_focus_siblings (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
+
+ priv = area->priv;
+
+ return g_hash_table_lookup (priv->focus_siblings, renderer);
+}
+
+/**
+ * gtk_cell_area_get_focus_from_sibling:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer
+ *
+ * Gets the #GtkCellRenderer which is expected to be focusable
+ * for which @renderer is, or may be a sibling.
+ *
+ * This is handy for #GtkCellArea subclasses when handling events,
+ * after determining the renderer at the event location it can
+ * then chose to activate the focus cell for which the event
+ * cell may have been a sibling.
+ *
+ * Return value: the #GtkCellRenderer for which @renderer is a sibling, or %NULL.
+ *
+ * Since: 3.0
+ */
+GtkCellRenderer *
+gtk_cell_area_get_focus_from_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellRenderer *ret_renderer = NULL;
+ GList *renderers, *l;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
+
+ renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
+
+ for (l = renderers; l; l = l->next)
+ {
+ GtkCellRenderer *a_renderer = l->data;
+ const GList *list;
+
+ for (list = gtk_cell_area_get_focus_siblings (area, a_renderer);
+ list; list = list->next)
+ {
+ GtkCellRenderer *sibling_renderer = list->data;
+
+ if (sibling_renderer == renderer)
+ {
+ ret_renderer = a_renderer;
+ break;
+ }
+ }
+ }
+ g_list_free (renderers);
+
+ return ret_renderer;
+}
+
+/*************************************************************
+ * API: Cell Activation/Editing *
+ *************************************************************/
+static void
+gtk_cell_area_add_editable (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *editable,
+ const GdkRectangle *cell_area)
+{
+ g_signal_emit (area, cell_area_signals[SIGNAL_ADD_EDITABLE], 0,
+ renderer, editable, cell_area, area->priv->current_path);
+}
+
+static void
+gtk_cell_area_remove_editable (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *editable)
+{
+ g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable);
+}
+
+static void
+cell_area_remove_widget_cb (GtkCellEditable *editable,
+ GtkCellArea *area)
+{
+ GtkCellAreaPrivate *priv = area->priv;
+
+ g_assert (priv->edit_widget == editable);
+ g_assert (priv->edited_cell != NULL);
+
+ gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget);
+
+ /* Now that we're done with editing the widget and it can be removed,
+ * remove our references to the widget and disconnect handlers */
+ gtk_cell_area_set_edited_cell (area, NULL);
+ gtk_cell_area_set_edit_widget (area, NULL);
+}
+
+static void
+gtk_cell_area_set_edited_cell (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
+
+ priv = area->priv;
+
+ if (priv->edited_cell != renderer)
+ {
+ if (priv->edited_cell)
+ g_object_unref (priv->edited_cell);
+
+ priv->edited_cell = renderer;
+
+ if (priv->edited_cell)
+ g_object_ref (priv->edited_cell);
+
+ g_object_notify (G_OBJECT (area), "edited-cell");
+ }
+}
+
+static void
+gtk_cell_area_set_edit_widget (GtkCellArea *area,
+ GtkCellEditable *editable)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable));
+
+ priv = area->priv;
+
+ if (priv->edit_widget != editable)
+ {
+ if (priv->edit_widget)
+ {
+ g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id);
+
+ g_object_unref (priv->edit_widget);
+ }
+
+ priv->edit_widget = editable;
+
+ if (priv->edit_widget)
+ {
+ priv->remove_widget_id =
+ g_signal_connect (priv->edit_widget, "remove-widget",
+ G_CALLBACK (cell_area_remove_widget_cb), area);
+
+ g_object_ref (priv->edit_widget);
+ }
+
+ g_object_notify (G_OBJECT (area), "edit-widget");
+ }
+}
+
+/**
+ * gtk_cell_area_get_edited_cell:
+ * @area: a #GtkCellArea
+ *
+ * Gets the #GtkCellRenderer in @area that is currently
+ * being edited.
+ *
+ * Return value: The currently edited #GtkCellRenderer
+ *
+ * Since: 3.0
+ */
+GtkCellRenderer *
+gtk_cell_area_get_edited_cell (GtkCellArea *area)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+
+ priv = area->priv;
+
+ return priv->edited_cell;
+}
+
+/**
+ * gtk_cell_area_get_edit_widget:
+ * @area: a #GtkCellArea
+ *
+ * Gets the #GtkCellEditable widget currently used
+ * to edit the currently edited cell.
+ *
+ * Return value: The currently active #GtkCellEditable widget
+ *
+ * Since: 3.0
+ */
+GtkCellEditable *
+gtk_cell_area_get_edit_widget (GtkCellArea *area)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+
+ priv = area->priv;
+
+ return priv->edit_widget;
+}
+
+/**
+ * gtk_cell_area_activate_cell:
+ * @area: a #GtkCellArea
+ * @widget: the #GtkWidget that @area is rendering onto
+ * @renderer: the #GtkCellRenderer in @area to activate
+ * @event: the #GdkEvent for which cell activation should occur
+ * @cell_area: the #GdkRectangle in @widget relative coordinates
+ * of @renderer for the current row.
+ * @flags: the #GtkCellRendererState for @renderer
+ *
+ * This is used by #GtkCellArea subclasses when handling events
+ * to activate cells, the base #GtkCellArea class activates cells
+ * for keyboard events for free in it's own GtkCellArea->activate()
+ * implementation.
+ *
+ * Return value: whether cell activation was successful
+ *
+ * Since: 3.0
+ */
+gboolean
+gtk_cell_area_activate_cell (GtkCellArea *area,
+ GtkWidget *widget,
+ GtkCellRenderer *renderer,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ GtkCellRendererMode mode;
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
+ g_return_val_if_fail (cell_area != NULL, FALSE);
+
+ priv = area->priv;
+
+ g_object_get (renderer, "mode", &mode, NULL);
+
+ if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
+ {
+ if (gtk_cell_renderer_activate (renderer,
+ event, widget,
+ priv->current_path,
+ cell_area,
+ cell_area,
+ flags))
+ return TRUE;
+ }
+ else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
+ {
+ GtkCellEditable *editable_widget;
+ GdkRectangle inner_area;
+
+ gtk_cell_area_inner_cell_area (area, widget, cell_area, &inner_area);
+
+ editable_widget =
+ gtk_cell_renderer_start_editing (renderer,
+ event, widget,
+ priv->current_path,
+ &inner_area,
+ &inner_area,
+ flags);
+
+ if (editable_widget != NULL)
+ {
+ g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
+
+ gtk_cell_area_set_edited_cell (area, renderer);
+ gtk_cell_area_set_edit_widget (area, editable_widget);
+
+ /* Signal that editing started so that callers can get
+ * a handle on the editable_widget */
+ gtk_cell_area_add_editable (area, priv->focus_cell, editable_widget, cell_area);
+
+ /* If the signal was successfully handled start the editing */
+ if (gtk_widget_get_parent (GTK_WIDGET (editable_widget)))
+ {
+ gtk_cell_editable_start_editing (editable_widget, NULL);
+ gtk_widget_grab_focus (GTK_WIDGET (editable_widget));
+ }
+ else
+ {
+ /* Otherwise clear the editing state and fire a warning */
+ gtk_cell_area_set_edited_cell (area, NULL);
+ gtk_cell_area_set_edit_widget (area, NULL);
+
+ g_warning ("GtkCellArea::add-editable fired in the dark, no cell editing was started.");
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_cell_area_stop_editing:
+ * @area: a #GtkCellArea
+ * @canceled: whether editing was canceled.
+ *
+ * Explicitly stops the editing of the currently
+ * edited cell (see gtk_cell_area_get_edited_cell()).
+ *
+ * If @canceled is %TRUE, the cell renderer will emit
+ * the ::editing-canceled signal.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_stop_editing (GtkCellArea *area,
+ gboolean canceled)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+
+ priv = area->priv;
+
+ if (priv->edited_cell)
+ {
+ GtkCellEditable *edit_widget = g_object_ref (priv->edit_widget);
+ GtkCellRenderer *edit_cell = g_object_ref (priv->edited_cell);
+
+ /* Stop editing of the cell renderer */
+ gtk_cell_renderer_stop_editing (priv->edited_cell, canceled);
+
+ /* Remove any references to the editable widget */
+ gtk_cell_area_set_edited_cell (area, NULL);
+ gtk_cell_area_set_edit_widget (area, NULL);
+
+ /* Send the remove-widget signal explicitly (this is done after setting
+ * the edit cell/widget NULL to avoid feedback)
+ */
+ gtk_cell_area_remove_editable (area, edit_cell, edit_widget);
+ g_object_unref (edit_cell);
+ g_object_unref (edit_widget);
+ }
+}
+
+/*************************************************************
+ * API: Convenience for area implementations *
+ *************************************************************/
+
+/**
+ * gtk_cell_area_inner_cell_area:
+ * @area: a #GtkCellArea
+ * @widget: the #GtkWidget that @area is rendering onto
+ * @cell_area: the @widget relative coordinates where one of @area's cells
+ * is to be placed
+ * @inner_area: (out): the return location for the inner cell area
+ *
+ * This is a convenience function for #GtkCellArea implementations
+ * to get the inner area where a given #GtkCellRenderer will be
+ * rendered. It removes any padding previously added by gtk_cell_area_request_renderer().
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_inner_cell_area (GtkCellArea *area,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ GdkRectangle *inner_area)
+{
+ gint focus_line_width;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (cell_area != NULL);
+ g_return_if_fail (inner_area != NULL);
+
+ gtk_widget_style_get (widget, "focus-line-width", &focus_line_width, NULL);
+
+ *inner_area = *cell_area;
+
+ inner_area->x += focus_line_width;
+ inner_area->width -= focus_line_width * 2;
+ inner_area->y += focus_line_width;
+ inner_area->height -= focus_line_width * 2;
+}
+
+/**
+ * gtk_cell_area_request_renderer:
+ * @area: a #GtkCellArea
+ * @renderer: the #GtkCellRenderer to request size for
+ * @orientation: the #GtkOrientation in which to request size
+ * @widget: the #GtkWidget that @area is rendering onto
+ * @for_size: the allocation contextual size to request for, or -1 if
+ * the base request for the orientation is to be returned.
+ * @minimum_size: (out) (allow-none): location to store the minimum size, or %NULL
+ * @natural_size: (out) (allow-none): location to store the natural size, or %NULL
+ *
+ * This is a convenience function for #GtkCellArea implementations
+ * to request size for cell renderers. It's important to use this
+ * function to request size and then use gtk_cell_area_inner_cell_area()
+ * at render and event time since this function will add padding
+ * around the cell for focus painting.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_request_renderer (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkOrientation orientation,
+ GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkCellAreaPrivate *priv;
+ gint focus_line_width;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (minimum_size != NULL);
+ g_return_if_fail (natural_size != NULL);
+
+ priv = area->priv;
+
+ gtk_widget_style_get (widget, "focus-line-width", &focus_line_width, NULL);
+
+ focus_line_width *= 2;
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (for_size < 0)
+ gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
+ else
+ {
+ for_size = MAX (0, for_size - focus_line_width);
+
+ gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
+ minimum_size, natural_size);
+ }
+ }
+ else /* GTK_ORIENTATION_VERTICAL */
+ {
+ if (for_size < 0)
+ gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
+ else
+ {
+ for_size = MAX (0, for_size - focus_line_width);
+
+ gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
+ minimum_size, natural_size);
+ }
+ }
+
+ *minimum_size += focus_line_width;
+ *natural_size += focus_line_width;
+}
diff --git a/gtk/gtkcellarea.h b/gtk/gtkcellarea.h
new file mode 100644
index 0000000000..008b8bb10f
--- /dev/null
+++ b/gtk/gtkcellarea.h
@@ -0,0 +1,463 @@
+/* gtkcellarea.h
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_CELL_AREA_H__
+#define __GTK_CELL_AREA_H__
+
+#include <gtk/gtkcellrenderer.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtktreemodel.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CELL_AREA (gtk_cell_area_get_type ())
+#define GTK_CELL_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA, GtkCellArea))
+#define GTK_CELL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA, GtkCellAreaClass))
+#define GTK_IS_CELL_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA))
+#define GTK_IS_CELL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA))
+#define GTK_CELL_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA, GtkCellAreaClass))
+
+typedef struct _GtkCellArea GtkCellArea;
+typedef struct _GtkCellAreaClass GtkCellAreaClass;
+typedef struct _GtkCellAreaPrivate GtkCellAreaPrivate;
+typedef struct _GtkCellAreaContext GtkCellAreaContext;
+
+/**
+ * GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID:
+ * @object: the #GObject on which set_cell_property() or get_get_property()
+ * was called
+ * @property_id: the numeric id of the property
+ * @pspec: the #GParamSpec of the property
+ *
+ * This macro should be used to emit a standard warning about unexpected
+ * properties in set_cell_property() and get_cell_property() implementations.
+ */
+#define GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID(object, property_id, pspec) \
+ G_OBJECT_WARN_INVALID_PSPEC ((object), "cell property id", (property_id), (pspec))
+
+/**
+ * GtkCellCallback:
+ * @renderer: the cell renderer to operate on
+ * @data: user-supplied data
+ *
+ * The type of the callback functions used for iterating over
+ * the cell renderers of a #GtkCellArea, see gtk_cell_area_foreach().
+ *
+ * Return value: %TRUE to stop iterating over cells.
+ */
+typedef gboolean (*GtkCellCallback) (GtkCellRenderer *renderer,
+ gpointer data);
+
+/**
+ * GtkCellAllocCallback:
+ * @renderer: the cell renderer to operate on
+ * @cell_area: the area allocated to @renderer inside the rectangle provided to gtk_cell_area_foreach_alloc().
+ * @cell_background: the background area for @renderer inside the background
+ * area provided to gtk_cell_area_foreach_alloc().
+ * @data: user-supplied data
+ *
+ * The type of the callback functions used for iterating over
+ * the cell renderers and their allocated areas inside a #GtkCellArea,
+ * see gtk_cell_area_foreach_alloc().
+ *
+ * Return value: %TRUE to stop iterating over cells.
+ */
+typedef gboolean (*GtkCellAllocCallback) (GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *cell_background,
+ gpointer data);
+
+
+struct _GtkCellArea
+{
+ /*< private >*/
+ GInitiallyUnowned parent_instance;
+
+ GtkCellAreaPrivate *priv;
+};
+
+
+/**
+ * GtkCellAreaClass:
+ * @add: adds a #GtkCellRenderer to the area.
+ * @remove: removes a #GtkCellRenderer from the area.
+ * @foreach: Calls the #GtkCellCallback function on every #GtkCellRenderer in the area
+ * with the provided user data until the callback returns %TRUE.
+ * @foreach_alloc: Calls the #GtkCellAllocCallback function on every #GtkCellRenderer in the area
+ * with the allocated area for the cell and the provided user data until the callback returns %TRUE.
+ * @event: Handle an event in the area, this is generally used to activate a cell
+ * at the event location for button events but can also be used to generically pass
+ * events to #GtkWidgets drawn onto the area.
+ * @render: Actually render the area's cells to the specified rectangle, @background_area
+ * should be correctly distributed to the cells coresponding background areas.
+ * @apply_attributes: Apply the cell attributes to the cells. This is implemented as a signal and
+ * generally #GtkCellArea subclasses dont need to implement this since it's handled by the base
+ * class but can be overridden to apply some custom attributes.
+ * @create_context: Creates and returns a class specific #GtkCellAreaContext to store cell
+ * alignment and allocation details for a said #GtkCellArea class.
+ * @copy_context: Creates a new #GtkCellAreaContext in the same state as the passed @context
+ * with any cell alignment data and allocations in tact.
+ * @get_request_mode: This allows an area to tell its layouting widget whether it prefers to
+ * be allocated in %GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH or %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT mode.
+ * @get_preferred_width: Calculates the minimum and natural width of the area's cells
+ * with the current attributes applied while considering the particular layouting details
+ * of the said #GtkCellArea. While requests are performed over a series of rows, alignments
+ * and overall minimum and natural sizes should be stored in the corresponding #GtkCellAreaContext.
+ * @get_preferred_height_for_width: Calculates the minimum and natural height for the area
+ * if the passed @context would be allocated the given width. When implementing this virtual
+ * method it is safe to assume that @context has already stored the aligned cell widths
+ * for every #GtkTreeModel row that @context will be allocated for since this information
+ * was stored at #GtkCellAreaClass.get_preferred_width() time. This virtual method should
+ * also store any necessary alignments of cell heights for the case that the context is
+ * allocated a height.
+ * @get_preferred_height: Calculates the minimum and natural height of the area's cells
+ * with the current attributes applied. Essentially this is the same as
+ * #GtkCellAreaClass.get_preferred_width() only for areas that are being requested as
+ * %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT.
+ * @get_preferred_width_for_height: Calculates the minimum and natural width for the area
+ * if the passed @context would be allocated the given height. The same as
+ * #GtkCellAreaClass.get_preferred_height_for_width() only for handling requests in the
+ * %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT mode.
+ * @set_cell_property: This should be implemented to handle changes in child cell properties
+ * for a given #GtkCellRenderer that were previously installed on the #GtkCellAreaClass with
+ * gtk_cell_area_class_install_cell_property().
+ * @get_cell_property: This should be implemented to report the values of child cell properties
+ * for a given child #GtkCellRenderer.
+ * @focus: This virtual method should be implemented to navigate focus from cell to cell
+ * inside the #GtkCellArea. The #GtkCellArea should move focus from cell to cell inside
+ * the area and return %FALSE if focus logically leaves the area with the following exceptions:
+ * When the area contains no activatable cells, the entire area recieves focus. Focus should not
+ * be given to cells that are actually "focus siblings" of other sibling cells
+ * (see gtk_cell_area_get_focus_from_sibling()). Focus is set by calling gtk_cell_area_set_focus_cell().
+ * @is_activatable: Returns whether the #GtkCellArea can respond to #GtkCellAreaClass.activate(),
+ * usually this does not need to be implemented since the base class takes care of this however
+ * it can be enhanced if the #GtkCellArea subclass can handle activation in other ways than
+ * activating its #GtkCellRenderers.
+ * @activate: This is called when the layouting widget rendering the #GtkCellArea activates
+ * the focus cell (see gtk_cell_area_get_focus_cell()).
+ */
+struct _GtkCellAreaClass
+{
+ /*< private >*/
+ GInitiallyUnownedClass parent_class;
+
+ /*< public >*/
+
+ /* Basic methods */
+ void (* add) (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+ void (* remove) (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+ void (* foreach) (GtkCellArea *area,
+ GtkCellCallback callback,
+ gpointer callback_data);
+ void (* foreach_alloc) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *background_area,
+ GtkCellAllocCallback callback,
+ gpointer callback_data);
+ gint (* event) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags);
+ void (* render) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ cairo_t *cr,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean paint_focus);
+ void (* apply_attributes) (GtkCellArea *area,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gboolean is_expander,
+ gboolean is_expanded);
+
+ /* Geometry */
+ GtkCellAreaContext *(* create_context) (GtkCellArea *area);
+ GtkCellAreaContext *(* copy_context) (GtkCellArea *area,
+ GtkCellAreaContext *context);
+ GtkSizeRequestMode (* get_request_mode) (GtkCellArea *area);
+ void (* get_preferred_width) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width);
+ void (* get_preferred_height_for_width) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+ void (* get_preferred_height) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height);
+ void (* get_preferred_width_for_height) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+
+ /* Cell Properties */
+ void (* set_cell_property) (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+ void (* get_cell_property) (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+ /* Focus */
+ gboolean (* focus) (GtkCellArea *area,
+ GtkDirectionType direction);
+ gboolean (* is_activatable) (GtkCellArea *area);
+ gboolean (* activate) (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean edit_only);
+
+ /*< private >*/
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+ void (*_gtk_reserved5) (void);
+ void (*_gtk_reserved6) (void);
+ void (*_gtk_reserved7) (void);
+ void (*_gtk_reserved8) (void);
+};
+
+GType gtk_cell_area_get_type (void) G_GNUC_CONST;
+
+/* Basic methods */
+void gtk_cell_area_add (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+void gtk_cell_area_remove (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+gboolean gtk_cell_area_has_renderer (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+void gtk_cell_area_foreach (GtkCellArea *area,
+ GtkCellCallback callback,
+ gpointer callback_data);
+void gtk_cell_area_foreach_alloc (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *background_area,
+ GtkCellAllocCallback callback,
+ gpointer callback_data);
+gint gtk_cell_area_event (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags);
+void gtk_cell_area_render (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ cairo_t *cr,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean paint_focus);
+void gtk_cell_area_set_style_detail (GtkCellArea *area,
+ const gchar *detail);
+G_CONST_RETURN gchar *gtk_cell_area_get_style_detail (GtkCellArea *area);
+
+
+void gtk_cell_area_get_cell_allocation (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ GdkRectangle *allocation);
+GtkCellRenderer *gtk_cell_area_get_cell_at_position (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ gint x,
+ gint y,
+ GdkRectangle *alloc_area);
+
+/* Geometry */
+GtkCellAreaContext *gtk_cell_area_create_context (GtkCellArea *area);
+GtkCellAreaContext *gtk_cell_area_copy_context (GtkCellArea *area,
+ GtkCellAreaContext *context);
+GtkSizeRequestMode gtk_cell_area_get_request_mode (GtkCellArea *area);
+void gtk_cell_area_get_preferred_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size);
+void gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+void gtk_cell_area_get_preferred_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size);
+void gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+G_CONST_RETURN gchar *gtk_cell_area_get_current_path_string (GtkCellArea *area);
+
+
+/* Attributes */
+void gtk_cell_area_apply_attributes (GtkCellArea *area,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gboolean is_expander,
+ gboolean is_expanded);
+void gtk_cell_area_attribute_connect (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *attribute,
+ gint column);
+void gtk_cell_area_attribute_disconnect (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *attribute);
+
+/* Cell Properties */
+void gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
+ guint property_id,
+ GParamSpec *pspec);
+GParamSpec* gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
+ const gchar *property_name);
+GParamSpec** gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
+ guint *n_properties);
+void gtk_cell_area_add_with_properties (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_prop_name,
+ ...) G_GNUC_NULL_TERMINATED;
+void gtk_cell_area_cell_set (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_prop_name,
+ ...) G_GNUC_NULL_TERMINATED;
+void gtk_cell_area_cell_get (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_prop_name,
+ ...) G_GNUC_NULL_TERMINATED;
+void gtk_cell_area_cell_set_valist (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_property_name,
+ va_list var_args);
+void gtk_cell_area_cell_get_valist (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *first_property_name,
+ va_list var_args);
+void gtk_cell_area_cell_set_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *property_name,
+ const GValue *value);
+void gtk_cell_area_cell_get_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *property_name,
+ GValue *value);
+
+/* Focus */
+gboolean gtk_cell_area_is_activatable (GtkCellArea *area);
+gboolean gtk_cell_area_activate (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean edit_only);
+gboolean gtk_cell_area_focus (GtkCellArea *area,
+ GtkDirectionType direction);
+void gtk_cell_area_set_focus_cell (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+GtkCellRenderer *gtk_cell_area_get_focus_cell (GtkCellArea *area);
+
+
+/* Focus siblings */
+void gtk_cell_area_add_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling);
+void gtk_cell_area_remove_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling);
+gboolean gtk_cell_area_is_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling);
+G_CONST_RETURN GList *gtk_cell_area_get_focus_siblings (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+GtkCellRenderer *gtk_cell_area_get_focus_from_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+
+/* Cell Activation/Editing */
+GtkCellRenderer *gtk_cell_area_get_edited_cell (GtkCellArea *area);
+GtkCellEditable *gtk_cell_area_get_edit_widget (GtkCellArea *area);
+gboolean gtk_cell_area_activate_cell (GtkCellArea *area,
+ GtkWidget *widget,
+ GtkCellRenderer *renderer,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags);
+void gtk_cell_area_stop_editing (GtkCellArea *area,
+ gboolean canceled);
+
+/* Functions for area implementations */
+
+/* Distinguish the inner cell area from the whole requested area including margins */
+void gtk_cell_area_inner_cell_area (GtkCellArea *area,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ GdkRectangle *inner_area);
+
+/* Request the size of a cell while respecting the cell margins (requests are margin inclusive) */
+void gtk_cell_area_request_renderer (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkOrientation orientation,
+ GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size);
+
+G_END_DECLS
+
+#endif /* __GTK_CELL_AREA_H__ */
diff --git a/gtk/gtkcellareabox.c b/gtk/gtkcellareabox.c
new file mode 100644
index 0000000000..e21bb0c9e9
--- /dev/null
+++ b/gtk/gtkcellareabox.c
@@ -0,0 +1,2089 @@
+/* gtkcellareabox.c
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+/**
+ * SECTION:gtkcellareabox
+ * @Short_Description: A cell area that renders #GtkCellRenderers into a row or a column
+ * @Title: GtkCellAreaBox
+ *
+ * The #GtkCellAreaBox renders cell renderers into a row or a column depending on
+ * its #GtkOrientation.
+ *
+ * GtkCellAreaBox uses a notion of <emphasis>packing</emphasis>. Packing
+ * refers to adding cell renderers with reference to a particular position
+ * in a #GtkCellAreaBox. There are two reference positions: the
+ * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
+ * When the #GtkCellAreaBox is oriented in the %GTK_ORIENTATION_VERTICAL orientation,
+ * the start is defined as the top of the box and the end is defined as the bottom.
+ * In the %GTK_ORIENTATION_HORIZONTAL orientation start is defined as the
+ * left side and the end is defined as the right side.
+ *
+ * Alignments of #GtkCellRenderers rendered in adjacent rows can be configured
+ * by configuring the #GtkCellAreaBox:align child cell property with
+ * gtk_cell_area_cell_set_property() or by specifying the "align" argument
+ * to gtk_cell_area_box_pack_start() and gtk_cell_area_box_pack_end().
+ */
+
+#include "config.h"
+#include "gtkintl.h"
+#include "gtkorientable.h"
+#include "gtkcelllayout.h"
+#include "gtkcellareabox.h"
+#include "gtkcellareaboxcontext.h"
+#include "gtkprivate.h"
+
+
+/* GObjectClass */
+static void gtk_cell_area_box_finalize (GObject *object);
+static void gtk_cell_area_box_dispose (GObject *object);
+static void gtk_cell_area_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_cell_area_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* GtkCellAreaClass */
+static void gtk_cell_area_box_add (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+static void gtk_cell_area_box_remove (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+static void gtk_cell_area_box_foreach (GtkCellArea *area,
+ GtkCellCallback callback,
+ gpointer callback_data);
+static void gtk_cell_area_box_foreach_alloc (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *background_area,
+ GtkCellAllocCallback callback,
+ gpointer callback_data);
+static void gtk_cell_area_box_set_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_cell_area_box_get_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static GtkCellAreaContext *gtk_cell_area_box_create_context (GtkCellArea *area);
+static GtkCellAreaContext *gtk_cell_area_box_copy_context (GtkCellArea *area,
+ GtkCellAreaContext *context);
+static GtkSizeRequestMode gtk_cell_area_box_get_request_mode (GtkCellArea *area);
+static void gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width);
+static void gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+static gboolean gtk_cell_area_box_focus (GtkCellArea *area,
+ GtkDirectionType direction);
+
+/* GtkCellLayoutIface */
+static void gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface);
+static void gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gboolean expand);
+static void gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gboolean expand);
+static void gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gint position);
+static void gtk_cell_area_box_focus_changed (GtkCellArea *area,
+ GParamSpec *pspec,
+ GtkCellAreaBox *box);
+
+
+/* CellInfo/CellGroup metadata handling and convenience functions */
+typedef struct {
+ GtkCellRenderer *renderer;
+
+ guint expand : 1; /* Whether the cell expands */
+ guint pack : 1; /* Whether the cell is packed from the start or end */
+ guint align : 1; /* Whether to align this cell's position with adjacent rows */
+} CellInfo;
+
+typedef struct {
+ GList *cells;
+
+ guint id : 8;
+ guint n_cells : 8;
+ guint expand_cells : 8;
+} CellGroup;
+
+typedef struct {
+ GtkCellRenderer *renderer;
+
+ gint position;
+ gint size;
+} AllocatedCell;
+
+static CellInfo *cell_info_new (GtkCellRenderer *renderer,
+ GtkPackType pack,
+ gboolean expand,
+ gboolean align);
+static void cell_info_free (CellInfo *info);
+static gint cell_info_find (CellInfo *info,
+ GtkCellRenderer *renderer);
+
+static AllocatedCell *allocated_cell_new (GtkCellRenderer *renderer,
+ gint position,
+ gint size);
+static void allocated_cell_free (AllocatedCell *cell);
+static GList *list_consecutive_cells (GtkCellAreaBox *box);
+static gint count_expand_groups (GtkCellAreaBox *box);
+static void context_weak_notify (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *dead_context);
+static void reset_contexts (GtkCellAreaBox *box);
+static void init_context_groups (GtkCellAreaBox *box);
+static void init_context_group (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *context);
+static GSList *get_allocated_cells (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint height);
+
+
+struct _GtkCellAreaBoxPrivate
+{
+ GtkOrientation orientation;
+
+ /* We hold on to the previously focused cell when navigating
+ * up and down in a horizontal box (or left and right on a vertical one)
+ * this way we always re-enter the last focused cell. */
+ GtkCellRenderer *last_focus_cell;
+ gulong focus_cell_id;
+
+ GList *cells;
+ GArray *groups;
+
+ GSList *contexts;
+
+ gint spacing;
+
+ /* We hold on to the rtl state from a widget we are requested for
+ * so that we can navigate focus correctly */
+ gboolean rtl;
+};
+
+enum {
+ PROP_0,
+ PROP_ORIENTATION,
+ PROP_SPACING
+};
+
+enum {
+ CELL_PROP_0,
+ CELL_PROP_EXPAND,
+ CELL_PROP_ALIGN,
+ CELL_PROP_PACK_TYPE
+};
+
+G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
+ gtk_cell_area_box_cell_layout_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
+
+#define OPPOSITE_ORIENTATION(orientation) \
+ ((orientation) == GTK_ORIENTATION_HORIZONTAL ? \
+ GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL)
+
+static void
+gtk_cell_area_box_init (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv;
+
+ box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
+ GTK_TYPE_CELL_AREA_BOX,
+ GtkCellAreaBoxPrivate);
+ priv = box->priv;
+
+ priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+ priv->groups = g_array_new (FALSE, TRUE, sizeof (CellGroup));
+ priv->cells = NULL;
+ priv->contexts = NULL;
+ priv->spacing = 0;
+ priv->rtl = FALSE;
+
+ /* Watch whenever focus is given to a cell, even if it's not with keynav,
+ * this way we remember upon entry of the area where focus was last time around */
+ priv->focus_cell_id = g_signal_connect (box, "notify::focus-cell",
+ G_CALLBACK (gtk_cell_area_box_focus_changed), box);
+}
+
+static void
+gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkCellAreaClass *area_class = GTK_CELL_AREA_CLASS (class);
+
+ /* GObjectClass */
+ object_class->finalize = gtk_cell_area_box_finalize;
+ object_class->dispose = gtk_cell_area_box_dispose;
+ object_class->set_property = gtk_cell_area_box_set_property;
+ object_class->get_property = gtk_cell_area_box_get_property;
+
+ /* GtkCellAreaClass */
+ area_class->add = gtk_cell_area_box_add;
+ area_class->remove = gtk_cell_area_box_remove;
+ area_class->foreach = gtk_cell_area_box_foreach;
+ area_class->foreach_alloc = gtk_cell_area_box_foreach_alloc;
+ area_class->set_cell_property = gtk_cell_area_box_set_cell_property;
+ area_class->get_cell_property = gtk_cell_area_box_get_cell_property;
+
+ area_class->create_context = gtk_cell_area_box_create_context;
+ area_class->copy_context = gtk_cell_area_box_copy_context;
+ area_class->get_request_mode = gtk_cell_area_box_get_request_mode;
+ area_class->get_preferred_width = gtk_cell_area_box_get_preferred_width;
+ area_class->get_preferred_height = gtk_cell_area_box_get_preferred_height;
+ area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width;
+ area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height;
+
+ area_class->focus = gtk_cell_area_box_focus;
+
+ /* Properties */
+ g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
+
+ /**
+ * GtkCellAreaBox:spacing:
+ *
+ * The amount of space to reserve between cells.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_SPACING,
+ g_param_spec_int ("spacing",
+ P_("Spacing"),
+ P_("Space which is inserted between cells"),
+ 0,
+ G_MAXINT,
+ 0,
+ GTK_PARAM_READWRITE));
+
+ /* Cell Properties */
+ /**
+ * GtkCellAreaBox:expand:
+ *
+ * Whether the cell renderer should receive extra space when the area receives
+ * more than its natural size.
+ *
+ * Since: 3.0
+ */
+ gtk_cell_area_class_install_cell_property (area_class,
+ CELL_PROP_EXPAND,
+ g_param_spec_boolean
+ ("expand",
+ P_("Expand"),
+ P_("Whether the cell expands"),
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ /**
+ * GtkCellAreaBox:align:
+ *
+ * Whether the cell renderer should be aligned in adjacent rows.
+ *
+ * Since: 3.0
+ */
+ gtk_cell_area_class_install_cell_property (area_class,
+ CELL_PROP_ALIGN,
+ g_param_spec_boolean
+ ("align",
+ P_("Align"),
+ P_("Whether cell should align with adjacent rows"),
+ TRUE,
+ GTK_PARAM_READWRITE));
+
+ /**
+ * GtkCellAreaBox:pack-type:
+ *
+ * A GtkPackType indicating whether the cell renderer is packed with reference to the
+ * start or end of the area.
+ *
+ * Since: 3.0
+ */
+ gtk_cell_area_class_install_cell_property (area_class,
+ CELL_PROP_PACK_TYPE,
+ g_param_spec_enum
+ ("pack-type",
+ P_("Pack Type"),
+ P_("A GtkPackType indicating whether the cell is packed with "
+ "reference to the start or end of the cell area"),
+ GTK_TYPE_PACK_TYPE, GTK_PACK_START,
+ GTK_PARAM_READWRITE));
+
+ g_type_class_add_private (object_class, sizeof (GtkCellAreaBoxPrivate));
+}
+
+
+/*************************************************************
+ * CellInfo/CellGroup basics and convenience functions *
+ *************************************************************/
+static CellInfo *
+cell_info_new (GtkCellRenderer *renderer,
+ GtkPackType pack,
+ gboolean expand,
+ gboolean align)
+{
+ CellInfo *info = g_slice_new (CellInfo);
+
+ info->renderer = g_object_ref_sink (renderer);
+ info->pack = pack;
+ info->expand = expand;
+ info->align = align;
+
+ return info;
+}
+
+static void
+cell_info_free (CellInfo *info)
+{
+ g_object_unref (info->renderer);
+
+ g_slice_free (CellInfo, info);
+}
+
+static gint
+cell_info_find (CellInfo *info,
+ GtkCellRenderer *renderer)
+{
+ return (info->renderer == renderer) ? 0 : -1;
+}
+
+static AllocatedCell *
+allocated_cell_new (GtkCellRenderer *renderer,
+ gint position,
+ gint size)
+{
+ AllocatedCell *cell = g_slice_new (AllocatedCell);
+
+ cell->renderer = renderer;
+ cell->position = position;
+ cell->size = size;
+
+ return cell;
+}
+
+static void
+allocated_cell_free (AllocatedCell *cell)
+{
+ g_slice_free (AllocatedCell, cell);
+}
+
+static GList *
+list_consecutive_cells (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *l, *consecutive_cells = NULL, *pack_end_cells = NULL;
+ CellInfo *info;
+
+ /* List cells in consecutive order taking their
+ * PACK_START/PACK_END options into account
+ */
+ for (l = priv->cells; l; l = l->next)
+ {
+ info = l->data;
+
+ if (info->pack == GTK_PACK_START)
+ consecutive_cells = g_list_prepend (consecutive_cells, info);
+ }
+
+ for (l = priv->cells; l; l = l->next)
+ {
+ info = l->data;
+
+ if (info->pack == GTK_PACK_END)
+ pack_end_cells = g_list_prepend (pack_end_cells, info);
+ }
+
+ consecutive_cells = g_list_reverse (consecutive_cells);
+ consecutive_cells = g_list_concat (consecutive_cells, pack_end_cells);
+
+ return consecutive_cells;
+}
+
+static void
+cell_groups_clear (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ gint i;
+
+ for (i = 0; i < priv->groups->len; i++)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ g_list_free (group->cells);
+ }
+
+ g_array_set_size (priv->groups, 0);
+}
+
+static void
+cell_groups_rebuild (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ CellGroup group = { 0, };
+ CellGroup *group_ptr;
+ GList *cells, *l;
+ guint id = 0;
+
+ cell_groups_clear (box);
+
+ if (!priv->cells)
+ return;
+
+ cells = list_consecutive_cells (box);
+
+ /* First group is implied */
+ g_array_append_val (priv->groups, group);
+ group_ptr = &g_array_index (priv->groups, CellGroup, id);
+
+ for (l = cells; l; l = l->next)
+ {
+ CellInfo *info = l->data;
+
+ /* A new group starts with any aligned cell, the first group is implied */
+ if (info->align && l != cells)
+ {
+ memset (&group, 0x0, sizeof (CellGroup));
+ group.id = ++id;
+
+ g_array_append_val (priv->groups, group);
+ group_ptr = &g_array_index (priv->groups, CellGroup, id);
+ }
+
+ group_ptr->cells = g_list_prepend (group_ptr->cells, info);
+ group_ptr->n_cells++;
+
+ /* A group expands if it contains any expand cells */
+ if (info->expand)
+ group_ptr->expand_cells++;
+ }
+
+ g_list_free (cells);
+
+ for (id = 0; id < priv->groups->len; id++)
+ {
+ group_ptr = &g_array_index (priv->groups, CellGroup, id);
+
+ group_ptr->cells = g_list_reverse (group_ptr->cells);
+ }
+
+ /* Contexts need to be updated with the new grouping information */
+ init_context_groups (box);
+}
+
+static gint
+count_visible_cells (CellGroup *group,
+ gint *expand_cells)
+{
+ GList *l;
+ gint visible_cells = 0;
+ gint n_expand_cells = 0;
+
+ for (l = group->cells; l; l = l->next)
+ {
+ CellInfo *info = l->data;
+
+ if (gtk_cell_renderer_get_visible (info->renderer))
+ {
+ visible_cells++;
+
+ if (info->expand)
+ n_expand_cells++;
+ }
+ }
+
+ if (expand_cells)
+ *expand_cells = n_expand_cells;
+
+ return visible_cells;
+}
+
+static gint
+count_expand_groups (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ gint i;
+ gint expand_groups = 0;
+
+ for (i = 0; i < priv->groups->len; i++)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ if (group->expand_cells > 0)
+ expand_groups++;
+ }
+
+ return expand_groups;
+}
+
+static void
+context_weak_notify (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *dead_context)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+
+ priv->contexts = g_slist_remove (priv->contexts, dead_context);
+}
+
+static void
+init_context_group (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *context)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ gint *expand_groups, i;
+
+ expand_groups = g_new (gboolean, priv->groups->len);
+
+ for (i = 0; i < priv->groups->len; i++)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ expand_groups[i] = (group->expand_cells > 0);
+ }
+
+ /* This call implies reseting the request info */
+ gtk_cell_area_box_init_groups (context, priv->groups->len, expand_groups);
+ g_free (expand_groups);
+}
+
+static void
+init_context_groups (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GSList *l;
+
+ /* When the box's groups are reconstructed, contexts need to
+ * be reinitialized.
+ */
+ for (l = priv->contexts; l; l = l->next)
+ {
+ GtkCellAreaBoxContext *context = l->data;
+
+ init_context_group (box, context);
+ }
+}
+
+static void
+reset_contexts (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GSList *l;
+
+ /* When the box layout changes, contexts need to
+ * be reset and sizes for the box get requested again
+ */
+ for (l = priv->contexts; l; l = l->next)
+ {
+ GtkCellAreaContext *context = l->data;
+
+ gtk_cell_area_context_reset (context);
+ }
+}
+
+/* Fall back on a completely unaligned dynamic allocation of cells
+ * when not allocated for the said orientation, alignment of cells
+ * is not done when each area gets a different size in the orientation
+ * of the box.
+ */
+static GSList *
+allocate_cells_manually (GtkCellAreaBox *box,
+ GtkWidget *widget,
+ gint width,
+ gint height)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *cells, *l;
+ GSList *allocated_cells = NULL;
+ GtkRequestedSize *sizes;
+ gint i;
+ gint nvisible = 0, nexpand = 0, group_expand;
+ gint avail_size, extra_size, extra_extra, full_size;
+ gint position = 0, for_size;
+ gboolean rtl;
+
+ if (!priv->cells)
+ return NULL;
+
+ /* For vertical oriented boxes, we just let the cell renderers realign themselves for rtl */
+ rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+
+ cells = list_consecutive_cells (box);
+
+ /* Count the visible and expand cells */
+ for (i = 0; i < priv->groups->len; i++)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ nvisible += count_visible_cells (group, &group_expand);
+ nexpand += group_expand;
+ }
+
+ if (nvisible <= 0)
+ {
+ g_list_free (cells);
+ return NULL;
+ }
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ full_size = avail_size = width;
+ for_size = height;
+ }
+ else
+ {
+ full_size = avail_size = height;
+ for_size = width;
+ }
+
+ /* Go ahead and collect the requests on the fly */
+ sizes = g_new0 (GtkRequestedSize, nvisible);
+ for (l = cells, i = 0; l; l = l->next)
+ {
+ CellInfo *info = l->data;
+
+ if (!gtk_cell_renderer_get_visible (info->renderer))
+ continue;
+
+ gtk_cell_area_request_renderer (GTK_CELL_AREA (box), info->renderer,
+ priv->orientation,
+ widget, for_size,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+
+ avail_size -= sizes[i].minimum_size;
+
+ sizes[i].data = info;
+
+ i++;
+ }
+
+ /* Naturally distribute the allocation */
+ avail_size -= (nvisible - 1) * priv->spacing;
+ if (avail_size > 0)
+ avail_size = gtk_distribute_natural_allocation (avail_size, nvisible, sizes);
+ else
+ avail_size = 0;
+
+ /* Calculate/distribute expand for cells */
+ if (nexpand > 0)
+ {
+ extra_size = avail_size / nexpand;
+ extra_extra = avail_size % nexpand;
+ }
+ else
+ extra_size = extra_extra = 0;
+
+ /* Create the allocated cells */
+ for (i = 0; i < nvisible; i++)
+ {
+ CellInfo *info = sizes[i].data;
+ AllocatedCell *cell;
+
+ if (info->expand)
+ {
+ sizes[i].minimum_size += extra_size;
+ if (extra_extra)
+ {
+ sizes[i].minimum_size++;
+ extra_extra--;
+ }
+ }
+
+ if (rtl)
+ cell = allocated_cell_new (info->renderer,
+ full_size - (position + sizes[i].minimum_size),
+ sizes[i].minimum_size);
+ else
+ cell = allocated_cell_new (info->renderer, position, sizes[i].minimum_size);
+
+ allocated_cells = g_slist_prepend (allocated_cells, cell);
+
+ position += sizes[i].minimum_size;
+ position += priv->spacing;
+ }
+
+ g_free (sizes);
+ g_list_free (cells);
+
+ /* Note it might not be important to reverse the list here at all,
+ * we have the correct positions, no need to allocate from left to right */
+ return g_slist_reverse (allocated_cells);
+}
+
+/* Returns an allocation for each cell in the orientation of the box,
+ * used in ->render()/->event() implementations to get a straight-forward
+ * list of allocated cells to operate on.
+ */
+static GSList *
+get_allocated_cells (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint height)
+{
+ GtkCellAreaBoxAllocation *group_allocs;
+ GtkCellArea *area = GTK_CELL_AREA (box);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *cell_list;
+ GSList *allocated_cells = NULL;
+ gint i, j, n_allocs;
+ gint for_size, full_size;
+ gboolean rtl;
+
+ group_allocs = gtk_cell_area_box_context_get_orientation_allocs (context, &n_allocs);
+ if (!group_allocs)
+ return allocate_cells_manually (box, widget, width, height);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ full_size = width;
+ for_size = height;
+ }
+ else
+ {
+ full_size = height;
+ for_size = width;
+ }
+
+ /* For vertical oriented boxes, we just let the cell renderers realign themselves for rtl */
+ rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+
+ for (i = 0; i < n_allocs; i++)
+ {
+ /* We dont always allocate all groups, sometimes the requested group has only invisible
+ * cells for every row, hence the usage of group_allocs[i].group_idx here
+ */
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx);
+
+ /* Exception for single cell groups */
+ if (group->n_cells == 1)
+ {
+ CellInfo *info = group->cells->data;
+ AllocatedCell *cell;
+
+ if (rtl)
+ cell = allocated_cell_new (info->renderer,
+ full_size - (group_allocs[i].position + group_allocs[i].size),
+ group_allocs[i].size);
+ else
+ cell = allocated_cell_new (info->renderer, group_allocs[i].position, group_allocs[i].size);
+
+ allocated_cells = g_slist_prepend (allocated_cells, cell);
+ }
+ else
+ {
+ GtkRequestedSize *sizes;
+ gint avail_size, position;
+ gint visible_cells, expand_cells;
+ gint extra_size, extra_extra;
+
+ visible_cells = count_visible_cells (group, &expand_cells);
+
+ /* If this row has no visible cells in this group, just
+ * skip the allocation */
+ if (visible_cells == 0)
+ continue;
+
+ /* Offset the allocation to the group position and allocate into
+ * the group's available size */
+ position = group_allocs[i].position;
+ avail_size = group_allocs[i].size;
+
+ sizes = g_new (GtkRequestedSize, visible_cells);
+
+ for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next)
+ {
+ CellInfo *info = cell_list->data;
+
+ if (!gtk_cell_renderer_get_visible (info->renderer))
+ continue;
+
+ gtk_cell_area_request_renderer (area, info->renderer,
+ priv->orientation,
+ widget, for_size,
+ &sizes[j].minimum_size,
+ &sizes[j].natural_size);
+
+ sizes[j].data = info;
+ avail_size -= sizes[j].minimum_size;
+
+ j++;
+ }
+
+ /* Distribute cells naturally within the group */
+ avail_size -= (visible_cells - 1) * priv->spacing;
+ if (avail_size > 0)
+ avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes);
+ else
+ avail_size = 0;
+
+ /* Calculate/distribute expand for cells */
+ if (expand_cells > 0)
+ {
+ extra_size = avail_size / expand_cells;
+ extra_extra = avail_size % expand_cells;
+ }
+ else
+ extra_size = extra_extra = 0;
+
+ /* Create the allocated cells (loop only over visible cells here) */
+ for (j = 0; j < visible_cells; j++)
+ {
+ CellInfo *info = sizes[j].data;
+ AllocatedCell *cell;
+
+ if (info->expand)
+ {
+ sizes[j].minimum_size += extra_size;
+ if (extra_extra)
+ {
+ sizes[j].minimum_size++;
+ extra_extra--;
+ }
+ }
+
+ if (rtl)
+ cell = allocated_cell_new (info->renderer,
+ full_size - (position + sizes[j].minimum_size),
+ sizes[j].minimum_size);
+ else
+ cell = allocated_cell_new (info->renderer, position, sizes[j].minimum_size);
+
+ allocated_cells = g_slist_prepend (allocated_cells, cell);
+
+ position += sizes[j].minimum_size;
+ position += priv->spacing;
+ }
+
+ g_free (sizes);
+ }
+ }
+
+ /* Note it might not be important to reverse the list here at all,
+ * we have the correct positions, no need to allocate from left to right */
+ return g_slist_reverse (allocated_cells);
+}
+
+
+static void
+gtk_cell_area_box_focus_changed (GtkCellArea *area,
+ GParamSpec *pspec,
+ GtkCellAreaBox *box)
+{
+ if (gtk_cell_area_get_focus_cell (area))
+ box->priv->last_focus_cell = gtk_cell_area_get_focus_cell (area);
+}
+
+/*************************************************************
+ * GObjectClass *
+ *************************************************************/
+static void
+gtk_cell_area_box_finalize (GObject *object)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GSList *l;
+
+ /* Unref/free the context list */
+ for (l = priv->contexts; l; l = l->next)
+ g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)context_weak_notify, box);
+
+ g_slist_free (priv->contexts);
+ priv->contexts = NULL;
+
+ /* Free the cell grouping info */
+ cell_groups_clear (box);
+ g_array_free (priv->groups, TRUE);
+
+ G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
+}
+
+static void
+gtk_cell_area_box_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
+}
+
+static void
+gtk_cell_area_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ box->priv->orientation = g_value_get_enum (value);
+
+ /* Notify that size needs to be requested again */
+ reset_contexts (box);
+ break;
+ case PROP_SPACING:
+ gtk_cell_area_box_set_spacing (box, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_cell_area_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, box->priv->orientation);
+ break;
+ case PROP_SPACING:
+ g_value_set_int (value, gtk_cell_area_box_get_spacing (box));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*************************************************************
+ * GtkCellAreaClass *
+ *************************************************************/
+static void
+gtk_cell_area_box_add (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
+ renderer, FALSE, TRUE);
+}
+
+static void
+gtk_cell_area_box_remove (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *node;
+
+ if (priv->last_focus_cell == renderer)
+ priv->last_focus_cell = NULL;
+
+ node = g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find);
+
+ if (node)
+ {
+ CellInfo *info = node->data;
+
+ cell_info_free (info);
+
+ priv->cells = g_list_delete_link (priv->cells, node);
+
+ /* Reconstruct cell groups */
+ cell_groups_rebuild (box);
+ }
+ else
+ g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
+}
+
+static void
+gtk_cell_area_box_foreach (GtkCellArea *area,
+ GtkCellCallback callback,
+ gpointer callback_data)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *list;
+
+ for (list = priv->cells; list; list = list->next)
+ {
+ CellInfo *info = list->data;
+
+ if (callback (info->renderer, callback_data))
+ break;
+ }
+}
+
+static void
+gtk_cell_area_box_foreach_alloc (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ const GdkRectangle *background_area,
+ GtkCellAllocCallback callback,
+ gpointer callback_data)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
+ GSList *allocated_cells, *l;
+ GdkRectangle cell_alloc, cell_background;
+ gboolean rtl;
+
+ rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+
+ cell_alloc = *cell_area;
+
+ /* Get a list of cells with allocation sizes decided regardless
+ * of alignments and pack order etc. */
+ allocated_cells = get_allocated_cells (box, box_context, widget,
+ cell_area->width, cell_area->height);
+
+ for (l = allocated_cells; l; l = l->next)
+ {
+ AllocatedCell *cell = l->data;
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ cell_alloc.x = cell_area->x + cell->position;
+ cell_alloc.width = cell->size;
+ }
+ else
+ {
+ cell_alloc.y = cell_area->y + cell->position;
+ cell_alloc.height = cell->size;
+ }
+
+ /* Stop iterating over cells if they flow out of the render area,
+ * this can happen because the render area can actually be
+ * smaller than the requested area (treeview columns can
+ * be user resizable and can be resized to be smaller than
+ * the actual requested area). */
+ if (cell_alloc.x > cell_area->x + cell_area->width ||
+ cell_alloc.x + cell_alloc.width < cell_area->x ||
+ cell_alloc.y > cell_area->y + cell_area->height)
+ break;
+
+ /* Special case for the last cell (or first cell in rtl)... let the last cell consume
+ * the remaining space in the area (the last cell is allowed to consume the remaining
+ * space if the space given for rendering is actually larger than allocation, this can
+ * happen in the expander GtkTreeViewColumn where only the deepest depth column
+ * receives the allocation... shallow columns recieve more width). */
+ if (!l->next)
+ {
+ if (rtl)
+ {
+ /* Fill the leading space for the first cell in the area (still last in the list) */
+ cell_alloc.width = (cell_alloc.x - cell_area->x) + cell_alloc.width;
+ cell_alloc.x = cell_area->x;
+ }
+ else
+ {
+ cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x;
+ cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
+ }
+ }
+ else
+ {
+ /* If the cell we are rendering doesnt fit into the remaining space, clip it
+ * so that the underlying renderer has a chance to deal with it (for instance
+ * text renderers get a chance to ellipsize).
+ */
+ if (cell_alloc.x + cell_alloc.width > cell_area->x + cell_area->width)
+ cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x;
+
+ if (cell_alloc.y + cell_alloc.height > cell_area->y + cell_area->height)
+ cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
+ }
+
+ /* Add portions of the background_area to the cell_alloc
+ * to create the cell_background */
+ cell_background = cell_alloc;
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (l == allocated_cells)
+ {
+ /* Add the depth to the first cell */
+ if (rtl)
+ {
+ cell_background.width += background_area->width - cell_area->width;
+ cell_background.x = background_area->x + background_area->width - cell_background.width;
+ }
+ else
+ {
+ cell_background.width += cell_area->x - background_area->x;
+ cell_background.x = background_area->x;
+ }
+ }
+
+ if (l->next == NULL)
+ {
+ /* Grant this cell the remaining space */
+ int remain = cell_background.x - background_area->x;
+
+ if (rtl)
+ cell_background.x -= remain;
+ else
+ cell_background.width = background_area->width - remain;
+ }
+
+ cell_background.y = background_area->y;
+ cell_background.height = background_area->height;
+ }
+ else
+ {
+ if (l == allocated_cells)
+ {
+ cell_background.height += cell_background.y - background_area->y;
+ cell_background.y = background_area->y;
+ }
+
+ if (l->next == NULL)
+ cell_background.height =
+ background_area->height - (cell_background.y - background_area->y);
+
+ cell_background.x = background_area->x;
+ cell_background.width = background_area->width;
+ }
+
+ if (callback (cell->renderer, &cell_alloc, &cell_background, callback_data))
+ break;
+ }
+
+ g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
+ g_slist_free (allocated_cells);
+}
+
+static void
+gtk_cell_area_box_set_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *node;
+ CellInfo *info;
+ gboolean rebuild = FALSE;
+ gboolean val;
+ GtkPackType pack_type;
+
+ node = g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find);
+ if (!node)
+ return;
+
+ info = node->data;
+
+ switch (prop_id)
+ {
+ case CELL_PROP_EXPAND:
+ val = g_value_get_boolean (value);
+
+ if (info->expand != val)
+ {
+ info->expand = val;
+ rebuild = TRUE;
+ }
+ break;
+
+ case CELL_PROP_ALIGN:
+ val = g_value_get_boolean (value);
+
+ if (info->align != val)
+ {
+ info->align = val;
+ rebuild = TRUE;
+ }
+ break;
+
+ case CELL_PROP_PACK_TYPE:
+ pack_type = g_value_get_enum (value);
+
+ if (info->pack != pack_type)
+ {
+ info->pack = pack_type;
+ rebuild = TRUE;
+ }
+ break;
+ default:
+ GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
+ break;
+ }
+
+ /* Groups need to be rebuilt */
+ if (rebuild)
+ cell_groups_rebuild (box);
+}
+
+static void
+gtk_cell_area_box_get_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *node;
+ CellInfo *info;
+
+ node = g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find);
+ if (!node)
+ return;
+
+ info = node->data;
+
+ switch (prop_id)
+ {
+ case CELL_PROP_EXPAND:
+ g_value_set_boolean (value, info->expand);
+ break;
+
+ case CELL_PROP_ALIGN:
+ g_value_set_boolean (value, info->align);
+ break;
+
+ case CELL_PROP_PACK_TYPE:
+ g_value_set_enum (value, info->pack);
+ break;
+ default:
+ GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
+ break;
+ }
+}
+
+
+static GtkCellAreaContext *
+gtk_cell_area_box_create_context (GtkCellArea *area)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GtkCellAreaContext *context =
+ (GtkCellAreaContext *)g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT,
+ "area", area, NULL);
+
+ priv->contexts = g_slist_prepend (priv->contexts, context);
+
+ g_object_weak_ref (G_OBJECT (context), (GWeakNotify)context_weak_notify, box);
+
+ /* Tell the new group about our cell layout */
+ init_context_group (box, GTK_CELL_AREA_BOX_CONTEXT (context));
+
+ return context;
+}
+
+static GtkCellAreaContext *
+gtk_cell_area_box_copy_context (GtkCellArea *area,
+ GtkCellAreaContext *context)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GtkCellAreaContext *copy =
+ (GtkCellAreaContext *)gtk_cell_area_box_context_copy (GTK_CELL_AREA_BOX (area),
+ GTK_CELL_AREA_BOX_CONTEXT (context));
+
+ priv->contexts = g_slist_prepend (priv->contexts, copy);
+
+ g_object_weak_ref (G_OBJECT (copy), (GWeakNotify)context_weak_notify, box);
+
+ return copy;
+}
+
+static GtkSizeRequestMode
+gtk_cell_area_box_get_request_mode (GtkCellArea *area)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+
+ return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ?
+ GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
+ GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+}
+
+static void
+compute_size (GtkCellAreaBox *box,
+ GtkOrientation orientation,
+ GtkCellAreaBoxContext *context,
+ GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GtkCellArea *area = GTK_CELL_AREA (box);
+ GList *list;
+ gint i;
+ gint min_size = 0;
+ gint nat_size = 0;
+
+ for (i = 0; i < priv->groups->len; i++)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+ gint group_min_size = 0;
+ gint group_nat_size = 0;
+
+ for (list = group->cells; list; list = list->next)
+ {
+ CellInfo *info = list->data;
+ gint renderer_min_size, renderer_nat_size;
+
+ if (!gtk_cell_renderer_get_visible (info->renderer))
+ continue;
+
+ gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size,
+ &renderer_min_size, &renderer_nat_size);
+
+ if (orientation == priv->orientation)
+ {
+ if (min_size > 0)
+ {
+ min_size += priv->spacing;
+ nat_size += priv->spacing;
+ }
+
+ if (group_min_size > 0)
+ {
+ group_min_size += priv->spacing;
+ group_nat_size += priv->spacing;
+ }
+
+ min_size += renderer_min_size;
+ nat_size += renderer_nat_size;
+ group_min_size += renderer_min_size;
+ group_nat_size += renderer_nat_size;
+ }
+ else
+ {
+ min_size = MAX (min_size, renderer_min_size);
+ nat_size = MAX (nat_size, renderer_nat_size);
+ group_min_size = MAX (group_min_size, renderer_min_size);
+ group_nat_size = MAX (group_nat_size, renderer_nat_size);
+ }
+ }
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (for_size < 0)
+ gtk_cell_area_box_context_push_group_width (context, group->id, group_min_size, group_nat_size);
+ else
+ gtk_cell_area_box_context_push_group_width_for_height (context, group->id, for_size,
+ group_min_size, group_nat_size);
+ }
+ else
+ {
+ if (for_size < 0)
+ gtk_cell_area_box_context_push_group_height (context, group->id, group_min_size, group_nat_size);
+ else
+ gtk_cell_area_box_context_push_group_height_for_width (context, group->id, for_size,
+ group_min_size, group_nat_size);
+ }
+ }
+
+ *minimum_size = min_size;
+ *natural_size = nat_size;
+
+ /* Update rtl state for focus navigation to work */
+ priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+}
+
+GtkRequestedSize *
+get_group_sizes (GtkCellArea *area,
+ CellGroup *group,
+ GtkOrientation orientation,
+ GtkWidget *widget,
+ gint *n_sizes)
+{
+ GtkRequestedSize *sizes;
+ GList *l;
+ gint i;
+
+ *n_sizes = count_visible_cells (group, NULL);
+ sizes = g_new (GtkRequestedSize, *n_sizes);
+
+ for (l = group->cells, i = 0; l; l = l->next)
+ {
+ CellInfo *info = l->data;
+
+ if (!gtk_cell_renderer_get_visible (info->renderer))
+ continue;
+
+ sizes[i].data = info;
+
+ gtk_cell_area_request_renderer (area, info->renderer,
+ orientation, widget, -1,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+
+ i++;
+ }
+
+ return sizes;
+}
+
+static void
+compute_group_size_for_opposing_orientation (GtkCellAreaBox *box,
+ CellGroup *group,
+ GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GtkCellArea *area = GTK_CELL_AREA (box);
+
+ /* Exception for single cell groups */
+ if (group->n_cells == 1)
+ {
+ CellInfo *info = group->cells->data;
+
+ gtk_cell_area_request_renderer (area, info->renderer,
+ OPPOSITE_ORIENTATION (priv->orientation),
+ widget, for_size, minimum_size, natural_size);
+ }
+ else
+ {
+ GtkRequestedSize *orientation_sizes;
+ CellInfo *info;
+ gint n_sizes, i;
+ gint avail_size = for_size;
+ gint extra_size, extra_extra;
+ gint min_size = 0, nat_size = 0;
+
+ orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes);
+
+ /* First naturally allocate the cells in the group into the for_size */
+ avail_size -= (n_sizes - 1) * priv->spacing;
+ for (i = 0; i < n_sizes; i++)
+ avail_size -= orientation_sizes[i].minimum_size;
+
+ if (avail_size > 0)
+ avail_size = gtk_distribute_natural_allocation (avail_size, n_sizes, orientation_sizes);
+ else
+ avail_size = 0;
+
+ /* Calculate/distribute expand for cells */
+ if (group->expand_cells > 0)
+ {
+ extra_size = avail_size / group->expand_cells;
+ extra_extra = avail_size % group->expand_cells;
+ }
+ else
+ extra_size = extra_extra = 0;
+
+ for (i = 0; i < n_sizes; i++)
+ {
+ gint cell_min, cell_nat;
+
+ info = orientation_sizes[i].data;
+
+ if (info->expand)
+ {
+ orientation_sizes[i].minimum_size += extra_size;
+ if (extra_extra)
+ {
+ orientation_sizes[i].minimum_size++;
+ extra_extra--;
+ }
+ }
+
+ gtk_cell_area_request_renderer (area, info->renderer,
+ OPPOSITE_ORIENTATION (priv->orientation),
+ widget,
+ orientation_sizes[i].minimum_size,
+ &cell_min, &cell_nat);
+
+ min_size = MAX (min_size, cell_min);
+ nat_size = MAX (nat_size, cell_nat);
+ }
+
+ *minimum_size = min_size;
+ *natural_size = nat_size;
+
+ g_free (orientation_sizes);
+ }
+}
+
+static void
+compute_size_for_opposing_orientation (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *context,
+ GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ CellGroup *group;
+ GtkRequestedSize *orientation_sizes;
+ gint n_groups, n_expand_groups, i;
+ gint avail_size = for_size;
+ gint extra_size, extra_extra;
+ gint min_size = 0, nat_size = 0;
+
+ n_expand_groups = count_expand_groups (box);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ orientation_sizes = gtk_cell_area_box_context_get_widths (context, &n_groups);
+ else
+ orientation_sizes = gtk_cell_area_box_context_get_heights (context, &n_groups);
+
+ /* First start by naturally allocating space among groups of cells */
+ avail_size -= (n_groups - 1) * priv->spacing;
+ for (i = 0; i < n_groups; i++)
+ avail_size -= orientation_sizes[i].minimum_size;
+
+ if (avail_size > 0)
+ avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes);
+ else
+ avail_size = 0;
+
+ /* Calculate/distribute expand for groups */
+ if (n_expand_groups > 0)
+ {
+ extra_size = avail_size / n_expand_groups;
+ extra_extra = avail_size % n_expand_groups;
+ }
+ else
+ extra_size = extra_extra = 0;
+
+ /* Now we need to naturally allocate sizes for cells in each group
+ * and push the height-for-width for each group accordingly while accumulating
+ * the overall height-for-width for this row.
+ */
+ for (i = 0; i < n_groups; i++)
+ {
+ gint group_min, group_nat;
+ gint group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
+
+ group = &g_array_index (priv->groups, CellGroup, group_idx);
+
+ if (group->expand_cells > 0)
+ {
+ orientation_sizes[i].minimum_size += extra_size;
+ if (extra_extra)
+ {
+ orientation_sizes[i].minimum_size++;
+ extra_extra--;
+ }
+ }
+
+ /* Now we have the allocation for the group, request it's height-for-width */
+ compute_group_size_for_opposing_orientation (box, group, widget,
+ orientation_sizes[i].minimum_size,
+ &group_min, &group_nat);
+
+ min_size = MAX (min_size, group_min);
+ nat_size = MAX (nat_size, group_nat);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ gtk_cell_area_box_context_push_group_height_for_width (context, group_idx, for_size,
+ group_min, group_nat);
+ }
+ else
+ {
+ gtk_cell_area_box_context_push_group_width_for_height (context, group_idx, for_size,
+ group_min, group_nat);
+ }
+ }
+
+ *minimum_size = min_size;
+ *natural_size = nat_size;
+
+ g_free (orientation_sizes);
+
+ /* Update rtl state for focus navigation to work */
+ priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+}
+
+
+
+static void
+gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxContext *box_context;
+ gint min_width, nat_width;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
+
+ box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
+
+ /* Compute the size of all renderers for current row data,
+ * bumping cell alignments in the context along the way */
+ compute_size (box, GTK_ORIENTATION_HORIZONTAL,
+ box_context, widget, -1, &min_width, &nat_width);
+
+ if (minimum_width)
+ *minimum_width = min_width;
+
+ if (natural_width)
+ *natural_width = nat_width;
+}
+
+static void
+gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxContext *box_context;
+ gint min_height, nat_height;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
+
+ box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
+
+ /* Compute the size of all renderers for current row data,
+ * bumping cell alignments in the context along the way */
+ compute_size (box, GTK_ORIENTATION_VERTICAL,
+ box_context, widget, -1, &min_height, &nat_height);
+
+ if (minimum_height)
+ *minimum_height = min_height;
+
+ if (natural_height)
+ *natural_height = nat_height;
+}
+
+static void
+gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxContext *box_context;
+ GtkCellAreaBoxPrivate *priv;
+ gint min_height, nat_height;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
+
+ box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
+ priv = box->priv;
+
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL)
+ {
+ /* Add up vertical requests of height for width and push the overall
+ * cached sizes for alignments */
+ compute_size (box, priv->orientation, box_context, widget, width, &min_height, &nat_height);
+ }
+ else
+ {
+ /* Juice: virtually allocate cells into the for_width using the
+ * alignments and then return the overall height for that width, and cache it */
+ compute_size_for_opposing_orientation (box, box_context, widget, width, &min_height, &nat_height);
+ }
+
+ if (minimum_height)
+ *minimum_height = min_height;
+
+ if (natural_height)
+ *natural_height = nat_height;
+}
+
+static void
+gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
+ GtkCellAreaContext *context,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxContext *box_context;
+ GtkCellAreaBoxPrivate *priv;
+ gint min_width, nat_width;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
+
+ box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
+ priv = box->priv;
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ /* Add up horizontal requests of width for height and push the overall
+ * cached sizes for alignments */
+ compute_size (box, priv->orientation, box_context, widget, height, &min_width, &nat_width);
+ }
+ else
+ {
+ /* Juice: horizontally allocate cells into the for_height using the
+ * alignments and then return the overall width for that height, and cache it */
+ compute_size_for_opposing_orientation (box, box_context, widget, height, &min_width, &nat_width);
+ }
+
+ if (minimum_width)
+ *minimum_width = min_width;
+
+ if (natural_width)
+ *natural_width = nat_width;
+}
+
+enum {
+ FOCUS_NONE,
+ FOCUS_PREV,
+ FOCUS_NEXT,
+ FOCUS_LAST_CELL
+};
+
+static gboolean
+gtk_cell_area_box_focus (GtkCellArea *area,
+ GtkDirectionType direction)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ gint cycle = FOCUS_NONE;
+ gboolean cycled_focus = FALSE;
+ GtkCellRenderer *focus_cell;
+
+ focus_cell = gtk_cell_area_get_focus_cell (area);
+
+ /* Special case, when there is no activatable cell, focus
+ * is painted around the entire area... in this case we
+ * let focus leave the area directly.
+ */
+ if (focus_cell && !gtk_cell_area_is_activatable (area))
+ {
+ gtk_cell_area_set_focus_cell (area, NULL);
+ return FALSE;
+ }
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
+ break;
+ case GTK_DIR_TAB_BACKWARD:
+ cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
+ break;
+ case GTK_DIR_UP:
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
+ cycle = FOCUS_PREV;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ case GTK_DIR_DOWN:
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
+ cycle = FOCUS_NEXT;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ case GTK_DIR_LEFT:
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
+ cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ case GTK_DIR_RIGHT:
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
+ cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ default:
+ break;
+ }
+
+ if (cycle == FOCUS_LAST_CELL)
+ {
+ gtk_cell_area_set_focus_cell (area, priv->last_focus_cell);
+ cycled_focus = TRUE;
+ }
+ else if (cycle != FOCUS_NONE)
+ {
+ gboolean found_cell = FALSE;
+ GList *list;
+ gint i;
+
+ /* If there is no focused cell, focus on the first (or last) one in the list */
+ if (!focus_cell)
+ found_cell = TRUE;
+
+ for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1;
+ cycled_focus == FALSE && i >= 0 && i < priv->groups->len;
+ i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells);
+ cycled_focus == FALSE && list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev)
+ {
+ CellInfo *info = list->data;
+
+ if (info->renderer == focus_cell)
+ found_cell = TRUE;
+ else if (found_cell && /* Dont give focus to cells that are siblings to a focus cell */
+ gtk_cell_area_get_focus_from_sibling (area, info->renderer) == NULL)
+ {
+ gtk_cell_area_set_focus_cell (area, info->renderer);
+ cycled_focus = TRUE;
+ }
+ }
+ }
+ }
+
+ if (!cycled_focus)
+ gtk_cell_area_set_focus_cell (area, NULL);
+
+ return cycled_focus;
+}
+
+
+/*************************************************************
+ * GtkCellLayoutIface *
+ *************************************************************/
+static void
+gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface)
+{
+ iface->pack_start = gtk_cell_area_box_layout_pack_start;
+ iface->pack_end = gtk_cell_area_box_layout_pack_end;
+ iface->reorder = gtk_cell_area_box_layout_reorder;
+}
+
+static void
+gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gboolean expand)
+{
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
+}
+
+static void
+gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gboolean expand)
+{
+ gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
+}
+
+static void
+gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout,
+ GtkCellRenderer *renderer,
+ gint position)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (cell_layout);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *node;
+ CellInfo *info;
+
+ node = g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find);
+
+ if (node)
+ {
+ info = node->data;
+
+ priv->cells = g_list_delete_link (priv->cells, node);
+ priv->cells = g_list_insert (priv->cells, info, position);
+
+ cell_groups_rebuild (box);
+ }
+}
+
+/*************************************************************
+ * API *
+ *************************************************************/
+/**
+ * gtk_cell_area_box_new:
+ *
+ * Creates a new #GtkCellAreaBox.
+ *
+ * Return value: a newly created #GtkCellAreaBox
+ */
+GtkCellArea *
+gtk_cell_area_box_new (void)
+{
+ return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL);
+}
+
+/**
+ * gtk_cell_area_box_pack_start:
+ * @box: a #GtkCellAreaBox
+ * @renderer: the #GtkCellRenderer to add
+ * @expand: whether @renderer should receive extra space when the area receives
+ * more than its natural size
+ * @align: whether @renderer should be aligned in adjacent rows.
+ *
+ * Adds @renderer to @box, packed with reference to the start of @box.
+ *
+ * The @renderer is packed after any other #GtkCellRenderer packed with reference
+ * to the start of @box.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_box_pack_start (GtkCellAreaBox *box,
+ GtkCellRenderer *renderer,
+ gboolean expand,
+ gboolean align)
+{
+ GtkCellAreaBoxPrivate *priv;
+ CellInfo *info;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ priv = box->priv;
+
+ if (g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find))
+ {
+ g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice");
+ return;
+ }
+
+ info = cell_info_new (renderer, GTK_PACK_START, expand, align);
+
+ priv->cells = g_list_append (priv->cells, info);
+
+ cell_groups_rebuild (box);
+}
+
+/**
+ * gtk_cell_area_box_pack_end:
+ * @box: a #GtkCellAreaBox
+ * @renderer: the #GtkCellRenderer to add
+ * @expand: whether @renderer should receive extra space when the area receives
+ * more than its natural size
+ * @align: whether @renderer should be aligned in adjacent rows.
+ *
+ * Adds @renderer to @box, packed with reference to the end of @box.
+ *
+ * The @renderer is packed after (away from end of) any other #GtkCellRenderer
+ * packed with reference to the end of @box.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_box_pack_end (GtkCellAreaBox *box,
+ GtkCellRenderer *renderer,
+ gboolean expand,
+ gboolean align)
+{
+ GtkCellAreaBoxPrivate *priv;
+ CellInfo *info;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ priv = box->priv;
+
+ if (g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find))
+ {
+ g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice");
+ return;
+ }
+
+ info = cell_info_new (renderer, GTK_PACK_END, expand, align);
+
+ priv->cells = g_list_append (priv->cells, info);
+
+ cell_groups_rebuild (box);
+}
+
+/**
+ * gtk_cell_area_box_get_spacing:
+ * @box: a #GtkCellAreaBox
+ *
+ * Gets the spacing added between cell renderers.
+ *
+ * Return value: the space added between cell renderers in @box.
+ *
+ * Since: 3.0
+ */
+gint
+gtk_cell_area_box_get_spacing (GtkCellAreaBox *box)
+{
+ g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0);
+
+ return box->priv->spacing;
+}
+
+/**
+ * gtk_cell_area_box_set_spacing:
+ * @box: a #GtkCellAreaBox
+ * @spacing: the space to add between #GtkCellRenderers
+ *
+ * Sets the spacing to add between cell renderers in @box.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_box_set_spacing (GtkCellAreaBox *box,
+ gint spacing)
+{
+ GtkCellAreaBoxPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
+
+ priv = box->priv;
+
+ if (priv->spacing != spacing)
+ {
+ priv->spacing = spacing;
+
+ g_object_notify (G_OBJECT (box), "spacing");
+
+ /* Notify that size needs to be requested again */
+ reset_contexts (box);
+ }
+}
diff --git a/gtk/gtkcellareabox.h b/gtk/gtkcellareabox.h
new file mode 100644
index 0000000000..a6d3a061bb
--- /dev/null
+++ b/gtk/gtkcellareabox.h
@@ -0,0 +1,84 @@
+/* gtkcellareabox.h
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_CELL_AREA_BOX_H__
+#define __GTK_CELL_AREA_BOX_H__
+
+#include <gtk/gtkcellarea.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CELL_AREA_BOX (gtk_cell_area_box_get_type ())
+#define GTK_CELL_AREA_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_BOX, GtkCellAreaBox))
+#define GTK_CELL_AREA_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA_BOX, GtkCellAreaBoxClass))
+#define GTK_IS_CELL_AREA_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_BOX))
+#define GTK_IS_CELL_AREA_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA_BOX))
+#define GTK_CELL_AREA_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA_BOX, GtkCellAreaBoxClass))
+
+typedef struct _GtkCellAreaBox GtkCellAreaBox;
+typedef struct _GtkCellAreaBoxClass GtkCellAreaBoxClass;
+typedef struct _GtkCellAreaBoxPrivate GtkCellAreaBoxPrivate;
+
+struct _GtkCellAreaBox
+{
+ /*< private >*/
+ GtkCellArea parent_instance;
+
+ GtkCellAreaBoxPrivate *priv;
+};
+
+struct _GtkCellAreaBoxClass
+{
+ /*< private >*/
+ GtkCellAreaClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+};
+
+GType gtk_cell_area_box_get_type (void) G_GNUC_CONST;
+
+GtkCellArea *gtk_cell_area_box_new (void);
+void gtk_cell_area_box_pack_start (GtkCellAreaBox *box,
+ GtkCellRenderer *renderer,
+ gboolean expand,
+ gboolean align);
+void gtk_cell_area_box_pack_end (GtkCellAreaBox *box,
+ GtkCellRenderer *renderer,
+ gboolean expand,
+ gboolean align);
+gint gtk_cell_area_box_get_spacing (GtkCellAreaBox *box);
+void gtk_cell_area_box_set_spacing (GtkCellAreaBox *box,
+ gint spacing);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CELL_AREA_BOX_H__ */
diff --git a/gtk/gtkcellareaboxcontext.c b/gtk/gtkcellareaboxcontext.c
new file mode 100644
index 0000000000..2c0fa22ac7
--- /dev/null
+++ b/gtk/gtkcellareaboxcontext.c
@@ -0,0 +1,853 @@
+/* gtkcellareaboxcontext.c
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtkintl.h"
+#include "gtkcellareabox.h"
+#include "gtkcellareaboxcontext.h"
+#include "gtkorientable.h"
+
+/* GObjectClass */
+static void gtk_cell_area_box_context_finalize (GObject *object);
+
+/* GtkCellAreaContextClass */
+static void gtk_cell_area_box_context_reset (GtkCellAreaContext *context);
+static void gtk_cell_area_box_context_allocate (GtkCellAreaContext *context,
+ gint width,
+ gint height);
+static void gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+
+
+
+/* Internal functions */
+static void gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context,
+ GtkOrientation orientation,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size);
+static void free_cache_array (GArray *array);
+static GArray *group_array_new (GtkCellAreaBoxContext *context);
+static GArray *get_array (GtkCellAreaBoxContext *context,
+ GtkOrientation orientation,
+ gint for_size);
+static gboolean group_expands (GtkCellAreaBoxContext *context,
+ gint group_idx);
+static gint count_expand_groups (GtkCellAreaBoxContext *context);
+
+
+/* CachedSize management */
+typedef struct {
+ gint min_size;
+ gint nat_size;
+} CachedSize;
+
+struct _GtkCellAreaBoxContextPrivate
+{
+ /* Table of per renderer CachedSizes */
+ GArray *base_widths;
+ GArray *base_heights;
+
+ /* Table of per height/width hash tables of per renderer CachedSizes */
+ GHashTable *widths;
+ GHashTable *heights;
+
+ /* Whether each group expands */
+ gboolean *expand;
+
+ /* Allocation info for this context if any */
+ gint alloc_width;
+ gint alloc_height;
+ gint n_orientation_allocs;
+ GtkCellAreaBoxAllocation *orientation_allocs;
+};
+
+G_DEFINE_TYPE (GtkCellAreaBoxContext, gtk_cell_area_box_context, GTK_TYPE_CELL_AREA_CONTEXT);
+
+static void
+free_cache_array (GArray *array)
+{
+ g_array_free (array, TRUE);
+}
+
+static GArray *
+group_array_new (GtkCellAreaBoxContext *context)
+{
+ GtkCellAreaBoxContextPrivate *priv = context->priv;
+ GArray *group_array;
+
+ group_array = g_array_new (FALSE, TRUE, sizeof (CachedSize));
+ g_array_set_size (group_array, priv->base_widths->len);
+
+ return group_array;
+}
+
+static GArray *
+get_array (GtkCellAreaBoxContext *context,
+ GtkOrientation orientation,
+ gint for_size)
+{
+ GtkCellAreaBoxContextPrivate *priv = context->priv;
+ GArray *array;
+
+ if (for_size < 0)
+ {
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ array = priv->base_widths;
+ else
+ array = priv->base_heights;
+ }
+ else
+ {
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_size));
+
+ if (!array)
+ array = priv->base_widths;
+ }
+ else
+ {
+ array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_size));
+
+ if (!array)
+ array = priv->base_heights;
+ }
+ }
+
+ return array;
+}
+
+static gboolean
+group_expands (GtkCellAreaBoxContext *context,
+ gint group_idx)
+{
+ GtkCellAreaBoxContextPrivate *priv = context->priv;
+
+ g_assert (group_idx >= 0 && group_idx < priv->base_widths->len);
+
+ return priv->expand[group_idx];
+}
+
+static gint
+count_expand_groups (GtkCellAreaBoxContext *context)
+{
+ GtkCellAreaBoxContextPrivate *priv = context->priv;
+ gint i, expand = 0;
+
+ for (i = 0; i < priv->base_widths->len; i++)
+ {
+ if (priv->expand[i])
+ expand++;
+ }
+
+ return expand;
+}
+
+static void
+gtk_cell_area_box_context_init (GtkCellAreaBoxContext *box_context)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+
+ box_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (box_context,
+ GTK_TYPE_CELL_AREA_BOX_CONTEXT,
+ GtkCellAreaBoxContextPrivate);
+ priv = box_context->priv;
+
+ priv->base_widths = g_array_new (FALSE, TRUE, sizeof (CachedSize));
+ priv->base_heights = g_array_new (FALSE, TRUE, sizeof (CachedSize));
+
+ priv->widths = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify)free_cache_array);
+ priv->heights = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify)free_cache_array);
+
+ priv->alloc_width = 0;
+ priv->alloc_height = 0;
+ priv->orientation_allocs = NULL;
+ priv->n_orientation_allocs = 0;
+}
+
+static void
+gtk_cell_area_box_context_class_init (GtkCellAreaBoxContextClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkCellAreaContextClass *context_class = GTK_CELL_AREA_CONTEXT_CLASS (class);
+
+ /* GObjectClass */
+ object_class->finalize = gtk_cell_area_box_context_finalize;
+
+ context_class->reset = gtk_cell_area_box_context_reset;
+ context_class->allocate = gtk_cell_area_box_context_allocate;
+ context_class->get_preferred_height_for_width = gtk_cell_area_box_context_get_preferred_height_for_width;
+ context_class->get_preferred_width_for_height = gtk_cell_area_box_context_get_preferred_width_for_height;
+
+ g_type_class_add_private (object_class, sizeof (GtkCellAreaBoxContextPrivate));
+}
+
+/*************************************************************
+ * GObjectClass *
+ *************************************************************/
+static void
+gtk_cell_area_box_context_finalize (GObject *object)
+{
+ GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (object);
+ GtkCellAreaBoxContextPrivate *priv = box_context->priv;
+
+ g_array_free (priv->base_widths, TRUE);
+ g_array_free (priv->base_heights, TRUE);
+ g_hash_table_destroy (priv->widths);
+ g_hash_table_destroy (priv->heights);
+
+ g_free (priv->orientation_allocs);
+ g_free (priv->expand);
+
+ G_OBJECT_CLASS (gtk_cell_area_box_context_parent_class)->finalize (object);
+}
+
+/*************************************************************
+ * GtkCellAreaContextClass *
+ *************************************************************/
+static void
+gtk_cell_area_box_context_reset (GtkCellAreaContext *context)
+{
+ GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
+ GtkCellAreaBoxContextPrivate *priv = box_context->priv;
+ CachedSize *size;
+ gint i;
+
+ for (i = 0; i < priv->base_widths->len; i++)
+ {
+ size = &g_array_index (priv->base_widths, CachedSize, i);
+
+ size->min_size = 0;
+ size->nat_size = 0;
+
+ size = &g_array_index (priv->base_heights, CachedSize, i);
+
+ size->min_size = 0;
+ size->nat_size = 0;
+ }
+
+ /* Reset context sizes as well */
+ g_hash_table_remove_all (priv->widths);
+ g_hash_table_remove_all (priv->heights);
+
+ /* Clear the allocation */
+ g_free (priv->orientation_allocs);
+ priv->orientation_allocs = NULL;
+ priv->n_orientation_allocs = 0;
+
+ GTK_CELL_AREA_CONTEXT_CLASS
+ (gtk_cell_area_box_context_parent_class)->reset (context);
+}
+
+static GtkRequestedSize *
+gtk_cell_area_box_context_get_requests (GtkCellAreaBoxContext *box_context,
+ GtkOrientation orientation,
+ gint for_size,
+ gint *n_requests)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ GtkRequestedSize *requests;
+ GArray *array;
+ CachedSize *size;
+ gint visible_groups = 0;
+ gint i, j;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context), NULL);
+
+ priv = box_context->priv;
+ array = get_array (box_context, orientation, for_size);
+
+ for (i = 0; i < array->len; i++)
+ {
+ size = &g_array_index (array, CachedSize, i);
+
+ if (size->nat_size > 0)
+ visible_groups++;
+ }
+
+ requests = g_new (GtkRequestedSize, visible_groups);
+
+ for (j = 0, i = 0; i < array->len; i++)
+ {
+ size = &g_array_index (array, CachedSize, i);
+
+ if (size->nat_size > 0)
+ {
+ requests[j].data = GINT_TO_POINTER (i);
+ requests[j].minimum_size = size->min_size;
+ requests[j].natural_size = size->nat_size;
+ j++;
+ }
+ }
+
+ if (n_requests)
+ *n_requests = visible_groups;
+
+ return requests;
+}
+
+static GtkCellAreaBoxAllocation *
+allocate_for_orientation (GtkCellAreaBoxContext *context,
+ GtkOrientation orientation,
+ gint spacing,
+ gint size,
+ gint for_size,
+ gint *n_allocs)
+{
+ GtkCellAreaBoxAllocation *allocs;
+ GtkRequestedSize *sizes;
+ GArray *array;
+ gint n_expand_groups = 0;
+ gint i, n_groups, position;
+ gint extra_size, extra_extra;
+ gint avail_size = size;
+
+ sizes = gtk_cell_area_box_context_get_requests (context, orientation, for_size, &n_groups);
+ array = get_array (context, orientation, for_size);
+ n_expand_groups = count_expand_groups (context);
+
+ /* First start by naturally allocating space among groups */
+ avail_size -= (n_groups - 1) * spacing;
+ for (i = 0; i < n_groups; i++)
+ avail_size -= sizes[i].minimum_size;
+
+ if (avail_size > 0)
+ avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, sizes);
+ else
+ avail_size = 0;
+
+ /* Calculate/distribute expand for groups */
+ if (n_expand_groups > 0)
+ {
+ extra_size = avail_size / n_expand_groups;
+ extra_extra = avail_size % n_expand_groups;
+ }
+ else
+ extra_size = extra_extra = 0;
+
+ allocs = g_new (GtkCellAreaBoxAllocation, n_groups);
+
+ for (position = 0, i = 0; i < n_groups; i++)
+ {
+ allocs[i].group_idx = GPOINTER_TO_INT (sizes[i].data);
+ allocs[i].position = position;
+ allocs[i].size = sizes[i].minimum_size;
+
+ if (group_expands (context, allocs[i].group_idx))
+ {
+ allocs[i].size += extra_size;
+ if (extra_extra)
+ {
+ allocs[i].size++;
+ extra_extra--;
+ }
+ }
+
+ position += allocs[i].size;
+ position += spacing;
+ }
+
+ if (n_allocs)
+ *n_allocs = n_groups;
+
+ g_free (sizes);
+
+ return allocs;
+}
+
+static void
+gtk_cell_area_box_context_allocate (GtkCellAreaContext *context,
+ gint width,
+ gint height)
+{
+ GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
+ GtkCellAreaBoxContextPrivate *priv = box_context->priv;
+ GtkCellArea *area;
+ GtkOrientation orientation;
+ gint spacing;
+
+ area = gtk_cell_area_context_get_area (context);
+ orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
+ spacing = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
+
+ g_free (priv->orientation_allocs);
+ priv->orientation_allocs = NULL;
+ priv->n_orientation_allocs = 0;
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL && width > 0)
+ priv->orientation_allocs = allocate_for_orientation (box_context, orientation,
+ spacing, width, height,
+ &priv->n_orientation_allocs);
+ else if (orientation == GTK_ORIENTATION_VERTICAL && height > 0)
+ priv->orientation_allocs = allocate_for_orientation (box_context, orientation,
+ spacing, height, width,
+ &priv->n_orientation_allocs);
+
+ GTK_CELL_AREA_CONTEXT_CLASS (gtk_cell_area_box_context_parent_class)->allocate (context, width, height);
+}
+
+static void
+gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context,
+ GtkOrientation orientation,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkCellArea *area;
+ GtkOrientation box_orientation;
+ GArray *array;
+ gint spacing, i;
+ gint min_size = 0, nat_size = 0;
+
+ area = gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (context));
+ spacing = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
+ box_orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
+ array = get_array (context, orientation, for_size);
+
+ for (i = 0; i < array->len; i++)
+ {
+ CachedSize *size = &g_array_index (array, CachedSize, i);
+
+ if (box_orientation == orientation)
+ {
+ /* Dont add spacing for 0 size groups, they can be 0 size because
+ * they contain only invisible cells for this round of requests
+ */
+ if (min_size > 0 && size->nat_size > 0)
+ {
+ min_size += spacing;
+ nat_size += spacing;
+ }
+
+ min_size += size->min_size;
+ nat_size += size->nat_size;
+ }
+ else
+ {
+ min_size = MAX (min_size, size->min_size);
+ nat_size = MAX (nat_size, size->nat_size);
+ }
+ }
+
+ if (for_size < 0)
+ {
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_cell_area_context_push_preferred_width (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size);
+ else
+ gtk_cell_area_context_push_preferred_height (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size);
+ }
+
+ if (minimum_size)
+ *minimum_size = min_size;
+ if (natural_size)
+ *natural_size = nat_size;
+}
+
+static void
+gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_VERTICAL,
+ width, minimum_height, natural_height);
+}
+
+static void
+gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_HORIZONTAL,
+ height, minimum_width, natural_width);
+}
+
+/*************************************************************
+ * API *
+ *************************************************************/
+static void
+copy_size_array (GArray *src_array,
+ GArray *dest_array)
+{
+ gint i;
+
+ for (i = 0; i < src_array->len; i++)
+ {
+ CachedSize *src = &g_array_index (src_array, CachedSize, i);
+ CachedSize *dest = &g_array_index (dest_array, CachedSize, i);
+
+ memcpy (dest, src, sizeof (CachedSize));
+ }
+}
+
+static void
+for_size_copy (gpointer key,
+ GArray *size_array,
+ GHashTable *dest_hash)
+{
+ GArray *new_array;
+
+ new_array = g_array_new (FALSE, TRUE, sizeof (CachedSize));
+ g_array_set_size (new_array, size_array->len);
+
+ copy_size_array (size_array, new_array);
+
+ g_hash_table_insert (dest_hash, key, new_array);
+}
+
+GtkCellAreaBoxContext *
+gtk_cell_area_box_context_copy (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *context)
+{
+ GtkCellAreaBoxContext *copy;
+
+ copy = g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT,
+ "area", box, NULL);
+
+ gtk_cell_area_box_init_groups (copy,
+ context->priv->base_widths->len,
+ context->priv->expand);
+
+ /* Copy the base arrays */
+ copy_size_array (context->priv->base_widths,
+ copy->priv->base_widths);
+ copy_size_array (context->priv->base_heights,
+ copy->priv->base_heights);
+
+ /* Copy each for size */
+ g_hash_table_foreach (context->priv->heights,
+ (GHFunc)for_size_copy, copy->priv->heights);
+ g_hash_table_foreach (context->priv->widths,
+ (GHFunc)for_size_copy, copy->priv->widths);
+
+ /* Copy any active allocation */
+ copy->priv->n_orientation_allocs =
+ context->priv->n_orientation_allocs;
+
+ if (copy->priv->n_orientation_allocs)
+ copy->priv->orientation_allocs =
+ g_memdup (context->priv->orientation_allocs,
+ copy->priv->n_orientation_allocs * sizeof (GtkCellAreaBoxAllocation));
+
+ return copy;
+}
+
+void
+gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context,
+ guint n_groups,
+ gboolean *expand_groups)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+ g_return_if_fail (n_groups == 0 || expand_groups != NULL);
+
+ /* When the group dimensions change, all info must be reset
+ * Note this already clears the min/nat values on the CachedSizes
+ */
+ gtk_cell_area_context_reset (GTK_CELL_AREA_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_array_set_size (priv->base_widths, n_groups);
+ g_array_set_size (priv->base_heights, n_groups);
+
+ g_free (priv->expand);
+ priv->expand = g_memdup (expand_groups, n_groups * sizeof (gboolean));
+}
+
+void
+gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint minimum_width,
+ gint natural_width)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ CachedSize *size;
+ gboolean grew = FALSE;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_widths->len);
+
+ size = &g_array_index (priv->base_widths, CachedSize, group_idx);
+ if (minimum_width > size->min_size)
+ {
+ size->min_size = minimum_width;
+ grew = TRUE;
+ }
+ if (natural_width > size->nat_size)
+ {
+ size->nat_size = natural_width;
+ grew = TRUE;
+ }
+
+ if (grew)
+ gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL);
+}
+
+void
+gtk_cell_area_box_context_push_group_height_for_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_width,
+ gint minimum_height,
+ gint natural_height)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ GArray *group_array;
+ CachedSize *size;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_widths->len);
+
+ group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
+ if (!group_array)
+ {
+ group_array = group_array_new (box_context);
+ g_hash_table_insert (priv->heights, GINT_TO_POINTER (for_width), group_array);
+ }
+
+ size = &g_array_index (group_array, CachedSize, group_idx);
+ size->min_size = MAX (size->min_size, minimum_height);
+ size->nat_size = MAX (size->nat_size, natural_height);
+}
+
+void
+gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint minimum_height,
+ gint natural_height)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ CachedSize *size;
+ gboolean grew = FALSE;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_heights->len);
+
+ size = &g_array_index (priv->base_heights, CachedSize, group_idx);
+ if (minimum_height > size->min_size)
+ {
+ size->min_size = minimum_height;
+ grew = TRUE;
+ }
+ if (natural_height > size->nat_size)
+ {
+ size->nat_size = natural_height;
+ grew = TRUE;
+ }
+
+ if (grew)
+ gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_VERTICAL, -1, NULL, NULL);
+}
+
+void
+gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_height,
+ gint minimum_width,
+ gint natural_width)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ GArray *group_array;
+ CachedSize *size;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_widths->len);
+
+ group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
+ if (!group_array)
+ {
+ group_array = group_array_new (box_context);
+ g_hash_table_insert (priv->widths, GINT_TO_POINTER (for_height), group_array);
+ }
+
+ size = &g_array_index (group_array, CachedSize, group_idx);
+ size->min_size = MAX (size->min_size, minimum_width);
+ size->nat_size = MAX (size->nat_size, natural_width);
+}
+
+void
+gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ CachedSize *size;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_widths->len);
+
+ size = &g_array_index (priv->base_widths, CachedSize, group_idx);
+
+ if (minimum_width)
+ *minimum_width = size->min_size;
+
+ if (natural_width)
+ *natural_width = size->nat_size;
+}
+
+void
+gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ GArray *group_array;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_widths->len);
+
+ group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
+
+ if (group_array)
+ {
+ CachedSize *size = &g_array_index (group_array, CachedSize, group_idx);
+
+ if (minimum_height)
+ *minimum_height = size->min_size;
+
+ if (natural_height)
+ *natural_height = size->nat_size;
+ }
+ else
+ {
+ if (minimum_height)
+ *minimum_height = -1;
+
+ if (natural_height)
+ *natural_height = -1;
+ }
+}
+
+void
+gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ CachedSize *size;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_heights->len);
+
+ size = &g_array_index (priv->base_heights, CachedSize, group_idx);
+
+ if (minimum_height)
+ *minimum_height = size->min_size;
+
+ if (natural_height)
+ *natural_height = size->nat_size;
+}
+
+void
+gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+ GArray *group_array;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
+
+ priv = box_context->priv;
+ g_return_if_fail (group_idx < priv->base_widths->len);
+
+ group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
+
+ if (group_array)
+ {
+ CachedSize *size = &g_array_index (group_array, CachedSize, group_idx);
+
+ if (minimum_width)
+ *minimum_width = size->min_size;
+
+ if (natural_width)
+ *natural_width = size->nat_size;
+ }
+ else
+ {
+ if (minimum_width)
+ *minimum_width = -1;
+
+ if (natural_width)
+ *natural_width = -1;
+ }
+}
+
+GtkRequestedSize *
+gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context,
+ gint *n_widths)
+{
+ return gtk_cell_area_box_context_get_requests (box_context, GTK_ORIENTATION_HORIZONTAL, -1, n_widths);
+}
+
+GtkRequestedSize *
+gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context,
+ gint *n_heights)
+{
+ return gtk_cell_area_box_context_get_requests (box_context, GTK_ORIENTATION_VERTICAL, -1, n_heights);
+}
+
+GtkCellAreaBoxAllocation *
+gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context,
+ gint *n_allocs)
+{
+ GtkCellAreaBoxContextPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context), NULL);
+
+ priv = context->priv;
+
+ *n_allocs = priv->n_orientation_allocs;
+
+ return priv->orientation_allocs;
+}
diff --git a/gtk/gtkcellareaboxcontext.h b/gtk/gtkcellareaboxcontext.h
new file mode 100644
index 0000000000..1161d38b2f
--- /dev/null
+++ b/gtk/gtkcellareaboxcontext.h
@@ -0,0 +1,138 @@
+/* gtkcellareaboxcontext.h
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_CELL_AREA_BOX_CONTEXT_H__
+#define __GTK_CELL_AREA_BOX_CONTEXT_H__
+
+#include <gtk/gtkcellareacontext.h>
+#include <gtk/gtkcellareabox.h>
+#include <gtk/gtkcellrenderer.h>
+#include <gtk/gtksizerequest.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CELL_AREA_BOX_CONTEXT (gtk_cell_area_box_context_get_type ())
+#define GTK_CELL_AREA_BOX_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContext))
+#define GTK_CELL_AREA_BOX_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContextClass))
+#define GTK_IS_CELL_AREA_BOX_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT))
+#define GTK_IS_CELL_AREA_BOX_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA_BOX_CONTEXT))
+#define GTK_CELL_AREA_BOX_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContextClass))
+
+typedef struct _GtkCellAreaBoxContext GtkCellAreaBoxContext;
+typedef struct _GtkCellAreaBoxContextClass GtkCellAreaBoxContextClass;
+typedef struct _GtkCellAreaBoxContextPrivate GtkCellAreaBoxContextPrivate;
+
+struct _GtkCellAreaBoxContext
+{
+ GtkCellAreaContext parent_instance;
+
+ GtkCellAreaBoxContextPrivate *priv;
+};
+
+struct _GtkCellAreaBoxContextClass
+{
+ GtkCellAreaContextClass parent_class;
+
+};
+
+GType gtk_cell_area_box_context_get_type (void) G_GNUC_CONST;
+
+
+/* Create a duplicate of the context */
+GtkCellAreaBoxContext *gtk_cell_area_box_context_copy (GtkCellAreaBox *box,
+ GtkCellAreaBoxContext *box_context);
+
+/* Initialize group array dimensions */
+void gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context,
+ guint n_groups,
+ gboolean *expand_groups);
+
+/* Update cell-group sizes */
+void gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint minimum_width,
+ gint natural_width);
+
+void gtk_cell_area_box_context_push_group_height_for_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_width,
+ gint minimum_height,
+ gint natural_height);
+
+void gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint minimum_height,
+ gint natural_height);
+
+void gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_height,
+ gint minimum_width,
+ gint natural_width);
+
+/* Fetch cell-group sizes */
+void gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint *minimum_width,
+ gint *natural_width);
+
+void gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_width,
+ gint *minimum_height,
+ gint *natural_height);
+
+void gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint *minimum_height,
+ gint *natural_height);
+
+void gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context,
+ gint group_idx,
+ gint for_height,
+ gint *minimum_width,
+ gint *natural_width);
+
+GtkRequestedSize *gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context,
+ gint *n_widths);
+GtkRequestedSize *gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context,
+ gint *n_heights);
+
+/* Private context/area interaction */
+typedef struct {
+ gint group_idx; /* Groups containing only invisible cells are not allocated */
+ gint position; /* Relative group allocation position in the orientation of the box */
+ gint size; /* Full allocated size of the cells in this group spacing inclusive */
+} GtkCellAreaBoxAllocation;
+
+GtkCellAreaBoxAllocation *
+gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context,
+ gint *n_allocs);
+
+G_END_DECLS
+
+#endif /* __GTK_CELL_AREA_BOX_CONTEXT_H__ */
diff --git a/gtk/gtkcellareacontext.c b/gtk/gtkcellareacontext.c
new file mode 100644
index 0000000000..f3c14b00dd
--- /dev/null
+++ b/gtk/gtkcellareacontext.c
@@ -0,0 +1,674 @@
+/* gtkcellareacontext.c
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gtkcellareacontext
+ * @Short_Description: An object for a #GtkCellArea to store geometrical information for a series of rows.
+ * @Title: GtkCellAreaContext
+ *
+ * The #GtkCellAreaContext object is created by a given #GtkCellArea implementation via it's
+ * #GtkCellAreaClass.create_context() virtual method and is used to store cell sizes and alignments
+ * for a series of #GtkTreeModel rows that are requested and rendered in the same context.
+ *
+ * #GtkCellLayout widgets can create any number of contexts in which to request and render
+ * groups of data rows. However its important that the same context which was used to
+ * request sizes for a given #GtkTreeModel row also be used for the same row when calling
+ * other #GtkCellArea apis such as gtk_cell_area_render() and gtk_cell_area_event().
+ */
+#include "config.h"
+#include "gtkintl.h"
+#include "gtkmarshalers.h"
+#include "gtkcellareacontext.h"
+#include "gtkprivate.h"
+
+/* GObjectClass */
+static void gtk_cell_area_context_dispose (GObject *object);
+static void gtk_cell_area_context_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gtk_cell_area_context_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+/* GtkCellAreaContextClass */
+static void gtk_cell_area_context_real_reset (GtkCellAreaContext *context);
+static void gtk_cell_area_context_real_allocate (GtkCellAreaContext *context,
+ gint width,
+ gint height);
+
+struct _GtkCellAreaContextPrivate
+{
+ GtkCellArea *cell_area;
+
+ gint min_width;
+ gint nat_width;
+ gint min_height;
+ gint nat_height;
+ gint alloc_width;
+ gint alloc_height;
+};
+
+enum {
+ PROP_0,
+ PROP_CELL_AREA,
+ PROP_MIN_WIDTH,
+ PROP_NAT_WIDTH,
+ PROP_MIN_HEIGHT,
+ PROP_NAT_HEIGHT
+};
+
+G_DEFINE_TYPE (GtkCellAreaContext, gtk_cell_area_context, G_TYPE_OBJECT);
+
+static void
+gtk_cell_area_context_init (GtkCellAreaContext *context)
+{
+ GtkCellAreaContextPrivate *priv;
+
+ context->priv = G_TYPE_INSTANCE_GET_PRIVATE (context,
+ GTK_TYPE_CELL_AREA_CONTEXT,
+ GtkCellAreaContextPrivate);
+ priv = context->priv;
+
+ priv->min_width = -1;
+ priv->nat_width = -1;
+ priv->min_height = -1;
+ priv->nat_height = -1;
+}
+
+static void
+gtk_cell_area_context_class_init (GtkCellAreaContextClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ /* GObjectClass */
+ object_class->dispose = gtk_cell_area_context_dispose;
+ object_class->get_property = gtk_cell_area_context_get_property;
+ object_class->set_property = gtk_cell_area_context_set_property;
+
+ /* GtkCellAreaContextClass */
+ class->reset = gtk_cell_area_context_real_reset;
+ class->allocate = gtk_cell_area_context_real_allocate;
+
+ /**
+ * GtkCellAreaContext:area:
+ *
+ * The #GtkCellArea this context was created by
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_CELL_AREA,
+ g_param_spec_object ("area",
+ P_("Area"),
+ P_("The Cell Area this context was created for"),
+ GTK_TYPE_CELL_AREA,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GtkCellAreaContext:minimum-width:
+ *
+ * The minimum width for the #GtkCellArea in this context
+ * for all #GtkTreeModel rows that this context was requested
+ * for using gtk_cell_area_get_preferred_width().
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_MIN_WIDTH,
+ g_param_spec_int ("minimum-width",
+ P_("Minimum Width"),
+ P_("Minimum cached width"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READABLE));
+
+ /**
+ * GtkCellAreaContext:natural-width:
+ *
+ * The natural width for the #GtkCellArea in this context
+ * for all #GtkTreeModel rows that this context was requested
+ * for using gtk_cell_area_get_preferred_width().
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_NAT_WIDTH,
+ g_param_spec_int ("natural-width",
+ P_("Minimum Width"),
+ P_("Minimum cached width"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READABLE));
+
+ /**
+ * GtkCellAreaContext:minimum-height:
+ *
+ * The minimum height for the #GtkCellArea in this context
+ * for all #GtkTreeModel rows that this context was requested
+ * for using gtk_cell_area_get_preferred_height().
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_MIN_HEIGHT,
+ g_param_spec_int ("minimum-height",
+ P_("Minimum Height"),
+ P_("Minimum cached height"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READABLE));
+
+ /**
+ * GtkCellAreaContext:natural-height:
+ *
+ * The natural height for the #GtkCellArea in this context
+ * for all #GtkTreeModel rows that this context was requested
+ * for using gtk_cell_area_get_preferred_height().
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_NAT_HEIGHT,
+ g_param_spec_int ("natural-height",
+ P_("Minimum Height"),
+ P_("Minimum cached height"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READABLE));
+
+ g_type_class_add_private (object_class, sizeof (GtkCellAreaContextPrivate));
+}
+
+/*************************************************************
+ * GObjectClass *
+ *************************************************************/
+static void
+gtk_cell_area_context_dispose (GObject *object)
+{
+ GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
+ GtkCellAreaContextPrivate *priv = context->priv;
+
+ if (priv->cell_area)
+ {
+ g_object_unref (priv->cell_area);
+
+ priv->cell_area = NULL;
+ }
+
+ G_OBJECT_CLASS (gtk_cell_area_context_parent_class)->dispose (object);
+}
+
+static void
+gtk_cell_area_context_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
+ GtkCellAreaContextPrivate *priv = context->priv;
+
+ switch (prop_id)
+ {
+ case PROP_CELL_AREA:
+ priv->cell_area = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_cell_area_context_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
+ GtkCellAreaContextPrivate *priv = context->priv;
+
+ switch (prop_id)
+ {
+ case PROP_CELL_AREA:
+ g_value_set_object (value, priv->cell_area);
+ break;
+ case PROP_MIN_WIDTH:
+ g_value_set_int (value, priv->min_width);
+ break;
+ case PROP_NAT_WIDTH:
+ g_value_set_int (value, priv->nat_width);
+ break;
+ case PROP_MIN_HEIGHT:
+ g_value_set_int (value, priv->min_height);
+ break;
+ case PROP_NAT_HEIGHT:
+ g_value_set_int (value, priv->nat_height);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*************************************************************
+ * GtkCellAreaContextClass *
+ *************************************************************/
+static void
+gtk_cell_area_context_real_reset (GtkCellAreaContext *context)
+{
+ GtkCellAreaContextPrivate *priv = context->priv;
+
+ g_object_freeze_notify (G_OBJECT (context));
+
+ if (priv->min_width != -1)
+ {
+ priv->min_width = -1;
+ g_object_notify (G_OBJECT (context), "minimum-width");
+ }
+
+ if (priv->nat_width != -1)
+ {
+ priv->nat_width = -1;
+ g_object_notify (G_OBJECT (context), "natural-width");
+ }
+
+ if (priv->min_height != -1)
+ {
+ priv->min_height = -1;
+ g_object_notify (G_OBJECT (context), "minimum-height");
+ }
+
+ if (priv->nat_height != -1)
+ {
+ priv->nat_height = -1;
+ g_object_notify (G_OBJECT (context), "natural-height");
+ }
+
+ priv->alloc_width = 0;
+ priv->alloc_height = 0;
+
+ g_object_thaw_notify (G_OBJECT (context));
+}
+
+static void
+gtk_cell_area_context_real_allocate (GtkCellAreaContext *context,
+ gint width,
+ gint height)
+{
+ GtkCellAreaContextPrivate *priv = context->priv;
+
+ priv->alloc_width = width;
+ priv->alloc_height = height;
+}
+
+/*************************************************************
+ * API *
+ *************************************************************/
+/**
+ * gtk_cell_area_context_get_area:
+ * @context: a #GtkCellAreaContext
+ *
+ * Fetches the #GtkCellArea this @context was created by.
+ *
+ * This is generally unneeded by layouting widgets however
+ * its important for the context implementation itself to
+ * fetch information about the area it is being used for.
+ *
+ * For instance at #GtkCellAreaContextClass.allocate() time
+ * it's important to know details about any cell spacing
+ * that the #GtkCellArea is configured with in order to
+ * compute a proper allocation.
+ *
+ * Return value: the #GtkCellArea this context was created by.
+ *
+ * Since: 3.0
+ */
+GtkCellArea *
+gtk_cell_area_context_get_area (GtkCellAreaContext *context)
+{
+ GtkCellAreaContextPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL);
+
+ priv = context->priv;
+
+ return priv->cell_area;
+}
+
+/**
+ * gtk_cell_area_context_reset:
+ * @context: a #GtkCellAreaContext
+ *
+ * Resets any previously cached request and allocation
+ * data.
+ *
+ * When underlying #GtkTreeModel data changes it's
+ * important to reset the context if the content
+ * size is allowed to shrink. If the content size
+ * is only allowed to grow (this is usually an option
+ * for views rendering large data stores as a measure
+ * of optimization), then only the row that changed
+ * or was inserted needs to be (re)requested with
+ * gtk_cell_area_get_preferred_width().
+ *
+ * When the new overall size of the context requires
+ * that the allocated size changes (or whenever this
+ * allocation changes at all), the variable row
+ * sizes need to be re-requested for every row.
+ *
+ * For instance, if the rows are displayed all with
+ * the same width from top to bottom then a change
+ * in the allocated width necessitates a recalculation
+ * of all the displayed row heights using
+ * gtk_cell_area_get_preferred_height_for_width().
+ *
+ * Since 3.0
+ */
+void
+gtk_cell_area_context_reset (GtkCellAreaContext *context)
+{
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->reset (context);
+}
+
+/**
+ * gtk_cell_area_context_allocate:
+ * @context: a #GtkCellAreaContext
+ * @width: the allocated width for all #GtkTreeModel rows rendered with @context, or -1.
+ * @height: the allocated height for all #GtkTreeModel rows rendered with @context, or -1.
+ *
+ * Allocates a width and/or a height for all rows which are to be rendered with @context.
+ *
+ * Usually allocation is performed only horizontally or sometimes vertically since
+ * a group of rows are usually rendered side by side vertically or horizontally and
+ * share either the same width or the same hieght. Sometimes they are allocated in
+ * both horizontal and vertical orientations producing a homogenious effect of the
+ * rows. This is generally the case for #GtkTreeView when #GtkTreeView:fixed-height-mode
+ * is enabled.
+ *
+ * Since 3.0
+ */
+void
+gtk_cell_area_context_allocate (GtkCellAreaContext *context,
+ gint width,
+ gint height)
+{
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->allocate (context, width, height);
+}
+
+/**
+ * gtk_cell_area_context_get_preferred_width:
+ * @context: a #GtkCellAreaContext
+ * @minimum_width: (out) (allow-none): location to store the minimum width, or %NULL
+ * @natural_width: (out) (allow-none): location to store the natural width, or %NULL
+ *
+ * Gets the accumulative preferred width for all rows which have been requested
+ * with this context.
+ *
+ * After gtk_cell_area_context_reset() is called and/or before ever requesting
+ * the size of a #GtkCellArea, the returned values are -1.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkCellAreaContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ priv = context->priv;
+
+ if (minimum_width)
+ *minimum_width = priv->min_width;
+
+ if (natural_width)
+ *natural_width = priv->nat_width;
+}
+
+/**
+ * gtk_cell_area_context_get_preferred_height:
+ * @context: a #GtkCellAreaContext
+ * @minimum_height: (out) (allow-none): location to store the minimum height, or %NULL
+ * @natural_height: (out) (allow-none): location to store the natural height, or %NULL
+ *
+ * Gets the accumulative preferred height for all rows which have been requested
+ * with this context.
+ *
+ * After gtk_cell_area_context_reset() is called and/or before ever requesting
+ * the size of a #GtkCellArea, the returned values are -1.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkCellAreaContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ priv = context->priv;
+
+ if (minimum_height)
+ *minimum_height = priv->min_height;
+
+ if (natural_height)
+ *natural_height = priv->nat_height;
+}
+
+/**
+ * gtk_cell_area_context_get_preferred_height_for_width:
+ * @context: a #GtkCellAreaContext
+ * @width: a proposed width for allocation
+ * @minimum_height: (out) (allow-none): location to store the minimum height, or %NULL
+ * @natural_height: (out) (allow-none): location to store the natural height, or %NULL
+ *
+ * Gets the accumulative preferred height for @width for all rows which have been
+ * requested for the same said @width with this context.
+ *
+ * After gtk_cell_area_context_reset() is called and/or before ever requesting
+ * the size of a #GtkCellArea, the returned values are -1.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width)
+ GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width (context,
+ width,
+ minimum_height,
+ natural_height);
+}
+
+/**
+ * gtk_cell_area_context_get_preferred_width_for_height:
+ * @context: a #GtkCellAreaContext
+ * @height: a proposed height for allocation
+ * @minimum_width: (out) (allow-none): location to store the minimum width, or %NULL
+ * @natural_width: (out) (allow-none): location to store the natural width, or %NULL
+ *
+ * Gets the accumulative preferred width for @height for all rows which have
+ * been requested for the same said @height with this context.
+ *
+ * After gtk_cell_area_context_reset() is called and/or before ever requesting
+ * the size of a #GtkCellArea, the returned values are -1.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height)
+ GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height (context,
+ height,
+ minimum_width,
+ natural_width);
+}
+
+/**
+ * gtk_cell_area_context_get_allocation:
+ * @context: a #GtkCellAreaContext
+ * @width: (out) (allow-none): location to store the allocated width, or %NULL.
+ * @height: (out) (allow-none): location to store the allocated height, or %NULL.
+ *
+ * Fetches the current allocation size for @context.
+ *
+ * If the context was not allocated in width or height, or if the
+ * context was recently reset with gtk_cell_area_context_reset(),
+ * the returned value will be -1.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_context_get_allocation (GtkCellAreaContext *context,
+ gint *width,
+ gint *height)
+{
+ GtkCellAreaContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ priv = context->priv;
+
+ if (width)
+ *width = priv->alloc_width;
+
+ if (height)
+ *height = priv->alloc_height;
+}
+
+/**
+ * gtk_cell_area_context_push_preferred_width:
+ * @context: a #GtkCellAreaContext
+ * @minimum_width: the proposed new minimum width for @context.
+ * @natural_width: the proposed new natural width for @context.
+ *
+ * Causes the minimum and/or natural width to grow if the new
+ * proposed sizes exceed the current minimum and natural width.
+ *
+ * This is used by #GtkCellAreaContext implementations during
+ * the request process over a series of #GtkTreeModel rows to
+ * progressively push the requested width over a series of
+ * gtk_cell_area_get_preferred_width() requests.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context,
+ gint minimum_width,
+ gint natural_width)
+{
+ GtkCellAreaContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ priv = context->priv;
+
+ g_object_freeze_notify (G_OBJECT (context));
+
+ if (minimum_width > priv->min_width)
+ {
+ priv->min_width = minimum_width;
+
+ g_object_notify (G_OBJECT (context), "minimum-width");
+ }
+
+ if (natural_width > priv->nat_width)
+ {
+ priv->nat_width = natural_width;
+
+ g_object_notify (G_OBJECT (context), "natural-width");
+ }
+
+ g_object_thaw_notify (G_OBJECT (context));
+}
+
+/**
+ * gtk_cell_area_context_push_preferred_height:
+ * @context: a #GtkCellAreaContext
+ * @minimum_height: the proposed new minimum height for @context.
+ * @natural_height: the proposed new natural height for @context.
+ *
+ * Causes the minimum and/or natural height to grow if the new
+ * proposed sizes exceed the current minimum and natural height.
+ *
+ * This is used by #GtkCellAreaContext implementations during
+ * the request process over a series of #GtkTreeModel rows to
+ * progressively push the requested height over a series of
+ * gtk_cell_area_get_preferred_height() requests.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context,
+ gint minimum_height,
+ gint natural_height)
+{
+ GtkCellAreaContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
+
+ priv = context->priv;
+
+ g_object_freeze_notify (G_OBJECT (context));
+
+ if (minimum_height > priv->min_height)
+ {
+ priv->min_height = minimum_height;
+
+ g_object_notify (G_OBJECT (context), "minimum-height");
+ }
+
+ if (natural_height > priv->nat_height)
+ {
+ priv->nat_height = natural_height;
+
+ g_object_notify (G_OBJECT (context), "natural-height");
+ }
+
+ g_object_thaw_notify (G_OBJECT (context));
+}
diff --git a/gtk/gtkcellareacontext.h b/gtk/gtkcellareacontext.h
new file mode 100644
index 0000000000..3c3522bc59
--- /dev/null
+++ b/gtk/gtkcellareacontext.h
@@ -0,0 +1,133 @@
+/* gtkcellareacontext.h
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_CELL_AREA_CONTEXT_H__
+#define __GTK_CELL_AREA_CONTEXT_H__
+
+#include <gtk/gtkcellarea.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CELL_AREA_CONTEXT (gtk_cell_area_context_get_type ())
+#define GTK_CELL_AREA_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContext))
+#define GTK_CELL_AREA_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContextClass))
+#define GTK_IS_CELL_AREA_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_CONTEXT))
+#define GTK_IS_CELL_AREA_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA_CONTEXT))
+#define GTK_CELL_AREA_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContextClass))
+
+typedef struct _GtkCellAreaContextPrivate GtkCellAreaContextPrivate;
+typedef struct _GtkCellAreaContextClass GtkCellAreaContextClass;
+
+struct _GtkCellAreaContext
+{
+ /*< private >*/
+ GObject parent_instance;
+
+ GtkCellAreaContextPrivate *priv;
+};
+
+/**
+ * GtkCellAreaContextClass:
+ * @allocate: This tells the context that an allocation width or height (or both)
+ * have been decided for a group of rows. The context should store any allocations
+ * for internally aligned cells at this point so that they dont need to be
+ * recalculated at gtk_cell_area_render() time.
+ * @reset: Clear any previously stored information about requested and allocated
+ * sizes for the context.
+ * @get_preferred_height_for_width: Returns the aligned height for the given width
+ * that context must store while collecting sizes for it's rows.
+ * @get_preferred_width_for_height: Returns the aligned width for the given height
+ * that context must store while collecting sizes for it's rows.
+ */
+struct _GtkCellAreaContextClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ void (* allocate) (GtkCellAreaContext *context,
+ gint width,
+ gint height);
+ void (* reset) (GtkCellAreaContext *context);
+ void (* get_preferred_height_for_width) (GtkCellAreaContext *context,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+ void (* get_preferred_width_for_height) (GtkCellAreaContext *context,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+ void (*_gtk_reserved5) (void);
+ void (*_gtk_reserved6) (void);
+};
+
+GType gtk_cell_area_context_get_type (void) G_GNUC_CONST;
+
+/* Main apis */
+GtkCellArea *gtk_cell_area_context_get_area (GtkCellAreaContext *context);
+void gtk_cell_area_context_allocate (GtkCellAreaContext *context,
+ gint width,
+ gint height);
+void gtk_cell_area_context_reset (GtkCellAreaContext *context);
+
+/* Apis for GtkCellArea clients to consult cached values for a series of GtkTreeModel rows */
+void gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context,
+ gint *minimum_width,
+ gint *natural_width);
+void gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context,
+ gint *minimum_height,
+ gint *natural_height);
+void gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+void gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+void gtk_cell_area_context_get_allocation (GtkCellAreaContext *context,
+ gint *width,
+ gint *height);
+
+/* Apis for GtkCellArea implementations to update cached values for multiple GtkTreeModel rows */
+void gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context,
+ gint minimum_width,
+ gint natural_width);
+void gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context,
+ gint minimum_height,
+ gint natural_height);
+
+G_END_DECLS
+
+#endif /* __GTK_CELL_AREA_CONTEXT_H__ */
diff --git a/gtk/gtkcelllayout.c b/gtk/gtkcelllayout.c
index 8388b5f7d5..b2182d276e 100644
--- a/gtk/gtkcelllayout.c
+++ b/gtk/gtkcelllayout.c
@@ -17,11 +17,80 @@
* Boston, MA 02111-1307, USA.
*/
+/**
+ * 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 <emphasis>attributes</emphasis>. 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.
+ *
+ * <refsect2 id="GtkCellLayout-BUILDER-UI">
+ * <title>GtkCellLayouts as GtkBuildable</title>
+ * <para>
+ * Implementations of GtkCellLayout which also implement the GtkBuildable
+ * interface (#GtkCellView, #GtkIconView, #GtkComboBox, #GtkComboBoxEntry,
+ * #GtkEntryCompletion, #GtkTreeViewColumn) accept GtkCellRenderer objects
+ * as &lt;child&gt; elements in UI definitions. They support a custom
+ * &lt;attributes&gt; element for their children, which can contain
+ * multiple &lt;attribute&gt; elements. Each &lt;attribute&gt; element has
+ * a name attribute which specifies a property of the cell renderer; the
+ * content of the element is the attribute value.
+ *
+ * <example>
+ * <title>A UI definition fragment specifying attributes</title>
+ * <programlisting><![CDATA[
+ * <object class="GtkCellView">
+ * <child>
+ * <object class="GtkCellRendererText"/>
+ * <attributes>
+ * <attribute name="text">0</attribute>
+ * </attributes>
+ * </child>"
+ * </object>
+ * ]]></programlisting>
+ * </example>
+ *
+ * Furthermore for implementations of GtkCellLayout that use a #GtkCellArea
+ * to lay out cells (most, of not all GtkCellLayouts in GTK+ use a GtkCellArea)
+ * <link linkend="cell-properties">cell properties</link> can also be defined
+ * in the format by specifying the custom &lt;cell-packing&gt; attribute which
+ * can contain multiple &lt;property&gt; elements defined in the normal way.
+ * <example>
+ * <title>A UI definition fragment specifying cell properties</title>
+ * <programlisting><![CDATA[
+ * <object class="GtkTreeViewColumn">
+ * <child>
+ * <object class="GtkCellRendererText"/>
+ * <cell-packing>
+ * <property name="align">True</property>
+ * <property name="expand">False</property>
+ * </cell-packing>
+ * </child>"
+ * </object>
+ * ]]></programlisting>
+ * </example>
+ * </para>
+ * </refsect2>
+ */
+
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "gtkcelllayout.h"
+#include "gtkbuilderprivate.h"
#include "gtkintl.h"
@@ -53,12 +122,23 @@ gtk_cell_layout_pack_start (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
gboolean expand)
{
+ GtkCellLayoutIface *iface;
+ GtkCellArea *area;
+
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);
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ if (iface->pack_start)
+ iface->pack_start (cell_layout, cell, expand);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (area), cell, expand);
+ }
}
/**
@@ -80,12 +160,23 @@ gtk_cell_layout_pack_end (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
gboolean expand)
{
+ GtkCellLayoutIface *iface;
+ GtkCellArea *area;
+
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);
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ if (iface->pack_end)
+ iface->pack_end (cell_layout, cell, expand);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (area), cell, expand);
+ }
}
/**
@@ -100,9 +191,22 @@ gtk_cell_layout_pack_end (GtkCellLayout *cell_layout,
void
gtk_cell_layout_clear (GtkCellLayout *cell_layout)
{
+ GtkCellLayoutIface *iface;
+ GtkCellArea *area;
+
g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
- (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear) (cell_layout);
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ if (iface->clear)
+ iface->clear (cell_layout);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (area));
+ }
}
static void
@@ -113,17 +217,28 @@ gtk_cell_layout_set_attributesv (GtkCellLayout *cell_layout,
gchar *attribute;
gint column;
GtkCellLayoutIface *iface;
+ GtkCellArea *area;
attribute = va_arg (args, gchar *);
iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
- (* iface->clear_attributes) (cell_layout, cell);
+ if (iface->get_area)
+ area = iface->get_area (cell_layout);
+
+ if (iface->clear_attributes)
+ iface->clear_attributes (cell_layout, cell);
+ else if (area)
+ gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (area), cell);
while (attribute != NULL)
{
column = va_arg (args, gint);
- (* iface->add_attribute) (cell_layout, cell, attribute, column);
+ if (iface->add_attribute)
+ iface->add_attribute (cell_layout, cell, attribute, column);
+ else if (area)
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (area), cell, attribute, column);
+
attribute = va_arg (args, gchar *);
}
}
@@ -177,15 +292,28 @@ gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout,
const gchar *attribute,
gint column)
{
+ GtkCellLayoutIface *iface;
+ GtkCellArea *area;
+
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);
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ if (iface->add_attribute)
+ iface->add_attribute (cell_layout,
+ cell,
+ attribute,
+ column);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (area), cell, attribute, column);
+ }
}
/**
@@ -210,14 +338,27 @@ gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
gpointer func_data,
GDestroyNotify destroy)
{
+ GtkCellLayoutIface *iface;
+ GtkCellArea *area;
+
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);
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ if (iface->set_cell_data_func)
+ iface->set_cell_data_func (cell_layout,
+ cell,
+ func,
+ func_data,
+ destroy);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (area), cell, func, func_data, destroy);
+ }
}
/**
@@ -234,11 +375,23 @@ void
gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
GtkCellRenderer *cell)
{
+ GtkCellLayoutIface *iface;
+ GtkCellArea *area;
+
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);
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ if (iface->clear_attributes)
+ iface->clear_attributes (cell_layout, cell);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (area), cell);
+ }
}
/**
@@ -257,12 +410,23 @@ gtk_cell_layout_reorder (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
gint position)
{
+ GtkCellLayoutIface *iface;
+ GtkCellArea *area;
+
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);
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ if (iface->reorder)
+ iface->reorder (cell_layout, cell, position);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ gtk_cell_layout_reorder (GTK_CELL_LAYOUT (area), cell, position);
+ }
}
/**
@@ -281,16 +445,52 @@ GList *
gtk_cell_layout_get_cells (GtkCellLayout *cell_layout)
{
GtkCellLayoutIface *iface;
+ GtkCellArea *area;
g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL);
iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
if (iface->get_cells)
return iface->get_cells (cell_layout);
+ else
+ {
+ area = iface->get_area (cell_layout);
+
+ if (area)
+ return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
+ }
return NULL;
}
+/**
+ * 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.
+ *
+ * Return value: (transfer none): 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.
+ *
+ * Since: 3.0
+ */
+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;
@@ -364,6 +564,147 @@ static const GMarkupParser attributes_parser =
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 = { 0, };
+ 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;
+ 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 *parser_data = (CellPackingSubParserData*)user_data;
+ guint i;
+
+ if (strcmp (element_name, "property") == 0)
+ {
+ for (i = 0; names[i]; i++)
+ if (strcmp (names[i], "name") == 0)
+ parser_data->cell_prop_name = g_strdup (values[i]);
+ else if (strcmp (names[i], "translatable") == 0)
+ {
+ if (!_gtk_builder_boolean_from_string (values[i],
+ &parser_data->translatable,
+ error))
+ return;
+ }
+ else if (strcmp (names[i], "comments") == 0)
+ ; /* for translators */
+ else if (strcmp (names[i], "context") == 0)
+ parser_data->context = g_strdup (values[i]);
+ else
+ g_warning ("Unsupported attribute for GtkCellLayout Cell "
+ "property: %s\n", names[i]);
+ }
+ else if (strcmp (element_name, "cell-packing") == 0)
+ return;
+ else
+ g_warning ("Unsupported tag for GtkCellLayout: %s\n", element_name);
+}
+
+static void
+cell_packing_text_element (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ CellPackingSubParserData *parser_data = (CellPackingSubParserData*)user_data;
+ GtkCellArea *area;
+ gchar* value;
+
+ if (!parser_data->cell_prop_name)
+ return;
+
+ if (parser_data->translatable && text_len)
+ {
+ const gchar* domain;
+ domain = gtk_builder_get_translation_domain (parser_data->builder);
+
+ value = _gtk_builder_parser_translate (domain,
+ parser_data->context,
+ text);
+ }
+ else
+ {
+ value = g_strdup (text);
+ }
+
+ area = gtk_cell_layout_get_area (parser_data->cell_layout);
+
+ if (!area)
+ {
+ g_warning ("%s does not have an internal GtkCellArea class and cannot apply child cell properties",
+ g_type_name (G_OBJECT_TYPE (parser_data->cell_layout)));
+ return;
+ }
+
+ gtk_cell_layout_buildable_set_cell_property (area,
+ parser_data->builder,
+ parser_data->renderer,
+ parser_data->cell_prop_name,
+ value);
+
+ g_free (parser_data->cell_prop_name);
+ g_free (parser_data->context);
+ g_free (value);
+ parser_data->cell_prop_name = NULL;
+ parser_data->context = NULL;
+ parser_data->translatable = FALSE;
+}
+
+static const GMarkupParser cell_packing_parser =
+ {
+ cell_packing_start_element,
+ NULL,
+ cell_packing_text_element,
+ };
+
gboolean
_gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder,
@@ -372,38 +713,60 @@ _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable,
GMarkupParser *parser,
gpointer *data)
{
- AttributesSubParserData *parser_data;
+ AttributesSubParserData *attr_data;
+ CellPackingSubParserData *packing_data;
if (!child)
return FALSE;
if (strcmp (tagname, "attributes") == 0)
{
- parser_data = g_slice_new0 (AttributesSubParserData);
- parser_data->cell_layout = GTK_CELL_LAYOUT (buildable);
- parser_data->renderer = GTK_CELL_RENDERER (child);
- parser_data->attr_name = NULL;
+ attr_data = g_slice_new0 (AttributesSubParserData);
+ attr_data->cell_layout = GTK_CELL_LAYOUT (buildable);
+ attr_data->renderer = GTK_CELL_RENDERER (child);
+ attr_data->attr_name = NULL;
*parser = attributes_parser;
- *data = parser_data;
+ *data = attr_data;
+ return TRUE;
+ }
+ else if (strcmp (tagname, "cell-packing") == 0)
+ {
+ packing_data = g_slice_new0 (CellPackingSubParserData);
+ 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;
}
-void
+gboolean
_gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
gpointer *data)
{
- AttributesSubParserData *parser_data;
+ AttributesSubParserData *attr_data;
- parser_data = (AttributesSubParserData*)data;
- g_assert (!parser_data->attr_name);
- g_slice_free (AttributesSubParserData, parser_data);
+ if (strcmp (tagname, "attributes") == 0)
+ {
+ attr_data = (AttributesSubParserData*)data;
+ g_assert (!attr_data->attr_name);
+ g_slice_free (AttributesSubParserData, attr_data);
+ return TRUE;
+ }
+ else if (strcmp (tagname, "cell-packing") == 0)
+ {
+ g_slice_free (CellPackingSubParserData, (gpointer)data);
+ return TRUE;
+ }
+ return FALSE;
}
void
@@ -412,12 +775,8 @@ _gtk_cell_layout_buildable_add_child (GtkBuildable *buildable,
GObject *child,
const gchar *type)
{
- GtkCellLayoutIface *iface;
-
g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable));
g_return_if_fail (GTK_IS_CELL_RENDERER (child));
- iface = GTK_CELL_LAYOUT_GET_IFACE (buildable);
- g_return_if_fail (iface->pack_start != NULL);
- iface->pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
}
diff --git a/gtk/gtkcelllayout.h b/gtk/gtkcelllayout.h
index 04e4745ee9..6ab3844542 100644
--- a/gtk/gtkcelllayout.h
+++ b/gtk/gtkcelllayout.h
@@ -25,6 +25,7 @@
#define __GTK_CELL_LAYOUT_H__
#include <gtk/gtkcellrenderer.h>
+#include <gtk/gtkcellarea.h>
#include <gtk/gtktreeviewcolumn.h>
#include <gtk/gtkbuildable.h>
#include <gtk/gtkbuilder.h>
@@ -40,6 +41,17 @@ typedef struct _GtkCellLayout GtkCellLayout; /* dummy typedef */
typedef struct _GtkCellLayoutIface GtkCellLayoutIface;
/* keep in sync with GtkTreeCellDataFunc */
+/**
+ * GtkCellLayoutDataFunc:
+ * @cell_layout: a #GtkCellLayout
+ * @cell: the cell renderer whose value is to be set
+ * @tree_model: the model
+ * @iter: a #GtkTreeIter indicating the row to set the value for
+ * @data: user data passed to gtk_cell_layout_set_cell_data_func()
+ *
+ * A function which should set the value of @cell_layout's cell renderer(s)
+ * as appropriate.
+ */
typedef void (* GtkCellLayoutDataFunc) (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
@@ -73,6 +85,8 @@ struct _GtkCellLayoutIface
GtkCellRenderer *cell,
gint position);
GList* (* get_cells) (GtkCellLayout *cell_layout);
+
+ GtkCellArea *(* get_area) (GtkCellLayout *cell_layout);
};
GType gtk_cell_layout_get_type (void) G_GNUC_CONST;
@@ -101,13 +115,15 @@ void gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
void gtk_cell_layout_reorder (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
gint position);
+GtkCellArea *gtk_cell_layout_get_area (GtkCellLayout *cell_layout);
+
gboolean _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
GMarkupParser *parser,
gpointer *data);
-void _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
+gboolean _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
diff --git a/gtk/gtkcellrenderer.c b/gtk/gtkcellrenderer.c
index 05e60689d1..e15323e830 100644
--- a/gtk/gtkcellrenderer.c
+++ b/gtk/gtkcellrenderer.c
@@ -97,7 +97,11 @@ static void gtk_cell_renderer_real_get_preferred_width_for_height(GtkCellRendere
gint height,
gint *minimum_width,
gint *natural_width);
-
+static void gtk_cell_renderer_real_get_aligned_area (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GtkCellRendererState flags,
+ const GdkRectangle *cell_area,
+ GdkRectangle *aligned_area);
struct _GtkCellRendererPrivate
@@ -192,6 +196,7 @@ gtk_cell_renderer_class_init (GtkCellRendererClass *class)
class->get_preferred_height = gtk_cell_renderer_real_get_preferred_height;
class->get_preferred_width_for_height = gtk_cell_renderer_real_get_preferred_width_for_height;
class->get_preferred_height_for_width = gtk_cell_renderer_real_get_preferred_height_for_width;
+ class->get_aligned_area = gtk_cell_renderer_real_get_aligned_area;
/**
* GtkCellRenderer::editing-canceled:
@@ -693,6 +698,9 @@ gtk_cell_renderer_render (GtkCellRenderer *cell,
cairo_fill (cr);
}
+ gdk_cairo_rectangle (cr, background_area);
+ cairo_clip (cr);
+
GTK_CELL_RENDERER_GET_CLASS (cell)->render (cell,
cr,
widget,
@@ -1097,6 +1105,32 @@ gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell)
return cell->priv->sensitive;
}
+
+/**
+ * gtk_cell_renderer_is_activatable:
+ * @cell: A #GtkCellRenderer
+ *
+ * Checks whether the cell renderer can do something when activated.
+ *
+ * Returns: %TRUE if the cell renderer can do anything when activated.
+ *
+ * Since: 3.0
+ */
+gboolean
+gtk_cell_renderer_is_activatable (GtkCellRenderer *cell)
+{
+ GtkCellRendererPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE);
+
+ priv = cell->priv;
+
+ return (cell->priv->visible &&
+ (cell->priv->mode == GTK_CELL_RENDERER_MODE_EDITABLE ||
+ cell->priv->mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE));
+}
+
+
/**
* gtk_cell_renderer_stop_editing:
* @cell: A #GtkCellRenderer
@@ -1210,6 +1244,67 @@ gtk_cell_renderer_real_get_preferred_width_for_height (GtkCellRenderer *cell,
gtk_cell_renderer_get_preferred_width (cell, widget, minimum_width, natural_width);
}
+
+/* Default implementation assumes that a cell renderer will never use more
+ * space than it's natural size (this is fine for toggles and pixbufs etc
+ * but needs to be overridden from wrapping/ellipsizing text renderers) */
+static void
+gtk_cell_renderer_real_get_aligned_area (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GtkCellRendererState flags,
+ const GdkRectangle *cell_area,
+ GdkRectangle *aligned_area)
+{
+ gint opposite_size, x_offset, y_offset;
+ gint natural_size;
+
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (cell_area != NULL);
+ g_return_if_fail (aligned_area != NULL);
+
+ *aligned_area = *cell_area;
+
+ /* Trim up the aligned size */
+ if (gtk_cell_renderer_get_request_mode (cell) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ gtk_cell_renderer_get_preferred_width (cell, widget,
+ NULL, &natural_size);
+
+ aligned_area->width = MIN (aligned_area->width, natural_size);
+
+ gtk_cell_renderer_get_preferred_height_for_width (cell, widget,
+ aligned_area->width,
+ NULL, &opposite_size);
+
+ aligned_area->height = MIN (opposite_size, aligned_area->height);
+ }
+ else
+ {
+ gtk_cell_renderer_get_preferred_height (cell, widget,
+ NULL, &natural_size);
+
+ aligned_area->height = MIN (aligned_area->width, natural_size);
+
+ gtk_cell_renderer_get_preferred_width_for_height (cell, widget,
+ aligned_area->height,
+ NULL, &opposite_size);
+
+ aligned_area->width = MIN (opposite_size, aligned_area->width);
+ }
+
+ /* offset the cell position */
+ _gtk_cell_renderer_calc_offset (cell, cell_area,
+ gtk_widget_get_direction (widget),
+ aligned_area->width,
+ aligned_area->height,
+ &x_offset, &y_offset);
+
+ aligned_area->x += x_offset;
+ aligned_area->y += y_offset;
+}
+
+
/* An internal convenience function for some containers to peek at the
* cell alignment in a target allocation (used to draw focus and align
* cells in the icon view).
@@ -1535,3 +1630,35 @@ gtk_cell_renderer_get_preferred_size (GtkCellRenderer *cell,
}
}
}
+
+/**
+ * gtk_cell_renderer_get_aligned_area:
+ * @cell: a #GtkCellRenderer instance
+ * @widget: the #GtkWidget this cell will be rendering to
+ * @flags: render flags
+ * @cell_area: cell area which would be passed to gtk_cell_renderer_render()
+ * @aligned_area: the return location for the space inside @cell_area that
+ * would acually be used to render.
+ *
+ * Gets the aligned area used by @cell inside @cell_area. Used for finding
+ * the appropriate edit and focus rectangle.
+ *
+ * Since: 3.0
+ */
+void
+gtk_cell_renderer_get_aligned_area (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GtkCellRendererState flags,
+ const GdkRectangle *cell_area,
+ GdkRectangle *aligned_area)
+{
+ GtkCellRendererClass *klass;
+
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (cell_area != NULL);
+ g_return_if_fail (aligned_area != NULL);
+
+ klass = GTK_CELL_RENDERER_GET_CLASS (cell);
+ klass->get_aligned_area (cell, widget, flags, cell_area, aligned_area);
+}
diff --git a/gtk/gtkcellrenderer.h b/gtk/gtkcellrenderer.h
index d2abd128bd..22e86379cf 100644
--- a/gtk/gtkcellrenderer.h
+++ b/gtk/gtkcellrenderer.h
@@ -111,6 +111,11 @@ struct _GtkCellRendererClass
gint height,
gint *minimum_width,
gint *natural_width);
+ void (* get_aligned_area) (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GtkCellRendererState flags,
+ const GdkRectangle *cell_area,
+ GdkRectangle *aligned_area);
void (* get_size) (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *cell_area,
@@ -177,6 +182,12 @@ void gtk_cell_renderer_get_preferred_size (GtkCellRend
GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
+void gtk_cell_renderer_get_aligned_area (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GtkCellRendererState flags,
+ const GdkRectangle *cell_area,
+ GdkRectangle *aligned_area);
+
#ifndef GTK_DISABLE_DEPRECATED
void gtk_cell_renderer_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
@@ -236,6 +247,8 @@ void gtk_cell_renderer_set_sensitive (GtkCellRenderer *cell,
gboolean sensitive);
gboolean gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell);
+gboolean gtk_cell_renderer_is_activatable (GtkCellRenderer *cell);
+
/* For use by cell renderer implementations only */
void gtk_cell_renderer_stop_editing (GtkCellRenderer *cell,
gboolean canceled);
diff --git a/gtk/gtkcellrendererspinner.c b/gtk/gtkcellrendererspinner.c
index 5d50f8dfdd..bc63b5ea90 100644
--- a/gtk/gtkcellrendererspinner.c
+++ b/gtk/gtkcellrendererspinner.c
@@ -372,6 +372,11 @@ gtk_cell_renderer_spinner_render (GtkCellRenderer *cellr,
state = GTK_STATE_PRELIGHT;
}
+ cairo_save (cr);
+
+ gdk_cairo_rectangle (cr, cell_area);
+ cairo_clip (cr);
+
gtk_paint_spinner (gtk_widget_get_style (widget),
cr,
state,
@@ -380,4 +385,6 @@ gtk_cell_renderer_spinner_render (GtkCellRenderer *cellr,
priv->pulse,
draw_rect.x, draw_rect.y,
draw_rect.width, draw_rect.height);
+
+ cairo_restore (cr);
}
diff --git a/gtk/gtkcellrenderertext.c b/gtk/gtkcellrenderertext.c
index cf58d4dacb..2cff3a47c3 100644
--- a/gtk/gtkcellrenderertext.c
+++ b/gtk/gtkcellrenderertext.c
@@ -84,6 +84,13 @@ static void gtk_cell_renderer_text_get_preferred_height_for_width (GtkCell
gint width,
gint *minimum_height,
gint *natural_height);
+static void gtk_cell_renderer_text_get_aligned_area (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GtkCellRendererState flags,
+ const GdkRectangle *cell_area,
+ GdkRectangle *aligned_area);
+
+
enum {
EDITED,
@@ -240,6 +247,7 @@ gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class)
cell_class->get_preferred_width = gtk_cell_renderer_text_get_preferred_width;
cell_class->get_preferred_height = gtk_cell_renderer_text_get_preferred_height;
cell_class->get_preferred_height_for_width = gtk_cell_renderer_text_get_preferred_height_for_width;
+ cell_class->get_aligned_area = gtk_cell_renderer_text_get_aligned_area;
g_object_class_install_property (object_class,
PROP_TEXT,
@@ -1731,30 +1739,8 @@ get_size (GtkCellRenderer *cell,
if (height)
*height = ypad * 2 + rect.height;
- /* The minimum size for ellipsized labels is ~ 3 chars */
if (width)
- {
- if (priv->ellipsize || priv->width_chars > 0)
- {
- PangoContext *context;
- PangoFontMetrics *metrics;
- gint char_width;
-
- context = pango_layout_get_context (layout);
- metrics = pango_context_get_metrics (context,
- gtk_widget_get_style (widget)->font_desc,
- pango_context_get_language (context));
-
- char_width = pango_font_metrics_get_approximate_char_width (metrics);
- pango_font_metrics_unref (metrics);
-
- *width = xpad * 2 + (PANGO_PIXELS (char_width) * MAX (priv->width_chars, 3));
- }
- else
- {
- *width = xpad * 2 + rect.x + rect.width;
- }
- }
+ *width = xpad * 2 + rect.x + rect.width;
if (cell_area)
{
@@ -1847,6 +1833,11 @@ gtk_cell_renderer_text_render (GtkCellRenderer *cell,
else if (priv->wrap_width == -1)
pango_layout_set_width (layout, -1);
+ cairo_save (cr);
+
+ gdk_cairo_rectangle (cr, cell_area);
+ cairo_clip (cr);
+
gtk_paint_layout (gtk_widget_get_style (widget),
cr,
state,
@@ -1857,6 +1848,8 @@ gtk_cell_renderer_text_render (GtkCellRenderer *cell,
cell_area->y + y_offset + ypad,
layout);
+ cairo_restore (cr);
+
g_object_unref (layout);
}
@@ -2117,7 +2110,7 @@ gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell,
PangoContext *context;
PangoFontMetrics *metrics;
PangoRectangle rect;
- gint char_width, digit_width, char_pixels, text_width, ellipsize_chars, guess_width, xpad;
+ gint char_width, digit_width, char_pixels, text_width, ellipsize_chars, xpad;
gint min_width, nat_width;
/* "width-chars" Hard-coded minimum width:
@@ -2138,10 +2131,6 @@ gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell,
layout = get_layout (celltext, widget, NULL, 0);
- /* Get the layout with the text possibly wrapping at wrap_width */
- pango_layout_get_pixel_extents (layout, NULL, &rect);
- guess_width = rect.width;
-
/* Fetch the length of the complete unwrapped text */
pango_layout_set_width (layout, -1);
pango_layout_get_extents (layout, NULL, &rect);
@@ -2167,12 +2156,14 @@ gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell,
if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->width_chars > 0)
min_width =
- xpad * 2 + (PANGO_PIXELS (char_width) * MAX (priv->width_chars, ellipsize_chars));
+ xpad * 2 +
+ MIN (PANGO_PIXELS (text_width),
+ (PANGO_PIXELS (char_width) * MAX (priv->width_chars, ellipsize_chars)));
/* If no width-chars set, minimum for wrapping text will be the wrap-width */
else if (priv->wrap_width > -1)
- min_width = xpad * 2 + rect.x + priv->wrap_width;
+ min_width = xpad * 2 + rect.x + MIN (PANGO_PIXELS (text_width), priv->wrap_width);
else
- min_width = xpad * 2 + rect.x + guess_width;
+ min_width = xpad * 2 + rect.x + PANGO_PIXELS (text_width);
if (priv->width_chars > 0)
nat_width = xpad * 2 +
@@ -2180,6 +2171,7 @@ gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell,
else
nat_width = xpad * 2 + PANGO_PIXELS (text_width);
+
nat_width = MAX (nat_width, min_width);
if (priv->max_width_chars > 0)
@@ -2195,7 +2187,6 @@ gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell,
if (natural_size)
*natural_size = nat_width;
-
}
static void
@@ -2250,3 +2241,24 @@ gtk_cell_renderer_text_get_preferred_height (GtkCellRenderer *cell,
minimum_size, natural_size);
}
+static void
+gtk_cell_renderer_text_get_aligned_area (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GtkCellRendererState flags,
+ const GdkRectangle *cell_area,
+ GdkRectangle *aligned_area)
+{
+ GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell);
+ PangoLayout *layout;
+ gint x_offset = 0;
+ gint y_offset = 0;
+
+ layout = get_layout (celltext, widget, cell_area, flags);
+ get_size (cell, widget, cell_area, layout, &x_offset, &y_offset,
+ &aligned_area->width, &aligned_area->height);
+
+ aligned_area->x = cell_area->x + x_offset;
+ aligned_area->y = cell_area->y + y_offset;
+
+ g_object_unref (layout);
+}
diff --git a/gtk/gtkcellrenderertoggle.c b/gtk/gtkcellrenderertoggle.c
index e1aa78e03f..177cafd32a 100644
--- a/gtk/gtkcellrenderertoggle.c
+++ b/gtk/gtkcellrenderertoggle.c
@@ -372,6 +372,11 @@ gtk_cell_renderer_toggle_render (GtkCellRenderer *cell,
state = GTK_STATE_INSENSITIVE;
}
+ cairo_save (cr);
+
+ gdk_cairo_rectangle (cr, cell_area);
+ cairo_clip (cr);
+
if (priv->radio)
{
gtk_paint_option (gtk_widget_get_style (widget),
@@ -392,6 +397,8 @@ gtk_cell_renderer_toggle_render (GtkCellRenderer *cell,
cell_area->y + y_offset + ypad,
width, height);
}
+
+ cairo_restore (cr);
}
static gint
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index 4775fe930d..15c8df31f6 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -4415,10 +4415,12 @@ gtk_combo_box_list_select_func (GtkTreeSelection *selection,
gboolean path_currently_selected,
gpointer data)
{
- GList *list;
+ GList *list, *columns;
gboolean sensitive = FALSE;
- for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
+ columns = gtk_tree_view_get_columns (selection->tree_view);
+
+ for (list = columns; list && !sensitive; list = list->next)
{
GList *cells, *cell;
gboolean cell_sensitive, cell_visible;
@@ -4450,6 +4452,8 @@ gtk_combo_box_list_select_func (GtkTreeSelection *selection,
sensitive = cell_sensitive;
}
+ g_list_free (columns);
+
return sensitive;
}
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
index a14303588b..0808c1059f 100644
--- a/gtk/gtkentrycompletion.c
+++ b/gtk/gtkentrycompletion.c
@@ -23,6 +23,7 @@
#include "gtkentryprivate.h"
#include "gtkcelllayout.h"
+#include "gtkcellareabox.h"
#include "gtkintl.h"
#include "gtkcellrenderertext.h"
@@ -63,11 +64,17 @@ enum
PROP_POPUP_COMPLETION,
PROP_POPUP_SET_WIDTH,
PROP_POPUP_SINGLE_MATCH,
- PROP_INLINE_SELECTION
+ PROP_INLINE_SELECTION,
+ PROP_CELL_AREA
};
-static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface);
+static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface);
+static GtkCellArea* gtk_entry_completion_get_area (GtkCellLayout *cell_layout);
+
+static GObject *gtk_entry_completion_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties);
static void gtk_entry_completion_set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -76,30 +83,8 @@ static void gtk_entry_completion_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
-static void gtk_entry_completion_finalize (GObject *object);
-
-static void gtk_entry_completion_pack_start (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand);
-static void gtk_entry_completion_pack_end (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand);
-static void gtk_entry_completion_clear (GtkCellLayout *cell_layout);
-static void gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- const char *attribute,
- gint column);
-static void gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- GtkCellLayoutDataFunc func,
- gpointer func_data,
- GDestroyNotify destroy);
-static void gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell);
-static void gtk_entry_completion_reorder (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gint position);
-static GList * gtk_entry_completion_get_cells (GtkCellLayout *cell_layout);
+static void gtk_entry_completion_finalize (GObject *object);
+static void gtk_entry_completion_dispose (GObject *object);
static gboolean gtk_entry_completion_visible_func (GtkTreeModel *model,
GtkTreeIter *iter,
@@ -167,8 +152,10 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
object_class = (GObjectClass *)klass;
+ object_class->constructor = gtk_entry_completion_constructor;
object_class->set_property = gtk_entry_completion_set_property;
object_class->get_property = gtk_entry_completion_get_property;
+ object_class->dispose = gtk_entry_completion_dispose;
object_class->finalize = gtk_entry_completion_finalize;
klass->match_selected = gtk_entry_completion_match_selected;
@@ -391,37 +378,55 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
FALSE,
GTK_PARAM_READWRITE));
+
+ /**
+ * GtkEntryCompletion:cell-area:
+ *
+ * The #GtkCellArea used to layout cell renderers in the treeview column.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_CELL_AREA,
+ g_param_spec_object ("cell-area",
+ P_("Cell Area"),
+ P_("The GtkCellArea used to layout cells"),
+ GTK_TYPE_CELL_AREA,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
}
+
+static void
+gtk_entry_completion_buildable_custom_tag_end (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer *data)
+{
+ /* Just ignore the boolean return from here */
+ _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data);
+}
+
static void
gtk_entry_completion_buildable_init (GtkBuildableIface *iface)
{
iface->add_child = _gtk_cell_layout_buildable_add_child;
iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
- iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end;
+ iface->custom_tag_end = gtk_entry_completion_buildable_custom_tag_end;
}
static void
gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
{
- iface->pack_start = gtk_entry_completion_pack_start;
- iface->pack_end = gtk_entry_completion_pack_end;
- iface->clear = gtk_entry_completion_clear;
- iface->add_attribute = gtk_entry_completion_add_attribute;
- iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
- iface->clear_attributes = gtk_entry_completion_clear_attributes;
- iface->reorder = gtk_entry_completion_reorder;
- iface->get_cells = gtk_entry_completion_get_cells;
+ iface->get_area = gtk_entry_completion_get_area;
}
static void
gtk_entry_completion_init (GtkEntryCompletion *completion)
{
- GtkCellRenderer *cell;
- GtkTreeSelection *sel;
GtkEntryCompletionPrivate *priv;
- GtkWidget *popup_frame;
/* yes, also priv, need to keep the code readable */
completion->priv = G_TYPE_INSTANCE_GET_PRIVATE (completion,
@@ -438,9 +443,34 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
priv->popup_single_match = TRUE;
priv->inline_selection = FALSE;
- /* completions */
priv->filter_model = NULL;
+}
+
+static GObject *
+gtk_entry_completion_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GtkEntryCompletion *completion;
+ GtkEntryCompletionPrivate *priv;
+ GObject *object;
+ GtkCellRenderer *cell;
+ GtkTreeSelection *sel;
+ GtkWidget *popup_frame;
+
+ object = G_OBJECT_CLASS (gtk_entry_completion_parent_class)->constructor
+ (type, n_construct_properties, construct_properties);
+
+ completion = (GtkEntryCompletion *) object;
+ priv = completion->priv;
+
+ if (!priv->cell_area)
+ {
+ priv->cell_area = gtk_cell_area_box_new ();
+ g_object_ref_sink (priv->cell_area);
+ }
+ /* completions */
priv->tree_view = gtk_tree_view_new ();
g_signal_connect (priv->tree_view, "button-press-event",
G_CALLBACK (gtk_entry_completion_list_button_press),
@@ -463,7 +493,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
completion);
priv->first_sel_changed = TRUE;
- priv->column = gtk_tree_view_column_new ();
+ priv->column = gtk_tree_view_column_new_with_area (priv->cell_area);
gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
@@ -539,8 +569,11 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
* been inserted, so we pack the action treeview after the first
* action has been added
*/
+
+ return object;
}
+
static void
gtk_entry_completion_set_property (GObject *object,
guint prop_id,
@@ -549,6 +582,7 @@ gtk_entry_completion_set_property (GObject *object,
{
GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
GtkEntryCompletionPrivate *priv = completion->priv;
+ GtkCellArea *area;
switch (prop_id)
{
@@ -585,6 +619,14 @@ gtk_entry_completion_set_property (GObject *object,
case PROP_INLINE_SELECTION:
priv->inline_selection = g_value_get_boolean (value);
break;
+
+ case PROP_CELL_AREA:
+ /* Construct-only, can only be assigned once */
+ area = g_value_get_object (value);
+
+ if (area)
+ priv->cell_area = g_object_ref_sink (area);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -635,6 +677,10 @@ gtk_entry_completion_get_property (GObject *object,
g_value_set_boolean (value, gtk_entry_completion_get_inline_selection (completion));
break;
+ case PROP_CELL_AREA:
+ g_value_set_object (value, completion->priv->cell_area);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -647,123 +693,66 @@ gtk_entry_completion_finalize (GObject *object)
GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
GtkEntryCompletionPrivate *priv = completion->priv;
- if (priv->tree_view)
- gtk_widget_destroy (priv->tree_view);
-
- if (priv->entry)
- gtk_entry_set_completion (GTK_ENTRY (priv->entry), NULL);
-
- if (priv->actions)
- g_object_unref (priv->actions);
- if (priv->action_view)
- g_object_unref (priv->action_view);
-
g_free (priv->case_normalized_key);
g_free (priv->completion_prefix);
- if (priv->popup_window)
- gtk_widget_destroy (priv->popup_window);
-
if (priv->match_notify)
(* priv->match_notify) (priv->match_data);
G_OBJECT_CLASS (gtk_entry_completion_parent_class)->finalize (object);
}
-/* implement cell layout interface */
-static void
-gtk_entry_completion_pack_start (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand)
-{
- GtkEntryCompletionPrivate *priv;
-
- priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
-
- gtk_tree_view_column_pack_start (priv->column, cell, expand);
-}
-
-static void
-gtk_entry_completion_pack_end (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand)
-{
- GtkEntryCompletionPrivate *priv;
-
- priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
-
- gtk_tree_view_column_pack_end (priv->column, cell, expand);
-}
-
-static void
-gtk_entry_completion_clear (GtkCellLayout *cell_layout)
-{
- GtkEntryCompletionPrivate *priv;
-
- priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
-
- gtk_tree_view_column_clear (priv->column);
-}
-
static void
-gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- const gchar *attribute,
- gint column)
+gtk_entry_completion_dispose (GObject *object)
{
- GtkEntryCompletionPrivate *priv;
-
- priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
-
- gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column);
-}
-
-static void
-gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- GtkCellLayoutDataFunc func,
- gpointer func_data,
- GDestroyNotify destroy)
-{
- GtkEntryCompletionPrivate *priv;
-
- priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
+ GtkEntryCompletionPrivate *priv = completion->priv;
- gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column),
- cell, func, func_data, destroy);
-}
+ if (priv->tree_view)
+ {
+ gtk_widget_destroy (priv->tree_view);
+ priv->tree_view = NULL;
+ }
-static void
-gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell)
-{
- GtkEntryCompletionPrivate *priv;
+ if (priv->entry)
+ gtk_entry_set_completion (GTK_ENTRY (priv->entry), NULL);
- priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
+ if (priv->actions)
+ {
+ g_object_unref (priv->actions);
+ priv->actions = NULL;
+ }
- gtk_tree_view_column_clear_attributes (priv->column, cell);
-}
+ if (priv->action_view)
+ {
+ g_object_unref (priv->action_view);
+ priv->action_view = NULL;
+ }
-static void
-gtk_entry_completion_reorder (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gint position)
-{
- GtkEntryCompletionPrivate *priv;
+ if (priv->popup_window)
+ {
+ gtk_widget_destroy (priv->popup_window);
+ priv->popup_window = NULL;
+ }
- priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
+ if (priv->cell_area)
+ {
+ g_object_unref (priv->cell_area);
+ priv->cell_area = NULL;
+ }
- gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
+ G_OBJECT_CLASS (gtk_entry_completion_parent_class)->dispose (object);
}
-static GList *
-gtk_entry_completion_get_cells (GtkCellLayout *cell_layout)
+/* implement cell layout interface (only need to return the underlying cell area) */
+static GtkCellArea*
+gtk_entry_completion_get_area (GtkCellLayout *cell_layout)
{
GtkEntryCompletionPrivate *priv;
priv = GTK_ENTRY_COMPLETION (cell_layout)->priv;
- return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
+ return priv->cell_area;
}
/* all those callbacks */
@@ -1007,6 +996,28 @@ gtk_entry_completion_new (void)
}
/**
+ * gtk_entry_completion_new_with_area:
+ * @area: the #GtkCellArea used to layout cells
+ *
+ * Creates a new #GtkEntryCompletion object using the
+ * specified @area to layout cells in the underlying
+ * #GtkTreeViewColumn for the drop-down menu.
+ *
+ * Return value: A newly created #GtkEntryCompletion object.
+ *
+ * Since: 3.0
+ */
+GtkEntryCompletion *
+gtk_entry_completion_new_with_area (GtkCellArea *area)
+{
+ GtkEntryCompletion *completion;
+
+ completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION, "cell-area", area, NULL);
+
+ return completion;
+}
+
+/**
* gtk_entry_completion_get_entry:
* @completion: A #GtkEntryCompletion.
*
diff --git a/gtk/gtkentrycompletion.h b/gtk/gtkentrycompletion.h
index f120c3a679..a5ba4226c9 100644
--- a/gtk/gtkentrycompletion.h
+++ b/gtk/gtkentrycompletion.h
@@ -26,6 +26,7 @@
#include <gtk/gtktreemodel.h>
#include <gtk/gtkliststore.h>
+#include <gtk/gtkcellarea.h>
#include <gtk/gtktreeviewcolumn.h>
#include <gtk/gtktreemodelfilter.h>
@@ -81,6 +82,7 @@ struct _GtkEntryCompletionClass
/* core */
GType gtk_entry_completion_get_type (void) G_GNUC_CONST;
GtkEntryCompletion *gtk_entry_completion_new (void);
+GtkEntryCompletion *gtk_entry_completion_new_with_area (GtkCellArea *area);
GtkWidget *gtk_entry_completion_get_entry (GtkEntryCompletion *completion);
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
index 01c2c7d014..59f24e6e5e 100644
--- a/gtk/gtkentryprivate.h
+++ b/gtk/gtkentryprivate.h
@@ -37,6 +37,7 @@ struct _GtkEntryCompletionPrivate
GtkTreeModelFilter *filter_model;
GtkListStore *actions;
gboolean first_sel_changed;
+ GtkCellArea *cell_area;
GtkEntryCompletionMatchFunc match_func;
gpointer match_data;
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
index 360793b122..ef98afef50 100644
--- a/gtk/gtkfilechooserdefault.c
+++ b/gtk/gtkfilechooserdefault.c
@@ -2998,14 +2998,18 @@ shortcuts_compute_drop_position (GtkFileChooserDefault *impl,
GdkRectangle cell;
int row;
int bookmarks_index;
+ int header_height = 0;
tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
+ if (gtk_tree_view_get_headers_visible (tree_view))
+ header_height = _gtk_tree_view_get_header_height (tree_view);
+
bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
if (!gtk_tree_view_get_path_at_pos (tree_view,
x,
- y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+ y - header_height,
path,
&column,
NULL,
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index 10f0dfd689..fa60a7c91d 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -69,6 +69,7 @@ VOID:ENUM,FLOAT,BOOLEAN
VOID:ENUM,INT
VOID:ENUM,INT,BOOLEAN
VOID:ENUM,BOXED
+VOID:ENUM,STRING
VOID:FLAGS
VOID:INT
VOID:INT,BOOLEAN
@@ -79,6 +80,7 @@ VOID:OBJECT
VOID:OBJECT,BOOLEAN
VOID:OBJECT,BOXED,BOXED
VOID:OBJECT,BOXED,UINT,UINT
+VOID:OBJECT,BOXED,BOOLEAN,BOOLEAN
VOID:OBJECT,INT
VOID:OBJECT,INT,OBJECT
VOID:OBJECT,INT,INT
@@ -90,7 +92,9 @@ VOID:OBJECT,STRING,STRING
VOID:OBJECT,UINT
VOID:OBJECT,UINT,FLAGS
VOID:OBJECT,STRING
+VOID:OBJECT,OBJECT,STRING
VOID:OBJECT,OBJECT,OBJECT
+VOID:OBJECT,OBJECT,BOXED,STRING
VOID:POINTER
VOID:POINTER,INT
VOID:POINTER,BOOLEAN
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
index 2304f69ab7..9ee61f1ef9 100644
--- a/gtk/gtktreeprivate.h
+++ b/gtk/gtktreeprivate.h
@@ -32,353 +32,11 @@ G_BEGIN_DECLS
typedef enum
{
- GTK_TREE_VIEW_IS_LIST = 1 << 0,
- GTK_TREE_VIEW_SHOW_EXPANDERS = 1 << 1,
- GTK_TREE_VIEW_IN_COLUMN_RESIZE = 1 << 2,
- GTK_TREE_VIEW_ARROW_PRELIT = 1 << 3,
- GTK_TREE_VIEW_HEADERS_VISIBLE = 1 << 4,
- GTK_TREE_VIEW_DRAW_KEYFOCUS = 1 << 5,
- GTK_TREE_VIEW_MODEL_SETUP = 1 << 6,
- GTK_TREE_VIEW_IN_COLUMN_DRAG = 1 << 7
-} GtkTreeViewFlags;
-
-typedef enum
-{
GTK_TREE_SELECT_MODE_TOGGLE = 1 << 0,
GTK_TREE_SELECT_MODE_EXTEND = 1 << 1
}
GtkTreeSelectMode;
-enum
-{
- DRAG_COLUMN_WINDOW_STATE_UNSET = 0,
- DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1,
- DRAG_COLUMN_WINDOW_STATE_ARROW = 2,
- DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3,
- DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4
-};
-
-enum
-{
- RUBBER_BAND_OFF = 0,
- RUBBER_BAND_MAYBE_START = 1,
- RUBBER_BAND_ACTIVE = 2
-};
-
-#define GTK_TREE_VIEW_SET_FLAG(tree_view, flag) G_STMT_START{ (tree_view->priv->flags|=flag); }G_STMT_END
-#define GTK_TREE_VIEW_UNSET_FLAG(tree_view, flag) G_STMT_START{ (tree_view->priv->flags&=~(flag)); }G_STMT_END
-#define GTK_TREE_VIEW_FLAG_SET(tree_view, flag) ((tree_view->priv->flags&flag)==flag)
-#define TREE_VIEW_HEADER_HEIGHT(tree_view) (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)?tree_view->priv->header_height:0)
-#define TREE_VIEW_COLUMN_REQUESTED_WIDTH(column) (CLAMP (column->requested_width, (column->min_width!=-1)?column->min_width:column->requested_width, (column->max_width!=-1)?column->max_width:column->requested_width))
-#define TREE_VIEW_DRAW_EXPANDERS(tree_view) (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST)&&GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
-
- /* This lovely little value is used to determine how far away from the title bar
- * you can move the mouse and still have a column drag work.
- */
-#define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*TREE_VIEW_HEADER_HEIGHT(tree_view))
-
-typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder;
-struct _GtkTreeViewColumnReorder
-{
- gint left_align;
- gint right_align;
- GtkTreeViewColumn *left_column;
- GtkTreeViewColumn *right_column;
-};
-
-struct _GtkTreeViewPrivate
-{
- GtkTreeModel *model;
-
- guint flags;
- /* tree information */
- GtkRBTree *tree;
-
- /* Container info */
- GList *children;
- gint width;
- gint height;
-
- /* Adjustments */
- GtkAdjustment *hadjustment;
- GtkAdjustment *vadjustment;
- gint min_display_width;
- gint min_display_height;
-
- /* Sub windows */
- GdkWindow *bin_window;
- GdkWindow *header_window;
-
- /* Scroll position state keeping */
- GtkTreeRowReference *top_row;
- gint top_row_dy;
- /* dy == y pos of top_row + top_row_dy */
- /* we cache it for simplicity of the code */
- gint dy;
-
- guint presize_handler_timer;
- guint validate_rows_timer;
- guint scroll_sync_timer;
-
- /* Indentation and expander layout */
- gint expander_size;
- GtkTreeViewColumn *expander_column;
-
- gint level_indentation;
-
- /* Key navigation (focus), selection */
- gint cursor_offset;
-
- GtkTreeRowReference *anchor;
- GtkTreeRowReference *cursor;
-
- GtkTreeViewColumn *focus_column;
-
- /* Current pressed node, previously pressed, prelight */
- GtkRBNode *button_pressed_node;
- GtkRBTree *button_pressed_tree;
-
- gint pressed_button;
- gint press_start_x;
- gint press_start_y;
-
- gint event_last_x;
- gint event_last_y;
-
- guint last_button_time;
- gint last_button_x;
- gint last_button_y;
-
- GtkRBNode *prelight_node;
- GtkRBTree *prelight_tree;
-
- /* Cell Editing */
- GtkTreeViewColumn *edited_column;
-
- /* The node that's currently being collapsed or expanded */
- GtkRBNode *expanded_collapsed_node;
- GtkRBTree *expanded_collapsed_tree;
- guint expand_collapse_timeout;
-
- /* Auto expand/collapse timeout in hover mode */
- guint auto_expand_timeout;
-
- /* Selection information */
- GtkTreeSelection *selection;
-
- /* Header information */
- gint n_columns;
- GList *columns;
- gint header_height;
-
- GtkTreeViewColumnDropFunc column_drop_func;
- gpointer column_drop_func_data;
- GDestroyNotify column_drop_func_data_destroy;
- GList *column_drag_info;
- GtkTreeViewColumnReorder *cur_reorder;
-
- gint prev_width_before_expander;
-
- /* Interactive Header reordering */
- GdkWindow *drag_window;
- GdkWindow *drag_highlight_window;
- GtkTreeViewColumn *drag_column;
- gint drag_column_x;
-
- /* Interactive Header Resizing */
- gint drag_pos;
- gint x_drag;
-
- /* Non-interactive Header Resizing, expand flag support */
- gint prev_width;
-
- gint last_extra_space;
- gint last_extra_space_per_column;
- gint last_number_of_expand_columns;
-
- /* ATK Hack */
- GtkTreeDestroyCountFunc destroy_count_func;
- gpointer destroy_count_data;
- GDestroyNotify destroy_count_destroy;
-
- /* Scroll timeout (e.g. during dnd, rubber banding) */
- guint scroll_timeout;
-
- /* Row drag-and-drop */
- GtkTreeRowReference *drag_dest_row;
- GtkTreeViewDropPosition drag_dest_pos;
- guint open_dest_timeout;
-
- /* Rubber banding */
- gint rubber_band_status;
- gint rubber_band_x;
- gint rubber_band_y;
- gint rubber_band_shift;
- gint rubber_band_ctrl;
-
- GtkRBNode *rubber_band_start_node;
- GtkRBTree *rubber_band_start_tree;
-
- GtkRBNode *rubber_band_end_node;
- GtkRBTree *rubber_band_end_tree;
-
- /* fixed height */
- gint fixed_height;
-
- /* Scroll-to functionality when unrealized */
- GtkTreeRowReference *scroll_to_path;
- GtkTreeViewColumn *scroll_to_column;
- gfloat scroll_to_row_align;
- gfloat scroll_to_col_align;
-
- /* Interactive search */
- gint selected_iter;
- gint search_column;
- GtkTreeViewSearchPositionFunc search_position_func;
- GtkTreeViewSearchEqualFunc search_equal_func;
- gpointer search_user_data;
- GDestroyNotify search_destroy;
- gpointer search_position_user_data;
- GDestroyNotify search_position_destroy;
- GtkWidget *search_window;
- GtkWidget *search_entry;
- gulong search_entry_changed_id;
- guint typeselect_flush_timeout;
-
- /* Grid and tree lines */
- GtkTreeViewGridLines grid_lines;
- double grid_line_dashes[2];
- int grid_line_width;
-
- gboolean tree_lines_enabled;
- double tree_line_dashes[2];
- int tree_line_width;
-
- /* Row separators */
- GtkTreeViewRowSeparatorFunc row_separator_func;
- gpointer row_separator_data;
- GDestroyNotify row_separator_destroy;
-
- /* Tooltip support */
- gint tooltip_column;
-
- /* Here comes the bitfield */
- guint scroll_to_use_align : 1;
-
- guint fixed_height_mode : 1;
- guint fixed_height_check : 1;
-
- guint reorderable : 1;
- guint header_has_focus : 1;
- guint drag_column_window_state : 3;
- /* hint to display rows in alternating colors */
- guint has_rules : 1;
- guint mark_rows_col_dirty : 1;
-
- /* for DnD */
- guint empty_view_drop : 1;
-
- guint ctrl_pressed : 1;
- guint shift_pressed : 1;
-
- guint init_hadjust_value : 1;
-
- guint in_top_row_to_dy : 1;
-
- /* interactive search */
- guint enable_search : 1;
- guint disable_popdown : 1;
- guint search_custom_entry_set : 1;
-
- guint hover_selection : 1;
- guint hover_expand : 1;
- guint imcontext_changed : 1;
-
- guint rubber_banding_enable : 1;
-
- guint in_grab : 1;
-
- guint post_validation_flag : 1;
-
- /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
- guint search_entry_avoid_unhandled_binding : 1;
-
- /* GtkScrollablePolicy needs to be checked when
- * driving the scrollable adjustment values */
- guint hscroll_policy : 1;
- guint vscroll_policy : 1;
-};
-
-#ifdef __GNUC__
-
-#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
- if (!(expr)) \
- { \
- g_log (G_LOG_DOMAIN, \
- G_LOG_LEVEL_CRITICAL, \
- "%s (%s): assertion `%s' failed.\n" \
- "There is a disparity between the internal view of the GtkTreeView,\n" \
- "and the GtkTreeModel. This generally means that the model has changed\n"\
- "without letting the view know. Any display from now on is likely to\n" \
- "be incorrect.\n", \
- G_STRLOC, \
- G_STRFUNC, \
- #expr); \
- return ret; \
- }; }G_STMT_END
-
-#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
- if (!(expr)) \
- { \
- g_log (G_LOG_DOMAIN, \
- G_LOG_LEVEL_CRITICAL, \
- "%s (%s): assertion `%s' failed.\n" \
- "There is a disparity between the internal view of the GtkTreeView,\n" \
- "and the GtkTreeModel. This generally means that the model has changed\n"\
- "without letting the view know. Any display from now on is likely to\n" \
- "be incorrect.\n", \
- G_STRLOC, \
- G_STRFUNC, \
- #expr); \
- return; \
- }; }G_STMT_END
-
-#else
-
-#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
- if (!(expr)) \
- { \
- g_log (G_LOG_DOMAIN, \
- G_LOG_LEVEL_CRITICAL, \
- "file %s: line %d: assertion `%s' failed.\n" \
- "There is a disparity between the internal view of the GtkTreeView,\n" \
- "and the GtkTreeModel. This generally means that the model has changed\n"\
- "without letting the view know. Any display from now on is likely to\n" \
- "be incorrect.\n", \
- __FILE__, \
- __LINE__, \
- #expr); \
- return ret; \
- }; }G_STMT_END
-
-#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
- if (!(expr)) \
- { \
- g_log (G_LOG_DOMAIN, \
- G_LOG_LEVEL_CRITICAL, \
- "file %s: line %d: assertion '%s' failed.\n" \
- "There is a disparity between the internal view of the GtkTreeView,\n" \
- "and the GtkTreeModel. This generally means that the model has changed\n"\
- "without letting the view know. Any display from now on is likely to\n" \
- "be incorrect.\n", \
- __FILE__, \
- __LINE__, \
- #expr); \
- return; \
- }; }G_STMT_END
-#endif
-
-
/* functions that shouldn't be exported */
void _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
GtkRBNode *node,
@@ -405,71 +63,92 @@ void _gtk_tree_view_queue_draw_node (GtkTreeView *tree_v
GtkRBNode *node,
const GdkRectangle *clip_rect);
+void _gtk_tree_view_add_editable (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column,
+ GtkTreePath *path,
+ GtkCellEditable *cell_editable,
+ GdkRectangle *cell_area);
+void _gtk_tree_view_remove_editable (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column,
+ GtkCellEditable *cell_editable);
+
+void _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view,
+ gboolean install_handler);
+void _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column);
+gint _gtk_tree_view_get_header_height (GtkTreeView *tree_view);
+
+void _gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view,
+ GtkTreeViewRowSeparatorFunc *func,
+ gpointer *data);
+GtkTreePath *_gtk_tree_view_get_anchor_path (GtkTreeView *tree_view);
+void _gtk_tree_view_set_anchor_path (GtkTreeView *tree_view,
+ GtkTreePath *anchor_path);
+GtkRBTree * _gtk_tree_view_get_rbtree (GtkTreeView *tree_view);
+
+void _gtk_tree_view_set_focus_column (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column);
+GdkWindow *_gtk_tree_view_get_header_window (GtkTreeView *tree_view);
+
+
+GtkTreeSelection* _gtk_tree_selection_new (void);
+GtkTreeSelection* _gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view);
+void _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
+ GtkTreeView *tree_view);
+gboolean _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
+ GtkRBNode *node,
+ GtkTreePath *path);
+
+
void _gtk_tree_view_column_realize_button (GtkTreeViewColumn *column);
void _gtk_tree_view_column_unrealize_button (GtkTreeViewColumn *column);
+
void _gtk_tree_view_column_set_tree_view (GtkTreeViewColumn *column,
GtkTreeView *tree_view);
+gint _gtk_tree_view_column_request_width (GtkTreeViewColumn *tree_column);
+void _gtk_tree_view_column_allocate (GtkTreeViewColumn *tree_column,
+ int x_offset,
+ int width);
void _gtk_tree_view_column_unset_model (GtkTreeViewColumn *column,
GtkTreeModel *old_model);
void _gtk_tree_view_column_unset_tree_view (GtkTreeViewColumn *column);
-void _gtk_tree_view_column_set_width (GtkTreeViewColumn *column,
- gint width);
void _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
GtkTreeViewColumn *column,
GdkDevice *device);
gboolean _gtk_tree_view_column_cell_event (GtkTreeViewColumn *tree_column,
- GtkCellEditable **editable_widget,
GdkEvent *event,
- gchar *path_string,
- const GdkRectangle *background_area,
const GdkRectangle *cell_area,
guint flags);
-void _gtk_tree_view_column_start_editing (GtkTreeViewColumn *tree_column,
- GtkCellEditable *editable_widget);
-void _gtk_tree_view_column_stop_editing (GtkTreeViewColumn *tree_column);
-void _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view);
-void _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
- GtkTreeViewColumn *column);
-
-gboolean _gtk_tree_view_column_has_editable_cell (GtkTreeViewColumn *column);
-GtkCellRenderer *_gtk_tree_view_column_get_edited_cell (GtkTreeViewColumn *column);
-gint _gtk_tree_view_column_count_special_cells (GtkTreeViewColumn *column);
-GtkCellRenderer *_gtk_tree_view_column_get_cell_at_pos (GtkTreeViewColumn *column,
- gint x);
-
-GtkTreeSelection* _gtk_tree_selection_new (void);
-GtkTreeSelection* _gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view);
-void _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
- GtkTreeView *tree_view);
-gboolean _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
- GtkRBNode *node,
- GtkTreePath *path);
+gboolean _gtk_tree_view_column_has_editable_cell(GtkTreeViewColumn *column);
+GtkCellRenderer *_gtk_tree_view_column_get_edited_cell (GtkTreeViewColumn *column);
+GtkCellRenderer *_gtk_tree_view_column_get_cell_at_pos (GtkTreeViewColumn *column,
+ GdkRectangle *cell_area,
+ GdkRectangle *background_area,
+ gint x,
+ gint y);
void _gtk_tree_view_column_cell_render (GtkTreeViewColumn *tree_column,
cairo_t *cr,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
- guint flags);
-void _gtk_tree_view_column_get_focus_area (GtkTreeViewColumn *tree_column,
- const GdkRectangle *background_area,
- const GdkRectangle *cell_area,
- GdkRectangle *focus_area);
-gboolean _gtk_tree_view_column_cell_focus (GtkTreeViewColumn *tree_column,
- gint direction,
- gboolean left,
- gboolean right);
-void _gtk_tree_view_column_cell_draw_focus (GtkTreeViewColumn *tree_column,
- cairo_t *cr,
- const GdkRectangle *background_area,
- const GdkRectangle *cell_area,
- guint flags);
+ guint flags,
+ gboolean draw_focus);
void _gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column,
gboolean install_handler);
-void _gtk_tree_view_column_get_neighbor_sizes (GtkTreeViewColumn *column,
- GtkCellRenderer *cell,
- gint *left,
- gint *right);
-
+gboolean _gtk_tree_view_column_cell_get_dirty (GtkTreeViewColumn *tree_column);
+GdkWindow *_gtk_tree_view_column_get_window (GtkTreeViewColumn *column);
+
+void _gtk_tree_view_column_push_padding (GtkTreeViewColumn *column,
+ gint padding);
+gint _gtk_tree_view_column_get_requested_width (GtkTreeViewColumn *column);
+void _gtk_tree_view_column_set_resized_width (GtkTreeViewColumn *column,
+ gint width);
+gint _gtk_tree_view_column_get_resized_width (GtkTreeViewColumn *column);
+void _gtk_tree_view_column_set_use_resized_width (GtkTreeViewColumn *column,
+ gboolean use_resized_width);
+gboolean _gtk_tree_view_column_get_use_resized_width (GtkTreeViewColumn *column);
+gint _gtk_tree_view_column_get_drag_x (GtkTreeViewColumn *column);
+GtkCellAreaContext *_gtk_tree_view_column_get_context (GtkTreeViewColumn *column);
G_END_DECLS
diff --git a/gtk/gtktreeselection.c b/gtk/gtktreeselection.c
index 5ae0866146..8d2a164541 100644
--- a/gtk/gtktreeselection.c
+++ b/gtk/gtktreeselection.c
@@ -214,8 +214,7 @@ gtk_tree_selection_set_mode (GtkTreeSelection *selection,
gtk_tree_selection_unselect_all (selection);
selection->user_func = tmp_func;
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
- selection->tree_view->priv->anchor = NULL;
+ _gtk_tree_view_set_anchor_path (selection->tree_view, NULL);
}
else if (type == GTK_SELECTION_SINGLE ||
type == GTK_SELECTION_BROWSE)
@@ -225,20 +224,17 @@ gtk_tree_selection_set_mode (GtkTreeSelection *selection,
gint selected = FALSE;
GtkTreePath *anchor_path = NULL;
- if (selection->tree_view->priv->anchor)
+ anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view);
+
+ if (anchor_path)
{
- anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
-
- if (anchor_path)
- {
- _gtk_tree_view_find_node (selection->tree_view,
- anchor_path,
- &tree,
- &node);
-
- if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
- selected = TRUE;
- }
+ _gtk_tree_view_find_node (selection->tree_view,
+ anchor_path,
+ &tree,
+ &node);
+
+ if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ selected = TRUE;
}
/* We do this so that we unconditionally unset all rows
@@ -393,12 +389,9 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection,
memset (iter, 0, sizeof (GtkTreeIter));
if (model)
- *model = selection->tree_view->priv->model;
-
- if (selection->tree_view->priv->anchor == NULL)
- return FALSE;
+ *model = gtk_tree_view_get_model (selection->tree_view);
- anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
+ anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view);
if (anchor_path == NULL)
return FALSE;
@@ -418,7 +411,7 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection,
if (iter == NULL)
retval = TRUE;
else
- retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
+ retval = gtk_tree_model_get_iter (gtk_tree_view_get_model (selection->tree_view),
iter,
anchor_path);
}
@@ -467,10 +460,11 @@ gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
g_return_val_if_fail (selection->tree_view != NULL, NULL);
if (model)
- *model = selection->tree_view->priv->model;
+ *model = gtk_tree_view_get_model (selection->tree_view);
- if (selection->tree_view->priv->tree == NULL ||
- selection->tree_view->priv->tree->root == NULL)
+ tree = _gtk_tree_view_get_rbtree (selection->tree_view);
+
+ if (tree == NULL || tree->root == NULL)
return NULL;
if (selection->type == GTK_SELECTION_NONE)
@@ -483,7 +477,7 @@ gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
{
GtkTreePath *path;
- path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
+ path = gtk_tree_model_get_path (gtk_tree_view_get_model (selection->tree_view), &iter);
list = g_list_append (list, path);
return list;
@@ -492,8 +486,7 @@ gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
return NULL;
}
- tree = selection->tree_view->priv->tree;
- node = selection->tree_view->priv->tree->root;
+ node = tree->root;
while (node->left != tree->nil)
node = node->left;
@@ -582,12 +575,14 @@ gint
gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
gint count = 0;
+ GtkRBTree *tree;
g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);
g_return_val_if_fail (selection->tree_view != NULL, 0);
- if (selection->tree_view->priv->tree == NULL ||
- selection->tree_view->priv->tree->root == NULL)
+ tree = _gtk_tree_view_get_rbtree (selection->tree_view);
+
+ if (tree == NULL || tree->root == NULL)
return 0;
if (selection->type == GTK_SELECTION_SINGLE ||
@@ -599,8 +594,7 @@ gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
return 0;
}
- _gtk_rbtree_traverse (selection->tree_view->priv->tree,
- selection->tree_view->priv->tree->root,
+ _gtk_rbtree_traverse (tree, tree->root,
G_PRE_ORDER,
gtk_tree_selection_count_selected_rows_helper,
&count);
@@ -644,31 +638,32 @@ gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (selection->tree_view != NULL);
- if (func == NULL ||
- selection->tree_view->priv->tree == NULL ||
- selection->tree_view->priv->tree->root == NULL)
+ tree = _gtk_tree_view_get_rbtree (selection->tree_view);
+
+ if (func == NULL || tree == NULL || tree->root == NULL)
return;
+ model = gtk_tree_view_get_model (selection->tree_view);
+
if (selection->type == GTK_SELECTION_SINGLE ||
selection->type == GTK_SELECTION_BROWSE)
{
- if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
+ path = _gtk_tree_view_get_anchor_path (selection->tree_view);
+
+ if (path)
{
- path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
- gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path);
- (* func) (selection->tree_view->priv->model, path, &iter, data);
+ gtk_tree_model_get_iter (model, &iter, path);
+ (* func) (model, path, &iter, data);
gtk_tree_path_free (path);
}
return;
}
- tree = selection->tree_view->priv->tree;
- node = selection->tree_view->priv->tree->root;
+ node = tree->root;
while (node->left != tree->nil)
node = node->left;
- model = selection->tree_view->priv->model;
g_object_ref (model);
/* connect to signals to monitor changes in treemodel */
@@ -848,14 +843,16 @@ gtk_tree_selection_select_iter (GtkTreeSelection *selection,
GtkTreeIter *iter)
{
GtkTreePath *path;
+ GtkTreeModel *model;
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (selection->tree_view != NULL);
- g_return_if_fail (selection->tree_view->priv->model != NULL);
+
+ model = gtk_tree_view_get_model (selection->tree_view);
+ g_return_if_fail (model != NULL);
g_return_if_fail (iter != NULL);
- path = gtk_tree_model_get_path (selection->tree_view->priv->model,
- iter);
+ path = gtk_tree_model_get_path (model, iter);
if (path == NULL)
return;
@@ -877,14 +874,16 @@ gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
GtkTreeIter *iter)
{
GtkTreePath *path;
+ GtkTreeModel *model;
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (selection->tree_view != NULL);
- g_return_if_fail (selection->tree_view->priv->model != NULL);
+
+ model = gtk_tree_view_get_model (selection->tree_view);
+ g_return_if_fail (model != NULL);
g_return_if_fail (iter != NULL);
- path = gtk_tree_model_get_path (selection->tree_view->priv->model,
- iter);
+ path = gtk_tree_model_get_path (model, iter);
if (path == NULL)
return;
@@ -915,7 +914,7 @@ gtk_tree_selection_path_is_selected (GtkTreeSelection *selection,
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (selection->tree_view != NULL, FALSE);
- if (selection->tree_view->priv->model == NULL)
+ if (gtk_tree_view_get_model (selection->tree_view) == NULL)
return FALSE;
ret = _gtk_tree_view_find_node (selection->tree_view,
@@ -944,14 +943,17 @@ gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection,
GtkTreeIter *iter)
{
GtkTreePath *path;
+ GtkTreeModel *model;
gboolean retval;
g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (selection->tree_view != NULL, FALSE);
- g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE);
- path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter);
+ model = gtk_tree_view_get_model (selection->tree_view);
+ g_return_val_if_fail (model != NULL, FALSE);
+
+ path = gtk_tree_model_get_path (model, iter);
if (path == NULL)
return FALSE;
@@ -995,8 +997,11 @@ static gint
gtk_tree_selection_real_select_all (GtkTreeSelection *selection)
{
struct _TempTuple *tuple;
+ GtkRBTree *tree;
- if (selection->tree_view->priv->tree == NULL)
+ tree = _gtk_tree_view_get_rbtree (selection->tree_view);
+
+ if (tree == NULL)
return FALSE;
/* Mark all nodes selected */
@@ -1004,8 +1009,7 @@ gtk_tree_selection_real_select_all (GtkTreeSelection *selection)
tuple->selection = selection;
tuple->dirty = FALSE;
- _gtk_rbtree_traverse (selection->tree_view->priv->tree,
- selection->tree_view->priv->tree->root,
+ _gtk_rbtree_traverse (tree, tree->root,
G_PRE_ORDER,
select_all_helper,
tuple);
@@ -1031,7 +1035,8 @@ gtk_tree_selection_select_all (GtkTreeSelection *selection)
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (selection->tree_view != NULL);
- if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
+ if (_gtk_tree_view_get_rbtree (selection->tree_view) == NULL ||
+ gtk_tree_view_get_model (selection->tree_view) == NULL)
return;
g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
@@ -1071,10 +1076,7 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
GtkRBNode *node = NULL;
GtkTreePath *anchor_path;
- if (selection->tree_view->priv->anchor == NULL)
- return FALSE;
-
- anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
+ anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view);
if (anchor_path == NULL)
return FALSE;
@@ -1093,8 +1095,7 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
{
if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE))
{
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
- selection->tree_view->priv->anchor = NULL;
+ _gtk_tree_view_set_anchor_path (selection->tree_view, NULL);
return TRUE;
}
}
@@ -1102,12 +1103,14 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
}
else
{
+ GtkRBTree *tree;
+
tuple = g_new (struct _TempTuple, 1);
tuple->selection = selection;
tuple->dirty = FALSE;
- _gtk_rbtree_traverse (selection->tree_view->priv->tree,
- selection->tree_view->priv->tree->root,
+ tree = _gtk_tree_view_get_rbtree (selection->tree_view);
+ _gtk_rbtree_traverse (tree, tree->root,
G_PRE_ORDER,
unselect_all_helper,
tuple);
@@ -1134,7 +1137,8 @@ gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (selection->tree_view != NULL);
- if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
+ if (_gtk_tree_view_get_rbtree (selection->tree_view) == NULL ||
+ gtk_tree_view_get_model (selection->tree_view) == NULL)
return;
if (gtk_tree_selection_real_unselect_all (selection))
@@ -1197,15 +1201,7 @@ gtk_tree_selection_real_modify_range (GtkTreeSelection *selection,
g_return_val_if_fail (end_node != NULL, FALSE);
if (anchor_path)
- {
- if (selection->tree_view->priv->anchor)
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
-
- selection->tree_view->priv->anchor =
- gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view),
- selection->tree_view->priv->model,
- anchor_path);
- }
+ _gtk_tree_view_set_anchor_path (selection->tree_view, anchor_path);
do
{
@@ -1254,7 +1250,7 @@ gtk_tree_selection_select_range (GtkTreeSelection *selection,
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (selection->tree_view != NULL);
g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
- g_return_if_fail (selection->tree_view->priv->model != NULL);
+ g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL);
if (gtk_tree_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
@@ -1278,7 +1274,7 @@ gtk_tree_selection_unselect_range (GtkTreeSelection *selection,
{
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (selection->tree_view != NULL);
- g_return_if_fail (selection->tree_view->priv->model != NULL);
+ g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL);
if (gtk_tree_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path))
g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
@@ -1290,22 +1286,28 @@ _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
GtkTreePath *path)
{
GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeViewRowSeparatorFunc separator_func;
+ gpointer separator_data;
gboolean sensitive = FALSE;
- if (!gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path))
+ model = gtk_tree_view_get_model (selection->tree_view);
+
+ _gtk_tree_view_get_row_separator_func (selection->tree_view,
+ &separator_func, &separator_data);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
sensitive = TRUE;
- if (!sensitive && selection->tree_view->priv->row_separator_func)
+ if (!sensitive && separator_func)
{
/* never allow separators to be selected */
- if ((* selection->tree_view->priv->row_separator_func) (selection->tree_view->priv->model,
- &iter,
- selection->tree_view->priv->row_separator_data))
+ if ((* separator_func) (model, &iter, separator_data))
return FALSE;
}
if (selection->user_func)
- return (*selection->user_func) (selection, selection->tree_view->priv->model, path,
+ return (*selection->user_func) (selection, model, path,
GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED),
selection->user_data);
else
@@ -1336,8 +1338,7 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
if (selection->type == GTK_SELECTION_NONE)
return;
- if (selection->tree_view->priv->anchor)
- anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
+ anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view);
if (selection->type == GTK_SELECTION_SINGLE ||
selection->type == GTK_SELECTION_BROWSE)
@@ -1374,17 +1375,11 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
* old one, and can then select the new one */
if (dirty)
{
- if (selection->tree_view->priv->anchor)
- {
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
- selection->tree_view->priv->anchor = NULL;
- }
+
+ _gtk_tree_view_set_anchor_path (selection->tree_view, NULL);
if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE))
- {
- selection->tree_view->priv->anchor =
- gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
- }
+ _gtk_tree_view_set_anchor_path (selection->tree_view, path);
}
}
else
@@ -1392,11 +1387,8 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE))
{
dirty = TRUE;
- if (selection->tree_view->priv->anchor)
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
- selection->tree_view->priv->anchor =
- gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
+ _gtk_tree_view_set_anchor_path (selection->tree_view, path);
}
}
}
@@ -1406,11 +1398,8 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
&& (anchor_path == NULL))
{
- if (selection->tree_view->priv->anchor)
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
+ _gtk_tree_view_set_anchor_path (selection->tree_view, path);
- selection->tree_view->priv->anchor =
- gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
dirty = gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
}
else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
@@ -1422,11 +1411,8 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
{
flags = node->flags;
- if (selection->tree_view->priv->anchor)
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
- selection->tree_view->priv->anchor =
- gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
+ _gtk_tree_view_set_anchor_path (selection->tree_view, path);
if ((flags & GTK_RBNODE_IS_SELECTED) == GTK_RBNODE_IS_SELECTED)
dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE);
@@ -1445,11 +1431,7 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
{
dirty = gtk_tree_selection_real_unselect_all (selection);
- if (selection->tree_view->priv->anchor)
- gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
-
- selection->tree_view->priv->anchor =
- gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
+ _gtk_tree_view_set_anchor_path (selection->tree_view, path);
dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
}
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 457c4facdd..d92aa2ff17 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -45,6 +45,7 @@
#include "gtktreemodelsort.h"
#include "gtktooltip.h"
#include "gtkscrollable.h"
+#include "gtkcelllayout.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
#include "gtkentryprivate.h"
@@ -133,6 +134,95 @@
* </refsect2>
*/
+enum
+{
+ DRAG_COLUMN_WINDOW_STATE_UNSET = 0,
+ DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1,
+ DRAG_COLUMN_WINDOW_STATE_ARROW = 2,
+ DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3,
+ DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4
+};
+
+enum
+{
+ RUBBER_BAND_OFF = 0,
+ RUBBER_BAND_MAYBE_START = 1,
+ RUBBER_BAND_ACTIVE = 2
+};
+
+ /* This lovely little value is used to determine how far away from the title bar
+ * you can move the mouse and still have a column drag work.
+ */
+#define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*gtk_tree_view_get_effective_header_height(tree_view))
+
+#ifdef __GNUC__
+
+#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "%s (%s): assertion `%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ G_STRLOC, \
+ G_STRFUNC, \
+ #expr); \
+ return ret; \
+ }; }G_STMT_END
+
+#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "%s (%s): assertion `%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ G_STRLOC, \
+ G_STRFUNC, \
+ #expr); \
+ return; \
+ }; }G_STMT_END
+
+#else
+
+#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d: assertion `%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ __FILE__, \
+ __LINE__, \
+ #expr); \
+ return ret; \
+ }; }G_STMT_END
+
+#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d: assertion '%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ __FILE__, \
+ __LINE__, \
+ #expr); \
+ return; \
+ }; }G_STMT_END
+#endif
#define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
#define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2)
@@ -142,28 +232,20 @@
#define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000
#define AUTO_EXPAND_TIMEOUT 500
-/* The "background" areas of all rows/cells add up to cover the entire tree.
- * The background includes all inter-row and inter-cell spacing.
- * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
- * i.e. just the cells, no spacing.
- */
-
-#define BACKGROUND_HEIGHT(node) (GTK_RBNODE_GET_HEIGHT (node))
-#define CELL_HEIGHT(node, separator) ((BACKGROUND_HEIGHT (node)) - (separator))
-
/* Translate from bin_window coordinates to rbtree (tree coordinates) and
* vice versa.
*/
#define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
#define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
-/* This is in bin_window coordinates */
-#define BACKGROUND_FIRST_PIXEL(tree_view,tree,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, _gtk_rbtree_node_find_offset ((tree), (node))))
-#define CELL_FIRST_PIXEL(tree_view,tree,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,tree,node) + separator/2)
-
-#define ROW_HEIGHT(tree_view,height) \
- ((height > 0) ? (height) : (tree_view)->priv->expander_size)
-
+typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder;
+struct _GtkTreeViewColumnReorder
+{
+ gint left_align;
+ gint right_align;
+ GtkTreeViewColumn *left_column;
+ GtkTreeViewColumn *right_column;
+};
typedef struct _GtkTreeViewChild GtkTreeViewChild;
struct _GtkTreeViewChild
@@ -190,6 +272,239 @@ struct _TreeViewDragInfo
};
+struct _GtkTreeViewPrivate
+{
+ GtkTreeModel *model;
+
+ /* tree information */
+ GtkRBTree *tree;
+
+ /* Container info */
+ GList *children;
+ gint width;
+ gint height;
+
+ /* Adjustments */
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+ gint min_display_width;
+ gint min_display_height;
+
+ /* Sub windows */
+ GdkWindow *bin_window;
+ GdkWindow *header_window;
+
+ /* Scroll position state keeping */
+ GtkTreeRowReference *top_row;
+ gint top_row_dy;
+ /* dy == y pos of top_row + top_row_dy */
+ /* we cache it for simplicity of the code */
+ gint dy;
+
+ guint presize_handler_timer;
+ guint validate_rows_timer;
+ guint scroll_sync_timer;
+
+ /* Indentation and expander layout */
+ gint expander_size;
+ GtkTreeViewColumn *expander_column;
+
+ gint level_indentation;
+
+ /* Key navigation (focus), selection */
+ gint cursor_offset;
+
+ GtkTreeRowReference *anchor;
+ GtkTreeRowReference *cursor;
+
+ GtkTreeViewColumn *focus_column;
+
+ /* Current pressed node, previously pressed, prelight */
+ GtkRBNode *button_pressed_node;
+ GtkRBTree *button_pressed_tree;
+
+ gint pressed_button;
+ gint press_start_x;
+ gint press_start_y;
+
+ gint event_last_x;
+ gint event_last_y;
+
+ guint last_button_time;
+ gint last_button_x;
+ gint last_button_y;
+
+ GtkRBNode *prelight_node;
+ GtkRBTree *prelight_tree;
+
+ /* Cell Editing */
+ GtkTreeViewColumn *edited_column;
+
+ /* The node that's currently being collapsed or expanded */
+ GtkRBNode *expanded_collapsed_node;
+ GtkRBTree *expanded_collapsed_tree;
+ guint expand_collapse_timeout;
+
+ /* Auto expand/collapse timeout in hover mode */
+ guint auto_expand_timeout;
+
+ /* Selection information */
+ GtkTreeSelection *selection;
+
+ /* Header information */
+ gint n_columns;
+ GList *columns;
+ gint header_height;
+
+ GtkTreeViewColumnDropFunc column_drop_func;
+ gpointer column_drop_func_data;
+ GDestroyNotify column_drop_func_data_destroy;
+ GList *column_drag_info;
+ GtkTreeViewColumnReorder *cur_reorder;
+
+ gint prev_width_before_expander;
+
+ /* Interactive Header reordering */
+ GdkWindow *drag_window;
+ GdkWindow *drag_highlight_window;
+ GtkTreeViewColumn *drag_column;
+ gint drag_column_x;
+
+ /* Interactive Header Resizing */
+ gint drag_pos;
+ gint x_drag;
+
+ /* Non-interactive Header Resizing, expand flag support */
+ gint prev_width;
+
+ gint last_extra_space;
+ gint last_extra_space_per_column;
+ gint last_number_of_expand_columns;
+
+ /* ATK Hack */
+ GtkTreeDestroyCountFunc destroy_count_func;
+ gpointer destroy_count_data;
+ GDestroyNotify destroy_count_destroy;
+
+ /* Scroll timeout (e.g. during dnd, rubber banding) */
+ guint scroll_timeout;
+
+ /* Row drag-and-drop */
+ GtkTreeRowReference *drag_dest_row;
+ GtkTreeViewDropPosition drag_dest_pos;
+ guint open_dest_timeout;
+
+ /* Rubber banding */
+ gint rubber_band_status;
+ gint rubber_band_x;
+ gint rubber_band_y;
+ gint rubber_band_shift;
+ gint rubber_band_ctrl;
+
+ GtkRBNode *rubber_band_start_node;
+ GtkRBTree *rubber_band_start_tree;
+
+ GtkRBNode *rubber_band_end_node;
+ GtkRBTree *rubber_band_end_tree;
+
+ /* fixed height */
+ gint fixed_height;
+
+ /* Scroll-to functionality when unrealized */
+ GtkTreeRowReference *scroll_to_path;
+ GtkTreeViewColumn *scroll_to_column;
+ gfloat scroll_to_row_align;
+ gfloat scroll_to_col_align;
+
+ /* Interactive search */
+ gint selected_iter;
+ gint search_column;
+ GtkTreeViewSearchPositionFunc search_position_func;
+ GtkTreeViewSearchEqualFunc search_equal_func;
+ gpointer search_user_data;
+ GDestroyNotify search_destroy;
+ gpointer search_position_user_data;
+ GDestroyNotify search_position_destroy;
+ GtkWidget *search_window;
+ GtkWidget *search_entry;
+ gulong search_entry_changed_id;
+ guint typeselect_flush_timeout;
+
+ /* Grid and tree lines */
+ GtkTreeViewGridLines grid_lines;
+ double grid_line_dashes[2];
+ int grid_line_width;
+
+ gboolean tree_lines_enabled;
+ double tree_line_dashes[2];
+ int tree_line_width;
+
+ /* Row separators */
+ GtkTreeViewRowSeparatorFunc row_separator_func;
+ gpointer row_separator_data;
+ GDestroyNotify row_separator_destroy;
+
+ /* Tooltip support */
+ gint tooltip_column;
+
+ /* Here comes the bitfield */
+ guint scroll_to_use_align : 1;
+
+ guint fixed_height_mode : 1;
+ guint fixed_height_check : 1;
+
+ guint reorderable : 1;
+ guint header_has_focus : 1;
+ guint drag_column_window_state : 3;
+ /* hint to display rows in alternating colors */
+ guint has_rules : 1;
+ guint mark_rows_col_dirty : 1;
+
+ /* for DnD */
+ guint empty_view_drop : 1;
+
+ guint ctrl_pressed : 1;
+ guint shift_pressed : 1;
+
+ guint init_hadjust_value : 1;
+
+ guint in_top_row_to_dy : 1;
+
+ /* interactive search */
+ guint enable_search : 1;
+ guint disable_popdown : 1;
+ guint search_custom_entry_set : 1;
+
+ guint hover_selection : 1;
+ guint hover_expand : 1;
+ guint imcontext_changed : 1;
+
+ guint rubber_banding_enable : 1;
+
+ guint in_grab : 1;
+
+ guint post_validation_flag : 1;
+
+ /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
+ guint search_entry_avoid_unhandled_binding : 1;
+
+ /* GtkScrollablePolicy needs to be checked when
+ * driving the scrollable adjustment values */
+ guint hscroll_policy : 1;
+ guint vscroll_policy : 1;
+
+ /* GtkTreeView flags */
+ guint is_list : 1;
+ guint show_expanders : 1;
+ guint in_column_resize : 1;
+ guint arrow_prelit : 1;
+ guint headers_visible : 1;
+ guint draw_keyfocus : 1;
+ guint model_setup : 1;
+ guint in_column_drag : 1;
+};
+
+
/* Signals */
enum
{
@@ -260,7 +575,8 @@ static void gtk_tree_view_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural);
static void gtk_tree_view_size_request (GtkWidget *widget,
- GtkRequisition *requisition);
+ GtkRequisition *requisition,
+ gboolean may_validate);
static void gtk_tree_view_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gboolean gtk_tree_view_draw (GtkWidget *widget,
@@ -406,6 +722,7 @@ static void invalidate_empty_focus (GtkTreeView *tree_view);
/* Internal functions */
static gboolean gtk_tree_view_is_expander_column (GtkTreeView *tree_view,
GtkTreeViewColumn *column);
+static inline gboolean gtk_tree_view_draw_expanders (GtkTreeView *tree_view);
static void gtk_tree_view_add_move_binding (GtkBindingSet *binding_set,
guint keyval,
guint modmask,
@@ -472,7 +789,7 @@ static void gtk_tree_view_real_set_cursor (GtkTreeView
GtkTreePath *path,
gboolean clear_and_select,
gboolean clamp_node);
-static gboolean gtk_tree_view_has_special_cell (GtkTreeView *tree_view);
+static gboolean gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view);
static void column_sizing_notify (GObject *object,
GParamSpec *pspec,
gpointer data);
@@ -489,6 +806,22 @@ static void update_prelight (GtkTreeView
int x,
int y);
+static inline gint gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view);
+
+static inline gint gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node,
+ gint vertical_separator);
+static inline gint gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view,
+ GtkRBNode *node,
+ gint vertical_separator);
+
+static inline gint gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node);
+static inline gint gtk_tree_view_get_row_height (GtkTreeView *tree_view,
+ GtkRBNode *node);
+
/* interactive search */
static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
static void gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog,
@@ -542,14 +875,8 @@ static void gtk_tree_view_put (GtkTreeView *tree_
gint width,
gint height);
static gboolean gtk_tree_view_start_editing (GtkTreeView *tree_view,
- GtkTreePath *cursor_path);
-static void gtk_tree_view_real_start_editing (GtkTreeView *tree_view,
- GtkTreeViewColumn *column,
- GtkTreePath *path,
- GtkCellEditable *cell_editable,
- GdkRectangle *cell_area,
- GdkEvent *event,
- guint flags);
+ GtkTreePath *cursor_path,
+ gboolean edit_only);
static void gtk_tree_view_stop_editing (GtkTreeView *tree_view,
gboolean cancel_editing);
static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
@@ -1382,9 +1709,9 @@ gtk_tree_view_init (GtkTreeView *tree_view)
gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
- tree_view->priv->flags = GTK_TREE_VIEW_SHOW_EXPANDERS
- | GTK_TREE_VIEW_DRAW_KEYFOCUS
- | GTK_TREE_VIEW_HEADERS_VISIBLE;
+ tree_view->priv->show_expanders = TRUE;
+ tree_view->priv->draw_keyfocus = TRUE;
+ tree_view->priv->headers_visible = TRUE;
/* We need some padding */
tree_view->priv->dy = 0;
@@ -1583,7 +1910,7 @@ gtk_tree_view_get_property (GObject *object,
g_value_set_boolean (value, tree_view->priv->hover_expand);
break;
case PROP_SHOW_EXPANDERS:
- g_value_set_boolean (value, GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS));
+ g_value_set_boolean (value, tree_view->priv->show_expanders);
break;
case PROP_LEVEL_INDENTATION:
g_value_set_int (value, tree_view->priv->level_indentation);
@@ -1790,29 +2117,35 @@ gtk_tree_view_map_buttons (GtkTreeView *tree_view)
g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+ if (tree_view->priv->headers_visible)
{
GtkTreeViewColumn *column;
+ GtkWidget *button;
+ GdkWindow *window;
for (list = tree_view->priv->columns; list; list = list->next)
{
column = list->data;
- if (gtk_widget_get_visible (column->button) &&
- !gtk_widget_get_mapped (column->button))
- gtk_widget_map (column->button);
+ button = gtk_tree_view_column_get_button (column);
+
+ if (gtk_widget_get_visible (button) &&
+ !gtk_widget_get_mapped (button))
+ gtk_widget_map (button);
}
for (list = tree_view->priv->columns; list; list = list->next)
{
column = list->data;
if (gtk_tree_view_column_get_visible (column) == FALSE)
continue;
+
+ window = _gtk_tree_view_column_get_window (column);
if (gtk_tree_view_column_get_resizable (column))
{
- gdk_window_raise (column->window);
- gdk_window_show (column->window);
+ gdk_window_raise (window);
+ gdk_window_show (window);
}
else
- gdk_window_hide (column->window);
+ gdk_window_hide (window);
}
gdk_window_show (tree_view->priv->header_window);
}
@@ -1881,7 +2214,7 @@ gtk_tree_view_realize (GtkWidget *widget)
/* Make the window for the tree */
attributes.x = 0;
- attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
+ attributes.y = gtk_tree_view_get_effective_header_height (tree_view);
attributes.width = MAX (tree_view->priv->width, allocation.width);
attributes.height = allocation.height;
attributes.event_mask = (GDK_EXPOSURE_MASK |
@@ -2035,16 +2368,16 @@ gtk_tree_view_size_request_columns (GtkTreeView *tree_view)
{
for (list = tree_view->priv->columns; list; list = list->next)
{
- GtkRequisition requisition;
+ GtkRequisition requisition;
GtkTreeViewColumn *column = list->data;
+ GtkWidget *button = gtk_tree_view_column_get_button (column);
- if (column->button == NULL)
+ if (button == NULL)
continue;
column = list->data;
- gtk_widget_get_preferred_size (column->button, &requisition, NULL);
- column->button_request = requisition.width;
+ gtk_widget_get_preferred_size (button, &requisition, NULL);
tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
}
}
@@ -2073,39 +2406,11 @@ gtk_tree_view_update_size (GtkTreeView *tree_view)
/* keep this in sync with size_allocate below */
for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
{
- gint max_width, min_width;
- gint real_requested_width = 0;
-
column = list->data;
if (!gtk_tree_view_column_get_visible (column))
continue;
- if (column->use_resized_width)
- {
- real_requested_width = column->resized_width;
- }
- else if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_FIXED)
- {
- real_requested_width = gtk_tree_view_column_get_fixed_width (column);
- }
- else if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
- {
- real_requested_width = MAX (column->requested_width, column->button_request);
- }
- else
- {
- real_requested_width = column->requested_width;
- }
-
- min_width = gtk_tree_view_column_get_min_width (column);
- if (min_width != -1)
- real_requested_width = MAX (real_requested_width, min_width);
-
- max_width = gtk_tree_view_column_get_max_width (column);
- if (max_width != -1)
- real_requested_width = MIN (real_requested_width, max_width);
-
- tree_view->priv->width += real_requested_width;
+ tree_view->priv->width += _gtk_tree_view_column_request_width (column);
}
if (tree_view->priv->tree == NULL)
@@ -2116,20 +2421,25 @@ gtk_tree_view_update_size (GtkTreeView *tree_view)
static void
gtk_tree_view_size_request (GtkWidget *widget,
- GtkRequisition *requisition)
+ GtkRequisition *requisition,
+ gboolean may_validate)
{
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
GList *tmp_list;
- /* we validate some rows initially just to make sure we have some size.
- * In practice, with a lot of static lists, this should get a good width.
- */
- do_validate_rows (tree_view, FALSE);
+ if (may_validate)
+ {
+ /* we validate some rows initially just to make sure we have some size.
+ * In practice, with a lot of static lists, this should get a good width.
+ */
+ do_validate_rows (tree_view, FALSE);
+ }
+
gtk_tree_view_size_request_columns (tree_view);
gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
requisition->width = tree_view->priv->width;
- requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
+ requisition->height = tree_view->priv->height + gtk_tree_view_get_effective_header_height (tree_view);
tmp_list = tree_view->priv->children;
}
@@ -2141,7 +2451,7 @@ gtk_tree_view_get_preferred_width (GtkWidget *widget,
{
GtkRequisition requisition;
- gtk_tree_view_size_request (widget, &requisition);
+ gtk_tree_view_size_request (widget, &requisition, TRUE);
*minimum = *natural = requisition.width;
}
@@ -2153,7 +2463,7 @@ gtk_tree_view_get_preferred_height (GtkWidget *widget,
{
GtkRequisition requisition;
- gtk_tree_view_size_request (widget, &requisition);
+ gtk_tree_view_size_request (widget, &requisition, TRUE);
*minimum = *natural = requisition.height;
}
@@ -2235,43 +2545,6 @@ invalidate_last_column (GtkTreeView *tree_view)
}
}
-static gint
-gtk_tree_view_get_real_requested_width_from_column (GtkTreeView *tree_view,
- GtkTreeViewColumn *column)
-{
- gint max_width, min_width;
- gint real_requested_width;
-
- if (column->use_resized_width)
- {
- real_requested_width = column->resized_width;
- }
- else if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_FIXED)
- {
- real_requested_width = gtk_tree_view_column_get_fixed_width (column);
- }
- else if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
- {
- real_requested_width = MAX (column->requested_width, column->button_request);
- }
- else
- {
- real_requested_width = column->requested_width;
- if (real_requested_width < 0)
- real_requested_width = 0;
- }
-
- min_width = gtk_tree_view_column_get_min_width (column);
- if (min_width != -1)
- real_requested_width = MAX (real_requested_width, min_width);
-
- max_width = gtk_tree_view_column_get_max_width (column);
- if (max_width != -1)
- real_requested_width = MIN (real_requested_width, max_width);
-
- return real_requested_width;
-}
-
/* GtkWidget::size_allocate helper */
static void
gtk_tree_view_size_allocate_columns (GtkWidget *widget,
@@ -2280,7 +2553,6 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
GtkTreeView *tree_view;
GList *list, *first_column, *last_column;
GtkTreeViewColumn *column;
- GtkAllocation allocation;
GtkAllocation widget_allocation;
gint width = 0;
gint extra, extra_per_column, extra_for_last;
@@ -2306,9 +2578,6 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
first_column = first_column->next)
;
- allocation.y = 0;
- allocation.height = tree_view->priv->header_height;
-
rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
/* find out how many extra space and expandable columns we have */
@@ -2319,7 +2588,7 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
if (!gtk_tree_view_column_get_visible (column))
continue;
- full_requested_width += gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
+ full_requested_width += _gtk_tree_view_column_request_width (column);
if (gtk_tree_view_column_get_expand (column))
number_of_expand_columns++;
@@ -2366,7 +2635,6 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
list != (rtl ? first_column->prev : last_column->next);
list = (rtl ? list->prev : list->next))
{
- gint real_requested_width = 0;
gint old_width, column_width;
column = list->data;
@@ -2380,21 +2648,20 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
if (column == tree_view->priv->drag_column)
{
GtkAllocation drag_allocation;
+ GtkWidget *button;
+
+ button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
drag_allocation.x = 0;
drag_allocation.y = 0;
drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
- gtk_widget_size_allocate (tree_view->priv->drag_column->button,
- &drag_allocation);
+ gtk_widget_size_allocate (button, &drag_allocation);
width += drag_allocation.width;
continue;
}
- real_requested_width = gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
-
- allocation.x = width;
- column->width = real_requested_width;
+ column_width = _gtk_tree_view_column_request_width (column);
if (gtk_tree_view_column_get_expand (column))
{
@@ -2402,11 +2669,11 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
{
/* We add the remander to the last column as
* */
- column->width += extra;
+ column_width += extra;
}
else
{
- column->width += extra_per_column;
+ column_width += extra_per_column;
extra -= extra_per_column;
number_of_expand_columns --;
}
@@ -2414,31 +2681,21 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
else if (number_of_expand_columns == 0 &&
list == last_column)
{
- column->width += extra;
+ column_width += extra;
}
/* In addition to expand, the last column can get even more
* extra space so all available space is filled up.
*/
if (extra_for_last > 0 && list == last_column)
- column->width += extra_for_last;
+ column_width += extra_for_last;
- g_object_notify (G_OBJECT (column), "width");
+ _gtk_tree_view_column_allocate (column, width, column_width);
- column_width = gtk_tree_view_column_get_width (column);
- allocation.width = column_width;
width += column_width;
if (column_width > old_width)
column_changed = TRUE;
-
- gtk_widget_size_allocate (column->button, &allocation);
-
- if (column->window)
- gdk_window_move_resize (column->window,
- allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
- allocation.y,
- TREE_VIEW_DRAG_WIDTH, allocation.height);
}
/* We change the width here. The user might have been resizing columns,
@@ -2544,7 +2801,7 @@ gtk_tree_view_size_allocate (GtkWidget *widget,
g_object_freeze_notify (G_OBJECT (tree_view->priv->vadjustment));
gtk_adjustment_set_page_size (tree_view->priv->vadjustment,
allocation->height -
- TREE_VIEW_HEADER_HEIGHT (tree_view));
+ gtk_tree_view_get_effective_header_height (tree_view));
gtk_adjustment_set_step_increment (tree_view->priv->vadjustment,
tree_view->priv->vadjustment->page_size * 0.1);
gtk_adjustment_set_page_increment (tree_view->priv->vadjustment,
@@ -2578,9 +2835,9 @@ gtk_tree_view_size_allocate (GtkWidget *widget,
tree_view->priv->header_height);
gdk_window_move_resize (tree_view->priv->bin_window,
- (gint) tree_view->priv->hadjustment->value,
- TREE_VIEW_HEADER_HEIGHT (tree_view),
+ gtk_tree_view_get_effective_header_height (tree_view),
MAX (tree_view->priv->width, allocation->width),
- allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
+ allocation->height - gtk_tree_view_get_effective_header_height (tree_view));
}
if (tree_view->priv->tree == NULL)
@@ -2639,7 +2896,7 @@ grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view)
if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ tree_view->priv->draw_keyfocus = 0;
}
static inline gboolean
@@ -2698,14 +2955,12 @@ gtk_tree_view_button_press (GtkWidget *widget,
GtkRBNode *node;
GtkRBTree *tree;
GtkTreePath *path;
- gchar *path_string;
gint depth;
gint new_y;
gint y_offset;
gint dval;
gint pre_val, aft_val;
GtkTreeViewColumn *column = NULL;
- GtkCellRenderer *focus_cell = NULL;
gint column_handled_click = FALSE;
gboolean row_double_click = FALSE;
gboolean rtl;
@@ -2720,8 +2975,8 @@ gtk_tree_view_button_press (GtkWidget *widget,
/* are we in an arrow? */
if (tree_view->priv->prelight_node &&
- GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT) &&
- TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ tree_view->priv->arrow_prelit &&
+ gtk_tree_view_draw_expanders (tree_view))
{
if (event->button == 1)
{
@@ -2763,7 +3018,7 @@ gtk_tree_view_button_press (GtkWidget *widget,
depth = gtk_tree_path_get_depth (path);
background_area.y = y_offset + event->y;
- background_area.height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ background_area.height = gtk_tree_view_get_row_height (tree_view, node);
background_area.x = 0;
@@ -2777,7 +3032,7 @@ gtk_tree_view_button_press (GtkWidget *widget,
if (!gtk_tree_view_column_get_visible (candidate))
continue;
- background_area.width = candidate->width;
+ background_area.width = gtk_tree_view_column_get_width (candidate);
if ((background_area.x > (gint) event->x) ||
(background_area.x + background_area.width <= (gint) event->x))
{
@@ -2798,7 +3053,7 @@ gtk_tree_view_button_press (GtkWidget *widget,
cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
- if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ if (gtk_tree_view_draw_expanders (tree_view))
{
if (!rtl)
cell_area.x += depth * tree_view->priv->expander_size;
@@ -2844,41 +3099,21 @@ gtk_tree_view_button_press (GtkWidget *widget,
/* FIXME: get the right flags */
guint flags = 0;
- path_string = gtk_tree_path_to_string (path);
-
if (_gtk_tree_view_column_cell_event (column,
- &cell_editable,
(GdkEvent *)event,
- path_string,
- &background_area,
&cell_area, flags))
{
+ GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
+ cell_editable = gtk_cell_area_get_edit_widget (area);
+
if (cell_editable != NULL)
{
- gint left, right;
- GdkRectangle area;
-
- area = cell_area;
- _gtk_tree_view_column_get_neighbor_sizes (column, _gtk_tree_view_column_get_edited_cell (column), &left, &right);
-
- area.x += left;
- area.width -= right + left;
-
- gtk_tree_view_real_start_editing (tree_view,
- column,
- path,
- cell_editable,
- &area,
- (GdkEvent *)event,
- flags);
- g_free (path_string);
gtk_tree_path_free (path);
gtk_tree_path_free (anchor);
return TRUE;
}
column_handled_click = TRUE;
}
- g_free (path_string);
}
if (anchor)
gtk_tree_path_free (anchor);
@@ -2892,12 +3127,24 @@ gtk_tree_view_button_press (GtkWidget *widget,
*/
if (event->type == GDK_BUTTON_PRESS)
{
+ GtkCellRenderer *focus_cell;
+
if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
tree_view->priv->ctrl_pressed = TRUE;
if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
tree_view->priv->shift_pressed = TRUE;
- focus_cell = _gtk_tree_view_column_get_cell_at_pos (column, event->x - background_area.x);
+ /* We update the focus cell here, this is also needed if the
+ * column does not contain an editable cell. In this case,
+ * GtkCellArea did not receive the event for processing (and
+ * could not update the focus cell).
+ */
+ focus_cell = _gtk_tree_view_column_get_cell_at_pos (column,
+ &cell_area,
+ &background_area,
+ event->x,
+ event->y);
+
if (focus_cell)
gtk_tree_view_column_focus_cell (column, focus_cell);
@@ -3016,22 +3263,23 @@ gtk_tree_view_button_press (GtkWidget *widget,
for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
{
column = list->data;
- if (event->window == column->window &&
+ if (event->window == _gtk_tree_view_column_get_window (column) &&
gtk_tree_view_column_get_resizable (column) &&
- column->window)
+ _gtk_tree_view_column_get_window (column))
{
+ GtkWidget *button;
GtkAllocation button_allocation;
gpointer drag_data;
if (event->type == GDK_2BUTTON_PRESS &&
gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE)
{
- column->use_resized_width = FALSE;
+ _gtk_tree_view_column_set_use_resized_width (column, FALSE);
_gtk_tree_view_column_autosize (tree_view, column);
return TRUE;
}
- if (gdk_pointer_grab (column->window, FALSE,
+ if (gdk_pointer_grab (_gtk_tree_view_column_get_window (column), FALSE,
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
@@ -3039,9 +3287,10 @@ gtk_tree_view_button_press (GtkWidget *widget,
return FALSE;
gtk_grab_add (widget);
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
- column->resized_width = gtk_tree_view_column_get_width (column) -
- tree_view->priv->last_extra_space_per_column;
+ tree_view->priv->in_column_resize = TRUE;
+
+ _gtk_tree_view_column_set_resized_width (column, gtk_tree_view_column_get_width (column) -
+ tree_view->priv->last_extra_space_per_column);
/* block attached dnd signal handler */
drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
@@ -3051,7 +3300,8 @@ gtk_tree_view_button_press (GtkWidget *widget,
0, 0, NULL, NULL,
drag_data);
- gtk_widget_get_allocation (column->button, &button_allocation);
+ button = gtk_tree_view_column_get_button (column);
+ gtk_widget_get_allocation (button, &button_allocation);
tree_view->priv->drag_pos = i;
tree_view->priv->x_drag = button_allocation.x + (rtl ? 0 : button_allocation.width);
@@ -3070,6 +3320,7 @@ gtk_tree_view_button_release_drag_column (GtkWidget *widget,
GdkEventButton *event)
{
GtkTreeView *tree_view;
+ GtkWidget *button;
GList *l;
gboolean rtl;
@@ -3080,21 +3331,22 @@ gtk_tree_view_button_release_drag_column (GtkWidget *widget,
gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
/* Move the button back */
- g_object_ref (tree_view->priv->drag_column->button);
- gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
- gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
- gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
- g_object_unref (tree_view->priv->drag_column->button);
+ button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
+ g_object_ref (button);
+ gtk_container_remove (GTK_CONTAINER (tree_view), button);
+ gtk_widget_set_parent_window (button, tree_view->priv->header_window);
+ gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
+ g_object_unref (button);
gtk_widget_queue_resize (widget);
if (gtk_tree_view_column_get_resizable (tree_view->priv->drag_column))
{
- gdk_window_raise (tree_view->priv->drag_column->window);
- gdk_window_show (tree_view->priv->drag_column->window);
+ gdk_window_raise (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
+ gdk_window_show (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
}
else
- gdk_window_hide (tree_view->priv->drag_column->window);
+ gdk_window_hide (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
- gtk_widget_grab_focus (tree_view->priv->drag_column->button);
+ gtk_widget_grab_focus (button);
if (rtl)
{
@@ -3124,7 +3376,7 @@ gtk_tree_view_button_release_drag_column (GtkWidget *widget,
/* Reset our flags */
tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG);
+ tree_view->priv->in_column_drag = FALSE;
return TRUE;
}
@@ -3149,7 +3401,7 @@ gtk_tree_view_button_release_column_resize (GtkWidget *widget,
0, 0, NULL, NULL,
drag_data);
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
+ tree_view->priv->in_column_resize = FALSE;
gtk_grab_remove (widget);
gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
event->time);
@@ -3162,7 +3414,7 @@ gtk_tree_view_button_release (GtkWidget *widget,
{
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
+ if (tree_view->priv->in_column_drag)
return gtk_tree_view_button_release_drag_column (widget, event);
if (tree_view->priv->rubber_band_status)
@@ -3171,7 +3423,7 @@ gtk_tree_view_button_release (GtkWidget *widget,
if (tree_view->priv->pressed_button == event->button)
tree_view->priv->pressed_button = -1;
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
+ if (tree_view->priv->in_column_resize)
return gtk_tree_view_button_release_column_resize (widget, event);
if (tree_view->priv->button_pressed_node == NULL)
@@ -3181,7 +3433,7 @@ gtk_tree_view_button_release (GtkWidget *widget,
{
gtk_grab_remove (widget);
if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node &&
- GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+ tree_view->priv->arrow_prelit)
{
GtkTreePath *path = NULL;
@@ -3214,10 +3466,10 @@ gtk_tree_view_grab_broken (GtkWidget *widget,
{
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
+ if (tree_view->priv->in_column_drag)
gtk_tree_view_button_release_drag_column (widget, (GdkEventButton *)event);
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
+ if (tree_view->priv->in_column_resize)
gtk_tree_view_button_release_column_resize (widget, (GdkEventButton *)event);
return TRUE;
@@ -3257,9 +3509,8 @@ coords_are_over_arrow (GtkTreeView *tree_view,
if ((node->flags & GTK_RBNODE_IS_PARENT) == 0)
return FALSE;
- arrow.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
-
- arrow.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
+ arrow.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
+ arrow.height = gtk_tree_view_get_row_height (tree_view, node);
gtk_tree_view_get_arrow_xrange (tree_view, tree, &arrow.x, &x2);
@@ -3320,23 +3571,18 @@ do_prelight (GtkTreeView *tree_view,
/* We are still on the same node,
but we might need to take care of the arrow */
- if (tree && node && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ if (tree && node && gtk_tree_view_draw_expanders (tree_view))
{
gboolean over_arrow;
- gboolean flag_set;
over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y);
- flag_set = GTK_TREE_VIEW_FLAG_SET (tree_view,
- GTK_TREE_VIEW_ARROW_PRELIT);
- if (over_arrow != flag_set)
+ if (over_arrow != tree_view->priv->arrow_prelit)
{
if (over_arrow)
- GTK_TREE_VIEW_SET_FLAG (tree_view,
- GTK_TREE_VIEW_ARROW_PRELIT);
+ tree_view->priv->arrow_prelit = TRUE;
else
- GTK_TREE_VIEW_UNSET_FLAG (tree_view,
- GTK_TREE_VIEW_ARROW_PRELIT);
+ tree_view->priv->arrow_prelit = FALSE;
gtk_tree_view_queue_draw_arrow (tree_view, tree, node);
}
@@ -3352,10 +3598,10 @@ do_prelight (GtkTreeView *tree_view,
GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node,
GTK_RBNODE_IS_PRELIT);
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)
- && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ if (tree_view->priv->arrow_prelit
+ && gtk_tree_view_draw_expanders (tree_view))
{
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ tree_view->priv->arrow_prelit = FALSE;
gtk_tree_view_queue_draw_arrow (tree_view,
tree_view->priv->prelight_tree,
@@ -3381,10 +3627,10 @@ do_prelight (GtkTreeView *tree_view,
/* Prelight the new node and arrow */
- if (TREE_VIEW_DRAW_EXPANDERS (tree_view)
+ if (gtk_tree_view_draw_expanders (tree_view)
&& coords_are_over_arrow (tree_view, tree, node, x, y))
{
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ tree_view->priv->arrow_prelit = TRUE;
gtk_tree_view_queue_draw_arrow (tree_view, tree, node);
}
@@ -3413,7 +3659,8 @@ prelight_or_select (GtkTreeView *tree_view,
if (tree_view->priv->hover_selection &&
(mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) &&
!(tree_view->priv->edited_column &&
- tree_view->priv->edited_column->editable_widget))
+ gtk_cell_area_get_edit_widget
+ (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column)))))
{
if (node)
{
@@ -3425,7 +3672,7 @@ prelight_or_select (GtkTreeView *tree_view,
gtk_tree_selection_select_path (tree_view->priv->selection, path);
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
{
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ tree_view->priv->draw_keyfocus = FALSE;
gtk_tree_view_real_set_cursor (tree_view, path, FALSE, FALSE);
}
gtk_tree_path_free (path);
@@ -3528,16 +3775,19 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
{
GtkAllocation left_allocation, right_allocation;
GdkRectangle visible_rect;
+ GtkWidget *button;
gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
if (reorder->left_column)
{
- gtk_widget_get_allocation (reorder->left_column->button, &left_allocation);
+ button = gtk_tree_view_column_get_button (reorder->left_column);
+ gtk_widget_get_allocation (button, &left_allocation);
x = left_allocation.x + left_allocation.width;
}
else
{
- gtk_widget_get_allocation (reorder->right_column->button, &right_allocation);
+ button = gtk_tree_view_column_get_button (reorder->right_column);
+ gtk_widget_get_allocation (button, &right_allocation);
x = right_allocation.x;
}
@@ -3555,6 +3805,7 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
{
GtkAllocation drag_allocation;
+ GtkWidget *button;
if (tree_view->priv->drag_highlight_window)
{
@@ -3563,11 +3814,12 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
gdk_window_destroy (tree_view->priv->drag_highlight_window);
}
+ button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.x = tree_view->priv->drag_column_x;
attributes.y = 0;
- gtk_widget_get_allocation (tree_view->priv->drag_column->button, &drag_allocation);
+ gtk_widget_get_allocation (button, &drag_allocation);
width = attributes.width = drag_allocation.width;
height = attributes.height = drag_allocation.height;
attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
@@ -3596,6 +3848,7 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
{
GtkAllocation button_allocation;
+ GtkWidget *button;
width = tree_view->priv->expander_size;
@@ -3603,13 +3856,15 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
if (reorder->left_column)
{
- gtk_widget_get_allocation (reorder->left_column->button, &button_allocation);
+ button = gtk_tree_view_column_get_button (reorder->left_column);
+ gtk_widget_get_allocation (button, &button_allocation);
x += button_allocation.x + button_allocation.width - width/2;
height = button_allocation.height;
}
else
{
- gtk_widget_get_allocation (reorder->right_column->button, &button_allocation);
+ button = gtk_tree_view_column_get_button (reorder->right_column);
+ gtk_widget_get_allocation (button, &button_allocation);
x += button_allocation.x - width/2;
height = button_allocation.height;
}
@@ -3666,6 +3921,7 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
{
GtkAllocation allocation;
+ GtkWidget *button;
width = tree_view->priv->expander_size;
@@ -3681,12 +3937,14 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
if (reorder->left_column)
{
- gtk_widget_get_allocation (reorder->left_column->button, &allocation);
+ button = gtk_tree_view_column_get_button (reorder->left_column);
+ gtk_widget_get_allocation (button, &allocation);
height = allocation.height;
}
else
{
- gtk_widget_get_allocation (reorder->right_column->button, &allocation);
+ button = gtk_tree_view_column_get_button (reorder->right_column);
+ gtk_widget_get_allocation (button, &allocation);
height = allocation.height;
}
@@ -3780,10 +4038,14 @@ gtk_tree_view_motion_resize_column (GtkWidget *widget,
if (x != tree_view->priv->x_drag &&
(new_width != gtk_tree_view_column_get_fixed_width (column)))
{
- column->use_resized_width = TRUE;
- column->resized_width = new_width;
+ _gtk_tree_view_column_set_use_resized_width (column, TRUE);
+
if (gtk_tree_view_column_get_expand (column))
- column->resized_width -= tree_view->priv->last_extra_space_per_column;
+ new_width -= tree_view->priv->last_extra_space_per_column;
+
+ _gtk_tree_view_column_set_resized_width (column, new_width);
+
+
gtk_widget_queue_resize (widget);
}
@@ -3874,6 +4136,7 @@ gtk_tree_view_motion_drag_column (GtkWidget *widget,
GtkAllocation allocation, button_allocation;
GtkTreeView *tree_view = (GtkTreeView *) widget;
GtkTreeViewColumn *column = tree_view->priv->drag_column;
+ GtkWidget *button;
gint x, y;
/* Sanity Check */
@@ -3881,11 +4144,13 @@ gtk_tree_view_motion_drag_column (GtkWidget *widget,
(event->window != tree_view->priv->drag_window))
return FALSE;
+ button = gtk_tree_view_column_get_button (column);
+
/* Handle moving the header */
gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
gtk_widget_get_allocation (widget, &allocation);
- gtk_widget_get_allocation (column->button, &button_allocation);
- x = CLAMP (x + (gint)event->x - column->drag_x, 0,
+ gtk_widget_get_allocation (button, &button_allocation);
+ x = CLAMP (x + (gint)event->x - _gtk_tree_view_column_get_drag_x (column), 0,
MAX (tree_view->priv->width, allocation.width) - button_allocation.width);
gdk_window_move (tree_view->priv->drag_window, x, y);
@@ -4294,11 +4559,11 @@ gtk_tree_view_motion (GtkWidget *widget,
tree_view = (GtkTreeView *) widget;
/* Resizing a column */
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
+ if (tree_view->priv->in_column_resize)
return gtk_tree_view_motion_resize_column (widget, event);
/* Drag column */
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
+ if (tree_view->priv->in_column_drag)
return gtk_tree_view_motion_drag_column (widget, event);
/* Sanity check it */
@@ -4476,7 +4741,7 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
gint horizontal_separator;
gint focus_line_width;
gboolean allow_rules;
- gboolean has_special_cell;
+ gboolean has_can_focus_cell;
gboolean rtl;
gint n_visible_columns;
gint pointer_x, pointer_y;
@@ -4603,7 +4868,7 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
is_separator = row_is_separator (tree_view, &iter, NULL);
- max_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
+ max_height = gtk_tree_view_get_row_height (tree_view, node);
cell_offset = 0;
highlight_x = 0; /* should match x coord of first cell */
@@ -4623,7 +4888,7 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
parity = _gtk_rbtree_node_find_parity (tree, node);
/* we *need* to set cell data on all cells before the call
- * to _has_special_cell, else _has_special_cell() does not
+ * to _has_can_focus_cell, else _has_can_focus_cell() does not
* return a correct value.
*/
for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
@@ -4638,7 +4903,7 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
node->children?TRUE:FALSE);
}
- has_special_cell = gtk_tree_view_has_special_cell (tree_view);
+ has_can_focus_cell = gtk_tree_view_has_can_focus_cell (tree_view);
for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
list;
@@ -4649,6 +4914,7 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
gchar new_detail[128];
gint width;
GtkStateType state;
+ gboolean draw_focus;
if (!gtk_tree_view_column_get_visible (column))
continue;
@@ -4767,6 +5033,15 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
else
state = GTK_STATE_NORMAL;
+ if (node == cursor && has_can_focus_cell
+ && ((column == tree_view->priv->focus_column
+ && tree_view->priv->draw_keyfocus &&
+ gtk_widget_has_focus (widget))
+ || (column == tree_view->priv->edited_column)))
+ draw_focus = TRUE;
+ else
+ draw_focus = FALSE;
+
/* Draw background */
is_first = (rtl ? !list->next : !list->prev);
is_last = (rtl ? !list->prev : !list->next);
@@ -4800,7 +5075,7 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
- if (TREE_VIEW_DRAW_EXPANDERS(tree_view))
+ if (gtk_tree_view_draw_expanders (tree_view))
{
if (!rtl)
cell_area.x += depth * tree_view->priv->expander_size;
@@ -4828,8 +5103,9 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
cr,
&background_area,
&cell_area,
- flags);
- if (TREE_VIEW_DRAW_EXPANDERS(tree_view)
+ flags,
+ draw_focus);
+ if (gtk_tree_view_draw_expanders (tree_view)
&& (node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
{
if (!got_pointer)
@@ -4862,7 +5138,8 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
cr,
&background_area,
&cell_area,
- flags);
+ flags,
+ draw_focus);
}
if (draw_hgrid_lines)
@@ -4954,19 +5231,6 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
}
}
- if (node == cursor && has_special_cell &&
- ((column == tree_view->priv->focus_column &&
- GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS) &&
- gtk_widget_has_focus (widget)) ||
- (column == tree_view->priv->edited_column)))
- {
- _gtk_tree_view_column_cell_draw_focus (column,
- cr,
- &background_area,
- &cell_area,
- flags);
- }
-
cell_offset += gtk_tree_view_column_get_width (column);
}
@@ -5004,10 +5268,10 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
(is_first
? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
: (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
- 0, BACKGROUND_FIRST_PIXEL (tree_view, tree, node)
+ 0, gtk_tree_view_get_row_y_offset (tree_view, tree, node)
- focus_line_width / 2,
gdk_window_get_width (tree_view->priv->bin_window),
- ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node))
+ gtk_tree_view_get_row_height (tree_view, node)
- focus_line_width + 1);
break;
}
@@ -5024,8 +5288,8 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
}
/* draw the big row-spanning focus rectangle, if needed */
- if (!has_special_cell && node == cursor &&
- GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS) &&
+ if (!has_can_focus_cell && node == cursor &&
+ tree_view->priv->draw_keyfocus &&
gtk_widget_has_focus (widget))
{
gint tmp_y, tmp_height;
@@ -5039,13 +5303,13 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
if (draw_hgrid_lines)
{
- tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node) + grid_line_width / 2;
- tmp_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node)) - grid_line_width;
+ tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node) + grid_line_width / 2;
+ tmp_height = gtk_tree_view_get_row_height (tree_view, node) - grid_line_width;
}
else
{
- tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
- tmp_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
+ tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
+ tmp_height = gtk_tree_view_get_row_height (tree_view, node);
}
gtk_paint_focus (style,
@@ -5140,6 +5404,7 @@ gtk_tree_view_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ GtkWidget *button;
if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
{
@@ -5179,18 +5444,20 @@ gtk_tree_view_draw (GtkWidget *widget,
continue;
if (gtk_tree_view_column_get_visible (column))
- gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
- column->button,
- cr);
+ {
+ button = gtk_tree_view_column_get_button (column);
+ gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
+ button, cr);
+ }
}
}
if (tree_view->priv->drag_window &&
gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
{
+ button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
- tree_view->priv->drag_column->button,
- cr);
+ button, cr);
}
return TRUE;
@@ -5415,6 +5682,7 @@ gtk_tree_view_key_press (GtkWidget *widget,
GdkEventKey *event)
{
GtkTreeView *tree_view = (GtkTreeView *) widget;
+ GtkWidget *button;
if (tree_view->priv->rubber_band_status)
{
@@ -5424,7 +5692,7 @@ gtk_tree_view_key_press (GtkWidget *widget,
return TRUE;
}
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
+ if (tree_view->priv->in_column_drag)
{
if (event->keyval == GDK_KEY_Escape)
{
@@ -5434,7 +5702,7 @@ gtk_tree_view_key_press (GtkWidget *widget,
return TRUE;
}
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+ if (tree_view->priv->headers_visible)
{
GList *focus_column;
gboolean rtl;
@@ -5446,8 +5714,9 @@ gtk_tree_view_key_press (GtkWidget *widget,
focus_column = focus_column->next)
{
GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
-
- if (gtk_widget_has_focus (column->button))
+
+ button = gtk_tree_view_column_get_button (column);
+ if (gtk_widget_has_focus (button))
break;
}
@@ -5469,54 +5738,62 @@ gtk_tree_view_key_press (GtkWidget *widget,
|| event->keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left))
{
GtkRequisition button_req;
- gint old_width = column->resized_width;
+ gint old_width = _gtk_tree_view_column_get_resized_width (column);
+ gint new_width;
+
+ button = gtk_tree_view_column_get_button (column);
+
+ gtk_widget_get_preferred_size (button, &button_req, NULL);
- gtk_widget_get_preferred_size (column->button, &button_req, NULL);
+ new_width = MAX (old_width, gtk_tree_view_column_get_width (column));
+ new_width -= 2;
+ if (new_width < 0)
+ new_width = 0;
- column->resized_width = MAX (column->resized_width,
- gtk_tree_view_column_get_width (column));
- column->resized_width -= 2;
- if (column->resized_width < 0)
- column->resized_width = 0;
+ _gtk_tree_view_column_set_resized_width (column, new_width);
min_width = gtk_tree_view_column_get_min_width (column);
if (min_width == -1)
- column->resized_width = MAX (button_req.width,
- column->resized_width);
+ new_width = MAX (button_req.width, new_width);
else
{
- column->resized_width = MAX (min_width,
- column->resized_width);
+ new_width = MAX (min_width, new_width);
}
max_width = gtk_tree_view_column_get_max_width (column);
if (max_width != -1)
- column->resized_width = MIN (column->resized_width, max_width);
+ new_width = MIN (new_width, max_width);
- column->use_resized_width = TRUE;
+ _gtk_tree_view_column_set_use_resized_width (column, TRUE);
- if (column->resized_width != old_width)
- gtk_widget_queue_resize (widget);
+ if (new_width != old_width)
+ {
+ _gtk_tree_view_column_set_resized_width (column, new_width);
+ gtk_widget_queue_resize (widget);
+ }
else
gtk_widget_error_bell (widget);
}
else if (event->keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right)
|| event->keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right))
{
- gint old_width = column->resized_width;
+ gint old_width = _gtk_tree_view_column_get_resized_width (column);
+ gint new_width;
- column->resized_width = MAX (column->resized_width,
- gtk_tree_view_column_get_width (column));
- column->resized_width += 2;
+ new_width = MAX (old_width, gtk_tree_view_column_get_width (column));
+ new_width += 2;
max_width = gtk_tree_view_column_get_max_width (column);
if (max_width != -1)
- column->resized_width = MIN (column->resized_width, max_width);
+ new_width = MIN (new_width, max_width);
- column->use_resized_width = TRUE;
+ _gtk_tree_view_column_set_use_resized_width (column, TRUE);
- if (column->resized_width != old_width)
- gtk_widget_queue_resize (widget);
+ if (new_width != old_width)
+ {
+ _gtk_tree_view_column_set_resized_width (column, new_width);
+ gtk_widget_queue_resize (widget);
+ }
else
gtk_widget_error_bell (widget);
}
@@ -5770,7 +6047,7 @@ gtk_tree_view_node_queue_redraw (GtkTreeView *tree_view,
y = _gtk_rbtree_node_find_offset (tree, node)
- tree_view->priv->vadjustment->value
- + TREE_VIEW_HEADER_HEIGHT (tree_view);
+ + gtk_tree_view_get_effective_header_height (tree_view);
gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
@@ -5788,7 +6065,7 @@ node_is_visible (GtkTreeView *tree_view,
int height;
y = _gtk_rbtree_node_find_offset (tree, node);
- height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ height = gtk_tree_view_get_row_height (tree_view, node);
if (y >= tree_view->priv->vadjustment->value &&
y + height <= (tree_view->priv->vadjustment->value
@@ -5812,7 +6089,6 @@ validate_row (GtkTreeView *tree_view,
gint height = 0;
gint horizontal_separator;
gint vertical_separator;
- gint focus_line_width;
gint depth = gtk_tree_path_get_depth (path);
gboolean retval = FALSE;
gboolean is_separator = FALSE;
@@ -5831,7 +6107,6 @@ validate_row (GtkTreeView *tree_view,
gtk_widget_style_get (GTK_WIDGET (tree_view),
"focus-padding", &focus_pad,
- "focus-line-width", &focus_line_width,
"horizontal-separator", &horizontal_separator,
"vertical-separator", &vertical_separator,
"grid-line-width", &grid_line_width,
@@ -5860,28 +6135,33 @@ validate_row (GtkTreeView *tree_view,
for (list = tree_view->priv->columns; list; list = list->next)
{
- gint tmp_width;
- gint tmp_height;
+ gint padding = 0;
+ gint original_width;
+ gint new_width;
+ gint row_height;
column = list->data;
if (!gtk_tree_view_column_get_visible (column))
continue;
- if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) && !column->dirty)
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) &&
+ !_gtk_tree_view_column_cell_get_dirty (column))
continue;
+ original_width = _gtk_tree_view_column_get_requested_width (column);
+
gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, iter,
GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
node->children?TRUE:FALSE);
gtk_tree_view_column_cell_get_size (column,
NULL, NULL, NULL,
- &tmp_width, &tmp_height);
+ NULL, &row_height);
if (!is_separator)
{
- tmp_height += vertical_separator;
- height = MAX (height, tmp_height);
+ row_height += vertical_separator;
+ height = MAX (height, row_height);
height = MAX (height, tree_view->priv->expander_size);
}
else
@@ -5894,27 +6174,28 @@ validate_row (GtkTreeView *tree_view,
if (gtk_tree_view_is_expander_column (tree_view, column))
{
- tmp_width = tmp_width + horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
+ padding += horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
- if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
- tmp_width += depth * tree_view->priv->expander_size;
+ if (gtk_tree_view_draw_expanders (tree_view))
+ padding += depth * tree_view->priv->expander_size;
}
else
- tmp_width = tmp_width + horizontal_separator;
+ padding += horizontal_separator;
if (draw_vgrid_lines)
{
if (list->data == first_column || list->data == last_column)
- tmp_width += grid_line_width / 2.0;
+ padding += grid_line_width / 2.0;
else
- tmp_width += grid_line_width;
+ padding += grid_line_width;
}
- if (tmp_width > column->requested_width)
- {
- retval = TRUE;
- column->requested_width = tmp_width;
- }
+ /* Update the padding for the column */
+ _gtk_tree_view_column_push_padding (column, padding);
+ new_width = _gtk_tree_view_column_get_requested_width (column);
+
+ if (new_width > original_width)
+ retval = TRUE;
}
if (draw_hgrid_lines)
@@ -5955,7 +6236,7 @@ validate_visible_area (GtkTreeView *tree_view)
return;
gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
- total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
+ total_height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
if (total_height == 0)
return;
@@ -5979,7 +6260,7 @@ validate_visible_area (GtkTreeView *tree_view)
if (tree_view->priv->scroll_to_use_align)
{
- gint height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ gint height = gtk_tree_view_get_row_height (tree_view, node);
area_above = (total_height - height) *
tree_view->priv->scroll_to_row_align;
area_below = total_height - area_above - height;
@@ -5993,7 +6274,7 @@ validate_visible_area (GtkTreeView *tree_view)
* 2) row visible
*/
gint dy;
- gint height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ gint height = gtk_tree_view_get_row_height (tree_view, node);
dy = _gtk_rbtree_node_find_offset (tree, node);
@@ -6090,7 +6371,7 @@ validate_visible_area (GtkTreeView *tree_view)
size_changed = TRUE;
}
area_above = 0;
- area_below = total_height - ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ area_below = total_height - gtk_tree_view_get_row_height (tree_view, node);
}
above_path = gtk_tree_path_copy (path);
@@ -6201,7 +6482,7 @@ validate_visible_area (GtkTreeView *tree_view)
size_changed = TRUE;
}
- area_below -= ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ area_below -= gtk_tree_view_get_row_height (tree_view, node);
}
gtk_tree_path_free (path);
@@ -6241,7 +6522,7 @@ validate_visible_area (GtkTreeView *tree_view)
if (validate_row (tree_view, tree, node, &iter, above_path))
size_changed = TRUE;
}
- area_above -= ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ area_above -= gtk_tree_view_get_row_height (tree_view, node);
}
/* if we scrolled to a path, we need to set the dy here,
@@ -6329,7 +6610,7 @@ initialize_fixed_height_mode (GtkTreeView *tree_view)
gtk_tree_path_free (path);
- tree_view->priv->fixed_height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ tree_view->priv->fixed_height = gtk_tree_view_get_row_height (tree_view, node);
}
_gtk_rbtree_set_fixed_height (tree_view->priv->tree,
@@ -6441,7 +6722,7 @@ do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize)
{
gint height;
- height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ height = gtk_tree_view_get_row_height (tree_view, node);
if (prev_height < 0)
prev_height = height;
else if (prev_height != height)
@@ -6479,7 +6760,7 @@ do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize)
* Currently bypassing this but the real solution is to not update the scroll adjustments
* untill we've recieved an allocation (never update scroll adjustments from size-requests).
*/
- gtk_tree_view_size_request (GTK_WIDGET (tree_view), &requisition);
+ gtk_tree_view_size_request (GTK_WIDGET (tree_view), &requisition, FALSE);
tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
@@ -6714,7 +6995,7 @@ gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view)
return;
}
- if (ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node))
+ if (gtk_tree_view_get_row_height (tree_view, node)
< tree_view->priv->top_row_dy)
{
/* new top row -- do NOT install the idle handler */
@@ -6737,11 +7018,13 @@ gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view)
void
-_gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view)
+_gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view,
+ gboolean install_handler)
{
tree_view->priv->mark_rows_col_dirty = TRUE;
- install_presize_handler (tree_view);
+ if (install_handler)
+ install_presize_handler (tree_view);
}
/*
@@ -7102,7 +7385,7 @@ set_destination_row (GtkTreeView *tree_view,
di = get_info (tree_view);
- if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
+ if (di == NULL || y - gtk_tree_view_get_effective_header_height (tree_view) < 0)
{
/* someone unset us as a drag dest, note that if
* we return FALSE drag_leave isn't called
@@ -7782,8 +8065,12 @@ gtk_tree_view_remove (GtkContainer *container,
while (tmp_list)
{
GtkTreeViewColumn *column;
+ GtkWidget *button;
+
column = tmp_list->data;
- if (column->button == widget)
+ button = gtk_tree_view_column_get_button (column);
+
+ if (button == widget)
{
gtk_widget_unparent (widget);
return;
@@ -7801,6 +8088,7 @@ gtk_tree_view_forall (GtkContainer *container,
GtkTreeView *tree_view = GTK_TREE_VIEW (container);
GtkTreeViewChild *child = NULL;
GtkTreeViewColumn *column;
+ GtkWidget *button;
GList *tmp_list;
tmp_list = tree_view->priv->children;
@@ -7817,25 +8105,29 @@ gtk_tree_view_forall (GtkContainer *container,
for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
{
column = tmp_list->data;
+ button = gtk_tree_view_column_get_button (column);
- if (column->button)
- (* callback) (column->button, callback_data);
+ if (button)
+ (* callback) (button, callback_data);
}
}
-/* Returns TRUE if the treeview contains no "special" (editable or activatable)
- * cells. If so we draw one big row-spanning focus rectangle.
+/* Returns TRUE is any of the columns contains a cell that can-focus.
+ * If this is not the case, a column-spanning focus rectangle will be
+ * drawn.
*/
static gboolean
-gtk_tree_view_has_special_cell (GtkTreeView *tree_view)
+gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view)
{
GList *list;
for (list = tree_view->priv->columns; list; list = list->next)
{
- if (!gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
+ GtkTreeViewColumn *column = list->data;
+
+ if (!gtk_tree_view_column_get_visible (column))
continue;
- if (_gtk_tree_view_column_count_special_cells (list->data))
+ if (gtk_cell_area_is_activatable (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column))))
return TRUE;
}
@@ -7937,12 +8229,12 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
{
GtkTreeViewColumn *column;
GtkWidget *focus_child;
-
+ GtkWidget *button;
GList *last_column, *first_column;
GList *tmp_list;
gboolean rtl;
- if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+ if (! tree_view->priv->headers_visible)
return FALSE;
focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
@@ -7951,8 +8243,9 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
while (first_column)
{
column = GTK_TREE_VIEW_COLUMN (first_column->data);
+ button = gtk_tree_view_column_get_button (column);
- if (gtk_widget_get_can_focus (column->button) &&
+ if (gtk_widget_get_can_focus (button) &&
gtk_tree_view_column_get_visible (column) &&
(gtk_tree_view_column_get_clickable (column) ||
gtk_tree_view_column_get_reorderable (column)))
@@ -7969,8 +8262,9 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
while (last_column)
{
column = GTK_TREE_VIEW_COLUMN (last_column->data);
+ button = gtk_tree_view_column_get_button (column);
- if (gtk_widget_get_can_focus (column->button) &&
+ if (gtk_widget_get_can_focus (button) &&
gtk_tree_view_column_get_visible (column) &&
(gtk_tree_view_column_get_clickable (column) ||
gtk_tree_view_column_get_reorderable (column)))
@@ -7989,11 +8283,16 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
case GTK_DIR_DOWN:
if (focus_child == NULL)
{
- if (tree_view->priv->focus_column != NULL &&
- gtk_widget_get_can_focus (tree_view->priv->focus_column->button))
- focus_child = tree_view->priv->focus_column->button;
+ if (tree_view->priv->focus_column != NULL)
+ button = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
+ else
+ button = NULL;
+
+ if (button && gtk_widget_get_can_focus (button))
+ focus_child = button;
else
- focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+ focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
+
gtk_widget_grab_focus (focus_child);
break;
}
@@ -8004,11 +8303,12 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
if (focus_child == NULL)
{
if (tree_view->priv->focus_column != NULL)
- focus_child = tree_view->priv->focus_column->button;
+ focus_child = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
else if (dir == GTK_DIR_LEFT)
- focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
+ focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data));
else
- focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+ focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
+
gtk_widget_grab_focus (focus_child);
break;
}
@@ -8022,7 +8322,7 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
/* We need to move the focus among the row of buttons. */
for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
- if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child)
+ if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
break;
if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
@@ -8035,6 +8335,7 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
while (tmp_list)
{
GtkTreeViewColumn *column;
+ GtkWidget *button;
if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
tmp_list = tmp_list->next;
@@ -8047,12 +8348,13 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
break;
}
column = tmp_list->data;
- if (column->button &&
+ button = gtk_tree_view_column_get_button (column);
+ if (button &&
gtk_tree_view_column_get_visible (column) &&
- gtk_widget_get_can_focus (column->button))
+ gtk_widget_get_can_focus (button))
{
- focus_child = column->button;
- gtk_widget_grab_focus (column->button);
+ focus_child = button;
+ gtk_widget_grab_focus (button);
break;
}
}
@@ -8067,7 +8369,7 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view,
if (focus_child)
{
for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
- if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child)
+ if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
{
tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
break;
@@ -8239,7 +8541,7 @@ gtk_tree_view_set_focus_child (GtkContainer *container,
for (list = tree_view->priv->columns; list; list = list->next)
{
- if (GTK_TREE_VIEW_COLUMN (list->data)->button == child)
+ if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (list->data)) == child)
{
tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
break;
@@ -8269,7 +8571,7 @@ gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
return FALSE;
gtk_tree_view_stop_editing (tree_view, FALSE);
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ tree_view->priv->draw_keyfocus = TRUE;
gtk_widget_grab_focus (GTK_WIDGET (tree_view));
if (gtk_get_current_event_state (&state))
@@ -8617,17 +8919,16 @@ gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
else
GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT);
- if (has_child && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST))
+ if (has_child && tree_view->priv->is_list)
{
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
+ tree_view->priv->is_list = FALSE;
+ if (tree_view->priv->show_expanders)
{
GList *list;
for (list = tree_view->priv->columns; list; list = list->next)
if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
{
- GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE;
_gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE);
break;
}
@@ -8849,6 +9150,7 @@ gtk_tree_view_get_background_xrange (GtkTreeView *tree_view,
*x2 = total_width; /* width of 0 */
}
}
+
static void
gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
GtkRBTree *tree,
@@ -8915,7 +9217,6 @@ gtk_tree_view_build_tree (GtkTreeView *tree_view,
{
GtkRBNode *temp = NULL;
GtkTreePath *path = NULL;
- gboolean is_list = GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST);
do
{
@@ -8931,7 +9232,7 @@ gtk_tree_view_build_tree (GtkTreeView *tree_view,
}
}
- if (is_list)
+ if (tree_view->priv->is_list)
continue;
if (recurse)
@@ -8986,7 +9287,7 @@ gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
/* just return if the node is visible, avoiding a costly expose */
node_dy = _gtk_rbtree_node_find_offset (tree, node);
- height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
+ height = gtk_tree_view_get_row_height (tree_view, node);
if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID)
&& node_dy >= tree_view->priv->vadjustment->value
&& node_dy + height <= (tree_view->priv->vadjustment->value
@@ -9016,7 +9317,7 @@ gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view,
if (column == NULL)
return;
- gtk_widget_get_allocation (column->button, &allocation);
+ gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
x = allocation.x;
width = allocation.width;
@@ -9028,42 +9329,31 @@ gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view,
* focus cell is bigger than the page size, we make sure the
* left-hand side of the cell is visible).
*
- * If the column does not have those so-called special cells, we
+ * If the column does not have an activatable cell, we
* make sure the left-hand side of the column is visible.
*/
- if (focus_to_cell && gtk_tree_view_has_special_cell (tree_view))
+ if (focus_to_cell && gtk_tree_view_has_can_focus_cell (tree_view))
{
- GtkTreePath *cursor_path;
- GdkRectangle background_area, cell_area, focus_area;
+ GtkCellArea *cell_area;
+ GtkCellRenderer *focus_cell;
- cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
+ cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
+ focus_cell = gtk_cell_area_get_focus_cell (cell_area);
- gtk_tree_view_get_cell_area (tree_view,
- cursor_path, column, &cell_area);
- gtk_tree_view_get_background_area (tree_view,
- cursor_path, column,
- &background_area);
-
- gtk_tree_path_free (cursor_path);
-
- _gtk_tree_view_column_get_focus_area (column,
- &background_area,
- &cell_area,
- &focus_area);
-
- x = focus_area.x;
- width = focus_area.width;
-
- if (width < tree_view->priv->hadjustment->page_size)
- {
- if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
- gtk_adjustment_set_value (tree_view->priv->hadjustment,
- x + width - tree_view->priv->hadjustment->page_size);
- else if (tree_view->priv->hadjustment->value > x)
- gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
- }
- }
+ if (gtk_tree_view_column_cell_get_position (column, focus_cell,
+ &x, &width))
+ {
+ if (width < tree_view->priv->hadjustment->page_size)
+ {
+ if (tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size < x + width)
+ gtk_adjustment_set_value (tree_view->priv->hadjustment,
+ x + width - tree_view->priv->hadjustment->page_size);
+ else if (tree_view->priv->hadjustment->value > x)
+ gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
+ }
+ }
+ }
gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
}
@@ -9172,7 +9462,7 @@ gtk_tree_view_is_expander_column (GtkTreeView *tree_view,
{
GList *list;
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST))
+ if (tree_view->priv->is_list)
return FALSE;
if (tree_view->priv->expander_column != NULL)
@@ -9194,6 +9484,15 @@ gtk_tree_view_is_expander_column (GtkTreeView *tree_view,
return FALSE;
}
+static inline gboolean
+gtk_tree_view_draw_expanders (GtkTreeView *tree_view)
+{
+ if (!tree_view->priv->is_list && tree_view->priv->show_expanders)
+ return TRUE;
+ /* else */
+ return FALSE;
+}
+
static void
gtk_tree_view_add_move_binding (GtkBindingSet *binding_set,
guint keyval,
@@ -9380,11 +9679,16 @@ gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view,
if (tmp_list->next != NULL)
{
GtkAllocation right_allocation, left_allocation;
+ GtkWidget *left_button, *right_button;
g_assert (tmp_list->next->data);
- gtk_widget_get_allocation (reorder->right_column->button, &right_allocation);
- gtk_widget_get_allocation (((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column->button, &left_allocation);
+ right_button = gtk_tree_view_column_get_button (reorder->right_column);
+ left_button = gtk_tree_view_column_get_button
+ (((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column);
+
+ gtk_widget_get_allocation (right_button, &right_allocation);
+ gtk_widget_get_allocation (left_button, &left_allocation);
left = reorder->right_align = (right_allocation.x + right_allocation.width + left_allocation.x) / 2;
}
else
@@ -9405,6 +9709,7 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
GtkAllocation button_allocation;
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
GdkDisplay *display = gdk_screen_get_display (screen);
+ GtkWidget *button;
g_return_if_fail (tree_view->priv->column_drag_info == NULL);
g_return_if_fail (tree_view->priv->cur_reorder == NULL);
@@ -9414,12 +9719,14 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
if (tree_view->priv->column_drag_info == NULL)
return;
+ button = gtk_tree_view_column_get_button (column);
+
if (tree_view->priv->drag_window == NULL)
{
GdkWindowAttr attributes;
guint attributes_mask;
- gtk_widget_get_allocation (column->button, &button_allocation);
+ gtk_widget_get_allocation (button, &button_allocation);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
@@ -9440,17 +9747,17 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
- gtk_grab_remove (column->button);
+ gtk_grab_remove (button);
send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
send_event->crossing.send_event = TRUE;
- send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
+ send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (button)));
send_event->crossing.subwindow = NULL;
send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
send_event->crossing.time = GDK_CURRENT_TIME;
gdk_event_set_device (send_event, device);
- gtk_propagate_event (column->button, send_event);
+ gtk_propagate_event (button, send_event);
gdk_event_free (send_event);
send_event = gdk_event_new (GDK_BUTTON_RELEASE);
@@ -9466,22 +9773,22 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
send_event->button.y_root = 0;
gdk_event_set_device (send_event, device);
- gtk_propagate_event (column->button, send_event);
+ gtk_propagate_event (button, send_event);
gdk_event_free (send_event);
/* Kids, don't try this at home */
- g_object_ref (column->button);
- gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
- gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
- gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
- g_object_unref (column->button);
+ g_object_ref (button);
+ gtk_container_remove (GTK_CONTAINER (tree_view), button);
+ gtk_widget_set_parent_window (button, tree_view->priv->drag_window);
+ gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
+ g_object_unref (button);
- gtk_widget_get_allocation (column->button, &button_allocation);
+ gtk_widget_get_allocation (button, &button_allocation);
tree_view->priv->drag_column_x = button_allocation.x;
allocation = button_allocation;
allocation.x = 0;
- gtk_widget_size_allocate (column->button, &allocation);
- gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
+ gtk_widget_size_allocate (button, &allocation);
+ gtk_widget_set_parent_window (button, tree_view->priv->drag_window);
tree_view->priv->drag_column = column;
gdk_window_show (tree_view->priv->drag_window);
@@ -9490,7 +9797,7 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
while (gtk_events_pending ())
gtk_main_iteration ();
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG);
+ tree_view->priv->in_column_drag = TRUE;
gdk_pointer_grab (tree_view->priv->drag_window,
FALSE,
GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
@@ -9515,8 +9822,8 @@ gtk_tree_view_queue_draw_arrow (GtkTreeView *tree_view,
rect.x = 0;
rect.width = MAX (tree_view->priv->expander_size, MAX (tree_view->priv->width, allocation.width));
- rect.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
- rect.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
+ rect.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
+ rect.height = gtk_tree_view_get_row_height (tree_view, node);
gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
}
@@ -9537,8 +9844,8 @@ _gtk_tree_view_queue_draw_node (GtkTreeView *tree_view,
rect.x = 0;
rect.width = MAX (tree_view->priv->width, allocation.width);
- rect.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
- rect.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
+ rect.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
+ rect.height = gtk_tree_view_get_row_height (tree_view, node);
if (clip_rect)
{
@@ -9554,6 +9861,75 @@ _gtk_tree_view_queue_draw_node (GtkTreeView *tree_view,
}
}
+static inline gint
+gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view)
+{
+ if (tree_view->priv->headers_visible)
+ return tree_view->priv->header_height;
+ /* else */
+ return 0;
+}
+
+gint
+_gtk_tree_view_get_header_height (GtkTreeView *tree_view)
+{
+ return tree_view->priv->header_height;
+}
+
+void
+_gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view,
+ GtkTreeViewRowSeparatorFunc *func,
+ gpointer *data)
+{
+ *func = tree_view->priv->row_separator_func;
+ *data = tree_view->priv->row_separator_data;
+}
+
+GtkTreePath *
+_gtk_tree_view_get_anchor_path (GtkTreeView *tree_view)
+{
+ if (tree_view->priv->anchor)
+ return gtk_tree_row_reference_get_path (tree_view->priv->anchor);
+
+ return NULL;
+}
+
+void
+_gtk_tree_view_set_anchor_path (GtkTreeView *tree_view,
+ GtkTreePath *anchor_path)
+{
+ if (tree_view->priv->anchor)
+ {
+ gtk_tree_row_reference_free (tree_view->priv->anchor);
+ tree_view->priv->anchor = NULL;
+ }
+
+ if (anchor_path && tree_view->priv->model)
+ tree_view->priv->anchor =
+ gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
+ tree_view->priv->model, anchor_path);
+}
+
+GtkRBTree *
+_gtk_tree_view_get_rbtree (GtkTreeView *tree_view)
+{
+ return tree_view->priv->tree;
+}
+
+GdkWindow *
+_gtk_tree_view_get_header_window (GtkTreeView *tree_view)
+{
+ return tree_view->priv->header_window;
+}
+
+void
+_gtk_tree_view_set_focus_column (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column)
+{
+ tree_view->priv->focus_column = column;
+}
+
+
static void
gtk_tree_view_queue_draw_path (GtkTreeView *tree_view,
GtkTreePath *path,
@@ -9601,9 +9977,11 @@ gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2);
area.x = x_offset;
- area.y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator);
+ area.y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node,
+ vertical_separator);
area.width = expander_size + 2;
- area.height = MAX (CELL_HEIGHT (node, vertical_separator), (expander_size - vertical_separator));
+ area.height = gtk_tree_view_get_cell_area_height (tree_view, node,
+ vertical_separator);
if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
{
@@ -9620,7 +9998,7 @@ gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
else
{
if (node == tree_view->priv->prelight_node &&
- GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+ tree_view->priv->arrow_prelit)
state = GTK_STATE_PRELIGHT;
else
state = GTK_STATE_NORMAL;
@@ -9698,7 +10076,7 @@ gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view)
if (cursor_path)
{
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ tree_view->priv->draw_keyfocus = TRUE;
gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
gtk_tree_path_free (cursor_path);
@@ -9710,7 +10088,22 @@ gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view)
{
if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
{
+ GtkCellArea *cell_area;
+
tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
+
+ /* This happens when the treeview initially grabs focus and there
+ * is no column in focus, here we explicitly focus into the first cell */
+ cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
+ if (!gtk_cell_area_get_focus_cell (cell_area))
+ {
+ gboolean rtl;
+
+ rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
+ gtk_cell_area_focus (cell_area,
+ rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT);
+ }
+
break;
}
}
@@ -9730,6 +10123,10 @@ gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
GtkTreePath *cursor_path = NULL;
gboolean grab_focus = TRUE;
gboolean selectable;
+ GtkDirectionType direction;
+ GtkCellArea *cell_area = NULL;
+ GtkCellRenderer *last_focus_cell = NULL;
+ GtkTreeIter iter;
if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
return;
@@ -9747,6 +10144,30 @@ gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
/* FIXME: we lost the cursor; should we get the first? */
return;
+ direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
+
+ if (tree_view->priv->focus_column)
+ cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
+
+ /* If focus stays in the area for this row, then just return for this round */
+ if (cell_area && (count == -1 || count == 1) &&
+ gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path))
+ {
+ gtk_tree_view_column_cell_set_cell_data (tree_view->priv->focus_column,
+ tree_view->priv->model,
+ &iter,
+ GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
+ cursor_node->children?TRUE:FALSE);
+
+ /* Save the last cell that had focus, if we hit the end of the view we'll give
+ * focus back to it. */
+ last_focus_cell = gtk_cell_area_get_focus_cell (cell_area);
+
+ /* If focus stays in the area, no need to change the cursor row */
+ if (gtk_cell_area_focus (cell_area, direction))
+ return;
+ }
+
selection_count = gtk_tree_selection_count_selected_rows (tree_view->priv->selection);
selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
cursor_node,
@@ -9818,6 +10239,10 @@ gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
cursor_path = _gtk_tree_view_find_path (tree_view, new_cursor_tree, new_cursor_node);
gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE);
gtk_tree_path_free (cursor_path);
+
+ /* Give focus to the area in the new row */
+ if (cell_area)
+ gtk_cell_area_focus (cell_area, direction);
}
else
{
@@ -9844,6 +10269,9 @@ gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
{
gtk_widget_error_bell (GTK_WIDGET (tree_view));
}
+
+ if (cell_area)
+ gtk_cell_area_set_focus_cell (cell_area, last_focus_cell);
}
if (grab_focus)
@@ -9905,11 +10333,12 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
return;
}
- if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (cursor_node))
+ if (tree_view->priv->cursor_offset
+ > gtk_tree_view_get_row_height (tree_view, cursor_node))
{
_gtk_rbtree_next_full (cursor_tree, cursor_node,
&cursor_tree, &cursor_node);
- tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (cursor_node);
+ tree_view->priv->cursor_offset -= gtk_tree_view_get_row_height (tree_view, cursor_node);
}
y -= tree_view->priv->cursor_offset;
@@ -9970,6 +10399,10 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
GList *list;
gboolean found_column = FALSE;
gboolean rtl;
+ GtkDirectionType direction;
+ GtkCellArea *cell_area;
+ GtkCellRenderer *last_focus_cell = NULL;
+ GtkCellArea *last_focus_area = NULL;
rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
@@ -9994,6 +10427,11 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
if (tree_view->priv->focus_column)
{
+ /* Save the cell/area we are moving focus from, if moving the cursor
+ * by one step hits the end we'll set focus back here */
+ last_focus_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
+ last_focus_cell = gtk_cell_area_get_focus_cell (last_focus_area);
+
for (; list; list = (rtl ? list->prev : list->next))
{
if (list->data == tree_view->priv->focus_column)
@@ -10001,10 +10439,10 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
}
}
+ direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT;
+
while (list)
{
- gboolean left, right;
-
column = list->data;
if (gtk_tree_view_column_get_visible (column) == FALSE)
goto loop_end;
@@ -10015,23 +10453,14 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
cursor_node->children?TRUE:FALSE);
- if (rtl)
- {
- right = list->prev ? TRUE : FALSE;
- left = list->next ? TRUE : FALSE;
- }
- else
- {
- left = list->prev ? TRUE : FALSE;
- right = list->next ? TRUE : FALSE;
- }
-
- if (_gtk_tree_view_column_cell_focus (column, count, left, right))
+ cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
+ if (gtk_cell_area_focus (cell_area, direction))
{
tree_view->priv->focus_column = column;
found_column = TRUE;
break;
}
+
loop_end:
if (count == 1)
list = rtl ? list->prev : list->next;
@@ -10041,7 +10470,7 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
if (found_column)
{
- if (!gtk_tree_view_has_special_cell (tree_view))
+ if (!gtk_tree_view_has_can_focus_cell (tree_view))
_gtk_tree_view_queue_draw_node (tree_view,
cursor_tree,
cursor_node,
@@ -10052,6 +10481,9 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
else
{
gtk_widget_error_bell (GTK_WIDGET (tree_view));
+
+ if (last_focus_area)
+ gtk_cell_area_set_focus_cell (last_focus_area, last_focus_cell);
}
gtk_tree_view_clamp_column_visible (tree_view,
@@ -10185,7 +10617,7 @@ gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
if (!tree_view->priv->shift_pressed && start_editing &&
tree_view->priv->focus_column)
{
- if (gtk_tree_view_start_editing (tree_view, cursor_path))
+ if (gtk_tree_view_start_editing (tree_view, cursor_path, FALSE))
{
gtk_tree_path_free (cursor_path);
return TRUE;
@@ -10541,12 +10973,14 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
for (list = tree_view->priv->columns; list; list = list->next)
{
GtkTreeViewColumn *column;
+ GtkWidget *button;
column = list->data;
if (!gtk_tree_view_column_get_visible (column))
continue;
- if (gtk_widget_has_focus (column->button))
+ button = gtk_tree_view_column_get_button (column);
+ if (gtk_widget_has_focus (button))
{
found_focus = TRUE;
break;
@@ -10627,14 +11061,14 @@ gtk_tree_view_new_column_width (GtkTreeView *tree_view,
*/
rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
column = g_list_nth (tree_view->priv->columns, i)->data;
- gtk_widget_get_allocation (column->button, &allocation);
+ gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
width = rtl ? (allocation.x + allocation.width - *x) : (*x - allocation.x);
/* Clamp down the value */
min_width = gtk_tree_view_column_get_min_width (column);
if (min_width == -1)
{
- gtk_widget_get_preferred_size (column->button, &button_req, NULL);
+ gtk_widget_get_preferred_size (gtk_tree_view_column_get_button (column), &button_req, NULL);
width = MAX (button_req.width, width);
}
else
@@ -10735,7 +11169,7 @@ gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
gdk_window_move (tree_view->priv->bin_window,
- tree_view->priv->hadjustment->value,
- TREE_VIEW_HEADER_HEIGHT (tree_view));
+ gtk_tree_view_get_effective_header_height (tree_view));
gdk_window_move (tree_view->priv->header_window,
- tree_view->priv->hadjustment->value,
0);
@@ -10746,25 +11180,29 @@ gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
tree_view->priv->event_last_x,
tree_view->priv->event_last_y - dy);
- if (tree_view->priv->edited_column &&
- GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
+ if (tree_view->priv->edited_column)
{
GList *list;
- GtkWidget *widget;
GtkTreeViewChild *child = NULL;
+ GtkCellEditable *edit_widget;
+ GtkCellArea *area;
- widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
- adjust_allocation (widget, 0, dy);
-
- for (list = tree_view->priv->children; list; list = list->next)
- {
- child = (GtkTreeViewChild *)list->data;
- if (child->widget == widget)
- {
- child->y += dy;
- break;
- }
- }
+ area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column));
+ edit_widget = gtk_cell_area_get_edit_widget (area);
+ if (GTK_IS_WIDGET (edit_widget))
+ {
+ adjust_allocation (GTK_WIDGET (edit_widget), 0, dy);
+
+ for (list = tree_view->priv->children; list; list = list->next)
+ {
+ child = (GtkTreeViewChild *)list->data;
+ if (child->widget == GTK_WIDGET (edit_widget))
+ {
+ child->y += dy;
+ break;
+ }
+ }
+ }
}
}
gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
@@ -10962,9 +11400,9 @@ gtk_tree_view_set_model (GtkTreeView *tree_view,
flags = gtk_tree_model_get_flags (tree_view->priv->model);
if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
+ tree_view->priv->is_list = TRUE;
else
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
+ tree_view->priv->is_list = FALSE;
path = gtk_tree_path_new_first ();
if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path))
@@ -11143,7 +11581,7 @@ gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
- return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+ return tree_view->priv->headers_visible;
}
/**
@@ -11161,18 +11599,16 @@ gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
GList *list;
GtkTreeViewColumn *column;
GtkAllocation allocation;
+ GtkWidget *button;
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
headers_visible = !! headers_visible;
- if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) == headers_visible)
+ if (tree_view->priv->headers_visible == headers_visible)
return;
- if (headers_visible)
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
- else
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+ tree_view->priv->headers_visible = headers_visible == TRUE;
if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
{
@@ -11181,8 +11617,8 @@ gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
{
gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
gdk_window_move_resize (tree_view->priv->bin_window,
- x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
- tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
+ x, y + gtk_tree_view_get_effective_header_height (tree_view),
+ tree_view->priv->width, allocation.height - + gtk_tree_view_get_effective_header_height (tree_view));
if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
gtk_tree_view_map_buttons (tree_view);
@@ -11194,15 +11630,16 @@ gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
for (list = tree_view->priv->columns; list; list = list->next)
{
column = list->data;
- gtk_widget_unmap (column->button);
+ button = gtk_tree_view_column_get_button (column);
+ gtk_widget_unmap (button);
}
gdk_window_hide (tree_view->priv->header_window);
}
}
gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
- tree_view->priv->vadjustment->page_size = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
- tree_view->priv->vadjustment->page_increment = (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
+ tree_view->priv->vadjustment->page_size = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
+ tree_view->priv->vadjustment->page_increment = (allocation.height - gtk_tree_view_get_effective_header_height (tree_view)) / 2;
tree_view->priv->vadjustment->lower = 0;
tree_view->priv->vadjustment->upper = tree_view->priv->height;
gtk_adjustment_changed (tree_view->priv->vadjustment);
@@ -11359,7 +11796,7 @@ gtk_tree_view_append_column (GtkTreeView *tree_view,
{
g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
- g_return_val_if_fail (column->tree_view == NULL, -1);
+ g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
return gtk_tree_view_insert_column (tree_view, column, -1);
}
@@ -11380,7 +11817,7 @@ gtk_tree_view_remove_column (GtkTreeView *tree_view,
{
g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
- g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
+ g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view), -1);
if (tree_view->priv->focus_column == column)
tree_view->priv->focus_column = NULL;
@@ -11452,7 +11889,7 @@ gtk_tree_view_insert_column (GtkTreeView *tree_view,
{
g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
- g_return_val_if_fail (column->tree_view == NULL, -1);
+ g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
if (tree_view->priv->fixed_height_mode)
g_return_val_if_fail (gtk_tree_view_column_get_sizing (column)
@@ -12481,7 +12918,7 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view,
}
g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path);
-
+
if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
{
/* now that we've collapsed all rows, we want to try to set the prelight
@@ -12890,7 +13327,8 @@ gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view,
/* cancel the current editing, if it exists */
if (tree_view->priv->edited_column &&
- tree_view->priv->edited_column->editable_widget)
+ gtk_cell_area_get_edit_widget
+ (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column))))
gtk_tree_view_stop_editing (tree_view, TRUE);
gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
@@ -12912,7 +13350,7 @@ gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view,
if (focus_cell)
gtk_tree_view_column_focus_cell (focus_column, focus_cell);
if (start_editing)
- gtk_tree_view_start_editing (tree_view, path);
+ gtk_tree_view_start_editing (tree_view, path, TRUE);
}
}
@@ -13071,6 +13509,42 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view,
}
+static inline gint
+gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view,
+ GtkRBNode *node,
+ gint vertical_separator)
+{
+ int height;
+
+ /* The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
+ * i.e. just the cells, no spacing.
+ *
+ * The cell area height is at least expander_size - vertical_separator.
+ * For regular nodes, the height is then at least expander_size. We should
+ * be able to enforce the expander_size minimum here, because this
+ * function will not be called for irregular (e.g. separator) rows.
+ */
+ height = gtk_tree_view_get_row_height (tree_view, node);
+ if (height < tree_view->priv->expander_size)
+ height = tree_view->priv->expander_size;
+
+ return height - vertical_separator;
+}
+
+static inline gint
+gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node,
+ gint vertical_separator)
+{
+ int offset;
+
+ offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
+ offset += vertical_separator / 2;
+
+ return offset;
+}
+
/**
* gtk_tree_view_get_cell_area:
* @tree_view: a #GtkTreeView
@@ -13103,7 +13577,7 @@ gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
g_return_if_fail (rect != NULL);
- g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
+ g_return_if_fail (!column || gtk_tree_view_column_get_tree_view (column) == (GtkWidget *) tree_view);
g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
gtk_widget_style_get (GTK_WIDGET (tree_view),
@@ -13118,7 +13592,7 @@ gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
if (column)
{
- gtk_widget_get_allocation (column->button, &allocation);
+ gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
rect->x = allocation.x + horizontal_separator/2;
rect->width = allocation.width - horizontal_separator;
}
@@ -13131,8 +13605,21 @@ gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
if ((!ret && tree == NULL) || ret)
return;
- rect->y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator);
- rect->height = MAX (CELL_HEIGHT (node, vertical_separator), tree_view->priv->expander_size - vertical_separator);
+ if (row_is_separator (tree_view, NULL, path))
+ {
+ /* There isn't really a "cell area" for separator, so we
+ * return the y, height values for background area instead.
+ */
+ rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
+ rect->height = gtk_tree_view_get_row_height (tree_view, node);
+ }
+ else
+ {
+ rect->y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node,
+ vertical_separator);
+ rect->height = gtk_tree_view_get_cell_area_height (tree_view, node,
+ vertical_separator);
+ }
if (column &&
gtk_tree_view_is_expander_column (tree_view, column))
@@ -13146,7 +13633,7 @@ gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
rect->x += (depth - 1) * tree_view->priv->level_indentation;
rect->width -= (depth - 1) * tree_view->priv->level_indentation;
- if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ if (gtk_tree_view_draw_expanders (tree_view))
{
if (!rtl)
rect->x += depth * tree_view->priv->expander_size;
@@ -13158,6 +13645,39 @@ gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
}
}
+static inline gint
+gtk_tree_view_get_row_height (GtkTreeView *tree_view,
+ GtkRBNode *node)
+{
+ int height;
+
+ /* The "background" areas of all rows/cells add up to cover the entire tree.
+ * The background includes all inter-row and inter-cell spacing.
+ *
+ * If the row pointed at by node does not have a height set, we default
+ * to expander_size, which is the minimum height for regular nodes.
+ * Non-regular nodes (e.g. separators) can have a height set smaller
+ * than expander_size and should not be overruled here.
+ */
+ height = GTK_RBNODE_GET_HEIGHT (node);
+ if (height <= 0)
+ height = tree_view->priv->expander_size;
+
+ return height;
+}
+
+static inline gint
+gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ int offset;
+
+ offset = _gtk_rbtree_node_find_offset (tree, node);
+
+ return RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, offset);
+}
+
/**
* gtk_tree_view_get_background_area:
* @tree_view: a #GtkTreeView
@@ -13202,9 +13722,8 @@ gtk_tree_view_get_background_area (GtkTreeView *tree_view,
tree == NULL)
return;
- rect->y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
-
- rect->height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
+ rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
+ rect->height = gtk_tree_view_get_row_height (tree_view, node);
}
if (column)
@@ -13244,7 +13763,7 @@ gtk_tree_view_get_visible_rect (GtkTreeView *tree_view,
visible_rect->x = tree_view->priv->hadjustment->value;
visible_rect->y = tree_view->priv->vadjustment->value;
visible_rect->width = allocation.width;
- visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
+ visible_rect->height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
}
}
@@ -13337,7 +13856,7 @@ gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view,
if (bx)
*bx = wx + tree_view->priv->hadjustment->value;
if (by)
- *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
+ *by = wy - gtk_tree_view_get_effective_header_height (tree_view);
}
/**
@@ -13365,7 +13884,7 @@ gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view,
if (wx)
*wx = bx - tree_view->priv->hadjustment->value;
if (wy)
- *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
+ *wy = by + gtk_tree_view_get_effective_header_height (tree_view);
}
/**
@@ -13907,7 +14426,7 @@ gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view,
cell_offset = x;
background_area.y = y;
- background_area.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
+ background_area.height = gtk_tree_view_get_row_height (tree_view, node);
bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
@@ -13955,7 +14474,7 @@ gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view,
cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
- if (TREE_VIEW_DRAW_EXPANDERS(tree_view))
+ if (gtk_tree_view_draw_expanders (tree_view))
{
if (!rtl)
cell_area.x += depth * tree_view->priv->expander_size;
@@ -13979,7 +14498,7 @@ gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view,
cr,
&background_area,
&cell_area,
- 0);
+ 0, FALSE);
}
cell_offset += gtk_tree_view_column_get_width (column);
}
@@ -14855,25 +15374,23 @@ gtk_tree_view_search_init (GtkWidget *entry,
tree_view->priv->selected_iter = 1;
}
-static void
-gtk_tree_view_remove_widget (GtkCellEditable *cell_editable,
- GtkTreeView *tree_view)
+void
+_gtk_tree_view_remove_editable (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column,
+ GtkCellEditable *cell_editable)
{
if (tree_view->priv->edited_column == NULL)
return;
- _gtk_tree_view_column_stop_editing (tree_view->priv->edited_column);
+ g_return_if_fail (column == tree_view->priv->edited_column);
+
tree_view->priv->edited_column = NULL;
if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
gtk_widget_grab_focus (GTK_WIDGET (tree_view));
- g_signal_handlers_disconnect_by_func (cell_editable,
- gtk_tree_view_remove_widget,
- tree_view);
-
gtk_container_remove (GTK_CONTAINER (tree_view),
- GTK_WIDGET (cell_editable));
+ GTK_WIDGET (cell_editable));
/* FIXME should only redraw a single node */
gtk_widget_queue_draw (GTK_WIDGET (tree_view));
@@ -14881,12 +15398,12 @@ gtk_tree_view_remove_widget (GtkCellEditable *cell_editable,
static gboolean
gtk_tree_view_start_editing (GtkTreeView *tree_view,
- GtkTreePath *cursor_path)
+ GtkTreePath *cursor_path,
+ gboolean edit_only)
{
GtkTreeIter iter;
- GdkRectangle background_area;
GdkRectangle cell_area;
- GtkCellEditable *editable_widget = NULL;
+ GtkTreeViewColumn *focus_column;
gchar *path_string;
guint flags = 0; /* can be 0, as the flags are primarily for rendering */
gint retval = FALSE;
@@ -14894,6 +15411,7 @@ gtk_tree_view_start_editing (GtkTreeView *tree_view,
GtkRBNode *cursor_node;
g_assert (tree_view->priv->focus_column);
+ focus_column = tree_view->priv->focus_column;
if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
return FALSE;
@@ -14907,71 +15425,37 @@ gtk_tree_view_start_editing (GtkTreeView *tree_view,
validate_row (tree_view, cursor_tree, cursor_node, &iter, cursor_path);
- gtk_tree_view_column_cell_set_cell_data (tree_view->priv->focus_column,
+ gtk_tree_view_column_cell_set_cell_data (focus_column,
tree_view->priv->model,
&iter,
GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
cursor_node->children?TRUE:FALSE);
- gtk_tree_view_get_background_area (tree_view,
- cursor_path,
- tree_view->priv->focus_column,
- &background_area);
gtk_tree_view_get_cell_area (tree_view,
cursor_path,
- tree_view->priv->focus_column,
+ focus_column,
&cell_area);
- if (_gtk_tree_view_column_cell_event (tree_view->priv->focus_column,
- &editable_widget,
- NULL,
- path_string,
- &background_area,
- &cell_area,
- flags))
- {
- retval = TRUE;
- if (editable_widget != NULL)
- {
- gint left, right;
- GdkRectangle area;
- GtkCellRenderer *cell;
-
- area = cell_area;
- cell = _gtk_tree_view_column_get_edited_cell (tree_view->priv->focus_column);
+ if (gtk_cell_area_activate (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (focus_column)),
+ _gtk_tree_view_column_get_context (focus_column),
+ GTK_WIDGET (tree_view),
+ &cell_area,
+ flags, edit_only))
+ retval = TRUE;
- _gtk_tree_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
-
- area.x += left;
- area.width -= right + left;
-
- gtk_tree_view_real_start_editing (tree_view,
- tree_view->priv->focus_column,
- cursor_path,
- editable_widget,
- &area,
- NULL,
- flags);
- }
-
- }
- g_free (path_string);
return retval;
}
-static void
-gtk_tree_view_real_start_editing (GtkTreeView *tree_view,
- GtkTreeViewColumn *column,
- GtkTreePath *path,
- GtkCellEditable *cell_editable,
- GdkRectangle *cell_area,
- GdkEvent *event,
- guint flags)
+void
+_gtk_tree_view_add_editable (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column,
+ GtkTreePath *path,
+ GtkCellEditable *cell_editable,
+ GdkRectangle *cell_area)
{
gint pre_val = tree_view->priv->vadjustment->value;
GtkRequisition requisition;
tree_view->priv->edited_column = column;
- _gtk_tree_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
@@ -14979,7 +15463,7 @@ gtk_tree_view_real_start_editing (GtkTreeView *tree_view,
gtk_widget_get_preferred_size (GTK_WIDGET (cell_editable),
&requisition, NULL);
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ tree_view->priv->draw_keyfocus = TRUE;
if (requisition.height < cell_area->height)
{
@@ -14996,13 +15480,6 @@ gtk_tree_view_real_start_editing (GtkTreeView *tree_view,
cell_area->x, cell_area->y,
cell_area->width, cell_area->height);
}
-
- gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
- (GdkEvent *)event);
-
- gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
- g_signal_connect (cell_editable, "remove-widget",
- G_CALLBACK (gtk_tree_view_remove_widget), tree_view);
}
static void
@@ -15010,7 +15487,6 @@ gtk_tree_view_stop_editing (GtkTreeView *tree_view,
gboolean cancel_editing)
{
GtkTreeViewColumn *column;
- GtkCellRenderer *cell;
if (tree_view->priv->edited_column == NULL)
return;
@@ -15026,17 +15502,8 @@ gtk_tree_view_stop_editing (GtkTreeView *tree_view,
*/
column = tree_view->priv->edited_column;
+ gtk_cell_area_stop_editing (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)), cancel_editing);
tree_view->priv->edited_column = NULL;
-
- cell = _gtk_tree_view_column_get_edited_cell (column);
- gtk_cell_renderer_stop_editing (cell, cancel_editing);
-
- if (!cancel_editing)
- gtk_cell_editable_editing_done (column->editable_widget);
-
- tree_view->priv->edited_column = column;
-
- gtk_cell_editable_remove_widget (column->editable_widget);
}
@@ -15473,12 +15940,9 @@ gtk_tree_view_set_show_expanders (GtkTreeView *tree_view,
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
enabled = enabled != FALSE;
- was_enabled = GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
+ was_enabled = tree_view->priv->show_expanders;
- if (enabled)
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
- else
- GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
+ tree_view->priv->show_expanders = enabled == TRUE;
if (enabled != was_enabled)
gtk_widget_queue_draw (GTK_WIDGET (tree_view));
@@ -15500,7 +15964,7 @@ gtk_tree_view_get_show_expanders (GtkTreeView *tree_view)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
- return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
+ return tree_view->priv->show_expanders;
}
/**
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
index 19d17c8901..905a7bb275 100644
--- a/gtk/gtktreeviewcolumn.c
+++ b/gtk/gtktreeviewcolumn.c
@@ -32,6 +32,8 @@
#include "gtkhbox.h"
#include "gtkmarshalers.h"
#include "gtkarrow.h"
+#include "gtkcellareacontext.h"
+#include "gtkcellareabox.h"
#include "gtkprivate.h"
#include "gtkintl.h"
@@ -54,50 +56,6 @@
*/
-enum
-{
- PROP_0,
- PROP_VISIBLE,
- PROP_RESIZABLE,
- PROP_WIDTH,
- PROP_SPACING,
- PROP_SIZING,
- PROP_FIXED_WIDTH,
- PROP_MIN_WIDTH,
- PROP_MAX_WIDTH,
- PROP_TITLE,
- PROP_EXPAND,
- PROP_CLICKABLE,
- PROP_WIDGET,
- PROP_ALIGNMENT,
- PROP_REORDERABLE,
- PROP_SORT_INDICATOR,
- PROP_SORT_ORDER,
- PROP_SORT_COLUMN_ID
-};
-
-enum
-{
- CLICKED,
- LAST_SIGNAL
-};
-
-typedef struct _GtkTreeViewColumnCellInfo GtkTreeViewColumnCellInfo;
-struct _GtkTreeViewColumnCellInfo
-{
- GtkCellRenderer *cell;
- GSList *attributes;
- GtkTreeCellDataFunc func;
- gpointer func_data;
- GDestroyNotify destroy;
- gint requested_width;
- gint real_width;
- guint expand : 1;
- guint pack : 1;
- guint has_focus : 1;
- guint in_editing_mode : 1;
-};
-
/* Type methods */
static void gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface);
@@ -111,30 +69,13 @@ static void gtk_tree_view_column_get_property (GObject
GValue *value,
GParamSpec *pspec);
static void gtk_tree_view_column_finalize (GObject *object);
+static void gtk_tree_view_column_dispose (GObject *object);
+static GObject *gtk_tree_view_column_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties);
/* GtkCellLayout implementation */
-static void gtk_tree_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand);
-static void gtk_tree_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand);
-static void gtk_tree_view_column_cell_layout_clear (GtkCellLayout *cell_layout);
-static void gtk_tree_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- const gchar *attribute,
- gint column);
-static void gtk_tree_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- GtkCellLayoutDataFunc func,
- gpointer func_data,
- GDestroyNotify destroy);
-static void gtk_tree_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell);
-static void gtk_tree_view_column_cell_layout_reorder (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gint position);
-static GList *gtk_tree_view_column_cell_layout_get_cells (GtkCellLayout *cell_layout);
+static GtkCellArea *gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout);
/* Button handling code */
static void gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column);
@@ -154,6 +95,21 @@ static gboolean gtk_tree_view_column_mnemonic_activate (GtkWidget *widge
static void gtk_tree_view_model_sort_column_changed (GtkTreeSortable *sortable,
GtkTreeViewColumn *tree_column);
+/* GtkCellArea/GtkCellAreaContext callbacks */
+static void gtk_tree_view_column_context_changed (GtkCellAreaContext *context,
+ GParamSpec *pspec,
+ GtkTreeViewColumn *tree_column);
+static void gtk_tree_view_column_add_editable_callback (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ GdkRectangle *cell_area,
+ const gchar *path_string,
+ gpointer user_data);
+static void gtk_tree_view_column_remove_editable_callback (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ gpointer user_data);
+
/* Internal functions */
static void gtk_tree_view_column_sort (GtkTreeViewColumn *tree_column,
gpointer data);
@@ -161,21 +117,92 @@ static void gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColum
static void gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell_renderer,
va_list args);
-static GtkTreeViewColumnCellInfo *gtk_tree_view_column_get_cell_info (GtkTreeViewColumn *tree_column,
- GtkCellRenderer *cell_renderer);
-
-/* cell list manipulation */
-static GList *gtk_tree_view_column_cell_first (GtkTreeViewColumn *tree_column);
-static GList *gtk_tree_view_column_cell_last (GtkTreeViewColumn *tree_column);
-static GList *gtk_tree_view_column_cell_next (GtkTreeViewColumn *tree_column,
- GList *current);
-static GList *gtk_tree_view_column_cell_prev (GtkTreeViewColumn *tree_column,
- GList *current);
-static void gtk_tree_view_column_clear_attributes_by_info (GtkTreeViewColumn *tree_column,
- GtkTreeViewColumnCellInfo *info);
+
/* GtkBuildable implementation */
static void gtk_tree_view_column_buildable_init (GtkBuildableIface *iface);
+
+struct _GtkTreeViewColumnPrivate
+{
+ GtkWidget *tree_view;
+ GtkWidget *button;
+ GtkWidget *child;
+ GtkWidget *arrow;
+ GtkWidget *alignment;
+ GdkWindow *window;
+ gfloat xalign;
+ gulong property_changed_signal;
+
+ /* Sizing fields */
+ /* see gtk+/doc/tree-column-sizing.txt for more information on them */
+ GtkTreeViewColumnSizing column_type;
+ gint padding;
+ gint resized_width;
+ gint width;
+ gint fixed_width;
+ gint min_width;
+ gint max_width;
+
+ /* dragging columns */
+ gint drag_x;
+ gint drag_y;
+
+ gchar *title;
+
+ /* Sorting */
+ gulong sort_clicked_signal;
+ gulong sort_column_changed_signal;
+ gint sort_column_id;
+ GtkSortType sort_order;
+
+ /* Cell area */
+ GtkCellArea *cell_area;
+ GtkCellAreaContext *cell_area_context;
+ gulong add_editable_signal;
+ gulong remove_editable_signal;
+ gulong context_changed_signal;
+
+ /* Flags */
+ guint visible : 1;
+ guint resizable : 1;
+ guint clickable : 1;
+ guint dirty : 1;
+ guint show_sort_indicator : 1;
+ guint maybe_reordered : 1;
+ guint reorderable : 1;
+ guint use_resized_width : 1;
+ guint expand : 1;
+};
+
+enum
+{
+ PROP_0,
+ PROP_VISIBLE,
+ PROP_RESIZABLE,
+ PROP_WIDTH,
+ PROP_SPACING,
+ PROP_SIZING,
+ PROP_FIXED_WIDTH,
+ PROP_MIN_WIDTH,
+ PROP_MAX_WIDTH,
+ PROP_TITLE,
+ PROP_EXPAND,
+ PROP_CLICKABLE,
+ PROP_WIDGET,
+ PROP_ALIGNMENT,
+ PROP_REORDERABLE,
+ PROP_SORT_INDICATOR,
+ PROP_SORT_ORDER,
+ PROP_SORT_COLUMN_ID,
+ PROP_CELL_AREA
+};
+
+enum
+{
+ CLICKED,
+ LAST_SIGNAL
+};
+
static guint tree_column_signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, G_TYPE_INITIALLY_UNOWNED,
@@ -194,7 +221,9 @@ gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
class->clicked = NULL;
+ object_class->constructor = gtk_tree_view_column_constructor;
object_class->finalize = gtk_tree_view_column_finalize;
+ object_class->dispose = gtk_tree_view_column_dispose;
object_class->set_property = gtk_tree_view_column_set_property;
object_class->get_property = gtk_tree_view_column_get_property;
@@ -365,6 +394,34 @@ gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
G_MAXINT,
-1,
GTK_PARAM_READWRITE));
+
+ /**
+ * GtkTreeViewColumn:cell-area:
+ *
+ * The #GtkCellArea used to layout cell renderers for this column.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_CELL_AREA,
+ g_param_spec_object ("cell-area",
+ P_("Cell Area"),
+ P_("The GtkCellArea used to layout cells"),
+ GTK_TYPE_CELL_AREA,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (class, sizeof (GtkTreeViewColumnPrivate));
+}
+
+static void
+gtk_tree_view_column_custom_tag_end (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer *data)
+{
+ /* Just ignore the boolean return from here */
+ _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data);
}
static void
@@ -372,79 +429,138 @@ gtk_tree_view_column_buildable_init (GtkBuildableIface *iface)
{
iface->add_child = _gtk_cell_layout_buildable_add_child;
iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
- iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end;
+ iface->custom_tag_end = gtk_tree_view_column_custom_tag_end;
}
static void
gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface)
{
- iface->pack_start = gtk_tree_view_column_cell_layout_pack_start;
- iface->pack_end = gtk_tree_view_column_cell_layout_pack_end;
- iface->clear = gtk_tree_view_column_cell_layout_clear;
- iface->add_attribute = gtk_tree_view_column_cell_layout_add_attribute;
- iface->set_cell_data_func = gtk_tree_view_column_cell_layout_set_cell_data_func;
- iface->clear_attributes = gtk_tree_view_column_cell_layout_clear_attributes;
- iface->reorder = gtk_tree_view_column_cell_layout_reorder;
- iface->get_cells = gtk_tree_view_column_cell_layout_get_cells;
+ iface->get_area = gtk_tree_view_column_cell_layout_get_area;
}
static void
gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
{
- tree_column->button = NULL;
- tree_column->xalign = 0.0;
- tree_column->width = 0;
- tree_column->spacing = 0;
- tree_column->requested_width = -1;
- tree_column->min_width = -1;
- tree_column->max_width = -1;
- tree_column->resized_width = 0;
- tree_column->column_type = GTK_TREE_VIEW_COLUMN_GROW_ONLY;
- tree_column->visible = TRUE;
- tree_column->resizable = FALSE;
- tree_column->expand = FALSE;
- tree_column->clickable = FALSE;
- tree_column->dirty = TRUE;
- tree_column->sort_order = GTK_SORT_ASCENDING;
- tree_column->show_sort_indicator = FALSE;
- tree_column->property_changed_signal = 0;
- tree_column->sort_clicked_signal = 0;
- tree_column->sort_column_changed_signal = 0;
- tree_column->sort_column_id = -1;
- tree_column->reorderable = FALSE;
- tree_column->maybe_reordered = FALSE;
- tree_column->fixed_width = 1;
- tree_column->use_resized_width = FALSE;
- tree_column->title = g_strdup ("");
+ GtkTreeViewColumnPrivate *priv;
+
+ tree_column->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_column,
+ GTK_TYPE_TREE_VIEW_COLUMN,
+ GtkTreeViewColumnPrivate);
+ priv = tree_column->priv;
+
+ priv->button = NULL;
+ priv->xalign = 0.0;
+ priv->width = 0;
+ priv->padding = -1;
+ priv->min_width = -1;
+ priv->max_width = -1;
+ priv->resized_width = 0;
+ priv->column_type = GTK_TREE_VIEW_COLUMN_GROW_ONLY;
+ priv->visible = TRUE;
+ priv->resizable = FALSE;
+ priv->expand = FALSE;
+ priv->clickable = FALSE;
+ priv->dirty = TRUE;
+ priv->sort_order = GTK_SORT_ASCENDING;
+ priv->show_sort_indicator = FALSE;
+ priv->property_changed_signal = 0;
+ priv->sort_clicked_signal = 0;
+ priv->sort_column_changed_signal = 0;
+ priv->sort_column_id = -1;
+ priv->reorderable = FALSE;
+ priv->maybe_reordered = FALSE;
+ priv->fixed_width = 1;
+ priv->use_resized_width = FALSE;
+ priv->title = g_strdup ("");
}
-static void
-gtk_tree_view_column_finalize (GObject *object)
+static GObject *
+gtk_tree_view_column_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
{
- GtkTreeViewColumn *tree_column = (GtkTreeViewColumn *) object;
- GList *list;
+ GtkTreeViewColumn *tree_column;
+ GtkTreeViewColumnPrivate *priv;
+ GObject *object;
+
+ object = G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->constructor
+ (type, n_construct_properties, construct_properties);
+
+ tree_column = (GtkTreeViewColumn *) object;
+ priv = tree_column->priv;
- for (list = tree_column->cell_list; list; list = list->next)
+ if (!priv->cell_area)
{
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+ priv->cell_area = gtk_cell_area_box_new ();
+ g_object_ref_sink (priv->cell_area);
+ }
- if (info->destroy)
- {
- GDestroyNotify d = info->destroy;
+ gtk_cell_area_set_style_detail (priv->cell_area, "treeview");
- info->destroy = NULL;
- d (info->func_data);
- }
- gtk_tree_view_column_clear_attributes_by_info (tree_column, info);
- g_object_unref (info->cell);
- g_free (info);
+ priv->add_editable_signal =
+ g_signal_connect (priv->cell_area, "add-editable",
+ G_CALLBACK (gtk_tree_view_column_add_editable_callback),
+ tree_column);
+ priv->remove_editable_signal =
+ g_signal_connect (priv->cell_area, "remove-editable",
+ G_CALLBACK (gtk_tree_view_column_remove_editable_callback),
+ tree_column);
+
+ priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area);
+
+ priv->context_changed_signal =
+ g_signal_connect (priv->cell_area_context, "notify",
+ G_CALLBACK (gtk_tree_view_column_context_changed), tree_column);
+
+ return object;
+}
+
+static void
+gtk_tree_view_column_dispose (GObject *object)
+{
+ GtkTreeViewColumn *tree_column = (GtkTreeViewColumn *) object;
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
+
+ if (priv->cell_area_context)
+ {
+ g_signal_handler_disconnect (priv->cell_area_context,
+ priv->context_changed_signal);
+
+ g_object_unref (priv->cell_area_context);
+
+ priv->cell_area_context = NULL;
+ priv->context_changed_signal = 0;
+ }
+
+ if (priv->cell_area)
+ {
+ g_signal_handler_disconnect (priv->cell_area,
+ priv->add_editable_signal);
+ g_signal_handler_disconnect (priv->cell_area,
+ priv->remove_editable_signal);
+
+ g_object_unref (priv->cell_area);
+ priv->cell_area = NULL;
+ priv->add_editable_signal = 0;
+ priv->remove_editable_signal = 0;
+ }
+
+ if (priv->child)
+ {
+ g_object_unref (priv->child);
+ priv->child = NULL;
}
- g_free (tree_column->title);
- g_list_free (tree_column->cell_list);
+ G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->dispose (object);
+}
+
+static void
+gtk_tree_view_column_finalize (GObject *object)
+{
+ GtkTreeViewColumn *tree_column = (GtkTreeViewColumn *) object;
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
- if (tree_column->child)
- g_object_unref (tree_column->child);
+ g_free (priv->title);
G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->finalize (object);
}
@@ -456,6 +572,7 @@ gtk_tree_view_column_set_property (GObject *object,
GParamSpec *pspec)
{
GtkTreeViewColumn *tree_column;
+ GtkCellArea *area;
tree_column = GTK_TREE_VIEW_COLUMN (object);
@@ -540,6 +657,14 @@ gtk_tree_view_column_set_property (GObject *object,
gtk_tree_view_column_set_sort_column_id (tree_column,
g_value_get_int (value));
break;
+
+ case PROP_CELL_AREA:
+ /* Construct-only, can only be assigned once */
+ area = g_value_get_object (value);
+
+ if (area)
+ tree_column->priv->cell_area = g_object_ref_sink (area);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -643,6 +768,10 @@ gtk_tree_view_column_get_property (GObject *object,
g_value_set_int (value,
gtk_tree_view_column_get_sort_column_id (tree_column));
break;
+
+ case PROP_CELL_AREA:
+ g_value_set_object (value, tree_column->priv->cell_area);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -652,236 +781,58 @@ gtk_tree_view_column_get_property (GObject *object,
/* Implementation of GtkCellLayout interface
*/
-
-static void
-gtk_tree_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand)
-{
- GtkTreeViewColumn *column;
- GtkTreeViewColumnCellInfo *cell_info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
- column = GTK_TREE_VIEW_COLUMN (cell_layout);
- g_return_if_fail (! gtk_tree_view_column_get_cell_info (column, cell));
-
- g_object_ref_sink (cell);
-
- cell_info = g_new0 (GtkTreeViewColumnCellInfo, 1);
- cell_info->cell = cell;
- cell_info->expand = expand ? TRUE : FALSE;
- cell_info->pack = GTK_PACK_START;
- cell_info->has_focus = 0;
- cell_info->attributes = NULL;
-
- column->cell_list = g_list_append (column->cell_list, cell_info);
-}
-
-static void
-gtk_tree_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gboolean expand)
-{
- GtkTreeViewColumn *column;
- GtkTreeViewColumnCellInfo *cell_info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
- column = GTK_TREE_VIEW_COLUMN (cell_layout);
- g_return_if_fail (! gtk_tree_view_column_get_cell_info (column, cell));
-
- g_object_ref_sink (cell);
-
- cell_info = g_new0 (GtkTreeViewColumnCellInfo, 1);
- cell_info->cell = cell;
- cell_info->expand = expand ? TRUE : FALSE;
- cell_info->pack = GTK_PACK_END;
- cell_info->has_focus = 0;
- cell_info->attributes = NULL;
-
- column->cell_list = g_list_append (column->cell_list, cell_info);
-}
-
-static void
-gtk_tree_view_column_cell_layout_clear (GtkCellLayout *cell_layout)
-{
- GtkTreeViewColumn *column;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
- column = GTK_TREE_VIEW_COLUMN (cell_layout);
-
- while (column->cell_list)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *)column->cell_list->data;
-
- gtk_tree_view_column_cell_layout_clear_attributes (cell_layout, info->cell);
- g_object_unref (info->cell);
- g_free (info);
- column->cell_list = g_list_delete_link (column->cell_list,
- column->cell_list);
- }
-}
-
-static void
-gtk_tree_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- const gchar *attribute,
- gint column)
-{
- GtkTreeViewColumn *tree_column;
- GtkTreeViewColumnCellInfo *info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
- tree_column = GTK_TREE_VIEW_COLUMN (cell_layout);
-
- info = gtk_tree_view_column_get_cell_info (tree_column, cell);
- g_return_if_fail (info != NULL);
-
- info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
- info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
-
- if (tree_column->tree_view)
- _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
-}
-
-static void
-gtk_tree_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- GtkCellLayoutDataFunc func,
- gpointer func_data,
- GDestroyNotify destroy)
+static GtkCellArea *
+gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout)
{
- GtkTreeViewColumn *column;
- GtkTreeViewColumnCellInfo *info;
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (cell_layout);
+ GtkTreeViewColumnPrivate *priv = column->priv;
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
- column = GTK_TREE_VIEW_COLUMN (cell_layout);
-
- info = gtk_tree_view_column_get_cell_info (column, cell);
- g_return_if_fail (info != NULL);
-
- if (info->destroy)
- {
- GDestroyNotify d = info->destroy;
-
- info->destroy = NULL;
- d (info->func_data);
- }
-
- info->func = (GtkTreeCellDataFunc)func;
- info->func_data = func_data;
- info->destroy = destroy;
-
- if (column->tree_view)
- _gtk_tree_view_column_cell_set_dirty (column, TRUE);
+ return priv->cell_area;
}
-static void
-gtk_tree_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell_renderer)
-{
- GtkTreeViewColumn *column;
- GtkTreeViewColumnCellInfo *info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
- column = GTK_TREE_VIEW_COLUMN (cell_layout);
-
- info = gtk_tree_view_column_get_cell_info (column, cell_renderer);
- if (info)
- gtk_tree_view_column_clear_attributes_by_info (column, info);
-}
-
-static void
-gtk_tree_view_column_cell_layout_reorder (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- gint position)
-{
- GList *link;
- GtkTreeViewColumn *column;
- GtkTreeViewColumnCellInfo *info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
- column = GTK_TREE_VIEW_COLUMN (cell_layout);
-
- info = gtk_tree_view_column_get_cell_info (column, cell);
-
- g_return_if_fail (info != NULL);
- g_return_if_fail (position >= 0);
-
- link = g_list_find (column->cell_list, info);
-
- g_return_if_fail (link != NULL);
-
- column->cell_list = g_list_delete_link (column->cell_list, link);
- column->cell_list = g_list_insert (column->cell_list, info, position);
-
- if (column->tree_view)
- gtk_widget_queue_draw (column->tree_view);
-}
-
-static void
-gtk_tree_view_column_clear_attributes_by_info (GtkTreeViewColumn *tree_column,
- GtkTreeViewColumnCellInfo *info)
-{
- GSList *list;
-
- list = info->attributes;
-
- while (list && list->next)
- {
- g_free (list->data);
- list = list->next->next;
- }
- g_slist_free (info->attributes);
- info->attributes = NULL;
-
- if (tree_column->tree_view)
- _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
-}
-
-/* Helper functions
- */
-
/* Button handling code
*/
static void
gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column)
{
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
GtkTreeView *tree_view;
GtkWidget *child;
GtkWidget *hbox;
- tree_view = (GtkTreeView *) tree_column->tree_view;
+ tree_view = (GtkTreeView *) priv->tree_view;
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
- g_return_if_fail (tree_column->button == NULL);
+ g_return_if_fail (priv->button == NULL);
gtk_widget_push_composite_child ();
- tree_column->button = gtk_button_new ();
- gtk_widget_add_events (tree_column->button, GDK_POINTER_MOTION_MASK);
+ priv->button = gtk_button_new ();
+ gtk_widget_add_events (priv->button, GDK_POINTER_MOTION_MASK);
gtk_widget_pop_composite_child ();
/* make sure we own a reference to it as well. */
- if (tree_view->priv->header_window)
- gtk_widget_set_parent_window (tree_column->button, tree_view->priv->header_window);
- gtk_widget_set_parent (tree_column->button, GTK_WIDGET (tree_view));
+ if (_gtk_tree_view_get_header_window (tree_view))
+ gtk_widget_set_parent_window (priv->button, _gtk_tree_view_get_header_window (tree_view));
+
+ gtk_widget_set_parent (priv->button, GTK_WIDGET (tree_view));
- g_signal_connect (tree_column->button, "event",
+ g_signal_connect (priv->button, "event",
G_CALLBACK (gtk_tree_view_column_button_event),
tree_column);
- g_signal_connect (tree_column->button, "clicked",
+ g_signal_connect (priv->button, "clicked",
G_CALLBACK (gtk_tree_view_column_button_clicked),
tree_column);
- tree_column->alignment = gtk_alignment_new (tree_column->xalign, 0.5, 0.0, 0.0);
+ priv->alignment = gtk_alignment_new (priv->xalign, 0.5, 0.0, 0.0);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
- tree_column->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
+ priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
- if (tree_column->child)
- child = tree_column->child;
+ if (priv->child)
+ child = priv->child;
else
{
- child = gtk_label_new (tree_column->title);
+ child = gtk_label_new (priv->title);
gtk_widget_show (child);
}
@@ -889,24 +840,25 @@ gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column)
G_CALLBACK (gtk_tree_view_column_mnemonic_activate),
tree_column);
- if (tree_column->xalign <= 0.5)
- gtk_box_pack_end (GTK_BOX (hbox), tree_column->arrow, FALSE, FALSE, 0);
+ if (priv->xalign <= 0.5)
+ gtk_box_pack_end (GTK_BOX (hbox), priv->arrow, FALSE, FALSE, 0);
else
- gtk_box_pack_start (GTK_BOX (hbox), tree_column->arrow, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), priv->arrow, FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (hbox), tree_column->alignment, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), priv->alignment, TRUE, TRUE, 0);
- gtk_container_add (GTK_CONTAINER (tree_column->alignment), child);
- gtk_container_add (GTK_CONTAINER (tree_column->button), hbox);
+ gtk_container_add (GTK_CONTAINER (priv->alignment), child);
+ gtk_container_add (GTK_CONTAINER (priv->button), hbox);
gtk_widget_show (hbox);
- gtk_widget_show (tree_column->alignment);
+ gtk_widget_show (priv->alignment);
gtk_tree_view_column_update_button (tree_column);
}
static void
gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
{
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
gint sort_column_id = -1;
GtkWidget *hbox;
GtkWidget *alignment;
@@ -915,38 +867,38 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
GtkArrowType arrow_type = GTK_ARROW_NONE;
GtkTreeModel *model;
- if (tree_column->tree_view)
- model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_column->tree_view));
+ if (priv->tree_view)
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
else
model = NULL;
/* Create a button if necessary */
- if (tree_column->visible &&
- tree_column->button == NULL &&
- tree_column->tree_view &&
- gtk_widget_get_realized (tree_column->tree_view))
+ if (priv->visible &&
+ priv->button == NULL &&
+ priv->tree_view &&
+ gtk_widget_get_realized (priv->tree_view))
gtk_tree_view_column_create_button (tree_column);
- if (! tree_column->button)
+ if (! priv->button)
return;
- hbox = gtk_bin_get_child (GTK_BIN (tree_column->button));
- alignment = tree_column->alignment;
- arrow = tree_column->arrow;
+ hbox = gtk_bin_get_child (GTK_BIN (priv->button));
+ alignment = priv->alignment;
+ arrow = priv->arrow;
current_child = gtk_bin_get_child (GTK_BIN (alignment));
/* Set up the actual button */
- gtk_alignment_set (GTK_ALIGNMENT (alignment), tree_column->xalign,
+ gtk_alignment_set (GTK_ALIGNMENT (alignment), priv->xalign,
0.5, 0.0, 0.0);
- if (tree_column->child)
+ if (priv->child)
{
- if (current_child != tree_column->child)
+ if (current_child != priv->child)
{
gtk_container_remove (GTK_CONTAINER (alignment),
current_child);
gtk_container_add (GTK_CONTAINER (alignment),
- tree_column->child);
+ priv->child);
}
}
else
@@ -961,9 +913,9 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
g_return_if_fail (GTK_IS_LABEL (current_child));
- if (tree_column->title)
+ if (priv->title)
gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
- tree_column->title);
+ priv->title);
else
gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
"");
@@ -974,15 +926,15 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
&sort_column_id,
NULL);
- if (tree_column->show_sort_indicator)
+ if (priv->show_sort_indicator)
{
gboolean alternative;
- g_object_get (gtk_widget_get_settings (tree_column->tree_view),
+ g_object_get (gtk_widget_get_settings (priv->tree_view),
"gtk-alternative-sort-arrows", &alternative,
NULL);
- switch (tree_column->sort_order)
+ switch (priv->sort_order)
{
case GTK_SORT_ASCENDING:
arrow_type = alternative ? GTK_ARROW_UP : GTK_ARROW_DOWN;
@@ -1009,7 +961,7 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
g_object_ref (arrow);
gtk_container_remove (GTK_CONTAINER (hbox), arrow);
- if (tree_column->xalign <= 0.5)
+ if (priv->xalign <= 0.5)
{
gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
}
@@ -1021,52 +973,52 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
}
g_object_unref (arrow);
- if (tree_column->show_sort_indicator
- || (GTK_IS_TREE_SORTABLE (model) && tree_column->sort_column_id >= 0))
+ if (priv->show_sort_indicator
+ || (GTK_IS_TREE_SORTABLE (model) && priv->sort_column_id >= 0))
gtk_widget_show (arrow);
else
gtk_widget_hide (arrow);
/* It's always safe to hide the button. It isn't always safe to show it, as
* if you show it before it's realized, it'll get the wrong window. */
- if (tree_column->button &&
- tree_column->tree_view != NULL &&
- gtk_widget_get_realized (tree_column->tree_view))
+ if (priv->button &&
+ priv->tree_view != NULL &&
+ gtk_widget_get_realized (priv->tree_view))
{
- if (tree_column->visible)
+ if (priv->visible)
{
- gtk_widget_show_now (tree_column->button);
- if (tree_column->window)
+ gtk_widget_show_now (priv->button);
+ if (priv->window)
{
- if (tree_column->resizable)
+ if (priv->resizable)
{
- gdk_window_show (tree_column->window);
- gdk_window_raise (tree_column->window);
+ gdk_window_show (priv->window);
+ gdk_window_raise (priv->window);
}
else
{
- gdk_window_hide (tree_column->window);
+ gdk_window_hide (priv->window);
}
}
}
else
{
- gtk_widget_hide (tree_column->button);
- if (tree_column->window)
- gdk_window_hide (tree_column->window);
+ gtk_widget_hide (priv->button);
+ if (priv->window)
+ gdk_window_hide (priv->window);
}
}
- if (tree_column->reorderable || tree_column->clickable)
+ if (priv->reorderable || priv->clickable)
{
- gtk_widget_set_can_focus (tree_column->button, TRUE);
+ gtk_widget_set_can_focus (priv->button, TRUE);
}
else
{
- gtk_widget_set_can_focus (tree_column->button, FALSE);
- if (gtk_widget_has_focus (tree_column->button))
+ gtk_widget_set_can_focus (priv->button, FALSE);
+ if (gtk_widget_has_focus (priv->button))
{
- GtkWidget *toplevel = gtk_widget_get_toplevel (tree_column->tree_view);
+ GtkWidget *toplevel = gtk_widget_get_toplevel (priv->tree_view);
if (gtk_widget_is_toplevel (toplevel))
{
gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
@@ -1076,8 +1028,8 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
/* Queue a resize on the assumption that we always want to catch all changes
* and columns don't change all that often.
*/
- if (gtk_widget_get_realized (tree_column->tree_view))
- gtk_widget_queue_resize (tree_column->tree_view);
+ if (gtk_widget_get_realized (priv->tree_view))
+ gtk_widget_queue_resize (priv->tree_view);
}
@@ -1089,40 +1041,42 @@ gtk_tree_view_column_button_event (GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
- GtkTreeViewColumn *column = (GtkTreeViewColumn *) data;
+ GtkTreeViewColumn *column = (GtkTreeViewColumn *) data;
+ GtkTreeViewColumnPrivate *priv = column->priv;
g_return_val_if_fail (event != NULL, FALSE);
if (event->type == GDK_BUTTON_PRESS &&
- column->reorderable &&
+ priv->reorderable &&
((GdkEventButton *)event)->button == 1)
{
- column->maybe_reordered = TRUE;
+ priv->maybe_reordered = TRUE;
gdk_window_get_pointer (gtk_button_get_event_window (GTK_BUTTON (widget)),
- &column->drag_x,
- &column->drag_y,
+ &priv->drag_x,
+ &priv->drag_y,
NULL);
gtk_widget_grab_focus (widget);
}
if (event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_LEAVE_NOTIFY)
- column->maybe_reordered = FALSE;
+ priv->maybe_reordered = FALSE;
if (event->type == GDK_MOTION_NOTIFY &&
- column->maybe_reordered &&
+ priv->maybe_reordered &&
(gtk_drag_check_threshold (widget,
- column->drag_x,
- column->drag_y,
+ priv->drag_x,
+ priv->drag_y,
(gint) ((GdkEventMotion *)event)->x,
(gint) ((GdkEventMotion *)event)->y)))
{
- column->maybe_reordered = FALSE;
- _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (column->tree_view), column,
+ priv->maybe_reordered = FALSE;
+ _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (priv->tree_view), column,
event->motion.device);
return TRUE;
}
- if (column->clickable == FALSE)
+
+ if (priv->clickable == FALSE)
{
switch (event->type)
{
@@ -1153,17 +1107,19 @@ gtk_tree_view_column_mnemonic_activate (GtkWidget *widget,
gboolean group_cycling,
gpointer data)
{
- GtkTreeViewColumn *column = (GtkTreeViewColumn *)data;
+ GtkTreeViewColumn *column = (GtkTreeViewColumn *)data;
+ GtkTreeViewColumnPrivate *priv = column->priv;
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), FALSE);
- GTK_TREE_VIEW (column->tree_view)->priv->focus_column = column;
- if (column->clickable)
- gtk_button_clicked (GTK_BUTTON (column->button));
- else if (gtk_widget_get_can_focus (column->button))
- gtk_widget_grab_focus (column->button);
+ _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (priv->tree_view), column);
+
+ if (priv->clickable)
+ gtk_button_clicked (GTK_BUTTON (priv->button));
+ else if (gtk_widget_get_can_focus (priv->button))
+ gtk_widget_grab_focus (priv->button);
else
- gtk_widget_grab_focus (column->tree_view);
+ gtk_widget_grab_focus (priv->tree_view);
return TRUE;
}
@@ -1172,6 +1128,7 @@ static void
gtk_tree_view_model_sort_column_changed (GtkTreeSortable *sortable,
GtkTreeViewColumn *column)
{
+ GtkTreeViewColumnPrivate *priv = column->priv;
gint sort_column_id;
GtkSortType order;
@@ -1179,7 +1136,7 @@ gtk_tree_view_model_sort_column_changed (GtkTreeSortable *sortable,
&sort_column_id,
&order))
{
- if (sort_column_id == column->sort_column_id)
+ if (sort_column_id == priv->sort_column_id)
{
gtk_tree_view_column_set_sort_indicator (column, TRUE);
gtk_tree_view_column_set_sort_order (column, order);
@@ -1199,74 +1156,80 @@ static void
gtk_tree_view_column_sort (GtkTreeViewColumn *tree_column,
gpointer data)
{
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
+ GtkTreeModel *model;
+ GtkTreeSortable *sortable;
gint sort_column_id;
GtkSortType order;
gboolean has_sort_column;
gboolean has_default_sort_func;
- g_return_if_fail (tree_column->tree_view != NULL);
+ g_return_if_fail (priv->tree_view != NULL);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
+ sortable = GTK_TREE_SORTABLE (model);
has_sort_column =
- gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (GTK_TREE_VIEW (tree_column->tree_view)->priv->model),
+ gtk_tree_sortable_get_sort_column_id (sortable,
&sort_column_id,
&order);
has_default_sort_func =
- gtk_tree_sortable_has_default_sort_func (GTK_TREE_SORTABLE (GTK_TREE_VIEW (tree_column->tree_view)->priv->model));
+ gtk_tree_sortable_has_default_sort_func (sortable);
if (has_sort_column &&
- sort_column_id == tree_column->sort_column_id)
+ sort_column_id == priv->sort_column_id)
{
if (order == GTK_SORT_ASCENDING)
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GTK_TREE_VIEW (tree_column->tree_view)->priv->model),
- tree_column->sort_column_id,
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ priv->sort_column_id,
GTK_SORT_DESCENDING);
else if (order == GTK_SORT_DESCENDING && has_default_sort_func)
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GTK_TREE_VIEW (tree_column->tree_view)->priv->model),
+ gtk_tree_sortable_set_sort_column_id (sortable,
GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
GTK_SORT_ASCENDING);
else
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GTK_TREE_VIEW (tree_column->tree_view)->priv->model),
- tree_column->sort_column_id,
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ priv->sort_column_id,
GTK_SORT_ASCENDING);
}
else
{
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GTK_TREE_VIEW (tree_column->tree_view)->priv->model),
- tree_column->sort_column_id,
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ priv->sort_column_id,
GTK_SORT_ASCENDING);
}
}
-
static void
gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColumn *tree_column)
{
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
GtkTreeModel *model;
- if (tree_column->tree_view == NULL)
+ if (priv->tree_view == NULL)
return;
- model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_column->tree_view));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
if (model == NULL)
return;
if (GTK_IS_TREE_SORTABLE (model) &&
- tree_column->sort_column_id != -1)
+ priv->sort_column_id != -1)
{
gint real_sort_column_id;
GtkSortType real_order;
- if (tree_column->sort_column_changed_signal == 0)
- tree_column->sort_column_changed_signal =
+ if (priv->sort_column_changed_signal == 0)
+ priv->sort_column_changed_signal =
g_signal_connect (model, "sort-column-changed",
G_CALLBACK (gtk_tree_view_model_sort_column_changed),
tree_column);
-
+
if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
&real_sort_column_id,
&real_order) &&
- (real_sort_column_id == tree_column->sort_column_id))
+ (real_sort_column_id == priv->sort_column_id))
{
gtk_tree_view_column_set_sort_indicator (tree_column, TRUE);
gtk_tree_view_column_set_sort_order (tree_column, real_order);
@@ -1278,32 +1241,94 @@ gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColumn *tree_colu
}
}
+static void
+gtk_tree_view_column_context_changed (GtkCellAreaContext *context,
+ GParamSpec *pspec,
+ GtkTreeViewColumn *tree_column)
+{
+ /* Here we want the column re-requested if the underlying context was
+ * actually reset for any reason, this can happen if the underlying
+ * area/cell configuration changes (i.e. cell packing properties
+ * or cell spacing and the like)
+ *
+ * Note that we block this handler while requesting for sizes
+ * so there is no need to check for the new context size being -1,
+ * we also block the handler when explicitly resetting the context
+ * so as to avoid some infinite stack recursion.
+ */
+ if (!strcmp (pspec->name, "minimum-width") ||
+ !strcmp (pspec->name, "natural-width") ||
+ !strcmp (pspec->name, "minimum-height") ||
+ !strcmp (pspec->name, "natural-height"))
+ _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+}
+
+static void
+gtk_tree_view_column_add_editable_callback (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ GdkRectangle *cell_area,
+ const gchar *path_string,
+ gpointer user_data)
+{
+ GtkTreeViewColumn *column = user_data;
+ GtkTreeViewColumnPrivate *priv = column->priv;
+ GtkTreePath *path;
+
+ if (priv->tree_view)
+ {
+ path = gtk_tree_path_new_from_string (path_string);
+
+ _gtk_tree_view_add_editable (GTK_TREE_VIEW (priv->tree_view),
+ column,
+ path,
+ edit_widget,
+ cell_area);
+
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+gtk_tree_view_column_remove_editable_callback (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ gpointer user_data)
+{
+ GtkTreeViewColumn *column = user_data;
+ GtkTreeViewColumnPrivate *priv = column->priv;
+
+ if (priv->tree_view)
+ _gtk_tree_view_remove_editable (GTK_TREE_VIEW (priv->tree_view),
+ column,
+ edit_widget);
+}
/* Exported Private Functions.
* These should only be called by gtktreeview.c or gtktreeviewcolumn.c
*/
-
void
_gtk_tree_view_column_realize_button (GtkTreeViewColumn *column)
{
+ GtkTreeViewColumnPrivate *priv = column->priv;
GtkAllocation allocation;
GtkTreeView *tree_view;
GdkWindowAttr attr;
guint attributes_mask;
gboolean rtl;
- tree_view = (GtkTreeView *)column->tree_view;
- rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
+ tree_view = (GtkTreeView *)priv->tree_view;
+ rtl = (gtk_widget_get_direction (priv->tree_view) == GTK_TEXT_DIR_RTL);
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
- g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
- g_return_if_fail (tree_view->priv->header_window != NULL);
- g_return_if_fail (column->button != NULL);
+ g_return_if_fail (gtk_widget_get_realized (priv->tree_view));
+ g_return_if_fail (priv->button != NULL);
- gtk_widget_set_parent_window (column->button, tree_view->priv->header_window);
+ g_return_if_fail (_gtk_tree_view_get_header_window (tree_view) != NULL);
+ gtk_widget_set_parent_window (priv->button, _gtk_tree_view_get_header_window (tree_view));
- if (column->visible)
- gtk_widget_show (column->button);
+ if (priv->visible)
+ gtk_widget_show (priv->button);
attr.window_type = GDK_WINDOW_CHILD;
attr.wclass = GDK_INPUT_ONLY;
@@ -1315,17 +1340,17 @@ _gtk_tree_view_column_realize_button (GtkTreeViewColumn *column)
GDK_POINTER_MOTION_HINT_MASK |
GDK_KEY_PRESS_MASK);
attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y;
- attr.cursor = gdk_cursor_new_for_display (gdk_window_get_display (tree_view->priv->header_window),
- GDK_SB_H_DOUBLE_ARROW);
+ attr.cursor = gdk_cursor_new_for_display
+ (gdk_window_get_display (_gtk_tree_view_get_header_window (tree_view)), GDK_SB_H_DOUBLE_ARROW);
attr.y = 0;
attr.width = TREE_VIEW_DRAG_WIDTH;
- attr.height = tree_view->priv->header_height;
+ attr.height = _gtk_tree_view_get_header_height (tree_view);
- gtk_widget_get_allocation (column->button, &allocation);
- attr.x = (allocation.x + (rtl ? 0 : allocation.width)) - TREE_VIEW_DRAG_WIDTH / 2;
- column->window = gdk_window_new (tree_view->priv->header_window,
- &attr, attributes_mask);
- gdk_window_set_user_data (column->window, tree_view);
+ gtk_widget_get_allocation (priv->button, &allocation);
+ attr.x = (allocation.x + (rtl ? 0 : allocation.width)) - TREE_VIEW_DRAG_WIDTH / 2;
+ priv->window = gdk_window_new (_gtk_tree_view_get_header_window (tree_view),
+ &attr, attributes_mask);
+ gdk_window_set_user_data (priv->window, tree_view);
gtk_tree_view_column_update_button (column);
@@ -1335,23 +1360,29 @@ _gtk_tree_view_column_realize_button (GtkTreeViewColumn *column)
void
_gtk_tree_view_column_unrealize_button (GtkTreeViewColumn *column)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (column != NULL);
- g_return_if_fail (column->window != NULL);
- gdk_window_set_user_data (column->window, NULL);
- gdk_window_destroy (column->window);
- column->window = NULL;
+ priv = column->priv;
+ g_return_if_fail (priv->window != NULL);
+
+ gdk_window_set_user_data (priv->window, NULL);
+ gdk_window_destroy (priv->window);
+ priv->window = NULL;
}
void
_gtk_tree_view_column_unset_model (GtkTreeViewColumn *column,
GtkTreeModel *old_model)
{
- if (column->sort_column_changed_signal)
+ GtkTreeViewColumnPrivate *priv = column->priv;
+
+ if (priv->sort_column_changed_signal)
{
g_signal_handler_disconnect (old_model,
- column->sort_column_changed_signal);
- column->sort_column_changed_signal = 0;
+ priv->sort_column_changed_signal);
+ priv->sort_column_changed_signal = 0;
}
gtk_tree_view_column_set_sort_indicator (column, FALSE);
}
@@ -1360,16 +1391,18 @@ void
_gtk_tree_view_column_set_tree_view (GtkTreeViewColumn *column,
GtkTreeView *tree_view)
{
- g_assert (column->tree_view == NULL);
+ GtkTreeViewColumnPrivate *priv = column->priv;
+
+ g_assert (priv->tree_view == NULL);
- column->tree_view = GTK_WIDGET (tree_view);
+ priv->tree_view = GTK_WIDGET (tree_view);
gtk_tree_view_column_create_button (column);
- column->property_changed_signal =
- g_signal_connect_swapped (tree_view,
- "notify::model",
- G_CALLBACK (gtk_tree_view_column_setup_sort_column_id_callback),
- column);
+ priv->property_changed_signal =
+ g_signal_connect_swapped (tree_view,
+ "notify::model",
+ G_CALLBACK (gtk_tree_view_column_setup_sort_column_id_callback),
+ column);
gtk_tree_view_column_setup_sort_column_id_callback (column);
}
@@ -1377,96 +1410,109 @@ _gtk_tree_view_column_set_tree_view (GtkTreeViewColumn *column,
void
_gtk_tree_view_column_unset_tree_view (GtkTreeViewColumn *column)
{
- if (column->tree_view && column->button)
+ GtkTreeViewColumnPrivate *priv = column->priv;
+
+ if (priv->tree_view && priv->button)
{
- gtk_container_remove (GTK_CONTAINER (column->tree_view), column->button);
+ gtk_container_remove (GTK_CONTAINER (priv->tree_view), priv->button);
}
- if (column->property_changed_signal)
+ if (priv->property_changed_signal)
{
- g_signal_handler_disconnect (column->tree_view, column->property_changed_signal);
- column->property_changed_signal = 0;
+ g_signal_handler_disconnect (priv->tree_view, priv->property_changed_signal);
+ priv->property_changed_signal = 0;
}
- if (column->sort_column_changed_signal)
+ if (priv->sort_column_changed_signal)
{
- g_signal_handler_disconnect (gtk_tree_view_get_model (GTK_TREE_VIEW (column->tree_view)),
- column->sort_column_changed_signal);
- column->sort_column_changed_signal = 0;
+ g_signal_handler_disconnect (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)),
+ priv->sort_column_changed_signal);
+ priv->sort_column_changed_signal = 0;
}
- column->tree_view = NULL;
- column->button = NULL;
+ priv->tree_view = NULL;
+ priv->button = NULL;
}
gboolean
_gtk_tree_view_column_has_editable_cell (GtkTreeViewColumn *column)
{
- GtkCellRenderer *cell;
- GtkCellRendererMode mode;
- GList *list;
+ GtkTreeViewColumnPrivate *priv = column->priv;
+ gboolean ret = FALSE;
+ GList *list, *cells;
+
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->cell_area));
- for (list = column->cell_list; list; list = list->next)
+ for (list = cells; list; list = list->next)
{
- cell = ((GtkTreeViewColumnCellInfo *)list->data)->cell;
+ GtkCellRenderer *cell = list->data;
+ GtkCellRendererMode mode;
+
g_object_get (cell, "mode", &mode, NULL);
+
if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
- return TRUE;
+ {
+ ret = TRUE;
+ break;
+ }
}
- return FALSE;
+ g_list_free (cells);
+
+ return ret;
}
/* gets cell being edited */
GtkCellRenderer *
_gtk_tree_view_column_get_edited_cell (GtkTreeViewColumn *column)
{
- GList *list;
-
- for (list = column->cell_list; list; list = list->next)
- if (((GtkTreeViewColumnCellInfo *)list->data)->in_editing_mode)
- return ((GtkTreeViewColumnCellInfo *)list->data)->cell;
-
- return NULL;
-}
-
-gint
-_gtk_tree_view_column_count_special_cells (GtkTreeViewColumn *column)
-{
- gint i = 0;
- GList *list;
-
- for (list = column->cell_list; list; list = list->next)
- {
- GtkCellRendererMode mode;
- GtkTreeViewColumnCellInfo *cellinfo = list->data;
-
- g_object_get (cellinfo->cell, "mode", &mode, NULL);
- if ((mode == GTK_CELL_RENDERER_MODE_EDITABLE ||
- mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) &&
- gtk_cell_renderer_get_visible (cellinfo->cell))
- i++;
- }
+ GtkTreeViewColumnPrivate *priv = column->priv;
- return i;
+ return gtk_cell_area_get_edited_cell (priv->cell_area);
}
GtkCellRenderer *
_gtk_tree_view_column_get_cell_at_pos (GtkTreeViewColumn *column,
- gint x)
-{
- GList *list;
- gint current_x = 0;
-
- list = gtk_tree_view_column_cell_first (column);
- for (; list; list = gtk_tree_view_column_cell_next (column, list))
- {
- GtkTreeViewColumnCellInfo *cellinfo = list->data;
- if (current_x <= x && x <= current_x + cellinfo->real_width)
- return cellinfo->cell;
- current_x += cellinfo->real_width;
- }
+ GdkRectangle *cell_area,
+ GdkRectangle *background_area,
+ gint x,
+ gint y)
+{
+ GtkCellRenderer *match = NULL;
+ GtkTreeViewColumnPrivate *priv = column->priv;
+
+ /* If (x, y) is outside of the background area, immediately return */
+ if (x < background_area->x ||
+ x > background_area->x + background_area->width ||
+ y < background_area->y ||
+ y > background_area->y + background_area->height)
+ return NULL;
+
+ /* If (x, y) is inside the background area, clamp it to the cell_area
+ * so that a cell is still returned. The main reason for doing this
+ * (on the x axis) is for handling clicks in the indentation area
+ * (either at the left or right depending on RTL setting). Another
+ * reason is for handling clicks on the area where the focus rectangle
+ * is drawn (this is outside of cell area), this manifests itself
+ * mainly when a large setting is used for focus-line-width.
+ */
+ if (x < cell_area->x)
+ x = cell_area->x;
+ else if (x > cell_area->x + cell_area->width)
+ x = cell_area->x + cell_area->width;
+
+ if (y < cell_area->y)
+ y = cell_area->y;
+ else if (y > cell_area->y + cell_area->height)
+ y = cell_area->y + cell_area->height;
- return NULL;
+ match = gtk_cell_area_get_cell_at_position (priv->cell_area,
+ priv->cell_area_context,
+ priv->tree_view,
+ cell_area,
+ x, y,
+ NULL);
+
+ return match;
}
/* Public Functions */
@@ -1490,6 +1536,27 @@ gtk_tree_view_column_new (void)
}
/**
+ * gtk_tree_view_column_new_with_area:
+ * @area: the #GtkCellArea that the newly created column should use to layout cells.
+ *
+ * Creates a new #GtkTreeViewColumn using @area to render it's cells.
+ *
+ * Return value: A newly created #GtkTreeViewColumn.
+ *
+ * Since: 3.0
+ */
+GtkTreeViewColumn *
+gtk_tree_view_column_new_with_area (GtkCellArea *area)
+{
+ GtkTreeViewColumn *tree_column;
+
+ tree_column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, "cell-area", area, NULL);
+
+ return tree_column;
+}
+
+
+/**
* gtk_tree_view_column_new_with_attributes:
* @title: The title to set the header to.
* @cell: The #GtkCellRenderer.
@@ -1538,18 +1605,6 @@ gtk_tree_view_column_new_with_attributes (const gchar *title,
return retval;
}
-static GtkTreeViewColumnCellInfo *
-gtk_tree_view_column_get_cell_info (GtkTreeViewColumn *tree_column,
- GtkCellRenderer *cell_renderer)
-{
- GList *list;
- for (list = tree_column->cell_list; list; list = list->next)
- if (((GtkTreeViewColumnCellInfo *)list->data)->cell == cell_renderer)
- return (GtkTreeViewColumnCellInfo *) list->data;
- return NULL;
-}
-
-
/**
* gtk_tree_view_column_pack_start:
* @tree_column: A #GtkTreeViewColumn.
@@ -1598,24 +1653,6 @@ gtk_tree_view_column_clear (GtkTreeViewColumn *tree_column)
gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column));
}
-static GList *
-gtk_tree_view_column_cell_layout_get_cells (GtkCellLayout *layout)
-{
- GtkTreeViewColumn *tree_column = GTK_TREE_VIEW_COLUMN (layout);
- GList *retval = NULL, *list;
-
- g_return_val_if_fail (tree_column != NULL, NULL);
-
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *)list->data;
-
- retval = g_list_append (retval, info->cell);
- }
-
- return retval;
-}
-
/**
* gtk_tree_view_column_add_attribute:
* @tree_column: A #GtkTreeViewColumn.
@@ -1645,17 +1682,20 @@ gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell_renderer,
va_list args)
{
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
gchar *attribute;
gint column;
attribute = va_arg (args, gchar *);
- gtk_tree_view_column_clear_attributes (tree_column, cell_renderer);
+ gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_area),
+ cell_renderer);
while (attribute != NULL)
{
column = va_arg (args, gint);
- gtk_tree_view_column_add_attribute (tree_column, cell_renderer, attribute, column);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->cell_area),
+ cell_renderer, attribute, column);
attribute = va_arg (args, gchar *);
}
}
@@ -1680,7 +1720,6 @@ gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column,
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer));
- g_return_if_fail (gtk_tree_view_column_get_cell_info (tree_column, cell_renderer));
va_start (args, cell_renderer);
gtk_tree_view_column_set_attributesv (tree_column, cell_renderer, args);
@@ -1744,14 +1783,16 @@ void
gtk_tree_view_column_set_spacing (GtkTreeViewColumn *tree_column,
gint spacing)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (spacing >= 0);
- if (tree_column->spacing == spacing)
- return;
+ priv = tree_column->priv;
- tree_column->spacing = spacing;
- if (tree_column->tree_view)
+ gtk_cell_area_box_set_spacing (GTK_CELL_AREA_BOX (priv->cell_area),
+ spacing);
+ if (priv->tree_view)
_gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
}
@@ -1766,9 +1807,13 @@ gtk_tree_view_column_set_spacing (GtkTreeViewColumn *tree_column,
gint
gtk_tree_view_column_get_spacing (GtkTreeViewColumn *tree_column)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
- return tree_column->spacing;
+ priv = tree_column->priv;
+
+ return gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (priv->cell_area));
}
/* Options for manipulating the columns */
@@ -1784,16 +1829,19 @@ void
gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column,
gboolean visible)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ priv = tree_column->priv;
visible = !! visible;
- if (tree_column->visible == visible)
+ if (priv->visible == visible)
return;
- tree_column->visible = visible;
+ priv->visible = visible;
- if (tree_column->visible)
+ if (priv->visible)
_gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
gtk_tree_view_column_update_button (tree_column);
@@ -1814,7 +1862,7 @@ gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
- return tree_column->visible;
+ return tree_column->priv->visible;
}
/**
@@ -1831,16 +1879,19 @@ void
gtk_tree_view_column_set_resizable (GtkTreeViewColumn *tree_column,
gboolean resizable)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ priv = tree_column->priv;
resizable = !! resizable;
- if (tree_column->resizable == resizable)
+ if (priv->resizable == resizable)
return;
- tree_column->resizable = resizable;
+ priv->resizable = resizable;
- if (resizable && tree_column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+ if (resizable && priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
gtk_tree_view_column_update_button (tree_column);
@@ -1861,7 +1912,7 @@ gtk_tree_view_column_get_resizable (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
- return tree_column->resizable;
+ return tree_column->priv->resizable;
}
@@ -1876,25 +1927,19 @@ void
gtk_tree_view_column_set_sizing (GtkTreeViewColumn *tree_column,
GtkTreeViewColumnSizing type)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- if (type == tree_column->column_type)
+ priv = tree_column->priv;
+
+ if (type == priv->column_type)
return;
if (type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
gtk_tree_view_column_set_resizable (tree_column, FALSE);
-#if 0
- /* I was clearly on crack when I wrote this. I'm not sure what's supposed to
- * be below so I'll leave it until I figure it out.
- */
- if (tree_column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE &&
- tree_column->requested_width != -1)
- {
- gtk_tree_view_column_set_sizing (tree_column, tree_column->requested_width);
- }
-#endif
- tree_column->column_type = type;
+ priv->column_type = type;
gtk_tree_view_column_update_button (tree_column);
@@ -1914,7 +1959,7 @@ gtk_tree_view_column_get_sizing (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
- return tree_column->column_type;
+ return tree_column->priv->column_type;
}
/**
@@ -1930,7 +1975,91 @@ gtk_tree_view_column_get_width (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
- return tree_column->width;
+ return tree_column->priv->width;
+}
+
+gint
+_gtk_tree_view_column_request_width (GtkTreeViewColumn *tree_column)
+{
+ GtkTreeViewColumnPrivate *priv;
+ gint real_requested_width;
+
+ priv = tree_column->priv;
+
+ if (priv->use_resized_width)
+ {
+ real_requested_width = priv->resized_width;
+ }
+ else if (priv->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+ {
+ real_requested_width = priv->fixed_width;
+ }
+ else if (gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view)))
+ {
+ gint button_request;
+ gint requested_width;
+
+ gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &requested_width, NULL);
+ requested_width += priv->padding;
+
+ gtk_widget_get_preferred_width (priv->button, &button_request, NULL);
+ real_requested_width = MAX (requested_width, button_request);
+ }
+ else
+ {
+ gint requested_width;
+
+ gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &requested_width, NULL);
+ requested_width += priv->padding;
+
+ real_requested_width = requested_width;
+ if (real_requested_width < 0)
+ real_requested_width = 0;
+ }
+
+ if (priv->min_width != -1)
+ real_requested_width = MAX (real_requested_width, priv->min_width);
+
+ if (priv->max_width != -1)
+ real_requested_width = MIN (real_requested_width, priv->max_width);
+
+ return real_requested_width;
+}
+
+void
+_gtk_tree_view_column_allocate (GtkTreeViewColumn *tree_column,
+ int x_offset,
+ int width)
+{
+ GtkTreeViewColumnPrivate *priv;
+ GtkAllocation allocation;
+ gboolean rtl;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ priv = tree_column->priv;
+
+ priv->width = width;
+
+ gtk_cell_area_context_allocate (priv->cell_area_context, priv->width - priv->padding, -1);
+
+ allocation.x = x_offset;
+ allocation.y = 0;
+ allocation.width = width;
+ allocation.height = _gtk_tree_view_get_header_height (GTK_TREE_VIEW (priv->tree_view));
+
+ gtk_widget_size_allocate (priv->button, &allocation);
+
+ if (priv->window)
+ {
+ rtl = (gtk_widget_get_direction (priv->tree_view) == GTK_TEXT_DIR_RTL);
+ gdk_window_move_resize (priv->window,
+ allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
+ allocation.y,
+ TREE_VIEW_DRAG_WIDTH, allocation.height);
+ }
+
+ g_object_notify (G_OBJECT (tree_column), "width");
}
/**
@@ -1948,17 +2077,21 @@ void
gtk_tree_view_column_set_fixed_width (GtkTreeViewColumn *tree_column,
gint fixed_width)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (fixed_width > 0);
- tree_column->fixed_width = fixed_width;
- tree_column->use_resized_width = FALSE;
+ priv = tree_column->priv;
+
+ priv->fixed_width = fixed_width;
+ priv->use_resized_width = FALSE;
- if (tree_column->tree_view &&
- gtk_widget_get_realized (tree_column->tree_view) &&
- tree_column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+ if (priv->tree_view &&
+ gtk_widget_get_realized (priv->tree_view) &&
+ priv->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
{
- gtk_widget_queue_resize (tree_column->tree_view);
+ gtk_widget_queue_resize (priv->tree_view);
}
g_object_notify (G_OBJECT (tree_column), "fixed-width");
@@ -1978,7 +2111,7 @@ gtk_tree_view_column_get_fixed_width (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
- return tree_column->fixed_width;
+ return tree_column->priv->fixed_width;
}
/**
@@ -1993,32 +2126,36 @@ void
gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column,
gint min_width)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (min_width >= -1);
- if (min_width == tree_column->min_width)
+ priv = tree_column->priv;
+
+ if (min_width == priv->min_width)
return;
- if (tree_column->visible &&
- tree_column->tree_view != NULL &&
- gtk_widget_get_realized (tree_column->tree_view))
+ if (priv->visible &&
+ priv->tree_view != NULL &&
+ gtk_widget_get_realized (priv->tree_view))
{
- if (min_width > tree_column->width)
- gtk_widget_queue_resize (tree_column->tree_view);
+ if (min_width > priv->width)
+ gtk_widget_queue_resize (priv->tree_view);
}
- tree_column->min_width = min_width;
+ priv->min_width = min_width;
g_object_freeze_notify (G_OBJECT (tree_column));
- if (tree_column->max_width != -1 && tree_column->max_width < min_width)
+ if (priv->max_width != -1 && priv->max_width < min_width)
{
- tree_column->max_width = min_width;
+ priv->max_width = min_width;
g_object_notify (G_OBJECT (tree_column), "max-width");
}
g_object_notify (G_OBJECT (tree_column), "min-width");
g_object_thaw_notify (G_OBJECT (tree_column));
- if (tree_column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
- _gtk_tree_view_column_autosize (GTK_TREE_VIEW (tree_column->tree_view),
+ if (priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+ _gtk_tree_view_column_autosize (GTK_TREE_VIEW (priv->tree_view),
tree_column);
}
@@ -2036,7 +2173,7 @@ gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1);
- return tree_column->min_width;
+ return tree_column->priv->min_width;
}
/**
@@ -2053,32 +2190,36 @@ void
gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column,
gint max_width)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (max_width >= -1);
- if (max_width == tree_column->max_width)
+ priv = tree_column->priv;
+
+ if (max_width == priv->max_width)
return;
- if (tree_column->visible &&
- tree_column->tree_view != NULL &&
- gtk_widget_get_realized (tree_column->tree_view))
+ if (priv->visible &&
+ priv->tree_view != NULL &&
+ gtk_widget_get_realized (priv->tree_view))
{
- if (max_width != -1 && max_width < tree_column->width)
- gtk_widget_queue_resize (tree_column->tree_view);
+ if (max_width != -1 && max_width < priv->width)
+ gtk_widget_queue_resize (priv->tree_view);
}
- tree_column->max_width = max_width;
+ priv->max_width = max_width;
g_object_freeze_notify (G_OBJECT (tree_column));
- if (max_width != -1 && max_width < tree_column->min_width)
+ if (max_width != -1 && max_width < priv->min_width)
{
- tree_column->min_width = max_width;
+ priv->min_width = max_width;
g_object_notify (G_OBJECT (tree_column), "min-width");
}
g_object_notify (G_OBJECT (tree_column), "max-width");
g_object_thaw_notify (G_OBJECT (tree_column));
- if (tree_column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
- _gtk_tree_view_column_autosize (GTK_TREE_VIEW (tree_column->tree_view),
+ if (priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+ _gtk_tree_view_column_autosize (GTK_TREE_VIEW (priv->tree_view),
tree_column);
}
@@ -2096,7 +2237,7 @@ gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1);
- return tree_column->max_width;
+ return tree_column->priv->max_width;
}
/**
@@ -2109,12 +2250,16 @@ gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column)
void
gtk_tree_view_column_clicked (GtkTreeViewColumn *tree_column)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- if (tree_column->visible &&
- tree_column->button &&
- tree_column->clickable)
- gtk_button_clicked (GTK_BUTTON (tree_column->button));
+ priv = tree_column->priv;
+
+ if (priv->visible &&
+ priv->button &&
+ priv->clickable)
+ gtk_button_clicked (GTK_BUTTON (priv->button));
}
/**
@@ -2129,13 +2274,16 @@ void
gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column,
const gchar *title)
{
- gchar *new_title;
-
+ GtkTreeViewColumnPrivate *priv;
+ gchar *new_title;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ priv = tree_column->priv;
+
new_title = g_strdup (title);
- g_free (tree_column->title);
- tree_column->title = new_title;
+ g_free (priv->title);
+ priv->title = new_title;
gtk_tree_view_column_update_button (tree_column);
g_object_notify (G_OBJECT (tree_column), "title");
@@ -2155,7 +2303,7 @@ gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
- return tree_column->title;
+ return tree_column->priv->title;
}
/**
@@ -2174,25 +2322,29 @@ void
gtk_tree_view_column_set_expand (GtkTreeViewColumn *tree_column,
gboolean expand)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ priv = tree_column->priv;
+
expand = expand?TRUE:FALSE;
- if (tree_column->expand == expand)
+ if (priv->expand == expand)
return;
- tree_column->expand = expand;
+ priv->expand = expand;
- if (tree_column->visible &&
- tree_column->tree_view != NULL &&
- gtk_widget_get_realized (tree_column->tree_view))
+ if (priv->visible &&
+ priv->tree_view != NULL &&
+ gtk_widget_get_realized (priv->tree_view))
{
/* We want to continue using the original width of the
* column that includes additional space added by the user
* resizing the columns and possibly extra (expanded) space, which
* are not included in the resized width.
*/
- tree_column->use_resized_width = FALSE;
+ priv->use_resized_width = FALSE;
- gtk_widget_queue_resize (tree_column->tree_view);
+ gtk_widget_queue_resize (priv->tree_view);
}
g_object_notify (G_OBJECT (tree_column), "expand");
@@ -2213,7 +2365,7 @@ gtk_tree_view_column_get_expand (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
- return tree_column->expand;
+ return tree_column->priv->expand;
}
/**
@@ -2228,13 +2380,17 @@ void
gtk_tree_view_column_set_clickable (GtkTreeViewColumn *tree_column,
gboolean clickable)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ priv = tree_column->priv;
+
clickable = !! clickable;
- if (tree_column->clickable == clickable)
+ if (priv->clickable == clickable)
return;
- tree_column->clickable = clickable;
+ priv->clickable = clickable;
gtk_tree_view_column_update_button (tree_column);
g_object_notify (G_OBJECT (tree_column), "clickable");
}
@@ -2252,7 +2408,7 @@ gtk_tree_view_column_get_clickable (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
- return tree_column->clickable;
+ return tree_column->priv->clickable;
}
/**
@@ -2267,16 +2423,20 @@ void
gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column,
GtkWidget *widget)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
+ priv = tree_column->priv;
+
if (widget)
g_object_ref_sink (widget);
- if (tree_column->child)
- g_object_unref (tree_column->child);
+ if (priv->child)
+ g_object_unref (priv->child);
- tree_column->child = widget;
+ priv->child = widget;
gtk_tree_view_column_update_button (tree_column);
g_object_notify (G_OBJECT (tree_column), "widget");
}
@@ -2296,7 +2456,7 @@ gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
- return tree_column->child;
+ return tree_column->priv->child;
}
/**
@@ -2312,14 +2472,18 @@ void
gtk_tree_view_column_set_alignment (GtkTreeViewColumn *tree_column,
gfloat xalign)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ priv = tree_column->priv;
+
xalign = CLAMP (xalign, 0.0, 1.0);
- if (tree_column->xalign == xalign)
+ if (priv->xalign == xalign)
return;
- tree_column->xalign = xalign;
+ priv->xalign = xalign;
gtk_tree_view_column_update_button (tree_column);
g_object_notify (G_OBJECT (tree_column), "alignment");
}
@@ -2338,7 +2502,7 @@ gtk_tree_view_column_get_alignment (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0.5);
- return tree_column->xalign;
+ return tree_column->priv->xalign;
}
/**
@@ -2353,15 +2517,19 @@ void
gtk_tree_view_column_set_reorderable (GtkTreeViewColumn *tree_column,
gboolean reorderable)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ priv = tree_column->priv;
+
/* if (reorderable)
gtk_tree_view_column_set_clickable (tree_column, TRUE);*/
- if (tree_column->reorderable == (reorderable?TRUE:FALSE))
+ if (priv->reorderable == (reorderable?TRUE:FALSE))
return;
- tree_column->reorderable = (reorderable?TRUE:FALSE);
+ priv->reorderable = (reorderable?TRUE:FALSE);
gtk_tree_view_column_update_button (tree_column);
g_object_notify (G_OBJECT (tree_column), "reorderable");
}
@@ -2379,7 +2547,7 @@ gtk_tree_view_column_get_reorderable (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
- return tree_column->reorderable;
+ return tree_column->priv->reorderable;
}
@@ -2395,29 +2563,33 @@ void
gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column,
gint sort_column_id)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (sort_column_id >= -1);
- if (tree_column->sort_column_id == sort_column_id)
+ priv = tree_column->priv;
+
+ if (priv->sort_column_id == sort_column_id)
return;
- tree_column->sort_column_id = sort_column_id;
+ priv->sort_column_id = sort_column_id;
/* Handle unsetting the id */
if (sort_column_id == -1)
{
- GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_column->tree_view));
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
- if (tree_column->sort_clicked_signal)
+ if (priv->sort_clicked_signal)
{
- g_signal_handler_disconnect (tree_column, tree_column->sort_clicked_signal);
- tree_column->sort_clicked_signal = 0;
+ g_signal_handler_disconnect (tree_column, priv->sort_clicked_signal);
+ priv->sort_clicked_signal = 0;
}
- if (tree_column->sort_column_changed_signal)
+ if (priv->sort_column_changed_signal)
{
- g_signal_handler_disconnect (model, tree_column->sort_column_changed_signal);
- tree_column->sort_column_changed_signal = 0;
+ g_signal_handler_disconnect (model, priv->sort_column_changed_signal);
+ priv->sort_column_changed_signal = 0;
}
gtk_tree_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING);
@@ -2429,11 +2601,11 @@ gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column,
gtk_tree_view_column_set_clickable (tree_column, TRUE);
- if (! tree_column->sort_clicked_signal)
- tree_column->sort_clicked_signal = g_signal_connect (tree_column,
- "clicked",
- G_CALLBACK (gtk_tree_view_column_sort),
- NULL);
+ if (! priv->sort_clicked_signal)
+ priv->sort_clicked_signal = g_signal_connect (tree_column,
+ "clicked",
+ G_CALLBACK (gtk_tree_view_column_sort),
+ NULL);
gtk_tree_view_column_setup_sort_column_id_callback (tree_column);
g_object_notify (G_OBJECT (tree_column), "sort-column-id");
@@ -2455,7 +2627,7 @@ gtk_tree_view_column_get_sort_column_id (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
- return tree_column->sort_column_id;
+ return tree_column->priv->sort_column_id;
}
/**
@@ -2477,10 +2649,10 @@ gtk_tree_view_column_set_sort_indicator (GtkTreeViewColumn *tree_column,
setting = setting != FALSE;
- if (setting == tree_column->show_sort_indicator)
+ if (setting == tree_column->priv->show_sort_indicator)
return;
- tree_column->show_sort_indicator = setting;
+ tree_column->priv->show_sort_indicator = setting;
gtk_tree_view_column_update_button (tree_column);
g_object_notify (G_OBJECT (tree_column), "sort-indicator");
}
@@ -2498,7 +2670,7 @@ gtk_tree_view_column_get_sort_indicator (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
- return tree_column->show_sort_indicator;
+ return tree_column->priv->show_sort_indicator;
}
/**
@@ -2524,10 +2696,10 @@ gtk_tree_view_column_set_sort_order (GtkTreeViewColumn *tree_column,
{
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- if (order == tree_column->sort_order)
+ if (order == tree_column->priv->sort_order)
return;
- tree_column->sort_order = order;
+ tree_column->priv->sort_order = order;
gtk_tree_view_column_update_button (tree_column);
g_object_notify (G_OBJECT (tree_column), "sort-order");
}
@@ -2545,7 +2717,7 @@ gtk_tree_view_column_get_sort_order (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
- return tree_column->sort_order;
+ return tree_column->priv->sort_order;
}
/**
@@ -2568,48 +2740,13 @@ gtk_tree_view_column_cell_set_cell_data (GtkTreeViewColumn *tree_column,
gboolean is_expander,
gboolean is_expanded)
{
- GSList *list;
- GValue value = { 0, };
- GList *cell_list;
- gboolean cell_is_expander, cell_is_expanded;
-
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
if (tree_model == NULL)
return;
- for (cell_list = tree_column->cell_list; cell_list; cell_list = cell_list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) cell_list->data;
- GObject *cell = (GObject *) info->cell;
-
- list = info->attributes;
-
- g_object_freeze_notify (cell);
-
- g_object_get (cell, "is-expander", &cell_is_expander, NULL);
- if (cell_is_expander != is_expander)
- g_object_set (cell, "is-expander", is_expander, NULL);
-
- g_object_get (cell, "is-expanded", &cell_is_expanded, NULL);
- if (cell_is_expanded != is_expanded)
- g_object_set (cell, "is-expanded", is_expanded, NULL);
-
- while (list && list->next)
- {
- gtk_tree_model_get_value (tree_model, iter,
- GPOINTER_TO_INT (list->next->data),
- &value);
- g_object_set_property (cell, (gchar *) list->data, &value);
- g_value_unset (&value);
- list = list->next->next;
- }
-
- if (info->func)
- (* info->func) (tree_column, info->cell, tree_model, iter, info->func_data);
- g_object_thaw_notify (G_OBJECT (info->cell));
- }
-
+ gtk_cell_area_apply_attributes (tree_column->priv->cell_area, tree_model, iter,
+ is_expander, is_expanded);
}
/**
@@ -2632,528 +2769,39 @@ gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column,
gint *width,
gint *height)
{
- GtkRequisition min_size;
- GList *list;
- gboolean first_cell = TRUE;
- gint focus_line_width;
+ GtkTreeViewColumnPrivate *priv;
+ gint min_width = 0, min_height = 0;
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- if (height)
- * height = 0;
- if (width)
- * width = 0;
+ priv = tree_column->priv;
- gtk_widget_style_get (tree_column->tree_view, "focus-line-width", &focus_line_width, NULL);
-
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
- gboolean visible;
- g_object_get (info->cell, "visible", &visible, NULL);
-
- if (visible == FALSE)
- continue;
-
- if (first_cell == FALSE && width)
- *width += tree_column->spacing;
-
- gtk_cell_renderer_get_preferred_size (info->cell,
- GTK_WIDGET (tree_column->tree_view),
- &min_size, NULL);
-
- if (height)
- * height = MAX (*height, min_size.height + focus_line_width * 2);
- info->requested_width = MAX (info->requested_width, min_size.width + focus_line_width * 2);
- if (width)
- * width += info->requested_width;
- first_cell = FALSE;
- }
-}
+ g_signal_handler_block (priv->cell_area_context,
+ priv->context_changed_signal);
-/* rendering, event handling and rendering focus are somewhat complicated, and
- * quite a bit of code. Rather than duplicate them, we put them together to
- * keep the code in one place.
- *
- * To better understand what's going on, check out
- * docs/tree-column-sizing.png
- */
-enum {
- CELL_ACTION_RENDER,
- CELL_ACTION_FOCUS,
- CELL_ACTION_EVENT
-};
+ gtk_cell_area_get_preferred_width (priv->cell_area,
+ priv->cell_area_context,
+ priv->tree_view,
+ NULL, NULL);
-static gboolean
-gtk_tree_view_column_cell_process_action (GtkTreeViewColumn *tree_column,
- cairo_t *cr,
- const GdkRectangle *background_area,
- const GdkRectangle *cell_area,
- guint flags,
- gint action,
- GdkRectangle *focus_rectangle, /* FOCUS */
- GtkCellEditable **editable_widget, /* EVENT */
- GdkEvent *event, /* EVENT */
- gchar *path_string) /* EVENT */
-{
- GList *list;
- GdkRectangle real_cell_area;
- GdkRectangle real_background_area;
- gint depth = 0;
- gint expand_cell_count = 0;
- gint full_requested_width = 0;
- gint extra_space;
- gint min_x, min_y, max_x, max_y;
- gint focus_line_width;
- gint special_cells;
- gint horizontal_separator;
- gboolean cursor_row = FALSE;
- gboolean first_cell = TRUE;
- gboolean rtl;
- /* If we have rtl text, we need to transform our areas */
- GdkRectangle rtl_cell_area;
- GdkRectangle rtl_background_area;
-
- min_x = G_MAXINT;
- min_y = G_MAXINT;
- max_x = 0;
- max_y = 0;
-
- rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL);
- special_cells = _gtk_tree_view_column_count_special_cells (tree_column);
-
- if (special_cells > 1 && action == CELL_ACTION_FOCUS)
- {
- GtkTreeViewColumnCellInfo *info = NULL;
- gboolean found_has_focus = FALSE;
-
- /* one should have focus */
- for (list = tree_column->cell_list; list; list = list->next)
- {
- info = list->data;
- if (info && info->has_focus)
- {
- found_has_focus = TRUE;
- break;
- }
- }
-
- if (!found_has_focus)
- {
- /* give the first one focus */
- info = gtk_tree_view_column_cell_first (tree_column)->data;
- info->has_focus = TRUE;
- }
- }
-
- cursor_row = flags & GTK_CELL_RENDERER_FOCUSED;
-
- gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
- "focus-line-width", &focus_line_width,
- "horizontal-separator", &horizontal_separator,
- NULL);
-
- real_cell_area = *cell_area;
- real_background_area = *background_area;
-
-
- real_cell_area.x += focus_line_width;
- real_cell_area.y += focus_line_width;
- real_cell_area.height -= 2 * focus_line_width;
-
- if (rtl)
- depth = real_background_area.width - real_cell_area.width;
- else
- depth = real_cell_area.x - real_background_area.x;
-
- /* Find out how much extra space we have to allocate */
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *)list->data;
-
- if (!gtk_cell_renderer_get_visible (info->cell))
- continue;
-
- if (info->expand == TRUE)
- expand_cell_count ++;
- full_requested_width += info->requested_width;
-
- if (!first_cell)
- full_requested_width += tree_column->spacing;
-
- first_cell = FALSE;
- }
-
- extra_space = cell_area->width - full_requested_width;
- if (extra_space < 0)
- extra_space = 0;
- else if (extra_space > 0 && expand_cell_count > 0)
- extra_space /= expand_cell_count;
-
- /* iterate list for GTK_PACK_START cells */
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
-
- if (info->pack == GTK_PACK_END)
- continue;
-
- if (!gtk_cell_renderer_get_visible (info->cell))
- continue;
-
- if ((info->has_focus || special_cells == 1) && cursor_row)
- flags |= GTK_CELL_RENDERER_FOCUSED;
- else
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
- info->real_width = info->requested_width + (info->expand?extra_space:0);
-
- /* We constrain ourselves to only the width available */
- if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
- {
- info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
- }
-
- if (real_cell_area.x > cell_area->x + cell_area->width)
- break;
-
- real_cell_area.width = info->real_width;
- real_cell_area.width -= 2 * focus_line_width;
-
- if (list->next)
- {
- real_background_area.width = info->real_width + depth;
- }
- else
- {
- /* fill the rest of background for the last cell */
- real_background_area.width = background_area->x + background_area->width - real_background_area.x;
- }
-
- rtl_cell_area = real_cell_area;
- rtl_background_area = real_background_area;
-
- if (rtl)
- {
- rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
- rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
- }
-
- /* RENDER */
- if (action == CELL_ACTION_RENDER)
- {
- gtk_cell_renderer_render (info->cell,
- cr,
- tree_column->tree_view,
- &rtl_background_area,
- &rtl_cell_area,
- flags);
- }
- /* FOCUS */
- else if (action == CELL_ACTION_FOCUS)
- {
- gint x_offset, y_offset;
- GtkRequisition min_size;
-
- gtk_cell_renderer_get_preferred_size (info->cell,
- tree_column->tree_view,
- &min_size, NULL);
-
- _gtk_cell_renderer_calc_offset (info->cell, &rtl_cell_area,
- gtk_widget_get_direction (tree_column->tree_view),
- min_size.width, min_size.height,
- &x_offset, &y_offset);
-
- if (special_cells > 1)
- {
- if (info->has_focus)
- {
- min_x = rtl_cell_area.x + x_offset;
- max_x = min_x + min_size.width;
- min_y = rtl_cell_area.y + y_offset;
- max_y = min_y + min_size.height;
- }
- }
- else
- {
- if (min_x > (rtl_cell_area.x + x_offset))
- min_x = rtl_cell_area.x + x_offset;
- if (max_x < rtl_cell_area.x + x_offset + min_size.width)
- max_x = rtl_cell_area.x + x_offset + min_size.width;
- if (min_y > (rtl_cell_area.y + y_offset))
- min_y = rtl_cell_area.y + y_offset;
- if (max_y < rtl_cell_area.y + y_offset + min_size.height)
- max_y = rtl_cell_area.y + y_offset + min_size.height;
- }
- }
- /* EVENT */
- else if (action == CELL_ACTION_EVENT)
- {
- gboolean try_event = FALSE;
-
- if (event)
- {
- if (special_cells == 1)
- {
- /* only 1 activatable cell -> whole column can activate */
- if (cell_area->x <= ((GdkEventButton *)event)->x &&
- cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
- try_event = TRUE;
- }
- else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
- rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
- /* only activate cell if the user clicked on an individual
- * cell
- */
- try_event = TRUE;
- }
- else if (special_cells > 1 && info->has_focus)
- try_event = TRUE;
- else if (special_cells == 1)
- try_event = TRUE;
-
- if (try_event)
- {
- gboolean visible, mode;
-
- g_object_get (info->cell,
- "visible", &visible,
- "mode", &mode,
- NULL);
- if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
- {
- if (gtk_cell_renderer_activate (info->cell,
- event,
- tree_column->tree_view,
- path_string,
- &rtl_background_area,
- &rtl_cell_area,
- flags))
- {
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
- return TRUE;
- }
- }
- else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
- {
- *editable_widget =
- gtk_cell_renderer_start_editing (info->cell,
- event,
- tree_column->tree_view,
- path_string,
- &rtl_background_area,
- &rtl_cell_area,
- flags);
-
- if (*editable_widget != NULL)
- {
- g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
- info->in_editing_mode = TRUE;
- gtk_tree_view_column_focus_cell (tree_column, info->cell);
-
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
- return TRUE;
- }
- }
- }
- }
-
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
- real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
- real_background_area.x += real_background_area.width + tree_column->spacing;
-
- /* Only needed for first cell */
- depth = 0;
- }
-
- /* iterate list for PACK_END cells */
- for (list = g_list_last (tree_column->cell_list); list; list = list->prev)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
-
- if (info->pack == GTK_PACK_START)
- continue;
-
- if (!gtk_cell_renderer_get_visible(info->cell))
- continue;
-
- if ((info->has_focus || special_cells == 1) && cursor_row)
- flags |= GTK_CELL_RENDERER_FOCUSED;
- else
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
- info->real_width = info->requested_width + (info->expand?extra_space:0);
-
- /* We constrain ourselves to only the width available */
- if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
- {
- info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
- }
-
- if (real_cell_area.x > cell_area->x + cell_area->width)
- break;
-
- real_cell_area.width = info->real_width;
- real_cell_area.width -= 2 * focus_line_width;
- real_background_area.width = info->real_width + depth;
+ gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &min_width, NULL);
- rtl_cell_area = real_cell_area;
- rtl_background_area = real_background_area;
- if (rtl)
- {
- rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
- rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
- }
-
- /* RENDER */
- if (action == CELL_ACTION_RENDER)
- {
- gtk_cell_renderer_render (info->cell,
- cr,
- tree_column->tree_view,
- &rtl_background_area,
- &rtl_cell_area,
- flags);
- }
- /* FOCUS */
- else if (action == CELL_ACTION_FOCUS)
- {
- gint x_offset, y_offset;
- GtkRequisition min_size;
-
- gtk_cell_renderer_get_preferred_size (info->cell,
- tree_column->tree_view,
- &min_size, NULL);
-
- _gtk_cell_renderer_calc_offset (info->cell, &rtl_cell_area,
- gtk_widget_get_direction (tree_column->tree_view),
- min_size.width, min_size.height,
- &x_offset, &y_offset);
-
- if (special_cells > 1)
- {
- if (info->has_focus)
- {
- min_x = rtl_cell_area.x + x_offset;
- max_x = min_x + min_size.width;
- min_y = rtl_cell_area.y + y_offset;
- max_y = min_y + min_size.height;
- }
- }
- else
- {
- if (min_x > (rtl_cell_area.x + x_offset))
- min_x = rtl_cell_area.x + x_offset;
- if (max_x < rtl_cell_area.x + x_offset + min_size.width)
- max_x = rtl_cell_area.x + x_offset + min_size.width;
- if (min_y > (rtl_cell_area.y + y_offset))
- min_y = rtl_cell_area.y + y_offset;
- if (max_y < rtl_cell_area.y + y_offset + min_size.height)
- max_y = rtl_cell_area.y + y_offset + min_size.height;
- }
- }
- /* EVENT */
- else if (action == CELL_ACTION_EVENT)
- {
- gboolean try_event = FALSE;
+ gtk_cell_area_get_preferred_height_for_width (priv->cell_area,
+ priv->cell_area_context,
+ priv->tree_view,
+ min_width,
+ &min_height,
+ NULL);
- if (event)
- {
- if (special_cells == 1)
- {
- /* only 1 activatable cell -> whole column can activate */
- if (cell_area->x <= ((GdkEventButton *)event)->x &&
- cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
- try_event = TRUE;
- }
- else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
- rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
- /* only activate cell if the user clicked on an individual
- * cell
- */
- try_event = TRUE;
- }
- else if (special_cells > 1 && info->has_focus)
- try_event = TRUE;
- else if (special_cells == 1)
- try_event = TRUE;
+ g_signal_handler_unblock (priv->cell_area_context,
+ priv->context_changed_signal);
- if (try_event)
- {
- gboolean visible, mode;
-
- g_object_get (info->cell,
- "visible", &visible,
- "mode", &mode,
- NULL);
- if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
- {
- if (gtk_cell_renderer_activate (info->cell,
- event,
- tree_column->tree_view,
- path_string,
- &rtl_background_area,
- &rtl_cell_area,
- flags))
- {
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
- return TRUE;
- }
- }
- else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
- {
- *editable_widget =
- gtk_cell_renderer_start_editing (info->cell,
- event,
- tree_column->tree_view,
- path_string,
- &rtl_background_area,
- &rtl_cell_area,
- flags);
-
- if (*editable_widget != NULL)
- {
- g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
- info->in_editing_mode = TRUE;
- gtk_tree_view_column_focus_cell (tree_column, info->cell);
-
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
- return TRUE;
- }
- }
- }
- }
- flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
- real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
- real_background_area.x += (real_background_area.width + tree_column->spacing);
-
- /* Only needed for first cell */
- depth = 0;
- }
-
- /* fill focus_rectangle when required */
- if (action == CELL_ACTION_FOCUS)
- {
- if (min_x >= max_x || min_y >= max_y)
- {
- *focus_rectangle = *cell_area;
- /* don't change the focus_rectangle, just draw it nicely inside
- * the cell area */
- }
- else
- {
- focus_rectangle->x = min_x - focus_line_width;
- focus_rectangle->y = min_y - focus_line_width;
- focus_rectangle->width = (max_x - min_x) + 2 * focus_line_width;
- focus_rectangle->height = (max_y - min_y) + 2 * focus_line_width;
- }
- }
+ if (height)
+ * height = min_height;
+ if (width)
+ * width = min_width;
- return FALSE;
}
/**
@@ -3172,371 +2820,46 @@ _gtk_tree_view_column_cell_render (GtkTreeViewColumn *tree_column,
cairo_t *cr,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
- guint flags)
+ guint flags,
+ gboolean draw_focus)
{
+ GtkTreeViewColumnPrivate *priv;
+
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (cr != NULL);
g_return_if_fail (background_area != NULL);
g_return_if_fail (cell_area != NULL);
+ priv = tree_column->priv;
+
cairo_save (cr);
- gtk_tree_view_column_cell_process_action (tree_column,
- cr,
- background_area,
- cell_area,
- flags,
- CELL_ACTION_RENDER,
- NULL, NULL, NULL, NULL);
+ gtk_cell_area_render (priv->cell_area, priv->cell_area_context,
+ priv->tree_view, cr,
+ background_area, cell_area, flags,
+ draw_focus);
cairo_restore (cr);
}
gboolean
_gtk_tree_view_column_cell_event (GtkTreeViewColumn *tree_column,
- GtkCellEditable **editable_widget,
GdkEvent *event,
- gchar *path_string,
- const GdkRectangle *background_area,
const GdkRectangle *cell_area,
guint flags)
{
- g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
-
- return gtk_tree_view_column_cell_process_action (tree_column,
- NULL,
- background_area,
- cell_area,
- flags,
- CELL_ACTION_EVENT,
- NULL,
- editable_widget,
- event,
- path_string);
-}
-
-void
-_gtk_tree_view_column_get_focus_area (GtkTreeViewColumn *tree_column,
- const GdkRectangle *background_area,
- const GdkRectangle *cell_area,
- GdkRectangle *focus_area)
-{
- gtk_tree_view_column_cell_process_action (tree_column,
- NULL,
- background_area,
- cell_area,
- 0,
- CELL_ACTION_FOCUS,
- focus_area,
- NULL, NULL, NULL);
-}
-
-
-/* cell list manipulation */
-static GList *
-gtk_tree_view_column_cell_first (GtkTreeViewColumn *tree_column)
-{
- GList *list = tree_column->cell_list;
-
- /* first GTK_PACK_START cell we find */
- for ( ; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = list->data;
- if (info->pack == GTK_PACK_START)
- return list;
- }
-
- /* hmm, else the *last* GTK_PACK_END cell */
- list = g_list_last (tree_column->cell_list);
-
- for ( ; list; list = list->prev)
- {
- GtkTreeViewColumnCellInfo *info = list->data;
- if (info->pack == GTK_PACK_END)
- return list;
- }
-
- return NULL;
-}
-
-static GList *
-gtk_tree_view_column_cell_last (GtkTreeViewColumn *tree_column)
-{
- GList *list = tree_column->cell_list;
-
- /* *first* GTK_PACK_END cell we find */
- for ( ; list ; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = list->data;
- if (info->pack == GTK_PACK_END)
- return list;
- }
-
- /* hmm, else the last GTK_PACK_START cell */
- list = g_list_last (tree_column->cell_list);
+ GtkTreeViewColumnPrivate *priv;
- for ( ; list; list = list->prev)
- {
- GtkTreeViewColumnCellInfo *info = list->data;
- if (info->pack == GTK_PACK_START)
- return list;
- }
-
- return NULL;
-}
-
-static GList *
-gtk_tree_view_column_cell_next (GtkTreeViewColumn *tree_column,
- GList *current)
-{
- GList *list;
- GtkTreeViewColumnCellInfo *info = current->data;
-
- if (info->pack == GTK_PACK_START)
- {
- for (list = current->next; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *inf = list->data;
- if (inf->pack == GTK_PACK_START)
- return list;
- }
-
- /* out of GTK_PACK_START cells, get *last* GTK_PACK_END one */
- list = g_list_last (tree_column->cell_list);
- for (; list; list = list->prev)
- {
- GtkTreeViewColumnCellInfo *inf = list->data;
- if (inf->pack == GTK_PACK_END)
- return list;
- }
- }
-
- for (list = current->prev; list; list = list->prev)
- {
- GtkTreeViewColumnCellInfo *inf = list->data;
- if (inf->pack == GTK_PACK_END)
- return list;
- }
-
- return NULL;
-}
-
-static GList *
-gtk_tree_view_column_cell_prev (GtkTreeViewColumn *tree_column,
- GList *current)
-{
- GList *list;
- GtkTreeViewColumnCellInfo *info = current->data;
-
- if (info->pack == GTK_PACK_END)
- {
- for (list = current->next; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *inf = list->data;
- if (inf->pack == GTK_PACK_END)
- return list;
- }
-
- /* out of GTK_PACK_END, get last GTK_PACK_START one */
- list = g_list_last (tree_column->cell_list);
- for ( ; list; list = list->prev)
- {
- GtkTreeViewColumnCellInfo *inf = list->data;
- if (inf->pack == GTK_PACK_START)
- return list;
- }
- }
-
- for (list = current->prev; list; list = list->prev)
- {
- GtkTreeViewColumnCellInfo *inf = list->data;
- if (inf->pack == GTK_PACK_START)
- return list;
- }
-
- return NULL;
-}
-
-gboolean
-_gtk_tree_view_column_cell_focus (GtkTreeViewColumn *tree_column,
- gint direction,
- gboolean left,
- gboolean right)
-{
- gint count;
- gboolean rtl;
-
- count = _gtk_tree_view_column_count_special_cells (tree_column);
- rtl = gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL;
-
- /* if we are the current focus column and have multiple editable cells,
- * try to select the next one, else move the focus to the next column
- */
- if (GTK_TREE_VIEW (tree_column->tree_view)->priv->focus_column == tree_column)
- {
- if (count > 1)
- {
- GList *next, *prev;
- GList *list = tree_column->cell_list;
- GtkTreeViewColumnCellInfo *info = NULL;
-
- /* find current focussed cell */
- for ( ; list; list = list->next)
- {
- info = list->data;
- if (info->has_focus)
- break;
- }
-
- /* not a focussed cell in the focus column? */
- if (!list || !info || !info->has_focus)
- return FALSE;
-
- if (rtl)
- {
- prev = gtk_tree_view_column_cell_next (tree_column, list);
- next = gtk_tree_view_column_cell_prev (tree_column, list);
- }
- else
- {
- next = gtk_tree_view_column_cell_next (tree_column, list);
- prev = gtk_tree_view_column_cell_prev (tree_column, list);
- }
-
- info->has_focus = FALSE;
- if (direction > 0 && next)
- {
- info = next->data;
- info->has_focus = TRUE;
- return TRUE;
- }
- else if (direction > 0 && !next && !right)
- {
- /* keep focus on last cell */
- if (rtl)
- info = gtk_tree_view_column_cell_first (tree_column)->data;
- else
- info = gtk_tree_view_column_cell_last (tree_column)->data;
-
- info->has_focus = TRUE;
- return TRUE;
- }
- else if (direction < 0 && prev)
- {
- info = prev->data;
- info->has_focus = TRUE;
- return TRUE;
- }
- else if (direction < 0 && !prev && !left)
- {
- /* keep focus on first cell */
- if (rtl)
- info = gtk_tree_view_column_cell_last (tree_column)->data;
- else
- info = gtk_tree_view_column_cell_first (tree_column)->data;
-
- info->has_focus = TRUE;
- return TRUE;
- }
- }
- return FALSE;
- }
-
- /* we get focus, if we have multiple editable cells, give the correct one
- * focus
- */
- if (count > 1)
- {
- GList *list = tree_column->cell_list;
-
- /* clear focus first */
- for ( ; list ; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = list->data;
- if (info->has_focus)
- info->has_focus = FALSE;
- }
-
- list = NULL;
- if (rtl)
- {
- if (direction > 0)
- list = gtk_tree_view_column_cell_last (tree_column);
- else if (direction < 0)
- list = gtk_tree_view_column_cell_first (tree_column);
- }
- else
- {
- if (direction > 0)
- list = gtk_tree_view_column_cell_first (tree_column);
- else if (direction < 0)
- list = gtk_tree_view_column_cell_last (tree_column);
- }
-
- if (list)
- ((GtkTreeViewColumnCellInfo *) list->data)->has_focus = TRUE;
- }
-
- return TRUE;
-}
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
-void
-_gtk_tree_view_column_cell_draw_focus (GtkTreeViewColumn *tree_column,
- cairo_t *cr,
- const GdkRectangle *background_area,
- const GdkRectangle *cell_area,
- guint flags)
-{
- gint focus_line_width;
- GtkStateType cell_state;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- g_return_if_fail (cr != NULL);
+ priv = tree_column->priv;
- gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
- "focus-line-width", &focus_line_width, NULL);
- if (tree_column->editable_widget)
- {
- /* This function is only called on the editable row when editing.
- */
-#if 0
- gtk_paint_focus (tree_column->tree_view->style,
- window,
- gtk_widget_get_state (tree_column->tree_view),
- NULL,
- tree_column->tree_view,
- "treeview",
- cell_area->x - focus_line_width,
- cell_area->y - focus_line_width,
- cell_area->width + 2 * focus_line_width,
- cell_area->height + 2 * focus_line_width);
-#endif
- }
- else
- {
- GdkRectangle focus_rectangle;
- gtk_tree_view_column_cell_process_action (tree_column,
- cr,
- background_area,
- cell_area,
- flags,
- CELL_ACTION_FOCUS,
- &focus_rectangle,
- NULL, NULL, NULL);
-
- cell_state = flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
- (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
- (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
- gtk_paint_focus (gtk_widget_get_style (tree_column->tree_view),
- cr,
- cell_state,
- tree_column->tree_view,
- "treeview",
- focus_rectangle.x,
- focus_rectangle.y,
- focus_rectangle.width,
- focus_rectangle.height);
- }
+ return gtk_cell_area_event (priv->cell_area,
+ priv->cell_area_context,
+ priv->tree_view,
+ event,
+ cell_area,
+ flags);
}
/**
@@ -3553,17 +2876,25 @@ gboolean
gtk_tree_view_column_cell_is_visible (GtkTreeViewColumn *tree_column)
{
GList *list;
+ GList *cells;
+ GtkTreeViewColumnPrivate *priv;
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+ priv = tree_column->priv;
- if (gtk_cell_renderer_get_visible (info->cell))
- return TRUE;
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->cell_area));
+ for (list = cells; list; list = list->next)
+ {
+ if (gtk_cell_renderer_get_visible (list->data))
+ {
+ g_list_free (cells);
+ return TRUE;
+ }
}
+ g_list_free (cells);
+
return FALSE;
}
@@ -3581,140 +2912,50 @@ void
gtk_tree_view_column_focus_cell (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell)
{
- GList *list;
- gboolean found_cell = FALSE;
-
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
- if (_gtk_tree_view_column_count_special_cells (tree_column) < 2)
- return;
-
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = list->data;
-
- if (info->cell == cell)
- {
- info->has_focus = TRUE;
- found_cell = TRUE;
- break;
- }
- }
-
- if (found_cell)
- {
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = list->data;
-
- if (info->cell != cell)
- info->has_focus = FALSE;
- }
-
- /* FIXME: redraw? */
- }
+ gtk_cell_area_set_focus_cell (tree_column->priv->cell_area, cell);
}
void
_gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column,
gboolean install_handler)
{
- GList *list;
+ GtkTreeViewColumnPrivate *priv = tree_column->priv;
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+ priv->dirty = TRUE;
+ priv->padding = 0;
+ priv->width = 0;
- info->requested_width = 0;
- }
- tree_column->dirty = TRUE;
- tree_column->requested_width = -1;
- tree_column->width = 0;
+ /* Issue a manual reset on the context to have all
+ * sizes re-requested for the context.
+ */
+ g_signal_handler_block (priv->cell_area_context,
+ priv->context_changed_signal);
+ gtk_cell_area_context_reset (priv->cell_area_context);
+ g_signal_handler_unblock (priv->cell_area_context,
+ priv->context_changed_signal);
- if (tree_column->tree_view &&
- gtk_widget_get_realized (tree_column->tree_view))
+ if (priv->tree_view &&
+ gtk_widget_get_realized (priv->tree_view))
{
- if (install_handler)
- _gtk_tree_view_install_mark_rows_col_dirty (GTK_TREE_VIEW (tree_column->tree_view));
- else
- GTK_TREE_VIEW (tree_column->tree_view)->priv->mark_rows_col_dirty = TRUE;
- gtk_widget_queue_resize (tree_column->tree_view);
+ _gtk_tree_view_install_mark_rows_col_dirty (GTK_TREE_VIEW (priv->tree_view), install_handler);
+ gtk_widget_queue_resize (priv->tree_view);
}
}
-void
-_gtk_tree_view_column_start_editing (GtkTreeViewColumn *tree_column,
- GtkCellEditable *cell_editable)
-{
- g_return_if_fail (tree_column->editable_widget == NULL);
-
- tree_column->editable_widget = cell_editable;
-}
-
-void
-_gtk_tree_view_column_stop_editing (GtkTreeViewColumn *tree_column)
+gboolean
+_gtk_tree_view_column_cell_get_dirty (GtkTreeViewColumn *tree_column)
{
- GList *list;
-
- g_return_if_fail (tree_column->editable_widget != NULL);
-
- tree_column->editable_widget = NULL;
- for (list = tree_column->cell_list; list; list = list->next)
- ((GtkTreeViewColumnCellInfo *)list->data)->in_editing_mode = FALSE;
-}
-
-void
-_gtk_tree_view_column_get_neighbor_sizes (GtkTreeViewColumn *column,
- GtkCellRenderer *cell,
- gint *left,
- gint *right)
-{
- GList *list;
- GtkTreeViewColumnCellInfo *info;
- gint l, r;
- gboolean rtl;
-
- l = r = 0;
-
- list = gtk_tree_view_column_cell_first (column);
-
- while (list)
- {
- info = (GtkTreeViewColumnCellInfo *)list->data;
-
- list = gtk_tree_view_column_cell_next (column, list);
-
- if (info->cell == cell)
- break;
-
- if (gtk_cell_renderer_get_visible (info->cell))
- l += info->real_width + column->spacing;
- }
-
- while (list)
- {
- info = (GtkTreeViewColumnCellInfo *)list->data;
-
- list = gtk_tree_view_column_cell_next (column, list);
-
- if (gtk_cell_renderer_get_visible (info->cell))
- r += info->real_width + column->spacing;
- }
-
- rtl = (gtk_widget_get_direction (GTK_WIDGET (column->tree_view)) == GTK_TEXT_DIR_RTL);
- if (left)
- *left = rtl ? r : l;
-
- if (right)
- *right = rtl ? l : r;
+ return tree_column->priv->dirty;
}
/**
* gtk_tree_view_column_cell_get_position:
* @tree_column: a #GtkTreeViewColumn
* @cell_renderer: a #GtkCellRenderer
- * @start_pos: return location for the horizontal position of @cell within
+ * @x_offset: return location for the horizontal position of @cell within
* @tree_column, may be %NULL
* @width: return location for the width of @cell, may be %NULL
*
@@ -3727,37 +2968,32 @@ _gtk_tree_view_column_get_neighbor_sizes (GtkTreeViewColumn *column,
gboolean
gtk_tree_view_column_cell_get_position (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell_renderer,
- gint *start_pos,
+ gint *x_offset,
gint *width)
{
- GList *list;
- gint current_x = 0;
- gboolean found_cell = FALSE;
- GtkTreeViewColumnCellInfo *cellinfo = NULL;
+ GtkTreeViewColumnPrivate *priv;
+ GdkRectangle zero_cell_area = { 0, };
+ GdkRectangle allocation;
- list = gtk_tree_view_column_cell_first (tree_column);
- for (; list; list = gtk_tree_view_column_cell_next (tree_column, list))
- {
- cellinfo = list->data;
- if (cellinfo->cell == cell_renderer)
- {
- found_cell = TRUE;
- break;
- }
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell_renderer), FALSE);
- if (gtk_cell_renderer_get_visible (cellinfo->cell))
- current_x += cellinfo->real_width;
- }
+ priv = tree_column->priv;
- if (found_cell)
- {
- if (start_pos)
- *start_pos = current_x;
- if (width)
- *width = cellinfo->real_width;
- }
+ /* FIXME: Could use a boolean return value for invalid cells */
+ gtk_cell_area_get_cell_allocation (priv->cell_area,
+ priv->cell_area_context,
+ priv->tree_view,
+ cell_renderer,
+ &zero_cell_area,
+ &allocation);
+
+ if (x_offset)
+ *x_offset = allocation.x;
+ if (width)
+ *width = allocation.width;
- return found_cell;
+ return TRUE;
}
/**
@@ -3774,7 +3010,7 @@ gtk_tree_view_column_queue_resize (GtkTreeViewColumn *tree_column)
{
g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- if (tree_column->tree_view)
+ if (tree_column->priv->tree_view)
_gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
}
@@ -3796,5 +3032,85 @@ gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column)
{
g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
- return tree_column->tree_view;
+ return tree_column->priv->tree_view;
+}
+
+/**
+ * gtk_tree_view_column_get_button:
+ * @tree_column: A #GtkTreeViewColumn
+ *
+ * Returns the button used in the treeview column header
+ *
+ * Return value: (transfer none): The button for the column header.
+ *
+ * Since: 3.0
+ */
+GtkWidget *
+gtk_tree_view_column_get_button (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
+
+ return tree_column->priv->button;
+}
+
+GdkWindow *
+_gtk_tree_view_column_get_window (GtkTreeViewColumn *column)
+{
+ return column->priv->window;
+}
+
+void
+_gtk_tree_view_column_push_padding (GtkTreeViewColumn *column,
+ gint padding)
+{
+ column->priv->padding = MAX (column->priv->padding, padding);
+}
+
+gint
+_gtk_tree_view_column_get_requested_width (GtkTreeViewColumn *column)
+{
+ gint requested_width;
+
+ gtk_cell_area_context_get_preferred_width (column->priv->cell_area_context, &requested_width, NULL);
+
+ return requested_width + column->priv->padding;
+}
+
+
+void
+_gtk_tree_view_column_set_resized_width (GtkTreeViewColumn *column,
+ gint width)
+{
+ column->priv->resized_width = width;
+}
+
+gint
+_gtk_tree_view_column_get_resized_width (GtkTreeViewColumn *column)
+{
+ return column->priv->resized_width;
+}
+
+void
+_gtk_tree_view_column_set_use_resized_width (GtkTreeViewColumn *column,
+ gboolean use_resized_width)
+{
+ column->priv->use_resized_width = use_resized_width;
+}
+
+gboolean
+_gtk_tree_view_column_get_use_resized_width (GtkTreeViewColumn *column)
+{
+ return column->priv->use_resized_width;
+}
+
+gint
+_gtk_tree_view_column_get_drag_x (GtkTreeViewColumn *column)
+{
+ return column->priv->drag_x;
+}
+
+GtkCellAreaContext *
+_gtk_tree_view_column_get_context (GtkTreeViewColumn *column)
+{
+ return column->priv->cell_area_context;
}
diff --git a/gtk/gtktreeviewcolumn.h b/gtk/gtktreeviewcolumn.h
index 6c0b3a1e88..f964177283 100644
--- a/gtk/gtktreeviewcolumn.h
+++ b/gtk/gtktreeviewcolumn.h
@@ -27,6 +27,7 @@
#include <gtk/gtkcellrenderer.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreesortable.h>
+#include <gtk/gtkcellarea.h>
G_BEGIN_DECLS
@@ -39,8 +40,9 @@ G_BEGIN_DECLS
#define GTK_IS_TREE_VIEW_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_VIEW_COLUMN))
#define GTK_TREE_VIEW_COLUMN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_VIEW_COLUMN, GtkTreeViewColumnClass))
-typedef struct _GtkTreeViewColumn GtkTreeViewColumn;
-typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass;
+typedef struct _GtkTreeViewColumn GtkTreeViewColumn;
+typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass;
+typedef struct _GtkTreeViewColumnPrivate GtkTreeViewColumnPrivate;
/**
* GtkTreeViewColumnSizing:
@@ -85,51 +87,7 @@ struct _GtkTreeViewColumn
{
GInitiallyUnowned parent_instance;
- GtkWidget *GSEAL (tree_view);
- GtkWidget *GSEAL (button);
- GtkWidget *GSEAL (child);
- GtkWidget *GSEAL (arrow);
- GtkWidget *GSEAL (alignment);
- GdkWindow *GSEAL (window);
- GtkCellEditable *GSEAL (editable_widget);
- gfloat GSEAL (xalign);
- gulong GSEAL (property_changed_signal);
- gint GSEAL (spacing);
-
- /* Sizing fields */
- /* see gtk+/doc/tree-column-sizing.txt for more information on them */
- GtkTreeViewColumnSizing GSEAL (column_type);
- gint GSEAL (requested_width);
- gint GSEAL (button_request);
- gint GSEAL (resized_width);
- gint GSEAL (width);
- gint GSEAL (fixed_width);
- gint GSEAL (min_width);
- gint GSEAL (max_width);
-
- /* dragging columns */
- gint GSEAL (drag_x);
- gint GSEAL (drag_y);
-
- gchar *GSEAL (title);
- GList *GSEAL (cell_list);
-
- /* Sorting */
- gulong GSEAL (sort_clicked_signal);
- gulong GSEAL (sort_column_changed_signal);
- gint GSEAL (sort_column_id);
- GtkSortType GSEAL (sort_order);
-
- /* Flags */
- guint GSEAL (visible) : 1;
- guint GSEAL (resizable) : 1;
- guint GSEAL (clickable) : 1;
- guint GSEAL (dirty) : 1;
- guint GSEAL (show_sort_indicator) : 1;
- guint GSEAL (maybe_reordered) : 1;
- guint GSEAL (reorderable) : 1;
- guint GSEAL (use_resized_width) : 1;
- guint GSEAL (expand) : 1;
+ GtkTreeViewColumnPrivate *priv;
};
struct _GtkTreeViewColumnClass
@@ -147,6 +105,7 @@ struct _GtkTreeViewColumnClass
GType gtk_tree_view_column_get_type (void) G_GNUC_CONST;
GtkTreeViewColumn *gtk_tree_view_column_new (void);
+GtkTreeViewColumn *gtk_tree_view_column_new_with_area (GtkCellArea *area);
GtkTreeViewColumn *gtk_tree_view_column_new_with_attributes (const gchar *title,
GtkCellRenderer *cell,
...) G_GNUC_NULL_TERMINATED;
@@ -252,10 +211,11 @@ void gtk_tree_view_column_focus_cell (GtkTreeViewCol
GtkCellRenderer *cell);
gboolean gtk_tree_view_column_cell_get_position (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell_renderer,
- gint *start_pos,
+ gint *x_offset,
gint *width);
void gtk_tree_view_column_queue_resize (GtkTreeViewColumn *tree_column);
GtkWidget *gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column);
+GtkWidget *gtk_tree_view_column_get_button (GtkTreeViewColumn *tree_column);
G_END_DECLS
diff --git a/gtk/tests/treeview.c b/gtk/tests/treeview.c
index 1b3ce9952c..fe42095530 100644
--- a/gtk/tests/treeview.c
+++ b/gtk/tests/treeview.c
@@ -150,6 +150,84 @@ test_select_collapsed_row (void)
gtk_tree_path_free (path);
}
+static gboolean
+test_row_separator_height_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gboolean ret = FALSE;
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (model, iter);
+ if (gtk_tree_path_get_indices (path)[0] == 2)
+ ret = TRUE;
+ gtk_tree_path_free (path);
+
+ return ret;
+}
+
+static void
+test_row_separator_height (void)
+{
+ int focus_pad, separator_height, height;
+ gboolean wide_separators;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkListStore *store;
+ GtkWidget *window;
+ GtkWidget *tree_view;
+ GdkRectangle rect, cell_rect;
+
+ store = gtk_list_store_new (1, G_TYPE_STRING);
+ gtk_list_store_insert_with_values (store, &iter, 0, 0, "Row content", -1);
+ gtk_list_store_insert_with_values (store, &iter, 1, 0, "Row content", -1);
+ gtk_list_store_insert_with_values (store, &iter, 2, 0, "Row content", -1);
+ gtk_list_store_insert_with_values (store, &iter, 3, 0, "Row content", -1);
+ gtk_list_store_insert_with_values (store, &iter, 4, 0, "Row content", -1);
+
+ window = gtk_offscreen_window_new ();
+
+ tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (tree_view),
+ test_row_separator_height_func,
+ NULL,
+ NULL);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view),
+ 0,
+ "Test",
+ gtk_cell_renderer_text_new (),
+ "text", 0,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (window), tree_view);
+ gtk_widget_show_all (window);
+
+
+ path = gtk_tree_path_new_from_indices (2, -1);
+ gtk_tree_view_get_background_area (GTK_TREE_VIEW (tree_view),
+ path, NULL, &rect);
+ gtk_tree_view_get_cell_area (GTK_TREE_VIEW (tree_view),
+ path, NULL, &cell_rect);
+ gtk_tree_path_free (path);
+
+ gtk_widget_style_get (tree_view,
+ "focus-padding", &focus_pad,
+ "wide-separators", &wide_separators,
+ "separator-height", &separator_height,
+ NULL);
+
+ if (wide_separators)
+ height = separator_height + 2 * focus_pad;
+ else
+ height = 2 + 2 * focus_pad;
+
+ g_assert_cmpint (rect.height, ==, height);
+ g_assert_cmpint (cell_rect.height, ==, height);
+
+ gtk_widget_destroy (tree_view);
+}
+
int
main (int argc,
char **argv)
@@ -160,6 +238,8 @@ main (int argc,
g_test_add_func ("/TreeView/cursor/bug-539377", test_bug_539377);
g_test_add_func ("/TreeView/cursor/select-collapsed_row",
test_select_collapsed_row);
+ g_test_add_func ("/TreeView/sizing/row-separator-height",
+ test_row_separator_height);
return g_test_run ();
}
diff --git a/modules/other/gail/gailtreeview.c b/modules/other/gail/gailtreeview.c
index 60de100e3a..f1b506357f 100644
--- a/modules/other/gail/gailtreeview.c
+++ b/modules/other/gail/gailtreeview.c
@@ -4651,7 +4651,7 @@ get_header_from_column (GtkTreeViewColumn *tv_col)
/* If the user has not set a header object, grab the column */
/* header object defined by the GtkTreeView */
- header_widget = tv_col->button;
+ header_widget = gtk_tree_view_column_get_button (tv_col);
if (header_widget)
{
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 50e3a3167f..681e9b3fda 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -87,6 +87,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testtreecolumns \
testtreecolumnsizing \
testtreesort \
+ testverticalcells \
treestoretest \
testxinerama \
testwindows \
@@ -98,6 +99,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testexpander \
testvolumebutton \
testscrolledwindow \
+ testcellarea \
testswitch \
styleexamples
@@ -178,6 +180,7 @@ testtreeflow_DEPENDENCIES = $(DEPS)
testtreecolumns_DEPENDENCIES = $(DEPS)
testtreecolumnsizing_DEPENDENCIES = $(DEPS)
testtreesort_DEPENDENCIES = $(DEPS)
+testverticalcells_DEPENDENCIES = $(DEPS)
treestoretest_DEPENDENCIES = $(TEST_DEPS)
testxinerama_DEPENDENCIES = $(TEST_DEPS)
testmerge_DEPENDENCIES = $(TEST_DEPS)
@@ -186,6 +189,7 @@ testgrouping_DEPENDENCIES = $(TEST_DEPS)
testtooltips_DEPENDENCIES = $(TEST_DEPS)
testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
testscrolledwindow_DEPENDENCIES = $(TEST_DEPS)
+testcellarea_DEPENDENCIES = $(TEST_DEPS)
testwindows_DEPENDENCIES = $(TEST_DEPS)
testexpand_DEPENDENCIES = $(TEST_DEPS)
testexpander_DEPENDENCIES = $(TEST_DEPS)
@@ -255,6 +259,7 @@ testtreeflow_LDADD = $(LDADDS)
testtreecolumns_LDADD = $(LDADDS)
testtreecolumnsizing_LDADD = $(LDADDS)
testtreesort_LDADD = $(LDADDS)
+testverticalcells_LDADD = $(LDADDS)
treestoretest_LDADD = $(LDADDS)
testxinerama_LDADD = $(LDADDS)
testmerge_LDADD = $(LDADDS)
@@ -263,6 +268,7 @@ testgrouping_LDADD = $(LDADDS)
testtooltips_LDADD = $(LDADDS)
testvolumebutton_LDADD = $(LDADDS)
testscrolledwindow_LDADD = $(LDADDS)
+testcellarea_LDADD = $(LDADDS)
testwindows_LDADD = $(LDADDS)
testexpand_LDADD = $(LDADDS)
testexpander_LDADD = $(LDADDS)
@@ -373,6 +379,11 @@ testvolumebutton_SOURCES = \
testscrolledwindow_SOURCES = \
testscrolledwindow.c
+testcellarea_SOURCES = \
+ testcellarea.c \
+ cellareascaffold.c \
+ cellareascaffold.h
+
testoffscreen_SOURCES = \
gtkoffscreenbox.c \
gtkoffscreenbox.h \
diff --git a/tests/cellareascaffold.c b/tests/cellareascaffold.c
new file mode 100644
index 0000000000..c623b20acd
--- /dev/null
+++ b/tests/cellareascaffold.c
@@ -0,0 +1,1140 @@
+/* cellareascaffold.c
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "cellareascaffold.h"
+
+/* GObjectClass */
+static void cell_area_scaffold_finalize (GObject *object);
+static void cell_area_scaffold_dispose (GObject *object);
+
+/* GtkWidgetClass */
+static void cell_area_scaffold_realize (GtkWidget *widget);
+static void cell_area_scaffold_unrealize (GtkWidget *widget);
+static gboolean cell_area_scaffold_draw (GtkWidget *widget,
+ cairo_t *cr);
+static void cell_area_scaffold_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void cell_area_scaffold_get_preferred_width (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void cell_area_scaffold_get_preferred_height_for_width (GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size);
+static void cell_area_scaffold_get_preferred_height (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void cell_area_scaffold_get_preferred_width_for_height (GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size);
+static void cell_area_scaffold_map (GtkWidget *widget);
+static void cell_area_scaffold_unmap (GtkWidget *widget);
+static gint cell_area_scaffold_focus (GtkWidget *widget,
+ GtkDirectionType direction);
+static gboolean cell_area_scaffold_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+
+/* GtkContainerClass */
+static void cell_area_scaffold_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data);
+static void cell_area_scaffold_remove (GtkContainer *container,
+ GtkWidget *child);
+static void cell_area_scaffold_put_edit_widget (CellAreaScaffold *scaffold,
+ GtkWidget *edit_widget,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+
+/* CellAreaScaffoldClass */
+static void cell_area_scaffold_activate (CellAreaScaffold *scaffold);
+
+/* CellArea/GtkTreeModel callbacks */
+static void size_changed_cb (GtkCellAreaContext *context,
+ GParamSpec *pspec,
+ CellAreaScaffold *scaffold);
+static void focus_changed_cb (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *path,
+ CellAreaScaffold *scaffold);
+static void add_editable_cb (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ GdkRectangle *cell_area,
+ const gchar *path,
+ CellAreaScaffold *scaffold);
+static void remove_editable_cb (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ CellAreaScaffold *scaffold);
+static void row_changed_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ CellAreaScaffold *scaffold);
+static void row_inserted_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ CellAreaScaffold *scaffold);
+static void row_deleted_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ CellAreaScaffold *scaffold);
+static void rows_reordered_cb (GtkTreeModel *model,
+ GtkTreePath *parent,
+ GtkTreeIter *iter,
+ gint *new_order,
+ CellAreaScaffold *scaffold);
+
+typedef struct {
+ gint size; /* The height of rows in the scaffold's */
+} RowData;
+
+struct _CellAreaScaffoldPrivate {
+
+ /* Window for catching events and dispatching them to the cell area */
+ GdkWindow *event_window;
+
+ /* The model we're showing data for */
+ GtkTreeModel *model;
+ gulong row_changed_id;
+ gulong row_inserted_id;
+ gulong row_deleted_id;
+ gulong rows_reordered_id;
+
+ /* The area rendering the data and a global context */
+ GtkCellArea *area;
+ GtkCellAreaContext *context;
+
+ /* Cache some info about rows (hieghts etc) */
+ GArray *row_data;
+
+ /* Focus handling */
+ gint focus_row;
+ gulong focus_changed_id;
+
+ /* Check when the underlying area changes the size and
+ * we need to queue a redraw */
+ gulong size_changed_id;
+
+ /* Currently edited widget */
+ GtkWidget *edit_widget;
+ GdkRectangle edit_rect;
+ gulong add_editable_id;
+ gulong remove_editable_id;
+
+
+ gint row_spacing;
+ gint indent;
+};
+
+enum {
+ ACTIVATE,
+ N_SIGNALS
+};
+
+static guint scaffold_signals[N_SIGNALS] = { 0 };
+
+#define DIRECTION_STR(dir) \
+ ((dir) == GTK_DIR_TAB_FORWARD ? "tab forward" : \
+ (dir) == GTK_DIR_TAB_BACKWARD ? "tab backward" : \
+ (dir) == GTK_DIR_UP ? "up" : \
+ (dir) == GTK_DIR_DOWN ? "down" : \
+ (dir) == GTK_DIR_LEFT ? "left" : \
+ (dir) == GTK_DIR_RIGHT ? "right" : "invalid")
+
+G_DEFINE_TYPE_WITH_CODE (CellAreaScaffold, cell_area_scaffold, GTK_TYPE_CONTAINER,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
+
+
+static void
+cell_area_scaffold_init (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ scaffold->priv = G_TYPE_INSTANCE_GET_PRIVATE (scaffold,
+ TYPE_CELL_AREA_SCAFFOLD,
+ CellAreaScaffoldPrivate);
+ priv = scaffold->priv;
+
+ priv->area = gtk_cell_area_box_new ();
+ priv->context = gtk_cell_area_create_context (priv->area);
+
+ priv->row_data = g_array_new (FALSE, FALSE, sizeof (RowData));
+
+ gtk_widget_set_has_window (GTK_WIDGET (scaffold), FALSE);
+ gtk_widget_set_can_focus (GTK_WIDGET (scaffold), TRUE);
+
+ priv->size_changed_id =
+ g_signal_connect (priv->context, "notify",
+ G_CALLBACK (size_changed_cb), scaffold);
+
+ priv->focus_changed_id =
+ g_signal_connect (priv->area, "focus-changed",
+ G_CALLBACK (focus_changed_cb), scaffold);
+
+ priv->add_editable_id =
+ g_signal_connect (priv->area, "add-editable",
+ G_CALLBACK (add_editable_cb), scaffold);
+
+ priv->remove_editable_id =
+ g_signal_connect (priv->area, "remove-editable",
+ G_CALLBACK (remove_editable_cb), scaffold);
+}
+
+static void
+cell_area_scaffold_class_init (CellAreaScaffoldClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+ gobject_class->dispose = cell_area_scaffold_dispose;
+ gobject_class->finalize = cell_area_scaffold_finalize;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->realize = cell_area_scaffold_realize;
+ widget_class->unrealize = cell_area_scaffold_unrealize;
+ widget_class->draw = cell_area_scaffold_draw;
+ widget_class->size_allocate = cell_area_scaffold_size_allocate;
+ widget_class->get_preferred_width = cell_area_scaffold_get_preferred_width;
+ widget_class->get_preferred_height_for_width = cell_area_scaffold_get_preferred_height_for_width;
+ widget_class->get_preferred_height = cell_area_scaffold_get_preferred_height;
+ widget_class->get_preferred_width_for_height = cell_area_scaffold_get_preferred_width_for_height;
+ widget_class->map = cell_area_scaffold_map;
+ widget_class->unmap = cell_area_scaffold_unmap;
+ widget_class->focus = cell_area_scaffold_focus;
+ widget_class->button_press_event = cell_area_scaffold_button_press;
+
+ container_class = GTK_CONTAINER_CLASS (class);
+ container_class->forall = cell_area_scaffold_forall;
+ container_class->remove = cell_area_scaffold_remove;
+
+ class->activate = cell_area_scaffold_activate;
+
+ scaffold_signals[ACTIVATE] =
+ g_signal_new ("activate",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (CellAreaScaffoldClass, activate),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ widget_class->activate_signal = scaffold_signals[ACTIVATE];
+
+
+ g_type_class_add_private (gobject_class, sizeof (CellAreaScaffoldPrivate));
+}
+
+/*********************************************************
+ * GObjectClass *
+ *********************************************************/
+static void
+cell_area_scaffold_finalize (GObject *object)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (object);
+ CellAreaScaffoldPrivate *priv;
+
+ priv = scaffold->priv;
+
+ g_array_free (priv->row_data, TRUE);
+
+ G_OBJECT_CLASS (cell_area_scaffold_parent_class)->finalize (object);
+}
+
+static void
+cell_area_scaffold_dispose (GObject *object)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (object);
+ CellAreaScaffoldPrivate *priv;
+
+ priv = scaffold->priv;
+
+ cell_area_scaffold_set_model (scaffold, NULL);
+
+ if (priv->context)
+ {
+ /* Disconnect signals */
+ g_signal_handler_disconnect (priv->context, priv->size_changed_id);
+
+ g_object_unref (priv->context);
+ priv->context = NULL;
+ priv->size_changed_id = 0;
+ }
+
+ if (priv->area)
+ {
+ /* Disconnect signals */
+ g_signal_handler_disconnect (priv->area, priv->focus_changed_id);
+ g_signal_handler_disconnect (priv->area, priv->add_editable_id);
+ g_signal_handler_disconnect (priv->area, priv->remove_editable_id);
+
+ g_object_unref (priv->area);
+ priv->area = NULL;
+ priv->focus_changed_id = 0;
+ }
+
+ G_OBJECT_CLASS (cell_area_scaffold_parent_class)->dispose (object);
+}
+
+/*********************************************************
+ * GtkWidgetClass *
+ *********************************************************/
+static void
+cell_area_scaffold_realize (GtkWidget *widget)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkAllocation allocation;
+ GdkWindow *window;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_widget_set_realized (widget, TRUE);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.wclass = GDK_INPUT_ONLY;
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+ window = gtk_widget_get_parent_window (widget);
+ gtk_widget_set_window (widget, window);
+ g_object_ref (window);
+
+ priv->event_window = gdk_window_new (window, &attributes, attributes_mask);
+ gdk_window_set_user_data (priv->event_window, widget);
+
+ gtk_widget_style_attach (widget);
+}
+
+static void
+cell_area_scaffold_unrealize (GtkWidget *widget)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ if (priv->event_window)
+ {
+ gdk_window_set_user_data (priv->event_window, NULL);
+ gdk_window_destroy (priv->event_window);
+ priv->event_window = NULL;
+ }
+
+ GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->unrealize (widget);
+}
+
+static gboolean
+cell_area_scaffold_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkTreeIter iter;
+ gboolean valid;
+ GdkRectangle background_area;
+ GdkRectangle render_area;
+ GtkAllocation allocation;
+ gint i = 0;
+ gboolean have_focus;
+ GtkCellRendererState flags;
+
+ if (!priv->model)
+ return FALSE;
+
+ have_focus = gtk_widget_has_focus (widget);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ render_area.x = 0;
+ render_area.y = 0;
+ render_area.width = allocation.width;
+ render_area.height = allocation.height;
+
+ background_area = render_area;
+
+ render_area.x = priv->indent;
+ render_area.width -= priv->indent;
+
+ valid = gtk_tree_model_get_iter_first (priv->model, &iter);
+ while (valid)
+ {
+ RowData *data = &g_array_index (priv->row_data, RowData, i);
+
+ if (have_focus && i == priv->focus_row)
+ flags = GTK_CELL_RENDERER_FOCUSED;
+ else
+ flags = 0;
+
+ render_area.height = data->size;
+
+ background_area.height = render_area.height;
+ background_area.y = render_area.y;
+
+ if (i == 0)
+ {
+ background_area.height += priv->row_spacing / 2;
+ background_area.height += priv->row_spacing % 2;
+ }
+ else if (i == priv->row_data->len - 1)
+ {
+ background_area.y -= priv->row_spacing / 2;
+ background_area.height += priv->row_spacing / 2;
+ }
+ else
+ {
+ background_area.y -= priv->row_spacing / 2;
+ background_area.height += priv->row_spacing;
+ }
+
+ gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+ gtk_cell_area_render (priv->area, priv->context, widget, cr,
+ &background_area, &render_area, flags,
+ (have_focus && i == priv->focus_row));
+
+ render_area.y += data->size;
+ render_area.y += priv->row_spacing;
+
+ i++;
+ valid = gtk_tree_model_iter_next (priv->model, &iter);
+ }
+
+ /* Draw the edit widget after drawing everything else */
+ GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->draw (widget, cr);
+
+ return FALSE;
+}
+
+static void
+request_all_base (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkWidget *widget = GTK_WIDGET (scaffold);
+ GtkTreeIter iter;
+ gboolean valid;
+
+ if (!priv->model)
+ return;
+
+ g_signal_handler_block (priv->context, priv->size_changed_id);
+
+ valid = gtk_tree_model_get_iter_first (priv->model, &iter);
+ while (valid)
+ {
+ gint min, nat;
+
+ gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+ gtk_cell_area_get_preferred_width (priv->area, priv->context, widget, &min, &nat);
+
+ valid = gtk_tree_model_iter_next (priv->model, &iter);
+ }
+
+ g_signal_handler_unblock (priv->context, priv->size_changed_id);
+}
+
+static void
+get_row_sizes (CellAreaScaffold *scaffold,
+ GArray *array,
+ gint for_size)
+{
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkWidget *widget = GTK_WIDGET (scaffold);
+ GtkTreeIter iter;
+ gboolean valid;
+ gint i = 0;
+
+ if (!priv->model)
+ return;
+
+ valid = gtk_tree_model_get_iter_first (priv->model, &iter);
+ while (valid)
+ {
+ RowData *data = &g_array_index (array, RowData, i);
+
+ gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+ gtk_cell_area_get_preferred_height_for_width (priv->area, priv->context, widget,
+ for_size, &data->size, NULL);
+
+ i++;
+ valid = gtk_tree_model_iter_next (priv->model, &iter);
+ }
+}
+
+static void
+cell_area_scaffold_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ if (gtk_widget_get_realized (widget))
+ gdk_window_move_resize (priv->event_window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+
+ /* Allocate the child GtkCellEditable widget if one is currently editing a row */
+ if (priv->edit_widget)
+ gtk_widget_size_allocate (priv->edit_widget, &priv->edit_rect);
+
+ if (!priv->model)
+ return;
+
+ /* Cache the per-row sizes and allocate the context */
+ gtk_cell_area_context_allocate (priv->context, allocation->width - priv->indent, -1);
+ get_row_sizes (scaffold, priv->row_data, allocation->width - priv->indent);
+}
+
+
+static void
+cell_area_scaffold_get_preferred_width (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ if (!priv->model)
+ return;
+
+ request_all_base (scaffold);
+ gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size);
+
+ *minimum_size += priv->indent;
+ *natural_size += priv->indent;
+}
+
+static void
+cell_area_scaffold_get_preferred_height_for_width (GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GArray *request_array;
+ gint n_rows, i, full_size = 0;
+
+ if (!priv->model)
+ return;
+
+ n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
+
+ /* Get an array for the contextual request */
+ request_array = g_array_new (FALSE, FALSE, sizeof (RowData));
+ g_array_set_size (request_array, n_rows);
+ memset (request_array->data, 0x0, n_rows * sizeof (RowData));
+
+ /* Gather each contextual size into the request array */
+ get_row_sizes (scaffold, request_array, for_size - priv->indent);
+
+ /* Sum up the size and add some row spacing */
+ for (i = 0; i < n_rows; i++)
+ {
+ RowData *data = &g_array_index (request_array, RowData, i);
+
+ full_size += data->size;
+ }
+
+ full_size += MAX (0, n_rows -1) * priv->row_spacing;
+
+ g_array_free (request_array, TRUE);
+
+ *minimum_size = full_size;
+ *natural_size = full_size;
+}
+
+static void
+cell_area_scaffold_get_preferred_height (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ gint min_size, nat_size;
+
+ if (!priv->model)
+ return;
+
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_size, &nat_size);
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_size,
+ minimum_size, natural_size);
+}
+
+static void
+cell_area_scaffold_get_preferred_width_for_height (GtkWidget *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ if (!priv->model)
+ return;
+
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
+}
+
+static void
+cell_area_scaffold_map (GtkWidget *widget)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->map (widget);
+
+ if (priv->event_window)
+ gdk_window_show (priv->event_window);
+}
+
+static void
+cell_area_scaffold_unmap (GtkWidget *widget)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->unmap (widget);
+
+ if (priv->event_window)
+ gdk_window_hide (priv->event_window);
+}
+
+
+static gint
+cell_area_scaffold_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkTreeIter iter;
+ gboolean valid;
+ gint focus_row;
+ gboolean changed = FALSE;
+
+ /* Grab focus on ourself if we dont already have focus */
+ if (!gtk_widget_has_focus (widget))
+ gtk_widget_grab_focus (widget);
+
+ /* Move focus from cell to cell and row to row */
+ focus_row = priv->focus_row;
+
+ g_signal_handler_block (priv->area, priv->focus_changed_id);
+
+ valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row);
+ while (valid)
+ {
+ gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+
+ /* If focus stays in the area we dont need to do any more */
+ if (gtk_cell_area_focus (priv->area, direction))
+ {
+ priv->focus_row = focus_row;
+
+ /* XXX A smarter implementation would only invalidate the rectangles where
+ * focus was removed from and new focus was placed */
+ gtk_widget_queue_draw (widget);
+ changed = TRUE;
+ break;
+ }
+ else
+ {
+ if (direction == GTK_DIR_RIGHT ||
+ direction == GTK_DIR_LEFT)
+ break;
+ else if (direction == GTK_DIR_UP ||
+ direction == GTK_DIR_TAB_BACKWARD)
+ {
+ if (focus_row == 0)
+ break;
+ else
+ {
+ /* XXX A real implementation should check if the
+ * previous row can focus with it's attributes setup */
+ focus_row--;
+ valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
+ }
+ }
+ else /* direction == GTK_DIR_DOWN || GTK_DIR_TAB_FORWARD */
+ {
+ if (focus_row == priv->row_data->len - 1)
+ break;
+ else
+ {
+ /* XXX A real implementation should check if the
+ * previous row can focus with it's attributes setup */
+ focus_row++;
+ valid = gtk_tree_model_iter_next (priv->model, &iter);
+ }
+ }
+ }
+ }
+
+ g_signal_handler_unblock (priv->area, priv->focus_changed_id);
+
+ /* XXX A smarter implementation would only invalidate the rectangles where
+ * focus was removed from and new focus was placed */
+ gtk_widget_queue_draw (widget);
+
+ return changed;
+}
+
+static gboolean
+cell_area_scaffold_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (widget);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkTreeIter iter;
+ gboolean valid;
+ gint i = 0;
+ GdkRectangle event_area;
+ GtkAllocation allocation;
+ gboolean handled = FALSE;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ event_area.x = 0;
+ event_area.y = 0;
+ event_area.width = allocation.width;
+ event_area.height = allocation.height;
+
+ event_area.x = priv->indent;
+ event_area.width -= priv->indent;
+
+ valid = gtk_tree_model_get_iter_first (priv->model, &iter);
+ while (valid)
+ {
+ RowData *data = &g_array_index (priv->row_data, RowData, i);
+
+ event_area.height = data->size;
+
+ if (event->y >= event_area.y &&
+ event->y <= event_area.y + event_area.height)
+ {
+ /* XXX A real implementation would assemble GtkCellRendererState flags here */
+ gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+ handled = gtk_cell_area_event (priv->area, priv->context, GTK_WIDGET (scaffold),
+ (GdkEvent *)event, &event_area, 0);
+ break;
+ }
+
+ event_area.y += data->size;
+ event_area.y += priv->row_spacing;
+
+ i++;
+ valid = gtk_tree_model_iter_next (priv->model, &iter);
+ }
+
+ return handled;
+}
+
+
+/*********************************************************
+ * GtkContainerClass *
+ *********************************************************/
+static void
+cell_area_scaffold_put_edit_widget (CellAreaScaffold *scaffold,
+ GtkWidget *edit_widget,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ priv->edit_rect.x = x;
+ priv->edit_rect.y = y;
+ priv->edit_rect.width = width;
+ priv->edit_rect.height = height;
+ priv->edit_widget = edit_widget;
+
+ gtk_widget_set_parent (edit_widget, GTK_WIDGET (scaffold));
+}
+
+static void
+cell_area_scaffold_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (container);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ if (priv->edit_widget)
+ (* callback) (priv->edit_widget, callback_data);
+}
+
+static void
+cell_area_scaffold_remove (GtkContainer *container,
+ GtkWidget *child)
+{
+ CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (container);
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+
+ g_return_if_fail (child == priv->edit_widget);
+
+ gtk_widget_unparent (priv->edit_widget);
+ priv->edit_widget = NULL;
+}
+
+/*********************************************************
+ * CellAreaScaffoldClass *
+ *********************************************************/
+static void
+cell_area_scaffold_activate (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkWidget *widget = GTK_WIDGET (scaffold);
+ GtkAllocation allocation;
+ GdkRectangle cell_area;
+ GtkTreeIter iter;
+ gboolean valid;
+ gint i = 0;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cell_area.x = 0;
+ cell_area.y = 0;
+ cell_area.width = allocation.width;
+ cell_area.height = allocation.height;
+
+ cell_area.x = priv->indent;
+ cell_area.width -= priv->indent;
+
+ valid = gtk_tree_model_get_iter_first (priv->model, &iter);
+ while (valid)
+ {
+ RowData *data = &g_array_index (priv->row_data, RowData, i);
+
+ if (i == priv->focus_row)
+ {
+ cell_area.height = data->size;
+
+ gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+ gtk_cell_area_activate (priv->area, priv->context, widget, &cell_area,
+ GTK_CELL_RENDERER_FOCUSED, FALSE);
+
+ break;
+ }
+
+ cell_area.y += data->size + priv->row_spacing;
+
+ i++;
+ valid = gtk_tree_model_iter_next (priv->model, &iter);
+ }
+}
+
+/*********************************************************
+ * CellArea/GtkTreeModel callbacks *
+ *********************************************************/
+static void
+size_changed_cb (GtkCellAreaContext *context,
+ GParamSpec *pspec,
+ CellAreaScaffold *scaffold)
+{
+ if (!strcmp (pspec->name, "minimum-width") ||
+ !strcmp (pspec->name, "natural-width") ||
+ !strcmp (pspec->name, "minimum-height") ||
+ !strcmp (pspec->name, "natural-height"))
+ gtk_widget_queue_resize (GTK_WIDGET (scaffold));
+}
+
+static void
+focus_changed_cb (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ const gchar *path,
+ CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ GtkWidget *widget = GTK_WIDGET (scaffold);
+ GtkTreePath *treepath;
+ gint *indices;
+
+ if (!priv->model)
+ return;
+
+ /* We can be signaled that a renderer lost focus, here
+ * we dont care */
+ if (!renderer)
+ return;
+
+ treepath = gtk_tree_path_new_from_string (path);
+ indices = gtk_tree_path_get_indices (treepath);
+
+ priv->focus_row = indices[0];
+
+ gtk_tree_path_free (treepath);
+
+ /* Make sure we have focus now */
+ if (!gtk_widget_has_focus (widget))
+ gtk_widget_grab_focus (widget);
+
+ gtk_widget_queue_draw (widget);
+}
+
+static void
+add_editable_cb (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ GdkRectangle *cell_area,
+ const gchar *path,
+ CellAreaScaffold *scaffold)
+{
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (GTK_WIDGET (scaffold), &allocation);
+
+ cell_area_scaffold_put_edit_widget (scaffold, GTK_WIDGET (edit_widget),
+ allocation.x + cell_area->x,
+ allocation.y + cell_area->y,
+ cell_area->width, cell_area->height);
+}
+
+static void
+remove_editable_cb (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellEditable *edit_widget,
+ CellAreaScaffold *scaffold)
+{
+ gtk_container_remove (GTK_CONTAINER (scaffold), GTK_WIDGET (edit_widget));
+
+ gtk_widget_grab_focus (GTK_WIDGET (scaffold));
+}
+
+static void
+rebuild_and_reset_internals (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv = scaffold->priv;
+ gint n_rows;
+
+ if (priv->model)
+ {
+ n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
+
+ /* Clear/reset the array */
+ g_array_set_size (priv->row_data, n_rows);
+ memset (priv->row_data->data, 0x0, n_rows * sizeof (RowData));
+ }
+ else
+ g_array_set_size (priv->row_data, 0);
+
+ /* Data changed, lets reset the context and consequently queue resize and
+ * start everything over again (note this is definitly far from optimized) */
+ gtk_cell_area_context_reset (priv->context);
+}
+
+static void
+row_changed_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ CellAreaScaffold *scaffold)
+{
+ rebuild_and_reset_internals (scaffold);
+}
+
+static void
+row_inserted_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ CellAreaScaffold *scaffold)
+{
+ rebuild_and_reset_internals (scaffold);
+}
+
+static void
+row_deleted_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ CellAreaScaffold *scaffold)
+{
+ rebuild_and_reset_internals (scaffold);
+}
+
+static void
+rows_reordered_cb (GtkTreeModel *model,
+ GtkTreePath *parent,
+ GtkTreeIter *iter,
+ gint *new_order,
+ CellAreaScaffold *scaffold)
+{
+ rebuild_and_reset_internals (scaffold);
+}
+
+/*********************************************************
+ * API *
+ *********************************************************/
+GtkWidget *
+cell_area_scaffold_new (void)
+{
+ return (GtkWidget *)g_object_new (TYPE_CELL_AREA_SCAFFOLD, NULL);
+}
+
+GtkCellArea *
+cell_area_scaffold_get_area (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
+
+ priv = scaffold->priv;
+
+ return priv->area;
+}
+
+void
+cell_area_scaffold_set_model (CellAreaScaffold *scaffold,
+ GtkTreeModel *model)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
+
+ priv = scaffold->priv;
+
+ if (priv->model != model)
+ {
+ if (priv->model)
+ {
+ g_signal_handler_disconnect (priv->model, priv->row_changed_id);
+ g_signal_handler_disconnect (priv->model, priv->row_inserted_id);
+ g_signal_handler_disconnect (priv->model, priv->row_deleted_id);
+ g_signal_handler_disconnect (priv->model, priv->rows_reordered_id);
+
+ g_object_unref (priv->model);
+ }
+
+ priv->model = model;
+
+ if (priv->model)
+ {
+ g_object_ref (priv->model);
+
+ priv->row_changed_id =
+ g_signal_connect (priv->model, "row-changed",
+ G_CALLBACK (row_changed_cb), scaffold);
+
+ priv->row_inserted_id =
+ g_signal_connect (priv->model, "row-inserted",
+ G_CALLBACK (row_inserted_cb), scaffold);
+
+ priv->row_deleted_id =
+ g_signal_connect (priv->model, "row-deleted",
+ G_CALLBACK (row_deleted_cb), scaffold);
+
+ priv->rows_reordered_id =
+ g_signal_connect (priv->model, "rows-reordered",
+ G_CALLBACK (rows_reordered_cb), scaffold);
+ }
+
+ rebuild_and_reset_internals (scaffold);
+ }
+}
+
+GtkTreeModel *
+cell_area_scaffold_get_model (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
+
+ priv = scaffold->priv;
+
+ return priv->model;
+}
+
+
+void
+cell_area_scaffold_set_row_spacing (CellAreaScaffold *scaffold,
+ gint spacing)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
+
+ priv = scaffold->priv;
+
+ if (priv->row_spacing != spacing)
+ {
+ priv->row_spacing = spacing;
+ gtk_widget_queue_resize (GTK_WIDGET (scaffold));
+ }
+}
+
+gint
+cell_area_scaffold_get_row_spacing (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
+
+ priv = scaffold->priv;
+
+ return priv->row_spacing;
+}
+
+void
+cell_area_scaffold_set_indentation (CellAreaScaffold *scaffold,
+ gint indent)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
+
+ priv = scaffold->priv;
+
+ if (priv->indent != indent)
+ {
+ priv->indent = indent;
+ gtk_widget_queue_resize (GTK_WIDGET (scaffold));
+ }
+}
+
+gint
+cell_area_scaffold_get_indentation (CellAreaScaffold *scaffold)
+{
+ CellAreaScaffoldPrivate *priv;
+
+ g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
+
+ priv = scaffold->priv;
+
+ return priv->indent;
+}
+
+
diff --git a/tests/cellareascaffold.h b/tests/cellareascaffold.h
new file mode 100644
index 0000000000..cdc7b254c9
--- /dev/null
+++ b/tests/cellareascaffold.h
@@ -0,0 +1,79 @@
+/* cellareascaffold.h
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CELL_AREA_SCAFFOLD_H__
+#define __CELL_AREA_SCAFFOLD_H__
+
+#include <gtk/gtk.h>
+
+
+G_BEGIN_DECLS
+
+#define TYPE_CELL_AREA_SCAFFOLD (cell_area_scaffold_get_type ())
+#define CELL_AREA_SCAFFOLD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_CELL_AREA_SCAFFOLD, CellAreaScaffold))
+#define CELL_AREA_SCAFFOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_CELL_AREA_SCAFFOLD, CellAreaScaffoldClass))
+#define IS_CELL_AREA_SCAFFOLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_CELL_AREA_SCAFFOLD))
+#define IS_CELL_AREA_SCAFFOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_CELL_AREA_SCAFFOLD))
+#define CELL_AREA_SCAFFOLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_CELL_AREA_SCAFFOLD, CellAreaScaffoldClass))
+
+
+typedef struct _CellAreaScaffold CellAreaScaffold;
+typedef struct _CellAreaScaffoldClass CellAreaScaffoldClass;
+typedef struct _CellAreaScaffoldPrivate CellAreaScaffoldPrivate;
+
+struct _CellAreaScaffold
+{
+ GtkContainer widget;
+
+ CellAreaScaffoldPrivate *priv;
+};
+
+struct _CellAreaScaffoldClass
+{
+ GtkContainerClass parent_class;
+
+ void (* activate) (CellAreaScaffold *scaffold);
+};
+
+
+GType cell_area_scaffold_get_type (void) G_GNUC_CONST;
+GtkWidget *cell_area_scaffold_new (void);
+
+GtkCellArea *cell_area_scaffold_get_area (CellAreaScaffold *scaffold);
+void cell_area_scaffold_set_model (CellAreaScaffold *scaffold,
+ GtkTreeModel *model);
+GtkTreeModel *cell_area_scaffold_get_model (CellAreaScaffold *scaffold);
+
+void cell_area_scaffold_set_row_spacing (CellAreaScaffold *scaffold,
+ gint spacing);
+gint cell_area_scaffold_get_row_spacing (CellAreaScaffold *scaffold);
+
+void cell_area_scaffold_set_indentation (CellAreaScaffold *scaffold,
+ gint indent);
+gint cell_area_scaffold_get_indentation (CellAreaScaffold *scaffold);
+
+
+
+G_END_DECLS
+
+#endif /* __CELL_AREA_SCAFFOLD_H__ */
diff --git a/tests/testcellarea.c b/tests/testcellarea.c
new file mode 100644
index 0000000000..ec9398d6f2
--- /dev/null
+++ b/tests/testcellarea.c
@@ -0,0 +1,627 @@
+#include <gtk/gtk.h>
+#include "cellareascaffold.h"
+
+/*******************************************************
+ * Simple Test *
+ *******************************************************/
+enum {
+ SIMPLE_COLUMN_NAME,
+ SIMPLE_COLUMN_ICON,
+ SIMPLE_COLUMN_DESCRIPTION,
+ N_SIMPLE_COLUMNS
+};
+
+static GtkCellRenderer *cell_1 = NULL, *cell_2 = NULL, *cell_3 = NULL;
+
+static GtkTreeModel *
+simple_list_model (void)
+{
+ GtkTreeIter iter;
+ GtkListStore *store =
+ gtk_list_store_new (N_SIMPLE_COLUMNS,
+ G_TYPE_STRING, /* name text */
+ G_TYPE_STRING, /* icon name */
+ G_TYPE_STRING); /* description text */
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ SIMPLE_COLUMN_NAME, "Alice in wonderland",
+ SIMPLE_COLUMN_ICON, "gtk-execute",
+ SIMPLE_COLUMN_DESCRIPTION,
+ "Twas brillig, and the slithy toves "
+ "did gyre and gimble in the wabe; "
+ "all mimsy were the borogoves, "
+ "and the mome raths outgrabe",
+ -1);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ SIMPLE_COLUMN_NAME, "Marry Poppins",
+ SIMPLE_COLUMN_ICON, "gtk-yes",
+ SIMPLE_COLUMN_DESCRIPTION, "Supercalifragilisticexpialidocious",
+ -1);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ SIMPLE_COLUMN_NAME, "George Bush",
+ SIMPLE_COLUMN_ICON, "gtk-dialog-warning",
+ SIMPLE_COLUMN_DESCRIPTION, "It's a very good question, very direct, "
+ "and I'm not going to answer it",
+ -1);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ SIMPLE_COLUMN_NAME, "Whinnie the pooh",
+ SIMPLE_COLUMN_ICON, "gtk-stop",
+ SIMPLE_COLUMN_DESCRIPTION, "The most wonderful thing about tiggers, "
+ "is tiggers are wonderful things",
+ -1);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ SIMPLE_COLUMN_NAME, "Aleister Crowley",
+ SIMPLE_COLUMN_ICON, "gtk-about",
+ SIMPLE_COLUMN_DESCRIPTION,
+ "Thou shalt do what thou wilt shall be the whole of the law",
+ -1);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ SIMPLE_COLUMN_NAME, "Mark Twain",
+ SIMPLE_COLUMN_ICON, "gtk-quit",
+ SIMPLE_COLUMN_DESCRIPTION,
+ "Giving up smoking is the easiest thing in the world. "
+ "I know because I've done it thousands of times.",
+ -1);
+
+
+ return (GtkTreeModel *)store;
+}
+
+static GtkWidget *
+simple_scaffold (void)
+{
+ GtkTreeModel *model;
+ GtkWidget *scaffold;
+ GtkCellArea *area;
+ GtkCellRenderer *renderer;
+
+ scaffold = cell_area_scaffold_new ();
+ gtk_widget_show (scaffold);
+
+ model = simple_list_model ();
+
+ cell_area_scaffold_set_model (CELL_AREA_SCAFFOLD (scaffold), model);
+
+ area = cell_area_scaffold_get_area (CELL_AREA_SCAFFOLD (scaffold));
+
+ cell_1 = renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, FALSE, FALSE);
+ gtk_cell_area_attribute_connect (area, renderer, "text", SIMPLE_COLUMN_NAME);
+
+ cell_2 = renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (G_OBJECT (renderer), "xalign", 0.0F, NULL);
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, TRUE, FALSE);
+ gtk_cell_area_attribute_connect (area, renderer, "stock-id", SIMPLE_COLUMN_ICON);
+
+ cell_3 = renderer = gtk_cell_renderer_text_new ();
+ g_object_set (G_OBJECT (renderer),
+ "wrap-mode", PANGO_WRAP_WORD,
+ "wrap-width", 215,
+ NULL);
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, FALSE, TRUE);
+ gtk_cell_area_attribute_connect (area, renderer, "text", SIMPLE_COLUMN_DESCRIPTION);
+
+ return scaffold;
+}
+
+static void
+orientation_changed (GtkComboBox *combo,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ GtkOrientation orientation = gtk_combo_box_get_active (combo);
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (area), orientation);
+}
+
+static void
+align_cell_2_toggled (GtkToggleButton *toggle,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gboolean align = gtk_toggle_button_get_active (toggle);
+
+ gtk_cell_area_cell_set (area, cell_2, "align", align, NULL);
+}
+
+static void
+align_cell_3_toggled (GtkToggleButton *toggle,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gboolean align = gtk_toggle_button_get_active (toggle);
+
+ gtk_cell_area_cell_set (area, cell_3, "align", align, NULL);
+}
+
+static void
+expand_cell_1_toggled (GtkToggleButton *toggle,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gboolean expand = gtk_toggle_button_get_active (toggle);
+
+ gtk_cell_area_cell_set (area, cell_1, "expand", expand, NULL);
+}
+
+static void
+expand_cell_2_toggled (GtkToggleButton *toggle,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gboolean expand = gtk_toggle_button_get_active (toggle);
+
+ gtk_cell_area_cell_set (area, cell_2, "expand", expand, NULL);
+}
+
+static void
+expand_cell_3_toggled (GtkToggleButton *toggle,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gboolean expand = gtk_toggle_button_get_active (toggle);
+
+ gtk_cell_area_cell_set (area, cell_3, "expand", expand, NULL);
+}
+
+static void
+simple_cell_area (void)
+{
+ GtkWidget *window, *widget;
+ GtkWidget *scaffold, *frame, *vbox, *hbox;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "CellArea expand and alignments");
+
+ scaffold = simple_scaffold ();
+
+ hbox = gtk_hbox_new (FALSE, 4);
+ frame = gtk_frame_new (NULL);
+ gtk_widget_show (hbox);
+ gtk_widget_show (frame);
+
+ gtk_widget_set_valign (frame, GTK_ALIGN_CENTER);
+ gtk_widget_set_halign (frame, GTK_ALIGN_FILL);
+
+ gtk_container_add (GTK_CONTAINER (frame), scaffold);
+
+ gtk_box_pack_end (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+
+ /* Now add some controls */
+ vbox = gtk_vbox_new (FALSE, 4);
+ gtk_widget_show (vbox);
+ gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Horizontal");
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Vertical");
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "changed",
+ G_CALLBACK (orientation_changed), scaffold);
+
+ widget = gtk_check_button_new_with_label ("Align 2nd Cell");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (align_cell_2_toggled), scaffold);
+
+ widget = gtk_check_button_new_with_label ("Align 3rd Cell");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (align_cell_3_toggled), scaffold);
+
+
+ widget = gtk_check_button_new_with_label ("Expand 1st Cell");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (expand_cell_1_toggled), scaffold);
+
+ widget = gtk_check_button_new_with_label ("Expand 2nd Cell");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (expand_cell_2_toggled), scaffold);
+
+ widget = gtk_check_button_new_with_label ("Expand 3rd Cell");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (expand_cell_3_toggled), scaffold);
+
+ gtk_container_add (GTK_CONTAINER (window), hbox);
+
+ gtk_widget_show (window);
+}
+
+/*******************************************************
+ * Focus Test *
+ *******************************************************/
+static GtkCellRenderer *focus_renderer, *sibling_renderer;
+
+enum {
+ FOCUS_COLUMN_NAME,
+ FOCUS_COLUMN_CHECK,
+ FOCUS_COLUMN_STATIC_TEXT,
+ N_FOCUS_COLUMNS
+};
+
+static GtkTreeModel *
+focus_list_model (void)
+{
+ GtkTreeIter iter;
+ GtkListStore *store =
+ gtk_list_store_new (N_FOCUS_COLUMNS,
+ G_TYPE_STRING, /* name text */
+ G_TYPE_BOOLEAN, /* check */
+ G_TYPE_STRING); /* static text */
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ FOCUS_COLUMN_NAME, "Enter a string",
+ FOCUS_COLUMN_CHECK, TRUE,
+ FOCUS_COLUMN_STATIC_TEXT, "Does it fly ?",
+ -1);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ FOCUS_COLUMN_NAME, "Enter a string",
+ FOCUS_COLUMN_CHECK, FALSE,
+ FOCUS_COLUMN_STATIC_TEXT, "Would you put it in a toaster ?",
+ -1);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ FOCUS_COLUMN_NAME, "Type something",
+ FOCUS_COLUMN_CHECK, FALSE,
+ FOCUS_COLUMN_STATIC_TEXT, "Does it feed on cute kittens ?",
+ -1);
+
+ return (GtkTreeModel *)store;
+}
+
+static void
+cell_toggled (GtkCellRendererToggle *cell_renderer,
+ const gchar *path,
+ CellAreaScaffold *scaffold)
+{
+ GtkTreeModel *model = cell_area_scaffold_get_model (scaffold);
+ GtkTreeIter iter;
+ gboolean active;
+
+ g_print ("Cell toggled !\n");
+
+ if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
+ return;
+
+ gtk_tree_model_get (model, &iter, FOCUS_COLUMN_CHECK, &active, -1);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, FOCUS_COLUMN_CHECK, !active, -1);
+}
+
+static void
+cell_edited (GtkCellRendererToggle *cell_renderer,
+ const gchar *path,
+ const gchar *new_text,
+ CellAreaScaffold *scaffold)
+{
+ GtkTreeModel *model = cell_area_scaffold_get_model (scaffold);
+ GtkTreeIter iter;
+
+ g_print ("Cell edited with new text '%s' !\n", new_text);
+
+ if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
+ return;
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, FOCUS_COLUMN_NAME, new_text, -1);
+}
+
+static GtkWidget *
+focus_scaffold (gboolean color_bg, GtkCellRenderer **focus, GtkCellRenderer **sibling)
+{
+ GtkTreeModel *model;
+ GtkWidget *scaffold;
+ GtkCellArea *area;
+ GtkCellRenderer *renderer, *toggle;
+
+ scaffold = cell_area_scaffold_new ();
+ gtk_widget_show (scaffold);
+
+ model = focus_list_model ();
+
+ cell_area_scaffold_set_model (CELL_AREA_SCAFFOLD (scaffold), model);
+
+ area = cell_area_scaffold_get_area (CELL_AREA_SCAFFOLD (scaffold));
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, TRUE, FALSE);
+ gtk_cell_area_attribute_connect (area, renderer, "text", FOCUS_COLUMN_NAME);
+
+ if (color_bg)
+ g_object_set (G_OBJECT (renderer), "cell-background", "red", NULL);
+
+ g_signal_connect (G_OBJECT (renderer), "edited",
+ G_CALLBACK (cell_edited), scaffold);
+
+ toggle = renderer = gtk_cell_renderer_toggle_new ();
+ g_object_set (G_OBJECT (renderer), "xalign", 0.0F, NULL);
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, FALSE, TRUE);
+ gtk_cell_area_attribute_connect (area, renderer, "active", FOCUS_COLUMN_CHECK);
+
+ if (color_bg)
+ g_object_set (G_OBJECT (renderer), "cell-background", "green", NULL);
+
+ if (focus)
+ *focus = renderer;
+
+ g_signal_connect (G_OBJECT (renderer), "toggled",
+ G_CALLBACK (cell_toggled), scaffold);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (G_OBJECT (renderer),
+ "wrap-mode", PANGO_WRAP_WORD,
+ "wrap-width", 150,
+ NULL);
+
+ if (color_bg)
+ g_object_set (G_OBJECT (renderer), "cell-background", "blue", NULL);
+
+ if (sibling)
+ *sibling = renderer;
+
+ gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, FALSE, TRUE);
+ gtk_cell_area_attribute_connect (area, renderer, "text", FOCUS_COLUMN_STATIC_TEXT);
+
+ gtk_cell_area_add_focus_sibling (area, toggle, renderer);
+
+ return scaffold;
+}
+
+static void
+focus_sibling_toggled (GtkToggleButton *toggle,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gboolean active = gtk_toggle_button_get_active (toggle);
+
+ if (active)
+ gtk_cell_area_add_focus_sibling (area, focus_renderer, sibling_renderer);
+ else
+ gtk_cell_area_remove_focus_sibling (area, focus_renderer, sibling_renderer);
+
+ gtk_widget_queue_draw (GTK_WIDGET (scaffold));
+}
+
+
+static void
+focus_cell_area (void)
+{
+ GtkWidget *window, *widget;
+ GtkWidget *scaffold, *frame, *vbox, *hbox;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ hbox = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (hbox);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Focus and editable cells");
+
+ scaffold = focus_scaffold (FALSE, &focus_renderer, &sibling_renderer);
+
+ frame = gtk_frame_new (NULL);
+ gtk_widget_show (frame);
+
+ gtk_widget_set_valign (frame, GTK_ALIGN_CENTER);
+ gtk_widget_set_halign (frame, GTK_ALIGN_FILL);
+
+ gtk_container_add (GTK_CONTAINER (frame), scaffold);
+
+ gtk_box_pack_end (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+
+ /* Now add some controls */
+ vbox = gtk_vbox_new (FALSE, 4);
+ gtk_widget_show (vbox);
+ gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Horizontal");
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Vertical");
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "changed",
+ G_CALLBACK (orientation_changed), scaffold);
+
+ widget = gtk_check_button_new_with_label ("Focus Sibling");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (focus_sibling_toggled), scaffold);
+
+ gtk_container_add (GTK_CONTAINER (window), hbox);
+
+ gtk_widget_show (window);
+}
+
+
+
+/*******************************************************
+ * Background Area *
+ *******************************************************/
+static void
+cell_spacing_changed (GtkSpinButton *spin_button,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gint value;
+
+ value = (gint)gtk_spin_button_get_value (spin_button);
+
+ gtk_cell_area_box_set_spacing (GTK_CELL_AREA_BOX (area), value);
+}
+
+static void
+row_spacing_changed (GtkSpinButton *spin_button,
+ CellAreaScaffold *scaffold)
+{
+ gint value;
+
+ value = (gint)gtk_spin_button_get_value (spin_button);
+
+ cell_area_scaffold_set_row_spacing (scaffold, value);
+}
+
+static void
+indentation_changed (GtkSpinButton *spin_button,
+ CellAreaScaffold *scaffold)
+{
+ gint value;
+
+ value = (gint)gtk_spin_button_get_value (spin_button);
+
+ cell_area_scaffold_set_indentation (scaffold, value);
+}
+
+static void
+background_area (void)
+{
+ GtkWidget *window, *widget, *label, *main_vbox;
+ GtkWidget *scaffold, *frame, *vbox, *hbox;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ hbox = gtk_hbox_new (FALSE, 4);
+ main_vbox = gtk_vbox_new (FALSE, 4);
+ gtk_widget_show (hbox);
+ gtk_widget_show (main_vbox);
+ gtk_container_add (GTK_CONTAINER (window), main_vbox);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Background Area");
+
+ label = gtk_label_new ("In this example, row spacing gets devided into the background area, "
+ "column spacing is added between each background area, indentation is "
+ "prepended space distributed to the background area.");
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_width_chars (GTK_LABEL (label), 40);
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
+
+ scaffold = focus_scaffold (TRUE, NULL, NULL);
+
+ frame = gtk_frame_new (NULL);
+ gtk_widget_show (frame);
+
+ gtk_widget_set_valign (frame, GTK_ALIGN_CENTER);
+ gtk_widget_set_halign (frame, GTK_ALIGN_FILL);
+
+ gtk_container_add (GTK_CONTAINER (frame), scaffold);
+
+ gtk_box_pack_end (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+
+ /* Now add some controls */
+ vbox = gtk_vbox_new (FALSE, 4);
+ gtk_widget_show (vbox);
+ gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Horizontal");
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Vertical");
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "changed",
+ G_CALLBACK (orientation_changed), scaffold);
+
+ widget = gtk_spin_button_new_with_range (0, 10, 1);
+ label = gtk_label_new ("Cell spacing");
+ hbox = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (hbox);
+ gtk_widget_show (label);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "value-changed",
+ G_CALLBACK (cell_spacing_changed), scaffold);
+
+
+ widget = gtk_spin_button_new_with_range (0, 10, 1);
+ label = gtk_label_new ("Row spacing");
+ hbox = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (hbox);
+ gtk_widget_show (label);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "value-changed",
+ G_CALLBACK (row_spacing_changed), scaffold);
+
+ widget = gtk_spin_button_new_with_range (0, 30, 1);
+ label = gtk_label_new ("Intentation");
+ hbox = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (hbox);
+ gtk_widget_show (label);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ g_signal_connect (G_OBJECT (widget), "value-changed",
+ G_CALLBACK (indentation_changed), scaffold);
+
+ gtk_widget_show (window);
+}
+
+
+
+
+
+
+int
+main (int argc, char *argv[])
+{
+ gtk_init (NULL, NULL);
+
+ if (g_getenv ("RTL"))
+ gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
+
+ simple_cell_area ();
+ focus_cell_area ();
+ background_area ();
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/tests/testtooltips.c b/tests/testtooltips.c
index 54c2e05b88..483b8fd2a3 100644
--- a/tests/testtooltips.c
+++ b/tests/testtooltips.c
@@ -359,7 +359,7 @@ main (int argc, char *argv[])
/* Set a tooltip on the column */
column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 0);
gtk_tree_view_column_set_clickable (column, TRUE);
- g_object_set (column->button, "tooltip-text", "Header", NULL);
+ g_object_set (gtk_tree_view_column_get_button (column), "tooltip-text", "Header", NULL);
gtk_box_pack_start (GTK_BOX (box), tree_view, FALSE, FALSE, 2);
diff --git a/tests/testtreeedit.c b/tests/testtreeedit.c
index 4375691a08..01e1f2bbe7 100644
--- a/tests/testtreeedit.c
+++ b/tests/testtreeedit.c
@@ -31,6 +31,7 @@ enum {
STRING_COLUMN,
IS_EDITABLE_COLUMN,
PIXBUF_COLUMN,
+ LAST_PIXBUF_COLUMN,
PROGRESS_COLUMN,
NUM_COLUMNS
};
@@ -51,17 +52,19 @@ create_model (void)
GtkTreeStore *model;
GtkTreeIter iter;
gint i;
- GdkPixbuf *foo;
+ GdkPixbuf *foo, *bar;
GtkWidget *blah;
blah = gtk_window_new (GTK_WINDOW_TOPLEVEL);
foo = gtk_widget_render_icon_pixbuf (blah, GTK_STOCK_NEW, GTK_ICON_SIZE_MENU);
+ bar = gtk_widget_render_icon_pixbuf (blah, GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU);
gtk_widget_destroy (blah);
model = gtk_tree_store_new (NUM_COLUMNS,
G_TYPE_STRING,
G_TYPE_BOOLEAN,
GDK_TYPE_PIXBUF,
+ GDK_TYPE_PIXBUF,
G_TYPE_INT);
for (i = 0; model_strings[i].string != NULL; i++)
@@ -72,6 +75,7 @@ create_model (void)
STRING_COLUMN, model_strings[i].string,
IS_EDITABLE_COLUMN, model_strings[i].is_editable,
PIXBUF_COLUMN, foo,
+ LAST_PIXBUF_COLUMN, bar,
PROGRESS_COLUMN, model_strings[i].progress,
-1);
}
@@ -128,15 +132,65 @@ button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer callback_
return FALSE;
}
+typedef struct {
+ GtkCellArea *area;
+ GtkCellRenderer *renderer;
+} CallbackData;
+
+static void
+align_cell_toggled (GtkToggleButton *toggle,
+ CallbackData *data)
+{
+ gboolean active = gtk_toggle_button_get_active (toggle);
+
+ gtk_cell_area_cell_set (data->area, data->renderer, "align", active, NULL);
+}
+
+static void
+expand_cell_toggled (GtkToggleButton *toggle,
+ CallbackData *data)
+{
+ gboolean active = gtk_toggle_button_get_active (toggle);
+
+ gtk_cell_area_cell_set (data->area, data->renderer, "expand", active, NULL);
+}
+
+static void
+create_control (GtkWidget *box, gint number, gboolean align, CallbackData *data)
+{
+ GtkWidget *checkbutton;
+ gchar *name;
+
+ if (align)
+ name = g_strdup_printf ("Align Cell #%d", number);
+ else
+ name = g_strdup_printf ("Expand Cell #%d", number);
+
+ checkbutton = gtk_check_button_new_with_label (name);
+ gtk_widget_show (checkbutton);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), align);
+ gtk_box_pack_start (GTK_BOX (box), checkbutton, FALSE, FALSE, 0);
+
+ if (align)
+ g_signal_connect (G_OBJECT (checkbutton), "toggled",
+ G_CALLBACK (align_cell_toggled), data);
+ else
+ g_signal_connect (G_OBJECT (checkbutton), "toggled",
+ G_CALLBACK (expand_cell_toggled), data);
+}
+
gint
main (gint argc, gchar **argv)
{
GtkWidget *window;
GtkWidget *scrolled_window;
GtkWidget *tree_view;
+ GtkWidget *vbox, *hbox, *cntl_vbox;
GtkTreeModel *tree_model;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
+ GtkCellArea *area;
+ CallbackData callback[4];
gtk_init (&argc, &argv);
@@ -147,10 +201,15 @@ main (gint argc, gchar **argv)
gtk_window_set_title (GTK_WINDOW (window), "GtkTreeView editing sample");
g_signal_connect (window, "destroy", gtk_main_quit, NULL);
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
- gtk_container_add (GTK_CONTAINER (window), scrolled_window);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
tree_model = create_model ();
tree_view = gtk_tree_view_new_with_model (tree_model);
@@ -160,33 +219,47 @@ main (gint argc, gchar **argv)
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "String");
+ area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
renderer = gtk_cell_renderer_pixbuf_new ();
- gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer,
"pixbuf", PIXBUF_COLUMN, NULL);
+ callback[0].area = area;
+ callback[0].renderer = renderer;
renderer = gtk_cell_renderer_text_new ();
- gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer,
"text", STRING_COLUMN,
"editable", IS_EDITABLE_COLUMN,
NULL);
+ callback[1].area = area;
+ callback[1].renderer = renderer;
g_signal_connect (renderer, "edited",
G_CALLBACK (edited), tree_model);
+
renderer = gtk_cell_renderer_text_new ();
- gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer,
"text", STRING_COLUMN,
"editable", IS_EDITABLE_COLUMN,
NULL);
+ callback[2].area = area;
+ callback[2].renderer = renderer;
g_signal_connect (renderer, "edited",
G_CALLBACK (edited), tree_model);
renderer = gtk_cell_renderer_pixbuf_new ();
- gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ g_object_set (renderer,
+ "xalign", 0.0,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer,
- "pixbuf", PIXBUF_COLUMN, NULL);
+ "pixbuf", LAST_PIXBUF_COLUMN, NULL);
+ callback[3].area = area;
+ callback[3].renderer = renderer;
+
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
renderer = gtk_cell_renderer_toggle_new ();
@@ -212,7 +285,31 @@ main (gint argc, gchar **argv)
gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
gtk_window_set_default_size (GTK_WINDOW (window),
- 800, 175);
+ 800, 250);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ /* Alignment controls */
+ cntl_vbox = gtk_vbox_new (FALSE, 2);
+ gtk_widget_show (cntl_vbox);
+ gtk_box_pack_start (GTK_BOX (hbox), cntl_vbox, FALSE, FALSE, 0);
+
+ create_control (cntl_vbox, 1, TRUE, &callback[0]);
+ create_control (cntl_vbox, 2, TRUE, &callback[1]);
+ create_control (cntl_vbox, 3, TRUE, &callback[2]);
+ create_control (cntl_vbox, 4, TRUE, &callback[3]);
+
+ /* Expand controls */
+ cntl_vbox = gtk_vbox_new (FALSE, 2);
+ gtk_widget_show (cntl_vbox);
+ gtk_box_pack_start (GTK_BOX (hbox), cntl_vbox, FALSE, FALSE, 0);
+
+ create_control (cntl_vbox, 1, FALSE, &callback[0]);
+ create_control (cntl_vbox, 2, FALSE, &callback[1]);
+ create_control (cntl_vbox, 3, FALSE, &callback[2]);
+ create_control (cntl_vbox, 4, FALSE, &callback[3]);
gtk_widget_show_all (window);
gtk_main ();
diff --git a/tests/testtreeview.c b/tests/testtreeview.c
index d28cb3520a..80efc90fb8 100644
--- a/tests/testtreeview.c
+++ b/tests/testtreeview.c
@@ -351,7 +351,7 @@ set_columns_type (GtkTreeView *tree_view, ColumnsType type)
gtk_tree_view_set_rules_hint (tree_view, TRUE);
rend = gtk_cell_renderer_text_new ();
-
+
col = gtk_tree_view_column_new_with_attributes ("Column 1",
rend,
"text", 1,
@@ -709,6 +709,9 @@ main (int argc,
gtk_init (&argc, &argv);
+ if (g_getenv ("RTL"))
+ gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
+
our_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) book_closed_xpm);
#if 0
diff --git a/tests/testverticalcells.c b/tests/testverticalcells.c
new file mode 100644
index 0000000000..e1a522d8d8
--- /dev/null
+++ b/tests/testverticalcells.c
@@ -0,0 +1,378 @@
+/* testverticalcells.c
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ * Tristan Van Berkom <tristanvb@openismus.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <gtk/gtk.h>
+
+typedef struct _TreeEntry TreeEntry;
+
+struct _TreeEntry {
+ const gchar *icon;
+ const gchar *info;
+ const gchar *description;
+ const gchar *fine_print;
+ const gchar *fine_print_color;
+ gint progress;
+ TreeEntry *entries;
+};
+
+enum {
+ ICON_COLUMN,
+ INFO_COLUMN,
+ DESCRIPTION_COLUMN,
+ FINE_PRINT_COLUMN,
+ FINE_PRINT_COLOR_COLUMN,
+ PROGRESS_COLUMN,
+ NUM_COLUMNS
+};
+
+
+static TreeEntry info_entries[] =
+ {
+ {
+ "gtk-execute",
+ "Will you\n"
+ "run this ?",
+ "Currently executing that thing... you might want to abort",
+ "and every day he went fishing for pigs in the sky",
+ "green",
+ 83,
+ NULL
+ },
+ {
+ "gtk-dialog-authentication",
+ "This is the\n"
+ "realest of the real",
+ "We are about to authenticate the actual realness, this could take some time",
+ "one day he caught a giant ogre who barked and barked and barked",
+ "purple",
+ 4,
+ NULL
+ },
+ { 0, },
+ };
+
+static TreeEntry directory_entries[] =
+ {
+ {
+ "gtk-edit",
+ "We can edit\n"
+ "things in here",
+ "Time to edit your directory, almost finished now",
+ "she thought the best remedy for daydreams was going to be sleep",
+ "dark sea green",
+ 99,
+ NULL
+ },
+ {
+ "gtk-file",
+ "You have a\n"
+ "file here",
+ "Who would of thought there would be a file in the directory ?",
+ "after taking loads of sleeping pills he could still hear the pigs barking",
+ "green yellow",
+ 33,
+ NULL
+ },
+ {
+ "gtk-dialog-question",
+ "Any questions ?",
+ "This file would like to ask you a question",
+ "so he decided that the fine print underneath the progress bar probably made no sense at all",
+ "lavender",
+ 73,
+ NULL
+ },
+ { 0, },
+ };
+
+static TreeEntry other_entries[] =
+ {
+ {
+ "gtk-zoom-fit",
+ "Thats the\n"
+ "perfect fit",
+ "Now fitting foo into bar using frobnicator",
+ "using his nifty wide angle lense, he was able to catch a 'dark salmon', it was no flying pig "
+ "however it was definitely a keeper",
+ "dark salmon",
+ 59,
+ NULL
+ },
+ {
+ "gtk-underline",
+ "Under the\n"
+ "line",
+ "Now underlining that this demo would look alot niftier with some real content",
+ "it was indeed strange to catch a red salmon while fishing for pigs in the deep sky blue.",
+ "deep sky blue",
+ 99,
+ NULL
+ },
+ };
+
+static TreeEntry add_entries[] =
+ {
+ {
+ "gtk-about",
+ "its about\n"
+ "to start",
+ "This is what it's all about",
+ "so he went ahead and added the 'gtk-about' icon to his story, thinking 'mint cream' would be the "
+ "right color for something like that",
+ "dark violet",
+ 1,
+ NULL
+ },
+ {
+ "gtk-zoom-in",
+ "Watch it\n"
+ "Zoom !",
+ "Now zooming into something",
+ "while fishing for pigs in the sky, maybe he would have caught something faster if only he had zoomed in",
+ "orchid",
+ 6,
+ NULL
+ },
+ {
+ "gtk-zoom-out",
+ "Zoom Zoom\n"
+ "Zoom !",
+ "Now zooming out of something else",
+ "the daydream had a much prettier picture over all once he had zoomed out for the wide angle, "
+ "jill agreed",
+ "dark magenta",
+ 46,
+ other_entries
+ },
+ { 0, },
+ };
+
+
+static TreeEntry main_entries[] =
+ {
+ {
+ "gtk-info",
+ "This is all\n"
+ "the info",
+ "We are currently informing you",
+ "once upon a time in a land far far away there was a guy named buba",
+ "red",
+ 64,
+ info_entries
+ },
+ {
+ "gtk-dialog-warning",
+ "This is a\n"
+ "warning",
+ "We would like to warn you that your laptop might explode after we're done",
+ "so he decided that he must be stark raving mad",
+ "orange",
+ 43,
+ NULL
+ },
+ {
+ "gtk-dialog-error",
+ "An error will\n"
+ "occur",
+ "Once we're done here, dont worry... an error will surely occur.",
+ "and went to a see a yellow physiotherapist who's name was jill",
+ "yellow",
+ 98,
+ NULL
+ },
+ {
+ "gtk-directory",
+ "The directory",
+ "Currently scanning your directories.",
+ "jill didnt know what to make of the barking pigs either so she fed him sleeping pills",
+ "brown",
+ 20,
+ directory_entries
+ },
+ {
+ "gtk-delete",
+ "Now deleting\n"
+ "the whole thing",
+ "Time to delete the sucker",
+ "and he decided to just delete the whole conversation since it didnt make sense to him",
+ "dark orange",
+ 26,
+ NULL
+ },
+ {
+ "gtk-add",
+ "Anything\n"
+ "to add ?",
+ "Now adding stuff... please be patient",
+ "but on second thought, maybe he had something to add so that things could make a little less sense.",
+ "maroon",
+ 72,
+ add_entries
+ },
+ {
+ "gtk-redo",
+ "Time to\n"
+ "do it again",
+ "For the hell of it, lets add the content to the treeview over and over again !",
+ "buba likes telling his story, so maybe he's going to tell it 20 times more.",
+ "deep pink",
+ 100,
+ NULL
+ },
+ { 0, },
+ };
+
+
+static void
+populate_model (GtkTreeStore *model,
+ GtkTreeIter *parent,
+ TreeEntry *entries)
+{
+ GtkTreeIter iter;
+ gint i;
+
+ for (i = 0; entries[i].info != NULL; i++)
+ {
+ gtk_tree_store_append (model, &iter, parent);
+ gtk_tree_store_set (model, &iter,
+ ICON_COLUMN, entries[i].icon,
+ INFO_COLUMN, entries[i].info,
+ DESCRIPTION_COLUMN, entries[i].description,
+ FINE_PRINT_COLUMN, entries[i].fine_print,
+ FINE_PRINT_COLOR_COLUMN, entries[i].fine_print_color,
+ PROGRESS_COLUMN, entries[i].progress,
+ -1);
+
+ if (entries[i].entries)
+ populate_model (model, &iter, entries[i].entries);
+ }
+}
+
+static GtkTreeModel *
+create_model (void)
+{
+ GtkTreeStore *model;
+ gint i;
+
+ model = gtk_tree_store_new (NUM_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+
+ for (i = 0; i < 20; i++)
+ populate_model (model, NULL, main_entries);
+
+ return GTK_TREE_MODEL (model);
+}
+
+gint
+main (gint argc, gchar **argv)
+{
+ GtkWidget *window;
+ GtkWidget *scrolled_window;
+ GtkWidget *tree_view;
+ GtkTreeModel *tree_model;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkCellArea *area;
+
+ gtk_init (&argc, &argv);
+
+ if (g_getenv ("RTL"))
+ gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Vertical cells in GtkTreeViewColumn example");
+ g_signal_connect (window, "destroy", gtk_main_quit, NULL);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (window), scrolled_window);
+
+ tree_model = create_model ();
+ tree_view = gtk_tree_view_new_with_model (tree_model);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
+
+ /* First column */
+ column = gtk_tree_view_column_new ();
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "stock-id", ICON_COLUMN, NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "scale", 1.2F, "weight", PANGO_WEIGHT_BOLD, NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "text", INFO_COLUMN,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ /* Second (vertical) column */
+ column = gtk_tree_view_column_new ();
+ area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (area), GTK_ORIENTATION_VERTICAL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, "editable", TRUE, NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "text", DESCRIPTION_COLUMN,
+ NULL);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "value", PROGRESS_COLUMN,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "scale", 0.6F, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "text", FINE_PRINT_COLUMN,
+ "foreground", FINE_PRINT_COLOR_COLUMN,
+ NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
+ gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ 800, 400);
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+
+ return 0;
+}