+static GObject *
+gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
+
+ if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
+ return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
+
+ return parent_buildable_iface->get_internal_child (buildable, builder, childname);
+}
+
+static void
+gtk_combo_box_remeasure (GtkComboBox *combo_box)
+{
+ GtkComboBoxPrivate *priv = combo_box->priv;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ if (!priv->model ||
+ !gtk_tree_model_get_iter_first (priv->model, &iter))
+ return;
+
+ priv->minimum_width = priv->natural_width = 0;
+
+ path = gtk_tree_path_new_from_indices (0, -1);
+
+ do
+ {
+ gint row_min = 0, row_nat = 0;
+
+ if (priv->cell_view)
+ gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
+ path, &row_min, &row_nat);
+
+ priv->minimum_width = MAX (priv->minimum_width, row_min);
+ priv->natural_width = MAX (priv->natural_width, row_nat);
+
+ gtk_tree_path_next (path);
+ }
+ while (gtk_tree_model_iter_next (priv->model, &iter));
+
+ gtk_tree_path_free (path);
+}
+
+
+static void
+gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
+ gint avail_width,
+ gint *min_height,
+ gint *nat_height)
+{
+ GtkWidget *child;
+ GtkComboBoxPrivate *priv = combo_box->priv;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gint child_min, child_nat;
+
+ child = gtk_bin_get_child (GTK_BIN (combo_box));
+
+ gtk_widget_get_preferred_height_for_width (child, avail_width,
+ &child_min, &child_nat);
+
+ if (!priv->model ||
+ !gtk_tree_model_get_iter_first (priv->model, &iter))
+ goto out;
+
+ path = gtk_tree_path_new_from_indices (0, -1);
+
+ do
+ {
+ gint row_min = 0, row_nat = 0;
+
+ if (priv->cell_view)
+ gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view),
+ path, avail_width,
+ &row_min, &row_nat);
+
+ child_min = MAX (child_min, row_min);
+ child_nat = MAX (child_nat, row_nat);
+
+ gtk_tree_path_next (path);
+ }
+ while (gtk_tree_model_iter_next (priv->model, &iter));
+
+ gtk_tree_path_free (path);
+
+ out:
+
+ if (min_height)
+ *min_height = child_min;
+ if (nat_height)
+ *nat_height = child_nat;
+}
+
+
+static void
+gtk_combo_box_get_preferred_width (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
+ GtkComboBoxPrivate *priv = combo_box->priv;
+ GtkStyle *style;
+ gint focus_width, focus_pad;
+ gint font_size, arrow_size;
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ PangoFontDescription *font_desc;
+ GtkWidget *child;
+ gint minimum_width, natural_width;
+ gint child_min, child_nat;
+
+ child = gtk_bin_get_child (GTK_BIN (widget));
+
+ /* common */
+ gtk_widget_get_preferred_width (child, &child_min, &child_nat);
+ gtk_combo_box_remeasure (combo_box);
+
+ child_min = MAX (child_min, priv->minimum_width);
+ child_nat = MAX (child_nat, priv->natural_width);
+
+ gtk_widget_style_get (GTK_WIDGET (widget),
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ "arrow-size", &arrow_size,
+ NULL);
+
+ font_desc = gtk_widget_get_style (child)->font_desc;
+ context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
+ metrics = pango_context_get_metrics (context, font_desc,
+ pango_context_get_language (context));
+ font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
+ pango_font_metrics_get_descent (metrics));
+ pango_font_metrics_unref (metrics);
+
+ arrow_size = MAX (arrow_size, font_size);
+
+ gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
+
+ if (!priv->tree_view)
+ {
+ /* menu mode */
+
+ if (priv->cell_view)
+ {
+ gint sep_width, arrow_width;
+ gint border_width, xthickness, xpad;
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
+ xthickness = gtk_widget_get_style (priv->button)->xthickness;
+
+ gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
+ gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
+
+ xpad = 2*(border_width + xthickness + focus_width + focus_pad);
+
+ minimum_width = child_min + sep_width + arrow_width + xpad;
+ natural_width = child_nat + sep_width + arrow_width + xpad;
+ }
+ else
+ {
+ gint but_width, but_nat_width;
+
+ gtk_widget_get_preferred_width (priv->button,
+ &but_width, &but_nat_width);
+
+ minimum_width = child_min + but_width;
+ natural_width = child_nat + but_nat_width;
+ }
+ }
+ else
+ {
+ /* list mode */
+ gint button_width, button_nat_width;
+
+ /* sample + frame */
+ minimum_width = child_min;
+ natural_width = child_nat;
+
+ minimum_width += 2 * focus_width;
+ natural_width += 2 * focus_width;
+
+ if (priv->cell_view_frame)
+ {
+ if (priv->has_frame)
+ {
+ gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
+ gint xpad = 2 * (border_width + gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame))->xthickness);
+
+ minimum_width += xpad;
+ natural_width += xpad;
+ }
+ }
+
+ /* the button */
+ gtk_widget_get_preferred_width (priv->button,
+ &button_width, &button_nat_width);
+
+ minimum_width += button_width;
+ natural_width += button_nat_width;
+ }
+
+ if (GTK_SHADOW_NONE != priv->shadow_type)
+ {
+ style = gtk_widget_get_style (GTK_WIDGET (widget));
+
+ minimum_width += 2 * style->xthickness;
+ natural_width += 2 * style->xthickness;
+ }
+
+ if (minimum_size)
+ *minimum_size = minimum_width;
+
+ if (natural_size)
+ *natural_size = natural_width;
+}
+
+static void
+gtk_combo_box_get_preferred_height (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ gint min_width;
+
+ /* Combo box is height-for-width only
+ * (so we always just reserve enough height for the minimum width) */
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
+}
+
+static void
+gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
+ gint avail_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ /* Combo box is height-for-width only
+ * (so we assume we always reserved enough height for the minimum width) */
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
+}
+
+
+static void
+gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
+ gint avail_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
+ GtkComboBoxPrivate *priv = combo_box->priv;
+ GtkStyle *style;
+ gint focus_width, focus_pad;
+ gint min_height, nat_height;
+ gint size;
+
+ gtk_widget_style_get (GTK_WIDGET (widget),
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ size = avail_size;
+
+ if (GTK_SHADOW_NONE != priv->shadow_type)
+ size -= gtk_widget_get_style (GTK_WIDGET (widget))->xthickness;
+
+ if (!priv->tree_view)
+ {
+ /* menu mode */
+ if (priv->cell_view)
+ {
+ GtkStyle *button_style;
+ /* calculate x/y padding and separator/arrow size */
+ gint sep_width, arrow_width, sep_height, arrow_height;
+ gint border_width, xthickness, ythickness, xpad, ypad;
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
+ button_style = gtk_widget_get_style (priv->button);
+
+ xthickness = button_style->xthickness;
+ ythickness = button_style->ythickness;
+
+ gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
+ gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
+ gtk_widget_get_preferred_height_for_width (priv->separator,
+ sep_width, &sep_height, NULL);
+ gtk_widget_get_preferred_height_for_width (priv->arrow,
+ arrow_width, &arrow_height, NULL);
+
+ xpad = 2*(border_width + xthickness + focus_width + focus_pad);
+ ypad = 2*(border_width + ythickness + focus_width + focus_pad);
+
+ size -= sep_width + arrow_width + xpad;
+
+ gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+
+ arrow_height = MAX (arrow_height, sep_height);
+ min_height = MAX (min_height, arrow_height);
+ nat_height = MAX (nat_height, arrow_height);
+
+ min_height += ypad;
+ nat_height += ypad;
+ }
+ else
+ {
+ /* there is a custom child widget inside (no priv->cell_view) */
+ gint but_width, but_height;
+
+ gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
+ gtk_widget_get_preferred_height_for_width (priv->button,
+ but_width, &but_height, NULL);
+
+ size -= but_width;
+
+ gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+
+ min_height = MAX (min_height, but_height);
+ nat_height = MAX (nat_height, but_height);
+ }
+ }
+ else
+ {
+ /* list mode */
+ gint but_width, but_height;
+ gint xpad = 0, ypad = 0;
+
+ gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
+ gtk_widget_get_preferred_height_for_width (priv->button,
+ but_width, &but_height, NULL);
+
+ if (priv->cell_view_frame && priv->has_frame)
+ {
+ GtkStyle *cell_style;
+ gint border_width;
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
+ cell_style = gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame));
+
+ xpad = 2 * (border_width + cell_style->xthickness);
+ ypad = 2 * (border_width + cell_style->ythickness);
+ }
+
+ size -= but_width;
+ size -= 2 * focus_width;
+ size -= xpad;
+
+ gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+
+ min_height = MAX (min_height, but_height);
+ nat_height = MAX (nat_height, but_height);
+
+ min_height += ypad;
+ nat_height += ypad;
+ }
+
+ if (GTK_SHADOW_NONE != priv->shadow_type)
+ {
+ style = gtk_widget_get_style (GTK_WIDGET (widget));
+
+ min_height += 2 * style->ythickness;
+ nat_height += 2 * style->ythickness;
+ }
+
+ if (minimum_size)
+ *minimum_size = min_height;
+
+ if (natural_size)
+ *natural_size = nat_height;
+}