summaryrefslogtreecommitdiffstats
path: root/libgimpwidgets/gimpframe.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgimpwidgets/gimpframe.c')
-rw-r--r--libgimpwidgets/gimpframe.c372
1 files changed, 372 insertions, 0 deletions
diff --git a/libgimpwidgets/gimpframe.c b/libgimpwidgets/gimpframe.c
new file mode 100644
index 0000000..97fb487
--- /dev/null
+++ b/libgimpwidgets/gimpframe.c
@@ -0,0 +1,372 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpframe.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * 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, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimp3migration.h"
+#include "gimpframe.h"
+
+
+/**
+ * SECTION: gimpframe
+ * @title: GimpFrame
+ * @short_description: A widget providing a HIG-compliant subclass
+ * of #GtkFrame.
+ *
+ * A widget providing a HIG-compliant subclass of #GtkFrame.
+ **/
+
+
+#define DEFAULT_LABEL_SPACING 6
+#define DEFAULT_LABEL_BOLD TRUE
+
+#define GIMP_FRAME_INDENT_KEY "gimp-frame-indent"
+#define GIMP_FRAME_IN_EXPANDER_KEY "gimp-frame-in-expander"
+
+
+static void gimp_frame_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gimp_frame_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gimp_frame_style_set (GtkWidget *widget,
+ GtkStyle *previous);
+static gboolean gimp_frame_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+static void gimp_frame_child_allocate (GtkFrame *frame,
+ GtkAllocation *allocation);
+static void gimp_frame_label_widget_notify (GtkFrame *frame);
+static gint gimp_frame_get_indent (GtkWidget *widget);
+static gint gimp_frame_get_label_spacing (GtkFrame *frame);
+
+
+G_DEFINE_TYPE (GimpFrame, gimp_frame, GTK_TYPE_FRAME)
+
+#define parent_class gimp_frame_parent_class
+
+
+static void
+gimp_frame_class_init (GimpFrameClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->size_request = gimp_frame_size_request;
+ widget_class->size_allocate = gimp_frame_size_allocate;
+ widget_class->style_set = gimp_frame_style_set;
+ widget_class->expose_event = gimp_frame_expose_event;
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_boolean ("label-bold",
+ "Label Bold",
+ "Whether the frame's label should be bold",
+ DEFAULT_LABEL_BOLD,
+ G_PARAM_READABLE));
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_int ("label-spacing",
+ "Label Spacing",
+ "The spacing between the label and the frame content",
+ 0,
+ G_MAXINT,
+ DEFAULT_LABEL_SPACING,
+ G_PARAM_READABLE));
+}
+
+
+static void
+gimp_frame_init (GimpFrame *frame)
+{
+ g_signal_connect (frame, "notify::label-widget",
+ G_CALLBACK (gimp_frame_label_widget_notify),
+ NULL);
+}
+
+static void
+gimp_frame_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GtkFrame *frame = GTK_FRAME (widget);
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
+ GtkRequisition child_requisition;
+ gint border_width;
+
+ if (label_widget && gtk_widget_get_visible (label_widget))
+ {
+ gtk_widget_size_request (label_widget, requisition);
+ }
+ else
+ {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
+ requisition->height += gimp_frame_get_label_spacing (frame);
+
+ if (child && gtk_widget_get_visible (child))
+ {
+ gint indent = gimp_frame_get_indent (widget);
+
+ gtk_widget_size_request (child, &child_requisition);
+
+ requisition->width = MAX (requisition->width,
+ child_requisition.width + indent);
+ requisition->height += child_requisition.height;
+ }
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+ requisition->width += 2 * border_width;
+ requisition->height += 2 * border_width;
+}
+
+static void
+gimp_frame_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkFrame *frame = GTK_FRAME (widget);
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
+ GtkAllocation child_allocation;
+
+ /* must not chain up here */
+ gtk_widget_set_allocation (widget, allocation);
+
+ gimp_frame_child_allocate (frame, &child_allocation);
+
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_size_allocate (child, &child_allocation);
+
+ if (label_widget && gtk_widget_get_visible (label_widget))
+ {
+ GtkAllocation label_allocation;
+ GtkRequisition label_requisition;
+ gint border_width;
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+ gtk_widget_get_child_requisition (label_widget, &label_requisition);
+
+ label_allocation.x = allocation->x + border_width;
+ label_allocation.y = allocation->y + border_width;
+ label_allocation.width = MAX (label_requisition.width,
+ allocation->width - 2 * border_width);
+ label_allocation.height = label_requisition.height;
+
+ gtk_widget_size_allocate (label_widget, &label_allocation);
+ }
+}
+
+static void
+gimp_frame_child_allocate (GtkFrame *frame,
+ GtkAllocation *child_allocation)
+{
+ GtkWidget *widget = GTK_WIDGET (frame);
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ GtkAllocation allocation;
+ gint border_width;
+ gint spacing = 0;
+ gint indent = gimp_frame_get_indent (widget);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (frame));
+
+ if (label_widget && gtk_widget_get_visible (label_widget))
+ {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_child_requisition (label_widget, &child_requisition);
+ spacing += child_requisition.height;
+ }
+
+ spacing += gimp_frame_get_label_spacing (frame);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ child_allocation->x = border_width + indent;
+ else
+ child_allocation->x = border_width;
+
+ child_allocation->y = border_width + spacing;
+ child_allocation->width = MAX (1,
+ allocation.width - 2 * border_width - indent);
+ child_allocation->height = MAX (1,
+ allocation.height -
+ child_allocation->y - border_width);
+
+ child_allocation->x += allocation.x;
+ child_allocation->y += allocation.y;
+}
+
+static void
+gimp_frame_style_set (GtkWidget *widget,
+ GtkStyle *previous)
+{
+ /* font changes invalidate the indentation */
+ g_object_set_data (G_OBJECT (widget), GIMP_FRAME_INDENT_KEY, NULL);
+
+ /* for "label_bold" */
+ gimp_frame_label_widget_notify (GTK_FRAME (widget));
+}
+
+static gboolean
+gimp_frame_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ if (gtk_widget_is_drawable (widget))
+ {
+ GtkWidgetClass *widget_class = g_type_class_peek_parent (parent_class);
+
+ return widget_class->expose_event (widget, event);
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_frame_label_widget_notify (GtkFrame *frame)
+{
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+
+ if (label_widget)
+ {
+ GtkLabel *label = NULL;
+
+ if (GTK_IS_LABEL (label_widget))
+ {
+ gfloat xalign, yalign;
+
+ label = GTK_LABEL (label_widget);
+
+ gtk_frame_get_label_align (frame, &xalign, &yalign);
+ gtk_label_set_xalign (GTK_LABEL (label), xalign);
+ gtk_label_set_yalign (GTK_LABEL (label), yalign);
+ }
+ else if (GTK_IS_BIN (label_widget))
+ {
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (label_widget));
+
+ if (GTK_IS_LABEL (child))
+ label = GTK_LABEL (child);
+ }
+
+ if (label)
+ {
+ PangoAttrList *attrs = pango_attr_list_new ();
+ PangoAttribute *attr;
+ gboolean bold;
+
+ gtk_widget_style_get (GTK_WIDGET (frame), "label_bold", &bold, NULL);
+
+ attr = pango_attr_weight_new (bold ?
+ PANGO_WEIGHT_BOLD :
+ PANGO_WEIGHT_NORMAL);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert (attrs, attr);
+
+ gtk_label_set_attributes (label, attrs);
+
+ pango_attr_list_unref (attrs);
+ }
+ }
+}
+
+static gint
+gimp_frame_get_indent (GtkWidget *widget)
+{
+ gint width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ GIMP_FRAME_INDENT_KEY));
+
+ if (! width)
+ {
+ PangoLayout *layout;
+
+ /* the HIG suggests to use four spaces so do just that */
+ layout = gtk_widget_create_pango_layout (widget, " ");
+ pango_layout_get_pixel_size (layout, &width, NULL);
+ g_object_unref (layout);
+
+ g_object_set_data (G_OBJECT (widget),
+ GIMP_FRAME_INDENT_KEY, GINT_TO_POINTER (width));
+ }
+
+ return width;
+}
+
+static gint
+gimp_frame_get_label_spacing (GtkFrame *frame)
+{
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ gint spacing = 0;
+
+ if ((label_widget && gtk_widget_get_visible (label_widget)) ||
+ (g_object_get_data (G_OBJECT (frame), GIMP_FRAME_IN_EXPANDER_KEY)))
+ {
+ gtk_widget_style_get (GTK_WIDGET (frame),
+ "label_spacing", &spacing,
+ NULL);
+ }
+
+ return spacing;
+}
+
+/**
+ * gimp_frame_new:
+ * @label: text to set as the frame's title label (or %NULL for no title)
+ *
+ * Creates a #GimpFrame widget. A #GimpFrame is a HIG-compliant
+ * variant of #GtkFrame. It doesn't render a frame at all but
+ * otherwise behaves like a frame. The frame's title is rendered in
+ * bold and the frame content is indented four spaces as suggested by
+ * the GNOME HIG (see https://developer.gnome.org/hig/stable/).
+ *
+ * Return value: a new #GimpFrame widget
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_frame_new (const gchar *label)
+{
+ GtkWidget *frame;
+ gboolean expander = FALSE;
+
+ /* somewhat hackish, should perhaps be an object property of GimpFrame */
+ if (label && strcmp (label, "<expander>") == 0)
+ {
+ expander = TRUE;
+ label = NULL;
+ }
+
+ frame = g_object_new (GIMP_TYPE_FRAME,
+ "label", label,
+ NULL);
+
+ if (expander)
+ g_object_set_data (G_OBJECT (frame),
+ GIMP_FRAME_IN_EXPANDER_KEY, (gpointer) TRUE);
+
+ return frame;
+}