+static void
+gtk_spin_button_panel_nthchildize_context (GtkSpinButton *spin_button,
+ GtkStyleContext *context,
+ GdkWindow *panel)
+{
+ GtkSpinButtonPrivate *priv = spin_button->priv;
+ GtkWidget *widget = GTK_WIDGET (spin_button);
+ GtkWidgetPath *path, *siblings_path;
+ GtkTextDirection direction;
+ gint up_pos, down_pos;
+
+ /* We are a subclass of GtkEntry, which is not a GtkContainer, so we
+ * have to emulate what gtk_container_get_path_for_child() would do
+ * for the button panels
+ */
+ path = _gtk_widget_create_path (widget);
+ siblings_path = gtk_widget_path_new ();
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ direction = gtk_widget_get_direction (widget);
+
+ /* flip children order for RTL */
+ if (direction == GTK_TEXT_DIR_RTL)
+ {
+ up_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
+ down_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
+ gtk_widget_path_append_type (siblings_path, GTK_TYPE_ENTRY);
+ }
+ else
+ {
+ gtk_widget_path_append_type (siblings_path, GTK_TYPE_ENTRY);
+ down_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
+ up_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
+ }
+ }
+ else
+ {
+ up_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
+ gtk_widget_path_append_type (siblings_path, GTK_TYPE_ENTRY);
+ down_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
+ }
+
+ gtk_widget_path_iter_add_class (siblings_path, up_pos, GTK_STYLE_CLASS_BUTTON);
+ gtk_widget_path_iter_add_class (siblings_path, down_pos, GTK_STYLE_CLASS_BUTTON);
+
+ /* this allows compatibility for themes that use .spinbutton.button */
+ gtk_widget_path_iter_add_class (siblings_path, up_pos, GTK_STYLE_CLASS_SPINBUTTON);
+ gtk_widget_path_iter_add_class (siblings_path, down_pos, GTK_STYLE_CLASS_SPINBUTTON);
+
+ if (panel == priv->down_panel)
+ gtk_widget_path_append_with_siblings (path, siblings_path, down_pos);
+ else
+ gtk_widget_path_append_with_siblings (path, siblings_path, up_pos);
+
+ gtk_style_context_set_path (context, path);
+ gtk_widget_path_unref (path);
+ gtk_widget_path_unref (siblings_path);
+}
+
+static gboolean
+gtk_spin_button_panel_at_limit (GtkSpinButton *spin_button,
+ GdkWindow *panel)
+{
+ GtkSpinButtonPrivate *priv = spin_button->priv;
+ GdkWindow *effective_panel;
+
+ if (priv->wrap)
+ return FALSE;
+
+ if (gtk_adjustment_get_step_increment (priv->adjustment) > 0)
+ effective_panel = panel;
+ else
+ effective_panel = panel == priv->up_panel ? priv->down_panel : priv->up_panel;
+
+ if (effective_panel == priv->up_panel &&
+ (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_value (priv->adjustment) <= EPSILON))
+ return TRUE;
+
+ if (effective_panel == priv->down_panel &&
+ (gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) <= EPSILON))
+ return TRUE;
+
+ return FALSE;
+}
+
+static GtkStateFlags
+gtk_spin_button_panel_get_state (GtkSpinButton *spin_button,
+ GdkWindow *panel)
+{
+ GtkStateFlags state;
+ GtkSpinButtonPrivate *priv = spin_button->priv;
+
+ state = gtk_widget_get_state_flags (GTK_WIDGET (spin_button));
+
+ state &= ~(GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT);
+
+ if ((state & GTK_STATE_FLAG_INSENSITIVE) ||
+ gtk_spin_button_panel_at_limit (spin_button, panel) ||
+ !gtk_editable_get_editable (GTK_EDITABLE (spin_button)))
+ {
+ state |= GTK_STATE_FLAG_INSENSITIVE;
+ }
+ else
+ {
+ if (priv->click_child == panel)
+ state |= GTK_STATE_FLAG_ACTIVE;
+ else if (priv->in_child == panel &&
+ priv->click_child == NULL)
+ state |= GTK_STATE_FLAG_PRELIGHT;
+ }
+
+ return state;
+}
+
+static GtkStyleContext *
+gtk_spin_button_panel_get_context (GtkSpinButton *spin_button,
+ GdkWindow *panel)
+{
+ GtkStyleContext *context;
+
+ context = gtk_style_context_new ();
+ gtk_spin_button_panel_nthchildize_context (spin_button, context, panel);
+
+ return context;
+}
+
+static void
+gtk_spin_button_panel_get_size (GtkSpinButton *spin_button,
+ GdkWindow *panel,
+ gint *width,
+ gint *height)
+{
+ GtkBorder button_padding, button_border;
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ gint icon_size, w, h;
+
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
+ icon_size = MAX (w, h);
+
+ context = gtk_spin_button_panel_get_context (spin_button, panel);
+ state = gtk_spin_button_panel_get_state (spin_button, panel);
+
+ gtk_style_context_get_padding (context, state, &button_padding);
+ gtk_style_context_get_border (context, state, &button_border);
+
+ g_object_unref (context);
+
+ if (width)
+ *width = icon_size + button_padding.left + button_padding.right +
+ button_border.left + button_border.right;
+
+ if (height)
+ *height = icon_size + button_padding.top + button_padding.bottom +
+ button_border.top + button_border.bottom;
+}
+
+static void
+gtk_spin_button_panel_get_allocations (GtkSpinButton *spin_button,
+ GtkAllocation *down_allocation_out,
+ GtkAllocation *up_allocation_out)
+{
+ GtkWidget *widget = GTK_WIDGET (spin_button);
+ GtkSpinButtonPrivate *priv = spin_button->priv;
+ GtkAllocation spin_allocation, down_allocation, up_allocation, allocation;
+ GtkRequisition requisition;
+ gint req_height;
+ gint down_panel_width, down_panel_height;
+ gint up_panel_width, up_panel_height;
+ GtkStyleContext *context;
+ GtkBorder border;
+
+ gtk_widget_get_allocation (widget, &spin_allocation);
+ gtk_widget_get_preferred_size (widget, &requisition, NULL);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (spin_button));
+ gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border);
+
+ gtk_spin_button_panel_get_size (spin_button, priv->down_panel, &down_panel_width, &down_panel_height);
+ gtk_spin_button_panel_get_size (spin_button, priv->up_panel, &up_panel_width, &up_panel_height);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom (widget);
+
+ /* both panels have the same size, and they're as tall as the entry allocation,
+ * excluding margins
+ */
+ allocation.height = MIN (req_height, spin_allocation.height) - border.top - border.bottom;
+ allocation.y = spin_allocation.y + border.top + (spin_allocation.height - req_height) / 2;
+ down_allocation = up_allocation = allocation;
+
+ down_allocation.width = down_panel_width;
+ up_allocation.width = up_panel_width;
+
+ /* invert x axis allocation for RTL */
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ {
+ up_allocation.x = spin_allocation.x + border.left;
+ down_allocation.x = up_allocation.x + up_panel_width;
+ }
+ else
+ {
+ up_allocation.x = spin_allocation.x + spin_allocation.width - up_panel_width - border.right;
+ down_allocation.x = up_allocation.x - down_panel_width;
+ }
+ }
+ else
+ {
+ /* both panels have the same size, and they're as wide as the entry allocation */
+ allocation.width = spin_allocation.width;
+ allocation.x = spin_allocation.x;
+ down_allocation = up_allocation = allocation;
+
+ down_allocation.height = down_panel_height;
+ up_allocation.height = up_panel_height;
+
+ up_allocation.y = spin_allocation.y;
+ down_allocation.y = spin_allocation.y + spin_allocation.height - down_allocation.height;
+ }
+
+ if (down_allocation_out)
+ *down_allocation_out = down_allocation;
+ if (up_allocation_out)
+ *up_allocation_out = up_allocation;
+}
+
+static void
+gtk_spin_button_panel_draw (GtkSpinButton *spin_button,
+ cairo_t *cr,
+ GdkWindow *panel)
+{
+ GtkSpinButtonPrivate *priv = spin_button->priv;
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkWidget *widget;
+ gdouble width, height, x, y;
+ gint icon_width, icon_height;
+ GtkIconHelper *icon_helper;
+
+ widget = GTK_WIDGET (spin_button);
+
+ cairo_save (cr);
+ gtk_cairo_transform_to_window (cr, widget, panel);
+
+ context = gtk_spin_button_panel_get_context (spin_button, panel);
+ state = gtk_spin_button_panel_get_state (spin_button, panel);
+ gtk_style_context_set_state (context, state);
+
+ height = gdk_window_get_height (panel);
+ width = gdk_window_get_width (panel);
+
+ icon_helper = _gtk_icon_helper_new ();
+ _gtk_icon_helper_set_use_fallback (icon_helper, TRUE);
+
+ if (panel == priv->down_panel)
+ _gtk_icon_helper_set_icon_name (icon_helper, "list-remove-symbolic", GTK_ICON_SIZE_MENU);
+ else
+ _gtk_icon_helper_set_icon_name (icon_helper, "list-add-symbolic", GTK_ICON_SIZE_MENU);
+
+ _gtk_icon_helper_get_size (icon_helper, context,
+ &icon_width, &icon_height);
+
+ gtk_render_background (context, cr,
+ 0, 0, width, height);
+ gtk_render_frame (context, cr,
+ 0, 0, width, height);
+
+ x = floor ((width - icon_width) / 2.0);
+ y = floor ((height - icon_height) / 2.0);
+
+ _gtk_icon_helper_draw (icon_helper, context, cr,
+ x, y);
+ cairo_restore (cr);
+
+ g_object_unref (icon_helper);
+ g_object_unref (context);
+}
+