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

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * Measure tool
  5.  * Copyright (C) 1999 Sven Neumann <sven@gimp.org>
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  */
  21.  
  22. #include "config.h"
  23.  
  24. #include <glib.h>
  25.  
  26. #include "apptypes.h"
  27.  
  28. #include "appenv.h"
  29. #include "draw_core.h"
  30. #include "gimpui.h"
  31. #include "info_dialog.h"
  32. #include "measure.h"
  33. #include "undo.h"
  34.  
  35. #include "libgimp/gimpintl.h"
  36. #include "libgimp/gimpmath.h"
  37.  
  38. /*  definitions  */
  39. #define  TARGET         8
  40. #define  ARC_RADIUS     30
  41. #define  ARC_RADIUS_2   1100
  42. #define  STATUSBAR_SIZE 128
  43.  
  44. /*  possible measure functions  */
  45. typedef enum 
  46. {
  47.   CREATING,
  48.   ADDING,
  49.   MOVING,
  50.   MOVING_ALL,
  51.   GUIDING,
  52.   FINISHED
  53. } MeasureFunction;
  54.  
  55. /*  the measure structure  */
  56. typedef struct _MeasureTool MeasureTool;
  57.  
  58. struct _MeasureTool
  59. {
  60.   DrawCore        *core;        /*  draw core                  */
  61.   MeasureFunction  function;    /*  what are we doing?         */
  62.   gint             last_x;      /*  last x coordinate          */
  63.   gint             last_y;      /*  last y coordinate          */
  64.   gint             point;       /*  what are we manipulating?  */
  65.   gint             num_points;  /*  how many points?           */
  66.   gint             x[3];        /*  three x coordinates        */
  67.   gint             y[3];        /*  three y coordinates        */
  68.   gdouble          angle1;      /*  first angle                */
  69.   gdouble          angle2;      /*  second angle               */
  70.   guint            context_id;  /*  for the statusbar          */
  71. };
  72.  
  73. /*  the measure tool options  */
  74. typedef struct _MeasureOptions MeasureOptions;
  75.  
  76. struct _MeasureOptions
  77. {
  78.   ToolOptions  tool_options;
  79.  
  80.   gboolean     use_info_window;
  81.   gboolean     use_info_window_d;
  82.   GtkWidget   *use_info_window_w;
  83. };
  84.  
  85.  
  86. /*  maximum information buffer size  */
  87. #define MAX_INFO_BUF 16
  88.  
  89. static MeasureOptions *measure_tool_options = NULL;
  90.  
  91. /*  the measure tool info window  */
  92. static InfoDialog  *measure_tool_info = NULL;
  93. static gchar        distance_buf [MAX_INFO_BUF];
  94. static gchar        angle_buf    [MAX_INFO_BUF];
  95.  
  96.  
  97. /*  local function prototypes  */
  98. static void   measure_tool_button_press   (Tool *, GdkEventButton *, gpointer);
  99. static void   measure_tool_button_release (Tool *, GdkEventButton *, gpointer);
  100. static void   measure_tool_motion         (Tool *, GdkEventMotion *, gpointer);
  101. static void   measure_tool_cursor_update  (Tool *, GdkEventMotion *, gpointer);
  102. static void   measure_tool_control      (Tool *, ToolAction,       gpointer);
  103.  
  104. static void   measure_tool_info_window_close_callback (GtkWidget *, gpointer);
  105. static void   measure_tool_info_update                (void);
  106.  
  107.  
  108. static void
  109. measure_tool_options_reset (void)
  110. {
  111.   MeasureOptions *options = measure_tool_options;
  112.  
  113.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->use_info_window_w),
  114.                 options->use_info_window_d);
  115. }
  116.  
  117. static MeasureOptions *
  118. measure_tool_options_new (void)
  119. {
  120.   MeasureOptions *options;
  121.   GtkWidget *vbox;
  122.  
  123.   /*  the new measure tool options structure  */
  124.   options = g_new (MeasureOptions, 1);
  125.   tool_options_init ((ToolOptions *) options,
  126.              _("Measure Tool"),
  127.              measure_tool_options_reset);
  128.   options->use_info_window = options->use_info_window_d  = FALSE;
  129.  
  130.     /*  the main vbox  */
  131.   vbox = options->tool_options.main_vbox;
  132.  
  133.   /*  the use_info_window toggle button  */
  134.   options->use_info_window_w =
  135.     gtk_check_button_new_with_label (_("Use Info Window"));
  136.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->use_info_window_w),
  137.                 options->use_info_window_d);
  138.   gtk_box_pack_start (GTK_BOX (vbox), options->use_info_window_w, FALSE, FALSE, 0);
  139.   gtk_signal_connect (GTK_OBJECT (options->use_info_window_w), "toggled",
  140.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  141.               &options->use_info_window);
  142.   gtk_widget_show (options->use_info_window_w);
  143.  
  144.   return options;
  145. }
  146.  
  147. static double
  148. measure_get_angle (gint    dx,
  149.            gint    dy,
  150.            gdouble xres,
  151.            gdouble yres)
  152. {
  153.   gdouble angle;
  154.  
  155.   if (dx)
  156.     angle = gimp_rad_to_deg (atan (((double)(dy) / yres) / ((double)(dx) / xres)));
  157.   else if (dy)
  158.     angle = dy > 0 ? 270.0 : 90.0;
  159.   else
  160.     angle = 180.0;
  161.   
  162.   if (dx > 0)
  163.     {
  164.       if (dy > 0)
  165.     angle = 360.0 - angle;
  166.       else
  167.     angle = -angle;
  168.     }
  169.   else
  170.     angle = 180.0 - angle;
  171.  
  172.   return (angle);
  173. }
  174.  
  175. static void
  176. measure_tool_button_press (Tool           *tool,
  177.                GdkEventButton *bevent,
  178.                gpointer        gdisp_ptr)
  179. {
  180.   GDisplay * gdisp;
  181.   MeasureTool * measure_tool;
  182.   gint x[3];
  183.   gint y[3];
  184.   gint i;
  185.  
  186.   gdisp = (GDisplay *) gdisp_ptr;
  187.   measure_tool = (MeasureTool *) tool->private;
  188.  
  189.   /*  if we are changing displays, pop the statusbar of the old one  */ 
  190.   if (tool->state == ACTIVE && gdisp_ptr != tool->gdisp_ptr)
  191.     {
  192.       GDisplay *old_gdisp = tool->gdisp_ptr;
  193.       gtk_statusbar_pop (GTK_STATUSBAR (old_gdisp->statusbar),
  194.              measure_tool->context_id);
  195.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), 
  196.               measure_tool->context_id, (""));
  197.     } 
  198.   
  199.   measure_tool->function = CREATING;
  200.  
  201.   if (tool->state == ACTIVE && gdisp_ptr == tool->gdisp_ptr)
  202.     {
  203.       /*  if the cursor is in one of the handles, 
  204.       the new function will be moving or adding a new point or guide */
  205.       for (i=0; i < measure_tool->num_points; i++)
  206.     {
  207.       gdisplay_transform_coords (gdisp, measure_tool->x[i], measure_tool->y[i], 
  208.                      &x[i], &y[i], FALSE);      
  209.       if (bevent->x == CLAMP (bevent->x, x[i] - TARGET, x[i] + TARGET) &&
  210.           bevent->y == CLAMP (bevent->y, y[i] - TARGET, y[i] + TARGET))
  211.         {
  212.           if (bevent->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))
  213.         {
  214.           gboolean  undo_group;
  215.           Guide    *guide;
  216.  
  217.           undo_group = (bevent->state & GDK_CONTROL_MASK &&
  218.                 bevent->state & GDK_MOD1_MASK &&
  219.                 (measure_tool->y[i] ==
  220.                  CLAMP (measure_tool->y[i],
  221.                     0, gdisp->gimage->height)) &&
  222.                 (measure_tool->x[i] ==
  223.                  CLAMP (measure_tool->x[i],
  224.                     0, gdisp->gimage->width)));
  225.  
  226.           if (undo_group)
  227.             undo_push_group_start (gdisp->gimage, GUIDE_UNDO);
  228.  
  229.           if (bevent->state & GDK_CONTROL_MASK && 
  230.               (measure_tool->y[i] ==
  231.                CLAMP (measure_tool->y[i], 0, gdisp->gimage->height)))
  232.             {
  233.               guide = gimp_image_add_hguide (gdisp->gimage);
  234.               undo_push_guide (gdisp->gimage, guide);
  235.               guide->position = measure_tool->y[i];
  236.               gdisplays_expose_guide (gdisp->gimage, guide);
  237.             }
  238.  
  239.           if (bevent->state & GDK_MOD1_MASK &&
  240.               (measure_tool->x[i] ==
  241.                CLAMP (measure_tool->x[i], 0, gdisp->gimage->width)))
  242.             {
  243.               guide = gimp_image_add_vguide (gdisp->gimage);
  244.               undo_push_guide (gdisp->gimage, guide);
  245.               guide->position = measure_tool->x[i];
  246.               gdisplays_expose_guide (gdisp->gimage, guide);
  247.             }
  248.  
  249.           if (undo_group)
  250.             undo_push_group_end (gdisp->gimage);
  251.  
  252.           gdisplays_flush ();
  253.           measure_tool->function = GUIDING;
  254.           break;
  255.         }
  256.           measure_tool->function = (bevent->state & GDK_SHIFT_MASK) ? ADDING : MOVING;
  257.           measure_tool->point = i;
  258.           break;
  259.         }
  260.     }
  261.       /*  adding to the middle point makes no sense  */
  262.       if (i == 0 && measure_tool->function == ADDING && measure_tool->num_points == 3)
  263.     measure_tool->function = MOVING;
  264.  
  265.       /*  if the function is still CREATING, we are outside the handles  */
  266.       if (measure_tool->function == CREATING) 
  267.     {
  268.       if (measure_tool->num_points > 1 && bevent->state & GDK_MOD1_MASK)
  269.         {
  270.           measure_tool->function = MOVING_ALL;
  271.           gdisplay_untransform_coords (gdisp,  bevent->x, bevent->y, 
  272.                        &measure_tool->last_x, &measure_tool->last_y, 
  273.                        TRUE, FALSE);
  274.         }
  275.     }
  276.     }
  277.   
  278.   if (measure_tool->function == CREATING)
  279.     {
  280.       if (tool->state == ACTIVE)
  281.     {
  282.       /* reset everything */
  283.       draw_core_stop (measure_tool->core, tool);
  284.  
  285.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  286.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id, "");
  287.  
  288.       distance_buf[0] = '\0';
  289.       angle_buf[0] = '\0';
  290.       if (measure_tool_info)
  291.         measure_tool_info_update ();
  292.     }
  293.       else
  294.     {
  295.       /* initialize the statusbar display */
  296.       measure_tool->context_id = 
  297.         gtk_statusbar_get_context_id (GTK_STATUSBAR (gdisp->statusbar), "measure");
  298.     }
  299.       
  300.       /*  set the first point and go into ADDING mode  */
  301.       gdisplay_untransform_coords (gdisp,  bevent->x, bevent->y, 
  302.                    &measure_tool->x[0], &measure_tool->y[0], TRUE, FALSE);
  303.       measure_tool->point = 0;
  304.       measure_tool->num_points = 1;
  305.       measure_tool->function = ADDING;
  306.  
  307.       /*  set the gdisplay  */
  308.       tool->gdisp_ptr = gdisp_ptr;
  309.  
  310.       /*  start drawing the measure tool  */
  311.       draw_core_start (measure_tool->core, gdisp->canvas->window, tool);
  312.     }
  313.  
  314.   /*  create the info window if necessary  */
  315.   if (!measure_tool_info &&
  316.       (measure_tool_options->use_info_window ||
  317.        !GTK_WIDGET_VISIBLE (gdisp->statusarea)))
  318.     {
  319.       measure_tool_info = info_dialog_new (_("Measure Tool"),
  320.                        tools_help_func, NULL);
  321.       info_dialog_add_label (measure_tool_info, _("Distance:"), distance_buf);
  322.       info_dialog_add_label (measure_tool_info, _("Angle:"), angle_buf);
  323.       
  324.       gimp_dialog_create_action_area (GTK_DIALOG (measure_tool_info->shell),
  325.                       
  326.                       _("Close"), measure_tool_info_window_close_callback,
  327.                       measure_tool_info, NULL, NULL, TRUE, FALSE,
  328.                       
  329.                       NULL);
  330.     }
  331.   
  332.   gdk_pointer_grab (gdisp->canvas->window, FALSE,
  333.             GDK_POINTER_MOTION_HINT_MASK |
  334.             GDK_BUTTON1_MOTION_MASK |
  335.             GDK_BUTTON_RELEASE_MASK,
  336.             NULL, NULL, bevent->time);
  337.   tool->state = ACTIVE;
  338.   
  339.   /*  set the pointer to the crosshair,
  340.    *  so one actually sees the cursor position
  341.    */
  342.   gdisplay_install_tool_cursor (gdisp, GIMP_CROSSHAIR_SMALL_CURSOR,
  343.                 MEASURE,
  344.                 CURSOR_MODIFIER_NONE,
  345.                 FALSE);
  346. }
  347.  
  348. static void
  349. measure_tool_button_release (Tool           *tool,
  350.                  GdkEventButton *bevent,
  351.                  gpointer        gdisp_ptr)
  352. {
  353.   GDisplay * gdisp;
  354.   MeasureTool * measure_tool;
  355.  
  356.   gdisp = (GDisplay *) gdisp_ptr;
  357.   measure_tool = (MeasureTool *) tool->private;
  358.   
  359.   measure_tool->function = FINISHED;
  360.  
  361.   gdk_pointer_ungrab (bevent->time);
  362.   gdk_flush ();
  363. }
  364.  
  365. static void
  366. measure_tool_motion (Tool           *tool,
  367.              GdkEventMotion *mevent,
  368.              gpointer        gdisp_ptr)
  369. {
  370.   GDisplay * gdisp;
  371.   MeasureTool * measure_tool;
  372.   gint x, y;
  373.   gint ax, ay;
  374.   gint bx, by;
  375.   gint dx, dy;
  376.   gint i;
  377.   gint tmp;
  378.   gdouble angle;
  379.   gdouble distance;
  380.   gchar status_str[STATUSBAR_SIZE];
  381.  
  382.   gdisp = (GDisplay *) gdisp_ptr;
  383.   measure_tool = (MeasureTool *) tool->private;
  384.  
  385.   /*  undraw the current tool  */
  386.   draw_core_pause (measure_tool->core, tool);
  387.  
  388.   /*  get the coordinates  */
  389.   gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, FALSE);
  390.   
  391.   /*  
  392.    *  A few comments here, because this routine looks quite weird at first ...
  393.    *
  394.    *  The goal is to keep point 0, called the start point, to be always the one 
  395.    *  in the middle or, if there are only two points, the one that is fixed.
  396.    *  The angle is then always measured at this point.
  397.    */
  398.  
  399.   switch (measure_tool->function)
  400.     {
  401.     case ADDING:
  402.       switch (measure_tool->point)
  403.     {
  404.     case 0:  /*  we are adding to the start point  */  
  405.       break;
  406.     case 1:  /*  we are adding to the end point, make it the new start point  */
  407.       tmp = measure_tool->x[0];
  408.       measure_tool->x[0] = measure_tool->x[1];
  409.       measure_tool->x[1] = tmp;
  410.       tmp = measure_tool->y[0];
  411.       measure_tool->y[0] = measure_tool->y[1];
  412.       measure_tool->y[1] = tmp;
  413.       break;
  414.     case 2:  /*  we are adding to the third point, make it the new start point  */
  415.       measure_tool->x[1] = measure_tool->x[0];
  416.       measure_tool->y[1] = measure_tool->y[0];
  417.       measure_tool->x[0] = measure_tool->x[2];
  418.       measure_tool->y[0] = measure_tool->y[2];
  419.       break;
  420.     default:
  421.       break;
  422.     }
  423.       measure_tool->num_points = MIN (measure_tool->num_points + 1, 3);
  424.       measure_tool->point = measure_tool->num_points - 1;
  425.       measure_tool->function = MOVING;
  426.       /*  no, don't break here!  */
  427.  
  428.     case MOVING: 
  429.       /*  if we are moving the start point and only have two, make it the end point  */
  430.       if (measure_tool->num_points == 2 && measure_tool->point == 0)
  431.     {
  432.       tmp = measure_tool->x[0];
  433.       measure_tool->x[0] = measure_tool->x[1];
  434.       measure_tool->x[1] = tmp;
  435.       tmp = measure_tool->y[0];
  436.       measure_tool->y[0] = measure_tool->y[1];
  437.       measure_tool->y[1] = tmp;
  438.       measure_tool->point = 1;
  439.     }
  440.       i = measure_tool->point;
  441.  
  442.       measure_tool->x[i] = x;
  443.       measure_tool->y[i] = y;
  444.  
  445.       /*  restrict to horizontal/vertical movements, if modifiers are pressed */
  446.       if (mevent->state & GDK_MOD1_MASK)
  447.     {
  448.       if (mevent->state & GDK_CONTROL_MASK)
  449.         {
  450.           dx = measure_tool->x[i] - measure_tool->x[0];
  451.           dy = measure_tool->y[i] - measure_tool->y[0];
  452.       
  453.           measure_tool->x[i] = measure_tool->x[0] + 
  454.         (dx > 0 ? MAX (abs (dx), abs (dy)) : - MAX (abs (dx), abs (dy)));
  455.           measure_tool->y[i] = measure_tool->y[0] + 
  456.         (dy > 0  ? MAX (abs (dx), abs (dy)) : - MAX (abs (dx), abs (dy)));
  457.         }
  458.       else
  459.         measure_tool->x[i] = measure_tool->x[0];
  460.     }
  461.       else if (mevent->state & GDK_CONTROL_MASK)
  462.     measure_tool->y[i] = measure_tool->y[0];
  463.       break;
  464.  
  465.     case MOVING_ALL:
  466.       dx = x - measure_tool->last_x;
  467.       dy = y - measure_tool->last_y;
  468.       for (i = 0; i < measure_tool->num_points; i++)
  469.     {
  470.       measure_tool->x[i] += dx;
  471.       measure_tool->y[i] += dy;
  472.     }
  473.       measure_tool->last_x = x;
  474.       measure_tool->last_y = y;
  475.       break;
  476.  
  477.     default:
  478.       break;
  479.     }
  480.  
  481.   if (measure_tool->function == MOVING)
  482.     {
  483.       /*  calculate distance and angle  */
  484.       ax = measure_tool->x[1] - measure_tool->x[0];
  485.       ay = measure_tool->y[1] - measure_tool->y[0];
  486.       
  487.       if (measure_tool->num_points == 3)
  488.     {
  489.       bx = measure_tool->x[2] - measure_tool->x[0];
  490.       by = measure_tool->y[2] - measure_tool->y[0];
  491.     }
  492.       else
  493.     {
  494.       bx = 0;
  495.       by = 0;
  496.     }
  497.       
  498.       if (gdisp->dot_for_dot)
  499.     {
  500.       distance = sqrt (SQR (ax - bx) + SQR (ay - by));
  501.       
  502.       if (measure_tool->num_points != 3)
  503.         bx = ax > 0 ? 1 : -1;
  504.       
  505.       measure_tool->angle1 = measure_get_angle (ax, ay, 1.0, 1.0);
  506.       measure_tool->angle2 = measure_get_angle (bx, by, 1.0, 1.0);
  507.       angle = fabs (measure_tool->angle1 - measure_tool->angle2);
  508.       if (angle > 180.0)
  509.         angle = fabs (360.0 - angle);
  510.       
  511.       g_snprintf (status_str, STATUSBAR_SIZE, "%.1f %s, %.2f %s",
  512.               distance, _("pixels"), angle, _("degrees"));
  513.       
  514.       if (measure_tool_options)
  515.         {
  516.           g_snprintf (distance_buf, MAX_INFO_BUF, "%.1f %s", distance, _("pixels"));
  517.           g_snprintf (angle_buf, MAX_INFO_BUF, "%.2f %s", angle, _("degrees"));
  518.         }
  519.     }
  520.       else /* show real world units */
  521.     {
  522.       gchar *format_str = g_strdup_printf ("%%.%df %s, %%.2f %s",
  523.                            gimp_unit_get_digits (gdisp->gimage->unit),
  524.                            gimp_unit_get_symbol (gdisp->gimage->unit),
  525.                            _("degrees"));
  526.       
  527.       distance =  gimp_unit_get_factor (gdisp->gimage->unit) * 
  528.         sqrt (SQR ((gdouble)(ax - bx) / gdisp->gimage->xresolution) +
  529.           SQR ((gdouble)(ay - by) / gdisp->gimage->yresolution));
  530.       
  531.       if (measure_tool->num_points != 3)
  532.         bx = ax > 0 ? 1 : -1;
  533.       
  534.       measure_tool->angle1 = measure_get_angle (ax, ay, 
  535.                             gdisp->gimage->xresolution, 
  536.                             gdisp->gimage->yresolution); 
  537.       measure_tool->angle2 = measure_get_angle (bx, by,
  538.                             gdisp->gimage->xresolution, 
  539.                             gdisp->gimage->yresolution);
  540.       angle = fabs (measure_tool->angle1 - measure_tool->angle2);     
  541.       if (angle > 180.0)
  542.         angle = fabs (360.0 - angle);
  543.       
  544.       g_snprintf (status_str, STATUSBAR_SIZE, format_str, distance , angle);
  545.       g_free (format_str);
  546.       
  547.       if (measure_tool_options)
  548.         {
  549.           gchar *format_str = g_strdup_printf ("%%.%df %s",
  550.                            gimp_unit_get_digits (gdisp->gimage->unit),
  551.                            gimp_unit_get_symbol (gdisp->gimage->unit));
  552.           g_snprintf (distance_buf, MAX_INFO_BUF, format_str, distance);
  553.           g_snprintf (angle_buf, MAX_INFO_BUF, "%.2f %s", angle, _("degrees"));
  554.           g_free (format_str);
  555.         }
  556.     }
  557.       
  558.       /*  show info in statusbar  */
  559.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  560.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id,
  561.               status_str);
  562.       
  563.       /*  and in the info window  */
  564.       if (measure_tool_info)
  565.     measure_tool_info_update ();
  566.  
  567.     }  /*  measure_tool->function == MOVING  */
  568.   
  569.   /*  redraw the current tool  */
  570.   draw_core_resume (measure_tool->core, tool);
  571. }
  572.  
  573. static void
  574. measure_tool_cursor_update (Tool           *tool,
  575.                 GdkEventMotion *mevent,
  576.                 gpointer        gdisp_ptr)
  577. {
  578.   MeasureTool   *measure_tool;
  579.   GDisplay      *gdisp;
  580.   gint           x[3];
  581.   gint           y[3];
  582.   gint           i;
  583.   gboolean       in_handle = FALSE;
  584.  
  585.   GdkCursorType  ctype     = GIMP_CROSSHAIR_SMALL_CURSOR;
  586.   CursorModifier cmodifier = CURSOR_MODIFIER_NONE;
  587.  
  588.   gdisp = (GDisplay *) gdisp_ptr;
  589.   measure_tool = (MeasureTool *) tool->private;
  590.  
  591.   if (tool->state == ACTIVE && tool->gdisp_ptr == gdisp_ptr)
  592.     {
  593.       for (i = 0; i < measure_tool->num_points; i++)
  594.     {
  595.       gdisplay_transform_coords (gdisp, measure_tool->x[i], measure_tool->y[i], 
  596.                      &x[i], &y[i], FALSE);      
  597.       
  598.       if (mevent->x == CLAMP (mevent->x, x[i] - TARGET, x[i] + TARGET) &&
  599.           mevent->y == CLAMP (mevent->y, y[i] - TARGET, y[i] + TARGET))
  600.         {
  601.           in_handle = TRUE;
  602.           
  603.           if (mevent->state & GDK_CONTROL_MASK)
  604.         {
  605.           if (mevent->state & GDK_MOD1_MASK)
  606.             ctype = GDK_BOTTOM_RIGHT_CORNER;
  607.           else
  608.             ctype = GDK_BOTTOM_SIDE;
  609.           break;
  610.         }
  611.           if (mevent->state & GDK_MOD1_MASK)
  612.         {
  613.           ctype = GDK_RIGHT_SIDE;
  614.           break;
  615.         }
  616.  
  617.           if (mevent->state & GDK_SHIFT_MASK)
  618.         cmodifier = CURSOR_MODIFIER_PLUS;
  619.           else
  620.         cmodifier = CURSOR_MODIFIER_MOVE;
  621.  
  622.           if (i == 0 && measure_tool->num_points == 3 &&
  623.           cmodifier == CURSOR_MODIFIER_PLUS)
  624.         cmodifier = CURSOR_MODIFIER_MOVE;
  625.           break;
  626.         }
  627.     }
  628.  
  629.       if (!in_handle && measure_tool->num_points > 1 &&
  630.       mevent->state & GDK_MOD1_MASK)
  631.     cmodifier = CURSOR_MODIFIER_MOVE;
  632.     }
  633.  
  634.   gdisplay_install_tool_cursor (gdisp, ctype,
  635.                 MEASURE,
  636.                 cmodifier,
  637.                 FALSE);
  638. }
  639.  
  640. static void
  641. measure_tool_draw (Tool *tool)
  642. {
  643.   GDisplay * gdisp;
  644.   MeasureTool * measure_tool;
  645.   gint x[3];
  646.   gint y[3];
  647.   gint i;
  648.   gint angle1, angle2;
  649.   gint draw_arc = 0;
  650.  
  651.   gdisp = (GDisplay *) tool->gdisp_ptr;
  652.   measure_tool = (MeasureTool *) tool->private;
  653.  
  654.   for (i = 0; i < measure_tool->num_points; i++)
  655.     {
  656.       gdisplay_transform_coords (gdisp, measure_tool->x[i], measure_tool->y[i],
  657.                  &x[i], &y[i], FALSE);
  658.       if (i == 0 && measure_tool->num_points == 3)
  659.     {
  660.       gdk_draw_arc (measure_tool->core->win, measure_tool->core->gc, FALSE,
  661.             x[i] - (TARGET >> 1), y[i] - (TARGET >> 1),
  662.             TARGET, TARGET, 0, 23040);
  663.     }
  664.       else
  665.     {
  666.       gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  667.              x[i] - TARGET, y[i], 
  668.              x[i] + TARGET, y[i]);
  669.       gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  670.              x[i], y[i] - TARGET, 
  671.              x[i], y[i] + TARGET);
  672.     }
  673.       if (i > 0)
  674.     {
  675.       gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  676.              x[0], y[0],
  677.              x[i], y[i]);
  678.       /*  only draw the arc if the lines are long enough  */
  679.       if ((SQR (x[i] - x[0]) + SQR (y[i] - y[0])) > ARC_RADIUS_2)
  680.         draw_arc++;
  681.     }
  682.     }
  683.   
  684.   if (measure_tool->num_points > 1 && draw_arc == measure_tool->num_points - 1)
  685.     {
  686.       angle1 = measure_tool->angle2 * 64.0;
  687.       angle2 = (measure_tool->angle1 - measure_tool->angle2) * 64.0;
  688.   
  689.       if (angle2 > 11520)
  690.       angle2 -= 23040;
  691.       if (angle2 < -11520)
  692.       angle2 += 23040;
  693.      
  694.       if (angle2 != 0)
  695.     {
  696.       gdk_draw_arc (measure_tool->core->win, measure_tool->core->gc, FALSE,
  697.             x[0] - ARC_RADIUS, y[0] - ARC_RADIUS,
  698.             2 * ARC_RADIUS, 2 * ARC_RADIUS, 
  699.             angle1, angle2); 
  700.       
  701.       if (measure_tool->num_points == 2)
  702.         gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  703.                x[0], y[0],
  704.                x[1] - x[0] <= 0 ? x[0] - ARC_RADIUS - (TARGET >> 1) : 
  705.                                   x[0] + ARC_RADIUS + (TARGET >> 1), 
  706.                y[0]);
  707.     }
  708.     }
  709. }
  710.  
  711. static void
  712. measure_tool_control (Tool       *tool,
  713.               ToolAction  action,
  714.               gpointer    gdisp_ptr)
  715. {
  716.   GDisplay * gdisp;
  717.   MeasureTool * measure_tool;
  718.  
  719.   gdisp = (GDisplay *) tool->gdisp_ptr;
  720.   measure_tool = (MeasureTool *) tool->private;
  721.  
  722.   switch (action)
  723.     {
  724.     case PAUSE:
  725.       draw_core_pause (measure_tool->core, tool);
  726.       break;
  727.  
  728.     case RESUME:
  729.       draw_core_resume (measure_tool->core, tool);
  730.       break;
  731.  
  732.     case HALT:
  733.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  734.       draw_core_stop (measure_tool->core, tool);
  735.       tool->state = INACTIVE;
  736.       break;
  737.  
  738.     default:
  739.       break;
  740.     }
  741. }
  742.  
  743. static void
  744. measure_tool_info_update (void)
  745. {
  746.   info_dialog_update (measure_tool_info);
  747.   info_dialog_popup  (measure_tool_info);
  748. }
  749.  
  750. static void
  751. measure_tool_info_window_close_callback (GtkWidget *widget,
  752.                      gpointer   client_data)
  753. {
  754.   info_dialog_free (measure_tool_info);
  755.   measure_tool_info = NULL;
  756. }
  757.  
  758. Tool *
  759. tools_new_measure_tool (void)
  760. {
  761.   Tool * tool;
  762.   MeasureTool * private;
  763.  
  764.   /*  The tool options  */
  765.   if (! measure_tool_options)
  766.     {
  767.       measure_tool_options = measure_tool_options_new ();
  768.       tools_register (MEASURE, (ToolOptions *) measure_tool_options);
  769.     }
  770.  
  771.   tool = tools_new_tool (MEASURE);
  772.   private = g_new0 (MeasureTool, 1);
  773.  
  774.   private->core       = draw_core_new (measure_tool_draw);
  775.   private->num_points = 0;
  776.   private->function   = CREATING;
  777.  
  778.   tool->private = (void *) private;
  779.  
  780.   tool->preserve = TRUE;  /*  Preserve tool across drawable changes  */
  781.  
  782.   tool->button_press_func   = measure_tool_button_press;
  783.   tool->button_release_func = measure_tool_button_release;
  784.   tool->motion_func         = measure_tool_motion;
  785.   tool->cursor_update_func  = measure_tool_cursor_update;
  786.   tool->control_func        = measure_tool_control;
  787.  
  788.   return tool;
  789. }
  790.  
  791. void
  792. tools_free_measure_tool (Tool *tool)
  793. {
  794.   GDisplay * gdisp;
  795.   MeasureTool * measure_tool;
  796.  
  797.   measure_tool = (MeasureTool *) tool->private;
  798.   gdisp = (GDisplay *) tool->gdisp_ptr;
  799.  
  800.   if (tool->state == ACTIVE)
  801.     {
  802.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  803.       draw_core_stop (measure_tool->core, tool);
  804.     }
  805.  
  806.   draw_core_free (measure_tool->core);
  807.  
  808.   if (measure_tool_info)
  809.     {
  810.       info_dialog_free (measure_tool_info);
  811.       measure_tool_info = NULL;
  812.     }
  813.   g_free (measure_tool);
  814. }
  815.