+static void
+gtk_scale_get_preferred_width (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GTK_WIDGET_CLASS (gtk_scale_parent_class)->get_preferred_width (widget, minimum, natural);
+
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL)
+ {
+ gint n1, w1, h1, n2, w2, h2;
+ gint slider_length;
+ gint w;
+
+ gtk_widget_style_get (widget, "slider-length", &slider_length, NULL);
+
+ gtk_scale_get_mark_label_size (GTK_SCALE (widget), GTK_POS_TOP, &n1, &w1, &h1, &n2, &w2, &h2);
+
+ w1 = (n1 - 1) * w1 + MAX (w1, slider_length);
+ w2 = (n2 - 1) * w2 + MAX (w2, slider_length);
+ w = MAX (w1, w2);
+
+ *minimum = MAX (*minimum, w);
+ *natural = MAX (*natural, w);
+ }
+}
+
+static void
+gtk_scale_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GTK_WIDGET_CLASS (gtk_scale_parent_class)->get_preferred_height (widget, minimum, natural);
+
+
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_VERTICAL)
+ {
+ gint n1, w1, h1, n2, w2, h2;
+ gint slider_length;
+ gint h;
+
+ gtk_widget_style_get (widget, "slider-length", &slider_length, NULL);
+
+ gtk_scale_get_mark_label_size (GTK_SCALE (widget), GTK_POS_TOP, &n1, &w1, &h1, &n2, &w2, &h2);
+ h1 = (n1 - 1) * h1 + MAX (h1, slider_length);
+ h2 = (n2 - 1) * h1 + MAX (h2, slider_length);
+ h = MAX (h1, h2);
+
+ *minimum = MAX (*minimum, h);
+ *natural = MAX (*natural, h);
+ }
+}
+
+static gint
+find_next_pos (GtkWidget *widget,
+ GSList *list,
+ gint *marks,
+ GtkPositionType pos)
+{
+ GtkAllocation allocation;
+ GSList *m;
+ gint i;
+
+ for (m = list->next, i = 1; m; m = m->next, i++)
+ {
+ GtkScaleMark *mark = m->data;
+
+ if (mark->position == pos)
+ return marks[i];
+ }
+
+ gtk_widget_get_allocation (widget, &allocation);
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL)
+ return allocation.width;
+ else
+ return allocation.height;
+}
+
+static gboolean
+gtk_scale_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GtkScale *scale = GTK_SCALE (widget);
+ GtkScalePrivate *priv = scale->priv;
+ GtkRange *range = GTK_RANGE (scale);
+ GtkStyleContext *context;
+ gint *marks;
+ gint focus_padding;
+ gint slider_width;
+ gint value_spacing;
+ gint min_sep = 4;
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_widget_style_get (widget,
+ "focus-padding", &focus_padding,
+ "slider-width", &slider_width,
+ "value-spacing", &value_spacing,
+ NULL);
+
+ /* We need to chain up _first_ so the various geometry members of
+ * GtkRange struct are updated.
+ */
+ GTK_WIDGET_CLASS (gtk_scale_parent_class)->draw (widget, cr);
+
+ if (priv->marks)
+ {
+ GtkOrientation orientation;
+ GdkRectangle range_rect;
+ gint i;
+ gint x1, x2, x3, y1, y2, y3;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+ GSList *m;
+ gint min_pos_before, min_pos_after;
+ gint min_pos, max_pos;
+
+ orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (range));
+ _gtk_range_get_stop_positions (range, &marks);
+
+ layout = gtk_widget_create_pango_layout (widget, NULL);
+ gtk_range_get_range_rect (range, &range_rect);
+
+ min_pos_before = min_pos_after = 0;
+
+ for (m = priv->marks, i = 0; m; m = m->next, i++)
+ {
+ GtkScaleMark *mark = m->data;
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ x1 = marks[i];
+ if (mark->position == GTK_POS_TOP)
+ {
+ y1 = range_rect.y;
+ y2 = y1 - slider_width / 2;
+ min_pos = min_pos_before;
+ max_pos = find_next_pos (widget, m, marks + i, GTK_POS_TOP) - min_sep;
+ }
+ else
+ {
+ y1 = range_rect.y + range_rect.height;
+ y2 = y1 + slider_width / 2;
+ min_pos = min_pos_after;
+ max_pos = find_next_pos (widget, m, marks + i, GTK_POS_BOTTOM) - min_sep;
+ }
+
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_MARK);
+
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR);
+ gtk_render_line (context, cr, x1, y1, x1, y2);
+ gtk_style_context_remove_class (context, GTK_STYLE_CLASS_SEPARATOR);
+
+ if (mark->markup)
+ {
+ pango_layout_set_markup (layout, mark->markup, -1);
+ pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
+
+ x3 = x1 - logical_rect.width / 2;
+ if (x3 < min_pos)
+ x3 = min_pos;
+ if (x3 + logical_rect.width > max_pos)
+ x3 = max_pos - logical_rect.width;
+ if (x3 < 0)
+ x3 = 0;
+ if (mark->position == GTK_POS_TOP)
+ {
+ y3 = y2 - value_spacing - logical_rect.height;
+ min_pos_before = x3 + logical_rect.width + min_sep;
+ }
+ else
+ {
+ y3 = y2 + value_spacing;
+ min_pos_after = x3 + logical_rect.width + min_sep;
+ }
+
+ gtk_render_layout (context, cr, x3, y3, layout);
+ }
+
+ gtk_style_context_restore (context);
+ }
+ else
+ {
+ if (mark->position == GTK_POS_TOP)
+ {
+ x1 = range_rect.x;
+ x2 = range_rect.x - slider_width / 2;
+ min_pos = min_pos_before;
+ max_pos = find_next_pos (widget, m, marks + i, GTK_POS_TOP) - min_sep;
+ }
+ else
+ {
+ x1 = range_rect.x + range_rect.width;
+ x2 = range_rect.x + range_rect.width + slider_width / 2;
+ min_pos = min_pos_after;
+ max_pos = find_next_pos (widget, m, marks + i, GTK_POS_BOTTOM) - min_sep;
+ }
+ y1 = marks[i];
+
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_MARK);
+
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR);
+ gtk_render_line (context, cr, x1, y1, x2, y1);
+ gtk_style_context_remove_class (context, GTK_STYLE_CLASS_SEPARATOR);
+
+ if (mark->markup)
+ {
+ pango_layout_set_markup (layout, mark->markup, -1);
+ pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
+
+ y3 = y1 - logical_rect.height / 2;
+ if (y3 < min_pos)
+ y3 = min_pos;
+ if (y3 + logical_rect.height > max_pos)
+ y3 = max_pos - logical_rect.height;
+ if (y3 < 0)
+ y3 = 0;
+ if (mark->position == GTK_POS_TOP)
+ {
+ x3 = x2 - value_spacing - logical_rect.width;
+ min_pos_before = y3 + logical_rect.height + min_sep;
+ }
+ else
+ {
+ x3 = x2 + value_spacing;
+ min_pos_after = y3 + logical_rect.height + min_sep;
+ }
+
+ gtk_render_layout (context, cr, x3, y3, layout);
+ }
+
+ gtk_style_context_restore (context);
+ }
+ }
+
+ g_object_unref (layout);
+ g_free (marks);
+ }
+
+ if (priv->draw_value)
+ {
+ GtkAllocation allocation;
+
+ PangoLayout *layout;
+ gint x, y;
+
+ layout = gtk_scale_get_layout (scale);
+ gtk_scale_get_layout_offsets (scale, &x, &y);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_render_layout (context, cr,
+ x - allocation.x,
+ y - allocation.y,
+ layout);
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_scale_real_get_layout_offsets (GtkScale *scale,
+ gint *x,
+ gint *y)
+{
+ GtkScalePrivate *priv = scale->priv;
+ GtkAllocation allocation;
+ GtkWidget *widget = GTK_WIDGET (scale);
+ GtkRange *range = GTK_RANGE (widget);
+ GdkRectangle range_rect;
+ PangoLayout *layout = gtk_scale_get_layout (scale);
+ PangoRectangle logical_rect;
+ gint slider_start, slider_end;
+ gint value_spacing;
+
+ if (!layout)
+ {
+ *x = 0;
+ *y = 0;
+
+ return;
+ }
+
+ gtk_widget_style_get (widget, "value-spacing", &value_spacing, NULL);
+
+ pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
+
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_range_get_range_rect (range, &range_rect);
+ gtk_range_get_slider_range (range,
+ &slider_start,
+ &slider_end);
+
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) == GTK_ORIENTATION_HORIZONTAL)
+ {
+ switch (priv->value_pos)
+ {
+ case GTK_POS_LEFT:
+ *x = range_rect.x - value_spacing - logical_rect.width;
+ *y = range_rect.y + (range_rect.height - logical_rect.height) / 2;
+ break;
+
+ case GTK_POS_RIGHT:
+ *x = range_rect.x + range_rect.width + value_spacing;
+ *y = range_rect.y + (range_rect.height - logical_rect.height) / 2;
+ break;
+
+ case GTK_POS_TOP:
+ *x = slider_start + (slider_end - slider_start - logical_rect.width) / 2;
+ *x = CLAMP (*x, 0, allocation.width - logical_rect.width);
+ *y = range_rect.y - logical_rect.height - value_spacing;
+ break;
+
+ case GTK_POS_BOTTOM:
+ *x = slider_start + (slider_end - slider_start - logical_rect.width) / 2;
+ *x = CLAMP (*x, 0, allocation.width - logical_rect.width);
+ *y = range_rect.y + range_rect.height + value_spacing;
+ break;
+
+ default:
+ g_return_if_reached ();
+ break;
+ }
+ }
+ else
+ {
+ switch (priv->value_pos)
+ {
+ case GTK_POS_LEFT:
+ *x = range_rect.x - logical_rect.width - value_spacing;
+ *y = slider_start + (slider_end - slider_start - logical_rect.height) / 2;
+ *y = CLAMP (*y, 0, allocation.height - logical_rect.height);
+ break;
+
+ case GTK_POS_RIGHT:
+ *x = range_rect.x + range_rect.width + value_spacing;
+ *y = slider_start + (slider_end - slider_start - logical_rect.height) / 2;
+ *y = CLAMP (*y, 0, allocation.height - logical_rect.height);
+ break;
+
+ case GTK_POS_TOP:
+ *x = range_rect.x + (range_rect.width - logical_rect.width) / 2;
+ *y = range_rect.y - logical_rect.height - value_spacing;
+ break;
+
+ case GTK_POS_BOTTOM:
+ *x = range_rect.x + (range_rect.width - logical_rect.width) / 2;
+ *y = range_rect.y + range_rect.height + value_spacing;
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+ }
+
+ *x += allocation.x;
+ *y += allocation.y;
+}
+