
/*
 * canvasLine.c --
 *
 * This file implements the line item of the canvas widget
 *
 * Copyright (c) 2001 - 2002 P.G. Baum
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */

/*
   History:
   2002-01: removed "smooth" and "splineSteps" options since they are not
            implemented in gnome-canvas-line.c
   2001-05: Begin of developement
 */

#include "canvas.h"
#include <string.h>
#include <assert.h>


typedef struct
{
   Gnocl_ListBool   coords;
   Gnocl_ObjBool    fill;
   Gnocl_DoubleBool width;
   Gnocl_ObjBool    arrow;     /* none|first|last|both */
   Gnocl_ListBool   arrowShape;  /* size 3 */
#if 0
   /* smooth and spline_steps are still not implemented 
      in gnome-canvas-line.c */
   Gnocl_BoolBool   smooth;
   Gnocl_IntBool    splineSteps;
#endif
   Gnocl_ObjBool    capStyle;  /* butt|projecting|round */
   Gnocl_ObjBool    joinStyle; /* bevel|miter|round */
} LineParams;

static const GnoclWidgetOptions lineOptions[] = 
{
   { "-coords", GNOCL_LIST, GNOCL_OFFSET( LineParams, coords ) },
   { "-fill", GNOCL_OBJ, GNOCL_OFFSET( LineParams, fill ) },
   { "-width", GNOCL_DOUBLE, GNOCL_OFFSET( LineParams, width ) },
   { "-arrow", GNOCL_OBJ, GNOCL_OFFSET( LineParams, arrow ) },
   { "-arrowShape", GNOCL_LIST, GNOCL_OFFSET( LineParams, arrowShape ) },
#if 0
   { "-smooth", GNOCL_BOOL, GNOCL_OFFSET( LineParams, smooth ) },
   { "-splineSteps", GNOCL_INT, GNOCL_OFFSET( LineParams, splineSteps ) },
#endif
   { "-capStyle", GNOCL_OBJ, GNOCL_OFFSET( LineParams, capStyle ) },
   { "-joinStyle", GNOCL_OBJ, GNOCL_OFFSET( LineParams, joinStyle ) },
   { NULL, 0, 0 }
};

static int freeParams( LineParams *params )
{
   GNOCL_FREE_TCL_OPT( params->fill );

   return 0;
}

static int canvasDeleteLine( Tcl_Interp *interp, 
      Gnocl_CanvasItemInfo *info )
{
   /* no internal params, so no need to free anything */
   return 0;
}

static int gnoclGetCapStyle( Tcl_Interp *interp, Tcl_Obj *obj, 
      GdkCapStyle *style )
{
   char *styles[] = { "notLast", "butt", "round", "projecting", NULL };
   GdkCapStyle gStyles[] = { GDK_CAP_NOT_LAST, GDK_CAP_BUTT,
          GDK_CAP_ROUND, GDK_CAP_PROJECTING };

   int idx;
   if( Tcl_GetIndexFromObj( interp, obj, styles, "cap style",
         TCL_EXACT, &idx ) != TCL_OK )
      return TCL_ERROR;

   *style = gStyles[idx];
   return TCL_OK;
}

static int gnoclGetJoinStyle( Tcl_Interp *interp, Tcl_Obj *obj, 
      GdkJoinStyle *style )
{
   char *styles[] = { "miter", "round", "bevel", NULL };
   GdkJoinStyle gStyles[] = { GDK_JOIN_MITER, GDK_JOIN_ROUND, 
         GDK_JOIN_BEVEL };

   int idx;
   if( Tcl_GetIndexFromObj( interp, obj, styles, "join style",
         TCL_EXACT, &idx ) != TCL_OK )
      return TCL_ERROR;

   *style = gStyles[idx];
   return TCL_OK;
}

static int gnoclGetArrowHead( Tcl_Interp *interp, Tcl_Obj *obj )
{
   char *styles[] = { "none", "first", "last", "both", NULL };
   int retVal[] = { 0, 1, 2, 3 };

   int idx;
   if( Tcl_GetIndexFromObj( interp, obj, styles, "join style",
         TCL_EXACT, &idx ) != TCL_OK )
      return -1;

   return retVal[idx];
}

static int setParams( Tcl_Interp *interp, LineParams *params,
      Gnocl_CanvasItemInfo *info )
{
   if( params->coords.val )
   {
      GnomeCanvasPoints *points;
      int   noCoords;
      int   n;
      if( Tcl_ListObjLength( interp, params->coords.val, &noCoords ) != TCL_OK
            || noCoords < 4 || ( noCoords % 2 != 0 ) )
      {
         Tcl_SetResult( interp, "list-of-coordinates must contain an "
               "even numer of elements (at least 4)", TCL_STATIC );
         return TCL_ERROR;
      }
      points = gnome_canvas_points_new( noCoords / 2 );
      for( n = 0; n < noCoords; ++n )
      {
         Tcl_Obj *tp;
         if( Tcl_ListObjIndex( interp, params->coords.val, n, &tp ) != TCL_OK 
               || Tcl_GetDoubleFromObj( interp, tp, &points->coords[n] ) 
               != TCL_OK )
         {
            gnome_canvas_points_free( points );
            freeParams( params );
            return TCL_ERROR;
         }
      }
      gnome_canvas_item_set( info->item, "points", points, NULL );
      gnome_canvas_points_free( points );
   }

   if( params->fill.changed )
   {
      GdkColor color;
      int      alpha;
      if( gnoclGetGdkColorAlloc( interp, params->fill.val, 
            GTK_WIDGET( info->canvas ), &color, &alpha ) != TCL_OK )
         return TCL_ERROR;
      if( alpha == 0 ) /* it seems that transparancy does not work for line */
         gnome_canvas_item_set( info->item, "fill_color", NULL, NULL );
      else
         gnome_canvas_item_set( info->item, "fill_color_gdk", &color, NULL );
   }

   if( params->width.changed )
      gnome_canvas_item_set( info->item, 
            "width_units", params->width.val, NULL );

   if( params->arrow.changed )
   {
      int head = gnoclGetArrowHead( interp, params->arrow.val );
      if( head < 0 )
      {
         freeParams( params );
         return TCL_ERROR;
      }
      gnome_canvas_item_set( info->item, 
            "first_arrowhead", (gboolean)(head & 1), 
            "last_arrowhead", (gboolean)((head & 2) != 0), 
            NULL );
      /* TODO: if no arrowShape is set, set a reasonable one (5 10 5?) */
   }
   if( params->arrowShape.changed )
   {
      int   no;
      double arrow_shape_a, arrow_shape_b, arrow_shape_c;
      Tcl_Obj *tp;
      Tcl_Obj * const as = params->arrowShape.val;

      if( Tcl_ListObjLength( interp, as, &no ) != TCL_OK
            || no != 3 )
      {
         Tcl_SetResult( interp, 
               "arrow shape must be a list of 3 double values", TCL_STATIC );
         return TCL_ERROR;
      }

      if( Tcl_ListObjIndex( interp, as, 0, &tp ) != TCL_OK 
            || Tcl_GetDoubleFromObj( interp, tp, &arrow_shape_a ) 
            || Tcl_ListObjIndex( interp, as, 1, &tp ) != TCL_OK 
            || Tcl_GetDoubleFromObj( interp, tp, &arrow_shape_b ) 
            || Tcl_ListObjIndex( interp, as, 2, &tp ) != TCL_OK 
            || Tcl_GetDoubleFromObj( interp, tp, &arrow_shape_c )  )
      {
         freeParams( params );
         return TCL_ERROR;
      }
      gnome_canvas_item_set( info->item, "arrow_shape_a", arrow_shape_a, 
            "arrow_shape_b", arrow_shape_b, "arrow_shape_c", arrow_shape_c,
            NULL );
   }

#if 0
   if( params->smooth.changed )
   {
      gnome_canvas_item_set( info->item, 
         "smooth", (gboolean)params->smooth.val,
         NULL );
   }
   if( params->splineSteps.changed )
   {
      gnome_canvas_item_set( info->item, 
         "spline_steps", (guint)params->splineSteps.val, NULL );
   }
#endif

   if( params->capStyle.changed )
   {
      GdkCapStyle style;
      if( gnoclGetCapStyle( interp, params->capStyle.val, &style ) != TCL_OK )
      {
         freeParams( params );
         return TCL_ERROR;
      }
      gnome_canvas_item_set( info->item, "cap_style", style, NULL );
   }

   if( params->joinStyle.changed )
   {
      GdkJoinStyle style;
      if( gnoclGetJoinStyle( interp, params->joinStyle.val, &style ) 
            != TCL_OK )
      {
         freeParams( params );
         return TCL_ERROR;
      }
      gnome_canvas_item_set( info->item, "join_style", style, NULL );
   }

   return TCL_OK;
}

static int canvasConfigLine( Tcl_Interp *interp, int objc, 
      Tcl_Obj * const objv[], Gnocl_CanvasItemInfo *info )
{
   LineParams params;
   memset( &params, 0, sizeof( params ) );
   /* canvas itemconfigure tag params ... */
   if( gnoclParseAllWidgetOpts( interp, objc, objv, 2,
         lineOptions, (char *)&params ) != TCL_OK )
      return TCL_ERROR;

   return setParams( interp, &params, info );
}


Gnocl_CanvasItemInfo *gnoclCanvasCreateLine( Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[], GnomeCanvas *widget )
{
   Gnocl_CanvasItemInfo *info;
   LineParams params;

   memset( &params, 0, sizeof( params ) );
   if( gnoclParseAllWidgetOpts( interp, objc, objv, 3,
         lineOptions, (char *)&params ) != TCL_OK )
      return NULL;

   if( params.coords.changed == 1 )
   {
      Tcl_SetResult( interp, 
            "in creating coordinates may not be given as option",
            TCL_STATIC );
      return NULL;
   }

   params.coords.val = objv[3];

   info = g_new( Gnocl_CanvasItemInfo, 1 );
   info->canvas = widget;
   info->deleteFunc = &canvasDeleteLine;
   info->configFunc = &canvasConfigLine;
   info->group = gnome_canvas_root( widget );
   info->item = gnome_canvas_item_new( info->group, 
         gnome_canvas_line_get_type( ), 
         "fill_color", "black", 
         "spline_steps", (guint)10, 
         "arrow_shape_a", 5., 
         "arrow_shape_b", 10., 
         "arrow_shape_c", 5.,
         NULL );

   setParams( interp, &params, info );

   return info;
}

