diff options
Diffstat (limited to 'eel/eel-labeled-image.c')
-rw-r--r-- | eel/eel-labeled-image.c | 2493 |
1 files changed, 2493 insertions, 0 deletions
diff --git a/eel/eel-labeled-image.c b/eel/eel-labeled-image.c new file mode 100644 index 00000000..76a23876 --- /dev/null +++ b/eel/eel-labeled-image.c @@ -0,0 +1,2493 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* eel-labeled-image.c - A labeled image. + + Copyright (C) 2000 Eazel, Inc. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Ramiro Estrugo <[email protected]> +*/ + +#include <config.h> +#include "eel-labeled-image.h" + +#include "eel-art-extensions.h" +#include "eel-art-gtk-extensions.h" +#include "eel-debug-drawing.h" +#include "eel-gtk-container.h" +#include "eel-gtk-extensions.h" +#include "eel-gtk-macros.h" +#include "eel-accessibility.h" +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <atk/atkimage.h> + +#define DEFAULT_SPACING 0 +#define DEFAULT_X_PADDING 0 +#define DEFAULT_Y_PADDING 0 +#define DEFAULT_X_ALIGNMENT 0.5 +#define DEFAULT_Y_ALIGNMENT 0.5 + +/* Signals */ +enum +{ + ACTIVATE, + LAST_SIGNAL +}; + +/* Arguments */ +enum +{ + PROP_0, + PROP_FILL, + PROP_LABEL, + PROP_LABEL_POSITION, + PROP_PIXBUF, + PROP_SHOW_IMAGE, + PROP_SHOW_LABEL, + PROP_SPACING, + PROP_X_ALIGNMENT, + PROP_X_PADDING, + PROP_Y_ALIGNMENT, + PROP_Y_PADDING +}; + +/* Detail member struct */ +struct EelLabeledImageDetails +{ + GtkWidget *image; + GtkWidget *label; + GtkPositionType label_position; + gboolean show_label; + gboolean show_image; + guint spacing; + float x_alignment; + float y_alignment; + int x_padding; + int y_padding; + int fixed_image_height; + gboolean fill; +}; + +/* derived types so we can add our accessibility interfaces */ +static GType eel_labeled_image_button_get_type (void); +static GType eel_labeled_image_check_button_get_type (void); +static GType eel_labeled_image_radio_button_get_type (void); +static GType eel_labeled_image_toggle_button_get_type (void); + + +static void eel_labeled_image_class_init (EelLabeledImageClass *labeled_image_class); +static void eel_labeled_image_init (EelLabeledImage *image); +static void eel_labeled_image_finalize (GObject *object); + + + +/* GObjectClass methods */ +static void eel_labeled_image_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void eel_labeled_image_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +/* GtkObjectClass methods */ +static void eel_labeled_image_destroy (GtkObject *object); + +/* GtkWidgetClass methods */ +static void eel_labeled_image_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static int eel_labeled_image_expose_event (GtkWidget *widget, + GdkEventExpose *event); +static void eel_labeled_image_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void eel_labeled_image_map (GtkWidget *widget); +static void eel_labeled_image_unmap (GtkWidget *widget); +static AtkObject *eel_labeled_image_get_accessible (GtkWidget *widget); + +/* GtkContainerClass methods */ +static void eel_labeled_image_add (GtkContainer *container, + GtkWidget *widget); +static void eel_labeled_image_remove (GtkContainer *container, + GtkWidget *widget); +static void eel_labeled_image_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +/* Private EelLabeledImage methods */ +static EelDimensions labeled_image_get_image_dimensions (const EelLabeledImage *labeled_image); +static EelDimensions labeled_image_get_label_dimensions (const EelLabeledImage *labeled_image); +static void labeled_image_ensure_label (EelLabeledImage *labeled_image); +static void labeled_image_ensure_image (EelLabeledImage *labeled_image); +static EelIRect labeled_image_get_content_bounds (const EelLabeledImage *labeled_image); +static EelDimensions labeled_image_get_content_dimensions (const EelLabeledImage *labeled_image); +static void labeled_image_update_alignments (EelLabeledImage *labeled_image); +static gboolean labeled_image_show_label (const EelLabeledImage *labeled_image); +static gboolean labeled_image_show_image (const EelLabeledImage *labeled_image); + +static guint labeled_image_signals[LAST_SIGNAL] = { 0 }; + +EEL_CLASS_BOILERPLATE (EelLabeledImage, eel_labeled_image, GTK_TYPE_CONTAINER) + +/* Class init methods */ +static void +eel_labeled_image_class_init (EelLabeledImageClass *labeled_image_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (labeled_image_class); + GtkObjectClass *object_class = GTK_OBJECT_CLASS (labeled_image_class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (labeled_image_class); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (labeled_image_class); + GtkBindingSet *binding_set; + + gobject_class->finalize = eel_labeled_image_finalize; + + /* GObjectClass */ + gobject_class->set_property = eel_labeled_image_set_property; + gobject_class->get_property = eel_labeled_image_get_property; + + /* GtkObjectClass */ + object_class->destroy = eel_labeled_image_destroy; + + /* GtkWidgetClass */ + widget_class->size_request = eel_labeled_image_size_request; + widget_class->size_allocate = eel_labeled_image_size_allocate; + widget_class->expose_event = eel_labeled_image_expose_event; + widget_class->map = eel_labeled_image_map; + widget_class->unmap = eel_labeled_image_unmap; + widget_class->get_accessible = eel_labeled_image_get_accessible; + + /* GtkContainerClass */ + container_class->add = eel_labeled_image_add; + container_class->remove = eel_labeled_image_remove; + container_class->forall = eel_labeled_image_forall; + + labeled_image_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (labeled_image_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EelLabeledImageClass, + activate), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + widget_class->activate_signal = labeled_image_signals[ACTIVATE]; + + binding_set = gtk_binding_set_by_class (gobject_class); + + gtk_binding_entry_add_signal (binding_set, + GDK_Return, 0, + "activate", 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KP_Enter, 0, + "activate", 0); + gtk_binding_entry_add_signal (binding_set, + GDK_space, 0, + "activate", 0); + + + /* Properties */ + g_object_class_install_property ( + gobject_class, + PROP_PIXBUF, + g_param_spec_object ("pixbuf", NULL, NULL, + GDK_TYPE_PIXBUF, G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_LABEL, + g_param_spec_string ("label", NULL, NULL, + "", G_PARAM_READWRITE)); + + + g_object_class_install_property ( + gobject_class, + PROP_LABEL_POSITION, + g_param_spec_enum ("label_position", NULL, NULL, + GTK_TYPE_POSITION_TYPE, + GTK_POS_BOTTOM, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_SHOW_LABEL, + g_param_spec_boolean ("show_label", NULL, NULL, + TRUE, G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_SHOW_IMAGE, + g_param_spec_boolean ("show_image", NULL, NULL, + TRUE, G_PARAM_READWRITE)); + + + g_object_class_install_property ( + gobject_class, + PROP_SPACING, + g_param_spec_uint ("spacing", NULL, NULL, + 0, + G_MAXINT, + DEFAULT_SPACING, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_X_PADDING, + g_param_spec_int ("x_padding", NULL, NULL, + 0, + G_MAXINT, + DEFAULT_X_PADDING, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_Y_PADDING, + g_param_spec_int ("y_padding", NULL, NULL, + 0, + G_MAXINT, + DEFAULT_Y_PADDING, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_X_ALIGNMENT, + g_param_spec_float ("x_alignment", NULL, NULL, + 0.0, + 1.0, + DEFAULT_X_ALIGNMENT, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_Y_ALIGNMENT, + g_param_spec_float ("y_alignment", NULL, NULL, + 0.0, + 1.0, + DEFAULT_Y_ALIGNMENT, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_FILL, + g_param_spec_boolean ("fill", NULL, NULL, + FALSE, + G_PARAM_READWRITE)); +} + +static void +eel_labeled_image_init (EelLabeledImage *labeled_image) +{ + gtk_widget_set_has_window (GTK_WIDGET (labeled_image), FALSE); + + labeled_image->details = g_new0 (EelLabeledImageDetails, 1); + labeled_image->details->show_label = TRUE; + labeled_image->details->show_image = TRUE; + labeled_image->details->label_position = GTK_POS_BOTTOM; + labeled_image->details->spacing = DEFAULT_SPACING; + labeled_image->details->x_padding = DEFAULT_X_PADDING; + labeled_image->details->y_padding = DEFAULT_Y_PADDING; + labeled_image->details->x_alignment = DEFAULT_X_ALIGNMENT; + labeled_image->details->y_alignment = DEFAULT_Y_ALIGNMENT; + labeled_image->details->fixed_image_height = 0; + + eel_labeled_image_set_fill (labeled_image, FALSE); +} + +static void +eel_labeled_image_finalize (GObject *object) +{ + EelLabeledImage *labeled_image; + + labeled_image = EEL_LABELED_IMAGE (object); + + g_free (labeled_image->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + + +static void +eel_labeled_image_destroy (GtkObject *object) +{ + EelLabeledImage *labeled_image; + + labeled_image = EEL_LABELED_IMAGE (object); + + if (labeled_image->details->image != NULL) + { + gtk_widget_destroy (labeled_image->details->image); + } + + if (labeled_image->details->label != NULL) + { + gtk_widget_destroy (labeled_image->details->label); + } + + EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} + +/* GObjectClass methods */ +static void +eel_labeled_image_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EelLabeledImage *labeled_image; + + g_assert (EEL_IS_LABELED_IMAGE (object)); + + labeled_image = EEL_LABELED_IMAGE (object); + + switch (property_id) + { + case PROP_PIXBUF: + eel_labeled_image_set_pixbuf (labeled_image, + g_value_get_object (value)); + break; + + case PROP_LABEL: + eel_labeled_image_set_text (labeled_image, g_value_get_string (value)); + break; + + case PROP_LABEL_POSITION: + eel_labeled_image_set_label_position (labeled_image, + g_value_get_enum (value)); + break; + + case PROP_SHOW_LABEL: + eel_labeled_image_set_show_label (labeled_image, + g_value_get_boolean (value)); + break; + + case PROP_SHOW_IMAGE: + eel_labeled_image_set_show_image (labeled_image, + g_value_get_boolean (value)); + break; + + case PROP_SPACING: + eel_labeled_image_set_spacing (labeled_image, + g_value_get_uint (value)); + break; + + case PROP_X_PADDING: + eel_labeled_image_set_x_padding (labeled_image, + g_value_get_int (value)); + break; + + case PROP_Y_PADDING: + eel_labeled_image_set_y_padding (labeled_image, + g_value_get_int (value)); + break; + + case PROP_X_ALIGNMENT: + eel_labeled_image_set_x_alignment (labeled_image, + g_value_get_float (value)); + break; + + case PROP_Y_ALIGNMENT: + eel_labeled_image_set_y_alignment (labeled_image, + g_value_get_float (value)); + break; + + case PROP_FILL: + eel_labeled_image_set_fill (labeled_image, + g_value_get_boolean (value)); + break; + default: + g_assert_not_reached (); + } +} + +static void +eel_labeled_image_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EelLabeledImage *labeled_image; + + g_assert (EEL_IS_LABELED_IMAGE (object)); + + labeled_image = EEL_LABELED_IMAGE (object); + + switch (property_id) + { + case PROP_LABEL: + if (labeled_image->details->label == NULL) + { + g_value_set_string (value, NULL); + } + else + { + g_value_set_string (value, + gtk_label_get_text (GTK_LABEL ( + labeled_image->details->label))); + } + break; + + case PROP_LABEL_POSITION: + g_value_set_enum (value, eel_labeled_image_get_label_position (labeled_image)); + break; + + case PROP_SHOW_LABEL: + g_value_set_boolean (value, eel_labeled_image_get_show_label (labeled_image)); + break; + + case PROP_SHOW_IMAGE: + g_value_set_boolean (value, eel_labeled_image_get_show_image (labeled_image)); + break; + + case PROP_SPACING: + g_value_set_uint (value, eel_labeled_image_get_spacing (labeled_image)); + break; + + case PROP_X_PADDING: + g_value_set_int (value, eel_labeled_image_get_x_padding (labeled_image)); + break; + + case PROP_Y_PADDING: + g_value_set_int (value, eel_labeled_image_get_y_padding (labeled_image)); + break; + + case PROP_X_ALIGNMENT: + g_value_set_float (value, eel_labeled_image_get_x_alignment (labeled_image)); + break; + + case PROP_Y_ALIGNMENT: + g_value_set_float (value, eel_labeled_image_get_y_alignment (labeled_image)); + break; + + case PROP_FILL: + g_value_set_boolean (value, eel_labeled_image_get_fill (labeled_image)); + break; + + default: + g_assert_not_reached (); + } +} + +/* GtkWidgetClass methods */ +static void +eel_labeled_image_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + EelLabeledImage *labeled_image; + EelDimensions content_dimensions; + + g_assert (EEL_IS_LABELED_IMAGE (widget)); + g_assert (requisition != NULL); + + labeled_image = EEL_LABELED_IMAGE (widget); + + content_dimensions = labeled_image_get_content_dimensions (labeled_image); + + requisition->width = + MAX (1, content_dimensions.width) + + 2 * labeled_image->details->x_padding; + + requisition->height = + MAX (1, content_dimensions.height) + + 2 * labeled_image->details->y_padding; +} + +static void +eel_labeled_image_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + EelLabeledImage *labeled_image; + EelIRect image_bounds; + EelIRect label_bounds; + + g_assert (EEL_IS_LABELED_IMAGE (widget)); + g_assert (allocation != NULL); + + labeled_image = EEL_LABELED_IMAGE (widget); + + gtk_widget_set_allocation (widget, allocation); + + label_bounds = eel_labeled_image_get_label_bounds (labeled_image); + eel_gtk_container_child_size_allocate (GTK_CONTAINER (widget), + labeled_image->details->label, + label_bounds); + + image_bounds = eel_labeled_image_get_image_bounds (labeled_image); + eel_gtk_container_child_size_allocate (GTK_CONTAINER (widget), + labeled_image->details->image, + image_bounds); +} + +static int +eel_labeled_image_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + EelLabeledImage *labeled_image; + EelIRect label_bounds; + GtkStyle *style; + GdkWindow *window; + + g_assert (EEL_IS_LABELED_IMAGE (widget)); + g_assert (gtk_widget_get_realized (widget)); + g_assert (event != NULL); + + labeled_image = EEL_LABELED_IMAGE (widget); + + style = gtk_widget_get_style (widget); + window = gtk_widget_get_window (widget); + if (gtk_widget_get_state (widget) == GTK_STATE_SELECTED || + gtk_widget_get_state (widget) == GTK_STATE_ACTIVE) + { + label_bounds = eel_labeled_image_get_label_bounds (EEL_LABELED_IMAGE (widget)); + + gtk_paint_flat_box (style, + window, + gtk_widget_get_state (widget), + GTK_SHADOW_NONE, + &event->area, + widget, + "eel-labeled-image", + label_bounds.x0, label_bounds.y0, + label_bounds.x1 - label_bounds.x0, + label_bounds.y1 - label_bounds.y0); + } + + if (labeled_image_show_label (labeled_image)) + { + eel_gtk_container_child_expose_event (GTK_CONTAINER (widget), + labeled_image->details->label, + event); + } + + if (labeled_image_show_image (labeled_image)) + { + eel_gtk_container_child_expose_event (GTK_CONTAINER (widget), + labeled_image->details->image, + event); + } + + if (gtk_widget_has_focus (widget)) + { + label_bounds = eel_labeled_image_get_image_bounds (EEL_LABELED_IMAGE (widget)); + gtk_paint_focus (style, window, + GTK_STATE_NORMAL, + &event->area, widget, + "eel-focusable-labeled-image", + label_bounds.x0, label_bounds.y0, + label_bounds.x1 - label_bounds.x0, + label_bounds.y1 - label_bounds.y0); + } + + return FALSE; +} + +static void +eel_labeled_image_map (GtkWidget *widget) +{ + EelLabeledImage *labeled_image; + + g_assert (EEL_IS_LABELED_IMAGE (widget)); + + labeled_image = EEL_LABELED_IMAGE (widget); + + gtk_widget_set_mapped (widget, TRUE); + + if (labeled_image_show_label (labeled_image)) + { + eel_gtk_container_child_map (GTK_CONTAINER (widget), labeled_image->details->label); + } + + if (labeled_image_show_image (labeled_image)) + { + eel_gtk_container_child_map (GTK_CONTAINER (widget), labeled_image->details->image); + } +} + +static void +eel_labeled_image_unmap (GtkWidget *widget) +{ + EelLabeledImage *labeled_image; + + g_assert (EEL_IS_LABELED_IMAGE (widget)); + + labeled_image = EEL_LABELED_IMAGE (widget); + + gtk_widget_set_mapped (widget, FALSE); + + eel_gtk_container_child_unmap (GTK_CONTAINER (widget), labeled_image->details->label); + eel_gtk_container_child_unmap (GTK_CONTAINER (widget), labeled_image->details->image); +} + +/* GtkContainerClass methods */ +static void +eel_labeled_image_add (GtkContainer *container, + GtkWidget *child) +{ + g_assert (GTK_IS_LABEL (child) || GTK_IS_IMAGE (child)); + + eel_gtk_container_child_add (container, child); +} + +static void +eel_labeled_image_remove (GtkContainer *container, + GtkWidget *child) +{ + EelLabeledImage *labeled_image; + + g_assert (GTK_IS_LABEL (child) || GTK_IS_IMAGE (child)); + + labeled_image = EEL_LABELED_IMAGE (container);; + + g_assert (child == labeled_image->details->image || child == labeled_image->details->label); + + eel_gtk_container_child_remove (container, child); + + if (labeled_image->details->image == child) + { + labeled_image->details->image = NULL; + } + + if (labeled_image->details->label == child) + { + labeled_image->details->label = NULL; + } +} + +static void +eel_labeled_image_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + EelLabeledImage *labeled_image; + + g_assert (EEL_IS_LABELED_IMAGE (container)); + g_assert (callback != NULL); + + labeled_image = EEL_LABELED_IMAGE (container); + + if (include_internals) + { + if (labeled_image->details->image != NULL) + { + (* callback) (labeled_image->details->image, callback_data); + } + + if (labeled_image->details->label != NULL) + { + (* callback) (labeled_image->details->label, callback_data); + } + } +} + +/* Private EelLabeledImage methods */ +static gboolean +is_fixed_height (const EelLabeledImage *labeled_image) +{ + return labeled_image->details->fixed_image_height > 0; +} + +static EelDimensions +labeled_image_get_image_dimensions (const EelLabeledImage *labeled_image) +{ + EelDimensions image_dimensions; + GtkRequisition image_requisition; + + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (!labeled_image_show_image (labeled_image)) + { + return eel_dimensions_empty; + } + + gtk_widget_size_request (labeled_image->details->image, &image_requisition); + + image_dimensions.width = (int) image_requisition.width; + image_dimensions.height = (int) image_requisition.height; + + if (is_fixed_height (labeled_image)) + { + image_dimensions.height = labeled_image->details->fixed_image_height; + } + + return image_dimensions; +} + +static EelDimensions +labeled_image_get_label_dimensions (const EelLabeledImage *labeled_image) +{ + EelDimensions label_dimensions; + GtkRequisition label_requisition; + + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (!labeled_image_show_label (labeled_image)) + { + return eel_dimensions_empty; + } + + gtk_widget_size_request (labeled_image->details->label, &label_requisition); + + label_dimensions.width = (int) label_requisition.width; + label_dimensions.height = (int) label_requisition.height; + + return label_dimensions; +} + +static EelIRect +labeled_image_get_image_bounds_fill (const EelLabeledImage *labeled_image) +{ + EelIRect image_bounds; + EelDimensions image_dimensions; + EelIRect content_bounds; + EelIRect bounds; + + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + image_dimensions = labeled_image_get_image_dimensions (labeled_image); + + if (eel_dimensions_are_empty (image_dimensions)) + { + return eel_irect_empty; + } + + content_bounds = labeled_image_get_content_bounds (labeled_image); + bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (labeled_image)); + + if (!labeled_image_show_label (labeled_image)) + { + image_bounds = bounds; + } + else + { + switch (labeled_image->details->label_position) + { + case GTK_POS_LEFT: + image_bounds.y0 = bounds.y0; + image_bounds.x0 = content_bounds.x1 - image_dimensions.width; + image_bounds.y1 = bounds.y1; + image_bounds.x1 = bounds.x1; + break; + + case GTK_POS_RIGHT: + image_bounds.y0 = bounds.y0; + image_bounds.x0 = bounds.x0; + image_bounds.y1 = bounds.y1; + image_bounds.x1 = content_bounds.x0 + image_dimensions.width; + break; + + case GTK_POS_TOP: + image_bounds.x0 = bounds.x0; + image_bounds.y0 = content_bounds.y1 - image_dimensions.height; + image_bounds.x1 = bounds.x1; + image_bounds.y1 = bounds.y1; + break; + + case GTK_POS_BOTTOM: + image_bounds.x0 = bounds.x0; + image_bounds.y0 = bounds.y0; + image_bounds.x1 = bounds.x1; + image_bounds.y1 = content_bounds.y0 + image_dimensions.height; + break; + + default: + image_bounds.x0 = 0; + image_bounds.y0 = 0; + image_bounds.x1 = 0; + image_bounds.y1 = 0; + g_assert_not_reached (); + } + } + + return image_bounds; +} + +EelIRect +eel_labeled_image_get_image_bounds (const EelLabeledImage *labeled_image) +{ + EelDimensions image_dimensions; + EelDimensions label_dimensions; + GtkRequisition image_requisition; + EelIRect image_bounds; + EelIRect content_bounds; + + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), eel_irect_empty); + + if (labeled_image->details->fill) + { + return labeled_image_get_image_bounds_fill (labeled_image); + } + + /* get true real dimensions if we're in fixed height mode */ + if (is_fixed_height (labeled_image) && labeled_image_show_image (labeled_image)) + { + gtk_widget_size_request (labeled_image->details->image, &image_requisition); + image_dimensions.width = (int) image_requisition.width; + image_dimensions.height = (int) image_requisition.height; + } + else + { + image_dimensions = labeled_image_get_image_dimensions (labeled_image); + } + + label_dimensions = labeled_image_get_label_dimensions (labeled_image); + + if (eel_dimensions_are_empty (image_dimensions)) + { + return eel_irect_empty; + } + + content_bounds = labeled_image_get_content_bounds (labeled_image); + + if (!labeled_image_show_label (labeled_image)) + { + image_bounds.x0 = + content_bounds.x0 + + (eel_irect_get_width (content_bounds) - image_dimensions.width) / 2; + image_bounds.y0 = + content_bounds.y0 + + (eel_irect_get_height (content_bounds) - image_dimensions.height) / 2; + } + else + { + switch (labeled_image->details->label_position) + { + case GTK_POS_LEFT: + image_bounds.x0 = content_bounds.x1 - image_dimensions.width; + image_bounds.y0 = + content_bounds.y0 + + (eel_irect_get_height (content_bounds) - image_dimensions.height) / 2; + break; + + case GTK_POS_RIGHT: + image_bounds.x0 = content_bounds.x0; + image_bounds.y0 = + content_bounds.y0 + + (eel_irect_get_height (content_bounds) - image_dimensions.height) / 2; + break; + + case GTK_POS_TOP: + image_bounds.x0 = + content_bounds.x0 + + (eel_irect_get_width (content_bounds) - image_dimensions.width) / 2; + image_bounds.y0 = content_bounds.y1 - image_dimensions.height; + break; + + case GTK_POS_BOTTOM: + image_bounds.x0 = + content_bounds.x0 + + (eel_irect_get_width (content_bounds) - image_dimensions.width) / 2; + + if (is_fixed_height (labeled_image)) + { + image_bounds.y0 = content_bounds.y0 + eel_irect_get_height (content_bounds) + - image_dimensions.height + - label_dimensions.height + - labeled_image->details->spacing; + } + else + { + image_bounds.y0 = content_bounds.y0; + } + + break; + + default: + image_bounds.x0 = 0; + image_bounds.y0 = 0; + g_assert_not_reached (); + } + } + + image_bounds.x1 = image_bounds.x0 + image_dimensions.width; + image_bounds.y1 = image_bounds.y0 + image_dimensions.height; + + return image_bounds; +} + +static EelIRect +labeled_image_get_label_bounds_fill (const EelLabeledImage *labeled_image) +{ + EelIRect label_bounds; + EelDimensions label_dimensions; + EelIRect content_bounds; + EelIRect bounds; + + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + label_dimensions = labeled_image_get_label_dimensions (labeled_image); + + if (eel_dimensions_are_empty (label_dimensions)) + { + return eel_irect_empty; + } + + content_bounds = labeled_image_get_content_bounds (labeled_image); + bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (labeled_image)); + + /* Only the label is shown */ + if (!labeled_image_show_image (labeled_image)) + { + label_bounds = bounds; + /* Both label and image are shown */ + } + else + { + switch (labeled_image->details->label_position) + { + case GTK_POS_LEFT: + label_bounds.y0 = bounds.y0; + label_bounds.x0 = bounds.x0; + label_bounds.y1 = bounds.y1; + label_bounds.x1 = content_bounds.x0 + label_dimensions.width; + break; + + case GTK_POS_RIGHT: + label_bounds.y0 = bounds.y0; + label_bounds.x0 = content_bounds.x1 - label_dimensions.width; + label_bounds.y1 = bounds.y1; + label_bounds.x1 = bounds.x1; + break; + + case GTK_POS_TOP: + label_bounds.x0 = bounds.x0; + label_bounds.y0 = bounds.y0; + label_bounds.x1 = bounds.x1; + label_bounds.y1 = content_bounds.y0 + label_dimensions.height; + break; + + case GTK_POS_BOTTOM: + label_bounds.x0 = bounds.x0; + label_bounds.y0 = content_bounds.y1 - label_dimensions.height; + label_bounds.x1 = bounds.x1; + label_bounds.y1 = bounds.y1; + break; + + default: + label_bounds.x0 = 0; + label_bounds.y0 = 0; + label_bounds.x1 = 0; + label_bounds.y1 = 0; + g_assert_not_reached (); + } + } + + return label_bounds; +} + +EelIRect +eel_labeled_image_get_label_bounds (const EelLabeledImage *labeled_image) +{ + EelIRect label_bounds; + EelDimensions label_dimensions; + EelIRect content_bounds; + + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), eel_irect_empty); + + if (labeled_image->details->fill) + { + return labeled_image_get_label_bounds_fill (labeled_image); + } + + label_dimensions = labeled_image_get_label_dimensions (labeled_image); + + if (eel_dimensions_are_empty (label_dimensions)) + { + return eel_irect_empty; + } + + content_bounds = labeled_image_get_content_bounds (labeled_image); + + /* Only the label is shown */ + if (!labeled_image_show_image (labeled_image)) + { + label_bounds.x0 = + content_bounds.x0 + + (eel_irect_get_width (content_bounds) - label_dimensions.width) / 2; + label_bounds.y0 = + content_bounds.y0 + + (eel_irect_get_height (content_bounds) - label_dimensions.height) / 2; + /* Both label and image are shown */ + } + else + { + switch (labeled_image->details->label_position) + { + case GTK_POS_LEFT: + label_bounds.x0 = content_bounds.x0; + label_bounds.y0 = + content_bounds.y0 + + (eel_irect_get_height (content_bounds) - label_dimensions.height) / 2; + break; + + case GTK_POS_RIGHT: + label_bounds.x0 = content_bounds.x1 - label_dimensions.width; + label_bounds.y0 = + content_bounds.y0 + + (eel_irect_get_height (content_bounds) - label_dimensions.height) / 2; + break; + + case GTK_POS_TOP: + label_bounds.x0 = + content_bounds.x0 + + (eel_irect_get_width (content_bounds) - label_dimensions.width) / 2; + label_bounds.y0 = content_bounds.y0; + break; + + case GTK_POS_BOTTOM: + label_bounds.x0 = + content_bounds.x0 + + (eel_irect_get_width (content_bounds) - label_dimensions.width) / 2; + label_bounds.y0 = content_bounds.y1 - label_dimensions.height; + break; + + default: + label_bounds.x0 = 0; + label_bounds.y0 = 0; + g_assert_not_reached (); + } + } + + label_bounds.x1 = label_bounds.x0 + label_dimensions.width; + label_bounds.y1 = label_bounds.y0 + label_dimensions.height; + + return label_bounds; +} + +static void +labeled_image_update_alignments (EelLabeledImage *labeled_image) +{ + + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->label != NULL) + { + float x_alignment; + float y_alignment; + + if (labeled_image->details->fill) + { + gtk_misc_get_alignment (GTK_MISC (labeled_image->details->label), + &x_alignment, &y_alignment); + + /* Only the label is shown */ + if (!labeled_image_show_image (labeled_image)) + { + x_alignment = 0.5; + y_alignment = 0.5; + /* Both label and image are shown */ + } + else + { + switch (labeled_image->details->label_position) + { + case GTK_POS_LEFT: + x_alignment = 1.0; + y_alignment = 0.5; + break; + + case GTK_POS_RIGHT: + x_alignment = 0.0; + y_alignment = 0.5; + break; + + case GTK_POS_TOP: + x_alignment = 0.5; + y_alignment = 1.0; + break; + + case GTK_POS_BOTTOM: + x_alignment = 0.5; + y_alignment = 0.0; + break; + } + + } + + gtk_misc_set_alignment (GTK_MISC (labeled_image->details->label), + x_alignment, + y_alignment); + } + } + + if (labeled_image->details->image != NULL) + { + float x_alignment; + float y_alignment; + + if (labeled_image->details->fill) + { + gtk_misc_get_alignment (GTK_MISC (labeled_image->details->image), + &x_alignment, &y_alignment); + + /* Only the image is shown */ + if (!labeled_image_show_label (labeled_image)) + { + x_alignment = 0.5; + y_alignment = 0.5; + /* Both label and image are shown */ + } + else + { + switch (labeled_image->details->label_position) + { + case GTK_POS_LEFT: + x_alignment = 0.0; + y_alignment = 0.5; + break; + + case GTK_POS_RIGHT: + x_alignment = 1.0; + y_alignment = 0.5; + break; + + case GTK_POS_TOP: + x_alignment = 0.5; + y_alignment = 0.0; + break; + + case GTK_POS_BOTTOM: + x_alignment = 0.5; + y_alignment = 1.0; + break; + } + } + + gtk_misc_set_alignment (GTK_MISC (labeled_image->details->image), + x_alignment, + y_alignment); + } + } +} + +static EelDimensions +labeled_image_get_content_dimensions (const EelLabeledImage *labeled_image) +{ + EelDimensions image_dimensions; + EelDimensions label_dimensions; + EelDimensions content_dimensions; + + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + image_dimensions = labeled_image_get_image_dimensions (labeled_image); + label_dimensions = labeled_image_get_label_dimensions (labeled_image); + + content_dimensions = eel_dimensions_empty; + + /* Both shown */ + if (!eel_dimensions_are_empty (image_dimensions) && !eel_dimensions_are_empty (label_dimensions)) + { + content_dimensions.width = + image_dimensions.width + labeled_image->details->spacing + label_dimensions.width; + switch (labeled_image->details->label_position) + { + case GTK_POS_LEFT: + case GTK_POS_RIGHT: + content_dimensions.width = + image_dimensions.width + labeled_image->details->spacing + label_dimensions.width; + content_dimensions.height = MAX (image_dimensions.height, label_dimensions.height); + break; + + case GTK_POS_TOP: + case GTK_POS_BOTTOM: + content_dimensions.width = MAX (image_dimensions.width, label_dimensions.width); + content_dimensions.height = + image_dimensions.height + labeled_image->details->spacing + label_dimensions.height; + break; + } + /* Only image shown */ + } + else if (!eel_dimensions_are_empty (image_dimensions)) + { + content_dimensions.width = image_dimensions.width; + content_dimensions.height = image_dimensions.height; + /* Only label shown */ + } + else + { + content_dimensions.width = label_dimensions.width; + content_dimensions.height = label_dimensions.height; + } + + return content_dimensions; +} + +static EelIRect +labeled_image_get_content_bounds (const EelLabeledImage *labeled_image) +{ + EelDimensions content_dimensions; + EelIRect content_bounds; + EelIRect bounds; + + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (labeled_image)); + + content_dimensions = labeled_image_get_content_dimensions (labeled_image); + content_bounds = eel_irect_align (bounds, + content_dimensions.width, + content_dimensions.height, + labeled_image->details->x_alignment, + labeled_image->details->y_alignment); + + return content_bounds; +} + +static void +labeled_image_ensure_label (EelLabeledImage *labeled_image) +{ + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->label != NULL) + { + return; + } + + labeled_image->details->label = gtk_label_new (NULL); + gtk_container_add (GTK_CONTAINER (labeled_image), labeled_image->details->label); + gtk_widget_show (labeled_image->details->label); +} + +static void +labeled_image_ensure_image (EelLabeledImage *labeled_image) +{ + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->image != NULL) + { + return; + } + + labeled_image->details->image = gtk_image_new (); + gtk_container_add (GTK_CONTAINER (labeled_image), labeled_image->details->image); + gtk_widget_show (labeled_image->details->image); +} + +static gboolean +labeled_image_show_image (const EelLabeledImage *labeled_image) +{ + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + return labeled_image->details->image != NULL && labeled_image->details->show_image; +} + +static gboolean +labeled_image_show_label (const EelLabeledImage *labeled_image) +{ + g_assert (EEL_IS_LABELED_IMAGE (labeled_image)); + + return labeled_image->details->label != NULL && labeled_image->details->show_label; +} + +/** + * eel_labeled_image_new: + * @text: Text to use for label or NULL. + * @pixbuf: Pixbuf to use for image or NULL. + * + * Returns A newly allocated EelLabeledImage. If the &text parameter is not + * NULL then the LabeledImage will show a label. If the &pixbuf parameter is not + * NULL then the LabeledImage will show a pixbuf. Either of these can be NULL at + * creation time. + * + * Later in the lifetime of the widget you can invoke methods that affect the + * label and/or the image. If at creation time these were NULL, then they will + * be created as neeeded. + * + * Thus, using this widget in place of EelImage or EelLabel is "free" with + * only the GtkObject and function call overhead. + * + */ +GtkWidget* +eel_labeled_image_new (const char *text, + GdkPixbuf *pixbuf) +{ + EelLabeledImage *labeled_image; + + labeled_image = EEL_LABELED_IMAGE (gtk_widget_new (eel_labeled_image_get_type (), NULL)); + + if (text != NULL) + { + eel_labeled_image_set_text (labeled_image, text); + } + + if (pixbuf != NULL) + { + eel_labeled_image_set_pixbuf (labeled_image, pixbuf); + } + + labeled_image_update_alignments (labeled_image); + + return GTK_WIDGET (labeled_image); +} + +/** + * eel_labeled_image_new_from_file_name: + * @text: Text to use for label or NULL. + * @file_name: File name of picture to use for pixbuf. Cannot be NULL. + * + * Returns A newly allocated EelLabeledImage. If the &text parameter is not + * NULL then the LabeledImage will show a label. + * + */ +GtkWidget* +eel_labeled_image_new_from_file_name (const char *text, + const char *pixbuf_file_name) +{ + EelLabeledImage *labeled_image; + + g_return_val_if_fail (pixbuf_file_name != NULL, NULL); + + labeled_image = EEL_LABELED_IMAGE (eel_labeled_image_new (text, NULL)); + eel_labeled_image_set_pixbuf_from_file_name (labeled_image, pixbuf_file_name); + return GTK_WIDGET (labeled_image); +} + +/** + * eel_labeled_image_set_label_position: + * @labeled_image: A EelLabeledImage. + * @label_position: The position of the label with respect to the image. + * + * Set the position of the label with respect to the image as follows: + * + * GTK_POS_LEFT: + * [ <label> <image> ] + * + * GTK_POS_RIGHT: + * [ <image> <label> ] + * + * GTK_POS_TOP: + * [ <label> ] + * [ <image> ] + * + * GTK_POS_BOTTOM: + * [ <image> ] + * [ <label> ] + * + */ +void +eel_labeled_image_set_label_position (EelLabeledImage *labeled_image, + GtkPositionType label_position) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + g_return_if_fail (label_position >= GTK_POS_LEFT); + g_return_if_fail (label_position <= GTK_POS_BOTTOM); + + if (labeled_image->details->label_position == label_position) + { + return; + } + + labeled_image->details->label_position = label_position; + + labeled_image_update_alignments (labeled_image); + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_label_postiion: + * @labeled_image: A EelLabeledImage. + * + * Returns an enumeration indicating the position of the label with respect to the image. + */ +GtkPositionType +eel_labeled_image_get_label_position (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->label_position; +} + +/** + * eel_labeled_image_set_show_label: + * @labeled_image: A EelLabeledImage. + * @show_image: A boolean value indicating whether the label should be shown. + * + * Update the labeled image to either show or hide the internal label widget. + * This function doesnt have any effect if the LabeledImage doesnt already + * contain an label. + */ +void +eel_labeled_image_set_show_label (EelLabeledImage *labeled_image, + gboolean show_label) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->show_label == show_label) + { + return; + } + + labeled_image->details->show_label = show_label; + + if (labeled_image->details->label != NULL) + { + if (labeled_image->details->show_label) + { + gtk_widget_show (labeled_image->details->label); + } + else + { + gtk_widget_hide (labeled_image->details->label); + } + } + + labeled_image_update_alignments (labeled_image); + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_show_label: + * @labeled_image: A EelLabeledImage. + * + * Returns a boolean value indicating whether the internal label is shown. + */ +gboolean +eel_labeled_image_get_show_label (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->show_label; +} + +/** + * eel_labeled_image_set_show_image: + * @labeled_image: A EelLabeledImage. + * @show_image: A boolean value indicating whether the image should be shown. + * + * Update the labeled image to either show or hide the internal image widget. + * This function doesnt have any effect if the LabeledImage doesnt already + * contain an image. + */ +void +eel_labeled_image_set_show_image (EelLabeledImage *labeled_image, + gboolean show_image) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->show_image == show_image) + { + return; + } + + labeled_image->details->show_image = show_image; + + if (labeled_image->details->image != NULL) + { + if (labeled_image->details->show_image) + { + gtk_widget_show (labeled_image->details->image); + } + else + { + gtk_widget_hide (labeled_image->details->image); + } + } + + labeled_image_update_alignments (labeled_image); + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_show_image: + * @labeled_image: A EelLabeledImage. + * + * Returns a boolean value indicating whether the internal image is shown. + */ +gboolean +eel_labeled_image_get_show_image (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->show_image; +} + + +/** + * eel_labeled_image_set_fixed_image_height: + * @labeled_image: A EelLabeledImage. + * @fixed_image_height: The new fixed image height. + * + * Normally, we measure the height of images, but it's sometimes useful + * to use a fixed height for all the images. This routine sets the + * image height to the passed in value + * + */ +void +eel_labeled_image_set_fixed_image_height (EelLabeledImage *labeled_image, + int new_height) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->fixed_image_height == new_height) + { + return; + } + + labeled_image->details->fixed_image_height = new_height; + + labeled_image_update_alignments (labeled_image); + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_set_selected: + * @labeled_image: A EelLabeledImage. + * @selected: A boolean value indicating whether the labeled image + * should be selected. + * + * Selects or deselects the labeled image. + * + */ +void +eel_labeled_image_set_selected (EelLabeledImage *labeled_image, + gboolean selected) +{ + GtkStateType state; + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + state = selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL; + + gtk_widget_set_state (GTK_WIDGET (labeled_image), state); + gtk_widget_set_state (labeled_image->details->image, state); + gtk_widget_set_state (labeled_image->details->label, state); +} + +/** + * eel_labeled_image_get_selected: + * @labeled_image: A EelLabeledImage. + * + * Returns the selected state of the labeled image. + * + */ +gboolean +eel_labeled_image_get_selected (EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), FALSE); + + return gtk_widget_get_state (GTK_WIDGET (labeled_image)) == GTK_STATE_SELECTED; +} + +/** + * eel_labeled_image_set_spacing: + * @labeled_image: A EelLabeledImage. + * @spacing: The new spacing between label and image. + * + * Set the spacing between label and image. This will only affect + * the geometry of the widget if both a label and image are currently + * visible. + * + */ +void +eel_labeled_image_set_spacing (EelLabeledImage *labeled_image, + guint spacing) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->spacing == spacing) + { + return; + } + + labeled_image->details->spacing = spacing; + + labeled_image_update_alignments (labeled_image); + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_spacing: + * @labeled_image: A EelLabeledImage. + * + * Returns: The spacing between the label and image. + */ +guint +eel_labeled_image_get_spacing (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->spacing; +} + +/** + * eel_labeled_image_set_x_padding: + * @labeled_image: A EelLabeledImage. + * @x_padding: The new horizontal padding. + * + * Set horizontal padding for the EelLabeledImage. The padding + * attribute work just like that in GtkMisc. + */ +void +eel_labeled_image_set_x_padding (EelLabeledImage *labeled_image, + int x_padding) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + x_padding = MAX (0, x_padding); + + if (labeled_image->details->x_padding == x_padding) + { + return; + } + + labeled_image->details->x_padding = x_padding; + labeled_image_update_alignments (labeled_image); + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_x_padding: + * @labeled_image: A EelLabeledImage. + * + * Returns: The horizontal padding for the LabeledImage's content. + */ +int +eel_labeled_image_get_x_padding (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->x_padding; +} + +/** + * eel_labeled_image_set_y_padding: + * @labeled_image: A EelLabeledImage. + * @x_padding: The new vertical padding. + * + * Set vertical padding for the EelLabeledImage. The padding + * attribute work just like that in GtkMisc. + */ +void +eel_labeled_image_set_y_padding (EelLabeledImage *labeled_image, + int y_padding) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + y_padding = MAX (0, y_padding); + + if (labeled_image->details->y_padding == y_padding) + { + return; + } + + labeled_image->details->y_padding = y_padding; + labeled_image_update_alignments (labeled_image); + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_x_padding: + * @labeled_image: A EelLabeledImage. + * + * Returns: The vertical padding for the LabeledImage's content. + */ +int +eel_labeled_image_get_y_padding (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->y_padding; +} + +/** + * eel_labeled_image_set_x_alignment: + * @labeled_image: A EelLabeledImage. + * @x_alignment: The new horizontal alignment. + * + * Set horizontal alignment for the EelLabeledImage's content. + * The 'content' is the union of the image and label. The alignment + * attribute work just like that in GtkMisc. + */ +void +eel_labeled_image_set_x_alignment (EelLabeledImage *labeled_image, + float x_alignment) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + x_alignment = MAX (0, x_alignment); + x_alignment = MIN (1.0, x_alignment); + + if (labeled_image->details->x_alignment == x_alignment) + { + return; + } + + labeled_image->details->x_alignment = x_alignment; + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_x_alignment: + * @labeled_image: A EelLabeledImage. + * + * Returns: The horizontal alignment for the LabeledImage's content. + */ +float +eel_labeled_image_get_x_alignment (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->x_alignment; +} + +/** + * eel_labeled_image_set_y_alignment: + * @labeled_image: A EelLabeledImage. + * @y_alignment: The new vertical alignment. + * + * Set vertical alignment for the EelLabeledImage's content. + * The 'content' is the union of the image and label. The alignment + * attribute work just like that in GtkMisc. + */ +void +eel_labeled_image_set_y_alignment (EelLabeledImage *labeled_image, + float y_alignment) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + y_alignment = MAX (0, y_alignment); + y_alignment = MIN (1.0, y_alignment); + + if (labeled_image->details->y_alignment == y_alignment) + { + return; + } + + labeled_image->details->y_alignment = y_alignment; + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_y_alignment: + * @labeled_image: A EelLabeledImage. + * + * Returns: The vertical alignment for the LabeledImage's content. + */ +float +eel_labeled_image_get_y_alignment (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->y_alignment; +} + +/** + * eel_labeled_image_set_fill: + * @labeled_image: A EelLabeledImage. + * @fill: A boolean value indicating whether the internal image and label + * widgets should fill all the available allocation. + * + * By default the internal image and label wigets are sized to their natural + * preferred geometry. You can use the 'fill' attribute of LabeledImage + * to have the internal widgets fill as much of the LabeledImage allocation + * as is available. + */ +void +eel_labeled_image_set_fill (EelLabeledImage *labeled_image, + gboolean fill) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (labeled_image->details->fill == fill) + { + return; + } + + labeled_image->details->fill = fill; + + labeled_image_update_alignments (labeled_image); + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); +} + +/** + * eel_labeled_image_get_fill: + * @labeled_image: A EelLabeledImage. + * + * Retruns a boolean value indicating whether the internal widgets fill + * all the available allocation. + */ +gboolean +eel_labeled_image_get_fill (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0); + + return labeled_image->details->fill; +} + +static void +eel_labled_set_mnemonic_widget (GtkWidget *image_widget, + GtkWidget *mnemonic_widget) +{ + EelLabeledImage *image; + + g_assert (EEL_IS_LABELED_IMAGE (image_widget)); + + image = EEL_LABELED_IMAGE (image_widget); + + if (image->details->label) + gtk_label_set_mnemonic_widget + (GTK_LABEL (image->details->label), mnemonic_widget); +} + +/** + * eel_labeled_image_button_new: + * @text: Text to use for label or NULL. + * @pixbuf: Pixbuf to use for image or NULL. + * + * Create a stock GtkButton with a EelLabeledImage child. + * + */ +GtkWidget * +eel_labeled_image_button_new (const char *text, + GdkPixbuf *pixbuf) +{ + GtkWidget *button; + GtkWidget *labeled_image; + + button = g_object_new (eel_labeled_image_button_get_type (), NULL); + labeled_image = eel_labeled_image_new (text, pixbuf); + gtk_container_add (GTK_CONTAINER (button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, button); + gtk_widget_show (labeled_image); + + return button; +} + +/** + * eel_labeled_image_button_new_from_file_name: + * @text: Text to use for label or NULL. + * @pixbuf_file_name: Name of pixbuf to use for image. Cannot be NULL. + * + * Create a stock GtkToggleButton with a EelLabeledImage child. + * + */ +GtkWidget * +eel_labeled_image_button_new_from_file_name (const char *text, + const char *pixbuf_file_name) +{ + GtkWidget *button; + GtkWidget *labeled_image; + + g_return_val_if_fail (pixbuf_file_name != NULL, NULL); + + button = g_object_new (eel_labeled_image_button_get_type (), NULL); + labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name); + gtk_container_add (GTK_CONTAINER (button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, button); + gtk_widget_show (labeled_image); + + return button; +} + +/** + * eel_labeled_image_toggle_button_new: + * @text: Text to use for label or NULL. + * @pixbuf: Pixbuf to use for image or NULL. + * + * Create a stock GtkToggleButton with a EelLabeledImage child. + * + */ +GtkWidget * +eel_labeled_image_toggle_button_new (const char *text, + GdkPixbuf *pixbuf) +{ + GtkWidget *toggle_button; + GtkWidget *labeled_image; + + toggle_button = g_object_new (eel_labeled_image_toggle_button_get_type (), NULL); + labeled_image = eel_labeled_image_new (text, pixbuf); + gtk_container_add (GTK_CONTAINER (toggle_button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, toggle_button); + gtk_widget_show (labeled_image); + + return toggle_button; +} + +/** + * eel_labeled_image_toggle_button_new_from_file_name: + * @text: Text to use for label or NULL. + * @pixbuf_file_name: Name of pixbuf to use for image. Cannot be NULL. + * + * Create a stock GtkToggleButton with a EelLabeledImage child. + * + */ +GtkWidget * +eel_labeled_image_toggle_button_new_from_file_name (const char *text, + const char *pixbuf_file_name) +{ + GtkWidget *toggle_button; + GtkWidget *labeled_image; + + g_return_val_if_fail (pixbuf_file_name != NULL, NULL); + + toggle_button = g_object_new (eel_labeled_image_toggle_button_get_type (), NULL); + labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name); + gtk_container_add (GTK_CONTAINER (toggle_button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, toggle_button); + gtk_widget_show (labeled_image); + + return toggle_button; +} + +/** + * eel_labeled_image_toggle_button_new: + * @text: Text to use for label or NULL. + * @pixbuf: Pixbuf to use for image or NULL. + * + * Create a stock GtkToggleButton with a EelLabeledImage child. + * + * Returns: the new radio button. + */ +GtkWidget * +eel_labeled_image_radio_button_new (const char *text, + GdkPixbuf *pixbuf) +{ + GtkWidget *radio_button; + GtkWidget *labeled_image; + + radio_button = g_object_new (eel_labeled_image_radio_button_get_type (), NULL); + labeled_image = eel_labeled_image_new (text, pixbuf); + gtk_container_add (GTK_CONTAINER (radio_button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, radio_button); + gtk_widget_show (labeled_image); + + return radio_button; +} + +/** + * eel_labeled_image_radio_button_new_from_file_name: + * @text: Text to use for label or NULL. + * @pixbuf_file_name: Name of pixbuf to use for image. Cannot be NULL. + * + * Create a stock GtkRadioButton with a EelLabeledImage child. + * + * Returns: the new radio button. + */ +GtkWidget * +eel_labeled_image_radio_button_new_from_file_name (const char *text, + const char *pixbuf_file_name) +{ + GtkWidget *radio_button; + GtkWidget *labeled_image; + + g_return_val_if_fail (pixbuf_file_name != NULL, NULL); + + radio_button = g_object_new (eel_labeled_image_radio_button_get_type (), NULL); + labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name); + gtk_container_add (GTK_CONTAINER (radio_button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, radio_button); + gtk_widget_show (labeled_image); + + return radio_button; +} + +/* + * Workaround some bugs in GtkCheckButton where the widget + * does not redraw properly after leave or focus out events + * + * The workaround is to draw a little bit more than the + * widget itself - 4 pixels worth. For some reason the + * widget does not properly redraw its edges. + */ +static void +button_leave_callback (GtkWidget *widget, + gpointer callback_data) +{ + g_assert (GTK_IS_WIDGET (widget)); + + if (gtk_widget_is_drawable (widget)) + { + const int fudge = 4; + EelIRect bounds; + + bounds = eel_gtk_widget_get_bounds (widget); + + bounds.x0 -= fudge; + bounds.y0 -= fudge; + bounds.x1 += fudge; + bounds.y1 += fudge; + + gtk_widget_queue_draw_area (gtk_widget_get_parent (widget), + bounds.x0, + bounds.y0, + eel_irect_get_width (bounds), + eel_irect_get_height (bounds)); + } +} + +static gint +button_focus_out_event_callback (GtkWidget *widget, + GdkEventFocus *event, + gpointer callback_data) +{ + g_assert (GTK_IS_WIDGET (widget)); + + button_leave_callback (widget, callback_data); + + return FALSE; +} + +/** + * eel_labeled_image_check_button_new: + * @text: Text to use for label or NULL. + * @pixbuf: Pixbuf to use for image or NULL. + * + * Create a stock GtkCheckButton with a EelLabeledImage child. + * + */ +GtkWidget * +eel_labeled_image_check_button_new (const char *text, + GdkPixbuf *pixbuf) +{ + GtkWidget *check_button; + GtkWidget *labeled_image; + + check_button = g_object_new (eel_labeled_image_check_button_get_type (), NULL); + labeled_image = eel_labeled_image_new (text, pixbuf); + gtk_container_add (GTK_CONTAINER (check_button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, check_button); + gtk_widget_show (labeled_image); + + /* + * Workaround some bugs in GtkCheckButton where the widget + * does not redraw properly after leave or focus out events + */ + g_signal_connect (check_button, "leave", + G_CALLBACK (button_leave_callback), NULL); + g_signal_connect (check_button, "focus_out_event", + G_CALLBACK (button_focus_out_event_callback), NULL); + + return check_button; +} + +/** + * eel_labeled_image_check_button_new_from_file_name: + * @text: Text to use for label or NULL. + * @pixbuf_file_name: Name of pixbuf to use for image. Cannot be NULL. + * + * Create a stock GtkCheckButton with a EelLabeledImage child. + * + */ +GtkWidget * +eel_labeled_image_check_button_new_from_file_name (const char *text, + const char *pixbuf_file_name) +{ + GtkWidget *check_button; + GtkWidget *labeled_image; + + g_return_val_if_fail (pixbuf_file_name != NULL, NULL); + + check_button = g_object_new (eel_labeled_image_check_button_get_type (), NULL); + labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name); + gtk_container_add (GTK_CONTAINER (check_button), labeled_image); + eel_labled_set_mnemonic_widget (labeled_image, check_button); + gtk_widget_show (labeled_image); + + return check_button; +} + +/* + * The rest of the methods are proxies for those in EelImage and + * EelLabel. We have all these so that we dont have to expose + * our internal widgets at all. Probably more of these will be added + * as they are needed. + */ + +/** + * eel_labeled_image_set_pixbuf: + * @labaled_image: A EelLabeledImage. + * @pixbuf: New pixbuf to use or NULL. + * + * Change the pixbuf displayed by the LabeledImage. Note that the widget display + * is only updated if the show_image attribute is TRUE. + * + * If no internal image widget exists as of yet, a new one will be created. + * + * A NULL &pixbuf will cause the internal image widget (if alive) to be destroyed. + */ +void +eel_labeled_image_set_pixbuf (EelLabeledImage *labeled_image, + GdkPixbuf *pixbuf) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (pixbuf == NULL) + { + if (labeled_image->details->image != NULL) + { + gtk_widget_destroy (labeled_image->details->image); + labeled_image->details->image = NULL; + } + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); + } + else + { + labeled_image_ensure_image (labeled_image); + gtk_image_set_from_pixbuf (GTK_IMAGE (labeled_image->details->image), pixbuf); + } +} + +void +eel_labeled_image_set_pixbuf_from_file_name (EelLabeledImage *labeled_image, + const char *pixbuf_file_name) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + labeled_image_ensure_image (labeled_image); + gtk_image_set_from_file (GTK_IMAGE (labeled_image->details->image), pixbuf_file_name); +} + +/** + * eel_labeled_image_set_text: + * @labaled_image: A EelLabeledImage. + * @text: New text (with mnemnonic) to use or NULL. + * + * Change the text displayed by the LabeledImage. Note that the widget display + * is only updated if the show_label attribute is TRUE. + * + * If no internal label widget exists as of yet, a new one will be created. + * + * A NULL &text will cause the internal label widget (if alive) to be destroyed. + */ +void +eel_labeled_image_set_text (EelLabeledImage *labeled_image, + const char *text) +{ + g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image)); + + if (text == NULL) + { + if (labeled_image->details->label) + { + gtk_widget_destroy (labeled_image->details->label); + labeled_image->details->label = NULL; + } + + gtk_widget_queue_resize (GTK_WIDGET (labeled_image)); + } + else + { + labeled_image_ensure_label (labeled_image); + gtk_label_set_text_with_mnemonic + (GTK_LABEL (labeled_image->details->label), text); + } +} + +char * +eel_labeled_image_get_text (const EelLabeledImage *labeled_image) +{ + g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), NULL); + + if (labeled_image->details->label == NULL) + { + return NULL; + } + + return g_strdup (gtk_label_get_text (GTK_LABEL (labeled_image->details->label))); +} + +void +eel_labeled_image_set_can_focus (EelLabeledImage *labeled_image, + gboolean can_focus) +{ + gtk_widget_set_can_focus (GTK_WIDGET (labeled_image), can_focus); +} + +static AtkObjectClass *a11y_parent_class = NULL; + +static void +eel_labeled_image_accessible_initialize (AtkObject *accessible, + gpointer widget) +{ + a11y_parent_class->initialize (accessible, widget); +} + +static EelLabeledImage * +get_image (gpointer object) +{ + GtkWidget *widget; + + if (!(widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (object)))) + { + return NULL; + } + + if (GTK_IS_BUTTON (widget)) + widget = gtk_bin_get_child (GTK_BIN (widget)); + + return EEL_LABELED_IMAGE (widget); +} + +static const gchar* eel_labeled_image_accessible_get_name(AtkObject* accessible) +{ + EelLabeledImage* labeled_image; + + labeled_image = get_image(accessible); + + if (labeled_image && labeled_image->details && labeled_image->details->label) + { + return gtk_label_get_text(GTK_LABEL(labeled_image->details->label)); + } + + g_warning("no label on '%p'", labeled_image); + + return NULL; +} + +static void +eel_labeled_image_accessible_image_get_size (AtkImage *image, + gint *width, + gint *height) +{ + EelLabeledImage *labeled_image; + GtkAllocation allocation; + + labeled_image = get_image (image); + + if (!labeled_image || !labeled_image->details->image) + { + *width = *height = 0; + return; + } + + gtk_widget_get_allocation (labeled_image->details->image, &allocation); + *width = allocation.width; + *height = allocation.height; +} + +static void +eel_labeled_image_accessible_image_interface_init (AtkImageIface *iface) +{ + iface->get_image_size = eel_labeled_image_accessible_image_get_size; +} + +static void +eel_labeled_image_accessible_class_init (AtkObjectClass *klass) +{ + a11y_parent_class = g_type_class_peek_parent (klass); + + klass->get_name = eel_labeled_image_accessible_get_name; + klass->initialize = eel_labeled_image_accessible_initialize; +} + +enum +{ + BUTTON, + CHECK, + TOGGLE, + RADIO, + PLAIN, + LAST_ONE +}; + +static AtkObject * +eel_labeled_image_get_accessible (GtkWidget *widget) +{ + int i; + static GType types[LAST_ONE] = { 0 }; + const char *tname; + AtkRole role; + AtkObject *accessible; + + if ((accessible = eel_accessibility_get_atk_object (widget))) + return accessible; + + if (GTK_IS_CHECK_BUTTON (widget)) + { + i = BUTTON; + role = ATK_ROLE_CHECK_BOX; + tname = "EelLabeledImageCheckButtonAccessible"; + + } + else if (GTK_IS_TOGGLE_BUTTON (widget)) + { + i = CHECK; + role = ATK_ROLE_TOGGLE_BUTTON; + tname = "EelLabeledImageToggleButtonAccessible"; + + } + else if (GTK_IS_RADIO_BUTTON (widget)) + { + i = RADIO; + role = ATK_ROLE_RADIO_BUTTON; + tname = "EelLabeledImageRadioButtonAccessible"; + + } + else if (GTK_IS_BUTTON (widget)) + { + i = TOGGLE; + role = ATK_ROLE_PUSH_BUTTON; + tname = "EelLabeledImagePushButtonAccessible"; + + } + else /* plain */ + { + i = PLAIN; + role = ATK_ROLE_IMAGE; + tname = "EelLabeledImagePlainAccessible"; + } + + if (!types [i]) + { + const GInterfaceInfo atk_image_info = + { + (GInterfaceInitFunc) eel_labeled_image_accessible_image_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + types [i] = eel_accessibility_create_derived_type + (tname, G_TYPE_FROM_INSTANCE (widget), + eel_labeled_image_accessible_class_init); + + if (!types [i]) + return NULL; + + g_type_add_interface_static ( + types [i], ATK_TYPE_IMAGE, &atk_image_info); + } + + accessible = g_object_new (types [i], NULL); + atk_object_set_role (accessible, role); + + return eel_accessibility_set_atk_object_return (widget, accessible); +} + +static void +eel_labeled_image_button_class_init (GtkWidgetClass *klass) +{ + klass->get_accessible = eel_labeled_image_get_accessible; +} + +static GType +eel_labeled_image_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + GTypeInfo info = + { + sizeof (GtkButtonClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) eel_labeled_image_button_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkButton), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL + }; + + type = g_type_register_static + (GTK_TYPE_BUTTON, + "EelLabeledImageButton", &info, 0); + } + + return type; +} + +static GType +eel_labeled_image_check_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + GTypeInfo info = + { + sizeof (GtkCheckButtonClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) eel_labeled_image_button_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkCheckButton), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL + }; + + type = g_type_register_static + (GTK_TYPE_CHECK_BUTTON, + "EelLabeledImageCheckButton", &info, 0); + } + + return type; +} + +static GType +eel_labeled_image_toggle_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + GTypeInfo info = + { + sizeof (GtkToggleButtonClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) eel_labeled_image_button_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkToggleButton), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL + }; + + type = g_type_register_static + (GTK_TYPE_TOGGLE_BUTTON, + "EelLabeledImageToggleButton", &info, 0); + } + + return type; +} + + +static GType +eel_labeled_image_radio_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + GTypeInfo info = + { + sizeof (GtkRadioButtonClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) eel_labeled_image_button_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkRadioButton), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL + }; + + type = g_type_register_static + (GTK_TYPE_RADIO_BUTTON, + "EelLabeledImageRadioButton", &info, 0); + } + + return type; +} |