/* This is -*- C -*- */
/* $Id: guppi-axis-item.c,v 1.37 2002/01/20 05:21:09 trow Exp $ */

/*
 * guppi-axis-item.c
 *
 * Copyright (C) 2000 EMC Capital Management, Inc.
 * Copyright (C) 2001 Free Software Foundation, Inc.
 *
 * Developed by Jon Trowbridge <trow@gnu.org> and
 * Havoc Pennington <hp@pobox.com>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include <config.h>
#include "guppi-axis-item.h"

#include <guppi-useful.h>
#include <guppi-alpha-template.h>
#include <guppi-seq-scalar.h>
#include <guppi-basic-tools.h>

#include "guppi-axis-view.h"
#include "guppi-axis-state.h"

static GtkObjectClass *parent_class = NULL;

static void
guppi_axis_item_destroy (GtkObject *obj)
{
  if (parent_class->destroy)
    parent_class->destroy (obj);
}

static void
guppi_axis_item_finalize (GtkObject *obj)
{
  GuppiAxisItem *item = GUPPI_AXIS_ITEM (obj);
  GList *iter;

  if (item->label_list) {
    iter = item->label_list;
    while (iter != NULL) {
      guppi_unref (iter->data);
      iter = g_list_next (iter);
    }
  }

  guppi_unref0 (item->legend_text);

  if (parent_class->finalize)
    parent_class->finalize (obj);
}

/**************************************************************************/

static void
prepare_legend (GuppiCanvasItem *gci)
{
  GuppiAxisState *state;
  GuppiAxisItem *item;
  GnomeFont *font;
  gint pos;
  double rot;
  gchar *txt;
  double sc;

  GuppiTextBlock *legend_block;

  g_return_if_fail (gci != NULL);

  state = GUPPI_AXIS_STATE (guppi_canvas_item_state (gci));
  item = GUPPI_AXIS_ITEM (gci);

  sc = guppi_canvas_item_scale (gci);

  txt = guppi_axis_state_displayed_legend (state);
  if (txt == NULL) {
    guppi_unref0 (item->legend_text);
    return;
  }

  if (item->legend_text == NULL)
    item->legend_text = guppi_raster_text_new (NULL);

  legend_block = guppi_raster_text_block (item->legend_text);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "position", &pos,
			   "legend_font", &font,
			   NULL);

  rot = 0;
  if (pos == GUPPI_WEST)
    rot = -90;
  else if (pos == GUPPI_EAST)
    rot = +90;

  guppi_text_block_set_angle (legend_block, rot);
  guppi_text_block_set_text (legend_block, txt);
  guppi_text_block_set_font (legend_block, font);

  guppi_raster_text_set_scale (item->legend_text, sc);

  guppi_free  (txt);
  guppi_unref (font);
}

static void
prepare_labels (GuppiCanvasItem *gci)
{
  GuppiAxisMarkers *marks;
  GuppiAxisItem *item;
  GuppiAxisState *state;
  GuppiElementView *view;
  GList *iter;
  GList *new_list;
  gint i, N, pos;
  double sc, size, span, rot = 0;
  gboolean rotate_labels, shrink_to_fit;

  g_return_if_fail (gci != NULL);

  item = GUPPI_AXIS_ITEM (gci);
  state = GUPPI_AXIS_STATE (guppi_canvas_item_state (gci));
  view = guppi_canvas_item_view (gci);
  sc = guppi_canvas_item_scale (gci);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "position", &pos,
			   "rotate_labels", &rotate_labels,
			   "shrink_labels_to_fit", &shrink_to_fit,
			   NULL);

  if (pos == GUPPI_NORTH || pos == GUPPI_SOUTH) {
    
    marks = guppi_element_view_axis_markers (view, GUPPI_X_AXIS);
    if (rotate_labels)
      rot = -90;
    span = guppi_geometry_height (guppi_element_view_geometry (view));

  } else {

    marks = guppi_element_view_axis_markers (view, GUPPI_Y_AXIS);
    if (rotate_labels)
      rot = -90;
    span = guppi_geometry_width (guppi_element_view_geometry (view));

  }

  if (marks == NULL)
    return;

  if (shrink_to_fit) {
    item->label_scale = guppi_axis_view_label_shrink_to_fit_factor ((GuppiAxisView *) view, marks);
  } else {
    item->label_scale = 1.0;
  }
  
  N = guppi_axis_markers_size (marks);

  new_list = NULL;

  for (i = 0; i < N; ++i) {
    const GuppiTick *tick;
    GnomeFont *font = NULL;
    gboolean shown = FALSE;

    tick = guppi_axis_markers_get (marks, i);

    if (tick != NULL)
      guppi_axis_view_tick_properties (GUPPI_AXIS_VIEW (view), tick, item->label_scale,
				       NULL, NULL, NULL, NULL,
				       &shown, NULL, NULL, &font);

    if (shown && tick->label) {
      GuppiRasterText *raster_text = NULL;

      /* We attempt to re-use our previous text rasterizations. */
      /* (This crappy linear search is ugly, but mostly harmless) */
      iter = item->label_list;
      while (iter != NULL && raster_text == NULL) {
	if (iter->data) {
	  GuppiRasterText *rt = GUPPI_RASTER_TEXT (iter->data);
	  GuppiTextBlock *block = guppi_raster_text_block (rt);
	  gchar *txt = guppi_text_block_text (block);
	  if (txt && !strcmp (tick->label, txt)) {
	    raster_text = rt;
	    iter->data = NULL;
	  }
	  guppi_free (txt);
	}
	
	iter = g_list_next (iter);
      }

      size = sc * item->label_scale;

      /* If we didn't find anything to re-use, create a clean slate. */
      if (raster_text == NULL && font != NULL) {
	GuppiTextBlock *block;
	raster_text = guppi_raster_text_new (NULL);
	block = guppi_raster_text_block (raster_text);

	guppi_text_block_set_text (block, guppi_tick_label (tick));
	guppi_text_block_set_font (block, font);
	guppi_text_block_set_angle (block, rot);
      }

      if (size > 0)
	guppi_raster_text_set_scale (raster_text, size);

      gtk_object_set_user_data (GTK_OBJECT (raster_text), (gpointer)tick);
	
      new_list = g_list_append (new_list, raster_text);

    }
  }

  /* Clean up the old list and replace it with the new one. */
  iter = item->label_list;
  while (iter != NULL) {
    guppi_unref (iter->data);
    iter = g_list_next (iter);
  }
  g_list_free (item->label_list);

  item->label_list = new_list;

}

static void
update (GuppiCanvasItem *gci, double aff[6], ArtSVP *svp, gint flags)
{
  prepare_legend (gci);
  prepare_labels (gci);
}

static void
render (GuppiCanvasItem *gci, GnomeCanvasBuf *buf)
{
  GuppiAxisItem *axi = GUPPI_AXIS_ITEM (gci);
  GuppiElementView *view = guppi_canvas_item_view (gci);
  GuppiAxisState *state = GUPPI_AXIS_STATE (guppi_canvas_item_state (gci));
  double sc = guppi_canvas_item_scale (gci);

  gint cx0, cy0, cx1, cy1;
  double x0 = 0, y0 = 0, x1 = 0, y1 = 0;

  gint pos;
  gboolean horizontal, show_edge;
  double edge_thickness;
  guint32 edge_color, legend_color;

  guppi_canvas_item_get_bbox_c (gci, &cx0, &cy0, &cx1, &cy1);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "position", &pos,
			   "show_edge", &show_edge,
			   "edge_thickness", &edge_thickness,
			   "edge_color", &edge_color,
			   "legend_color", &legend_color,
			   NULL);
  horizontal = pos == GUPPI_NORTH || pos == GUPPI_SOUTH;

  /* First, paint the edge (if necessary) */

  if (show_edge && edge_thickness > 0) {
    double t;

    t = edge_thickness * sc;
    if (horizontal) {
      t = guppi_y_pt2px (t);
    } else {
      t = guppi_x_pt2px (t);
    }

    switch (pos) {
    case GUPPI_NORTH:
      x0 = cx0;
      x1 = cx1;
      y0 = cy1 - t;
      y1 = cy1;
      break;

    case GUPPI_SOUTH:
      x0 = cx0;
      x1 = cx1;
      y0 = cy0;
      y1 = cy0 + t;
      break;

    case GUPPI_WEST:
      x0 = cx1;
      x1 = cx1 - t;
      y0 = cy0;
      y1 = cy1;
      break;

    case GUPPI_EAST:
      x0 = cx0;
      x1 = cx0 + t;
      y0 = cy0;
      y1 = cy1;
      break;

    default:
      g_assert_not_reached ();
    }

    guppi_paint_sharp_box (buf, x0, y0, x1, y1, edge_color);
  }

  /* Next, paint the legend (if necessary) */

  if (axi->legend_text != NULL) {
    double lx = 0, ly = 0;
    double w, h;
    guint r, g, b, a;

    w = guppi_raster_text_template (axi->legend_text)->width;
    h = guppi_raster_text_template (axi->legend_text)->height;

    switch (pos) {

    case GUPPI_NORTH:
      lx = (cx0 + cx1) / 2 - w / 2;
      ly = cy0;
      break;

    case GUPPI_SOUTH:
      lx = (cx0 + cx1) / 2 - w / 2;
      ly = cy1 - h;
      break;

    case GUPPI_WEST:
      lx = cx0;
      ly = (cy0 + cy1) / 2 - h / 2;
      break;

    case GUPPI_EAST:
      lx = cx1 - w;
      ly = (cy0 + cy1) / 2 - h / 2;
      break;

    default:
      g_assert_not_reached ();

    }

    UINT_TO_RGBA (legend_color, &r, &g, &b, &a);

    guppi_alpha_template_print (guppi_raster_text_template (axi->legend_text),
				lx, ly, r, g, b, a, buf);

  }

  /* Next, paint the markers (again, if necessary) */

  {
    GuppiAxisMarkers *marks = NULL;

    marks = guppi_element_view_axis_markers (view, horizontal ? GUPPI_X_AXIS : GUPPI_Y_AXIS);

    if (marks) {
      gint i, N;
      GList *label_iter;

      N = guppi_axis_markers_size (marks);
      /* label_iter = axi->label_list; */

      for (i = 0; i < N; ++i) {
	const GuppiTick *tick = guppi_axis_markers_get (marks, i);
	double tick_pos = tick->position, pos_c = 0;
	gboolean show = FALSE, show_label = FALSE;
	double length = 0, thick = 0, label_offset = 0;
	guint32 tick_color = 0, label_color = 0;
	GnomeFont *font = NULL;

	guppi_axis_view_tick_properties (GUPPI_AXIS_VIEW (view), tick, axi->label_scale,
					 &show, &tick_color, &thick, &length,
					 &show_label, &label_offset,
					 &label_color, &font);

	if (show || show_label) {
	  if (horizontal)
	    guppi_canvas_item_vp2c_d (gci, tick_pos, 0, &pos_c, NULL);
	  else
	    guppi_canvas_item_vp2c_d (gci, 0, tick_pos, NULL, &pos_c);
	}

	/* Paint tick */
	if (show && length > 0 && thick > 0) {

	  label_offset += length;

	  if (horizontal) {
	    length = guppi_y_pt2px (length);
	    thick = guppi_x_pt2px (thick);
	  } else {
	    length = guppi_x_pt2px (length);
	    thick = guppi_y_pt2px (thick);
	  }

	  length *= sc;
	  thick *= sc;

	  switch (pos) {

	  case GUPPI_NORTH:
	    x0 = pos_c - thick / 2;
	    x1 = pos_c + thick / 2;
	    y0 = cy1 - length;
	    y1 = cy1;
	    break;

	  case GUPPI_SOUTH:
	    x0 = pos_c - thick / 2;
	    x1 = pos_c + thick / 2;
	    y0 = cy0;
	    y1 = cy0 + length;
	    break;

	  case GUPPI_WEST:
	    x0 = cx1 - length;
	    x1 = cx1;
	    y0 = pos_c - thick / 2;
	    y1 = pos_c + thick / 2;
	    break;

	  case GUPPI_EAST:
	    x0 = cx0;
	    x1 = cx0 + length;
	    y0 = pos_c - thick / 2;
	    y1 = pos_c + thick / 2;
	    break;

	  default:
	    g_assert_not_reached ();
	  }

	  guppi_paint_sharp_box (buf, x0, y0, x1, y1, tick_color);
	}

	/* Paint label */
	if (show_label && font != NULL) {
	  GuppiRasterText *raster_text = NULL;
	  gint xl = 0, yl = 0;
	  guint r, g, b, a;

	  label_offset *= sc;

	  label_iter = axi->label_list;
	  while (label_iter != NULL && raster_text == NULL) {
	    GuppiRasterText *rt = GUPPI_RASTER_TEXT (label_iter->data);
	    if (gtk_object_get_user_data (GTK_OBJECT (rt)) == (gpointer) tick)
	      raster_text = rt;
	    label_iter = g_list_next (label_iter);
	  }

	  if (raster_text) {

	    gboolean print_template = TRUE;
	    GuppiAlphaTemplate *template;

	    template = guppi_raster_text_template (raster_text);

	    /* template can be NULL if we try to rasterize the empty string */
	    if (template) {


	      switch (pos) {

	      case GUPPI_NORTH:
		xl = pos_c - template->width / 2;
		yl = cy1 - guppi_y_pt2px (label_offset) - template->height;
		break;

	      case GUPPI_SOUTH:
		xl = pos_c - template->width / 2;
		yl = cy0 + guppi_y_pt2px (label_offset);
		break;

	      case GUPPI_WEST:
		xl = cx1 - guppi_x_pt2px (label_offset) - template->width;
		yl = pos_c - template->height / 2;
		break;

	      case GUPPI_EAST:
		xl = cx0 + guppi_x_pt2px (label_offset);
		yl = pos_c - template->height / 2;
		break;

	      default:
		g_assert_not_reached ();
	      }

	      UINT_TO_RGBA (label_color, &r, &g, &b, &a);

	      if (print_template)
		guppi_alpha_template_print (template, xl, yl,
					    r, g, b, a, buf);
	    }
	  }
	}

      }
    }
  }
}

static gboolean
data_drop (GuppiCanvasItem *gci, GuppiData *data)
{
  if (data == NULL || GUPPI_IS_SEQ_SCALAR (data)) {
    guppi_element_state_set (guppi_canvas_item_state (gci),
			     "data", data,
			     NULL);
    return TRUE;
  }

  return FALSE;
}

/**************************************************************************/

static GuppiPlotToolkit *
guppi_axis_toolkit_default (void)
{
  GuppiPlotToolkit *tk = guppi_plot_toolkit_new (_("Default"));

  guppi_plot_toolkit_set_button_tool (tk, 1, 0,
				      guppi_basic_tool_new_rescale (0.9));
  guppi_plot_toolkit_set_button_tool (tk, 2, 0, guppi_basic_tool_new_drag ());
  guppi_plot_toolkit_set_button_tool (tk, 3, 0,
				      guppi_basic_tool_new_rescale (1 / 0.9));

  guppi_add_keyboard_navigation_to_toolkit (tk);

  return tk;
}

static void
guppi_axis_item_class_init (GuppiAxisItemClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;
  GuppiCanvasItemClass *gci_class = GUPPI_CANVAS_ITEM_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_CANVAS_ITEM);

  object_class->destroy = guppi_axis_item_destroy;
  object_class->finalize = guppi_axis_item_finalize;

  gci_class->guppi_update = update;
  gci_class->guppi_render = render;
  gci_class->data_drop = data_drop;

  gci_class->uses_vp_coordinates = TRUE;

  guppi_canvas_item_class_set_item_class_toolkit (gci_class, guppi_axis_toolkit_default ());

}

static void
guppi_axis_item_init (GuppiAxisItem *obj)
{

}

GtkType guppi_axis_item_get_type (void)
{
  static GtkType guppi_axis_item_type = 0;
  if (!guppi_axis_item_type) {
    static const GtkTypeInfo guppi_axis_item_info = {
      "GuppiAxisItem",
      sizeof (GuppiAxisItem),
      sizeof (GuppiAxisItemClass),
      (GtkClassInitFunc) guppi_axis_item_class_init,
      (GtkObjectInitFunc) guppi_axis_item_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_axis_item_type = gtk_type_unique (GUPPI_TYPE_CANVAS_ITEM,
					    &guppi_axis_item_info);
  }
  return guppi_axis_item_type;
}

/* $Id: guppi-axis-item.c,v 1.37 2002/01/20 05:21:09 trow Exp $ */


syntax highlighted by Code2HTML, v. 0.9.1