summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimpgradienteditor.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/widgets/gimpgradienteditor.c')
-rw-r--r--app/widgets/gimpgradienteditor.c2234
1 files changed, 2234 insertions, 0 deletions
diff --git a/app/widgets/gimpgradienteditor.c b/app/widgets/gimpgradienteditor.c
new file mode 100644
index 0000000..a8851c5
--- /dev/null
+++ b/app/widgets/gimpgradienteditor.c
@@ -0,0 +1,2234 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Gradient editor module copyight (C) 1996-1997 Federico Mena Quintero
+ * federico@nuclecu.unam.mx
+ *
+ * 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 PURIGHTE. 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 <https://www.gnu.org/licenses/>.
+ */
+
+/* Special thanks to:
+ *
+ * Luis Albarran (luis4@mindspring.com) - Nice UI suggestions
+ *
+ * Miguel de Icaza (miguel@nuclecu.unam.mx) - Pop-up menu suggestion
+ *
+ * Marcelo Malheiros (malheiro@dca.fee.unicamp.br) - many, many
+ * suggestions, nice gradient files
+ *
+ * Adam Moss (adam@uunet.pipex.com) - idea for the hint bar
+ *
+ * Everyone on #gimp - many suggestions
+ */
+
+/* TODO:
+ *
+ * - Add all of Marcelo's neat suggestions:
+ * - Hue rotate, saturation, brightness, contrast.
+ *
+ * - Better handling of bogus gradient files and inconsistent
+ * segments. Do not loop indefinitely in seg_get_segment_at() if
+ * there is a missing segment between two others.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpcontext.h"
+#include "core/gimpdatafactory.h"
+#include "core/gimpgradient.h"
+
+#include "gimpcolordialog.h"
+#include "gimpdialogfactory.h"
+#include "gimpdnd.h"
+#include "gimpdocked.h"
+#include "gimpgradienteditor.h"
+#include "gimphelp-ids.h"
+#include "gimpuimanager.h"
+#include "gimpview.h"
+#include "gimpviewrenderergradient.h"
+#include "gimpwidgets-utils.h"
+
+#include "gimp-intl.h"
+
+
+#define EPSILON 1e-10
+
+#define GRAD_SCROLLBAR_STEP_SIZE 0.05
+#define GRAD_SCROLLBAR_PAGE_SIZE 0.75
+
+#define GRAD_VIEW_SIZE 96
+#define GRAD_CONTROL_HEIGHT 14
+#define GRAD_CURRENT_COLOR_WIDTH 16
+
+#define GRAD_MOVE_TIME 150 /* ms between mouse click and detection of movement in gradient control */
+
+
+#define GRAD_VIEW_EVENT_MASK (GDK_EXPOSURE_MASK | \
+ GDK_LEAVE_NOTIFY_MASK | \
+ GDK_POINTER_MOTION_MASK | \
+ GDK_POINTER_MOTION_HINT_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK)
+
+#define GRAD_CONTROL_EVENT_MASK (GDK_EXPOSURE_MASK | \
+ GDK_LEAVE_NOTIFY_MASK | \
+ GDK_POINTER_MOTION_MASK | \
+ GDK_POINTER_MOTION_HINT_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON1_MOTION_MASK)
+
+
+/* local function prototypes */
+
+static void gimp_gradient_editor_docked_iface_init (GimpDockedInterface *face);
+
+static void gimp_gradient_editor_constructed (GObject *object);
+static void gimp_gradient_editor_dispose (GObject *object);
+
+static void gimp_gradient_editor_unmap (GtkWidget *widget);
+static void gimp_gradient_editor_set_data (GimpDataEditor *editor,
+ GimpData *data);
+
+static void gimp_gradient_editor_set_context (GimpDocked *docked,
+ GimpContext *context);
+
+static void gimp_gradient_editor_update (GimpGradientEditor *editor);
+static void gimp_gradient_editor_gradient_dirty (GimpGradientEditor *editor,
+ GimpGradient *gradient);
+static void gradient_editor_drop_gradient (GtkWidget *widget,
+ gint x,
+ gint y,
+ GimpViewable *viewable,
+ gpointer data);
+static void gradient_editor_drop_color (GtkWidget *widget,
+ gint x,
+ gint y,
+ const GimpRGB *color,
+ gpointer data);
+static void gradient_editor_control_drop_color (GtkWidget *widget,
+ gint x,
+ gint y,
+ const GimpRGB *color,
+ gpointer data);
+static void gradient_editor_scrollbar_update (GtkAdjustment *adj,
+ GimpGradientEditor *editor);
+
+static void gradient_editor_set_hint (GimpGradientEditor *editor,
+ const gchar *str1,
+ const gchar *str2,
+ const gchar *str3,
+ const gchar *str4);
+
+static GimpGradientSegment *
+ gradient_editor_save_selection (GimpGradientEditor *editor);
+static void gradient_editor_replace_selection (GimpGradientEditor *editor,
+ GimpGradientSegment *replace_seg);
+
+static void gradient_editor_left_color_update (GimpColorDialog *dialog,
+ const GimpRGB *color,
+ GimpColorDialogState state,
+ GimpGradientEditor *editor);
+static void gradient_editor_right_color_update (GimpColorDialog *dialog,
+ const GimpRGB *color,
+ GimpColorDialogState state,
+ GimpGradientEditor *editor);
+
+
+/* Gradient view functions */
+
+static gboolean view_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpGradientEditor *editor);
+static void view_set_hint (GimpGradientEditor *editor,
+ gint x);
+
+static void view_pick_color (GimpGradientEditor *editor,
+ GimpColorPickTarget pick_target,
+ GimpColorPickState pick_state,
+ gint x);
+
+/* Gradient control functions */
+
+static gboolean control_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpGradientEditor *editor);
+static gboolean control_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpGradientEditor *editor);
+static void control_do_hint (GimpGradientEditor *editor,
+ gint x,
+ gint y);
+static void control_button_press (GimpGradientEditor *editor,
+ gint x,
+ gint y,
+ guint button,
+ guint state);
+static gboolean control_point_in_handle (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ gint x,
+ gint y,
+ GimpGradientSegment *seg,
+ GradientEditorDragMode handle);
+static void control_select_single_segment (GimpGradientEditor *editor,
+ GimpGradientSegment *seg);
+static void control_extend_selection (GimpGradientEditor *editor,
+ GimpGradientSegment *seg,
+ gdouble pos);
+static void control_motion (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ gint x);
+
+static void control_compress_left (GimpGradient *gradient,
+ GimpGradientSegment *range_l,
+ GimpGradientSegment *range_r,
+ GimpGradientSegment *drag_seg,
+ gdouble pos);
+
+static double control_move (GimpGradientEditor *editor,
+ GimpGradientSegment *range_l,
+ GimpGradientSegment *range_r,
+ gdouble delta);
+
+/* Control update/redraw functions */
+
+static void control_update (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ gboolean recalculate);
+static void control_draw (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ cairo_t *cr,
+ gint width,
+ gint height,
+ gdouble left,
+ gdouble right);
+static void control_draw_normal_handle (GimpGradientEditor *editor,
+ cairo_t *cr,
+ gdouble pos,
+ gint height,
+ gboolean selected);
+static void control_draw_middle_handle (GimpGradientEditor *editor,
+ cairo_t *cr,
+ gdouble pos,
+ gint height,
+ gboolean selected);
+static void control_draw_handle (cairo_t *cr,
+ GdkColor *border,
+ GdkColor *fill,
+ gint xpos,
+ gint height);
+
+static gint control_calc_p_pos (GimpGradientEditor *editor,
+ gdouble pos);
+static gdouble control_calc_g_pos (GimpGradientEditor *editor,
+ gint pos);
+
+/* Segment functions */
+
+static void seg_get_closest_handle (GimpGradient *grad,
+ gdouble pos,
+ GimpGradientSegment **seg,
+ GradientEditorDragMode *handle);
+static gboolean seg_in_selection (GimpGradient *grad,
+ GimpGradientSegment *seg,
+ GimpGradientSegment *left,
+ GimpGradientSegment *right);
+
+static GtkWidget * gradient_hint_label_add (GtkBox *box);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpGradientEditor, gimp_gradient_editor,
+ GIMP_TYPE_DATA_EDITOR,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
+ gimp_gradient_editor_docked_iface_init))
+
+#define parent_class gimp_gradient_editor_parent_class
+
+static GimpDockedInterface *parent_docked_iface = NULL;
+
+
+static void
+gimp_gradient_editor_class_init (GimpGradientEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GimpDataEditorClass *editor_class = GIMP_DATA_EDITOR_CLASS (klass);
+
+ object_class->constructed = gimp_gradient_editor_constructed;
+ object_class->dispose = gimp_gradient_editor_dispose;
+
+ widget_class->unmap = gimp_gradient_editor_unmap;
+
+ editor_class->set_data = gimp_gradient_editor_set_data;
+ editor_class->title = _("Gradient Editor");
+}
+
+static void
+gimp_gradient_editor_docked_iface_init (GimpDockedInterface *iface)
+{
+ parent_docked_iface = g_type_interface_peek_parent (iface);
+
+ if (! parent_docked_iface)
+ parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
+
+ iface->set_context = gimp_gradient_editor_set_context;
+}
+
+static void
+gimp_gradient_editor_init (GimpGradientEditor *editor)
+{
+ GimpDataEditor *data_editor = GIMP_DATA_EDITOR (editor);
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *hint_vbox;
+ GimpRGB transp;
+
+ gimp_rgba_set (&transp, 0.0, 0.0, 0.0, 0.0);
+
+ /* Frame for gradient view and gradient control */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (editor), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ data_editor->view = gimp_view_new_full_by_types (NULL,
+ GIMP_TYPE_VIEW,
+ GIMP_TYPE_GRADIENT,
+ GRAD_VIEW_SIZE,
+ GRAD_VIEW_SIZE, 0,
+ FALSE, FALSE, FALSE);
+ gtk_widget_set_size_request (data_editor->view, -1, GRAD_VIEW_SIZE);
+ gtk_widget_set_events (data_editor->view, GRAD_VIEW_EVENT_MASK);
+ gimp_view_set_expand (GIMP_VIEW (data_editor->view), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), data_editor->view, TRUE, TRUE, 0);
+ gtk_widget_show (data_editor->view);
+
+ g_signal_connect (data_editor->view, "event",
+ G_CALLBACK (view_events),
+ editor);
+
+ gimp_dnd_viewable_dest_add (GTK_WIDGET (data_editor->view),
+ GIMP_TYPE_GRADIENT,
+ gradient_editor_drop_gradient,
+ editor);
+
+ gimp_dnd_color_dest_add (GTK_WIDGET (data_editor->view),
+ gradient_editor_drop_color,
+ editor);
+
+ /* Gradient control */
+ editor->control = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (editor->control, -1, GRAD_CONTROL_HEIGHT);
+ gtk_widget_set_events (editor->control, GRAD_CONTROL_EVENT_MASK);
+ gtk_box_pack_start (GTK_BOX (vbox), editor->control, FALSE, FALSE, 0);
+ gtk_widget_show (editor->control);
+
+ g_signal_connect (editor->control, "event",
+ G_CALLBACK (control_events),
+ editor);
+
+ g_signal_connect (editor->control, "expose-event",
+ G_CALLBACK (control_expose),
+ editor);
+
+ gimp_dnd_color_dest_add (GTK_WIDGET (editor->control),
+ gradient_editor_control_drop_color,
+ editor);
+
+ /* Scrollbar */
+ editor->zoom_factor = 1;
+
+ editor->scroll_data = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0,
+ GRAD_SCROLLBAR_STEP_SIZE,
+ GRAD_SCROLLBAR_PAGE_SIZE,
+ 1.0));
+
+ g_signal_connect (editor->scroll_data, "value-changed",
+ G_CALLBACK (gradient_editor_scrollbar_update),
+ editor);
+ g_signal_connect (editor->scroll_data, "changed",
+ G_CALLBACK (gradient_editor_scrollbar_update),
+ editor);
+
+ editor->scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL,
+ editor->scroll_data);
+ gtk_box_pack_start (GTK_BOX (editor), editor->scrollbar, FALSE, FALSE, 0);
+ gtk_widget_show (editor->scrollbar);
+
+ /* Box for current color and the hint labels */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (editor), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Frame showing current active color */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ editor->current_color = gimp_color_area_new (&transp,
+ GIMP_COLOR_AREA_SMALL_CHECKS,
+ GDK_BUTTON1_MASK |
+ GDK_BUTTON2_MASK);
+ gtk_container_add (GTK_CONTAINER (frame), editor->current_color);
+ gtk_widget_set_size_request (editor->current_color,
+ GRAD_CURRENT_COLOR_WIDTH, -1);
+ gtk_widget_show (editor->current_color);
+
+ /* Hint box */
+ hint_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), hint_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (hint_vbox);
+
+ editor->hint_label1 = gradient_hint_label_add (GTK_BOX (hint_vbox));
+ editor->hint_label2 = gradient_hint_label_add (GTK_BOX (hint_vbox));
+ editor->hint_label3 = gradient_hint_label_add (GTK_BOX (hint_vbox));
+ editor->hint_label4 = gradient_hint_label_add (GTK_BOX (hint_vbox));
+
+ /* Black, 50% Gray, White, Clear */
+ gimp_rgba_set (&editor->saved_colors[0], 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[1], 0.5, 0.5, 0.5, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[2], 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[3], 0.0, 0.0, 0.0, GIMP_OPACITY_TRANSPARENT);
+
+ /* Red, Yellow, Green, Cyan, Blue, Magenta */
+ gimp_rgba_set (&editor->saved_colors[4], 1.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[5], 1.0, 1.0, 0.0, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[6], 0.0, 1.0, 0.0, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[7], 0.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[8], 0.0, 0.0, 1.0, GIMP_OPACITY_OPAQUE);
+ gimp_rgba_set (&editor->saved_colors[9], 1.0, 0.0, 1.0, GIMP_OPACITY_OPAQUE);
+}
+
+static void
+gimp_gradient_editor_constructed (GObject *object)
+{
+ GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (object);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gimp_editor_add_action_button (GIMP_EDITOR (editor), "gradient-editor",
+ "gradient-editor-zoom-out", NULL);
+
+ gimp_editor_add_action_button (GIMP_EDITOR (editor), "gradient-editor",
+ "gradient-editor-zoom-in", NULL);
+
+ gimp_editor_add_action_button (GIMP_EDITOR (editor), "gradient-editor",
+ "gradient-editor-zoom-all", NULL);
+}
+
+static void
+gimp_gradient_editor_dispose (GObject *object)
+{
+ GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (object);
+
+ if (editor->color_dialog)
+ gtk_dialog_response (GTK_DIALOG (editor->color_dialog),
+ GTK_RESPONSE_CANCEL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_gradient_editor_unmap (GtkWidget *widget)
+{
+ GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (widget);
+
+ if (editor->color_dialog)
+ gtk_dialog_response (GTK_DIALOG (editor->color_dialog),
+ GTK_RESPONSE_CANCEL);
+
+ GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+}
+
+static void
+gimp_gradient_editor_set_data (GimpDataEditor *editor,
+ GimpData *data)
+{
+ GimpGradientEditor *gradient_editor = GIMP_GRADIENT_EDITOR (editor);
+ GimpData *old_data;
+
+ if (gradient_editor->color_dialog)
+ gtk_dialog_response (GTK_DIALOG (gradient_editor->color_dialog),
+ GTK_RESPONSE_CANCEL);
+
+ old_data = gimp_data_editor_get_data (editor);
+
+ if (old_data)
+ g_signal_handlers_disconnect_by_func (old_data,
+ gimp_gradient_editor_gradient_dirty,
+ gradient_editor);
+
+ GIMP_DATA_EDITOR_CLASS (parent_class)->set_data (editor, data);
+
+ if (data)
+ g_signal_connect_swapped (data, "dirty",
+ G_CALLBACK (gimp_gradient_editor_gradient_dirty),
+ gradient_editor);
+
+ gimp_view_set_viewable (GIMP_VIEW (editor->view),
+ GIMP_VIEWABLE (data));
+
+ control_update (gradient_editor, GIMP_GRADIENT (data), TRUE);
+}
+
+static void
+gimp_gradient_editor_set_context (GimpDocked *docked,
+ GimpContext *context)
+{
+ GimpDataEditor *data_editor = GIMP_DATA_EDITOR (docked);
+
+ parent_docked_iface->set_context (docked, context);
+
+ gimp_view_renderer_set_context (GIMP_VIEW (data_editor->view)->renderer,
+ context);
+}
+
+
+/* public functions */
+
+GtkWidget *
+gimp_gradient_editor_new (GimpContext *context,
+ GimpMenuFactory *menu_factory)
+{
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ return g_object_new (GIMP_TYPE_GRADIENT_EDITOR,
+ "menu-factory", menu_factory,
+ "menu-identifier", "<GradientEditor>",
+ "ui-path", "/gradient-editor-popup",
+ "data-factory", context->gimp->gradient_factory,
+ "context", context,
+ "data", gimp_context_get_gradient (context),
+ NULL);
+}
+
+void
+gimp_gradient_editor_get_selection (GimpGradientEditor *editor,
+ GimpGradient **gradient,
+ GimpGradientSegment **left,
+ GimpGradientSegment **right)
+{
+ g_return_if_fail (GIMP_IS_GRADIENT_EDITOR (editor));
+
+ if (gradient)
+ *gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ if (left)
+ *left = editor->control_sel_l;
+
+ if (right)
+ *right = editor->control_sel_r;
+}
+
+void
+gimp_gradient_editor_set_selection (GimpGradientEditor *editor,
+ GimpGradientSegment *left,
+ GimpGradientSegment *right)
+{
+ g_return_if_fail (GIMP_IS_GRADIENT_EDITOR (editor));
+ g_return_if_fail (left != NULL);
+ g_return_if_fail (right != NULL);
+
+ editor->control_sel_l = left;
+ editor->control_sel_r = right;
+}
+
+void
+gimp_gradient_editor_edit_left_color (GimpGradientEditor *editor)
+{
+ GimpGradient *gradient;
+
+ g_return_if_fail (GIMP_IS_GRADIENT_EDITOR (editor));
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ if (! gradient ||
+ ! editor->control_sel_l ||
+ editor->control_sel_l->left_color_type != GIMP_GRADIENT_COLOR_FIXED)
+ return;
+
+ editor->saved_dirty = gimp_data_is_dirty (GIMP_DATA (gradient));
+ editor->saved_segments = gradient_editor_save_selection (editor);
+
+ editor->color_dialog =
+ gimp_color_dialog_new (GIMP_VIEWABLE (gradient),
+ GIMP_DATA_EDITOR (editor)->context,
+ _("Left Endpoint Color"),
+ GIMP_ICON_GRADIENT,
+ _("Gradient Segment's Left Endpoint Color"),
+ GTK_WIDGET (editor),
+ gimp_dialog_factory_get_singleton (),
+ "gimp-gradient-editor-color-dialog",
+ &editor->control_sel_l->left_color,
+ TRUE, TRUE);
+
+ g_signal_connect (editor->color_dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &editor->color_dialog);
+
+ g_signal_connect (editor->color_dialog, "update",
+ G_CALLBACK (gradient_editor_left_color_update),
+ editor);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
+ gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)),
+ gimp_editor_get_popup_data (GIMP_EDITOR (editor)));
+
+ gtk_window_present (GTK_WINDOW (editor->color_dialog));
+}
+
+void
+gimp_gradient_editor_edit_right_color (GimpGradientEditor *editor)
+{
+ GimpGradient *gradient;
+
+ g_return_if_fail (GIMP_IS_GRADIENT_EDITOR (editor));
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ if (! gradient ||
+ ! editor->control_sel_r ||
+ editor->control_sel_r->right_color_type != GIMP_GRADIENT_COLOR_FIXED)
+ return;
+
+ editor->saved_dirty = gimp_data_is_dirty (GIMP_DATA (gradient));
+ editor->saved_segments = gradient_editor_save_selection (editor);
+
+ editor->color_dialog =
+ gimp_color_dialog_new (GIMP_VIEWABLE (gradient),
+ GIMP_DATA_EDITOR (editor)->context,
+ _("Right Endpoint Color"),
+ GIMP_ICON_GRADIENT,
+ _("Gradient Segment's Right Endpoint Color"),
+ GTK_WIDGET (editor),
+ gimp_dialog_factory_get_singleton (),
+ "gimp-gradient-editor-color-dialog",
+ &editor->control_sel_l->right_color,
+ TRUE, TRUE);
+
+ g_signal_connect (editor->color_dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &editor->color_dialog);
+
+ g_signal_connect (editor->color_dialog, "update",
+ G_CALLBACK (gradient_editor_right_color_update),
+ editor);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
+ gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)),
+ gimp_editor_get_popup_data (GIMP_EDITOR (editor)));
+
+ gtk_window_present (GTK_WINDOW (editor->color_dialog));
+}
+
+void
+gimp_gradient_editor_zoom (GimpGradientEditor *editor,
+ GimpZoomType zoom_type)
+{
+ GtkAdjustment *adjustment;
+ gdouble old_value;
+ gdouble old_page_size;
+ gdouble value = 0.0;
+ gdouble page_size = 1.0;
+
+ g_return_if_fail (GIMP_IS_GRADIENT_EDITOR (editor));
+
+ adjustment = editor->scroll_data;
+
+ old_value = gtk_adjustment_get_value (adjustment);
+ old_page_size = gtk_adjustment_get_page_size (adjustment);
+
+ switch (zoom_type)
+ {
+ case GIMP_ZOOM_IN_MAX:
+ case GIMP_ZOOM_IN_MORE:
+ case GIMP_ZOOM_IN:
+ editor->zoom_factor++;
+
+ page_size = 1.0 / editor->zoom_factor;
+ value = old_value + (old_page_size - page_size) / 2.0;
+ break;
+
+ case GIMP_ZOOM_OUT_MORE:
+ case GIMP_ZOOM_OUT:
+ if (editor->zoom_factor <= 1)
+ return;
+
+ editor->zoom_factor--;
+
+ page_size = 1.0 / editor->zoom_factor;
+ value = old_value - (page_size - old_page_size) / 2.0;
+
+ if (value < 0.0)
+ value = 0.0;
+ else if ((value + page_size) > 1.0)
+ value = 1.0 - page_size;
+ break;
+
+ case GIMP_ZOOM_OUT_MAX:
+ case GIMP_ZOOM_TO: /* abused as ZOOM_ALL */
+ editor->zoom_factor = 1;
+
+ value = 0.0;
+ page_size = 1.0;
+ break;
+ }
+
+ gtk_adjustment_configure (adjustment,
+ value,
+ gtk_adjustment_get_lower (adjustment),
+ gtk_adjustment_get_upper (adjustment),
+ page_size * GRAD_SCROLLBAR_STEP_SIZE,
+ page_size * GRAD_SCROLLBAR_PAGE_SIZE,
+ page_size);
+}
+
+
+/* private functions */
+
+static void
+gimp_gradient_editor_update (GimpGradientEditor *editor)
+{
+ GimpGradient *gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ control_update (editor, gradient, FALSE);
+}
+
+static void
+gimp_gradient_editor_gradient_dirty (GimpGradientEditor *editor,
+ GimpGradient *gradient)
+{
+ GimpGradientSegment *segment;
+ gboolean left_seen = FALSE;
+ gboolean right_seen = FALSE;
+
+ for (segment = gradient->segments; segment; segment = segment->next)
+ {
+ if (segment == editor->control_sel_l)
+ left_seen = TRUE;
+
+ if (segment == editor->control_sel_r)
+ right_seen = TRUE;
+
+ if (right_seen && ! left_seen)
+ {
+ GimpGradientSegment *tmp;
+
+ tmp = editor->control_sel_l;
+ editor->control_sel_l = editor->control_sel_r;
+ editor->control_sel_r = tmp;
+
+ right_seen = FALSE;
+ left_seen = TRUE;
+ }
+ }
+
+ control_update (editor, gradient, ! (left_seen && right_seen));
+}
+
+static void
+gradient_editor_drop_gradient (GtkWidget *widget,
+ gint x,
+ gint y,
+ GimpViewable *viewable,
+ gpointer data)
+{
+ gimp_data_editor_set_data (GIMP_DATA_EDITOR (data), GIMP_DATA (viewable));
+}
+
+static void
+gradient_editor_drop_color (GtkWidget *widget,
+ gint x,
+ gint y,
+ const GimpRGB *color,
+ gpointer data)
+{
+ GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data);
+ GimpGradient *gradient;
+ gdouble xpos;
+ GimpGradientSegment *seg, *lseg, *rseg;
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ xpos = control_calc_g_pos (editor, x);
+ seg = gimp_gradient_get_segment_at (gradient, xpos);
+
+ gimp_data_freeze (GIMP_DATA (gradient));
+
+ gimp_gradient_segment_split_midpoint (gradient,
+ GIMP_DATA_EDITOR (editor)->context,
+ seg,
+ editor->blend_color_space,
+ &lseg, &rseg);
+
+ if (lseg)
+ {
+ lseg->right = xpos;
+ lseg->middle = (lseg->left + lseg->right) / 2.0;
+ lseg->right_color = *color;
+ }
+
+ if (rseg)
+ {
+ rseg->left = xpos;
+ rseg->middle = (rseg->left + rseg->right) / 2.0;
+ rseg->left_color = *color;
+ }
+
+ gimp_data_thaw (GIMP_DATA (gradient));
+}
+
+static void
+gradient_editor_control_drop_color (GtkWidget *widget,
+ gint x,
+ gint y,
+ const GimpRGB *color,
+ gpointer data)
+{
+ GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data);
+ GimpGradient *gradient;
+ gdouble xpos;
+ GimpGradientSegment *seg, *lseg, *rseg;
+ GradientEditorDragMode handle;
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ xpos = control_calc_g_pos (editor, x);
+ seg_get_closest_handle (gradient, xpos, &seg, &handle);
+
+ if (seg)
+ {
+ if (handle == GRAD_DRAG_LEFT)
+ {
+ lseg = seg->prev;
+ rseg = seg;
+ }
+ else
+ return;
+ }
+ else
+ {
+ lseg = gimp_gradient_get_segment_at (gradient, xpos);
+ rseg = NULL;
+ }
+
+ gimp_data_freeze (GIMP_DATA (gradient));
+
+ if (lseg)
+ lseg->right_color = *color;
+
+ if (rseg)
+ rseg->left_color = *color;
+
+ gimp_data_thaw (GIMP_DATA (gradient));
+}
+
+static void
+gradient_editor_scrollbar_update (GtkAdjustment *adjustment,
+ GimpGradientEditor *editor)
+{
+ GimpDataEditor *data_editor = GIMP_DATA_EDITOR (editor);
+ GimpViewRendererGradient *renderer;
+ gchar *str1;
+ gchar *str2;
+
+ str1 = g_strdup_printf (_("Zoom factor: %d:1"),
+ editor->zoom_factor);
+
+ str2 = g_strdup_printf (_("Displaying [%0.4f, %0.4f]"),
+ gtk_adjustment_get_value (adjustment),
+ gtk_adjustment_get_value (adjustment) +
+ gtk_adjustment_get_page_size (adjustment));
+
+ gradient_editor_set_hint (editor, str1, str2, NULL, NULL);
+
+ g_free (str1);
+ g_free (str2);
+
+ renderer = GIMP_VIEW_RENDERER_GRADIENT (GIMP_VIEW (data_editor->view)->renderer);
+
+ gimp_view_renderer_gradient_set_offsets (renderer,
+ gtk_adjustment_get_value (adjustment),
+ gtk_adjustment_get_value (adjustment) +
+ gtk_adjustment_get_page_size (adjustment));
+
+ gimp_view_renderer_update (GIMP_VIEW_RENDERER (renderer));
+ gimp_gradient_editor_update (editor);
+}
+
+static void
+gradient_editor_set_hint (GimpGradientEditor *editor,
+ const gchar *str1,
+ const gchar *str2,
+ const gchar *str3,
+ const gchar *str4)
+{
+ gtk_label_set_text (GTK_LABEL (editor->hint_label1), str1);
+ gtk_label_set_text (GTK_LABEL (editor->hint_label2), str2);
+ gtk_label_set_text (GTK_LABEL (editor->hint_label3), str3);
+ gtk_label_set_text (GTK_LABEL (editor->hint_label4), str4);
+}
+
+static GimpGradientSegment *
+gradient_editor_save_selection (GimpGradientEditor *editor)
+{
+ GimpGradientSegment *seg, *prev, *tmp;
+ GimpGradientSegment *oseg, *oaseg;
+
+ prev = NULL;
+ oseg = editor->control_sel_l;
+ tmp = NULL;
+
+ do
+ {
+ seg = gimp_gradient_segment_new ();
+
+ *seg = *oseg; /* Copy everything */
+
+ if (prev == NULL)
+ tmp = seg; /* Remember first segment */
+ else
+ prev->next = seg;
+
+ seg->prev = prev;
+ seg->next = NULL;
+
+ prev = seg;
+ oaseg = oseg;
+ oseg = oseg->next;
+ }
+ while (oaseg != editor->control_sel_r);
+
+ return tmp;
+}
+
+static void
+gradient_editor_replace_selection (GimpGradientEditor *editor,
+ GimpGradientSegment *replace_seg)
+{
+ GimpGradient *gradient;
+ GimpGradientSegment *lseg, *rseg;
+ GimpGradientSegment *replace_last;
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ /* Remember left and right segments */
+
+ lseg = editor->control_sel_l->prev;
+ rseg = editor->control_sel_r->next;
+
+ replace_last = gimp_gradient_segment_get_last (replace_seg);
+
+ /* Free old selection */
+
+ editor->control_sel_r->next = NULL;
+
+ gimp_gradient_segments_free (editor->control_sel_l);
+
+ /* Link in new segments */
+
+ if (lseg)
+ lseg->next = replace_seg;
+ else
+ gradient->segments = replace_seg;
+
+ replace_seg->prev = lseg;
+
+ if (rseg)
+ rseg->prev = replace_last;
+
+ replace_last->next = rseg;
+
+ editor->control_sel_l = replace_seg;
+ editor->control_sel_r = replace_last;
+}
+
+static void
+gradient_editor_left_color_update (GimpColorDialog *dialog,
+ const GimpRGB *color,
+ GimpColorDialogState state,
+ GimpGradientEditor *editor)
+{
+ GimpGradient *gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ switch (state)
+ {
+ case GIMP_COLOR_DIALOG_UPDATE:
+ gimp_gradient_segment_range_blend (gradient,
+ editor->control_sel_l,
+ editor->control_sel_r,
+ color,
+ &editor->control_sel_r->right_color,
+ TRUE, TRUE);
+ break;
+
+ case GIMP_COLOR_DIALOG_OK:
+ gimp_gradient_segment_range_blend (gradient,
+ editor->control_sel_l,
+ editor->control_sel_r,
+ color,
+ &editor->control_sel_r->right_color,
+ TRUE, TRUE);
+ gimp_gradient_segments_free (editor->saved_segments);
+ gtk_widget_destroy (editor->color_dialog);
+ editor->color_dialog = NULL;
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
+ gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)),
+ gimp_editor_get_popup_data (GIMP_EDITOR (editor)));
+ break;
+
+ case GIMP_COLOR_DIALOG_CANCEL:
+ gradient_editor_replace_selection (editor, editor->saved_segments);
+ if (! editor->saved_dirty)
+ gimp_data_clean (GIMP_DATA (gradient));
+ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gradient));
+ gtk_widget_destroy (editor->color_dialog);
+ editor->color_dialog = NULL;
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
+ gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)),
+ gimp_editor_get_popup_data (GIMP_EDITOR (editor)));
+ break;
+ }
+}
+
+static void
+gradient_editor_right_color_update (GimpColorDialog *dialog,
+ const GimpRGB *color,
+ GimpColorDialogState state,
+ GimpGradientEditor *editor)
+{
+ GimpGradient *gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ switch (state)
+ {
+ case GIMP_COLOR_DIALOG_UPDATE:
+ gimp_gradient_segment_range_blend (gradient,
+ editor->control_sel_l,
+ editor->control_sel_r,
+ &editor->control_sel_l->left_color,
+ color,
+ TRUE, TRUE);
+ break;
+
+ case GIMP_COLOR_DIALOG_OK:
+ gimp_gradient_segment_range_blend (gradient,
+ editor->control_sel_l,
+ editor->control_sel_r,
+ &editor->control_sel_l->left_color,
+ color,
+ TRUE, TRUE);
+ gimp_gradient_segments_free (editor->saved_segments);
+ gtk_widget_destroy (editor->color_dialog);
+ editor->color_dialog = NULL;
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
+ gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)),
+ gimp_editor_get_popup_data (GIMP_EDITOR (editor)));
+ break;
+
+ case GIMP_COLOR_DIALOG_CANCEL:
+ gradient_editor_replace_selection (editor, editor->saved_segments);
+ if (! editor->saved_dirty)
+ gimp_data_clean (GIMP_DATA (gradient));
+ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gradient));
+ gtk_widget_destroy (editor->color_dialog);
+ editor->color_dialog = NULL;
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
+ gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)),
+ gimp_editor_get_popup_data (GIMP_EDITOR (editor)));
+ break;
+ }
+}
+
+
+/***** Gradient view functions *****/
+
+static gboolean
+view_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpGradientEditor *editor)
+{
+ GimpDataEditor *data_editor = GIMP_DATA_EDITOR (editor);
+
+ if (! data_editor->data)
+ return TRUE;
+
+ switch (event->type)
+ {
+ case GDK_LEAVE_NOTIFY:
+ gradient_editor_set_hint (editor, NULL, NULL, NULL, NULL);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ {
+ GdkEventMotion *mevent = (GdkEventMotion *) event;
+
+ if (mevent->x != editor->view_last_x)
+ {
+ editor->view_last_x = mevent->x;
+
+ if (editor->view_button_down)
+ {
+ view_pick_color (editor,
+ (mevent->state & gimp_get_toggle_behavior_mask ()) ?
+ GIMP_COLOR_PICK_TARGET_BACKGROUND :
+ GIMP_COLOR_PICK_TARGET_FOREGROUND,
+ GIMP_COLOR_PICK_STATE_UPDATE,
+ mevent->x);
+ }
+ else
+ {
+ view_set_hint (editor, mevent->x);
+ }
+ }
+
+ gdk_event_request_motions (mevent);
+ }
+ break;
+
+ case GDK_BUTTON_PRESS:
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
+ {
+ gimp_editor_popup_menu (GIMP_EDITOR (editor), NULL, NULL);
+ }
+ else if (bevent->button == 1)
+ {
+ editor->view_last_x = bevent->x;
+ editor->view_button_down = TRUE;
+
+ view_pick_color (editor,
+ (bevent->state & gimp_get_toggle_behavior_mask ()) ?
+ GIMP_COLOR_PICK_TARGET_BACKGROUND :
+ GIMP_COLOR_PICK_TARGET_FOREGROUND,
+ GIMP_COLOR_PICK_STATE_START,
+ bevent->x);
+ }
+ }
+ break;
+
+ case GDK_SCROLL:
+ {
+ GdkEventScroll *sevent = (GdkEventScroll *) event;
+
+ if (sevent->state & gimp_get_toggle_behavior_mask ())
+ {
+ switch (sevent->direction)
+ {
+ case GDK_SCROLL_UP:
+ gimp_gradient_editor_zoom (editor, GIMP_ZOOM_IN);
+ break;
+
+ case GDK_SCROLL_DOWN:
+ gimp_gradient_editor_zoom (editor, GIMP_ZOOM_OUT);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ GtkAdjustment *adj = editor->scroll_data;
+ gfloat value = gtk_adjustment_get_value (adj);
+
+ switch (sevent->direction)
+ {
+ case GDK_SCROLL_UP:
+ value -= gtk_adjustment_get_page_increment (adj) / 2;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ value += gtk_adjustment_get_page_increment (adj) / 2;
+ break;
+
+ default:
+ break;
+ }
+
+ value = CLAMP (value,
+ gtk_adjustment_get_lower (adj),
+ gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_page_size (adj));
+
+ gtk_adjustment_set_value (adj, value);
+ }
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (editor->view_button_down)
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ editor->view_last_x = bevent->x;
+ editor->view_button_down = FALSE;
+
+ view_pick_color (editor,
+ (bevent->state & gimp_get_toggle_behavior_mask ()) ?
+ GIMP_COLOR_PICK_TARGET_BACKGROUND :
+ GIMP_COLOR_PICK_TARGET_FOREGROUND,
+ GIMP_COLOR_PICK_STATE_END,
+ bevent->x);
+ break;
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+view_set_hint (GimpGradientEditor *editor,
+ gint x)
+{
+ GimpDataEditor *data_editor = GIMP_DATA_EDITOR (editor);
+ GimpRGB rgb;
+ GimpHSV hsv;
+ gdouble xpos;
+ gchar *str1;
+ gchar *str2;
+ gchar *str3;
+ gchar *str4;
+
+ xpos = control_calc_g_pos (editor, x);
+
+ gimp_gradient_get_color_at (GIMP_GRADIENT (data_editor->data),
+ data_editor->context, NULL,
+ xpos, FALSE, FALSE, &rgb);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (editor->current_color), &rgb);
+
+ gimp_rgb_to_hsv (&rgb, &hsv);
+
+ str1 = g_strdup_printf (_("Position: %0.4f"), xpos);
+ str2 = g_strdup_printf (_("RGB (%0.3f, %0.3f, %0.3f)"),
+ rgb.r, rgb.g, rgb.b);
+ str3 = g_strdup_printf (_("HSV (%0.1f, %0.1f, %0.1f)"),
+ hsv.h * 360.0, hsv.s * 100.0, hsv.v * 100.0);
+ str4 = g_strdup_printf (_("Luminance: %0.1f Opacity: %0.1f"),
+ GIMP_RGB_LUMINANCE (rgb.r, rgb.g, rgb.b) * 100.0,
+ rgb.a * 100.0);
+
+ gradient_editor_set_hint (editor, str1, str2, str3, str4);
+
+ g_free (str1);
+ g_free (str2);
+ g_free (str3);
+ g_free (str4);
+}
+
+static void
+view_pick_color (GimpGradientEditor *editor,
+ GimpColorPickTarget pick_target,
+ GimpColorPickState pick_state,
+ gint x)
+{
+ GimpDataEditor *data_editor = GIMP_DATA_EDITOR (editor);
+ GimpRGB color;
+ gdouble xpos;
+ gchar *str2;
+ gchar *str3;
+
+ xpos = control_calc_g_pos (editor, x);
+
+ gimp_gradient_get_color_at (GIMP_GRADIENT (data_editor->data),
+ data_editor->context, NULL,
+ xpos, FALSE, FALSE, &color);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (editor->current_color), &color);
+
+ str2 = g_strdup_printf (_("RGB (%d, %d, %d)"),
+ (gint) (color.r * 255.0),
+ (gint) (color.g * 255.0),
+ (gint) (color.b * 255.0));
+
+ str3 = g_strdup_printf ("(%0.3f, %0.3f, %0.3f)", color.r, color.g, color.b);
+
+ if (pick_target == GIMP_COLOR_PICK_TARGET_FOREGROUND)
+ {
+ gimp_context_set_foreground (data_editor->context, &color);
+
+ gradient_editor_set_hint (editor, _("Foreground color set to:"),
+ str2, str3, NULL);
+ }
+ else
+ {
+ gimp_context_set_background (data_editor->context, &color);
+
+ gradient_editor_set_hint (editor, _("Background color set to:"),
+ str2, str3, NULL);
+ }
+
+ g_free (str2);
+ g_free (str3);
+}
+
+/***** Gradient control functions *****/
+
+static gboolean
+control_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpGradientEditor *editor)
+{
+ GimpGradient *gradient;
+ GimpGradientSegment *seg;
+
+ if (! GIMP_DATA_EDITOR (editor)->data)
+ return TRUE;
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ switch (event->type)
+ {
+ case GDK_LEAVE_NOTIFY:
+ gradient_editor_set_hint (editor, NULL, NULL, NULL, NULL);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ if (editor->control_drag_mode == GRAD_DRAG_NONE)
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ editor->control_last_x = bevent->x;
+ editor->control_click_time = bevent->time;
+
+ control_button_press (editor,
+ bevent->x, bevent->y,
+ bevent->button, bevent->state);
+
+ if (editor->control_drag_mode != GRAD_DRAG_NONE)
+ {
+ gtk_grab_add (widget);
+
+ if (GIMP_DATA_EDITOR (editor)->data_editable)
+ {
+ g_signal_handlers_block_by_func (gradient,
+ gimp_gradient_editor_gradient_dirty,
+ editor);
+ }
+ }
+ }
+ break;
+
+ case GDK_SCROLL:
+ {
+ GdkEventScroll *sevent = (GdkEventScroll *) event;
+
+ if (sevent->state & gimp_get_toggle_behavior_mask ())
+ {
+ if (sevent->direction == GDK_SCROLL_UP)
+ gimp_gradient_editor_zoom (editor, GIMP_ZOOM_IN);
+ else
+ gimp_gradient_editor_zoom (editor, GIMP_ZOOM_OUT);
+ }
+ else
+ {
+ GtkAdjustment *adj = editor->scroll_data;
+
+ gfloat new_value;
+
+ new_value = (gtk_adjustment_get_value (adj) +
+ ((sevent->direction == GDK_SCROLL_UP) ?
+ - gtk_adjustment_get_page_increment (adj) / 2 :
+ gtk_adjustment_get_page_increment (adj) / 2));
+
+ new_value = CLAMP (new_value,
+ gtk_adjustment_get_lower (adj),
+ gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_page_size (adj));
+
+ gtk_adjustment_set_value (adj, new_value);
+ }
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ gradient_editor_set_hint (editor, NULL, NULL, NULL, NULL);
+
+ if (editor->control_drag_mode != GRAD_DRAG_NONE)
+ {
+ if (GIMP_DATA_EDITOR (editor)->data_editable)
+ {
+ g_signal_handlers_unblock_by_func (gradient,
+ gimp_gradient_editor_gradient_dirty,
+ editor);
+ }
+
+ gtk_grab_remove (widget);
+
+ if ((bevent->time - editor->control_click_time) >= GRAD_MOVE_TIME)
+ {
+ /* stuff was done in motion */
+ }
+ else if ((editor->control_drag_mode == GRAD_DRAG_MIDDLE) ||
+ (editor->control_drag_mode == GRAD_DRAG_ALL))
+ {
+ seg = editor->control_drag_segment;
+
+ if ((editor->control_drag_mode == GRAD_DRAG_ALL) &&
+ editor->control_compress)
+ {
+ control_extend_selection (editor, seg,
+ control_calc_g_pos (editor,
+ bevent->x));
+ }
+ else
+ {
+ control_select_single_segment (editor, seg);
+ }
+
+ gimp_gradient_editor_update (editor);
+ }
+
+ editor->control_drag_mode = GRAD_DRAG_NONE;
+ editor->control_compress = FALSE;
+
+ control_do_hint (editor, bevent->x, bevent->y);
+ }
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ {
+ GdkEventMotion *mevent = (GdkEventMotion *) event;
+
+ if (mevent->x != editor->control_last_x)
+ {
+ editor->control_last_x = mevent->x;
+
+ if (GIMP_DATA_EDITOR (editor)->data_editable &&
+ editor->control_drag_mode != GRAD_DRAG_NONE)
+ {
+
+ if ((mevent->time - editor->control_click_time) >= GRAD_MOVE_TIME)
+ control_motion (editor, gradient, mevent->x);
+ }
+ else
+ {
+ gimp_gradient_editor_update (editor);
+
+ control_do_hint (editor, mevent->x, mevent->y);
+ }
+ }
+
+ gdk_event_request_motions (mevent);
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+control_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpGradientEditor *editor)
+{
+ GtkAdjustment *adj = editor->scroll_data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget));
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ control_draw (editor,
+ GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data),
+ cr,
+ allocation.width,
+ allocation.height,
+ gtk_adjustment_get_value (adj),
+ gtk_adjustment_get_value (adj) +
+ gtk_adjustment_get_page_size (adj));
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static void
+control_do_hint (GimpGradientEditor *editor,
+ gint x,
+ gint y)
+{
+ GimpGradient *gradient;
+ GimpGradientSegment *seg;
+ GradientEditorDragMode handle;
+ gboolean in_handle;
+ gdouble pos;
+ gchar *str;
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ pos = control_calc_g_pos (editor, x);
+
+ if ((pos < 0.0) || (pos > 1.0))
+ return;
+
+ seg_get_closest_handle (gradient, pos, &seg, &handle);
+
+ in_handle = control_point_in_handle (editor, gradient,
+ x, y, seg, handle);
+
+ if (in_handle)
+ {
+ switch (handle)
+ {
+ case GRAD_DRAG_LEFT:
+ if (seg != NULL)
+ {
+ if (seg->prev != NULL)
+ {
+ str = g_strdup_printf (_("%s-Drag: move & compress"),
+ gimp_get_mod_string (GDK_SHIFT_MASK));
+
+ gradient_editor_set_hint (editor,
+ NULL,
+ _("Drag: move"),
+ str,
+ NULL);
+ g_free (str);
+ }
+ else
+ {
+ str = g_strdup_printf (_("%s-Click: extend selection"),
+ gimp_get_mod_string (GDK_SHIFT_MASK));
+
+ gradient_editor_set_hint (editor,
+ NULL,
+ _("Click: select"),
+ str,
+ NULL);
+ g_free (str);
+ }
+ }
+ else
+ {
+ str = g_strdup_printf (_("%s-Click: extend selection"),
+ gimp_get_mod_string (GDK_SHIFT_MASK));
+
+ gradient_editor_set_hint (editor,
+ NULL,
+ _("Click: select"),
+ str,
+ NULL);
+ g_free (str);
+ }
+ break;
+
+ case GRAD_DRAG_MIDDLE:
+ str = g_strdup_printf (_("%s-Click: extend selection"),
+ gimp_get_mod_string (GDK_SHIFT_MASK));
+
+ gradient_editor_set_hint (editor,
+ NULL,
+ _("Click: select Drag: move"),
+ str,
+ NULL);
+ g_free (str);
+ break;
+
+ default:
+ g_warning ("%s: in_handle is true, but received handle type %d.",
+ G_STRFUNC, in_handle);
+ break;
+ }
+ }
+ else
+ {
+ gchar *str2;
+
+ str = g_strdup_printf (_("%s-Click: extend selection"),
+ gimp_get_mod_string (GDK_SHIFT_MASK));
+ str2 = g_strdup_printf (_("%s-Drag: move & compress"),
+ gimp_get_mod_string (GDK_SHIFT_MASK));
+
+ gradient_editor_set_hint (editor,
+ _("Click: select Drag: move"),
+ str,
+ str2,
+ NULL);
+ g_free (str);
+ g_free (str2);
+ }
+}
+
+static void
+control_button_press (GimpGradientEditor *editor,
+ gint x,
+ gint y,
+ guint button,
+ guint state)
+{
+ GimpGradient *gradient;
+ GimpGradientSegment *seg;
+ GradientEditorDragMode handle;
+ gdouble xpos;
+ gboolean in_handle;
+
+ gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ if (button == 3)
+ {
+ gimp_editor_popup_menu (GIMP_EDITOR (editor), NULL, NULL);
+ return;
+ }
+
+ /* Find the closest handle */
+
+ xpos = control_calc_g_pos (editor, x);
+
+ seg_get_closest_handle (gradient, xpos, &seg, &handle);
+
+ in_handle = control_point_in_handle (editor, gradient, x, y, seg, handle);
+
+ /* Now see what we have */
+
+ if (in_handle)
+ {
+ switch (handle)
+ {
+ case GRAD_DRAG_LEFT:
+ if (seg != NULL)
+ {
+ /* Left handle of some segment */
+ if (state & GDK_SHIFT_MASK)
+ {
+ if (seg->prev != NULL)
+ {
+ editor->control_drag_mode = GRAD_DRAG_LEFT;
+ editor->control_drag_segment = seg;
+ editor->control_compress = TRUE;
+ }
+ else
+ {
+ control_extend_selection (editor, seg, xpos);
+ gimp_gradient_editor_update (editor);
+ }
+ }
+ else if (seg->prev != NULL)
+ {
+ editor->control_drag_mode = GRAD_DRAG_LEFT;
+ editor->control_drag_segment = seg;
+ }
+ else
+ {
+ control_select_single_segment (editor, seg);
+ gimp_gradient_editor_update (editor);
+ }
+ }
+ else /* seg == NULL */
+ {
+ /* Right handle of last segment */
+ seg = gimp_gradient_segment_get_last (gradient->segments);
+
+ if (state & GDK_SHIFT_MASK)
+ {
+ control_extend_selection (editor, seg, xpos);
+ gimp_gradient_editor_update (editor);
+ }
+ else
+ {
+ control_select_single_segment (editor, seg);
+ gimp_gradient_editor_update (editor);
+ }
+ }
+
+ break;
+
+ case GRAD_DRAG_MIDDLE:
+ if (state & GDK_SHIFT_MASK)
+ {
+ control_extend_selection (editor, seg, xpos);
+ gimp_gradient_editor_update (editor);
+ }
+ else
+ {
+ editor->control_drag_mode = GRAD_DRAG_MIDDLE;
+ editor->control_drag_segment = seg;
+ }
+
+ break;
+
+ default:
+ g_warning ("%s: in_handle is true, but received handle type %d.",
+ G_STRFUNC, in_handle);
+ }
+ }
+ else /* !in_handle */
+ {
+ seg = gimp_gradient_get_segment_at (gradient, xpos);
+
+ editor->control_drag_mode = GRAD_DRAG_ALL;
+ editor->control_drag_segment = seg;
+ editor->control_last_gx = xpos;
+ editor->control_orig_pos = xpos;
+
+ if (state & GDK_SHIFT_MASK)
+ editor->control_compress = TRUE;
+ }
+}
+
+static gboolean
+control_point_in_handle (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ gint x,
+ gint y,
+ GimpGradientSegment *seg,
+ GradientEditorDragMode handle)
+{
+ gint handle_pos;
+
+ switch (handle)
+ {
+ case GRAD_DRAG_LEFT:
+ if (seg)
+ {
+ handle_pos = control_calc_p_pos (editor, seg->left);
+ }
+ else
+ {
+ seg = gimp_gradient_segment_get_last (gradient->segments);
+
+ handle_pos = control_calc_p_pos (editor, seg->right);
+ }
+
+ break;
+
+ case GRAD_DRAG_MIDDLE:
+ handle_pos = control_calc_p_pos (editor, seg->middle);
+ break;
+
+ default:
+ g_warning ("%s: Cannot handle drag mode %d.", G_STRFUNC, handle);
+ return FALSE;
+ }
+
+ y /= 2;
+
+ if ((x >= (handle_pos - y)) && (x <= (handle_pos + y)))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*****/
+
+static void
+control_select_single_segment (GimpGradientEditor *editor,
+ GimpGradientSegment *seg)
+{
+ editor->control_sel_l = seg;
+ editor->control_sel_r = seg;
+}
+
+static void
+control_extend_selection (GimpGradientEditor *editor,
+ GimpGradientSegment *seg,
+ gdouble pos)
+{
+ if (fabs (pos - editor->control_sel_l->left) <
+ fabs (pos - editor->control_sel_r->right))
+ editor->control_sel_l = seg;
+ else
+ editor->control_sel_r = seg;
+}
+
+/*****/
+
+static void
+control_motion (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ gint x)
+{
+ GimpGradientSegment *seg = editor->control_drag_segment;
+ gdouble pos;
+ gdouble delta;
+ gchar *str = NULL;
+
+ switch (editor->control_drag_mode)
+ {
+ case GRAD_DRAG_LEFT:
+ pos = control_calc_g_pos (editor, x);
+
+ if (! editor->control_compress)
+ gimp_gradient_segment_set_left_pos (gradient, seg, pos);
+ else
+ control_compress_left (gradient,
+ editor->control_sel_l,
+ editor->control_sel_r,
+ seg, pos);
+
+ str = g_strdup_printf (_("Handle position: %0.4f"), seg->left);
+ break;
+
+ case GRAD_DRAG_MIDDLE:
+ pos = control_calc_g_pos (editor, x);
+
+ gimp_gradient_segment_set_middle_pos (gradient, seg, pos);
+
+ str = g_strdup_printf (_("Handle position: %0.4f"), seg->middle);
+ break;
+
+ case GRAD_DRAG_ALL:
+ pos = control_calc_g_pos (editor, x);
+ delta = pos - editor->control_last_gx;
+
+ if ((seg->left >= editor->control_sel_l->left) &&
+ (seg->right <= editor->control_sel_r->right))
+ delta = control_move (editor,
+ editor->control_sel_l,
+ editor->control_sel_r, delta);
+ else
+ delta = control_move (editor, seg, seg, delta);
+
+ editor->control_last_gx += delta;
+
+ str = g_strdup_printf (_("Distance: %0.4f"),
+ editor->control_last_gx -
+ editor->control_orig_pos);
+ break;
+
+ default:
+ g_warning ("%s: Attempting to move bogus handle %d.",
+ G_STRFUNC, editor->control_drag_mode);
+ break;
+ }
+
+ gradient_editor_set_hint (editor, str, NULL, NULL, NULL);
+ g_free (str);
+
+ gimp_gradient_editor_update (editor);
+}
+
+static void
+control_compress_left (GimpGradient *gradient,
+ GimpGradientSegment *range_l,
+ GimpGradientSegment *range_r,
+ GimpGradientSegment *drag_seg,
+ gdouble pos)
+{
+ GimpGradientSegment *seg;
+ gdouble lbound, rbound;
+ gint k;
+
+ /* Check what we have to compress */
+
+ if (!((drag_seg->left >= range_l->left) &&
+ ((drag_seg->right <= range_r->right) || (drag_seg == range_r->next))))
+ {
+ /* We are compressing a segment outside the selection */
+
+ range_l = range_r = drag_seg;
+ }
+
+ /* Calculate left bound for dragged hadle */
+
+ if (drag_seg == range_l)
+ lbound = range_l->prev->left + 2.0 * EPSILON;
+ else
+ {
+ /* Count number of segments to the left of the dragged handle */
+
+ seg = drag_seg;
+ k = 0;
+
+ while (seg != range_l)
+ {
+ k++;
+ seg = seg->prev;
+ }
+
+ /* 2*k handles have to fit */
+
+ lbound = range_l->left + 2.0 * k * EPSILON;
+ }
+
+ /* Calculate right bound for dragged handle */
+
+ if (drag_seg == range_r->next)
+ rbound = range_r->next->right - 2.0 * EPSILON;
+ else
+ {
+ /* Count number of segments to the right of the dragged handle */
+
+ seg = drag_seg;
+ k = 1;
+
+ while (seg != range_r)
+ {
+ k++;
+ seg = seg->next;
+ }
+
+ /* 2*k handles have to fit */
+
+ rbound = range_r->right - 2.0 * k * EPSILON;
+ }
+
+ /* Calculate position */
+
+ pos = CLAMP (pos, lbound, rbound);
+
+ /* Compress segments to the left of the handle */
+
+ if (drag_seg == range_l)
+ gimp_gradient_segment_range_compress (gradient,
+ range_l->prev, range_l->prev,
+ range_l->prev->left, pos);
+ else
+ gimp_gradient_segment_range_compress (gradient,
+ range_l, drag_seg->prev,
+ range_l->left, pos);
+
+ /* Compress segments to the right of the handle */
+
+ if (drag_seg != range_r->next)
+ gimp_gradient_segment_range_compress (gradient,
+ drag_seg, range_r,
+ pos, range_r->right);
+ else
+ gimp_gradient_segment_range_compress (gradient,
+ drag_seg, drag_seg,
+ pos, drag_seg->right);
+}
+
+/*****/
+
+static gdouble
+control_move (GimpGradientEditor *editor,
+ GimpGradientSegment *range_l,
+ GimpGradientSegment *range_r,
+ gdouble delta)
+{
+ GimpGradient *gradient = GIMP_GRADIENT (GIMP_DATA_EDITOR (editor)->data);
+
+ return gimp_gradient_segment_range_move (gradient,
+ range_l,
+ range_r,
+ delta,
+ editor->control_compress);
+}
+
+/*****/
+
+static void
+control_update (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ gboolean reset_selection)
+{
+ if (! editor->control_sel_l || ! editor->control_sel_r)
+ reset_selection = TRUE;
+
+ if (reset_selection)
+ {
+ if (gradient)
+ control_select_single_segment (editor, gradient->segments);
+ else
+ control_select_single_segment (editor, NULL);
+ }
+
+ gtk_widget_queue_draw (editor->control);
+}
+
+static void
+control_draw (GimpGradientEditor *editor,
+ GimpGradient *gradient,
+ cairo_t *cr,
+ gint width,
+ gint height,
+ gdouble left,
+ gdouble right)
+{
+ GtkStyle *control_style;
+ GimpGradientSegment *seg;
+ GradientEditorDragMode handle;
+ gint sel_l;
+ gint sel_r;
+ gdouble g_pos;
+ gboolean selected;
+
+ if (! gradient)
+ return;
+
+ /* Draw selection */
+
+ control_style = gtk_widget_get_style (editor->control);
+
+ sel_l = control_calc_p_pos (editor, editor->control_sel_l->left);
+ sel_r = control_calc_p_pos (editor, editor->control_sel_r->right);
+
+ gdk_cairo_set_source_color (cr,
+ &control_style->base[GTK_STATE_NORMAL]);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_fill (cr);
+
+ gdk_cairo_set_source_color (cr,
+ &control_style->base[GTK_STATE_SELECTED]);
+ cairo_rectangle (cr, sel_l, 0, sel_r - sel_l + 1, height);
+ cairo_fill (cr);
+
+ /* Draw handles */
+
+ selected = FALSE;
+
+ for (seg = gradient->segments; seg; seg = seg->next)
+ {
+ if (seg == editor->control_sel_l)
+ selected = TRUE;
+
+ control_draw_normal_handle (editor, cr, seg->left, height, selected);
+ control_draw_middle_handle (editor, cr, seg->middle, height, selected);
+
+ /* Draw right handle only if this is the last segment */
+ if (seg->next == NULL)
+ control_draw_normal_handle (editor, cr, seg->right, height, selected);
+
+ if (seg == editor->control_sel_r)
+ selected = FALSE;
+ }
+
+ /* Draw the handle which is closest to the mouse position */
+
+ g_pos = control_calc_g_pos (editor, editor->control_last_x);
+
+ seg_get_closest_handle (gradient, CLAMP (g_pos, 0.0, 1.0), &seg, &handle);
+
+ selected = (seg &&
+ seg_in_selection (gradient, seg,
+ editor->control_sel_l, editor->control_sel_r));
+
+ switch (handle)
+ {
+ case GRAD_DRAG_LEFT:
+ if (seg)
+ {
+ control_draw_normal_handle (editor, cr, seg->left, height, selected);
+ }
+ else
+ {
+ seg = gimp_gradient_segment_get_last (gradient->segments);
+
+ selected = (seg == editor->control_sel_r);
+
+ control_draw_normal_handle (editor, cr, seg->right, height, selected);
+ }
+
+ break;
+
+ case GRAD_DRAG_MIDDLE:
+ control_draw_middle_handle (editor, cr, seg->middle, height, selected);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+control_draw_normal_handle (GimpGradientEditor *editor,
+ cairo_t *cr,
+ gdouble pos,
+ gint height,
+ gboolean selected)
+{
+ GtkStyle *style = gtk_widget_get_style (editor->control);
+ GtkStateType state = selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL;
+
+ control_draw_handle (cr,
+ &style->text_aa[state],
+ &style->black,
+ control_calc_p_pos (editor, pos), height);
+}
+
+static void
+control_draw_middle_handle (GimpGradientEditor *editor,
+ cairo_t *cr,
+ gdouble pos,
+ gint height,
+ gboolean selected)
+{
+ GtkStyle *style = gtk_widget_get_style (editor->control);
+ GtkStateType state = selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL;
+
+ control_draw_handle (cr,
+ &style->text_aa[state],
+ &style->white,
+ control_calc_p_pos (editor, pos), height);
+}
+
+static void
+control_draw_handle (cairo_t *cr,
+ GdkColor *border,
+ GdkColor *fill,
+ gint xpos,
+ gint height)
+{
+ cairo_move_to (cr, xpos, 0);
+ cairo_line_to (cr, xpos - height / 2.0, height);
+ cairo_line_to (cr, xpos + height / 2.0, height);
+ cairo_line_to (cr, xpos, 0);
+
+ gdk_cairo_set_source_color (cr, fill);
+ cairo_fill_preserve (cr);
+
+ gdk_cairo_set_source_color (cr, border);
+ cairo_set_line_width (cr, 1);
+ cairo_stroke (cr);
+}
+
+/*****/
+
+static gint
+control_calc_p_pos (GimpGradientEditor *editor,
+ gdouble pos)
+{
+ GtkAdjustment *adjustment = editor->scroll_data;
+ GtkAllocation allocation;
+ gint pwidth;
+
+ gtk_widget_get_allocation (editor->control, &allocation);
+
+ pwidth = allocation.width;
+
+ /* Calculate the position (in widget's coordinates) of the
+ * requested point from the gradient. Rounding is done to
+ * minimize mismatches between the rendered gradient view
+ * and the gradient control's handles.
+ */
+
+ return RINT ((pwidth - 1) * (pos - gtk_adjustment_get_value (adjustment)) /
+ gtk_adjustment_get_page_size (adjustment));
+}
+
+static gdouble
+control_calc_g_pos (GimpGradientEditor *editor,
+ gint pos)
+{
+ GtkAdjustment *adjustment = editor->scroll_data;
+ GtkAllocation allocation;
+ gint pwidth;
+
+ gtk_widget_get_allocation (editor->control, &allocation);
+
+ pwidth = allocation.width;
+
+ /* Calculate the gradient position that corresponds to widget's coordinates */
+
+ return (gtk_adjustment_get_page_size (adjustment) * pos / (pwidth - 1) +
+ gtk_adjustment_get_value (adjustment));
+}
+
+/***** Segment functions *****/
+
+static void
+seg_get_closest_handle (GimpGradient *grad,
+ gdouble pos,
+ GimpGradientSegment **seg,
+ GradientEditorDragMode *handle)
+{
+ gdouble l_delta, m_delta, r_delta;
+
+ *seg = gimp_gradient_get_segment_at (grad, pos);
+
+ m_delta = fabs (pos - (*seg)->middle);
+
+ if (pos < (*seg)->middle)
+ {
+ l_delta = fabs (pos - (*seg)->left);
+
+ if (l_delta < m_delta)
+ *handle = GRAD_DRAG_LEFT;
+ else
+ *handle = GRAD_DRAG_MIDDLE;
+ }
+ else
+ {
+ r_delta = fabs (pos - (*seg)->right);
+
+ if (m_delta < r_delta)
+ {
+ *handle = GRAD_DRAG_MIDDLE;
+ }
+ else
+ {
+ *seg = (*seg)->next;
+ *handle = GRAD_DRAG_LEFT;
+ }
+ }
+}
+
+static gboolean
+seg_in_selection (GimpGradient *grad,
+ GimpGradientSegment *seg,
+ GimpGradientSegment *left,
+ GimpGradientSegment *right)
+{
+ GimpGradientSegment *s;
+
+ for (s = left; s; s = s->next)
+ {
+ if (s == seg)
+ return TRUE;
+
+ if (s == right)
+ break;
+ }
+
+ return FALSE;
+}
+
+static GtkWidget *
+gradient_hint_label_add (GtkBox *box)
+{
+ GtkWidget *label = g_object_new (GTK_TYPE_LABEL,
+ "xalign", 0.0,
+ "yalign", 0.5,
+ "single-line-mode", TRUE,
+ NULL);
+ gtk_box_pack_start (box, label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ return label;
+}