gimp/app/gui/gradient-editor.c
Michael Natterer 0164596064 app/core/Makefile.am app/core/core-types.h added an "application object"
2001-07-04  Michael Natterer  <mitch@gimp.org>

	* app/core/Makefile.am
	* app/core/core-types.h
	* app/core/gimp.[ch]: added an "application object" called Gimp.

	Currently, it contains the image list, the clipboard, the data
	factories, the procedural hashtable and the tool info list.  It's
	the toplevel object of the core object system. Finally, creating a
	Gimp object will return a standalone gimp core engine instance
	with no other global states/variables involved.

	* app/app_procs.[ch]: allocate a "Gimp" instance called "the_gimp" :)
	Removed stuff which is now done by the "Gimp" object. Merged
	gimp_init() into app_init() because gimp_init() is taken now.

	* app/context_manager.[ch]: removed stuff done by "Gimp".

	* app/batch.[ch]
	* app/gimage.[ch]
	* app/xcf/xcf-load.[ch]
	* app/xcf/xcf.[ch]
	* app/core/gimpedit.[ch]
	* app/tools/tool_manager.[ch]: pass around an additional "Gimp"
	argument.

	* app/pdb/procedural_db.[ch]: pass a "Gimp" pointer as first
	parameter to all internal procedures and to all procedural_db_*
	functions.

	* app/core/gimpcontext.[ch]
	* app/core/gimpimage.[ch]: added a "Gimp" pointer to the structs.

	* app/devices.c
	* app/errors.c
	* app/file-open.c
	* app/file-save.c
	* app/gimphelp.c
	* app/gimpunit.c
	* app/image_new.c
	* app/main.c
	* app/nav_window.c
	* app/plug_in.c
	* app/base/base.c
	* app/core/gimpdatafactory.c
	* app/core/gimpimage-duplicate.c
	* app/core/gimpimage-mask.c
	* app/core/gimptoolinfo.[ch]
	* app/gui/brush-select.c
	* app/gui/convert-dialog.c
	* app/gui/dialogs-constructors.c
	* app/gui/edit-commands.c
	* app/gui/file-open-dialog.c
	* app/gui/file-save-dialog.c
	* app/gui/gradient-editor.c
	* app/gui/gradient-select.c
	* app/gui/gui.c
	* app/gui/image-commands.c
	* app/gui/info-window.c
	* app/gui/menus.c
	* app/gui/palette-editor.c
	* app/gui/palette-import-dialog.c
	* app/gui/palette-select.c
	* app/gui/paths-dialog.c
	* app/gui/pattern-select.c
	* app/gui/preferences-dialog.c
	* app/gui/test-commands.c
	* app/gui/toolbox.c
	* app/gui/tools-commands.c
	* app/tools/gimpbezierselecttool.c
	* app/tools/gimpbucketfilltool.c
	* app/tools/gimppainttool.h
	* app/tools/gimptexttool.c
	* app/tools/gimptransformtool.h
	* app/widgets/gimpbufferview.c
	* app/widgets/gimpcontainerview-utils.c
	* app/widgets/gimpcursor.c
	* app/widgets/gimpdnd.c
	* app/widgets/gimpimagedock.c: changed accordingly. Cleaned up
	lots of includes. Many files still access the global "the_gimp"
	variable exported by app_procs.h.

	* tools/pdbgen/app.pl
	* tools/pdbgen/pdb/brush_select.pdb
	* tools/pdbgen/pdb/brushes.pdb
	* tools/pdbgen/pdb/convert.pdb
	* tools/pdbgen/pdb/edit.pdb
	* tools/pdbgen/pdb/fileops.pdb
	* tools/pdbgen/pdb/gradient_select.pdb
	* tools/pdbgen/pdb/gradients.pdb
	* tools/pdbgen/pdb/image.pdb
	* tools/pdbgen/pdb/palette.pdb
	* tools/pdbgen/pdb/pattern_select.pdb
	* tools/pdbgen/pdb/patterns.pdb
	* tools/pdbgen/pdb/procedural_db.pdb: changed accordingly. Don't
	use "the_gimp" here because all procedures get passed a "Gimp"
	pointer now.

	* app/pdb/*: regenerated.
2001-07-04 19:31:35 +00:00

4726 lines
132 KiB
C

/* The GIMP -- an 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* 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:
*
* - Fix memory leaks: grad_free_gradient_editor() and any others
* which I may have missed.
*
* - 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "core/core-types.h"
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpdatafactory.h"
#include "core/gimpgradient.h"
#include "widgets/gimpdnd.h"
#include "color-notebook.h"
#include "gradient-editor.h"
#include "app_procs.h"
#include "errors.h"
#include "gimprc.h"
#include "libgimp/gimpintl.h"
#include "pixmaps/zoom_in.xpm"
#include "pixmaps/zoom_out.xpm"
#define EPSILON 1e-10
#define GRAD_SCROLLBAR_STEP_SIZE 0.05
#define GRAD_SCROLLBAR_PAGE_SIZE 0.75
#define GRAD_PREVIEW_WIDTH 600
#define GRAD_PREVIEW_HEIGHT 64
#define GRAD_CONTROL_HEIGHT 10
#define GRAD_COLOR_BOX_WIDTH 24
#define GRAD_COLOR_BOX_HEIGHT 16
#define GRAD_NUM_COLORS 10
#define GRAD_MOVE_TIME 150 /* ms between mouse click and detection of movement in gradient control */
#define GRAD_PREVIEW_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)
enum
{
GRAD_UPDATE_GRADIENT = 1 << 0,
GRAD_UPDATE_PREVIEW = 1 << 1,
GRAD_UPDATE_CONTROL = 1 << 2,
GRAD_RESET_CONTROL = 1 << 3
};
/* Gradient editor type */
typedef enum
{
GRAD_DRAG_NONE = 0,
GRAD_DRAG_LEFT,
GRAD_DRAG_MIDDLE,
GRAD_DRAG_ALL
} control_drag_mode_t;
struct _GradientEditor
{
GtkWidget *shell;
GtkWidget *name;
GtkWidget *hint_label;
GtkWidget *scrollbar;
GtkWidget *preview;
GtkWidget *control;
GimpContext *context;
/* Zoom and scrollbar */
guint zoom_factor;
GtkObject *scroll_data;
/* Instant update */
gboolean instant_update;
/* Gradient preview */
guchar *preview_rows[2]; /* For caching redraw info */
gint preview_last_x;
gboolean preview_button_down;
/* Gradient control */
GdkPixmap *control_pixmap;
GimpGradientSegment *control_drag_segment; /* Segment which is being dragged */
GimpGradientSegment *control_sel_l; /* Left segment of selection */
GimpGradientSegment *control_sel_r; /* Right segment of selection */
control_drag_mode_t control_drag_mode; /* What is being dragged? */
guint32 control_click_time; /* Time when mouse was pressed */
gboolean control_compress; /* Compressing/expanding handles */
gint control_last_x; /* Last mouse position when dragging */
gdouble control_last_gx; /* Last position (wrt gradient) when dragging */
gdouble control_orig_pos; /* Original click position when dragging */
GtkWidget *control_main_popup; /* Popup menu */
GtkWidget *control_blending_label; /* Blending function label */
GtkWidget *control_coloring_label; /* Coloring type label */
GtkWidget *control_split_m_label; /* Split at midpoint label */
GtkWidget *control_split_u_label; /* Split uniformly label */
GtkWidget *control_delete_menu_item; /* Delete menu item */
GtkWidget *control_delete_label; /* Delete label */
GtkWidget *control_recenter_label; /* Re-center label */
GtkWidget *control_redistribute_label; /* Re-distribute label */
GtkWidget *control_flip_label; /* Flip label */
GtkWidget *control_replicate_label; /* Replicate label */
GtkWidget *control_blend_colors_menu_item; /* Blend colors menu item */
GtkWidget *control_blend_opacity_menu_item; /* Blend opacity menu item */
GtkWidget *control_left_load_popup; /* Left endpoint load menu */
GtkWidget *control_left_save_popup; /* Left endpoint save menu */
GtkWidget *control_right_load_popup; /* Right endpoint load menu */
GtkWidget *control_right_save_popup; /* Right endpoint save menu */
GtkWidget *control_blending_popup; /* Blending function menu */
GtkWidget *control_coloring_popup; /* Coloring type menu */
GtkWidget *control_sel_ops_popup; /* Selection ops menu */
GtkAccelGroup *accel_group;
/* Blending and coloring menus */
GtkWidget *control_blending_items[5 + 1]; /* Add 1 for the "Varies" item */
GtkWidget *control_coloring_items[3 + 1];
/* Split uniformly dialog */
gint split_parts;
/* Replicate dialog */
gint replicate_times;
/* Saved colors */
GimpRGB saved_colors[GRAD_NUM_COLORS];
GtkWidget *left_load_color_boxes[GRAD_NUM_COLORS + 3];
GtkWidget *left_load_labels[GRAD_NUM_COLORS + 3];
GtkWidget *left_save_color_boxes[GRAD_NUM_COLORS];
GtkWidget *left_save_labels[GRAD_NUM_COLORS];
GtkWidget *right_load_color_boxes[GRAD_NUM_COLORS + 3];
GtkWidget *right_load_labels[GRAD_NUM_COLORS + 3];
GtkWidget *right_save_color_boxes[GRAD_NUM_COLORS];
GtkWidget *right_save_labels[GRAD_NUM_COLORS];
/* Color dialogs */
GtkWidget *left_color_preview;
GimpGradientSegment *left_saved_segments;
gboolean left_saved_dirty;
GtkWidget *right_color_preview;
GimpGradientSegment *right_saved_segments;
gboolean right_saved_dirty;
};
/***** Local functions *****/
static void gradient_editor_name_activate (GtkWidget *widget,
GradientEditor *gradient_editor);
static void gradient_editor_name_focus_out (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor);
static void gradient_editor_drop_gradient (GtkWidget *widget,
GimpViewable *viewable,
gpointer data);
static void gradient_editor_gradient_changed (GimpContext *context,
GimpGradient *gradient,
GradientEditor *gradient_editor);
/* Gradient editor functions */
static void ed_update_editor (GradientEditor *gradient_editor,
gint flags);
static void ed_set_hint (GradientEditor *gradient_editor,
gchar *str);
static void ed_initialize_saved_colors (GradientEditor *gradient_editor);
/* Main dialog button callbacks & functions */
static void ed_save_pov_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void ed_do_save_pov_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void ed_cancel_save_pov_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static gint ed_delete_save_pov_callback (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor);
static void ed_close_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
/* Zoom, scrollbar & instant update callbacks */
static void ed_scrollbar_update (GtkAdjustment *adjustment,
GradientEditor *gradient_editor);
static void ed_zoom_all_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void ed_zoom_out_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void ed_zoom_in_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void ed_instant_update_update (GtkWidget *widget,
GradientEditor *gradient_editor);
/* Gradient preview functions */
static gint preview_events (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor);
static void preview_set_hint (GradientEditor *gradient_editor,
gint x);
static void preview_set_foreground (GradientEditor *gradient_editor,
gint x);
static void preview_set_background (GradientEditor *gradient_editor,
gint x);
static void preview_update (GradientEditor *gradient_editor,
gboolean recalculate);
static void preview_fill_image (GradientEditor *gradient_editor,
gint width,
gint height,
gdouble left,
gdouble right);
/* Gradient control functions */
static gint control_events (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor);
static void control_do_hint (GradientEditor *gradient_editor,
gint x,
gint y);
static void control_button_press (GradientEditor *gradient_editor,
gint x,
gint y,
guint button,
guint state);
static gboolean control_point_in_handle (GradientEditor *gradient_editor,
GimpGradient *gradient,
gint x,
gint y,
GimpGradientSegment *seg,
control_drag_mode_t handle);
static void control_select_single_segment (GradientEditor *gradient_editor,
GimpGradientSegment *seg);
static void control_extend_selection (GradientEditor *gradient_editor,
GimpGradientSegment *seg,
gdouble pos);
static void control_motion (GradientEditor *gradient_editor,
GimpGradient *gradient,
gint x);
static void control_compress_left (GimpGradientSegment *range_l,
GimpGradientSegment *range_r,
GimpGradientSegment *drag_seg,
gdouble pos);
static void control_compress_range (GimpGradientSegment *range_l,
GimpGradientSegment *range_r,
gdouble new_l,
gdouble new_r);
static double control_move (GradientEditor *gradient_editor,
GimpGradientSegment *range_l,
GimpGradientSegment *range_r,
gdouble delta);
/* Control update/redraw functions */
static void control_update (GradientEditor *gradient_editor,
GimpGradient *gradient,
gboolean recalculate);
static void control_draw (GradientEditor *gradient_editor,
GimpGradient *gradient,
GdkPixmap *pixmap,
gint width,
gint height,
gdouble left,
gdouble right);
static void control_draw_normal_handle (GradientEditor *gradient_editor,
GdkPixmap *pixmap,
gdouble pos,
gint height);
static void control_draw_middle_handle (GradientEditor *gradient_editor,
GdkPixmap *pixmap,
gdouble pos,
gint height);
static void control_draw_handle (GdkPixmap *pixmap,
GdkGC *border_gc,
GdkGC *fill_gc,
gint xpos,
gint height);
static gint control_calc_p_pos (GradientEditor *gradient_editor,
gdouble pos);
static gdouble control_calc_g_pos (GradientEditor *gradient_editor,
gint pos);
/* Control popup functions */
static void cpopup_create_main_menu (GradientEditor *gradient_editor);
static void cpopup_do_popup (GradientEditor *gradient_editor);
static GtkWidget * cpopup_create_color_item (GtkWidget **color_box,
GtkWidget **label);
static GtkWidget * cpopup_create_menu_item_with_label (gchar *str,
GtkWidget **label);
static void cpopup_adjust_menus (GradientEditor *gradient_editor);
static void cpopup_adjust_blending_menu (GradientEditor *gradient_editor);
static void cpopup_adjust_coloring_menu (GradientEditor *gradient_editor);
static void cpopup_check_selection_params (GradientEditor *gradient_editor,
gint *equal_blending,
gint *equal_coloring);
static void cpopup_render_color_box (GtkPreview *preview,
GimpRGB *color);
static GtkWidget * cpopup_create_load_menu (GradientEditor *gradient_editor,
GtkWidget **color_boxes,
GtkWidget **labels,
gchar *label1,
gchar *label2,
GtkSignalFunc callback,
gchar accel_key_0,
guint8 accel_mods_0,
gchar accel_key_1,
guint8 accel_mods_1,
gchar accel_key_2,
guint8 accel_mods_2);
static GtkWidget * cpopup_create_save_menu (GradientEditor *gradient_editor,
GtkWidget **color_boxes,
GtkWidget **labels,
GtkSignalFunc callback);
static void cpopup_update_saved_color (GradientEditor *gradient_editor,
gint n,
GimpRGB *color);
static void cpopup_load_left_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_save_left_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_load_right_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_save_right_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static GimpGradientSegment * cpopup_save_selection(GradientEditor *gradient_editor);
static void cpopup_replace_selection (GradientEditor *gradient_editor,
GimpGradientSegment *replace_seg);
/* ----- */
static void cpopup_set_left_color_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_set_right_color_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_left_color_changed (ColorNotebook *cnb,
const GimpRGB *color,
ColorNotebookState state,
gpointer data);
static void cpopup_right_color_changed (ColorNotebook *cnb,
const GimpRGB *color,
ColorNotebookState state,
gpointer data);
/* ----- */
static GtkWidget * cpopup_create_blending_menu (GradientEditor *gradient_editor);
static void cpopup_blending_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static GtkWidget * cpopup_create_coloring_menu (GradientEditor *gradient_editor);
static void cpopup_coloring_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
/* ----- */
static void cpopup_split_midpoint_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_split_midpoint (GimpGradient *gradient,
GimpGradientSegment *lseg,
GimpGradientSegment **newl,
GimpGradientSegment **newr);
static void cpopup_split_uniform_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_split_uniform_scale_update (GtkAdjustment *adjustment,
GradientEditor *gradient_editor);
static void cpopup_split_uniform_split_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_split_uniform_cancel_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_split_uniform (GimpGradient *gradient,
GimpGradientSegment *lseg,
gint parts,
GimpGradientSegment **newl,
GimpGradientSegment **newr);
static void cpopup_delete_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_recenter_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_redistribute_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
/* Control popup -> Selection operations functions */
static GtkWidget * cpopup_create_sel_ops_menu (GradientEditor *gradient_editor);
static void cpopup_flip_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_replicate_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_replicate_scale_update (GtkAdjustment *widget,
GradientEditor *gradient_editor);
static void cpopup_do_replicate_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_replicate_cancel_callback (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_blend_colors (GtkWidget *widget,
GradientEditor *gradient_editor);
static void cpopup_blend_opacity (GtkWidget *widget,
GradientEditor *gradient_editor);
/* Blend function */
static void cpopup_blend_endpoints (GradientEditor *gradient_editor,
GimpRGB *left,
GimpRGB *right,
gint blend_colors,
gint blend_opacity);
/* Segment functions */
static void seg_get_closest_handle (GimpGradient *grad,
gdouble pos,
GimpGradientSegment **seg,
control_drag_mode_t *handle);
/***** Public gradient editor functions *****/
GradientEditor *
gradient_editor_new (void)
{
GradientEditor *gradient_editor;
GtkWidget *main_vbox;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *vbox2;
GtkWidget *button;
GtkWidget *frame;
gint i;
gradient_editor = g_new (GradientEditor, 1);
gradient_editor->context = gimp_context_new (the_gimp, NULL, NULL);
gtk_signal_connect (GTK_OBJECT (gradient_editor->context), "gradient_changed",
GTK_SIGNAL_FUNC (gradient_editor_gradient_changed),
gradient_editor);
/* Shell and main vbox */
gradient_editor->shell =
gimp_dialog_new (_("Gradient Editor"), "gradient_editor",
gimp_standard_help_func,
"dialogs/gradient_editor/gradient_editor.html",
GTK_WIN_POS_NONE,
FALSE, TRUE, FALSE,
"_delete_event_", ed_close_callback,
gradient_editor, NULL, NULL, FALSE, TRUE,
NULL);
main_vbox = gtk_vbox_new (FALSE, 4);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 4);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (gradient_editor->shell)->vbox),
main_vbox);
gtk_widget_show (main_vbox);
vbox = gtk_vbox_new (FALSE, 1);
gtk_box_pack_start (GTK_BOX (main_vbox), vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
/* Gradient's name */
gradient_editor->name = gtk_entry_new ();
gtk_box_pack_start (GTK_BOX (vbox), gradient_editor->name, FALSE, FALSE, 0);
gtk_widget_show (gradient_editor->name);
gtk_signal_connect (GTK_OBJECT (gradient_editor->name), "activate",
GTK_SIGNAL_FUNC (gradient_editor_name_activate),
gradient_editor);
gtk_signal_connect (GTK_OBJECT (gradient_editor->name), "focus_out_event",
GTK_SIGNAL_FUNC (gradient_editor_name_focus_out),
gradient_editor);
/* Frame for gradient preview 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 (vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox2 = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (frame), vbox2);
gtk_widget_show (vbox2);
/* Gradient preview */
gradient_editor->preview_rows[0] = NULL;
gradient_editor->preview_rows[1] = NULL;
gradient_editor->preview_last_x = 0;
gradient_editor->preview_button_down = FALSE;
gradient_editor->preview = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_preview_set_dither (GTK_PREVIEW (gradient_editor->preview), GDK_RGB_DITHER_MAX);
gtk_preview_size (GTK_PREVIEW (gradient_editor->preview),
GRAD_PREVIEW_WIDTH, GRAD_PREVIEW_HEIGHT);
/* Enable auto-resizing of the preview but ensure a minimal size */
gtk_widget_set_usize (gradient_editor->preview,
GRAD_PREVIEW_WIDTH, GRAD_PREVIEW_HEIGHT);
gtk_preview_set_expand (GTK_PREVIEW (gradient_editor->preview), TRUE);
gtk_widget_set_events (gradient_editor->preview, GRAD_PREVIEW_EVENT_MASK);
gtk_signal_connect (GTK_OBJECT (gradient_editor->preview), "event",
GTK_SIGNAL_FUNC (preview_events),
gradient_editor);
gimp_gtk_drag_dest_set_by_type (gradient_editor->preview,
GTK_DEST_DEFAULT_ALL,
GIMP_TYPE_GRADIENT,
GDK_ACTION_COPY);
gimp_dnd_viewable_dest_set (GTK_WIDGET (gradient_editor->preview),
GIMP_TYPE_GRADIENT,
gradient_editor_drop_gradient,
gradient_editor);
gtk_box_pack_start (GTK_BOX (vbox2), gradient_editor->preview, TRUE, TRUE, 0);
gtk_widget_show (gradient_editor->preview);
/* Gradient control */
gradient_editor->control_pixmap = NULL;
gradient_editor->control_drag_segment = NULL;
gradient_editor->control_sel_l = NULL;
gradient_editor->control_sel_r = NULL;
gradient_editor->control_drag_mode = GRAD_DRAG_NONE;
gradient_editor->control_click_time = 0;
gradient_editor->control_compress = FALSE;
gradient_editor->control_last_x = 0;
gradient_editor->control_last_gx = 0.0;
gradient_editor->control_orig_pos = 0.0;
gradient_editor->control_main_popup = NULL;
gradient_editor->control_blending_label = NULL;
gradient_editor->control_coloring_label = NULL;
gradient_editor->control_split_m_label = NULL;
gradient_editor->control_split_u_label = NULL;
gradient_editor->control_delete_menu_item = NULL;
gradient_editor->control_delete_label = NULL;
gradient_editor->control_recenter_label = NULL;
gradient_editor->control_redistribute_label = NULL;
gradient_editor->control_flip_label = NULL;
gradient_editor->control_replicate_label = NULL;
gradient_editor->control_blend_colors_menu_item = NULL;
gradient_editor->control_blend_opacity_menu_item = NULL;
gradient_editor->control_left_load_popup = NULL;
gradient_editor->control_left_save_popup = NULL;
gradient_editor->control_right_load_popup = NULL;
gradient_editor->control_right_save_popup = NULL;
gradient_editor->control_blending_popup = NULL;
gradient_editor->control_coloring_popup = NULL;
gradient_editor->control_sel_ops_popup = NULL;
gradient_editor->accel_group = NULL;
for (i = 0;
i < (sizeof (gradient_editor->control_blending_items) /
sizeof (gradient_editor->control_blending_items[0]));
i++)
gradient_editor->control_blending_items[i] = NULL;
for (i = 0;
i < (sizeof (gradient_editor->control_coloring_items) /
sizeof (gradient_editor->control_coloring_items[0]));
i++)
gradient_editor->control_coloring_items[i] = NULL;
gradient_editor->control = gtk_drawing_area_new ();
gtk_drawing_area_size (GTK_DRAWING_AREA (gradient_editor->control),
GRAD_PREVIEW_WIDTH, GRAD_CONTROL_HEIGHT);
gtk_widget_set_events (gradient_editor->control, GRAD_CONTROL_EVENT_MASK);
gtk_signal_connect (GTK_OBJECT (gradient_editor->control), "event",
GTK_SIGNAL_FUNC (control_events),
gradient_editor);
gtk_box_pack_start (GTK_BOX (vbox2), gradient_editor->control,
FALSE, FALSE, 0);
gtk_widget_show (gradient_editor->control);
/* Scrollbar */
gradient_editor->zoom_factor = 1;
gradient_editor->scroll_data =
gtk_adjustment_new (0.0, 0.0, 1.0,
1.0 * GRAD_SCROLLBAR_STEP_SIZE,
1.0 * GRAD_SCROLLBAR_PAGE_SIZE,
1.0);
gtk_signal_connect (gradient_editor->scroll_data, "value_changed",
GTK_SIGNAL_FUNC (ed_scrollbar_update),
gradient_editor);
gtk_signal_connect (gradient_editor->scroll_data, "changed",
GTK_SIGNAL_FUNC (ed_scrollbar_update),
gradient_editor);
gradient_editor->scrollbar =
gtk_hscrollbar_new (GTK_ADJUSTMENT (gradient_editor->scroll_data));
gtk_range_set_update_policy (GTK_RANGE (gradient_editor->scrollbar),
GTK_UPDATE_CONTINUOUS);
gtk_box_pack_start (GTK_BOX (vbox), gradient_editor->scrollbar,
FALSE, FALSE, 0);
gtk_widget_show (gradient_editor->scrollbar);
/* Horizontal box for name, zoom controls and instant update toggle */
hbox = gtk_hbox_new (FALSE, 4);
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* Save as POV-Ray button */
button = gtk_button_new_with_label (_("Save as POV-Ray"));
gimp_help_set_help_data (button, NULL,
"dialogs/gradient_editor/save_as_povray.html");
gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (ed_save_pov_callback),
gradient_editor);
/* + and - buttons */
gtk_widget_realize (gradient_editor->shell);
button = gimp_pixmap_button_new (zoom_out_xpm, NULL);
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (ed_zoom_out_callback),
gradient_editor);
button = gimp_pixmap_button_new (zoom_in_xpm, NULL);
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (ed_zoom_in_callback),
gradient_editor);
/* Zoom all button */
button = gtk_button_new_with_label (_("Zoom all"));
gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (ed_zoom_all_callback),
gradient_editor);
/* Instant update toggle */
gradient_editor->instant_update = TRUE;
button = gtk_check_button_new_with_label (_("Instant update"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
gtk_signal_connect (GTK_OBJECT (button), "toggled",
GTK_SIGNAL_FUNC (ed_instant_update_update),
gradient_editor);
/* Hint bar */
hbox = GTK_DIALOG (gradient_editor->shell)->action_area;
gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
gradient_editor->hint_label = gtk_label_new ("");
gtk_misc_set_alignment (GTK_MISC (gradient_editor->hint_label), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), gradient_editor->hint_label,
FALSE, FALSE, 0);
gtk_widget_show (gradient_editor->hint_label);
/* Initialize other data */
gradient_editor->left_color_preview = NULL;
gradient_editor->left_saved_segments = NULL;
gradient_editor->left_saved_dirty = FALSE;
gradient_editor->right_color_preview = NULL;
gradient_editor->right_saved_segments = NULL;
gradient_editor->right_saved_dirty = FALSE;
ed_initialize_saved_colors (gradient_editor);
cpopup_create_main_menu (gradient_editor);
if (gimp_container_num_children (the_gimp->gradient_factory->container))
{
gimp_context_set_gradient (gradient_editor->context,
GIMP_GRADIENT (gimp_container_get_child_by_index (the_gimp->gradient_factory->container, 0)));
}
else
{
GimpGradient *gradient;
gradient = GIMP_GRADIENT (gimp_gradient_new (_("Default")));
gimp_container_add (the_gimp->gradient_factory->container,
GIMP_OBJECT (gradient));
}
gtk_widget_show (gradient_editor->shell);
return gradient_editor;
}
void
gradient_editor_set_gradient (GradientEditor *gradient_editor,
GimpGradient *gradient)
{
g_return_if_fail (gradient_editor != NULL);
if (gimp_container_have (gradient_editor->context->gimp->gradient_factory->container,
GIMP_OBJECT (gradient)))
{
gimp_context_set_gradient (gradient_editor->context, gradient);
}
if (! GTK_WIDGET_VISIBLE (gradient_editor->shell))
gtk_widget_show (gradient_editor->shell);
else
gdk_window_raise (gradient_editor->shell->window);
}
void
gradient_editor_free (GradientEditor *gradient_editor)
{
g_return_if_fail (gradient_editor != NULL);
}
/* private functions */
static void
gradient_editor_name_activate (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
gchar *entry_text;
gradient = gimp_context_get_gradient (gradient_editor->context);
entry_text = gtk_entry_get_text (GTK_ENTRY (widget));
gimp_object_set_name (GIMP_OBJECT (gradient), entry_text);
}
static void
gradient_editor_name_focus_out (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor)
{
gradient_editor_name_activate (widget, gradient_editor);
}
static void
gradient_editor_drop_gradient (GtkWidget *widget,
GimpViewable *viewable,
gpointer data)
{
GradientEditor *gradient_editor;
gradient_editor = (GradientEditor *) data;
gradient_editor_set_gradient (gradient_editor,
GIMP_GRADIENT (viewable));
}
static void
gradient_editor_gradient_changed (GimpContext *context,
GimpGradient *gradient,
GradientEditor *gradient_editor)
{
gtk_entry_set_text (GTK_ENTRY (gradient_editor->name),
gimp_object_get_name (GIMP_OBJECT (gradient)));
ed_update_editor (gradient_editor, GRAD_UPDATE_PREVIEW | GRAD_RESET_CONTROL);
}
/*****/
static void
ed_update_editor (GradientEditor *gradient_editor,
gint flags)
{
GimpGradient *gradient;
gradient = gimp_context_get_gradient (gradient_editor->context);
if (flags & GRAD_UPDATE_GRADIENT)
{
preview_update (gradient_editor, TRUE);
}
if (flags & GRAD_UPDATE_PREVIEW)
preview_update (gradient_editor, TRUE);
if (flags & GRAD_UPDATE_CONTROL)
control_update (gradient_editor, gradient, FALSE);
if (flags & GRAD_RESET_CONTROL)
control_update (gradient_editor, gradient, TRUE);
}
static void
ed_set_hint (GradientEditor *gradient_editor,
gchar *str)
{
gtk_label_set_text (GTK_LABEL (gradient_editor->hint_label), str);
}
static void
ed_initialize_saved_colors (GradientEditor *gradient_editor)
{
gint i;
for (i = 0; i < (GRAD_NUM_COLORS + 3); i++)
{
gradient_editor->left_load_color_boxes[i] = NULL;
gradient_editor->left_load_labels[i] = NULL;
gradient_editor->right_load_color_boxes[i] = NULL;
gradient_editor->right_load_labels[i] = NULL;
}
for (i = 0; i < GRAD_NUM_COLORS; i++)
{
gradient_editor->left_save_color_boxes[i] = NULL;
gradient_editor->left_save_labels[i] = NULL;
gradient_editor->right_save_color_boxes[i] = NULL;
gradient_editor->right_save_labels[i] = NULL;
}
gradient_editor->saved_colors[0].r = 0.0; /* Black */
gradient_editor->saved_colors[0].g = 0.0;
gradient_editor->saved_colors[0].b = 0.0;
gradient_editor->saved_colors[0].a = 1.0;
gradient_editor->saved_colors[1].r = 0.5; /* 50% Gray */
gradient_editor->saved_colors[1].g = 0.5;
gradient_editor->saved_colors[1].b = 0.5;
gradient_editor->saved_colors[1].a = 1.0;
gradient_editor->saved_colors[2].r = 1.0; /* White */
gradient_editor->saved_colors[2].g = 1.0;
gradient_editor->saved_colors[2].b = 1.0;
gradient_editor->saved_colors[2].a = 1.0;
gradient_editor->saved_colors[3].r = 0.0; /* Clear */
gradient_editor->saved_colors[3].g = 0.0;
gradient_editor->saved_colors[3].b = 0.0;
gradient_editor->saved_colors[3].a = 0.0;
gradient_editor->saved_colors[4].r = 1.0; /* Red */
gradient_editor->saved_colors[4].g = 0.0;
gradient_editor->saved_colors[4].b = 0.0;
gradient_editor->saved_colors[4].a = 1.0;
gradient_editor->saved_colors[5].r = 1.0; /* Yellow */
gradient_editor->saved_colors[5].g = 1.0;
gradient_editor->saved_colors[5].b = 0.0;
gradient_editor->saved_colors[5].a = 1.0;
gradient_editor->saved_colors[6].r = 0.0; /* Green */
gradient_editor->saved_colors[6].g = 1.0;
gradient_editor->saved_colors[6].b = 0.0;
gradient_editor->saved_colors[6].a = 1.0;
gradient_editor->saved_colors[7].r = 0.0; /* Cyan */
gradient_editor->saved_colors[7].g = 1.0;
gradient_editor->saved_colors[7].b = 1.0;
gradient_editor->saved_colors[7].a = 1.0;
gradient_editor->saved_colors[8].r = 0.0; /* Blue */
gradient_editor->saved_colors[8].g = 0.0;
gradient_editor->saved_colors[8].b = 1.0;
gradient_editor->saved_colors[8].a = 1.0;
gradient_editor->saved_colors[9].r = 1.0; /* Magenta */
gradient_editor->saved_colors[9].g = 0.0;
gradient_editor->saved_colors[9].b = 1.0;
gradient_editor->saved_colors[9].a = 1.0;
}
/***** Main gradient editor dialog callbacks *****/
/***** The "save as pov" dialog functions *****/
static void
ed_save_pov_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GtkWidget *window;
gradient = gimp_context_get_gradient (gradient_editor->context);
if (! gradient)
return;
window = gtk_file_selection_new (_("Save as POV-Ray"));
gtk_window_set_wmclass (GTK_WINDOW (window), "save_gradient", "Gimp");
gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE);
gtk_container_set_border_width (GTK_CONTAINER (window), 2);
gtk_container_set_border_width (GTK_CONTAINER (GTK_FILE_SELECTION (window)->button_area), 2);
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button),
"clicked",
GTK_SIGNAL_FUNC (ed_do_save_pov_callback),
gradient_editor);
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button),
"clicked",
GTK_SIGNAL_FUNC (ed_cancel_save_pov_callback),
gradient_editor);
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
GTK_SIGNAL_FUNC (ed_delete_save_pov_callback),
gradient_editor);
/* Connect the "F1" help key */
gimp_help_connect_help_accel (window, gimp_standard_help_func,
"dialogs/gradient_editor/save_as_povray.html");
gtk_widget_show (window);
gtk_widget_set_sensitive (gradient_editor->shell, FALSE);
}
static void
ed_do_save_pov_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
gchar *filename;
FILE *file;
GimpGradientSegment *seg;
gradient = gimp_context_get_gradient (gradient_editor->context);
if (! gradient)
return;
filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (gtk_widget_get_toplevel (widget)));
file = fopen (filename, "wb");
if (!file)
{
g_message ("Could not open \"%s\"", filename);
}
else
{
fprintf (file, "/* color_map file created by the GIMP */\n");
fprintf (file, "/* http://www.gimp.org/ */\n");
fprintf (file, "color_map {\n");
for (seg = gradient->segments; seg; seg = seg->next)
{
/* Left */
fprintf (file, "\t[%f color rgbt <%f, %f, %f, %f>]\n",
seg->left,
seg->left_color.r,
seg->left_color.g,
seg->left_color.b,
1.0 - seg->left_color.a);
/* Middle */
fprintf (file, "\t[%f color rgbt <%f, %f, %f, %f>]\n",
seg->middle,
(seg->left_color.r + seg->right_color.r) / 2.0,
(seg->left_color.g + seg->right_color.g) / 2.0,
(seg->left_color.b + seg->right_color.b) / 2.0,
1.0 - (seg->left_color.a + seg->right_color.a) / 2.0);
/* Right */
fprintf (file, "\t[%f color rgbt <%f, %f, %f, %f>]\n",
seg->right,
seg->right_color.r,
seg->right_color.g,
seg->right_color.b,
1.0 - seg->right_color.a);
}
fprintf (file, "} /* color_map */\n");
fclose (file);
}
gtk_widget_destroy (gtk_widget_get_toplevel (widget));
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
}
static void
ed_cancel_save_pov_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
gtk_widget_destroy (gtk_widget_get_toplevel (widget));
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
}
static gint
ed_delete_save_pov_callback (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor)
{
ed_cancel_save_pov_callback (widget, gradient_editor);
return TRUE;
}
/***** The main dialog action area button callbacks *****/
static void
ed_close_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
if (GTK_WIDGET_VISIBLE (gradient_editor->shell))
gtk_widget_hide (gradient_editor->shell);
}
/***** Zoom, scrollbar & instant update callbacks *****/
static void
ed_scrollbar_update (GtkAdjustment *adjustment,
GradientEditor *gradient_editor)
{
gchar *str;
str = g_strdup_printf (_("Zoom factor: %d:1 Displaying [%0.6f, %0.6f]"),
gradient_editor->zoom_factor, adjustment->value,
adjustment->value + adjustment->page_size);
ed_set_hint (gradient_editor, str);
g_free (str);
ed_update_editor (gradient_editor, GRAD_UPDATE_PREVIEW | GRAD_UPDATE_CONTROL);
}
static void
ed_zoom_all_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GtkAdjustment *adjustment;
adjustment = GTK_ADJUSTMENT (gradient_editor->scroll_data);
gradient_editor->zoom_factor = 1;
adjustment->value = 0.0;
adjustment->page_size = 1.0;
adjustment->step_increment = 1.0 * GRAD_SCROLLBAR_STEP_SIZE;
adjustment->page_increment = 1.0 * GRAD_SCROLLBAR_PAGE_SIZE;
gtk_adjustment_changed (GTK_ADJUSTMENT (gradient_editor->scroll_data));
}
static void
ed_zoom_out_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GtkAdjustment *adjustment;
gdouble old_value;
gdouble value;
gdouble old_page_size;
gdouble page_size;
if (gradient_editor->zoom_factor <= 1)
return;
adjustment = GTK_ADJUSTMENT (gradient_editor->scroll_data);
old_value = adjustment->value;
old_page_size = adjustment->page_size;
gradient_editor->zoom_factor--;
page_size = 1.0 / gradient_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;
adjustment->value = value;
adjustment->page_size = page_size;
adjustment->step_increment = page_size * GRAD_SCROLLBAR_STEP_SIZE;
adjustment->page_increment = page_size * GRAD_SCROLLBAR_PAGE_SIZE;
gtk_adjustment_changed (GTK_ADJUSTMENT (gradient_editor->scroll_data));
}
static void
ed_zoom_in_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GtkAdjustment *adjustment;
gdouble old_value;
gdouble old_page_size;
gdouble page_size;
adjustment = GTK_ADJUSTMENT (gradient_editor->scroll_data);
old_value = adjustment->value;
old_page_size = adjustment->page_size;
gradient_editor->zoom_factor++;
page_size = 1.0 / gradient_editor->zoom_factor;
adjustment->value = old_value + (old_page_size - page_size) / 2.0;
adjustment->page_size = page_size;
adjustment->step_increment = page_size * GRAD_SCROLLBAR_STEP_SIZE;
adjustment->page_increment = page_size * GRAD_SCROLLBAR_PAGE_SIZE;
gtk_adjustment_changed (GTK_ADJUSTMENT (gradient_editor->scroll_data));
}
static void
ed_instant_update_update (GtkWidget *widget,
GradientEditor *gradient_editor)
{
if (GTK_TOGGLE_BUTTON (widget)->active)
{
gradient_editor->instant_update = TRUE;
gtk_range_set_update_policy (GTK_RANGE (gradient_editor->scrollbar),
GTK_UPDATE_CONTINUOUS);
}
else
{
gradient_editor->instant_update = FALSE;
gtk_range_set_update_policy (GTK_RANGE (gradient_editor->scrollbar),
GTK_UPDATE_DELAYED);
}
}
/***** Gradient preview functions *****/
static gint
preview_events (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor)
{
gint x, y;
GdkEventButton *bevent;
GdkEventMotion *mevent;
switch (event->type)
{
case GDK_EXPOSE:
preview_update (gradient_editor, FALSE);
break;
case GDK_LEAVE_NOTIFY:
ed_set_hint (gradient_editor, "");
break;
case GDK_MOTION_NOTIFY:
gtk_widget_get_pointer (gradient_editor->preview, &x, &y);
mevent = (GdkEventMotion *) event;
if (x != gradient_editor->preview_last_x)
{
gradient_editor->preview_last_x = x;
if (gradient_editor->preview_button_down)
{
if (mevent->state & GDK_CONTROL_MASK)
preview_set_background (gradient_editor, x);
else
preview_set_foreground (gradient_editor, x);
}
else
{
preview_set_hint (gradient_editor, x);
}
}
break;
case GDK_BUTTON_PRESS:
gtk_widget_get_pointer (gradient_editor->preview, &x, &y);
bevent = (GdkEventButton *) event;
switch (bevent->button)
{
case 1:
gradient_editor->preview_last_x = x;
gradient_editor->preview_button_down = TRUE;
if (bevent->state & GDK_CONTROL_MASK)
preview_set_background (gradient_editor, x);
else
preview_set_foreground (gradient_editor, x);
break;
case 3:
cpopup_do_popup (gradient_editor);
break;
/* wheelmouse support */
case 4:
{
GtkAdjustment *adj = GTK_ADJUSTMENT (gradient_editor->scroll_data);
gfloat new_value = adj->value - adj->page_increment / 2;
new_value =
CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
gtk_adjustment_set_value (adj, new_value);
}
break;
case 5:
{
GtkAdjustment *adj = GTK_ADJUSTMENT (gradient_editor->scroll_data);
gfloat new_value = adj->value + adj->page_increment / 2;
new_value =
CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
gtk_adjustment_set_value (adj, new_value);
}
break;
default:
break;
}
break;
case GDK_BUTTON_RELEASE:
if (gradient_editor->preview_button_down)
{
gtk_widget_get_pointer (gradient_editor->preview, &x, &y);
bevent = (GdkEventButton *) event;
gradient_editor->preview_last_x = x;
gradient_editor->preview_button_down = FALSE;
if (bevent->state & GDK_CONTROL_MASK)
preview_set_background (gradient_editor, x);
else
preview_set_foreground (gradient_editor, x);
break;
}
break;
default:
break;
}
return FALSE;
}
static void
preview_set_hint (GradientEditor *gradient_editor,
gint x)
{
gdouble xpos;
GimpRGB rgb;
GimpHSV hsv;
gchar *str;
xpos = control_calc_g_pos (gradient_editor, x);
gimp_gradient_get_color_at
(gimp_context_get_gradient (gradient_editor->context),
xpos, &rgb);
gimp_rgb_to_hsv (&rgb, &hsv);
str = g_strdup_printf (_("Position: %0.6f "
"RGB (%0.3f, %0.3f, %0.3f) "
"HSV (%0.3f, %0.3f, %0.3f) "
"Opacity: %0.3f"),
xpos,
rgb.r,
rgb.g,
rgb.b,
hsv.h * 360.0,
hsv.s,
hsv.v,
rgb.a);
ed_set_hint (gradient_editor, str);
g_free (str);
}
static void
preview_set_foreground (GradientEditor *gradient_editor,
gint x)
{
GimpRGB color;
gdouble xpos;
gchar *str;
xpos = control_calc_g_pos (gradient_editor, x);
gimp_gradient_get_color_at
(gimp_context_get_gradient (gradient_editor->context),
xpos, &color);
gimp_context_set_foreground (gimp_context_get_user (), &color);
str = g_strdup_printf (_("Foreground color set to RGB (%d, %d, %d) <-> "
"(%0.3f, %0.3f, %0.3f)"),
(gint) (color.r * 255.0),
(gint) (color.g * 255.0),
(gint) (color.b * 255.0),
color.r, color.g, color.b);
ed_set_hint (gradient_editor, str);
g_free (str);
}
static void
preview_set_background (GradientEditor *gradient_editor,
gint x)
{
GimpRGB color;
gdouble xpos;
gchar *str;
xpos = control_calc_g_pos (gradient_editor, x);
gimp_gradient_get_color_at
(gimp_context_get_gradient (gradient_editor->context),
xpos, &color);
gimp_context_set_background (gimp_context_get_user (), &color);
str = g_strdup_printf (_("Background color to RGB (%d, %d, %d) <-> "
"(%0.3f, %0.3f, %0.3f)"),
(gint) (color.r * 255.0),
(gint) (color.g * 255.0),
(gint) (color.b * 255.0),
color.r, color.g, color.b);
ed_set_hint (gradient_editor, str);
g_free (str);
}
/*****/
static void
preview_update (GradientEditor *gradient_editor,
gboolean recalculate)
{
glong rowsiz;
GtkAdjustment *adjustment;
guint16 width;
guint16 height;
static guint16 last_width = 0;
static guint16 last_height = 0;
if (! GTK_WIDGET_DRAWABLE (gradient_editor->preview))
return;
/* See whether we have to re-create the preview widget
* (note that the preview automatically follows the size of it's container)
*/
width = gradient_editor->preview->allocation.width;
height = gradient_editor->preview->allocation.height;
if (! gradient_editor->preview_rows[0] ||
! gradient_editor->preview_rows[1] ||
(width != last_width) ||
(height != last_height))
{
if (gradient_editor->preview_rows[0])
g_free (gradient_editor->preview_rows[0]);
if (gradient_editor->preview_rows[1])
g_free (gradient_editor->preview_rows[1]);
rowsiz = width * 3 * sizeof (guchar);
gradient_editor->preview_rows[0] = g_malloc (rowsiz);
gradient_editor->preview_rows[1] = g_malloc (rowsiz);
recalculate = TRUE; /* Force recalculation */
}
last_width = width;
last_height = height;
/* Have to redraw? */
if (recalculate)
{
adjustment = GTK_ADJUSTMENT (gradient_editor->scroll_data);
preview_fill_image (gradient_editor,
width, height,
adjustment->value,
adjustment->value + adjustment->page_size);
gtk_widget_draw (gradient_editor->preview, NULL);
}
}
static void
preview_fill_image (GradientEditor *gradient_editor,
gint width,
gint height,
gdouble left,
gdouble right)
{
GimpGradient *gradient;
guchar *p0, *p1;
gint x, y;
gdouble dx, cur_x;
GimpRGB color;
gdouble c0, c1;
gradient = gimp_context_get_gradient (gradient_editor->context);
dx = (right - left) / (width - 1);
cur_x = left;
p0 = gradient_editor->preview_rows[0];
p1 = gradient_editor->preview_rows[1];
/* Create lines to fill the image */
for (x = 0; x < width; x++)
{
gimp_gradient_get_color_at (gradient, cur_x, &color);
if ((x / GIMP_CHECK_SIZE) & 1)
{
c0 = GIMP_CHECK_LIGHT;
c1 = GIMP_CHECK_DARK;
}
else
{
c0 = GIMP_CHECK_DARK;
c1 = GIMP_CHECK_LIGHT;
}
*p0++ = (c0 + (color.r - c0) * color.a) * 255.0;
*p0++ = (c0 + (color.g - c0) * color.a) * 255.0;
*p0++ = (c0 + (color.b - c0) * color.a) * 255.0;
*p1++ = (c1 + (color.r - c1) * color.a) * 255.0;
*p1++ = (c1 + (color.g - c1) * color.a) * 255.0;
*p1++ = (c1 + (color.b - c1) * color.a) * 255.0;
cur_x += dx;
}
/* Fill image */
for (y = 0; y < height; y++)
{
if ((y / GIMP_CHECK_SIZE) & 1)
gtk_preview_draw_row (GTK_PREVIEW (gradient_editor->preview),
gradient_editor->preview_rows[1], 0, y, width);
else
gtk_preview_draw_row (GTK_PREVIEW (gradient_editor->preview),
gradient_editor->preview_rows[0], 0, y, width);
}
}
/***** Gradient control functions *****/
/* *** WARNING *** WARNING *** WARNING ***
*
* All the event-handling code for the gradient control widget is
* extremely hairy. You are not expected to understand it. If you
* find bugs, mail me unless you are very brave and you want to fix
* them yourself ;-)
*/
static gint
control_events (GtkWidget *widget,
GdkEvent *event,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GdkEventButton *bevent;
GimpGradientSegment *seg;
gint x, y;
guint32 time;
gradient = gimp_context_get_gradient (gradient_editor->context);
switch (event->type)
{
case GDK_EXPOSE:
control_update (gradient_editor, gradient, FALSE);
break;
case GDK_LEAVE_NOTIFY:
ed_set_hint (gradient_editor, "");
break;
case GDK_BUTTON_PRESS:
if (gradient_editor->control_drag_mode == GRAD_DRAG_NONE)
{
gtk_widget_get_pointer (gradient_editor->control, &x, &y);
bevent = (GdkEventButton *) event;
gradient_editor->control_last_x = x;
gradient_editor->control_click_time = bevent->time;
control_button_press (gradient_editor,
x, y, bevent->button, bevent->state);
if (gradient_editor->control_drag_mode != GRAD_DRAG_NONE)
gtk_grab_add (widget);
}
break;
case GDK_BUTTON_RELEASE:
ed_set_hint (gradient_editor, "");
if (gradient_editor->control_drag_mode != GRAD_DRAG_NONE)
{
gtk_grab_remove (widget);
gtk_widget_get_pointer (gradient_editor->control, &x, &y);
time = ((GdkEventButton *) event)->time;
if ((time - gradient_editor->control_click_time) >= GRAD_MOVE_TIME)
{
if (! gradient_editor->instant_update)
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor,
GRAD_UPDATE_GRADIENT); /* Possible move */
}
else if ((gradient_editor->control_drag_mode == GRAD_DRAG_MIDDLE) ||
(gradient_editor->control_drag_mode == GRAD_DRAG_ALL))
{
seg = gradient_editor->control_drag_segment;
if ((gradient_editor->control_drag_mode == GRAD_DRAG_ALL) &&
gradient_editor->control_compress)
control_extend_selection (gradient_editor, seg,
control_calc_g_pos (gradient_editor,
x));
else
control_select_single_segment (gradient_editor, seg);
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
}
gradient_editor->control_drag_mode = GRAD_DRAG_NONE;
gradient_editor->control_compress = FALSE;
control_do_hint (gradient_editor, x, y);
}
break;
case GDK_MOTION_NOTIFY:
gtk_widget_get_pointer (gradient_editor->control, &x, &y);
if (x != gradient_editor->control_last_x)
{
gradient_editor->control_last_x = x;
if (gradient_editor->control_drag_mode != GRAD_DRAG_NONE)
{
time = ((GdkEventButton *) event)->time;
if ((time - gradient_editor->control_click_time) >= GRAD_MOVE_TIME)
control_motion (gradient_editor, gradient, x);
}
else
{
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
control_do_hint (gradient_editor, x, y);
}
}
break;
default:
break;
}
return FALSE;
}
static void
control_do_hint (GradientEditor *gradient_editor,
gint x,
gint y)
{
GimpGradient *gradient;
GimpGradientSegment *seg;
control_drag_mode_t handle;
gboolean in_handle;
double pos;
gradient = gimp_context_get_gradient (gradient_editor->context);
pos = control_calc_g_pos (gradient_editor, x);
if ((pos < 0.0) || (pos > 1.0))
return;
seg_get_closest_handle (gradient, pos, &seg, &handle);
in_handle = control_point_in_handle (gradient_editor, gradient,
x, y, seg, handle);
if (in_handle)
{
switch (handle)
{
case GRAD_DRAG_LEFT:
if (seg != NULL)
{
if (seg->prev != NULL)
ed_set_hint (gradient_editor,
_("Drag: move Shift+drag: move & compress"));
else
ed_set_hint (gradient_editor,
_("Click: select Shift+click: extend selection"));
}
else
{
ed_set_hint (gradient_editor,
_("Click: select Shift+click: extend selection"));
}
break;
case GRAD_DRAG_MIDDLE:
ed_set_hint (gradient_editor,
_("Click: select Shift+click: extend selection "
"Drag: move"));
break;
default:
g_warning ("in_handle is true yet we got handle type %d",
(int) handle);
break;
}
}
else
ed_set_hint (gradient_editor,
_("Click: select Shift+click: extend selection "
"Drag: move Shift+drag: move & compress"));
}
static void
control_button_press (GradientEditor *gradient_editor,
gint x,
gint y,
guint button,
guint state)
{
GimpGradient *gradient;
GimpGradientSegment *seg;
control_drag_mode_t handle;
double xpos;
gboolean in_handle;
gradient = gimp_context_get_gradient (gradient_editor->context);
switch (button)
{
case 1:
break;
case 3:
cpopup_do_popup (gradient_editor);
return;
/* wheelmouse support */
case 4:
{
GtkAdjustment *adj = GTK_ADJUSTMENT (gradient_editor->scroll_data);
gfloat new_value = adj->value - adj->page_increment / 2;
new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
gtk_adjustment_set_value (adj, new_value);
}
return;
case 5:
{
GtkAdjustment *adj = GTK_ADJUSTMENT (gradient_editor->scroll_data);
gfloat new_value = adj->value + adj->page_increment / 2;
new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
gtk_adjustment_set_value (adj, new_value);
}
return;
default:
return;
}
/* Find the closest handle */
xpos = control_calc_g_pos (gradient_editor, x);
seg_get_closest_handle (gradient, xpos, &seg, &handle);
in_handle = control_point_in_handle (gradient_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)
{
gradient_editor->control_drag_mode = GRAD_DRAG_LEFT;
gradient_editor->control_drag_segment = seg;
gradient_editor->control_compress = TRUE;
}
else
{
control_extend_selection (gradient_editor, seg, xpos);
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
}
}
else if (seg->prev != NULL)
{
gradient_editor->control_drag_mode = GRAD_DRAG_LEFT;
gradient_editor->control_drag_segment = seg;
}
else
{
control_select_single_segment (gradient_editor, seg);
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
}
return;
}
else /* seg == NULL */
{
/* Right handle of last segment */
seg = gimp_gradient_segment_get_last (gradient->segments);
if (state & GDK_SHIFT_MASK)
{
control_extend_selection (gradient_editor, seg, xpos);
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
}
else
{
control_select_single_segment (gradient_editor, seg);
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
}
return;
}
break;
case GRAD_DRAG_MIDDLE:
if (state & GDK_SHIFT_MASK)
{
control_extend_selection (gradient_editor, seg, xpos);
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
}
else
{
gradient_editor->control_drag_mode = GRAD_DRAG_MIDDLE;
gradient_editor->control_drag_segment = seg;
}
return;
default:
g_warning ("in_handle is true yet we got handle type %d",
(int) handle);
return;
}
}
else /* !in_handle */
{
seg = gimp_gradient_get_segment_at (gradient, xpos);
gradient_editor->control_drag_mode = GRAD_DRAG_ALL;
gradient_editor->control_drag_segment = seg;
gradient_editor->control_last_gx = xpos;
gradient_editor->control_orig_pos = xpos;
if (state & GDK_SHIFT_MASK)
gradient_editor->control_compress = TRUE;
return;
}
}
static gboolean
control_point_in_handle (GradientEditor *gradient_editor,
GimpGradient *gradient,
gint x,
gint y,
GimpGradientSegment *seg,
control_drag_mode_t handle)
{
gint handle_pos;
switch (handle)
{
case GRAD_DRAG_LEFT:
if (seg)
{
handle_pos = control_calc_p_pos (gradient_editor, seg->left);
}
else
{
seg = gimp_gradient_segment_get_last (gradient->segments);
handle_pos = control_calc_p_pos (gradient_editor, seg->right);
}
break;
case GRAD_DRAG_MIDDLE:
handle_pos = control_calc_p_pos (gradient_editor, seg->middle);
break;
default:
g_warning ("can not handle drag mode %d", (gint) 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 (GradientEditor *gradient_editor,
GimpGradientSegment *seg)
{
gradient_editor->control_sel_l = seg;
gradient_editor->control_sel_r = seg;
}
static void
control_extend_selection (GradientEditor *gradient_editor,
GimpGradientSegment *seg,
gdouble pos)
{
if (fabs (pos - gradient_editor->control_sel_l->left) <
fabs (pos - gradient_editor->control_sel_r->right))
gradient_editor->control_sel_l = seg;
else
gradient_editor->control_sel_r = seg;
}
/*****/
static void
control_motion (GradientEditor *gradient_editor,
GimpGradient *gradient,
gint x)
{
GimpGradientSegment *seg;
gdouble pos;
gdouble delta;
gchar *str = NULL;
seg = gradient_editor->control_drag_segment;
switch (gradient_editor->control_drag_mode)
{
case GRAD_DRAG_LEFT:
pos = control_calc_g_pos (gradient_editor, x);
if (! gradient_editor->control_compress)
seg->prev->right = seg->left = CLAMP (pos,
seg->prev->middle + EPSILON,
seg->middle - EPSILON);
else
control_compress_left (gradient_editor->control_sel_l,
gradient_editor->control_sel_r,
seg, pos);
str = g_strdup_printf (_("Handle position: %0.6f"), seg->left);
ed_set_hint (gradient_editor, str);
break;
case GRAD_DRAG_MIDDLE:
pos = control_calc_g_pos (gradient_editor, x);
seg->middle = CLAMP (pos, seg->left + EPSILON, seg->right - EPSILON);
str = g_strdup_printf (_("Handle position: %0.6f"), seg->middle);
ed_set_hint (gradient_editor, str);
break;
case GRAD_DRAG_ALL:
pos = control_calc_g_pos (gradient_editor, x);
delta = pos - gradient_editor->control_last_gx;
if ((seg->left >= gradient_editor->control_sel_l->left) &&
(seg->right <= gradient_editor->control_sel_r->right))
delta = control_move (gradient_editor,
gradient_editor->control_sel_l,
gradient_editor->control_sel_r, delta);
else
delta = control_move (gradient_editor, seg, seg, delta);
gradient_editor->control_last_gx += delta;
str = g_strdup_printf (_("Distance: %0.6f"),
gradient_editor->control_last_gx -
gradient_editor->control_orig_pos);
ed_set_hint (gradient_editor, str);
break;
default:
gimp_fatal_error ("Attempt to move bogus handle %d",
(gint) gradient_editor->control_drag_mode);
break;
}
if (str)
g_free (str);
if (gradient_editor->instant_update)
{
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor,
GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
else
{
ed_update_editor (gradient_editor, GRAD_UPDATE_CONTROL);
}
}
static void
control_compress_left (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)
control_compress_range (range_l->prev, range_l->prev,
range_l->prev->left, pos);
else
control_compress_range (range_l, drag_seg->prev, range_l->left, pos);
/* Compress segments to the right of the handle */
if (drag_seg != range_r->next)
control_compress_range (drag_seg, range_r, pos, range_r->right);
else
control_compress_range (drag_seg, drag_seg, pos, drag_seg->right);
}
static void
control_compress_range (GimpGradientSegment *range_l,
GimpGradientSegment *range_r,
gdouble new_l,
gdouble new_r)
{
gdouble orig_l, orig_r;
gdouble scale;
GimpGradientSegment *seg, *aseg;
orig_l = range_l->left;
orig_r = range_r->right;
scale = (new_r - new_l) / (orig_r - orig_l);
seg = range_l;
do
{
seg->left = new_l + (seg->left - orig_l) * scale;
seg->middle = new_l + (seg->middle - orig_l) * scale;
seg->right = new_l + (seg->right - orig_l) * scale;
/* Next */
aseg = seg;
seg = seg->next;
}
while (aseg != range_r);
}
/*****/
static gdouble
control_move (GradientEditor *gradient_editor,
GimpGradientSegment *range_l,
GimpGradientSegment *range_r,
gdouble delta)
{
gdouble lbound, rbound;
gint is_first, is_last;
GimpGradientSegment *seg, *aseg;
/* First or last segments in gradient? */
is_first = (range_l->prev == NULL);
is_last = (range_r->next == NULL);
/* Calculate drag bounds */
if (! gradient_editor->control_compress)
{
if (!is_first)
lbound = range_l->prev->middle + EPSILON;
else
lbound = range_l->left + EPSILON;
if (!is_last)
rbound = range_r->next->middle - EPSILON;
else
rbound = range_r->right - EPSILON;
}
else
{
if (!is_first)
lbound = range_l->prev->left + 2.0 * EPSILON;
else
lbound = range_l->left + EPSILON;
if (!is_last)
rbound = range_r->next->right - 2.0 * EPSILON;
else
rbound = range_r->right - EPSILON;
}
/* Fix the delta if necessary */
if (delta < 0.0)
{
if (!is_first)
{
if (range_l->left + delta < lbound)
delta = lbound - range_l->left;
}
else
if (range_l->middle + delta < lbound)
delta = lbound - range_l->middle;
}
else
{
if (!is_last)
{
if (range_r->right + delta > rbound)
delta = rbound - range_r->right;
}
else
if (range_r->middle + delta > rbound)
delta = rbound - range_r->middle;
}
/* Move all the segments inside the range */
seg = range_l;
do
{
if (!((seg == range_l) && is_first))
seg->left += delta;
seg->middle += delta;
if (!((seg == range_r) && is_last))
seg->right += delta;
/* Next */
aseg = seg;
seg = seg->next;
}
while (aseg != range_r);
/* Fix the segments that surround the range */
if (!is_first)
{
if (! gradient_editor->control_compress)
range_l->prev->right = range_l->left;
else
control_compress_range (range_l->prev, range_l->prev,
range_l->prev->left, range_l->left);
}
if (!is_last)
{
if (! gradient_editor->control_compress)
range_r->next->left = range_r->right;
else
control_compress_range (range_r->next, range_r->next,
range_r->right, range_r->next->right);
}
return delta;
}
/*****/
static void
control_update (GradientEditor *gradient_editor,
GimpGradient *gradient,
gboolean recalculate)
{
GtkAdjustment *adjustment;
gint cwidth, cheight;
gint pwidth, pheight;
if (! GTK_WIDGET_DRAWABLE (gradient_editor->control))
return;
/* See whether we have to re-create the control pixmap
* depending on the preview's width
*/
cwidth = gradient_editor->preview->allocation.width;
cheight = GRAD_CONTROL_HEIGHT;
if (gradient_editor->control_pixmap)
gdk_window_get_size (gradient_editor->control_pixmap, &pwidth, &pheight);
if (! gradient_editor->control_pixmap ||
(cwidth != pwidth) ||
(cheight != pheight))
{
if (gradient_editor->control_pixmap)
gdk_pixmap_unref (gradient_editor->control_pixmap);
gradient_editor->control_pixmap =
gdk_pixmap_new (gradient_editor->control->window, cwidth, cheight, -1);
recalculate = TRUE;
}
/* Avaoid segfault on first invocation */
if (cwidth < GRAD_PREVIEW_WIDTH)
return;
/* Have to reset the selection? */
if (recalculate)
control_select_single_segment (gradient_editor, gradient->segments);
/* Redraw pixmap */
adjustment = GTK_ADJUSTMENT (gradient_editor->scroll_data);
control_draw (gradient_editor,
gradient,
gradient_editor->control_pixmap,
cwidth, cheight,
adjustment->value,
adjustment->value + adjustment->page_size);
gdk_draw_pixmap (gradient_editor->control->window,
gradient_editor->control->style->black_gc,
gradient_editor->control_pixmap,
0, 0, 0, 0,
cwidth, cheight);
}
static void
control_draw (GradientEditor *gradient_editor,
GimpGradient *gradient,
GdkPixmap *pixmap,
gint width,
gint height,
gdouble left,
gdouble right)
{
gint sel_l, sel_r;
gdouble g_pos;
GimpGradientSegment *seg;
control_drag_mode_t handle;
/* Clear the pixmap */
gdk_draw_rectangle (pixmap, gradient_editor->control->style->bg_gc[GTK_STATE_NORMAL],
TRUE, 0, 0, width, height);
/* Draw selection */
sel_l = control_calc_p_pos (gradient_editor,
gradient_editor->control_sel_l->left);
sel_r = control_calc_p_pos (gradient_editor,
gradient_editor->control_sel_r->right);
gdk_draw_rectangle (pixmap,
gradient_editor->control->style->dark_gc[GTK_STATE_NORMAL],
TRUE, sel_l, 0, sel_r - sel_l + 1, height);
/* Draw handles */
seg = gradient->segments;
while (seg)
{
control_draw_normal_handle (gradient_editor, pixmap, seg->left, height);
control_draw_middle_handle (gradient_editor, pixmap, seg->middle, height);
/* Draw right handle only if this is the last segment */
if (seg->next == NULL)
control_draw_normal_handle (gradient_editor, pixmap, seg->right, height);
/* Next! */
seg = seg->next;
}
/* Draw the handle which is closest to the mouse position */
g_pos = control_calc_g_pos (gradient_editor, gradient_editor->control_last_x);
seg_get_closest_handle (gradient, CLAMP (g_pos, 0.0, 1.0), &seg, &handle);
switch (handle)
{
case GRAD_DRAG_LEFT:
if (seg)
{
control_draw_normal_handle (gradient_editor, pixmap,
seg->left, height);
}
else
{
seg = gimp_gradient_segment_get_last (gradient->segments);
control_draw_normal_handle (gradient_editor, pixmap,
seg->right, height);
}
break;
case GRAD_DRAG_MIDDLE:
control_draw_middle_handle (gradient_editor, pixmap, seg->middle, height);
break;
default:
break;
}
}
static void
control_draw_normal_handle (GradientEditor *gradient_editor,
GdkPixmap *pixmap,
gdouble pos,
gint height)
{
control_draw_handle (pixmap,
gradient_editor->control->style->black_gc,
gradient_editor->control->style->black_gc,
control_calc_p_pos (gradient_editor, pos), height);
}
static void
control_draw_middle_handle (GradientEditor *gradient_editor,
GdkPixmap *pixmap,
gdouble pos,
gint height)
{
control_draw_handle (pixmap,
gradient_editor->control->style->black_gc,
gradient_editor->control->style->bg_gc[GTK_STATE_PRELIGHT],
control_calc_p_pos (gradient_editor, pos), height);
}
static void
control_draw_handle (GdkPixmap *pixmap,
GdkGC *border_gc,
GdkGC *fill_gc,
gint xpos,
gint height)
{
gint y;
gint left, right, bottom;
for (y = 0; y < height; y++)
gdk_draw_line (pixmap, fill_gc, xpos - y / 2, y, xpos + y / 2, y);
bottom = height - 1;
left = xpos - bottom / 2;
right = xpos + bottom / 2;
gdk_draw_line (pixmap, border_gc, xpos, 0, left, bottom);
gdk_draw_line (pixmap, border_gc, xpos, 0, right, bottom);
gdk_draw_line (pixmap, border_gc, left, bottom, right, bottom);
}
/*****/
static gint
control_calc_p_pos (GradientEditor *gradient_editor,
gdouble pos)
{
gint pwidth, pheight;
GtkAdjustment *adjustment;
/* Calculate the position (in widget's coordinates) of the
* requested point from the gradient. Rounding is done to
* minimize mismatches between the rendered gradient preview
* and the gradient control's handles.
*/
adjustment = GTK_ADJUSTMENT (gradient_editor->scroll_data);
gdk_window_get_size (gradient_editor->control_pixmap, &pwidth, &pheight);
return RINT ((pwidth - 1) * (pos - adjustment->value) / adjustment->page_size);
}
static gdouble
control_calc_g_pos (GradientEditor *gradient_editor,
gint pos)
{
gint pwidth, pheight;
GtkAdjustment *adjustment;
/* Calculate the gradient position that corresponds to widget's coordinates */
adjustment = GTK_ADJUSTMENT (gradient_editor->scroll_data);
gdk_window_get_size (gradient_editor->control_pixmap, &pwidth, &pheight);
return adjustment->page_size * pos / (pwidth - 1) + adjustment->value;
}
/***** Control popup functions *****/
static void
cpopup_create_main_menu (GradientEditor *gradient_editor)
{
GtkWidget *menu;
GtkWidget *menuitem;
GtkWidget *label;
GtkAccelGroup *accel_group;
menu = gtk_menu_new ();
accel_group = gtk_accel_group_new ();
gradient_editor->accel_group = accel_group;
gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
gtk_window_add_accel_group (GTK_WINDOW (gradient_editor->shell), accel_group);
/* Left endpoint */
menuitem = cpopup_create_color_item (&gradient_editor->left_color_preview,
&label);
gtk_label_set_text (GTK_LABEL (label), _("Left endpoint's color"));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
(GtkSignalFunc) cpopup_set_left_color_callback,
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'L', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
menuitem = gtk_menu_item_new_with_label (_("Load from"));
gradient_editor->control_left_load_popup =
cpopup_create_load_menu (gradient_editor,
gradient_editor->left_load_color_boxes,
gradient_editor->left_load_labels,
_("Left neighbor's right endpoint"),
_("Right endpoint"),
GTK_SIGNAL_FUNC (cpopup_load_left_callback),
'L', GDK_CONTROL_MASK,
'L', GDK_MOD1_MASK,
'F', GDK_CONTROL_MASK);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem),
gradient_editor->control_left_load_popup);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label (_("Save to"));
gradient_editor->control_left_save_popup =
cpopup_create_save_menu (gradient_editor,
gradient_editor->left_save_color_boxes,
gradient_editor->left_save_labels,
GTK_SIGNAL_FUNC (cpopup_save_left_callback));
gtk_menu_item_set_submenu (GTK_MENU_ITEM(menuitem),
gradient_editor->control_left_save_popup);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
/* Right endpoint */
menuitem = gtk_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = cpopup_create_color_item (&gradient_editor->right_color_preview,
&label);
gtk_label_set_text (GTK_LABEL (label), _("Right endpoint's color"));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
(GtkSignalFunc) cpopup_set_right_color_callback,
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'R', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
menuitem = gtk_menu_item_new_with_label (_("Load from"));
gradient_editor->control_right_load_popup =
cpopup_create_load_menu (gradient_editor,
gradient_editor->right_load_color_boxes,
gradient_editor->right_load_labels,
_("Right neighbor's left endpoint"),
_("Left endpoint"),
GTK_SIGNAL_FUNC (cpopup_load_right_callback),
'R', GDK_CONTROL_MASK,
'R', GDK_MOD1_MASK,
'F', GDK_MOD1_MASK);
gtk_menu_item_set_submenu (GTK_MENU_ITEM(menuitem),
gradient_editor->control_right_load_popup);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label (_("Save to"));
gradient_editor->control_right_save_popup =
cpopup_create_save_menu (gradient_editor,
gradient_editor->right_save_color_boxes,
gradient_editor->right_save_labels,
GTK_SIGNAL_FUNC (cpopup_save_right_callback));
gtk_menu_item_set_submenu (GTK_MENU_ITEM(menuitem),
gradient_editor->control_right_save_popup);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
/* Blending function */
menuitem = gtk_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = cpopup_create_menu_item_with_label ("", &gradient_editor->control_blending_label);
gradient_editor->control_blending_popup =
cpopup_create_blending_menu (gradient_editor);
gtk_menu_item_set_submenu (GTK_MENU_ITEM(menuitem),
gradient_editor->control_blending_popup);
gtk_menu_append (GTK_MENU(menu), menuitem);
gtk_widget_show (menuitem);
/* Coloring type */
menuitem = cpopup_create_menu_item_with_label ("", &gradient_editor->control_coloring_label);
gradient_editor->control_coloring_popup =
cpopup_create_coloring_menu (gradient_editor);
gtk_menu_item_set_submenu (GTK_MENU_ITEM(menuitem),
gradient_editor->control_coloring_popup);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
/* Operations */
menuitem = gtk_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
/* Split at midpoint */
menuitem = cpopup_create_menu_item_with_label ("", &gradient_editor->control_split_m_label);
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_split_midpoint_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator(menuitem, "activate",
accel_group,
'S', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
/* Split uniformly */
menuitem = cpopup_create_menu_item_with_label ("", &gradient_editor->control_split_u_label);
gtk_signal_connect (GTK_OBJECT(menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_split_uniform_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'U', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
/* Delete */
menuitem = cpopup_create_menu_item_with_label ("", &gradient_editor->control_delete_label);
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_delete_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gradient_editor->control_delete_menu_item = menuitem;
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'D', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
/* Recenter */
menuitem = cpopup_create_menu_item_with_label ("", &gradient_editor->control_recenter_label);
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_recenter_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'C', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
/* Redistribute */
menuitem = cpopup_create_menu_item_with_label ("", &gradient_editor->control_redistribute_label);
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_redistribute_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'C', GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
/* Selection ops */
menuitem = gtk_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label (_("Selection operations"));
gradient_editor->control_sel_ops_popup =
cpopup_create_sel_ops_menu (gradient_editor);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem),
gradient_editor->control_sel_ops_popup);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
/* Done */
gradient_editor->control_main_popup = menu;
}
static void
cpopup_do_popup (GradientEditor *gradient_editor)
{
cpopup_adjust_menus (gradient_editor);
gtk_menu_popup (GTK_MENU (gradient_editor->control_main_popup),
NULL, NULL, NULL, NULL, 3, 0);
}
/***** Create a single menu item *****/
static GtkWidget *
cpopup_create_color_item (GtkWidget **color_box,
GtkWidget **label)
{
GtkWidget *menuitem;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *wcolor_box;
GtkWidget *wlabel;
menuitem = gtk_menu_item_new();
hbox = gtk_hbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (menuitem), hbox);
gtk_widget_show (hbox);
vbox = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
wcolor_box = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_preview_set_dither (GTK_PREVIEW (wcolor_box), GDK_RGB_DITHER_MAX);
gtk_preview_size (GTK_PREVIEW (wcolor_box),
GRAD_COLOR_BOX_WIDTH, GRAD_COLOR_BOX_HEIGHT);
gtk_box_pack_start (GTK_BOX (vbox), wcolor_box, FALSE, FALSE, 2);
gtk_widget_show (wcolor_box);
if (color_box)
*color_box = wcolor_box;
wlabel = gtk_label_new ("");
gtk_misc_set_alignment (GTK_MISC (wlabel), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), wlabel, FALSE, FALSE, 4);
gtk_widget_show (wlabel);
if (label)
*label = wlabel;
return menuitem;
}
static GtkWidget *
cpopup_create_menu_item_with_label (gchar *str,
GtkWidget **label)
{
GtkWidget *menuitem;
GtkWidget *accel_label;
menuitem = gtk_menu_item_new ();
accel_label = gtk_accel_label_new (str);
gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
gtk_container_add (GTK_CONTAINER (menuitem), accel_label);
gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menuitem);
gtk_widget_show (accel_label);
if (label)
*label = accel_label;
return menuitem;
}
/***** Update all menus *****/
static void
cpopup_adjust_menus (GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *seg;
gint i;
GimpRGB fg;
gradient = gimp_context_get_gradient (gradient_editor->context);
/* Render main menu color boxes */
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->left_color_preview),
&gradient_editor->control_sel_l->left_color);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->right_color_preview),
&gradient_editor->control_sel_r->right_color);
/* Render load color from endpoint color boxes */
if (gradient_editor->control_sel_l->prev != NULL)
seg = gradient_editor->control_sel_l->prev;
else
seg = gimp_gradient_segment_get_last (gradient_editor->control_sel_l);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->left_load_color_boxes[0]),
&seg->right_color);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->left_load_color_boxes[1]),
&gradient_editor->control_sel_r->right_color);
if (gradient_editor->control_sel_r->next != NULL)
seg = gradient_editor->control_sel_r->next;
else
seg = gradient->segments;
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->right_load_color_boxes[0]),
&seg->left_color);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->right_load_color_boxes[1]),
&gradient_editor->control_sel_l->left_color);
/* Render Foreground color boxes */
gimp_context_get_foreground (gimp_context_get_user (), &fg);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->left_load_color_boxes[2]),
&fg);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->right_load_color_boxes[2]),
&fg);
/* Render saved color boxes */
for (i = 0; i < GRAD_NUM_COLORS; i++)
cpopup_update_saved_color (gradient_editor,
i, &gradient_editor->saved_colors[i]);
/* Adjust labels */
if (gradient_editor->control_sel_l == gradient_editor->control_sel_r)
{
gtk_label_set_text (GTK_LABEL (gradient_editor->control_blending_label),
_("Blending function for segment"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_coloring_label),
_("Coloring type for segment"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_split_m_label),
_("Split segment at midpoint"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_split_u_label),
_("Split segment uniformly"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_delete_label),
_("Delete segment"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_recenter_label),
_("Re-center segment's midpoint"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_redistribute_label),
_("Re-distribute handles in segment"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_flip_label),
_("Flip segment"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_replicate_label),
_("Replicate segment"));
}
else
{
gtk_label_set_text (GTK_LABEL (gradient_editor->control_blending_label),
_("Blending function for selection"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_coloring_label),
_("Coloring type for selection"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_split_m_label),
_("Split segments at midpoints"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_split_u_label),
_("Split segments uniformly"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_delete_label),
_("Delete selection"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_recenter_label),
_("Re-center midpoints in selection"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_redistribute_label),
_("Re-distribute handles in selection"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_flip_label),
_("Flip selection"));
gtk_label_set_text (GTK_LABEL (gradient_editor->control_replicate_label),
_("Replicate selection"));
}
/* Adjust blending and coloring menus */
cpopup_adjust_blending_menu (gradient_editor);
cpopup_adjust_coloring_menu (gradient_editor);
/* Can invoke delete? */
if ((gradient_editor->control_sel_l->prev == NULL) &&
(gradient_editor->control_sel_r->next == NULL))
gtk_widget_set_sensitive (gradient_editor->control_delete_menu_item, FALSE);
else
gtk_widget_set_sensitive (gradient_editor->control_delete_menu_item, TRUE);
/* Can invoke blend colors / opacity? */
if (gradient_editor->control_sel_l == gradient_editor->control_sel_r)
{
gtk_widget_set_sensitive (gradient_editor->control_blend_colors_menu_item,
FALSE);
gtk_widget_set_sensitive (gradient_editor->control_blend_opacity_menu_item,
FALSE);
}
else
{
gtk_widget_set_sensitive (gradient_editor->control_blend_colors_menu_item,
TRUE);
gtk_widget_set_sensitive (gradient_editor->control_blend_opacity_menu_item,
TRUE);
}
}
static void
cpopup_adjust_blending_menu (GradientEditor *gradient_editor)
{
gint equal;
glong i, num_items;
gint type;
cpopup_check_selection_params (gradient_editor, &equal, NULL);
/* Block activate signals */
num_items = (sizeof (gradient_editor->control_blending_items) /
sizeof (gradient_editor->control_blending_items[0]));
type = (int) gradient_editor->control_sel_l->type;
for (i = 0; i < num_items; i++)
gtk_signal_handler_block_by_data
(GTK_OBJECT (gradient_editor->control_blending_items[i]),
gradient_editor);
/* Set state */
if (equal)
{
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gradient_editor->control_blending_items[type]), TRUE);
gtk_widget_hide (gradient_editor->control_blending_items[num_items - 1]);
}
else
{
gtk_widget_show (gradient_editor->control_blending_items[num_items - 1]);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gradient_editor->control_blending_items[num_items - 1]), TRUE);
}
/* Unblock signals */
for (i = 0; i < num_items; i++)
gtk_signal_handler_unblock_by_data (GTK_OBJECT (gradient_editor->control_blending_items[i]),
gradient_editor);
}
static void
cpopup_adjust_coloring_menu (GradientEditor *gradient_editor)
{
gint equal;
glong i, num_items;
gint coloring;
cpopup_check_selection_params (gradient_editor, NULL, &equal);
/* Block activate signals */
num_items = (sizeof (gradient_editor->control_coloring_items) /
sizeof (gradient_editor->control_coloring_items[0]));
coloring = (int) gradient_editor->control_sel_l->color;
for (i = 0; i < num_items; i++)
gtk_signal_handler_block_by_data (GTK_OBJECT (gradient_editor->control_coloring_items[i]),
gradient_editor);
/* Set state */
if (equal)
{
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gradient_editor->control_coloring_items[coloring]), TRUE);
gtk_widget_hide (gradient_editor->control_coloring_items[num_items - 1]);
}
else
{
gtk_widget_show (gradient_editor->control_coloring_items[num_items - 1]);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gradient_editor->control_coloring_items[num_items - 1]), TRUE);
}
/* Unblock signals */
for (i = 0; i < num_items; i++)
gtk_signal_handler_unblock_by_data (GTK_OBJECT (gradient_editor->control_coloring_items[i]),
gradient_editor);
}
static void
cpopup_check_selection_params (GradientEditor *gradient_editor,
gint *equal_blending,
gint *equal_coloring)
{
GimpGradientSegmentType type;
GimpGradientSegmentColor color;
gint etype, ecolor;
GimpGradientSegment *seg, *aseg;
type = gradient_editor->control_sel_l->type;
color = gradient_editor->control_sel_l->color;
etype = 1;
ecolor = 1;
seg = gradient_editor->control_sel_l;
do
{
etype = etype && (seg->type == type);
ecolor = ecolor && (seg->color == color);
aseg = seg;
seg = seg->next;
}
while (aseg != gradient_editor->control_sel_r);
if (equal_blending)
*equal_blending = etype;
if (equal_coloring)
*equal_coloring = ecolor;
}
/*****/
static void
cpopup_render_color_box (GtkPreview *preview,
GimpRGB *color)
{
guchar rows[3][GRAD_COLOR_BOX_WIDTH * 3];
gint x, y;
gint r0, g0, b0;
gint r1, g1, b1;
guchar *p0, *p1, *p2;
/* Fill rows */
r0 = (GIMP_CHECK_DARK + (color->r - GIMP_CHECK_DARK) * color->a) * 255.0;
r1 = (GIMP_CHECK_LIGHT + (color->r - GIMP_CHECK_LIGHT) * color->a) * 255.0;
g0 = (GIMP_CHECK_DARK + (color->g - GIMP_CHECK_DARK) * color->a) * 255.0;
g1 = (GIMP_CHECK_LIGHT + (color->g - GIMP_CHECK_LIGHT) * color->a) * 255.0;
b0 = (GIMP_CHECK_DARK + (color->b - GIMP_CHECK_DARK) * color->a) * 255.0;
b1 = (GIMP_CHECK_LIGHT + (color->b - GIMP_CHECK_LIGHT) * color->a) * 255.0;
p0 = rows[0];
p1 = rows[1];
p2 = rows[2];
for (x = 0; x < GRAD_COLOR_BOX_WIDTH; x++)
{
if ((x == 0) || (x == (GRAD_COLOR_BOX_WIDTH - 1)))
{
*p0++ = 0;
*p0++ = 0;
*p0++ = 0;
*p1++ = 0;
*p1++ = 0;
*p1++ = 0;
}
else
if ((x / GIMP_CHECK_SIZE) & 1)
{
*p0++ = r1;
*p0++ = g1;
*p0++ = b1;
*p1++ = r0;
*p1++ = g0;
*p1++ = b0;
}
else
{
*p0++ = r0;
*p0++ = g0;
*p0++ = b0;
*p1++ = r1;
*p1++ = g1;
*p1++ = b1;
}
*p2++ = 0;
*p2++ = 0;
*p2++ = 0;
}
/* Fill preview */
gtk_preview_draw_row (preview, rows[2], 0, 0, GRAD_COLOR_BOX_WIDTH);
for (y = 1; y < (GRAD_COLOR_BOX_HEIGHT - 1); y++)
if ((y / GIMP_CHECK_SIZE) & 1)
gtk_preview_draw_row (preview, rows[1], 0, y, GRAD_COLOR_BOX_WIDTH);
else
gtk_preview_draw_row (preview, rows[0], 0, y, GRAD_COLOR_BOX_WIDTH);
gtk_preview_draw_row (preview, rows[2], 0, y, GRAD_COLOR_BOX_WIDTH);
}
/***** Creale load & save menus *****/
static GtkWidget *
cpopup_create_load_menu (GradientEditor *gradient_editor,
GtkWidget **color_boxes,
GtkWidget **labels,
gchar *label1,
gchar *label2,
GtkSignalFunc callback,
gchar accel_key_0, guint8 accel_mods_0,
gchar accel_key_1, guint8 accel_mods_1,
gchar accel_key_2, guint8 accel_mods_2)
{
GtkWidget *menu;
GtkWidget *menuitem;
GtkAccelGroup *accel_group;
gint i;
menu = gtk_menu_new ();
accel_group = gradient_editor->accel_group;
gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
/* Create items */
for (i = 0; i < (GRAD_NUM_COLORS + 3); i++)
{
if (i == 3)
{
/* Insert separator between "to fetch" and "saved" colors */
menuitem = gtk_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
}
menuitem = cpopup_create_color_item (&color_boxes[i], &labels[i]);
gtk_object_set_user_data (GTK_OBJECT (menuitem),
GINT_TO_POINTER (i));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
callback,
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
switch (i)
{
case 0:
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
accel_key_0, accel_mods_0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
break;
case 1:
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
accel_key_1, accel_mods_1,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
break;
case 2:
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
accel_key_2, accel_mods_2,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
break;
default:
break;
}
}
/* Set labels */
gtk_label_set_text (GTK_LABEL (labels[0]), label1);
gtk_label_set_text (GTK_LABEL (labels[1]), label2);
gtk_label_set_text (GTK_LABEL (labels[2]), _("FG color"));
return menu;
}
static GtkWidget *
cpopup_create_save_menu (GradientEditor *gradient_editor,
GtkWidget **color_boxes,
GtkWidget **labels,
GtkSignalFunc callback)
{
GtkWidget *menu;
GtkWidget *menuitem;
gint i;
menu = gtk_menu_new ();
for (i = 0; i < GRAD_NUM_COLORS; i++)
{
menuitem = cpopup_create_color_item (&color_boxes[i], &labels[i]);
gtk_object_set_user_data (GTK_OBJECT (menuitem),
GINT_TO_POINTER (i));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
callback,
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
}
return menu;
}
static void
cpopup_update_saved_color (GradientEditor *gradient_editor,
gint n,
GimpRGB *color)
{
gchar *str;
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->left_load_color_boxes[n + 3]),
color);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->left_save_color_boxes[n]),
color);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->right_load_color_boxes[n + 3]),
color);
cpopup_render_color_box (GTK_PREVIEW (gradient_editor->right_save_color_boxes[n]),
color);
str = g_strdup_printf (_("RGBA (%0.3f, %0.3f, %0.3f, %0.3f)"),
color->r,
color->g,
color->b,
color->a);
gtk_label_set_text (GTK_LABEL (gradient_editor->left_load_labels[n + 3]), str);
gtk_label_set_text (GTK_LABEL (gradient_editor->left_save_labels[n]), str);
gtk_label_set_text (GTK_LABEL (gradient_editor->right_load_labels[n + 3]), str);
gtk_label_set_text (GTK_LABEL (gradient_editor->right_save_labels[n]), str);
g_free (str);
gradient_editor->saved_colors[n] = *color;
}
/*****/
static void
cpopup_load_left_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradientSegment *seg;
GimpRGB fg;
gint i;
i = GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (widget)));
switch (i)
{
case 0: /* Fetch from left neighbor's right endpoint */
if (gradient_editor->control_sel_l->prev != NULL)
seg = gradient_editor->control_sel_l->prev;
else
seg = gimp_gradient_segment_get_last (gradient_editor->control_sel_l);
cpopup_blend_endpoints (gradient_editor,
&seg->right_color,
&gradient_editor->control_sel_r->right_color,
TRUE, TRUE);
break;
case 1: /* Fetch from right endpoint */
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_r->right_color,
&gradient_editor->control_sel_r->right_color,
TRUE, TRUE);
break;
case 2: /* Fetch from FG color */
gimp_context_get_foreground (gimp_context_get_user (), &fg);
cpopup_blend_endpoints (gradient_editor,
&fg,
&gradient_editor->control_sel_r->right_color,
TRUE, TRUE);
break;
default: /* Load a color */
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->saved_colors[i - 3],
&gradient_editor->control_sel_r->right_color,
TRUE, TRUE);
break;
}
gimp_data_dirty (GIMP_DATA (gimp_context_get_gradient (gradient_editor->context)));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
static void
cpopup_save_left_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
gint i;
i = GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (widget)));
gradient_editor->saved_colors[i] = gradient_editor->control_sel_l->left_color;
}
static void
cpopup_load_right_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *seg;
GimpRGB fg;
gint i;
i = GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (widget)));
gradient = gimp_context_get_gradient (gradient_editor->context);
switch (i)
{
case 0: /* Fetch from right neighbor's left endpoint */
if (gradient_editor->control_sel_r->next != NULL)
seg = gradient_editor->control_sel_r->next;
else
seg = gradient->segments;
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_r->left_color,
&seg->left_color,
TRUE, TRUE);
break;
case 1: /* Fetch from left endpoint */
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_l->left_color,
&gradient_editor->control_sel_l->left_color,
TRUE, TRUE);
break;
case 2: /* Fetch from FG color */
gimp_context_get_foreground (gimp_context_get_user (), &fg);
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_l->left_color,
&fg,
TRUE, TRUE);
break;
default: /* Load a color */
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_l->left_color,
&gradient_editor->saved_colors[i - 3],
TRUE, TRUE);
break;
}
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
static void
cpopup_save_right_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
gint i;
i = GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (widget)));
gradient_editor->saved_colors[i] = gradient_editor->control_sel_r->right_color;
}
/*****/
static GimpGradientSegment *
cpopup_save_selection (GradientEditor *gradient_editor)
{
GimpGradientSegment *seg, *prev, *tmp;
GimpGradientSegment *oseg, *oaseg;
prev = NULL;
oseg = gradient_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 != gradient_editor->control_sel_r);
return tmp;
}
static void
cpopup_replace_selection (GradientEditor *gradient_editor,
GimpGradientSegment *replace_seg)
{
GimpGradient *gradient;
GimpGradientSegment *lseg, *rseg;
GimpGradientSegment *replace_last;
gradient = gimp_context_get_gradient (gradient_editor->context);
/* Remember left and right segments */
lseg = gradient_editor->control_sel_l->prev;
rseg = gradient_editor->control_sel_r->next;
replace_last = gimp_gradient_segment_get_last (replace_seg);
/* Free old selection */
gradient_editor->control_sel_r->next = NULL;
gimp_gradient_segments_free (gradient_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;
gradient_editor->control_sel_l = replace_seg;
gradient_editor->control_sel_r = replace_last;
gradient->last_visited = NULL; /* Force re-search */
}
/***** Color dialogs for left and right endpoint *****/
static void
cpopup_set_left_color_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
gradient = gimp_context_get_gradient (gradient_editor->context);
gradient_editor->left_saved_dirty = GIMP_DATA (gradient)->dirty;
gradient_editor->left_saved_segments = cpopup_save_selection (gradient_editor);
color_notebook_new (_("Left Endpoint Color"),
&gradient_editor->control_sel_l->left_color,
(GtkSignalFunc) cpopup_left_color_changed,
gradient_editor,
gradient_editor->instant_update,
TRUE);
gtk_widget_set_sensitive (gradient_editor->shell, FALSE);
}
static void
cpopup_set_right_color_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
gradient = gimp_context_get_gradient (gradient_editor->context);
gradient_editor->right_saved_dirty = GIMP_DATA (gradient)->dirty;
gradient_editor->right_saved_segments = cpopup_save_selection (gradient_editor);
color_notebook_new (_("Right Endpoint Color"),
&gradient_editor->control_sel_l->right_color,
(GtkSignalFunc) cpopup_right_color_changed,
gradient_editor,
gradient_editor->instant_update,
TRUE);
gtk_widget_set_sensitive (gradient_editor->shell, FALSE);
}
static void
cpopup_left_color_changed (ColorNotebook *cnb,
const GimpRGB *color,
ColorNotebookState state,
gpointer data)
{
GradientEditor *gradient_editor;
GimpGradient *gradient;
gradient_editor = (GradientEditor *) data;
gradient = gimp_context_get_gradient (gradient_editor->context);
switch (state)
{
case COLOR_NOTEBOOK_OK:
cpopup_blend_endpoints (gradient_editor,
(GimpRGB *) color,
&gradient_editor->control_sel_r->right_color,
TRUE, TRUE);
gimp_gradient_segments_free (gradient_editor->left_saved_segments);
gimp_data_dirty (GIMP_DATA (gradient));
color_notebook_free (cnb);
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
break;
case COLOR_NOTEBOOK_UPDATE:
cpopup_blend_endpoints (gradient_editor,
(GimpRGB *) color,
&gradient_editor->control_sel_r->right_color,
TRUE, TRUE);
gimp_data_dirty (GIMP_DATA (gradient));
break;
case COLOR_NOTEBOOK_CANCEL:
cpopup_replace_selection (gradient_editor,
gradient_editor->left_saved_segments);
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
GIMP_DATA (gradient)->dirty = gradient_editor->left_saved_dirty;
color_notebook_free (cnb);
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
break;
}
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
static void
cpopup_right_color_changed (ColorNotebook *cnb,
const GimpRGB *color,
ColorNotebookState state,
gpointer data)
{
GradientEditor *gradient_editor;
GimpGradient *gradient;
gradient_editor = (GradientEditor *) data;
gradient = gimp_context_get_gradient (gradient_editor->context);
switch (state)
{
case COLOR_NOTEBOOK_UPDATE:
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_r->left_color,
(GimpRGB *) color,
TRUE, TRUE);
gimp_data_dirty (GIMP_DATA (gradient));
break;
case COLOR_NOTEBOOK_OK:
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_r->left_color,
(GimpRGB *) color,
TRUE, TRUE);
gimp_gradient_segments_free (gradient_editor->right_saved_segments);
gimp_data_dirty (GIMP_DATA (gradient));
color_notebook_free (cnb);
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
break;
case COLOR_NOTEBOOK_CANCEL:
cpopup_replace_selection (gradient_editor,
gradient_editor->right_saved_segments);
GIMP_DATA (gradient)->dirty = gradient_editor->right_saved_dirty;
color_notebook_free (cnb);
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
break;
}
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
/***** Blending menu *****/
static GtkWidget *
cpopup_create_blending_menu (GradientEditor *gradient_editor)
{
GtkWidget *menu;
GtkWidget *menuitem;
GSList *group;
gint i;
gint num_items;
static const gchar *blending_types[] =
{
N_("Linear"),
N_("Curved"),
N_("Sinusoidal"),
N_("Spherical (increasing)"),
N_("Spherical (decreasing)")
};
menu = gtk_menu_new ();
group = NULL;
num_items = (sizeof (gradient_editor->control_blending_items) /
sizeof (gradient_editor->control_blending_items[0]));
for (i = 0; i < num_items; i++)
{
if (i == (num_items - 1))
menuitem = gtk_radio_menu_item_new_with_label (group, _("(Varies)"));
else
menuitem =
gtk_radio_menu_item_new_with_label (group,
gettext (blending_types[i]));
group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
gtk_object_set_user_data (GTK_OBJECT (menuitem),
GINT_TO_POINTER (i));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_blending_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gradient_editor->control_blending_items[i] = menuitem;
}
/* "Varies" is always disabled */
gtk_widget_set_sensitive (gradient_editor->control_blending_items[num_items - 1], FALSE);
return menu;
}
static void
cpopup_blending_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegmentType type;
GimpGradientSegment *seg, *aseg;
gradient = gimp_context_get_gradient (gradient_editor->context);
if (!GTK_CHECK_MENU_ITEM (widget)->active)
return; /* Do nothing if the menu item is being deactivated */
type = (GimpGradientSegmentType)
GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (widget)));
seg = gradient_editor->control_sel_l;
do
{
seg->type = type;
aseg = seg;
seg = seg->next;
}
while (aseg != gradient_editor->control_sel_r);
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
/***** Coloring menu *****/
static GtkWidget *
cpopup_create_coloring_menu (GradientEditor *gradient_editor)
{
GtkWidget *menu;
GtkWidget *menuitem;
GSList *group;
gint i;
gint num_items;
static const gchar *coloring_types[] =
{
N_("Plain RGB"),
N_("HSV (counter-clockwise hue)"),
N_("HSV (clockwise hue)")
};
menu = gtk_menu_new ();
group = NULL;
num_items = (sizeof (gradient_editor->control_coloring_items) /
sizeof (gradient_editor->control_coloring_items[0]));
for (i = 0; i < num_items; i++)
{
if (i == (num_items - 1))
menuitem = gtk_radio_menu_item_new_with_label(group, _("(Varies)"));
else
menuitem = gtk_radio_menu_item_new_with_label (group, gettext (coloring_types[i]));
group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
gtk_object_set_user_data (GTK_OBJECT (menuitem),
GINT_TO_POINTER (i));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_coloring_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gradient_editor->control_coloring_items[i] = menuitem;
}
/* "Varies" is always disabled */
gtk_widget_set_sensitive (gradient_editor->control_coloring_items[num_items - 1], FALSE);
return menu;
}
static void
cpopup_coloring_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegmentColor color;
GimpGradientSegment *seg, *aseg;
gradient = gimp_context_get_gradient (gradient_editor->context);
if (! GTK_CHECK_MENU_ITEM (widget)->active)
return; /* Do nothing if the menu item is being deactivated */
color = (GimpGradientSegmentColor)
GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (widget)));
seg = gradient_editor->control_sel_l;
do
{
seg->color = color;
aseg = seg;
seg = seg->next;
}
while (aseg != gradient_editor->control_sel_r);
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
/*****/
static void
cpopup_split_midpoint_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *seg, *lseg, *rseg;
gradient = gimp_context_get_gradient (gradient_editor->context);
seg = gradient_editor->control_sel_l;
do
{
cpopup_split_midpoint (gradient, seg, &lseg, &rseg);
seg = rseg->next;
}
while (lseg != gradient_editor->control_sel_r);
gradient_editor->control_sel_r = rseg;
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
static void
cpopup_split_midpoint (GimpGradient *gradient,
GimpGradientSegment *lseg,
GimpGradientSegment **newl,
GimpGradientSegment **newr)
{
GimpRGB color;
GimpGradientSegment *newseg;
/* Get color at original segment's midpoint */
gimp_gradient_get_color_at (gradient, lseg->middle, &color);
/* Create a new segment and insert it in the list */
newseg = gimp_gradient_segment_new ();
newseg->prev = lseg;
newseg->next = lseg->next;
lseg->next = newseg;
if (newseg->next)
newseg->next->prev = newseg;
/* Set coordinates of new segment */
newseg->left = lseg->middle;
newseg->right = lseg->right;
newseg->middle = (newseg->left + newseg->right) / 2.0;
/* Set coordinates of original segment */
lseg->right = newseg->left;
lseg->middle = (lseg->left + lseg->right) / 2.0;
/* Set colors of both segments */
newseg->right_color = lseg->right_color;
lseg->right_color.r = newseg->left_color.r = color.r;
lseg->right_color.g = newseg->left_color.g = color.g;
lseg->right_color.b = newseg->left_color.b = color.b;
lseg->right_color.a = newseg->left_color.a = color.a;
/* Set parameters of new segment */
newseg->type = lseg->type;
newseg->color = lseg->color;
/* Done */
*newl = lseg;
*newr = newseg;
}
/*****/
static void
cpopup_split_uniform_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *scale;
GtkObject *scale_data;
/* Create dialog window */
dialog =
gimp_dialog_new ((gradient_editor->control_sel_l ==
gradient_editor->control_sel_r) ?
_("Split segment uniformly") :
_("Split segments uniformly"),
"gradient_segment_split_uniformly",
gimp_standard_help_func,
"dialogs/gradient_editor/split_segments_uniformly.html",
GTK_WIN_POS_MOUSE,
FALSE, TRUE, FALSE,
_("Split"), cpopup_split_uniform_split_callback,
gradient_editor, NULL, NULL, TRUE, FALSE,
_("Cancel"), cpopup_split_uniform_cancel_callback,
gradient_editor, NULL, NULL, FALSE, TRUE,
NULL);
/* The main vbox */
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
gtk_widget_show (vbox);
/* Instructions */
label = gtk_label_new (_("Please select the number of uniform parts"));
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
label =
gtk_label_new ((gradient_editor->control_sel_l ==
gradient_editor->control_sel_r) ?
_("in which you want to split the selected segment") :
_("in which you want to split the segments in the selection"));
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
/* Scale */
gradient_editor->split_parts = 2;
scale_data = gtk_adjustment_new (2.0, 2.0, 21.0, 1.0, 1.0, 1.0);
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
gtk_scale_set_digits (GTK_SCALE (scale), 0);
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 4);
gtk_widget_show (scale);
gtk_signal_connect (scale_data, "value_changed",
GTK_SIGNAL_FUNC (cpopup_split_uniform_scale_update),
gradient_editor);
/* Show! */
gtk_widget_show (dialog);
gtk_widget_set_sensitive (gradient_editor->shell, FALSE);
}
static void
cpopup_split_uniform_scale_update (GtkAdjustment *adjustment,
GradientEditor *gradient_editor)
{
gradient_editor->split_parts = ROUND (adjustment->value + 0.5);
}
static void
cpopup_split_uniform_split_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *seg, *aseg, *lseg, *rseg, *lsel;
gradient = gimp_context_get_gradient (gradient_editor->context);
gtk_widget_destroy (gtk_widget_get_toplevel (widget));
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
seg = gradient_editor->control_sel_l;
lsel = NULL;
do
{
aseg = seg;
cpopup_split_uniform (gradient, seg,
gradient_editor->split_parts, &lseg, &rseg);
if (seg == gradient_editor->control_sel_l)
lsel = lseg;
seg = rseg->next;
}
while (aseg != gradient_editor->control_sel_r);
gradient_editor->control_sel_l = lsel;
gradient_editor->control_sel_r = rseg;
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
static void
cpopup_split_uniform_cancel_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
gtk_widget_destroy (gtk_widget_get_toplevel (widget));
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
}
static void
cpopup_split_uniform (GimpGradient *gradient,
GimpGradientSegment *lseg,
gint parts,
GimpGradientSegment **newl,
GimpGradientSegment **newr)
{
GimpGradientSegment *seg, *prev, *tmp;
gdouble seg_len;
gint i;
seg_len = (lseg->right - lseg->left) / parts; /* Length of divisions */
seg = NULL;
prev = NULL;
tmp = NULL;
for (i = 0; i < parts; i++)
{
seg = gimp_gradient_segment_new ();
if (i == 0)
tmp = seg; /* Remember first segment */
seg->left = lseg->left + i * seg_len;
seg->right = lseg->left + (i + 1) * seg_len;
seg->middle = (seg->left + seg->right) / 2.0;
gimp_gradient_get_color_at (gradient, seg->left, &seg->left_color);
gimp_gradient_get_color_at (gradient, seg->right, &seg->right_color);
seg->type = lseg->type;
seg->color = lseg->color;
seg->prev = prev;
seg->next = NULL;
if (prev)
prev->next = seg;
prev = seg;
}
/* Fix edges */
tmp->left_color = lseg->left_color;
seg->right_color = lseg->right_color;
tmp->left = lseg->left;
seg->right = lseg->right; /* To squish accumulative error */
/* Link in list */
tmp->prev = lseg->prev;
seg->next = lseg->next;
if (lseg->prev)
lseg->prev->next = tmp;
else
gradient->segments = tmp; /* We are on leftmost segment */
if (lseg->next)
lseg->next->prev = seg;
gradient->last_visited = NULL; /* Force re-search */
/* Done */
*newl = tmp;
*newr = seg;
/* Delete old segment */
gimp_gradient_segment_free (lseg);
}
/*****/
static void
cpopup_delete_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *lseg, *rseg, *seg, *aseg, *next;
double join;
gradient = gimp_context_get_gradient (gradient_editor->context);
/* Remember segments to the left and to the right of the selection */
lseg = gradient_editor->control_sel_l->prev;
rseg = gradient_editor->control_sel_r->next;
/* Cannot delete all the segments in the gradient */
if ((lseg == NULL) && (rseg == NULL))
return;
/* Calculate join point */
join = (gradient_editor->control_sel_l->left +
gradient_editor->control_sel_r->right) / 2.0;
if (lseg == NULL)
join = 0.0;
else if (rseg == NULL)
join = 1.0;
/* Move segments */
if (lseg != NULL)
control_compress_range (lseg, lseg, lseg->left, join);
if (rseg != NULL)
control_compress_range (rseg, rseg, join, rseg->right);
/* Link */
if (lseg)
lseg->next = rseg;
if (rseg)
rseg->prev = lseg;
/* Delete old segments */
seg = gradient_editor->control_sel_l;
do
{
next = seg->next;
aseg = seg;
gimp_gradient_segment_free (seg);
seg = next;
}
while (aseg != gradient_editor->control_sel_r);
/* Change selection */
if (rseg)
{
gradient_editor->control_sel_l = rseg;
gradient_editor->control_sel_r = rseg;
}
else
{
gradient_editor->control_sel_l = lseg;
gradient_editor->control_sel_r = lseg;
}
if (lseg == NULL)
gradient->segments = rseg;
/* Done */
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
static void
cpopup_recenter_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *seg, *aseg;
gradient = gimp_context_get_gradient (gradient_editor->context);
seg = gradient_editor->control_sel_l;
do
{
seg->middle = (seg->left + seg->right) / 2.0;
aseg = seg;
seg = seg->next;
}
while (aseg != gradient_editor->control_sel_r);
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
static void
cpopup_redistribute_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *seg, *aseg;
gdouble left, right, seg_len;
gint num_segs;
gint i;
gradient = gimp_context_get_gradient (gradient_editor->context);
/* Count number of segments in selection */
num_segs = 0;
seg = gradient_editor->control_sel_l;
do
{
num_segs++;
aseg = seg;
seg = seg->next;
}
while (aseg != gradient_editor->control_sel_r);
/* Calculate new segment length */
left = gradient_editor->control_sel_l->left;
right = gradient_editor->control_sel_r->right;
seg_len = (right - left) / num_segs;
/* Redistribute */
seg = gradient_editor->control_sel_l;
for (i = 0; i < num_segs; i++)
{
seg->left = left + i * seg_len;
seg->right = left + (i + 1) * seg_len;
seg->middle = (seg->left + seg->right) / 2.0;
seg = seg->next;
}
/* Fix endpoints to squish accumulative error */
gradient_editor->control_sel_l->left = left;
gradient_editor->control_sel_r->right = right;
/* Done */
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
/***** Control popup -> selection options functions *****/
static GtkWidget *
cpopup_create_sel_ops_menu (GradientEditor *gradient_editor)
{
GtkWidget *menu;
GtkWidget *menuitem;
GtkAccelGroup *accel_group;
menu = gtk_menu_new ();
accel_group = gradient_editor->accel_group;
gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
/* Flip */
menuitem =
cpopup_create_menu_item_with_label ("", &gradient_editor->control_flip_label);
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_flip_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'F', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
/* Replicate */
menuitem =
cpopup_create_menu_item_with_label ("", &gradient_editor->control_replicate_label);
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_replicate_callback),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'M', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
/* Blend colors / opacity */
menuitem = gtk_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label (_("Blend endpoints' colors"));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_blend_colors),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gradient_editor->control_blend_colors_menu_item = menuitem;
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'B', 0,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
menuitem = gtk_menu_item_new_with_label (_("Blend endpoints' opacity"));
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (cpopup_blend_opacity),
gradient_editor);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_widget_show (menuitem);
gtk_widget_add_accelerator (menuitem, "activate",
accel_group,
'B', GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
gradient_editor->control_blend_opacity_menu_item = menuitem;
return menu;
}
static void
cpopup_flip_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
GimpGradientSegment *oseg, *oaseg;
GimpGradientSegment *seg, *prev, *tmp;
GimpGradientSegment *lseg, *rseg;
gdouble left, right;
gradient = gimp_context_get_gradient (gradient_editor->context);
left = gradient_editor->control_sel_l->left;
right = gradient_editor->control_sel_r->right;
/* Build flipped segments */
prev = NULL;
oseg = gradient_editor->control_sel_r;
tmp = NULL;
do
{
seg = gimp_gradient_segment_new ();
if (prev == NULL)
{
seg->left = left;
tmp = seg; /* Remember first segment */
}
else
seg->left = left + right - oseg->right;
seg->middle = left + right - oseg->middle;
seg->right = left + right - oseg->left;
seg->left_color = oseg->right_color;
seg->right_color = oseg->left_color;
switch (oseg->type)
{
case GRAD_SPHERE_INCREASING:
seg->type = GRAD_SPHERE_DECREASING;
break;
case GRAD_SPHERE_DECREASING:
seg->type = GRAD_SPHERE_INCREASING;
break;
default:
seg->type = oseg->type;
}
switch (oseg->color)
{
case GRAD_HSV_CCW:
seg->color = GRAD_HSV_CW;
break;
case GRAD_HSV_CW:
seg->color = GRAD_HSV_CCW;
break;
default:
seg->color = oseg->color;
}
seg->prev = prev;
seg->next = NULL;
if (prev)
prev->next = seg;
prev = seg;
oaseg = oseg;
oseg = oseg->prev; /* Move backwards! */
}
while (oaseg != gradient_editor->control_sel_l);
seg->right = right; /* Squish accumulative error */
/* Free old segments */
lseg = gradient_editor->control_sel_l->prev;
rseg = gradient_editor->control_sel_r->next;
oseg = gradient_editor->control_sel_l;
do
{
oaseg = oseg->next;
gimp_gradient_segment_free (oseg);
oseg = oaseg;
}
while (oaseg != rseg);
/* Link in new segments */
if (lseg)
lseg->next = tmp;
else
gradient->segments = tmp;
tmp->prev = lseg;
seg->next = rseg;
if (rseg)
rseg->prev = seg;
/* Reset selection */
gradient_editor->control_sel_l = tmp;
gradient_editor->control_sel_r = seg;
/* Done */
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
static void
cpopup_replicate_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *scale;
GtkObject *scale_data;
/* Create dialog window */
dialog =
gimp_dialog_new ((gradient_editor->control_sel_l ==
gradient_editor->control_sel_r) ?
_("Replicate segment") :
_("Replicate selection"),
"gradient_segment_replicate",
gimp_standard_help_func,
"dialogs/gradient_editor/replicate_segment.html",
GTK_WIN_POS_MOUSE,
FALSE, TRUE, FALSE,
_("Replicate"), cpopup_do_replicate_callback,
gradient_editor, NULL, NULL, FALSE, FALSE,
_("Cancel"), cpopup_replicate_cancel_callback,
gradient_editor, NULL, NULL, TRUE, TRUE,
NULL);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
gtk_widget_show (vbox);
/* Instructions */
label = gtk_label_new (_("Please select the number of times"));
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
label = gtk_label_new ((gradient_editor->control_sel_l ==
gradient_editor->control_sel_r) ?
_("you want to replicate the selected segment") :
_("you want to replicate the selection"));
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
/* Scale */
gradient_editor->replicate_times = 2;
scale_data = gtk_adjustment_new (2.0, 2.0, 21.0, 1.0, 1.0, 1.0);
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
gtk_scale_set_digits (GTK_SCALE (scale), 0);
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, TRUE, 4);
gtk_widget_show (scale);
gtk_signal_connect (scale_data, "value_changed",
GTK_SIGNAL_FUNC (cpopup_replicate_scale_update),
gradient_editor);
/* Show! */
gtk_widget_show (dialog);
gtk_widget_set_sensitive (gradient_editor->shell, FALSE);
}
static void
cpopup_replicate_scale_update (GtkAdjustment *adjustment,
GradientEditor *gradient_editor)
{
gradient_editor->replicate_times = (int) (adjustment->value + 0.5);
}
static void
cpopup_do_replicate_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
GimpGradient *gradient;
gdouble sel_left, sel_right, sel_len;
gdouble new_left;
gdouble factor;
GimpGradientSegment *prev, *seg, *tmp;
GimpGradientSegment *oseg, *oaseg;
GimpGradientSegment *lseg, *rseg;
gint i;
gradient = gimp_context_get_gradient (gradient_editor->context);
gtk_widget_destroy (gtk_widget_get_toplevel (widget));
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
/* Remember original parameters */
sel_left = gradient_editor->control_sel_l->left;
sel_right = gradient_editor->control_sel_r->right;
sel_len = sel_right - sel_left;
factor = 1.0 / gradient_editor->replicate_times;
/* Build replicated segments */
prev = NULL;
seg = NULL;
tmp = NULL;
for (i = 0; i < gradient_editor->replicate_times; i++)
{
/* Build one cycle */
new_left = sel_left + i * factor * sel_len;
oseg = gradient_editor->control_sel_l;
do
{
seg = gimp_gradient_segment_new ();
if (prev == NULL)
{
seg->left = sel_left;
tmp = seg; /* Remember first segment */
}
else
seg->left = new_left + factor * (oseg->left - sel_left);
seg->middle = new_left + factor * (oseg->middle - sel_left);
seg->right = new_left + factor * (oseg->right - sel_left);
seg->left_color = oseg->right_color;
seg->right_color = oseg->right_color;
seg->type = oseg->type;
seg->color = oseg->color;
seg->prev = prev;
seg->next = NULL;
if (prev)
prev->next = seg;
prev = seg;
oaseg = oseg;
oseg = oseg->next;
}
while (oaseg != gradient_editor->control_sel_r);
}
seg->right = sel_right; /* Squish accumulative error */
/* Free old segments */
lseg = gradient_editor->control_sel_l->prev;
rseg = gradient_editor->control_sel_r->next;
oseg = gradient_editor->control_sel_l;
do
{
oaseg = oseg->next;
gimp_gradient_segment_free (oseg);
oseg = oaseg;
}
while (oaseg != rseg);
/* Link in new segments */
if (lseg)
lseg->next = tmp;
else
gradient->segments = tmp;
tmp->prev = lseg;
seg->next = rseg;
if (rseg)
rseg->prev = seg;
/* Reset selection */
gradient_editor->control_sel_l = tmp;
gradient_editor->control_sel_r = seg;
/* Done */
gimp_data_dirty (GIMP_DATA (gradient));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT | GRAD_UPDATE_CONTROL);
}
static void
cpopup_replicate_cancel_callback (GtkWidget *widget,
GradientEditor *gradient_editor)
{
gtk_widget_destroy (gtk_widget_get_toplevel (widget));
gtk_widget_set_sensitive (gradient_editor->shell, TRUE);
}
static void
cpopup_blend_colors (GtkWidget *widget,
GradientEditor *gradient_editor)
{
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_l->left_color,
&gradient_editor->control_sel_r->right_color,
TRUE, FALSE);
gimp_data_dirty (GIMP_DATA (gimp_context_get_gradient (gradient_editor->context)));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
static void
cpopup_blend_opacity (GtkWidget *widget,
GradientEditor *gradient_editor)
{
cpopup_blend_endpoints (gradient_editor,
&gradient_editor->control_sel_l->left_color,
&gradient_editor->control_sel_r->right_color,
FALSE, TRUE);
gimp_data_dirty (GIMP_DATA (gimp_context_get_gradient (gradient_editor->context)));
ed_update_editor (gradient_editor, GRAD_UPDATE_GRADIENT);
}
/***** Main blend function *****/
static void
cpopup_blend_endpoints (GradientEditor *gradient_editor,
GimpRGB *rgb1,
GimpRGB *rgb2,
gboolean blend_colors,
gboolean blend_opacity)
{
GimpRGB d;
gdouble left, len;
GimpGradientSegment *seg;
GimpGradientSegment *aseg;
d.r = rgb2->r - rgb1->r;
d.g = rgb2->g - rgb1->g;
d.b = rgb2->b - rgb1->b;
d.a = rgb2->a - rgb1->a;
left = gradient_editor->control_sel_l->left;
len = gradient_editor->control_sel_r->right - left;
seg = gradient_editor->control_sel_l;
do
{
if (blend_colors)
{
seg->left_color.r = rgb1->r + (seg->left - left) / len * d.r;
seg->left_color.g = rgb1->g + (seg->left - left) / len * d.g;
seg->left_color.b = rgb1->b + (seg->left - left) / len * d.b;
seg->right_color.r = rgb1->r + (seg->right - left) / len * d.r;
seg->right_color.g = rgb1->g + (seg->right - left) / len * d.g;
seg->right_color.b = rgb1->b + (seg->right - left) / len * d.b;
}
if (blend_opacity)
{
seg->left_color.a = rgb1->a + (seg->left - left) / len * d.a;
seg->right_color.a = rgb1->a + (seg->right - left) / len * d.a;
}
aseg = seg;
seg = seg->next;
}
while (aseg != gradient_editor->control_sel_r);
}
/***** Segment functions *****/
static void
seg_get_closest_handle (GimpGradient *grad,
gdouble pos,
GimpGradientSegment **seg,
control_drag_mode_t *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;
}
}
}