home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / crop.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  42.8 KB  |  1,568 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <string.h>
  22.  
  23. #include <gtk/gtk.h>
  24. #include <gdk/gdkkeysyms.h>
  25.  
  26. #include "apptypes.h"
  27.  
  28. #include "appenv.h"
  29. #include "crop.h"
  30. #include "cursorutil.h"
  31. #include "draw_core.h"
  32. #include "drawable.h"
  33. #include "floating_sel.h"
  34. #include "gdisplay.h"
  35. #include "gimage_mask.h"
  36. #include "gimphelp.h"
  37. #include "gimpui.h"
  38. #include "info_dialog.h"
  39. #include "undo.h"
  40.  
  41. #include "libgimp/gimpsizeentry.h"
  42. #include "libgimp/gimpmath.h"
  43. #include "libgimp/gimpintl.h"
  44.  
  45. #define STATUSBAR_SIZE 128
  46.  
  47. /*  possible crop functions  */
  48. #define CREATING        0
  49. #define MOVING          1
  50. #define RESIZING_LEFT   2
  51. #define RESIZING_RIGHT  3
  52. #define CROPPING        4
  53.  
  54. /*  speed of key movement  */
  55. #define ARROW_VELOCITY 25
  56.  
  57. /*  the crop structures  */
  58.  
  59. typedef struct _Crop Crop;
  60.  
  61. struct _Crop
  62. {
  63.   DrawCore *core;       /*  Core select object          */
  64.  
  65.   gint      startx;     /*  starting x coord            */
  66.   gint      starty;     /*  starting y coord            */
  67.  
  68.   gint      lastx;      /*  previous x coord            */
  69.   gint      lasty;      /*  previous y coord            */
  70.  
  71.   gint      x1, y1;     /*  upper left hand coordinate  */
  72.   gint      x2, y2;     /*  lower right hand coords     */
  73.  
  74.   gint      srw, srh;   /*  width and height of corners */
  75.  
  76.   gint      tx1, ty1;   /*  transformed coords          */
  77.   gint      tx2, ty2;   /*                              */
  78.  
  79.   gint      function;   /*  moving or resizing          */
  80.   guint     context_id; /*  for the statusbar           */
  81. };
  82.  
  83. typedef struct _CropOptions CropOptions;
  84.  
  85. struct _CropOptions
  86. {
  87.   ToolOptions  tool_options;
  88.  
  89.   gboolean     layer_only;
  90.   gboolean     layer_only_d;
  91.   GtkWidget   *layer_only_w;
  92.  
  93.   gboolean     allow_enlarge;
  94.   gboolean     allow_enlarge_d;
  95.   GtkWidget   *allow_enlarge_w;
  96.  
  97.   CropType     type;
  98.   CropType     type_d;
  99.   GtkWidget   *type_w[2];
  100. };
  101.  
  102.  
  103. /*  the crop tool options  */
  104. static CropOptions *crop_options = NULL;
  105.  
  106. /*  the crop tool info dialog  */
  107. static InfoDialog  *crop_info = NULL;
  108.  
  109. static gdouble      orig_vals[2];
  110. static gdouble      size_vals[2];
  111.  
  112. static GtkWidget   *origin_sizeentry;
  113. static GtkWidget   *size_sizeentry;
  114.  
  115.  
  116. /*  Crop type functions  */
  117. static void crop_button_press       (Tool *, GdkEventButton *, gpointer);
  118. static void crop_button_release     (Tool *, GdkEventButton *, gpointer);
  119. static void crop_motion             (Tool *, GdkEventMotion *, gpointer);
  120. static void crop_cursor_update      (Tool *, GdkEventMotion *, gpointer);
  121. static void crop_control            (Tool *, ToolAction,       gpointer);
  122. static void crop_arrow_keys_func    (Tool *, GdkEventKey *,    gpointer);
  123. static void crop_modifier_key_func  (Tool *, GdkEventKey *,    gpointer);
  124.  
  125. /*  Crop helper functions  */
  126. static void crop_recalc             (Tool *, Crop *);
  127. static void crop_start              (Tool *, Crop *);
  128. static void crop_adjust_guides      (GImage *, int, int, int, int);
  129.  
  130. /*  Crop dialog functions  */
  131. static void crop_info_update        (Tool *);
  132. static void crop_info_create        (Tool *);
  133. static void crop_crop_callback      (GtkWidget *, gpointer);
  134. static void crop_resize_callback    (GtkWidget *, gpointer);
  135. static void crop_close_callback     (GtkWidget *, gpointer);
  136.  
  137. /* Crop area-select functions */ 
  138. typedef enum
  139. {
  140.   AUTO_CROP_NOTHING = 0,
  141.   AUTO_CROP_ALPHA   = 1,
  142.   AUTO_CROP_COLOR   = 2
  143. } AutoCropType;
  144.  
  145. typedef guchar       * (*GetColorFunc)    (GtkObject *, int, int);
  146. typedef AutoCropType   (*ColorsEqualFunc) (guchar *, guchar *, int);
  147.  
  148. static void   crop_selection_callback    (GtkWidget *, gpointer);
  149. static void   crop_automatic_callback    (GtkWidget *, gpointer);
  150. static AutoCropType crop_guess_bgcolor   (GtkObject *, GetColorFunc, 
  151.                       int, int, guchar *, int, int, int, int);
  152. static int   crop_colors_equal           (guchar *, guchar *, int);
  153. static int   crop_colors_alpha           (guchar *, guchar *, int);
  154.  
  155. /*  Crop dialog callback funtions  */
  156. static void   crop_orig_changed (GtkWidget *, gpointer);
  157. static void   crop_size_changed (GtkWidget *, gpointer);
  158.  
  159.  
  160. /*  Functions  */
  161.  
  162. static void
  163. crop_options_reset (void)
  164. {
  165.   CropOptions *options = crop_options;
  166.  
  167.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->layer_only_w),
  168.                 options->layer_only_d);
  169.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(options->allow_enlarge_w),
  170.                 options->allow_enlarge_d);
  171.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); 
  172. }
  173.  
  174. static CropOptions *
  175. crop_options_new (void)
  176. {
  177.   CropOptions *options;
  178.   GtkWidget *vbox;
  179.   GtkWidget *frame;
  180.  
  181.   /*  the new crop tool options structure  */
  182.   options = g_new (CropOptions, 1);
  183.   tool_options_init ((ToolOptions *) options,
  184.              _("Crop & Resize"),
  185.              crop_options_reset);
  186.   options->layer_only    = options->layer_only_d    = FALSE;
  187.   options->allow_enlarge = options->allow_enlarge_d = FALSE;
  188.   options->type          = options->type_d          = CROP_CROP;
  189.  
  190.   /*  the main vbox  */
  191.   vbox = options->tool_options.main_vbox;
  192.  
  193.   /*  layer toggle  */
  194.   options->layer_only_w =
  195.     gtk_check_button_new_with_label(_("Current Layer only"));
  196.   gtk_box_pack_start (GTK_BOX (vbox), options->layer_only_w,
  197.               FALSE, FALSE, 0);
  198.   gtk_signal_connect (GTK_OBJECT (options->layer_only_w), "toggled",
  199.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  200.               &options->layer_only);
  201.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->layer_only_w),
  202.                 options->layer_only_d);
  203.   gtk_widget_show (options->layer_only_w);
  204.  
  205.   /*  enlarge toggle  */
  206.   options->allow_enlarge_w = gtk_check_button_new_with_label (_("Allow Enlarging"));
  207.   gtk_box_pack_start (GTK_BOX (vbox), options->allow_enlarge_w,
  208.               FALSE, FALSE, 0);
  209.   gtk_signal_connect (GTK_OBJECT (options->allow_enlarge_w), "toggled",
  210.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  211.               &options->allow_enlarge);
  212.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->allow_enlarge_w),
  213.                 options->allow_enlarge_d);
  214.   gtk_widget_show (options->allow_enlarge_w);
  215.  
  216.   /*  tool toggle  */
  217.   frame = gimp_radio_group_new2 (TRUE, _("Tool Toggle"),
  218.                  gimp_radio_button_update,
  219.                  &options->type, (gpointer) options->type,
  220.  
  221.                  _("Crop"), (gpointer) CROP_CROP,
  222.                  &options->type_w[0],
  223.                  _("Resize"), (gpointer) RESIZE_CROP,
  224.                  &options->type_w[1],
  225.  
  226.                  NULL);
  227.  
  228.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  229.   gtk_widget_show (frame);
  230.  
  231.   return options;
  232. }
  233.  
  234. static void
  235. crop_button_press (Tool           *tool,
  236.            GdkEventButton *bevent,
  237.            gpointer        gdisp_ptr)
  238. {
  239.   Crop * crop;
  240.   GDisplay * gdisp;
  241.  
  242.   gdisp = (GDisplay *) gdisp_ptr;
  243.   crop = (Crop *) tool->private;
  244.  
  245.   if (tool->state == INACTIVE ||
  246.       gdisp_ptr != tool->gdisp_ptr)
  247.     {
  248.       crop->function = CREATING;
  249.     }
  250.   else
  251.     {
  252.       /*  If the cursor is in either the upper left or lower right boxes,
  253.       The new function will be to resize the current crop area        */
  254.       if (bevent->x == CLAMP (bevent->x, crop->x1, crop->x1 + crop->srw) &&
  255.       bevent->y == CLAMP (bevent->y, crop->y1, crop->y1 + crop->srh))
  256.     crop->function = RESIZING_LEFT;
  257.       else if (bevent->x == CLAMP (bevent->x, crop->x2 - crop->srw, crop->x2) &&
  258.            bevent->y == CLAMP (bevent->y, crop->y2 - crop->srh, crop->y2))
  259.     crop->function = RESIZING_RIGHT;
  260.  
  261.       /*  If the cursor is in either the upper right or lower left boxes,
  262.       The new function will be to translate the current crop area     */
  263.       else if  ((bevent->x == CLAMP (bevent->x,
  264.                      crop->x1, crop->x1 + crop->srw) &&
  265.          bevent->y == CLAMP (bevent->y,
  266.                      crop->y2 - crop->srh, crop->y2)) ||
  267.         (bevent->x == CLAMP (bevent->x,
  268.                      crop->x2 - crop->srw, crop->x2) &&
  269.          bevent->y == CLAMP (bevent->y,
  270.                      crop->y1, crop->y1 + crop->srh)))
  271.     crop->function = MOVING;
  272.  
  273.       /*  If the pointer is in the rectangular region, crop or resize it!  */
  274.       else if (bevent->x > crop->x1 && bevent->x < crop->x2 &&
  275.            bevent->y > crop->y1 && bevent->y < crop->y2)
  276.     crop->function = CROPPING;
  277.       /*  otherwise, the new function will be creating, since we want to start anew  */
  278.       else
  279.     crop->function = CREATING;
  280.     }
  281.  
  282.   if (crop->function == CREATING)
  283.     {
  284.       if (tool->state == ACTIVE)
  285.     draw_core_stop (crop->core, tool);
  286.  
  287.       tool->gdisp_ptr = gdisp_ptr;
  288.       tool->drawable = gimage_active_drawable (gdisp->gimage);
  289.  
  290.       gdisplay_untransform_coords (gdisp, bevent->x, bevent->y,
  291.                    &crop->tx1, &crop->ty1, TRUE, FALSE);
  292.       crop->tx2 = crop->tx1;
  293.       crop->ty2 = crop->ty1;
  294.  
  295.       crop_start (tool, crop);
  296.     }
  297.  
  298.   gdisplay_untransform_coords (gdisp, bevent->x, bevent->y,
  299.                    &crop->startx, &crop->starty, TRUE, FALSE);
  300.   crop->lastx = crop->startx;
  301.   crop->lasty = crop->starty;
  302.  
  303.   gdk_pointer_grab (gdisp->canvas->window, FALSE,
  304.             GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
  305.             GDK_BUTTON_RELEASE_MASK,
  306.             NULL, NULL, bevent->time);
  307.  
  308.   tool->state = ACTIVE;
  309. }
  310.  
  311. static void
  312. crop_button_release (Tool           *tool,
  313.              GdkEventButton *bevent,
  314.              gpointer        gdisp_ptr)
  315. {
  316.   Crop * crop;
  317.   GDisplay *gdisp;
  318.  
  319.   crop = (Crop *) tool->private;
  320.   gdisp = (GDisplay *) gdisp_ptr;
  321.  
  322.   gdk_pointer_ungrab (bevent->time);
  323.   gdk_flush ();
  324.  
  325.   gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), crop->context_id);
  326.  
  327.   if (! (bevent->state & GDK_BUTTON3_MASK))
  328.     {
  329.       if (crop->function == CROPPING)
  330.     {
  331.       if (crop_options->type == CROP_CROP)
  332.         crop_image (gdisp->gimage, crop->tx1, crop->ty1, crop->tx2, crop->ty2, 
  333.             crop_options->layer_only, TRUE);
  334.       else
  335.         crop_image (gdisp->gimage, crop->tx1, crop->ty1, crop->tx2, crop->ty2, 
  336.             crop_options->layer_only, FALSE);
  337.  
  338.       /*  Finish the tool  */
  339.       crop_close_callback (NULL, NULL);
  340.     }
  341.       else
  342.     crop_info_update (tool);
  343.     }
  344. }
  345.  
  346. static void
  347. crop_adjust_guides (GImage *gimage,
  348.                     int x1, int y1,
  349.                     int x2, int y2)
  350.  
  351. {
  352.   GList * glist;
  353.   Guide * guide;
  354.   gint remove_guide;
  355.  
  356.   for (glist = gimage->guides; glist; glist = g_list_next (glist))
  357.     {
  358.       guide = (Guide *) glist->data;
  359.       remove_guide = FALSE;
  360.  
  361.       switch (guide->orientation)
  362.     {
  363.     case ORIENTATION_HORIZONTAL:
  364.       if ((guide->position < y1) ||(guide->position > y2))
  365.         remove_guide = TRUE;
  366.       break;
  367.  
  368.     case ORIENTATION_VERTICAL:
  369.       if ((guide->position < x1) ||(guide->position > x2))
  370.         remove_guide = TRUE;
  371.       break;
  372.     }
  373.     
  374.       /* edit the guide */
  375.       gdisplays_expose_guide (gimage, guide);
  376.       gdisplays_flush();
  377.  
  378.       if (remove_guide)
  379.     {
  380.       guide->position = -1;
  381.           guide = NULL;
  382.     }
  383.       else
  384.     {
  385.       if (guide->orientation == ORIENTATION_HORIZONTAL)
  386.         {
  387.           guide->position -= y1 ;
  388.         }
  389.       else
  390.         {
  391.           guide->position -= x1;
  392.         }
  393.     }
  394.     }
  395. }
  396.          
  397.  
  398. static void
  399. crop_motion (Tool           *tool,
  400.          GdkEventMotion *mevent,
  401.          gpointer        gdisp_ptr)
  402. {
  403.   Crop * crop;
  404.   GDisplay * gdisp;
  405.   Layer * layer;
  406.   int x1, y1, x2, y2;
  407.   int curx, cury;
  408.   int inc_x, inc_y;
  409.   gchar size[STATUSBAR_SIZE];
  410.   int min_x, min_y, max_x, max_y; 
  411.   crop = (Crop *) tool->private;
  412.   gdisp = (GDisplay *) gdisp_ptr;
  413.  
  414.   /*  This is the only case when the motion events should be ignored--
  415.       we're just waiting for the button release event to crop the image  */
  416.   if (crop->function == CROPPING)
  417.     return;
  418.  
  419.   gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &curx, &cury, TRUE, FALSE);
  420.   x1 = crop->startx;
  421.   y1 = crop->starty;
  422.   x2 = curx;
  423.   y2 = cury;
  424.  
  425.   inc_x = (x2 - x1);
  426.   inc_y = (y2 - y1);
  427.  
  428.   /*  If there have been no changes... return  */
  429.   if (crop->lastx == curx && crop->lasty == cury)
  430.     return;
  431.  
  432.   draw_core_pause (crop->core, tool);
  433.   
  434.   if (crop_options->layer_only)
  435.     {
  436.       layer = (gdisp->gimage)->active_layer;
  437.       drawable_offsets (GIMP_DRAWABLE(layer), &min_x, &min_y);
  438.       max_x  = drawable_width (GIMP_DRAWABLE(layer)) + min_x;
  439.       max_y = drawable_height (GIMP_DRAWABLE(layer)) + min_y;
  440.     }
  441.   else
  442.     {
  443.       min_x = min_y = 0;
  444.       max_x = gdisp->gimage->width;
  445.       max_y = gdisp->gimage->height;
  446.     }
  447.  
  448.   switch (crop->function)
  449.     {
  450.     case CREATING :
  451.       if (!crop_options->allow_enlarge)
  452.     {
  453.       x1 = CLAMP (x1, min_x, max_x);
  454.       y1 = CLAMP (y1, min_y, max_y);
  455.       x2 = CLAMP (x2, min_x, max_x);
  456.       y2 = CLAMP (y2, min_y, max_y);
  457.     }
  458.       break;
  459.  
  460.     case RESIZING_LEFT :
  461.       x1 = crop->tx1 + inc_x;
  462.       y1 = crop->ty1 + inc_y;
  463.       if (!crop_options->allow_enlarge)
  464.     {
  465.       x1 = CLAMP (x1, min_x, max_x);
  466.       y1 = CLAMP (y1, min_y, max_y);
  467.     }
  468.       x2 = MAX (x1, crop->tx2);
  469.       y2 = MAX (y1, crop->ty2);
  470.       crop->startx = curx;
  471.       crop->starty = cury;
  472.       break;
  473.  
  474.     case RESIZING_RIGHT :
  475.       x2 = crop->tx2 + inc_x;
  476.       y2 = crop->ty2 + inc_y;
  477.       if (!crop_options->allow_enlarge)
  478.     {
  479.       x2 = CLAMP (x2, min_x, max_x);
  480.       y2 = CLAMP (y2, min_y, max_y);
  481.     }
  482.       x1 = MIN (crop->tx1, x2);
  483.       y1 = MIN (crop->ty1, y2);
  484.       crop->startx = curx;
  485.       crop->starty = cury;
  486.       break;
  487.  
  488.     case MOVING :
  489.       if (!crop_options->allow_enlarge)
  490.     {
  491.       inc_x = CLAMP (inc_x, min_x - crop->tx1, max_x - crop->tx2);
  492.       inc_y = CLAMP (inc_y, min_y - crop->ty1, max_y - crop->ty2);
  493.     }
  494.       x1 = crop->tx1 + inc_x;
  495.       x2 = crop->tx2 + inc_x;
  496.       y1 = crop->ty1 + inc_y;
  497.       y2 = crop->ty2 + inc_y;
  498.       crop->startx = curx;
  499.       crop->starty = cury;
  500.       break;
  501.     }
  502.  
  503.   /*  make sure that the coords are in bounds  */
  504.   crop->tx1 = MIN (x1, x2);
  505.   crop->ty1 = MIN (y1, y2);
  506.   crop->tx2 = MAX (x1, x2);
  507.   crop->ty2 = MAX (y1, y2);
  508.  
  509.   crop->lastx = curx;
  510.   crop->lasty = cury;
  511.  
  512.   /*  recalculate the coordinates for crop_draw based on the new values  */
  513.   crop_recalc (tool, crop);
  514.  
  515.   if (crop->function == CREATING || 
  516.       crop->function == RESIZING_LEFT || crop->function == RESIZING_RIGHT)
  517.     {
  518.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), crop->context_id);
  519.       if (gdisp->dot_for_dot)
  520.     {
  521.       g_snprintf (size, STATUSBAR_SIZE, gdisp->cursor_format_str,
  522.               _("Crop: "), 
  523.               (crop->tx2 - crop->tx1), " x ", (crop->ty2 - crop->ty1));
  524.     }
  525.       else /* show real world units */
  526.     {
  527.       gdouble unit_factor = gimp_unit_get_factor (gdisp->gimage->unit);
  528.       
  529.       g_snprintf (size, STATUSBAR_SIZE, gdisp->cursor_format_str,
  530.               _("Crop: "), 
  531.               (gdouble) (crop->tx2 - crop->tx1) * unit_factor /
  532.               gdisp->gimage->xresolution,
  533.               " x ",
  534.               (gdouble) (crop->ty2 - crop->ty1) * unit_factor /
  535.               gdisp->gimage->yresolution);
  536.     }
  537.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), crop->context_id,
  538.               size);
  539.     }
  540.   
  541.   draw_core_resume (crop->core, tool);
  542. }
  543.  
  544. static void
  545. crop_cursor_update (Tool           *tool,
  546.             GdkEventMotion *mevent,
  547.             gpointer        gdisp_ptr)
  548. {
  549.   GDisplay *gdisp;
  550.   Crop     *crop;
  551.  
  552.   GdkCursorType ctype      = GIMP_MOUSE_CURSOR;
  553.   CursorModifier cmodifier = CURSOR_MODIFIER_NONE;
  554.  
  555.   gdisp = (GDisplay *) gdisp_ptr;
  556.   crop = (Crop *) tool->private;
  557.  
  558.   if (tool->state == INACTIVE ||
  559.       (tool->state == ACTIVE && tool->gdisp_ptr != gdisp_ptr))
  560.     {
  561.       ctype = GIMP_CROSSHAIR_SMALL_CURSOR;
  562.     }
  563.   else if (mevent->x == CLAMP (mevent->x, crop->x1, crop->x1 + crop->srw) &&
  564.        mevent->y == CLAMP (mevent->y, crop->y1, crop->y1 + crop->srh))
  565.     {
  566.       cmodifier = CURSOR_MODIFIER_RESIZE;
  567.     }
  568.   else if (mevent->x == CLAMP (mevent->x, crop->x2 - crop->srw, crop->x2) &&
  569.        mevent->y == CLAMP (mevent->y, crop->y2 - crop->srh, crop->y2))
  570.     {
  571.       cmodifier = CURSOR_MODIFIER_RESIZE;
  572.     }
  573.   else if  (mevent->x == CLAMP (mevent->x, crop->x1, crop->x1 + crop->srw) &&
  574.         mevent->y == CLAMP (mevent->y, crop->y2 - crop->srh, crop->y2))
  575.     {
  576.       cmodifier = CURSOR_MODIFIER_MOVE;
  577.     }
  578.   else if  (mevent->x == CLAMP (mevent->x, crop->x2 - crop->srw, crop->x2) &&
  579.         mevent->y == CLAMP (mevent->y, crop->y1, crop->y1 + crop->srh))
  580.     {
  581.       cmodifier = CURSOR_MODIFIER_MOVE;
  582.     }
  583.   else if (! (mevent->x > crop->x1 && mevent->x < crop->x2 &&
  584.           mevent->y > crop->y1 && mevent->y < crop->y2))
  585.     {
  586.       ctype = GIMP_CROSSHAIR_SMALL_CURSOR;
  587.     }
  588.  
  589.   gdisplay_install_tool_cursor (gdisp, ctype,
  590.                 CROP,
  591.                 cmodifier,
  592.                 crop_options->type != CROP_CROP);
  593. }
  594.  
  595. static void
  596. crop_arrow_keys_func (Tool        *tool,
  597.               GdkEventKey *kevent,
  598.               gpointer     gdisp_ptr)
  599. {
  600.   GDisplay *gdisp;
  601.   Layer    *layer;
  602.   Crop     *crop;
  603.   gint inc_x, inc_y;
  604.   gint min_x, min_y;
  605.   gint max_x, max_y;
  606.  
  607.   gdisp = (GDisplay *) gdisp_ptr;
  608.  
  609.   if (tool->state == ACTIVE)
  610.     {
  611.       crop = (Crop *) tool->private;
  612.       inc_x = inc_y = 0;
  613.  
  614.       switch (kevent->keyval)
  615.     {
  616.     case GDK_Up    : inc_y = -1; break;
  617.     case GDK_Left  : inc_x = -1; break;
  618.     case GDK_Right : inc_x =  1; break;
  619.     case GDK_Down  : inc_y =  1; break;
  620.     }
  621.  
  622.       /*  If the shift key is down, move by an accelerated increment  */
  623.       if (kevent->state & GDK_SHIFT_MASK)
  624.     {
  625.       inc_y *= ARROW_VELOCITY;
  626.       inc_x *= ARROW_VELOCITY;
  627.     }
  628.  
  629.       draw_core_pause (crop->core, tool);
  630.  
  631.       if (crop_options->layer_only)
  632.     {
  633.       layer = (gdisp->gimage)->active_layer;
  634.       drawable_offsets (GIMP_DRAWABLE (layer), &min_x, &min_y);
  635.       max_x  = drawable_width (GIMP_DRAWABLE (layer)) + min_x;
  636.       max_y = drawable_height (GIMP_DRAWABLE (layer)) + min_y;
  637.     }
  638.       else
  639.     {
  640.       min_x = min_y = 0;
  641.       max_x = gdisp->gimage->width;
  642.       max_y = gdisp->gimage->height;
  643.     }
  644.  
  645.       if (kevent->state & GDK_CONTROL_MASK)  /* RESIZING */
  646.     {
  647.       crop->tx2 = crop->tx2 + inc_x;
  648.       crop->ty2 = crop->ty2 + inc_y;
  649.       if (!crop_options->allow_enlarge)
  650.         {
  651.           crop->tx2 = CLAMP (crop->tx2, min_x, max_x);
  652.           crop->ty2 = CLAMP (crop->ty2, min_y, max_y);
  653.         }
  654.       crop->tx1 = MIN (crop->tx1, crop->tx2);
  655.       crop->ty1 = MIN (crop->ty1, crop->ty2);
  656.     }
  657.       else
  658.     {
  659.       if (!crop_options->allow_enlarge)
  660.         {      
  661.           inc_x = CLAMP (inc_x,
  662.                  -crop->tx1, gdisp->gimage->width - crop->tx2);
  663.           inc_y = CLAMP (inc_y,
  664.                  -crop->ty1, gdisp->gimage->height - crop->ty2);
  665.         }
  666.       crop->tx1 += inc_x;
  667.       crop->tx2 += inc_x;
  668.       crop->ty1 += inc_y;
  669.       crop->ty2 += inc_y;
  670.     }
  671.  
  672.       crop_recalc (tool, crop);
  673.       draw_core_resume (crop->core, tool);
  674.     }
  675. }
  676.  
  677. static void
  678. crop_modifier_key_func (Tool        *tool,
  679.             GdkEventKey *kevent,
  680.             gpointer     gdisp_ptr)
  681. {
  682.   switch (kevent->keyval)
  683.     {
  684.     case GDK_Alt_L: case GDK_Alt_R:
  685.       gtk_toggle_button_set_active
  686.     (GTK_TOGGLE_BUTTON (crop_options->allow_enlarge_w),
  687.      !crop_options->allow_enlarge);
  688.       break;
  689.     case GDK_Shift_L: case GDK_Shift_R:
  690.       break;
  691.     case GDK_Control_L: case GDK_Control_R:
  692.       if (crop_options->type == CROP_CROP)
  693.     gtk_toggle_button_set_active
  694.       (GTK_TOGGLE_BUTTON (crop_options->type_w[RESIZE_CROP]), TRUE);
  695.       else
  696.     gtk_toggle_button_set_active
  697.       (GTK_TOGGLE_BUTTON (crop_options->type_w[CROP_CROP]), TRUE);
  698.       break;
  699.     }
  700. }
  701.  
  702. static void
  703. crop_control (Tool       *tool,
  704.           ToolAction  action,
  705.           gpointer    gdisp_ptr)
  706. {
  707.   Crop *crop;
  708.  
  709.   crop = (Crop *) tool->private;
  710.  
  711.   switch (action)
  712.     {
  713.     case PAUSE:
  714.       draw_core_pause (crop->core, tool);
  715.       break;
  716.  
  717.     case RESUME:
  718.       crop_recalc (tool, crop);
  719.       draw_core_resume (crop->core, tool);
  720.       break;
  721.  
  722.     case HALT:
  723.       crop_close_callback (NULL, NULL);
  724.       break;
  725.  
  726.     default:
  727.       break;
  728.     }
  729. }
  730.  
  731. void
  732. crop_draw (Tool *tool)
  733. {
  734.   Crop     *crop;
  735.   GDisplay *gdisp;
  736.  
  737.   gdisp = (GDisplay *) tool->gdisp_ptr;
  738.   crop = (Crop *) tool->private;
  739.  
  740. #define SRW 10
  741. #define SRH 10
  742.  
  743.   gdk_draw_line (crop->core->win, crop->core->gc,
  744.          crop->x1, crop->y1, gdisp->disp_width, crop->y1);
  745.   gdk_draw_line (crop->core->win, crop->core->gc,
  746.          crop->x1, crop->y1, crop->x1, gdisp->disp_height);
  747.   gdk_draw_line (crop->core->win, crop->core->gc,
  748.          crop->x2, crop->y2, 0, crop->y2);
  749.   gdk_draw_line (crop->core->win, crop->core->gc,
  750.          crop->x2, crop->y2, crop->x2, 0);
  751.  
  752.   crop->srw = ((crop->x2 - crop->x1) < SRW) ? (crop->x2 - crop->x1) : SRW;
  753.   crop->srh = ((crop->y2 - crop->y1) < SRH) ? (crop->y2 - crop->y1) : SRH;
  754.  
  755.   gdk_draw_rectangle (crop->core->win, crop->core->gc, 1,
  756.               crop->x1, crop->y1, crop->srw, crop->srh);
  757.   gdk_draw_rectangle (crop->core->win, crop->core->gc, 1,
  758.               crop->x2 - crop->srw, crop->y2-crop->srh, crop->srw, crop->srh);
  759.   gdk_draw_rectangle (crop->core->win, crop->core->gc, 1,
  760.               crop->x2 - crop->srw, crop->y1, crop->srw, crop->srh);
  761.   gdk_draw_rectangle (crop->core->win, crop->core->gc, 1,
  762.               crop->x1, crop->y2-crop->srh, crop->srw, crop->srh);
  763.  
  764. #undef SRW
  765. #undef SRH
  766.  
  767.   crop_info_update (tool);
  768. }
  769.  
  770. Tool *
  771. tools_new_crop (void)
  772. {
  773.   Tool *tool;
  774.   Crop *private;
  775.  
  776.   /*  The tool options  */
  777.   if (! crop_options)
  778.     {
  779.       crop_options = crop_options_new ();
  780.       tools_register (CROP, (ToolOptions *) crop_options);
  781.     }
  782.  
  783.   tool = tools_new_tool (CROP);
  784.   private = g_new0 (Crop, 1);
  785.  
  786.   private->core = draw_core_new (crop_draw);
  787.   private->startx = private->starty = 0;
  788.   private->function = CREATING;
  789.  
  790.   tool->private = (void *) private;
  791.  
  792.   tool->preserve = FALSE;  /* XXX Check me */
  793.  
  794.   tool->button_press_func   = crop_button_press;
  795.   tool->button_release_func = crop_button_release;
  796.   tool->motion_func         = crop_motion;
  797.   tool->arrow_keys_func     = crop_arrow_keys_func;
  798.   tool->modifier_key_func   = crop_modifier_key_func;
  799.   tool->cursor_update_func  = crop_cursor_update;
  800.   tool->control_func        = crop_control;
  801.  
  802.   return tool;
  803. }
  804.  
  805. void
  806. tools_free_crop (Tool *tool)
  807. {
  808.   Crop *crop;
  809.  
  810.   crop = (Crop *) tool->private;
  811.  
  812.   if (tool->state == ACTIVE)
  813.     draw_core_stop (crop->core, tool);
  814.  
  815.   if (crop_info)
  816.     crop_close_callback (NULL, NULL);
  817.  
  818.   draw_core_free (crop->core);
  819.   g_free (crop);
  820. }
  821.  
  822. void
  823. crop_image (GImage   *gimage,
  824.         gint      x1,
  825.         gint      y1,
  826.         gint      x2,
  827.         gint      y2,
  828.         gboolean  layer_only,
  829.         gboolean  crop_layers)
  830. {
  831.   Layer   *layer;
  832.   Layer   *floating_layer;
  833.   Channel *channel;
  834.   GList   *guide_list_ptr;
  835.   GSList  *list;
  836.   gint width, height;
  837.   gint lx1, ly1, lx2, ly2;
  838.   gint off_x, off_y;
  839.   gint doff_x, doff_y;
  840.  
  841.   width = x2 - x1;
  842.   height = y2 - y1;
  843.  
  844.   /*  Make sure new width and height are non-zero  */
  845.   if (width && height)
  846.     {
  847.       gimp_add_busy_cursors ();
  848.  
  849.       if (layer_only)
  850.     {
  851.       undo_push_group_start (gimage, LAYER_RESIZE_UNDO);
  852.  
  853.       layer = gimage->active_layer;
  854.  
  855.       if (layer_is_floating_sel (layer))
  856.         floating_sel_relax (layer, TRUE);
  857.  
  858.       drawable_offsets (GIMP_DRAWABLE (layer), &doff_x, &doff_y);
  859.  
  860.       off_x = (doff_x - x1);
  861.       off_y = (doff_y - y1);
  862.  
  863.       layer_resize (layer, width, height, off_x, off_y);
  864.  
  865.       if (layer_is_floating_sel (layer))
  866.         floating_sel_rigor (layer, TRUE);
  867.  
  868.       undo_push_group_end (gimage);
  869.     }
  870.       else
  871.     {
  872.       floating_layer = gimage_floating_sel (gimage);
  873.  
  874.       undo_push_group_start (gimage, CROP_UNDO);
  875.  
  876.       /*  relax the floating layer  */
  877.       if (floating_layer)
  878.         floating_sel_relax (floating_layer, TRUE);
  879.  
  880.       /*  Push the image size to the stack  */
  881.       undo_push_gimage_mod (gimage);
  882.  
  883.       /*  Set the new width and height  */
  884.       gimage->width = width;
  885.       gimage->height = height;
  886.  
  887.       /*  Resize all channels  */
  888.       list = gimage->channels;
  889.       while (list)
  890.         {
  891.           channel = (Channel *) list->data;
  892.           channel_resize (channel, width, height, -x1, -y1);
  893.           list = g_slist_next (list);
  894.         }
  895.  
  896.       /*  Don't forget the selection mask!  */
  897.       channel_resize (gimage->selection_mask, width, height, -x1, -y1);
  898.       gimage_mask_invalidate (gimage);
  899.  
  900.       /*  crop all layers  */
  901.       list = gimage->layers;
  902.       while (list)
  903.         {
  904.           GSList *next;
  905.  
  906.           layer = (Layer *) list->data;
  907.           next = g_slist_next (list);
  908.  
  909.           layer_translate (layer, -x1, -y1);
  910.  
  911.           drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
  912.  
  913.           if (crop_layers)
  914.         {
  915.           drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
  916.  
  917.           lx1 = CLAMP (off_x, 0, gimage->width);
  918.           ly1 = CLAMP (off_y, 0, gimage->height);
  919.           lx2 = CLAMP ((drawable_width (GIMP_DRAWABLE (layer)) + off_x),
  920.                    0, gimage->width);
  921.           ly2 = CLAMP ((drawable_height (GIMP_DRAWABLE (layer)) + off_y),
  922.                    0, gimage->height);
  923.           width = lx2 - lx1;
  924.           height = ly2 - ly1;
  925.  
  926.           if (width && height)
  927.             layer_resize (layer, width, height,
  928.                   -(lx1 - off_x),
  929.                   -(ly1 - off_y));
  930.           else
  931.             gimage_remove_layer (gimage, layer);
  932.         }
  933.  
  934.           list = next;
  935.         }
  936.  
  937.       /*  Make sure the projection matches the gimage size  */
  938.       gimage_projection_realloc (gimage);
  939.  
  940.       /*  rigor the floating layer  */
  941.       if (floating_layer)
  942.         floating_sel_rigor (floating_layer, TRUE);
  943.  
  944.       guide_list_ptr = gimage->guides;
  945.       while ( guide_list_ptr != NULL)
  946.         {
  947.           undo_push_guide (gimage, (Guide *)guide_list_ptr->data);
  948.           guide_list_ptr = guide_list_ptr->next;
  949.         }
  950.       undo_push_group_end (gimage);
  951.   
  952.       /* Adjust any guides we might have laying about */
  953.       crop_adjust_guides (gimage, x1, y1, x2, y2); 
  954.  
  955.       /*  shrink wrap and update all views  */
  956.       channel_invalidate_previews (gimage);
  957.       layer_invalidate_previews (gimage);
  958.       gimage_invalidate_preview (gimage);
  959.       gdisplays_update_full (gimage);
  960.       gdisplays_shrink_wrap (gimage);
  961.     }
  962.       gimp_remove_busy_cursors (NULL);
  963.       gdisplays_flush ();
  964.     }
  965. }
  966.  
  967. static void
  968. crop_recalc (Tool *tool,
  969.          Crop *crop)
  970. {
  971.   GDisplay *gdisp;
  972.  
  973.   gdisp = (GDisplay *) tool->gdisp_ptr;
  974.  
  975.   gdisplay_transform_coords (gdisp, crop->tx1, crop->ty1,
  976.                  &crop->x1, &crop->y1, FALSE);
  977.   gdisplay_transform_coords (gdisp, crop->tx2, crop->ty2,
  978.                  &crop->x2, &crop->y2, FALSE);
  979. }
  980.  
  981. static void
  982. crop_start (Tool *tool,
  983.         Crop *crop)
  984. {
  985.   static GDisplay *old_gdisp = NULL;
  986.   GDisplay        *gdisp;
  987.  
  988.   gdisp = (GDisplay *) tool->gdisp_ptr;
  989.  
  990.   crop_recalc (tool, crop);
  991.  
  992.   if (! crop_info)
  993.     crop_info_create (tool);
  994.  
  995.   gtk_signal_handler_block_by_data (GTK_OBJECT (origin_sizeentry), crop_info);
  996.   gtk_signal_handler_block_by_data (GTK_OBJECT (size_sizeentry), crop_info);
  997.  
  998.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (origin_sizeentry), 0,
  999.                   gdisp->gimage->xresolution, FALSE);
  1000.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (origin_sizeentry), 1,
  1001.                   gdisp->gimage->yresolution, FALSE);
  1002.  
  1003.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (origin_sizeentry), 0,
  1004.                 0, gdisp->gimage->width);
  1005.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (origin_sizeentry), 1,
  1006.                 0, gdisp->gimage->height);
  1007.       
  1008.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (size_sizeentry), 0,
  1009.                   gdisp->gimage->xresolution, FALSE);
  1010.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (size_sizeentry), 1,
  1011.                   gdisp->gimage->yresolution, FALSE);
  1012.  
  1013.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (size_sizeentry), 0,
  1014.                 0, gdisp->gimage->width);
  1015.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (size_sizeentry), 1,
  1016.                 0, gdisp->gimage->height);
  1017.  
  1018.   if (old_gdisp != gdisp)
  1019.     {
  1020.       gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (origin_sizeentry),
  1021.                 gdisp->gimage->unit) ;
  1022.       gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (size_sizeentry),
  1023.                 gdisp->gimage->unit);
  1024.  
  1025.       if (gdisp->dot_for_dot)
  1026.     {
  1027.       gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (origin_sizeentry),
  1028.                     GIMP_UNIT_PIXEL);
  1029.       gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (size_sizeentry),
  1030.                     GIMP_UNIT_PIXEL);
  1031.     }
  1032.     }
  1033.  
  1034.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (size_sizeentry), crop_info);
  1035.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (origin_sizeentry), crop_info);
  1036.  
  1037.   old_gdisp = gdisp;
  1038.  
  1039.   /* initialize the statusbar display */
  1040.   crop->context_id =
  1041.     gtk_statusbar_get_context_id (GTK_STATUSBAR (gdisp->statusbar), "crop");
  1042.   gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar),
  1043.               crop->context_id, _("Crop: 0 x 0"));
  1044.  
  1045.   draw_core_start (crop->core, gdisp->canvas->window, tool);
  1046. }
  1047.  
  1048.  
  1049. /***************************/
  1050. /*  Crop dialog functions  */
  1051. /***************************/
  1052.  
  1053. static void
  1054. crop_info_create (Tool *tool)
  1055. {
  1056.   GDisplay  *gdisp;
  1057.   GtkWidget *spinbutton;
  1058.   GtkWidget *bbox;
  1059.   GtkWidget *button;
  1060.  
  1061.   gdisp = (GDisplay *) tool->gdisp_ptr;
  1062.  
  1063.   /*  create the info dialog  */
  1064.   crop_info = info_dialog_new (_("Crop & Resize Information"),
  1065.                    tools_help_func, NULL);
  1066.  
  1067.   /*  create the action area  */
  1068.   gimp_dialog_create_action_area (GTK_DIALOG (crop_info->shell),
  1069.  
  1070.                   _("Crop"), crop_crop_callback,
  1071.                   NULL, NULL, NULL, TRUE, FALSE,
  1072.                   _("Resize"), crop_resize_callback,
  1073.                   NULL, NULL, NULL, FALSE, FALSE,
  1074.                   _("Close"), crop_close_callback,
  1075.                   NULL, NULL, NULL, FALSE, FALSE,
  1076.  
  1077.                   NULL);
  1078.  
  1079.   /*  add the information fields  */
  1080.   spinbutton = info_dialog_add_spinbutton (crop_info, _("Origin X:"), NULL,
  1081.                         -1, 1, 1, 10, 1, 1, 2, NULL, NULL);
  1082.   origin_sizeentry =
  1083.     info_dialog_add_sizeentry (crop_info, _("Y:"), orig_vals, 1,
  1084.                    gdisp->dot_for_dot ? 
  1085.                    GIMP_UNIT_PIXEL : gdisp->gimage->unit, "%a",
  1086.                    TRUE, TRUE, FALSE, GIMP_SIZE_ENTRY_UPDATE_SIZE,
  1087.                    crop_orig_changed, crop_info);
  1088.   gimp_size_entry_add_field (GIMP_SIZE_ENTRY (origin_sizeentry),
  1089.                  GTK_SPIN_BUTTON (spinbutton), NULL);
  1090.  
  1091.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (origin_sizeentry), 0,
  1092.                      -65536, 65536);
  1093.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (origin_sizeentry), 1,
  1094.                      -65536, 65536);
  1095.  
  1096.   spinbutton = info_dialog_add_spinbutton (crop_info, _("Width:"), NULL,
  1097.                         -1, 1, 1, 10, 1, 1, 2, NULL, NULL);
  1098.   size_sizeentry =
  1099.     info_dialog_add_sizeentry (crop_info, _("Height:"), size_vals, 1,
  1100.                    gdisp->dot_for_dot ? 
  1101.                    GIMP_UNIT_PIXEL : gdisp->gimage->unit, "%a",
  1102.                    TRUE, TRUE, FALSE, GIMP_SIZE_ENTRY_UPDATE_SIZE,
  1103.                    crop_size_changed, crop_info);
  1104.   gimp_size_entry_add_field (GIMP_SIZE_ENTRY (size_sizeentry),
  1105.                  GTK_SPIN_BUTTON (spinbutton), NULL);
  1106.  
  1107.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (size_sizeentry), 0,
  1108.                      -65536, 65536);
  1109.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (size_sizeentry), 1,
  1110.                      -65536, 65536);
  1111.  
  1112.   gtk_table_set_row_spacing (GTK_TABLE (crop_info->info_table), 0, 0);
  1113.   gtk_table_set_row_spacing (GTK_TABLE (crop_info->info_table), 1, 6);
  1114.   gtk_table_set_row_spacing (GTK_TABLE (crop_info->info_table), 2, 0);
  1115.  
  1116.   /* Create the area selection buttons */
  1117.   bbox = gtk_hbutton_box_new ();
  1118.   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
  1119.   gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 4);
  1120.  
  1121.   button = gtk_button_new_with_label (_("From Selection"));
  1122.   gtk_container_add( GTK_CONTAINER(bbox), button);
  1123.   gtk_signal_connect(GTK_OBJECT (button) , "clicked",
  1124.              (GtkSignalFunc) crop_selection_callback, NULL);
  1125.   gtk_widget_show (button);
  1126.  
  1127.   button = gtk_button_new_with_label (_("Auto Shrink"));
  1128.   gtk_container_add(GTK_CONTAINER (bbox), button);
  1129.   gtk_signal_connect(GTK_OBJECT (button) , "clicked",
  1130.              (GtkSignalFunc) crop_automatic_callback, NULL);
  1131.   gtk_widget_show (button);
  1132.  
  1133.   gtk_box_pack_start (GTK_BOX (crop_info->vbox), bbox, FALSE, FALSE, 2);
  1134.   gtk_widget_show (bbox);
  1135. }
  1136.  
  1137. static void
  1138. crop_info_update (Tool *tool)
  1139. {
  1140.   Crop     *crop;
  1141.   GDisplay *gdisp;
  1142.  
  1143.   crop = (Crop *) tool->private;
  1144.   gdisp = (GDisplay *) tool->gdisp_ptr;
  1145.  
  1146.   orig_vals[0] = crop->tx1;
  1147.   orig_vals[1] = crop->ty1;
  1148.   size_vals[0] = crop->tx2 - crop->tx1;
  1149.   size_vals[1] = crop->ty2 - crop->ty1;
  1150.  
  1151.   info_dialog_update (crop_info);
  1152.   info_dialog_popup (crop_info);
  1153. }
  1154.  
  1155. static void
  1156. crop_crop_callback (GtkWidget *widget,
  1157.             gpointer   data)
  1158. {
  1159.   Tool     *tool;
  1160.   Crop     *crop;
  1161.   GDisplay *gdisp;
  1162.  
  1163.   tool  = active_tool;
  1164.   crop  = (Crop *) tool->private;
  1165.   gdisp = (GDisplay *) tool->gdisp_ptr;
  1166.  
  1167.   crop_image (gdisp->gimage,
  1168.           crop->tx1, crop->ty1,
  1169.           crop->tx2, crop->ty2,
  1170.           crop_options->layer_only,
  1171.           TRUE);
  1172.  
  1173.   /*  Finish the tool  */
  1174.   crop_close_callback (NULL, NULL);
  1175. }
  1176.  
  1177. static void
  1178. crop_resize_callback (GtkWidget *widget,
  1179.               gpointer   data)
  1180. {
  1181.   Tool     *tool;
  1182.   Crop     *crop;
  1183.   GDisplay *gdisp;
  1184.  
  1185.   tool  = active_tool;
  1186.   crop  = (Crop *) tool->private;
  1187.   gdisp = (GDisplay *) tool->gdisp_ptr;
  1188.  
  1189.   crop_image (gdisp->gimage,
  1190.           crop->tx1, crop->ty1,
  1191.           crop->tx2, crop->ty2, 
  1192.           crop_options->layer_only,
  1193.           FALSE);
  1194.  
  1195.   /*  Finish the tool  */
  1196.   crop_close_callback (NULL, NULL);
  1197. }
  1198.  
  1199. static void
  1200. crop_close_callback (GtkWidget *widget,
  1201.              gpointer   data)
  1202. {
  1203.   Tool *tool;
  1204.   Crop *crop;
  1205.  
  1206.   tool = active_tool;
  1207.   crop = (Crop *) tool->private;
  1208.  
  1209.   if (tool->state == ACTIVE)
  1210.     draw_core_stop (crop->core, tool);
  1211.  
  1212.   info_dialog_popdown (crop_info);
  1213.  
  1214.   tool->gdisp_ptr = NULL;
  1215.   tool->drawable = NULL;
  1216.   tool->state = INACTIVE;
  1217. }
  1218.  
  1219. static void
  1220. crop_selection_callback (GtkWidget *widget,
  1221.              gpointer   data)
  1222. {
  1223.   Tool     *tool;
  1224.   Crop     *crop;
  1225.   Layer    *layer;
  1226.   GDisplay *gdisp;
  1227.  
  1228.   tool = active_tool;
  1229.   crop = (Crop *) tool->private;
  1230.   gdisp = (GDisplay *) tool->gdisp_ptr;
  1231.  
  1232.   draw_core_pause (crop->core, tool);
  1233.   if (! gimage_mask_bounds (gdisp->gimage,
  1234.                 &crop->tx1, &crop->ty1, &crop->tx2, &crop->ty2))
  1235.     {
  1236.       if (crop_options->layer_only)
  1237.     {
  1238.       layer = (gdisp->gimage)->active_layer;
  1239.       drawable_offsets (GIMP_DRAWABLE (layer), &crop->tx1, &crop->ty1);
  1240.       crop->tx2 = drawable_width  (GIMP_DRAWABLE (layer)) + crop->tx1;
  1241.       crop->ty2 = drawable_height (GIMP_DRAWABLE (layer)) + crop->ty1;
  1242.     }
  1243.       else
  1244.     {
  1245.       crop->tx1 = crop->ty1 = 0;
  1246.       crop->tx2 = gdisp->gimage->width;
  1247.       crop->ty2 = gdisp->gimage->height;
  1248.     }
  1249.     }
  1250.   crop_recalc (tool, crop);
  1251.   draw_core_resume (crop->core, tool);
  1252. }
  1253.  
  1254.  
  1255. static void
  1256. crop_automatic_callback (GtkWidget *widget,
  1257.              gpointer   data)
  1258. {
  1259.   Tool     *tool;
  1260.   Crop     *crop;
  1261.   GDisplay *gdisp;
  1262.   GimpDrawable  *active_drawable = NULL;  
  1263.   GetColorFunc    get_color_func;
  1264.   ColorsEqualFunc colors_equal_func;
  1265.   GtkObject *get_color_obj;
  1266.   guchar bgcolor[4] = {0, 0, 0, 0};
  1267.   gboolean has_alpha = FALSE;
  1268.   PixelRegion PR;
  1269.   guchar *buffer = NULL;
  1270.   gint offset_x, offset_y, width, height, bytes;
  1271.   gint x, y, abort;
  1272.   gint x1, y1, x2, y2;
  1273.   
  1274.   tool  = active_tool;
  1275.   crop  = (Crop *) tool->private;
  1276.   gdisp = (GDisplay *) tool->gdisp_ptr;
  1277.  
  1278.   draw_core_pause (crop->core, tool);
  1279.   gimp_add_busy_cursors ();
  1280.  
  1281.   /* You should always keep in mind that crop->tx2 and crop->ty2 are the NOT the
  1282.      coordinates of the bottomright corner of the area to be cropped. They point 
  1283.      at the pixel located one to the right and one to the bottom.  
  1284.    */
  1285.  
  1286.   if (crop_options->layer_only)
  1287.     {
  1288.       if (!(active_drawable =  gimage_active_drawable (gdisp->gimage)))
  1289.     return;
  1290.       width  = drawable_width  (GIMP_DRAWABLE (active_drawable)); 
  1291.       height = drawable_height (GIMP_DRAWABLE (active_drawable));
  1292.       bytes  = drawable_bytes  (GIMP_DRAWABLE (active_drawable));
  1293.       if (drawable_has_alpha (GIMP_DRAWABLE (active_drawable)))
  1294.     has_alpha = TRUE;
  1295.       get_color_obj = GTK_OBJECT (active_drawable);
  1296.       get_color_func = (GetColorFunc) gimp_drawable_get_color_at;
  1297.       drawable_offsets (GIMP_DRAWABLE (active_drawable), &offset_x, &offset_y);
  1298.     }
  1299.   else
  1300.     {
  1301.       width  = gdisp->gimage->width;
  1302.       height = gdisp->gimage->height;
  1303.       has_alpha = TRUE; 
  1304.       bytes  = gimp_image_composite_bytes (gdisp->gimage);
  1305.       get_color_obj = GTK_OBJECT (gdisp->gimage);
  1306.       get_color_func = (GetColorFunc) gimp_image_get_color_at;
  1307.       offset_x = offset_y = 0;
  1308.    }
  1309.  
  1310.   x1 = crop->tx1 - offset_x  > 0      ? crop->tx1 - offset_x : 0;
  1311.   x2 = crop->tx2 - offset_x  < width  ? crop->tx2 - offset_x : width;
  1312.   y1 = crop->ty1 - offset_y  > 0      ? crop->ty1 - offset_y : 0;
  1313.   y2 = crop->ty2 - offset_y  < height ? crop->ty2 - offset_y : height;
  1314.       
  1315.   switch (crop_guess_bgcolor (get_color_obj, get_color_func, bytes, has_alpha, 
  1316.                   bgcolor, x1, x2-1, y1, y2-1))
  1317.     {
  1318.     case AUTO_CROP_ALPHA:
  1319.       colors_equal_func = (ColorsEqualFunc) crop_colors_alpha;
  1320.       break;
  1321.     case AUTO_CROP_COLOR:
  1322.       colors_equal_func = (ColorsEqualFunc) crop_colors_equal;
  1323.       break;
  1324.     default:
  1325.       goto FINISH;
  1326.     }
  1327.  
  1328.   width  = x2 - x1;
  1329.   height = y2 - y1;
  1330.  
  1331.   if (crop_options->layer_only)
  1332.     pixel_region_init (&PR, drawable_data (active_drawable), 
  1333.                x1, y1, width, height, FALSE);
  1334.   else
  1335.     pixel_region_init (&PR, gimp_image_composite (gdisp->gimage), 
  1336.                x1, y1, width, height, FALSE);
  1337.  
  1338.   /* The following could be optimized further by processing the smaller side first
  1339.      instead of defaulting to width    --Sven                                      
  1340.    */
  1341.  
  1342.   buffer = g_malloc ((width > height ? width : height) * bytes);
  1343.  
  1344.  /* Check how many of the top lines are uniform/transparent. */
  1345.   abort = FALSE;
  1346.   for (y = y1; y < y2 && !abort; y++)
  1347.     {
  1348.       pixel_region_get_row (&PR, x1, y, width, buffer, 1);
  1349.       for (x = 0; x < width && !abort; x++)
  1350.     abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes);
  1351.     }
  1352.   if (y == y2 && !abort) 
  1353.     goto FINISH;
  1354.   y1 = y - 1;
  1355.  
  1356.   /* Check how many of the bottom lines are uniform/transparent. */
  1357.   abort = FALSE;
  1358.   for (y = y2; y > y1 && !abort; y--)
  1359.     {
  1360.       pixel_region_get_row (&PR, x1, y-1 , width, buffer, 1);
  1361.       for (x = 0; x < width && !abort; x++)
  1362.     abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes);
  1363.     }
  1364.   y2 = y + 1;
  1365.  
  1366.   /* compute a new height for the next operations */
  1367.   height = y2 - y1;
  1368.  
  1369.   /* Check how many of the left lines are uniform/transparent. */
  1370.   abort = FALSE;
  1371.   for (x = x1; x < x2 && !abort; x++)
  1372.     {
  1373.       pixel_region_get_col (&PR, x, y1, height, buffer, 1);
  1374.       for (y = 0; y < height && !abort; y++)
  1375.     abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes);
  1376.     }
  1377.   x1 = x - 1;
  1378.  
  1379.   /* Check how many of the right lines are uniform/transparent. */
  1380.   abort = FALSE;
  1381.   for (x = x2; x > x1 && !abort; x--)
  1382.     {
  1383.       pixel_region_get_col (&PR, x-1, y1, height, buffer, 1);
  1384.       for (y = 0; y < height && !abort; y++)
  1385.     abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes);
  1386.     }
  1387.   x2 = x + 1;
  1388.  
  1389.   crop->tx1 = offset_x + x1;
  1390.   crop->tx2 = offset_x + x2;
  1391.   crop->ty1 = offset_y + y1;
  1392.   crop->ty2 = offset_y + y2;
  1393.  
  1394.   crop_recalc (tool, crop);
  1395.  
  1396.  FINISH:
  1397.   g_free (buffer);
  1398.   gimp_remove_busy_cursors (NULL);
  1399.   draw_core_resume (crop->core, tool);
  1400.  
  1401.   return;
  1402. }
  1403.  
  1404. static AutoCropType
  1405. crop_guess_bgcolor (GtkObject    *get_color_obj,
  1406.             GetColorFunc  get_color_func,
  1407.             gint          bytes,
  1408.             gboolean      has_alpha,
  1409.             guchar       *color,
  1410.             gint          x1, 
  1411.             gint          x2, 
  1412.             gint          y1, 
  1413.             gint          y2) 
  1414. {
  1415.   guchar *tl = NULL;
  1416.   guchar *tr = NULL;
  1417.   guchar *bl = NULL;
  1418.   guchar *br = NULL;
  1419.   gint i, alpha;
  1420.  
  1421.   for (i = 0; i < bytes; i++)
  1422.     color[i] = 0; 
  1423.  
  1424.   /* First check if there's transparency to crop. If not, guess the 
  1425.    * background-color to see if at least 2 corners are equal.
  1426.    */
  1427.  
  1428.   if (!(tl = (*get_color_func) (get_color_obj, x1, y1))) 
  1429.     goto ERROR;
  1430.   if (!(tr = (*get_color_func) (get_color_obj, x1, y2))) 
  1431.     goto ERROR;
  1432.   if (!(bl = (*get_color_func) (get_color_obj, x2, y1))) 
  1433.     goto ERROR;  
  1434.   if (!(br = (*get_color_func) (get_color_obj, x2, y2))) 
  1435.     goto ERROR;
  1436.  
  1437.   if (has_alpha)
  1438.     {
  1439.       alpha = bytes - 1;
  1440.       if ((tl[alpha] == 0 && tr[alpha] == 0) ||
  1441.       (tl[alpha] == 0 && bl[alpha] == 0) ||
  1442.       (tr[alpha] == 0 && br[alpha] == 0) ||
  1443.       (bl[alpha] == 0 && br[alpha] == 0))
  1444.     {
  1445.       g_free (tl);
  1446.       g_free (tr);
  1447.       g_free (bl);
  1448.       g_free (br);
  1449.       return AUTO_CROP_ALPHA;
  1450.     }
  1451.     }
  1452.   
  1453.   if (crop_colors_equal (tl, tr, bytes) || crop_colors_equal (tl, bl, bytes))
  1454.     memcpy (color, tl, bytes);
  1455.   else if (crop_colors_equal (br, bl, bytes) || crop_colors_equal (br, tr, bytes))
  1456.     memcpy (color, br, bytes);
  1457.   else
  1458.     goto ERROR;
  1459.  
  1460.   g_free (tl); 
  1461.   g_free (tr); 
  1462.   g_free (bl); 
  1463.   g_free (br);
  1464.   return AUTO_CROP_COLOR;
  1465.  
  1466.  ERROR:
  1467.   g_free (tl); 
  1468.   g_free (tr); 
  1469.   g_free (bl); 
  1470.   g_free (br);
  1471.   return AUTO_CROP_NOTHING;
  1472. }
  1473.  
  1474. static int 
  1475. crop_colors_equal (guchar *col1, 
  1476.            guchar *col2, 
  1477.            gint    bytes) 
  1478. {
  1479.   gboolean equal = TRUE;
  1480.   gint b;
  1481.   
  1482.   for (b = 0; b < bytes; b++)
  1483.     {
  1484.       if (col1[b] != col2[b])
  1485.     {
  1486.       equal = FALSE;
  1487.       break;
  1488.     }
  1489.     }
  1490.  
  1491.   return equal;
  1492. }
  1493.  
  1494. static gboolean
  1495. crop_colors_alpha (guchar *dummy, 
  1496.            guchar *col, 
  1497.            gint    bytes) 
  1498. {
  1499.   if (col[bytes-1] == 0)
  1500.     return TRUE;
  1501.   else
  1502.     return FALSE;
  1503. }
  1504.  
  1505. static void
  1506. crop_orig_changed (GtkWidget *widget,
  1507.            gpointer   data)
  1508. {
  1509.   Tool     *tool;
  1510.   Crop     *crop;
  1511.   GDisplay *gdisp;
  1512.   gint      ox;
  1513.   gint      oy;
  1514.  
  1515.   tool = active_tool;
  1516.  
  1517.   if (tool && active_tool->type == CROP)
  1518.     {
  1519.       gdisp = (GDisplay *) tool->gdisp_ptr;
  1520.       crop = (Crop *) tool->private;
  1521.       ox = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0));
  1522.       oy = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1));
  1523.  
  1524.       if ((ox != crop->tx1) ||
  1525.       (oy != crop->ty1))
  1526.     {
  1527.       draw_core_pause (crop->core, tool);
  1528.       crop->tx2 = crop->tx2 + (ox - crop->tx1);
  1529.       crop->tx1 = ox;
  1530.       crop->ty2 = crop->ty2 + (oy - crop->ty1);
  1531.       crop->ty1 = oy;
  1532.       crop_recalc (tool, crop);
  1533.       draw_core_resume (crop->core, tool);
  1534.     }
  1535.     }
  1536. }
  1537.  
  1538. static void
  1539. crop_size_changed (GtkWidget *widget,
  1540.            gpointer   data)
  1541. {
  1542.   Tool     *tool;
  1543.   Crop     *crop;
  1544.   GDisplay *gdisp;
  1545.   gint      sx;
  1546.   gint      sy;
  1547.  
  1548.   tool = active_tool;
  1549.  
  1550.   if (tool && active_tool->type == CROP)
  1551.     {
  1552.       gdisp = (GDisplay *) tool->gdisp_ptr;
  1553.       crop = (Crop *) tool->private;
  1554.       sx = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
  1555.       sy = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
  1556.  
  1557.       if ((sx != (crop->tx2 - crop->tx1)) ||
  1558.       (sy != (crop->ty2 - crop->ty1)))
  1559.     {
  1560.       draw_core_pause (crop->core, tool);
  1561.       crop->tx2 = sx + crop->tx1;
  1562.       crop->ty2 = sy + crop->ty1;
  1563.       crop_recalc (tool, crop);
  1564.       draw_core_resume (crop->core, tool);
  1565.     }
  1566.     }
  1567. }
  1568.