/* This is -*- C -*- */
/* $Id: guppi-axis-view.c,v 1.18 2002/01/22 02:01:14 trow Exp $ */
/*
* guppi-axis-view.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 "guppi-axis-view.h"
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>
#include <guppi-memory.h>
#include "guppi-axis-state.h"
#include "guppi-axis-item.h"
#include "guppi-axis-print.h"
static GtkObjectClass *parent_class = NULL;
static void
guppi_axis_view_finalize (GtkObject *obj)
{
if (parent_class->finalize)
parent_class->finalize (obj);
}
static void
compute_size (GuppiElementView *view)
{
GuppiElementState *state = guppi_element_view_state (view);
GuppiAxisMarkers *am;
guppi_compass_t pos;
gboolean shrink_to_fit;
double w, h, scale = 1.0;
guppi_element_state_get (state,
"position", &pos,
"shrink_labels_to_fit", &shrink_to_fit,
NULL);
if (pos == GUPPI_NORTH || pos == GUPPI_SOUTH) {
am = guppi_element_view_axis_markers (view, GUPPI_X_AXIS);
} else {
am = guppi_element_view_axis_markers (view, GUPPI_Y_AXIS);
}
/* Because of problems with how the layout engine does things, this doesn't
quite work right now... changed signals don't play out the way they should,
so we seem to end up with truncated labels way too often. I'm turning this
off for now. */
#if 0
if (shrink_to_fit && am) {
scale = guppi_axis_view_label_shrink_to_fit_factor (GUPPI_AXIS_VIEW (view), am);
if (scale < 1e-8)
scale = 1.0;
}
#endif
guppi_axis_state_get_size (GUPPI_AXIS_STATE (state), scale, am, &w, &h);
guppi_element_view_changed_size (view, w, h);
}
static void
changed_state (GuppiElementView *view)
{
compute_size (view);
if (GUPPI_ELEMENT_VIEW_CLASS (parent_class)->changed_state)
GUPPI_ELEMENT_VIEW_CLASS (parent_class)->changed_state (view);
}
static void
changed (GuppiElementView *view)
{
compute_size (view);
if (GUPPI_ELEMENT_VIEW_CLASS (parent_class)->changed)
GUPPI_ELEMENT_VIEW_CLASS (parent_class)->changed (view);
}
/**************************************************************************/
static void
view_init (GuppiElementView *view)
{
guppi_compass_t pos;
guppi_element_state_get (guppi_element_view_state (view),
"position", &pos,
NULL);
#if 0
guppi_element_view_set_axis_marker_type (view,
(pos == GUPPI_NORTH || pos == GUPPI_SOUTH) ? GUPPI_X_AXIS : GUPPI_Y_AXIS,
GUPPI_AXIS_SCALAR);
#endif
compute_size (view);
if (GUPPI_ELEMENT_VIEW_CLASS (parent_class)->view_init)
GUPPI_ELEMENT_VIEW_CLASS (parent_class)->view_init (view);
}
static void
guppi_axis_view_class_init (GuppiAxisViewClass *klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
GuppiElementViewClass *view_class = GUPPI_ELEMENT_VIEW_CLASS (klass);
parent_class = gtk_type_class (GUPPI_TYPE_ELEMENT_VIEW);
object_class->finalize = guppi_axis_view_finalize;
view_class->changed_state = changed_state;
view_class->changed = changed;
view_class->canvas_item_type = GUPPI_TYPE_AXIS_ITEM;
view_class->print_type = GUPPI_TYPE_AXIS_PRINT;
view_class->view_init = view_init;
}
static void
guppi_axis_view_init (GuppiAxisView *obj)
{
}
GtkType guppi_axis_view_get_type (void)
{
static GtkType guppi_axis_view_type = 0;
if (!guppi_axis_view_type) {
static const GtkTypeInfo guppi_axis_view_info = {
"GuppiAxisView",
sizeof (GuppiAxisView),
sizeof (GuppiAxisViewClass),
(GtkClassInitFunc) guppi_axis_view_class_init,
(GtkObjectInitFunc) guppi_axis_view_init,
NULL, NULL, (GtkClassInitFunc) NULL
};
guppi_axis_view_type =
gtk_type_unique (GUPPI_TYPE_ELEMENT_VIEW, &guppi_axis_view_info);
}
return guppi_axis_view_type;
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
void
guppi_axis_view_tick_properties (GuppiAxisView *view,
const GuppiTick *tick,
double label_scale,
gboolean *show_tick,
guint32 *color,
double *thickness,
double *length,
gboolean *show_label,
double *label_offset,
guint32 *label_color,
GnomeFont **label_font)
{
GuppiAxisState *state;
gboolean our_show_label;
guppi_compass_t pos;
gboolean rotate_labels;
GnomeFont *our_label_font;
g_return_if_fail (GUPPI_IS_AXIS_VIEW (view));
state = GUPPI_AXIS_STATE (guppi_element_view_state (GUPPI_ELEMENT_VIEW (view)));
guppi_axis_state_tick_properties (state, tick,
show_tick, color, thickness, length,
&our_show_label, label_offset, label_color, &our_label_font);
guppi_element_state_get ((GuppiElementState *) state,
"position", &pos,
"rotate_labels", &rotate_labels,
NULL);
/* Check if we should override showing the label because of being on the edge */
if (our_show_label && tick->label && *tick->label && !tick->critical_label) {
double x0, y0, x1, y1, t, label_w, label_h;
label_w = label_scale * gnome_font_get_width_utf8 (our_label_font, tick->label);
label_h = label_scale * gnome_font_get_ascender (our_label_font) + gnome_font_get_descender (our_label_font);
if (rotate_labels) {
double q = label_w;
label_w = label_h;
label_h = q;
}
guppi_element_view_get_bbox_pt ((GuppiElementView *) view, &x0, &y0, &x1, &y1);
switch (pos) {
case GUPPI_NORTH:
case GUPPI_SOUTH:
guppi_element_view_vp2pt ((GuppiElementView *) view, tick->position, 0, &t, NULL);
if (t - label_w/2 < x0 || t + label_w/2 > x1)
our_show_label = FALSE;
break;
case GUPPI_EAST:
case GUPPI_WEST:
guppi_element_view_vp2pt ((GuppiElementView *) view, 0, tick->position, NULL, &t);
if (t - label_h/2 < y0 || t + label_h/2 > y1)
our_show_label = FALSE;
break;
default:
our_show_label = FALSE;
}
}
if (show_label)
*show_label = our_show_label;
if (label_font)
*label_font = our_label_font;
}
double
guppi_axis_view_label_shrink_to_fit_factor (GuppiAxisView *view, GuppiAxisMarkers *markers)
{
GuppiElementState *state;
guppi_compass_t pos;
gboolean rotate_labels;
const GuppiTick *tick;
double *label_pos;
double *label_size;
gint i, j, N, thrash;
gint first_label = -1, last_label = -1;
double scale;
g_return_val_if_fail (GUPPI_IS_AXIS_VIEW (view), 1.0);
g_return_val_if_fail (GUPPI_IS_AXIS_MARKERS (markers), 1.0);
state = guppi_element_view_state ((GuppiElementView *) view);
if (! guppi_geometry_positioned (guppi_element_view_geometry ((GuppiElementView *) view)))
return 0;
guppi_element_state_get (state,
"position", &pos,
"rotate_labels", &rotate_labels,
NULL);
N = guppi_axis_markers_size (markers);
label_pos = guppi_new (double, N+2);
label_size = guppi_new (double, N+2);
/* Pre-scan for first and last label */
for (i = 0; i < N; ++i) {
tick = guppi_axis_markers_get (markers, i);
if (tick->label && *tick->label) {
if (first_label < 0)
first_label = i;
last_label = i;
}
}
for (i = 0, j = 0; i < N; ++i) {
const gchar *str;
tick = guppi_axis_markers_get (markers, i);
str = guppi_tick_label (tick);
if (str && *str) {
GnomeFont *font;
gboolean show_label;
guppi_axis_state_tick_properties ((GuppiAxisState *) state, tick,
NULL, NULL, NULL, NULL,
&show_label, NULL, NULL, &font);
if (show_label) {
double label_w, label_h;
label_w = gnome_font_get_width_utf8 (font, str);
label_h = gnome_font_get_ascender (font) + gnome_font_get_descender (font);
if (rotate_labels) {
double q = label_w;
label_w = label_h;
label_h = q;
}
switch (pos) {
case GUPPI_NORTH:
case GUPPI_SOUTH:
if (i == first_label && tick->critical_label) {
guppi_element_view_get_bbox_pt ((GuppiElementView *) view, &label_pos[j], NULL, NULL, NULL);
label_size[j] = 0;
++j;
}
guppi_element_view_vp2pt ((GuppiElementView *) view,
tick->position, 0, &label_pos[j], NULL);
label_size[j] = label_w;
++j;
if (i == last_label && tick->critical_label) {
guppi_element_view_get_bbox_pt ((GuppiElementView *) view, NULL, NULL, &label_pos[j], NULL);
label_size[j] = 0;
++j;
}
break;
case GUPPI_EAST:
case GUPPI_WEST:
if (i == first_label && tick->critical_label) {
guppi_element_view_get_bbox_pt ((GuppiElementView *) view, NULL, &label_pos[j], NULL, NULL);
label_size[j] = 0;
++j;
}
guppi_element_view_vp2pt ((GuppiElementView *) view, 0, tick->position, NULL, &label_pos[j]);
label_size[j] = label_h;
++j;
if (i == last_label && tick->critical_label) {
guppi_element_view_get_bbox_pt ((GuppiElementView *) view, NULL, NULL, NULL, &label_pos[j]);
label_size[j] = 0;
++j;
}
break;
default:
g_assert_not_reached ();
}
}
}
}
N = j;
/* Grow the label sizes a bit, so we have a little bit of "margin" around
the labels... we don't want them to get jammed up too close together. */
for (i = 0; i < N; ++i) {
label_size[i] *= 1.05;
}
/* Find a scale that will keep our labels from overlapping */
scale = 1.0;
i = 1;
thrash = 0;
while (i < N && thrash < N*N) {
if (label_pos[i-1] + scale * label_size[i-1]/2 <= label_pos[i] - scale * label_size[i]/2) {
++i;
} else {
scale = 0.98 * (label_pos[i] - label_pos[i-1]) / (label_size[i-1]/2 + label_size[i]/2);
i = 1;
++thrash;
}
}
guppi_free (label_pos);
guppi_free (label_size);
return scale;
}
/* $Id: guppi-axis-view.c,v 1.18 2002/01/22 02:01:14 trow Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1