/* This is -*- C -*- */
/* $Id: guppi-axis-state.c,v 1.30 2002/01/14 05:00:55 trow Exp $ */

/*
 * guppi-axis-state.c
 *
 * Copyright (C) 2000 EMC Capital Management, Inc.
 * Copyright (C) 2001 The Free Software Foundation
 *
 * 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 <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>

#include <math.h>
#include <guppi-useful.h>
#include <guppi-data-flavor.h>
#include <guppi-data-socket.h>
#include <guppi-seq-scalar.h>

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

static GtkObjectClass *parent_class = NULL;

static void
guppi_axis_state_finalize (GtkObject *obj)
{
  if (parent_class->finalize)
    parent_class->finalize (obj);
}

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

static GuppiElementView *
make_view (GuppiElementState *state)
{
  return GUPPI_ELEMENT_VIEW (guppi_type_new (GUPPI_TYPE_AXIS_VIEW));
}

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

static void
guppi_axis_state_class_init (GuppiAxisStateClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;
  GuppiElementStateClass *state_class = GUPPI_ELEMENT_STATE_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_ELEMENT_STATE);

  object_class->finalize = guppi_axis_state_finalize;

  state_class->name = _("Axis");

  state_class->make_view = make_view;
}

static void
guppi_axis_state_init (GuppiAxisState *obj)
{
  GuppiAttributeBag *bag;
  double inch = guppi_in2pt (1.0);

  bag = guppi_element_state_attribute_bag (GUPPI_ELEMENT_STATE (obj));

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "data::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_SCALAR));

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_INT,       "position",       NULL, GUPPI_NORTH);

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_edge",      NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "edge_color",     NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "edge_thickness", NULL, inch / 64);

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_legend",    NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_STRING,    "legend",         NULL, "");
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "legend_color",   NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_FONT,      "legend_font",    NULL, guppi_default_font ());
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "legend_offset",  NULL, inch / 16);

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "label_offset",         NULL, inch / 32);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "rotate_labels",        NULL, FALSE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "shrink_labels_to_fit", NULL, TRUE);

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_lone_labels",        NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "extra_lone_label_offset", NULL, inch / 16);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "lone_label_color",        NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_FONT,      "lone_label_font",         NULL, guppi_default_font ());

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_major_ticks",     NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "major_tick_color",     NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "major_tick_thickness", NULL, inch / 64);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "major_tick_length",    NULL, inch / 12);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_major_labels",    NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "major_label_color",    NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_FONT,      "major_label_font",     NULL, guppi_default_font ());

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_minor_ticks",     NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "minor_tick_color",     NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "minor_tick_thickness", NULL, inch / 96);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "minor_tick_length",    NULL, inch / 16);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_minor_labels",    NULL, FALSE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "minor_label_color",    NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_FONT,      "minor_label_font",     NULL, guppi_default_font ());

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_micro_ticks",     NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "micro_tick_color",     NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "micro_tick_thickness", NULL, inch / 128);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION, "micro_tick_length",    NULL, inch / 24);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,   "show_micro_labels",    NULL, FALSE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,      "micro_label_color",    NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_FONT,      "micro_label_font",     NULL, guppi_default_font ());

}

GtkType guppi_axis_state_get_type (void)
{
  static GtkType guppi_axis_state_type = 0;
  if (!guppi_axis_state_type) {
    static const GtkTypeInfo guppi_axis_state_info = {
      "GuppiAxisState",
      sizeof (GuppiAxisState),
      sizeof (GuppiAxisStateClass),
      (GtkClassInitFunc) guppi_axis_state_class_init,
      (GtkObjectInitFunc) guppi_axis_state_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_axis_state_type = gtk_type_unique (GUPPI_TYPE_ELEMENT_STATE,
					     &guppi_axis_state_info);
  }
  return guppi_axis_state_type;
}

GuppiElementState *
guppi_axis_state_new (void)
{
  return GUPPI_ELEMENT_STATE (guppi_type_new (guppi_axis_state_get_type ()));
}

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

void
guppi_axis_state_tick_properties (GuppiAxisState *state,
				  const GuppiTick *tick,
				  gboolean *show_tick,
				  guint32 *color,
				  double *thickness,
				  double *length,
				  gboolean *show_label,
				  double *label_offset,
				  guint32 *label_color,
				  GnomeFont **label_font)
{
  g_return_if_fail (GUPPI_IS_AXIS_STATE (state));

  if (show_tick)
    *show_tick = FALSE;

  if (show_label)
    *show_label = FALSE;

  g_return_if_fail (state != NULL);
  g_return_if_fail (GUPPI_IS_AXIS_STATE (state));

  g_return_if_fail (tick != NULL);

  if (label_offset)
    guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			     "label_offset", label_offset,
			     NULL);

  switch (guppi_tick_type (tick)) {

  case GUPPI_TICK_NONE:

    if (show_tick)
      *show_tick = FALSE;
    if (color)
      *color = 0;
    if (thickness)
      *thickness = 0;
    if (length)
      *length = 0;
    
    guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			     "show_lone_labels",        show_label,
			     "extra_lone_label_offset", label_offset,
			     "lone_label_color",        label_color,
			     "lone_label_font",         label_font,
			     NULL);
    break;

  case GUPPI_TICK_MAJOR:
  case GUPPI_TICK_MAJOR_RULE:

    guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			     "show_major_ticks",     show_tick,
			     "major_tick_color",     color,
			     "major_tick_thickness", thickness,
			     "major_tick_length",    length,
			     "show_major_labels",    show_label,
			     "major_label_color",    label_color,
			     "major_label_font",     label_font,
			     NULL);
    break;

  case GUPPI_TICK_MINOR:
  case GUPPI_TICK_MINOR_RULE:

    guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			     "show_minor_ticks",     show_tick,
			     "minor_tick_color",     color,
			     "minor_tick_thickness", thickness,
			     "minor_tick_length",    length,
			     "show_minor_labels",    show_label,
			     "minor_label_color",    label_color,
			     "minor_label_font",     label_font,
			     NULL);
    break;

  case GUPPI_TICK_MICRO:
  case GUPPI_TICK_MICRO_RULE:

    guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			     "show_micro_ticks",     show_tick,
			     "micro_tick_color",     color,
			     "micro_tick_thickness", thickness,
			     "micro_tick_length",    length,
			     "show_micro_labels",    show_label,
			     "micro_label_color",    label_color,
			     "micro_label_font",     label_font,
			     NULL);
    break;

  default:

    g_assert_not_reached ();

  }
}

gchar *
guppi_axis_state_displayed_legend (GuppiAxisState *state)
{
  gboolean show_legend;
  gchar *legend = NULL;
  GuppiData *data = NULL;

  g_return_val_if_fail (GUPPI_IS_AXIS_STATE (state), NULL);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "show_legend", &show_legend,
			   "legend",      &legend,
			   "data",        &data,
			   NULL);

  if (show_legend && data != NULL) {
    legend = guppi_strdup (guppi_data_get_label (data));
  }

  guppi_unref (data);

  return legend;
}

double
guppi_axis_state_legend_span (GuppiAxisState *state)
{
  gchar *txt;
  GnomeFont *font;
  gint span = 0;
  double offset;

  g_return_val_if_fail (GUPPI_IS_AXIS_STATE (state), 0);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "legend_font", &font,
			   "legend_offset", &offset,
			   NULL);

  txt = guppi_axis_state_displayed_legend (state);

  if (txt && *txt && font) {
    span = gnome_font_get_ascender (font) + gnome_font_get_descender (font) + offset;
  }

  guppi_free (txt);

  return span;
}

double
guppi_axis_state_maximum_span (GuppiAxisState *state,
			       double label_scale,
			       GuppiAxisMarkers *marks)
{
  gint i;
  double offset, max_span = 0, edge_thickness = 0;
  guppi_compass_t position;
  gboolean show_edge, care_about_width, rotate_labels;

  g_return_val_if_fail (GUPPI_IS_AXIS_STATE (state), 0);
  g_return_val_if_fail (label_scale > 0, 0);
  g_return_val_if_fail (GUPPI_IS_AXIS_MARKERS (marks), 0);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "position",       &position,
			   "show_edge",      &show_edge,
			   "edge_thickness", &edge_thickness,
			   "rotate_labels",  &rotate_labels,
			   "label_offset",   &offset,
			   NULL);

  if (show_edge) {
    /* Make our span slightly larger than the edge thickness, which
       tends to be a very small number. */
    max_span = edge_thickness * 1.20;
  }

  care_about_width = (position == GUPPI_WEST || position == GUPPI_EAST);
  if (rotate_labels)
    care_about_width = !care_about_width;

  for (i = 0; i < guppi_axis_markers_size (marks); ++i) {
    const GuppiTick *tick = guppi_axis_markers_get (marks, i);
    gboolean show_tick, show_label;
    double length;
    GnomeFont *font;
    double span = 0;

    guppi_axis_state_tick_properties (state, tick,
				      &show_tick, NULL, NULL, &length,
				      &show_label, &offset, NULL, &font);

    if (show_tick) {
      span += length;
    }

    if (show_label) {
      span += offset;
      if (care_about_width) {
	if (guppi_tick_label (tick) != NULL)
	  span += label_scale * gnome_font_get_width_string (font, guppi_tick_label (tick));
      } else {
	span += label_scale * (gnome_font_get_ascender (font) + gnome_font_get_descender (font));
      }
    }

    max_span = MAX (span, max_span);
  }

  max_span += guppi_axis_state_legend_span (state);

  /* This should become an optional parameter: */
  /* Round up to nearest eighth of an inch (72/8=9) */
  /* max_span = 9*ceil(max_span/9); */

  return max_span;
}

double
guppi_axis_state_label_shrink_to_fit_factor (GuppiAxisState *state,
					     GuppiAxisMarkers *marks,
					     double span)
{
  gint i;
  gboolean rotate_labels;
  double offset, min_factor = 1;

  g_return_val_if_fail (GUPPI_IS_AXIS_STATE (state), 0);
  g_return_val_if_fail (GUPPI_IS_AXIS_MARKERS (marks), 0);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "rotate_labels", &rotate_labels,
			   NULL);

  /* This is not exactly the right thing to do... */
  if (!rotate_labels)
    return 1.0;

  for (i = 0; i < guppi_axis_markers_size (marks); ++i) {
    const GuppiTick *tick = guppi_axis_markers_get (marks, i);
    gboolean show_tick, show_label;
    double length;
    GnomeFont *font;

    guppi_axis_state_tick_properties (state, tick,
				      &show_tick, NULL, NULL, &length,
				      &show_label, &offset, NULL, &font);

    if (show_label && guppi_tick_label (tick) != NULL && font != NULL) {

      if (!show_tick)
	length = 0;

      if (show_label) {
	double w = gnome_font_get_width_string (font, guppi_tick_label (tick));
	double t = (span - length - offset) / w;
	min_factor = MIN (t, min_factor);
      }
    }
  }

  return min_factor;
}

void
guppi_axis_state_get_size (GuppiAxisState *state, 
			   double text_scale,
			   GuppiAxisMarkers *am,
			   double *w, double *h)
{
  guppi_compass_t pos;
  double span = 0;

  g_return_if_fail (GUPPI_IS_AXIS_STATE (state));

  if (text_scale < 1e-8) {
    text_scale = 1.0;
  }

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

  if (am != NULL) {

    span = guppi_axis_state_maximum_span (state, text_scale, am);

  }

  if (pos == GUPPI_NORTH || pos == GUPPI_SOUTH) {
    if (w) *w = -1;
    if (h) *h = span;
  } else {
    if (w) *w = span;
    if (h) *h = -1;
  }
}

/* $Id: guppi-axis-state.c,v 1.30 2002/01/14 05:00:55 trow Exp $ */


syntax highlighted by Code2HTML, v. 0.9.1