--- /dev/null
+/* PSPPIRE - a graphical user interface for PSPP.
+ Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "pspp-widget-facade.h"
+
+#include <math.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+inset_rectangle (const GdkRectangle *src,
+ const GtkBorder *inset,
+ GdkRectangle *dst)
+{
+ dst->x = src->x + inset->left;
+ dst->y = src->y + inset->top;
+ dst->width = MAX (1, src->width - inset->left - inset->right);
+ dst->height = MAX (1, src->height - inset->top - inset->bottom);
+}
+
+static void
+thicken_border (gint x, gint y, GtkBorder *border)
+{
+ border->left += x;
+ border->right += x;
+ border->top += y;
+ border->bottom += y;
+}
+
+GtkStyle *
+facade_get_style (GtkWidget *base,
+ GType type1,
+ ...)
+{
+ GString path, class_path;
+ GType final_type;
+ GtkStyle *style;
+ va_list args;
+ GType type;
+
+ gtk_widget_path (base, NULL, &path.str, NULL);
+ path.len = path.allocated_len = strlen (path.str);
+
+ gtk_widget_class_path (base, NULL, &class_path.str, NULL);
+ class_path.len = class_path.allocated_len = strlen (class_path.str);
+
+ va_start (args, type1);
+ for (type = final_type = type1; type != 0; type = va_arg (args, GType))
+ {
+ const gchar *type_name = g_type_name (type);
+ g_string_append_printf (&path, ".%s", type_name);
+ g_string_append_printf (&class_path, ".%s", type_name);
+ final_type = type;
+ }
+ va_end (args);
+
+ style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (base),
+ path.str, class_path.str, final_type);
+
+ free (path.str);
+ free (class_path.str);
+
+ return style;
+}
+
+void
+facade_hbox_get_base_size_request (gint border_width,
+ gint spacing,
+ gint n_children,
+ GtkRequisition *request)
+{
+ request->width = border_width * 2;
+ if (n_children > 1)
+ request->width += spacing * (n_children - 1);
+
+ request->height = border_width * 2;
+}
+
+void
+facade_hbox_add_child_size_request (gint hbox_border_width,
+ const GtkRequisition *child_request,
+ gint child_padding,
+ GtkRequisition *request)
+{
+ request->width += child_request->width + child_padding * 2;
+ request->height = MAX (request->height,
+ hbox_border_width * 2 + child_request->height);
+}
+
+void
+facade_arrow_get_size_request (gint xpad,
+ gint ypad,
+ GtkRequisition *request)
+{
+#define MIN_ARROW_SIZE 15
+ request->width = MIN_ARROW_SIZE + xpad * 2;
+ request->height = MIN_ARROW_SIZE + ypad * 2;
+}
+
+void
+facade_alignment_get_size_request (gint border_width,
+ gint padding_left,
+ gint padding_right,
+ gint padding_top,
+ gint padding_bottom,
+ const GtkRequisition *child_request,
+ GtkRequisition *request)
+{
+ request->width = (border_width * 2 + padding_left + padding_right
+ + child_request->width);
+ request->height = (border_width * 2 + padding_top + padding_bottom
+ + child_request->height);
+}
+
+void
+facade_label_get_size_request (gint xpad,
+ gint ypad,
+ GtkWidget *base,
+ const char *text,
+ GtkRequisition *request)
+{
+ PangoLayout *layout;
+
+ layout = facade_label_get_layout (base, text);
+ facade_label_get_size_request_from_layout (xpad, ypad, layout, request);
+ g_object_unref (layout);
+}
+
+void
+facade_label_get_size_request_from_layout (gint xpad,
+ gint ypad,
+ PangoLayout *layout,
+ GtkRequisition *request)
+{
+ PangoRectangle logical_rect;
+
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+ request->width = xpad * 2 + PANGO_PIXELS (logical_rect.width);
+ request->height = ypad * 2 + PANGO_PIXELS (logical_rect.height);
+}
+
+PangoLayout *
+facade_label_get_layout (GtkWidget *base,
+ const char *text)
+{
+ PangoAlignment alignment;
+ PangoLayout *layout;
+ gboolean rtl;
+
+ rtl = gtk_widget_get_direction (base) == GTK_TEXT_DIR_RTL;
+ alignment = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+
+ layout = gtk_widget_create_pango_layout (base, text);
+ pango_layout_set_alignment (layout, alignment);
+
+ return layout;
+}
+
+static void
+facade_button_get_inner_border (GtkStyle *button_style,
+ GtkBorder *inner_border)
+{
+ GtkBorder *tmp_border;
+
+ gtk_style_get (button_style, GTK_TYPE_BUTTON,
+ "inner-border", &tmp_border,
+ NULL);
+
+ if (tmp_border)
+ {
+ *inner_border = *tmp_border;
+ gtk_border_free (tmp_border);
+ }
+ else
+ {
+ static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
+ *inner_border = default_inner_border;
+ }
+}
+
+void
+facade_button_get_size_request (gint border_width,
+ GtkWidget *base,
+ GtkStyle *button_style,
+ const GtkRequisition *child_request,
+ GtkRequisition *request)
+{
+ GtkBorder inner_border;
+ gint common_width;
+ gint focus_width;
+ gint focus_pad;
+
+ gtk_style_get (button_style, GTK_TYPE_BUTTON,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+ facade_button_get_inner_border (button_style, &inner_border);
+
+ common_width = 2 * (border_width + focus_width + focus_pad);
+ request->width = (common_width
+ + 2 * button_style->xthickness
+ + inner_border.left + inner_border.right
+ + child_request->width);
+ request->height = (common_width
+ + 2 * button_style->ythickness
+ + inner_border.top + inner_border.bottom
+ + child_request->height);
+}
+
+void
+facade_button_get_focus_inset (gint border_width,
+ GtkWidget *base,
+ GtkStyle *button_style,
+ GtkBorder *focus_inset)
+{
+ facade_button_get_inner_border (button_style, focus_inset);
+ thicken_border (border_width + button_style->xthickness,
+ border_width + button_style->ythickness,
+ focus_inset);
+}
+
+static void
+facade_button_get_label_inset (gint border_width,
+ GtkWidget *base,
+ GtkStyle *button_style,
+ GtkBorder *label_inset)
+{
+ gint focus_width;
+ gint focus_pad;
+
+ facade_button_get_focus_inset (border_width, base, button_style,
+ label_inset);
+
+ gtk_style_get (button_style, GTK_TYPE_BUTTON,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+ thicken_border (focus_width + focus_pad,
+ focus_width + focus_pad,
+ label_inset);
+}
+
+static void
+get_layout_location (GtkWidget *base,
+ const GdkRectangle *label_area,
+ PangoLayout *layout,
+ gint xpad,
+ gint ypad,
+ gfloat xalign,
+ gfloat yalign,
+ gint *x,
+ gint *y)
+{
+ PangoRectangle logical;
+ GtkRequisition req;
+
+ if (gtk_widget_get_direction (base) == GTK_TEXT_DIR_LTR)
+ xalign = xalign;
+ else
+ xalign = 1.0 - xalign;
+
+ pango_layout_get_pixel_extents (layout, NULL, &logical);
+
+ facade_label_get_size_request_from_layout (xpad, ypad, layout, &req);
+
+ *x = floor (label_area->x + xpad + xalign * (label_area->width - req.width));
+
+ if (gtk_widget_get_direction (base) == GTK_TEXT_DIR_LTR)
+ *x = MAX (*x, label_area->x + xpad);
+ else
+ *x = MIN (*x, label_area->x + label_area->width - xpad);
+ *x -= logical.x;
+
+ /* bgo#315462 - For single-line labels, *do* align the requisition with
+ * respect to the allocation, even if we are under-allocated. For multi-line
+ * labels, always show the top of the text when they are under-allocated.
+ * The rationale is this:
+ *
+ * - Single-line labels appear in GtkButtons, and it is very easy to get them
+ * to be smaller than their requisition. The button may clip the label,
+ * but the label will still be able to show most of itself and the focus
+ * rectangle. Also, it is fairly easy to read a single line of clipped
+ * text.
+ *
+ * - Multi-line labels should not be clipped to showing "something in the
+ * middle". You want to read the first line, at least, to get some
+ * context.
+ */
+ if (pango_layout_get_line_count (layout) == 1)
+ *y = floor (label_area->y + ypad
+ + (label_area->height - req.height) * yalign);
+ else
+ *y = floor (label_area->y + ypad
+ + MAX (((label_area->height - req.height) * yalign),
+ 0));
+}
+
+void
+facade_button_render (GtkWidget *base,
+ GdkDrawable *window,
+ GdkRectangle *expose_area,
+
+ GdkRectangle *button_area,
+ gint border_width,
+ GtkStyle *button_style,
+ GtkStateType state_type,
+
+ GtkStyle *label_style,
+ const gchar *label,
+ gint xpad,
+ gint ypad,
+ gfloat xalign,
+ gfloat yalign)
+{
+ GdkRectangle label_area;
+ PangoLayout *layout;
+ GtkBorder inset;
+ gint x, y;
+
+ /* Paint the button. */
+ gtk_paint_box (button_style, window,
+ state_type,
+ GTK_SHADOW_OUT, expose_area, base, "button",
+ button_area->x + border_width,
+ button_area->y + border_width,
+ button_area->width - border_width * 2,
+ button_area->height - border_width * 2);
+
+ /* Figure out where the label should go. */
+ facade_button_get_label_inset (border_width, base, button_style, &inset);
+ inset_rectangle (button_area, &inset, &label_area);
+
+ /* Paint the label. */
+ layout = facade_label_get_layout (base, label);
+ get_layout_location (base, &label_area, layout, xpad, ypad, xalign, yalign,
+ &x, &y);
+ gtk_paint_layout (label_style, window, state_type, FALSE, expose_area,
+ base, "label", x, y, layout);
+ g_object_unref (layout);
+}
+
--- /dev/null
+/* PSPPIRE - a graphical user interface for PSPP.
+ Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef FACADE_WIDGET_FACADE_H
+#define FACADE_WIDGET_FACADE_H 1
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GtkStyle *facade_get_style (GtkWidget *base, GType, ...);
+
+void facade_hbox_get_base_size_request (gint border_width,
+ gint spacing,
+ gint n_children,
+ GtkRequisition *);
+void facade_hbox_add_child_size_request (gint hbox_border_width,
+ const GtkRequisition *child_request,
+ gint child_padding,
+ GtkRequisition *);
+
+void facade_arrow_get_size_request (gint xpad,
+ gint ypad,
+ GtkRequisition *);
+
+
+void facade_alignment_get_size_request (gint border_width,
+ gint padding_left,
+ gint padding_right,
+ gint padding_top,
+ gint padding_bottom,
+ const GtkRequisition *child_request,
+ GtkRequisition *);
+
+void facade_label_get_size_request (gint xpad,
+ gint ypad,
+ GtkWidget *base,
+ const char *text,
+ GtkRequisition *);
+void facade_label_get_size_request_from_layout (gint xpad,
+ gint ypad,
+ PangoLayout *,
+ GtkRequisition *);
+PangoLayout *facade_label_get_layout (GtkWidget *base,
+ const char *text);
+
+void facade_button_get_size_request (gint border_width,
+ GtkWidget *base,
+ GtkStyle *button_style,
+ const GtkRequisition *child_request,
+ GtkRequisition *);
+void facade_button_render (GtkWidget *base,
+ GdkDrawable *window,
+ GdkRectangle *expose_area,
+
+ GdkRectangle *button_area,
+ gint border_width,
+ GtkStyle *button_style,
+ GtkStateType state_type,
+
+ GtkStyle *label_style,
+ const gchar *label,
+ gint xpad,
+ gint ypad,
+ gfloat xalign,
+ gfloat yalign);
+void facade_button_get_focus_inset (gint border_width,
+ GtkWidget *base,
+ GtkStyle *button_style,
+ GtkBorder *focus_inset);
+
+G_END_DECLS
+
+#endif /* FACADE_WIDGET_FACADE_H */