* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
#include "config.h"
#include "gtkcontainer.h"
+#include "gtkcontainerprivate.h"
#include <stdarg.h>
#include <string.h>
#include <gobject/gobjectnotifyqueue.c>
#include <gobject/gvaluecollector.h>
+#include "gtkadjustment.h"
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
#include "gtktypebuiltins.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtksizerequest.h"
+#include "gtksizerequestcacheprivate.h"
#include "gtkwidgetprivate.h"
#include "gtkwindow.h"
+#include "gtkassistant.h"
#include "gtkintl.h"
-#include "gtktoolbar.h"
-
+#include "gtkstylecontextprivate.h"
+#include "gtkwidgetpath.h"
+#include "a11y/gtkcontaineraccessible.h"
/**
* SECTION:gtkcontainer
* The second type of container can have more than one child; its purpose is to
* manage <emphasis>layout</emphasis>. This means that these containers assign
* sizes and positions to their children. For example, a #GtkHBox arranges its
- * children in a horizontal row, and a #GtkTable arranges the widgets it contains
+ * children in a horizontal row, and a #GtkGrid arranges the widgets it contains
* in a two-dimensional grid.
*
* <refsect2 id="container-geometry-management">
{
GtkWidget *focus_child;
+ guint resize_handler;
+ GdkFrameClock *resize_clock;
+
guint border_width : 16;
guint has_focus_chain : 1;
- guint need_resize : 1;
guint reallocate_redraws : 1;
+ guint resize_pending : 1;
+ guint restyle_pending : 1;
guint resize_mode : 2;
guint request_mode : 2;
};
static guint vadjustment_key_id = 0;
static const gchar hadjustment_key[] = "gtk-hadjustment";
static guint hadjustment_key_id = 0;
-static GSList *container_resize_queue = NULL;
static guint container_signals[LAST_SIGNAL] = { 0 };
static GtkWidgetClass *parent_class = NULL;
extern GParamSpecPool *_gtk_widget_child_property_pool;
GTK_TYPE_WIDGET);
g_type_class_add_private (class, sizeof (GtkContainerPrivate));
+
+ gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CONTAINER_ACCESSIBLE);
}
static void
const gchar *value)
{
GParamSpec *pspec;
- GValue gvalue = { 0, };
+ GValue gvalue = G_VALUE_INIT;
GError *error = NULL;
pspec = gtk_container_class_find_child_property
/**
* gtk_container_child_notify:
* @container: the #GtkContainer
- * @widget: the child widget
- * @child_property: the name of a chld property installed on
+ * @child: the child widget
+ * @child_property: the name of a child property installed on
* the class of @container
*
* Emits a #GtkWidget::child-notify signal for the
*/
void
gtk_container_child_notify (GtkContainer *container,
- GtkWidget *widget,
+ GtkWidget *child,
const gchar *child_property)
{
GObject *obj;
GParamSpec *pspec;
g_return_if_fail (GTK_IS_CONTAINER (container));
- g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GTK_IS_WIDGET (child));
g_return_if_fail (child_property != NULL);
- obj = G_OBJECT (widget);
+ obj = G_OBJECT (child);
if (obj->ref_count == 0)
return;
const GValue *value,
GObjectNotifyQueue *nqueue)
{
- GValue tmp_value = { 0, };
+ GValue tmp_value = G_VALUE_INIT;
GtkContainerClass *class = g_type_class_peek (pspec->owner_type);
/* provide a copy to work from, convert (if necessary) and validate */
g_return_if_fail (GTK_IS_CONTAINER (container));
g_return_if_fail (GTK_IS_WIDGET (child));
- g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (container));
g_object_ref (container);
g_object_ref (child);
name = first_property_name;
while (name)
{
- GValue value = { 0, };
+ GValue value = G_VALUE_INIT;
GParamSpec *pspec;
gchar *error;
g_return_if_fail (GTK_IS_CONTAINER (container));
g_return_if_fail (GTK_IS_WIDGET (child));
- g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (container));
g_return_if_fail (property_name != NULL);
g_return_if_fail (G_IS_VALUE (value));
G_OBJECT_TYPE_NAME (container));
else
{
- GValue *prop_value, tmp_value = { 0, };
+ GValue *prop_value, tmp_value = G_VALUE_INIT;
/* auto-conversion of the callers value type
*/
g_return_if_fail (GTK_IS_CONTAINER (container));
g_return_if_fail (GTK_IS_WIDGET (child));
- g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (container));
g_object_ref (container);
g_object_ref (child);
name = first_property_name;
while (name)
{
- GValue value = { 0, };
+ GValue value = G_VALUE_INIT;
gchar *error = NULL;
GParamSpec *pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
name,
g_return_if_fail (GTK_IS_CONTAINER (container));
g_return_if_fail (GTK_IS_WIDGET (child));
- g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (container));
g_return_if_fail (property_name != NULL);
g_return_if_fail (G_IS_VALUE (value));
* @container: a #GtkContainer
* @widget: a widget to be placed inside @container
* @first_prop_name: the name of the first child property to set
- * @Varargs: a %NULL-terminated list of property names and values, starting
- * with @first_prop_name
+ * @...: a %NULL-terminated list of property names and values, starting
+ * with @first_prop_name
*
* Adds @widget to @container, setting child properties at the same time.
* See gtk_container_add() and gtk_container_child_set() for more details.
- **/
+ */
void
gtk_container_add_with_properties (GtkContainer *container,
GtkWidget *widget,
* @container: a #GtkContainer
* @child: a widget which is a child of @container
* @first_prop_name: the name of the first property to set
- * @Varargs: a %NULL-terminated list of property names and values, starting
- * with @first_prop_name
+ * @...: a %NULL-terminated list of property names and values, starting
+ * with @first_prop_name
*
* Sets one or more child properties for @child and @container.
- **/
+ */
void
gtk_container_child_set (GtkContainer *container,
GtkWidget *child,
{
va_list var_args;
- g_return_if_fail (GTK_IS_CONTAINER (container));
- g_return_if_fail (GTK_IS_WIDGET (child));
- g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (container));
-
va_start (var_args, first_prop_name);
gtk_container_child_set_valist (container, child, first_prop_name, var_args);
va_end (var_args);
* @container: a #GtkContainer
* @child: a widget which is a child of @container
* @first_prop_name: the name of the first property to get
- * @Varargs: return location for the first property, followed
+ * @...: return location for the first property, followed
* optionally by more name/return location pairs, followed by %NULL
*
* Gets the values of one or more child properties for @child and @container.
- **/
+ */
void
gtk_container_child_get (GtkContainer *container,
GtkWidget *child,
{
va_list var_args;
- g_return_if_fail (GTK_IS_CONTAINER (container));
- g_return_if_fail (GTK_IS_WIDGET (child));
- g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (container));
-
va_start (var_args, first_prop_name);
gtk_container_child_get_valist (container, child, first_prop_name, var_args);
va_end (var_args);
* gtk_container_class_find_child_property:
* @cclass: (type GtkContainerClass): a #GtkContainerClass
* @property_name: the name of the child property to find
- * @returns: (transfer none): the #GParamSpec of the child property or
- * %NULL if @class has no child property with that name.
*
* Finds a child property of a container class by name.
+ *
+ * Returns: (transfer none): the #GParamSpec of the child property
+ * or %NULL if @class has no child property with that name.
*/
GParamSpec*
gtk_container_class_find_child_property (GObjectClass *cclass,
* gtk_container_class_list_child_properties:
* @cclass: (type GtkContainerClass): a #GtkContainerClass
* @n_properties: location to return the number of child properties found
- * @returns: (array length=n_properties) (transfer container): a newly
- * allocated %NULL-terminated array of #GParamSpec*. The
- * array must be freed with g_free().
*
* Returns all child properties of a container class.
+ *
+ * Returns: (array length=n_properties) (transfer container):
+ * a newly allocated %NULL-terminated array of #GParamSpec*.
+ * The array must be freed with g_free().
*/
GParamSpec**
gtk_container_class_list_child_properties (GObjectClass *cclass,
priv->focus_child = NULL;
priv->border_width = 0;
- priv->need_resize = FALSE;
priv->resize_mode = GTK_RESIZE_PARENT;
priv->reallocate_redraws = FALSE;
}
GtkContainer *container = GTK_CONTAINER (widget);
GtkContainerPrivate *priv = container->priv;
- if (_gtk_widget_get_resize_pending (GTK_WIDGET (container)))
+ if (priv->resize_pending)
_gtk_container_dequeue_resize_handler (container);
+ if (priv->restyle_pending)
+ priv->restyle_pending = FALSE;
+
if (priv->focus_child)
{
g_object_unref (priv->focus_child);
*
* Adds @widget to @container. Typically used for simple containers
* such as #GtkWindow, #GtkFrame, or #GtkButton; for more complicated
- * layout containers such as #GtkBox or #GtkTable, this function will
+ * layout containers such as #GtkBox or #GtkGrid, this function will
* pick default packing parameters that may not be correct. So
* consider functions such as gtk_box_pack_start() and
- * gtk_table_attach() as an alternative to gtk_container_add() in
+ * gtk_grid_attach() as an alternative to gtk_container_add() in
* those cases. A widget may be added to only one container at a time;
* you can't place the same widget inside two different containers.
**/
{
g_return_if_fail (GTK_IS_CONTAINER (container));
g_return_if_fail (GTK_IS_WIDGET (widget));
- g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (container));
+ g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (container) || GTK_IS_ASSISTANT (container));
g_signal_emit (container, container_signals[REMOVE], 0, widget);
}
_gtk_container_dequeue_resize_handler (GtkContainer *container)
{
g_return_if_fail (GTK_IS_CONTAINER (container));
- g_return_if_fail (_gtk_widget_get_resize_pending (GTK_WIDGET (container)));
+ g_return_if_fail (container->priv->resize_pending);
- container_resize_queue = g_slist_remove (container_resize_queue, container);
- _gtk_widget_set_resize_pending (GTK_WIDGET (container), FALSE);
+ container->priv->resize_pending = FALSE;
}
/**
container->priv->reallocate_redraws = needs_redraws ? TRUE : FALSE;
}
-static GtkContainer*
-gtk_container_get_resize_container (GtkContainer *container)
-{
- GtkWidget *parent;
- GtkWidget *widget = GTK_WIDGET (container);
-
- while ((parent = gtk_widget_get_parent (widget)))
+static void
+gtk_container_idle_sizer (GdkFrameClock *clock,
+ GtkContainer *container)
+{
+ /* We validate the style contexts in a single loop before even trying
+ * to handle resizes instead of doing validations inline.
+ * This is mostly necessary for compatibility reasons with old code,
+ * because both style_updated and size_allocate functions often change
+ * styles and so could cause infinite loops in this function.
+ *
+ * It's important to note that even an invalid style context returns
+ * sane values. So the result of an invalid style context will never be
+ * a program crash, but only a wrong layout or rendering.
+ */
+ if (container->priv->restyle_pending)
{
- widget = parent;
- if (GTK_IS_RESIZE_CONTAINER (widget))
- break;
- }
+ GtkBitmask *empty;
+ gint64 current_time;
- return GTK_IS_RESIZE_CONTAINER (widget) ? (GtkContainer*) widget : NULL;
-}
+ empty = _gtk_bitmask_new ();
+ current_time = g_get_monotonic_time ();
+
+ container->priv->restyle_pending = FALSE;
+ _gtk_style_context_validate (gtk_widget_get_style_context (GTK_WIDGET (container)),
+ current_time,
+ 0,
+ empty);
+
+ _gtk_bitmask_free (empty);
+ }
-static gboolean
-gtk_container_idle_sizer (gpointer data)
-{
/* we may be invoked with a container_resize_queue of NULL, because
* queue_resize could have been adding an extra idle function while
* the queue still got processed. we better just ignore such case
* than trying to explicitely work around them with some extra flags,
* since it doesn't cause any actual harm.
*/
- while (container_resize_queue)
+ if (container->priv->resize_pending)
{
- GSList *slist;
- GtkWidget *widget;
-
- slist = container_resize_queue;
- container_resize_queue = slist->next;
- widget = slist->data;
- g_slist_free_1 (slist);
+ container->priv->resize_pending = FALSE;
+ gtk_container_check_resize (container);
+ }
- _gtk_widget_set_resize_pending (widget, FALSE);
- gtk_container_check_resize (GTK_CONTAINER (widget));
+ if (!container->priv->restyle_pending && !container->priv->resize_pending)
+ {
+ _gtk_container_stop_idle_sizer (container);
}
+ else
+ {
+ gdk_frame_clock_request_phase (clock,
+ GDK_FRAME_CLOCK_PHASE_LAYOUT);
+ }
+}
- gdk_window_process_all_updates ();
+static void
+gtk_container_start_idle_sizer (GtkContainer *container)
+{
+ GdkFrameClock *clock;
- return FALSE;
+ if (container->priv->resize_handler != 0)
+ return;
+
+ clock = gtk_widget_get_frame_clock (GTK_WIDGET (container));
+ if (clock == NULL)
+ return;
+
+ container->priv->resize_clock = clock;
+ container->priv->resize_handler = g_signal_connect (clock, "layout",
+ G_CALLBACK (gtk_container_idle_sizer), container);
+ gdk_frame_clock_request_phase (clock,
+ GDK_FRAME_CLOCK_PHASE_LAYOUT);
+}
+
+void
+_gtk_container_stop_idle_sizer (GtkContainer *container)
+{
+ if (container->priv->resize_handler == 0)
+ return;
+
+ g_signal_handler_disconnect (container->priv->resize_clock,
+ container->priv->resize_handler);
+ container->priv->resize_handler = 0;
+ container->priv->resize_clock = NULL;
+}
+
+static void
+gtk_container_queue_resize_handler (GtkContainer *container)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (GTK_IS_RESIZE_CONTAINER (container));
+
+ widget = GTK_WIDGET (container);
+
+ if (gtk_widget_get_visible (widget) &&
+ (gtk_widget_is_toplevel (widget) ||
+ gtk_widget_get_realized (widget)))
+ {
+ switch (container->priv->resize_mode)
+ {
+ case GTK_RESIZE_QUEUE:
+ if (!container->priv->resize_pending)
+ {
+ container->priv->resize_pending = TRUE;
+ gtk_container_start_idle_sizer (container);
+ }
+ break;
+
+ case GTK_RESIZE_IMMEDIATE:
+ gtk_container_check_resize (container);
+ break;
+
+ case GTK_RESIZE_PARENT:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
}
static void
_gtk_container_queue_resize_internal (GtkContainer *container,
gboolean invalidate_only)
{
- GtkContainer *resize_container;
- GtkWidget *parent;
GtkWidget *widget;
g_return_if_fail (GTK_IS_CONTAINER (container));
widget = GTK_WIDGET (container);
- resize_container = gtk_container_get_resize_container (container);
-
- while (TRUE)
+ do
{
_gtk_widget_set_alloc_needed (widget, TRUE);
- _gtk_widget_set_width_request_needed (widget, TRUE);
- _gtk_widget_set_height_request_needed (widget, TRUE);
+ _gtk_size_request_cache_clear (_gtk_widget_peek_request_cache (widget));
- if ((resize_container && widget == GTK_WIDGET (resize_container)) ||
- !(parent = gtk_widget_get_parent (widget)))
+ if (GTK_IS_RESIZE_CONTAINER (widget))
break;
- widget = parent;
+ widget = gtk_widget_get_parent (widget);
}
+ while (widget);
- if (resize_container && !invalidate_only)
- {
- if (gtk_widget_get_visible (GTK_WIDGET (resize_container)) &&
- (gtk_widget_is_toplevel (GTK_WIDGET (resize_container)) ||
- gtk_widget_get_realized (GTK_WIDGET (resize_container))))
- {
- switch (resize_container->priv->resize_mode)
- {
- case GTK_RESIZE_QUEUE:
- if (!_gtk_widget_get_resize_pending (GTK_WIDGET (resize_container)))
- {
- _gtk_widget_set_resize_pending (GTK_WIDGET (resize_container), TRUE);
- if (container_resize_queue == NULL)
- gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE,
- gtk_container_idle_sizer,
- NULL, NULL);
- container_resize_queue = g_slist_prepend (container_resize_queue, resize_container);
- }
- break;
+ if (widget && !invalidate_only)
+ gtk_container_queue_resize_handler (GTK_CONTAINER (widget));
+}
+
+void
+_gtk_container_queue_restyle (GtkContainer *container)
+{
+ GtkContainerPrivate *priv;
- case GTK_RESIZE_IMMEDIATE:
- gtk_container_check_resize (resize_container);
- break;
+ g_return_if_fail (GTK_CONTAINER (container));
- case GTK_RESIZE_PARENT:
- g_assert_not_reached ();
- break;
- }
- }
- else
- {
- /* we need to let hidden resize containers know that something
- * changed while they where hidden (currently only evaluated by
- * toplevels).
- */
- resize_container->priv->need_resize = TRUE;
- }
- }
+ priv = container->priv;
+
+ if (priv->restyle_pending)
+ return;
+
+ gtk_container_start_idle_sizer (container);
+ priv->restyle_pending = TRUE;
}
/**
_gtk_container_queue_resize_internal (container, TRUE);
}
+void
+_gtk_container_maybe_start_idle_sizer (GtkContainer *container)
+{
+ if (container->priv->restyle_pending || container->priv->resize_pending)
+ gtk_container_start_idle_sizer (container);
+}
+
void
gtk_container_check_resize (GtkContainer *container)
{
container = GTK_CONTAINER (widget);
- if (!GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
+ if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
{
- parent_class->adjust_size_allocation (widget, orientation,
- minimum_size, natural_size, allocated_pos,
- allocated_size);
- return;
- }
-
- border_width = container->priv->border_width;
-
- *allocated_size -= border_width * 2;
-
- /* If we get a pathological too-small allocation to hold
- * even the border width, leave all allocation to the actual
- * widget, and leave x,y unchanged. (GtkWidget's min size is
- * 1x1 if you're wondering why <1 and not <0)
- *
- * As long as we have space, set x,y properly.
- */
+ border_width = container->priv->border_width;
- if (*allocated_size < 1)
- {
- *allocated_size += border_width * 2;
- }
- else
- {
+ *allocated_size -= border_width * 2;
*allocated_pos += border_width;
*minimum_size -= border_width * 2;
*natural_size -= border_width * 2;
static GtkSizeRequestMode
gtk_container_get_request_mode (GtkWidget *widget)
{
- GtkContainer *container = GTK_CONTAINER (widget);
- GtkContainerPrivate *priv = container->priv;
-
- /* Recalculate the request mode of the children by majority
- * vote whenever the internal content changes */
- if (_gtk_widget_get_width_request_needed (widget) ||
- _gtk_widget_get_height_request_needed (widget))
- {
- RequestModeCount count = { 0, 0 };
-
- gtk_container_forall (container, (GtkCallback)count_request_modes, &count);
+ GtkContainer *container = GTK_CONTAINER (widget);
+ RequestModeCount count = { 0, 0 };
- if (!count.hfw && !count.wfh)
- priv->request_mode = GTK_SIZE_REQUEST_CONSTANT_SIZE;
- else
- priv->request_mode = count.wfh > count.hfw ?
- GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
- GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
- }
+ gtk_container_forall (container, (GtkCallback)count_request_modes, &count);
- return priv->request_mode;
+ if (!count.hfw && !count.wfh)
+ return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+ else
+ return count.wfh > count.hfw ?
+ GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
+ GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}
/**
/* same as gtk_container_get_children, except it includes internals
*/
-static GList *
-gtk_container_get_all_children (GtkContainer *container)
+GList *
+_gtk_container_get_all_children (GtkContainer *container)
{
GList *children = NULL;
GList *classes;
context = gtk_widget_get_style_context (GTK_WIDGET (container));
- path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
+ path = _gtk_widget_create_path (GTK_WIDGET (container));
/* Copy any permanent classes to the path */
classes = gtk_style_context_list_classes (context);
g_list_free_1 (cur);
}
+ gtk_widget_path_append_for_widget (path, child);
+
return path;
}
if (priv->has_focus_chain)
children = g_list_copy (get_focus_chain (container));
else
- children = gtk_container_get_all_children (container);
+ children = _gtk_container_get_all_children (container);
if (priv->has_focus_chain &&
(direction == GTK_DIR_TAB_FORWARD ||
cairo_restore (cr);
}
-gboolean
-_gtk_container_get_need_resize (GtkContainer *container)
-{
- return container->priv->need_resize;
-}
-
-void
-_gtk_container_set_need_resize (GtkContainer *container,
- gboolean need_resize)
-{
- container->priv->need_resize = need_resize;
-}
-
gboolean
_gtk_container_get_reallocate_redraws (GtkContainer *container)
{
* @child: a child of @container
*
* Returns a newly created widget path representing all the widget hierarchy
- * from the toplevel down to @child (this one not being included).
+ * from the toplevel down to and including @child.
*
* Returns: A newly created #GtkWidgetPath
**/
gtk_container_get_path_for_child (GtkContainer *container,
GtkWidget *child)
{
+ GtkWidgetPath *path;
+
g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
g_return_val_if_fail (container == (GtkContainer *) gtk_widget_get_parent (child), NULL);
- return GTK_CONTAINER_GET_CLASS (container)->get_path_for_child (container, child);
+ path = GTK_CONTAINER_GET_CLASS (container)->get_path_for_child (container, child);
+ if (gtk_widget_path_get_object_type (path) != G_OBJECT_TYPE (child))
+ {
+ g_critical ("%s %p returned a widget path for type %s, but child is %s",
+ G_OBJECT_TYPE_NAME (container),
+ container,
+ g_type_name (gtk_widget_path_get_object_type (path)),
+ G_OBJECT_TYPE_NAME (child));
+ }
+
+ return path;
}