home *** CD-ROM | disk | FTP | other *** search
- /* The GIMP -- an image manipulation program
- * Copyright (C) 1995 Spencer Kimball and Peter Mattis
- *
- * 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 PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
- #include "config.h"
-
- #include <stdlib.h>
-
- #include <gtk/gtk.h>
- #include <gdk/gdkkeysyms.h>
-
- #include "apptypes.h"
-
- #include "appenv.h"
- #include "gdisplay.h"
- #include "gimage_mask.h"
- #include "edit_selection.h"
- #include "floating_sel.h"
- #include "rect_select.h"
- #include "rect_selectP.h"
- #include "selection_options.h"
- #include "cursorutil.h"
-
- #include "libgimp/gimpunitmenu.h"
-
- #include "libgimp/gimpintl.h"
-
-
- #define STATUSBAR_SIZE 128
-
-
- /* the rectangular selection tool options */
- static SelectionOptions *rect_options = NULL;
-
- /* in gimp, ellipses are rectangular, too ;) */
- extern SelectionOptions *ellipse_options;
- extern void ellipse_select (GimpImage *gimage,
- gint x,
- gint y,
- gint w,
- gint h,
- SelectOps op,
- gboolean antialias,
- gboolean feather,
- gdouble feather_radius);
-
- static void selection_tool_update_op_state (RectSelect *rect_sel,
- gint x,
- gint y,
- gint state,
- GDisplay *gdisp);
-
- /*************************************/
- /* Rectangular selection apparatus */
-
- void
- rect_select (GimpImage *gimage,
- gint x,
- gint y,
- gint w,
- gint h,
- SelectOps op,
- gboolean feather,
- gdouble feather_radius)
- {
- Channel *new_mask;
-
- /* if applicable, replace the current selection */
- if (op == SELECTION_REPLACE)
- gimage_mask_clear (gimage);
- else
- gimage_mask_undo (gimage);
-
- /* if feathering for rect, make a new mask with the
- * rectangle and feather that with the old mask
- */
- if (feather)
- {
- new_mask = channel_new_mask (gimage, gimage->width, gimage->height);
- channel_combine_rect (new_mask, ADD, x, y, w, h);
- channel_feather (new_mask, gimage_get_mask (gimage),
- feather_radius,
- feather_radius,
- op, 0, 0);
- channel_delete (new_mask);
- }
- else if (op == SELECTION_INTERSECT)
- {
- new_mask = channel_new_mask (gimage, gimage->width, gimage->height);
- channel_combine_rect (new_mask, ADD, x, y, w, h);
- channel_combine_mask (gimage_get_mask (gimage), new_mask, op, 0, 0);
- channel_delete (new_mask);
- }
- else
- channel_combine_rect (gimage_get_mask (gimage), op, x, y, w, h);
- }
-
- void
- rect_select_button_press (Tool *tool,
- GdkEventButton *bevent,
- gpointer gdisp_ptr)
- {
- GDisplay *gdisp;
- RectSelect *rect_sel;
- gchar select_mode[STATUSBAR_SIZE];
- gint x, y;
- GimpUnit unit = GIMP_UNIT_PIXEL;
- gdouble unit_factor;
-
- gdisp = (GDisplay *) gdisp_ptr;
- rect_sel = (RectSelect *) tool->private;
-
- gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y, TRUE, 0);
-
- rect_sel->x = x;
- rect_sel->y = y;
- switch (tool->type)
- {
- case RECT_SELECT:
- rect_sel->fixed_size = rect_options->fixed_size;
- rect_sel->fixed_width = rect_options->fixed_width;
- rect_sel->fixed_height = rect_options->fixed_height;
- unit = rect_options->fixed_unit;
- break;
- case ELLIPSE_SELECT:
- rect_sel->fixed_size = ellipse_options->fixed_size;
- rect_sel->fixed_width = ellipse_options->fixed_width;
- rect_sel->fixed_height = ellipse_options->fixed_height;
- unit = ellipse_options->fixed_unit;
- break;
- default:
- break;
- }
-
- switch (unit)
- {
- case GIMP_UNIT_PIXEL:
- break;
- case GIMP_UNIT_PERCENT:
- rect_sel->fixed_width =
- gdisp->gimage->width * rect_sel->fixed_width / 100;
- rect_sel->fixed_height =
- gdisp->gimage->height * rect_sel->fixed_height / 100;
- break;
- default:
- unit_factor = gimp_unit_get_factor (unit);
- rect_sel->fixed_width =
- rect_sel->fixed_width * gdisp->gimage->xresolution / unit_factor;
- rect_sel->fixed_height =
- rect_sel->fixed_height * gdisp->gimage->yresolution / unit_factor;
- break;
- }
-
- rect_sel->fixed_width = MAX (1, rect_sel->fixed_width);
- rect_sel->fixed_height = MAX (1, rect_sel->fixed_height);
-
- rect_sel->w = 0;
- rect_sel->h = 0;
-
- rect_sel->center = FALSE;
-
- gdk_pointer_grab (gdisp->canvas->window, FALSE,
- GDK_POINTER_MOTION_HINT_MASK |
- GDK_BUTTON1_MOTION_MASK |
- GDK_BUTTON_RELEASE_MASK,
- NULL, NULL, bevent->time);
-
- tool->state = ACTIVE;
- tool->gdisp_ptr = gdisp_ptr;
-
- switch (rect_sel->op)
- {
- case SELECTION_MOVE_MASK:
- init_edit_selection (tool, gdisp_ptr, bevent, EDIT_MASK_TRANSLATE);
- return;
- case SELECTION_MOVE:
- init_edit_selection (tool, gdisp_ptr, bevent, EDIT_MASK_TO_LAYER_TRANSLATE);
- return;
- default:
- break;
- }
-
- /* initialize the statusbar display */
- rect_sel->context_id =
- gtk_statusbar_get_context_id (GTK_STATUSBAR (gdisp->statusbar), "selection");
- switch (rect_sel->op)
- {
- case SELECTION_ADD:
- g_snprintf (select_mode, STATUSBAR_SIZE, _("Selection: ADD"));
- break;
- case SELECTION_SUB:
- g_snprintf (select_mode, STATUSBAR_SIZE, _("Selection: SUBTRACT"));
- break;
- case SELECTION_INTERSECT:
- g_snprintf (select_mode, STATUSBAR_SIZE, _("Selection: INTERSECT"));
- break;
- case SELECTION_REPLACE:
- g_snprintf (select_mode, STATUSBAR_SIZE, _("Selection: REPLACE"));
- break;
- default:
- break;
- }
- gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar),
- rect_sel->context_id, select_mode);
-
- draw_core_start (rect_sel->core, gdisp->canvas->window, tool);
- }
-
- void
- rect_select_button_release (Tool *tool,
- GdkEventButton *bevent,
- gpointer gdisp_ptr)
- {
- RectSelect *rect_sel;
- GDisplay *gdisp;
- gint x1, y1;
- gint x2, y2;
- gint w, h;
-
- gdisp = (GDisplay *) gdisp_ptr;
- rect_sel = (RectSelect *) tool->private;
-
- gdk_pointer_ungrab (bevent->time);
- gdk_flush ();
-
- gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), rect_sel->context_id);
-
- draw_core_stop (rect_sel->core, tool);
- tool->state = INACTIVE;
-
- /* First take care of the case where the user "cancels" the action */
- if (! (bevent->state & GDK_BUTTON3_MASK))
- {
- x1 = (rect_sel->w < 0) ? rect_sel->x + rect_sel->w : rect_sel->x;
- y1 = (rect_sel->h < 0) ? rect_sel->y + rect_sel->h : rect_sel->y;
- w = (rect_sel->w < 0) ? -rect_sel->w : rect_sel->w;
- h = (rect_sel->h < 0) ? -rect_sel->h : rect_sel->h;
-
- if ((!w || !h) && !rect_sel->fixed_size)
- {
- /* If there is a floating selection, anchor it */
- if (gimage_floating_sel (gdisp->gimage))
- floating_sel_anchor (gimage_floating_sel (gdisp->gimage));
- /* Otherwise, clear the selection mask */
- else
- gimage_mask_clear (gdisp->gimage);
-
- gdisplays_flush ();
- return;
- }
-
- x2 = x1 + w;
- y2 = y1 + h;
-
- switch (tool->type)
- {
- case RECT_SELECT:
- rect_select (gdisp->gimage,
- x1, y1, (x2 - x1), (y2 - y1),
- rect_sel->op,
- rect_options->feather,
- rect_options->feather_radius);
- break;
-
- case ELLIPSE_SELECT:
- ellipse_select (gdisp->gimage,
- x1, y1, (x2 - x1), (y2 - y1),
- rect_sel->op,
- ellipse_options->antialias,
- ellipse_options->feather,
- ellipse_options->feather_radius);
- break;
- default:
- break;
- }
-
- /* show selection on all views */
- gdisplays_flush ();
- }
- }
-
- void
- rect_select_motion (Tool *tool,
- GdkEventMotion *mevent,
- gpointer gdisp_ptr)
- {
- RectSelect *rect_sel;
- GDisplay *gdisp;
- gchar size[STATUSBAR_SIZE];
- gint ox, oy;
- gint x, y;
- gint w, h, s;
- gint tw, th;
- gdouble ratio;
-
- gdisp = (GDisplay *) gdisp_ptr;
- rect_sel = (RectSelect *) tool->private;
-
- /* needed for immediate cursor update on modifier event */
- rect_sel->current_x = mevent->x;
- rect_sel->current_y = mevent->y;
-
- if (tool->state != ACTIVE)
- return;
-
- if (rect_sel->op == SELECTION_ANCHOR)
- {
- rect_sel->op = SELECTION_REPLACE;
-
- rect_select_cursor_update (tool, mevent, gdisp_ptr);
- }
-
- draw_core_pause (rect_sel->core, tool);
-
- /* Calculate starting point */
-
- if (rect_sel->center)
- {
- ox = rect_sel->x + rect_sel->w / 2;
- oy = rect_sel->y + rect_sel->h / 2;
- }
- else
- {
- ox = rect_sel->x;
- oy = rect_sel->y;
- }
-
- gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0);
- if (rect_sel->fixed_size)
- {
- if (mevent->state & GDK_SHIFT_MASK)
- {
- ratio = (double)(rect_sel->fixed_height /
- (double)rect_sel->fixed_width);
- tw = x - ox;
- th = y - oy;
- /*
- * This is probably an inefficient way to do it, but it gives
- * nicer, more predictable results than the original agorithm
- */
-
- if ((abs(th) < (ratio * abs(tw))) && (abs(tw) > (abs(th) / ratio)))
- {
- w = tw;
- h = (int)(tw * ratio);
- /* h should have the sign of th */
- if ((th < 0 && h > 0) || (th > 0 && h < 0))
- h = -h;
- }
- else
- {
- h = th;
- w = (int)(th / ratio);
- /* w should have the sign of tw */
- if ((tw < 0 && w > 0) || (tw > 0 && w < 0))
- w = -w;
- }
- }
- else
- {
- w = (x - ox > 0 ? rect_sel->fixed_width : -rect_sel->fixed_width);
- h = (y - oy > 0 ? rect_sel->fixed_height : -rect_sel->fixed_height);
- }
- }
- else
- {
- w = (x - ox);
- h = (y - oy);
- }
-
- /* If the shift key is down, then make the rectangle square (or ellipse circular) */
- if ((mevent->state & GDK_SHIFT_MASK) && !rect_sel->fixed_size)
- {
- s = MAX (abs (w), abs (h));
-
- if (w < 0)
- w = -s;
- else
- w = s;
-
- if (h < 0)
- h = -s;
- else
- h = s;
- }
-
- /* If the control key is down, create the selection from the center out */
- if (mevent->state & GDK_CONTROL_MASK)
- {
- if (rect_sel->fixed_size)
- {
- if (mevent->state & GDK_SHIFT_MASK)
- {
- rect_sel->x = ox - w;
- rect_sel->y = oy - h;
- rect_sel->w = w * 2;
- rect_sel->h = h * 2;
- }
- else
- {
- rect_sel->x = ox - w / 2;
- rect_sel->y = oy - h / 2;
- rect_sel->w = w;
- rect_sel->h = h;
- }
- }
- else
- {
- w = abs(w);
- h = abs(h);
-
- rect_sel->x = ox - w;
- rect_sel->y = oy - h;
- rect_sel->w = 2 * w + 1;
- rect_sel->h = 2 * h + 1;
- }
- rect_sel->center = TRUE;
- }
- else
- {
- rect_sel->x = ox;
- rect_sel->y = oy;
- rect_sel->w = w;
- rect_sel->h = h;
-
- rect_sel->center = FALSE;
- }
-
- gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), rect_sel->context_id);
- if (gdisp->dot_for_dot)
- {
- g_snprintf (size, STATUSBAR_SIZE, gdisp->cursor_format_str,
- _("Selection: "), abs(rect_sel->w), " x ", abs(rect_sel->h));
- }
- else /* show real world units */
- {
- gdouble unit_factor = gimp_unit_get_factor (gdisp->gimage->unit);
-
- g_snprintf (size, STATUSBAR_SIZE, gdisp->cursor_format_str,
- _("Selection: "),
- (gdouble) abs(rect_sel->w) * unit_factor /
- gdisp->gimage->xresolution,
- " x ",
- (gdouble) abs(rect_sel->h) * unit_factor /
- gdisp->gimage->yresolution);
- }
- gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), rect_sel->context_id,
- size);
-
- draw_core_resume (rect_sel->core, tool);
- }
-
- void
- rect_select_draw (Tool *tool)
- {
- GDisplay *gdisp;
- RectSelect *rect_sel;
- gint x1, y1;
- gint x2, y2;
-
- gdisp = (GDisplay *) tool->gdisp_ptr;
- rect_sel = (RectSelect *) tool->private;
-
- x1 = MIN (rect_sel->x, rect_sel->x + rect_sel->w);
- y1 = MIN (rect_sel->y, rect_sel->y + rect_sel->h);
- x2 = MAX (rect_sel->x, rect_sel->x + rect_sel->w);
- y2 = MAX (rect_sel->y, rect_sel->y + rect_sel->h);
-
- gdisplay_transform_coords (gdisp, x1, y1, &x1, &y1, 0);
- gdisplay_transform_coords (gdisp, x2, y2, &x2, &y2, 0);
-
- gdk_draw_rectangle (rect_sel->core->win,
- rect_sel->core->gc, 0,
- x1, y1, (x2 - x1), (y2 - y1));
- }
-
- static void
- selection_tool_update_op_state (RectSelect *rect_sel,
- gint x,
- gint y,
- gint state,
- GDisplay *gdisp)
- {
- Layer *layer;
- Layer *floating_sel;
- gint tx, ty;
-
- if (active_tool->state == ACTIVE)
- return;
-
- gdisplay_untransform_coords (gdisp, x, y, &tx, &ty, FALSE, FALSE);
-
- layer = gimage_pick_correlate_layer (gdisp->gimage, tx, ty);
- floating_sel = gimage_floating_sel (gdisp->gimage);
-
- if (state & GDK_MOD1_MASK &&
- !gimage_mask_is_empty (gdisp->gimage))
- {
- rect_sel->op = SELECTION_MOVE_MASK; /* move just the selection mask */
- }
- else if (!(state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) &&
- layer &&
- (layer == floating_sel ||
- (gdisplay_mask_value (gdisp, x, y) &&
- floating_sel == NULL)))
- {
- rect_sel->op = SELECTION_MOVE; /* move the selection */
- }
- else if ((state & GDK_SHIFT_MASK) &&
- !(state & GDK_CONTROL_MASK))
- {
- rect_sel->op = SELECTION_ADD; /* add to the selection */
- }
- else if ((state & GDK_CONTROL_MASK) &&
- !(state & GDK_SHIFT_MASK))
- {
- rect_sel->op = SELECTION_SUB; /* subtract from the selection */
- }
- else if ((state & GDK_CONTROL_MASK) &&
- (state & GDK_SHIFT_MASK))
- {
- rect_sel->op = SELECTION_INTERSECT; /* intersect with selection */
- }
- else if (floating_sel)
- {
- rect_sel->op = SELECTION_ANCHOR; /* anchor the selection */
- }
- else
- {
- rect_sel->op = SELECTION_REPLACE; /* replace the selection */
- }
- }
-
- void
- rect_select_oper_update (Tool *tool,
- GdkEventMotion *mevent,
- gpointer gdisp_ptr)
- {
- RectSelect *rect_sel;
-
- rect_sel = (RectSelect *) tool->private;
-
- rect_sel->current_x = mevent->x;
- rect_sel->current_y = mevent->y;
-
- selection_tool_update_op_state (rect_sel,
- rect_sel->current_x,
- rect_sel->current_y,
- mevent->state, gdisp_ptr);
- }
-
- void
- rect_select_modifier_update (Tool *tool,
- GdkEventKey *kevent,
- gpointer gdisp_ptr)
- {
- RectSelect *rect_sel;
- gint state;
-
- state = kevent->state;
-
- switch (kevent->keyval)
- {
- case GDK_Alt_L: case GDK_Alt_R:
- if (state & GDK_MOD1_MASK)
- state &= ~GDK_MOD1_MASK;
- else
- state |= GDK_MOD1_MASK;
- break;
-
- case GDK_Shift_L: case GDK_Shift_R:
- if (state & GDK_SHIFT_MASK)
- state &= ~GDK_SHIFT_MASK;
- else
- state |= GDK_SHIFT_MASK;
- break;
-
- case GDK_Control_L: case GDK_Control_R:
- if (state & GDK_CONTROL_MASK)
- state &= ~GDK_CONTROL_MASK;
- else
- state |= GDK_CONTROL_MASK;
- break;
- }
-
- rect_sel = (RectSelect *) tool->private;
- selection_tool_update_op_state (rect_sel,
- rect_sel->current_x,
- rect_sel->current_y,
- state, gdisp_ptr);
- }
-
- void
- rect_select_cursor_update (Tool *tool,
- GdkEventMotion *mevent,
- gpointer gdisp_ptr)
- {
- RectSelect *rect_sel;
- GDisplay *gdisp;
-
- rect_sel = (RectSelect *) tool->private;
- gdisp = (GDisplay *) gdisp_ptr;
-
- switch (rect_sel->op)
- {
- case SELECTION_ADD:
- gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE_CURSOR,
- tool->type,
- CURSOR_MODIFIER_PLUS,
- FALSE);
- break;
- case SELECTION_SUB:
- gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE_CURSOR,
- tool->type,
- CURSOR_MODIFIER_MINUS,
- FALSE);
- break;
- case SELECTION_INTERSECT:
- gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE_CURSOR,
- tool->type,
- CURSOR_MODIFIER_INTERSECT,
- FALSE);
- break;
- case SELECTION_REPLACE:
- gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE_CURSOR,
- tool->type,
- CURSOR_MODIFIER_NONE,
- FALSE);
- break;
- case SELECTION_MOVE_MASK:
- gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE_CURSOR,
- tool->type,
- CURSOR_MODIFIER_MOVE,
- FALSE);
- break;
- case SELECTION_MOVE:
- gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE_CURSOR,
- MOVE,
- CURSOR_MODIFIER_NONE,
- FALSE);
- break;
- case SELECTION_ANCHOR:
- gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE_CURSOR,
- tool->type,
- CURSOR_MODIFIER_ANCHOR,
- FALSE);
- break;
- }
- }
-
- void
- rect_select_control (Tool *tool,
- ToolAction action,
- gpointer gdisp_ptr)
- {
- RectSelect *rect_sel;
-
- rect_sel = (RectSelect *) tool->private;
-
- switch (action)
- {
- case PAUSE:
- draw_core_pause (rect_sel->core, tool);
- break;
-
- case RESUME:
- draw_core_resume (rect_sel->core, tool);
- break;
-
- case HALT:
- draw_core_stop (rect_sel->core, tool);
- break;
-
- default:
- break;
- }
- }
-
- static void
- rect_select_options_reset (void)
- {
- selection_options_reset (rect_options);
- }
-
- Tool *
- tools_new_rect_select (void)
- {
- Tool * tool;
- RectSelect * private;
-
- /* The tool options */
- if (! rect_options)
- {
- rect_options =
- selection_options_new (RECT_SELECT, rect_select_options_reset);
- tools_register (RECT_SELECT, (ToolOptions *) rect_options);
- }
-
- tool = tools_new_tool (RECT_SELECT);
- private = g_new0 (RectSelect, 1);
-
- private->core = draw_core_new (rect_select_draw);
- private->x = private->y = 0;
- private->w = private->h = 0;
- private->op = SELECTION_REPLACE;
-
- tool->private = (void *) private;
-
- tool->button_press_func = rect_select_button_press;
- tool->button_release_func = rect_select_button_release;
- tool->motion_func = rect_select_motion;
- tool->modifier_key_func = rect_select_modifier_update;
- tool->cursor_update_func = rect_select_cursor_update;
- tool->oper_update_func = rect_select_oper_update;
- tool->control_func = rect_select_control;
-
- return tool;
- }
-
- void
- tools_free_rect_select (Tool *tool)
- {
- RectSelect * rect_sel;
-
- rect_sel = (RectSelect *) tool->private;
-
- draw_core_free (rect_sel->core);
- g_free (rect_sel);
- }
-