/* Dot item for the GNOME canvas
*
* Copyright (C) 1999 Red Hat, Inc.
*
* Author: Federico Mena <
[email protected]>
*
* 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 <math.h>
#include <gtk/gtkgc.h>
#include "gnome-canvas-dot.h"
/* Object argument IDs */
enum {
ARG_0,
ARG_X,
ARG_Y,
ARG_DIAMETER_PIXELS,
ARG_FILL_COLOR
};
static void gnome_canvas_dot_class_init (GnomeCanvasDotClass *class);
static void gnome_canvas_dot_init (GnomeCanvasDot *dot);
static void gnome_canvas_dot_destroy (GtkObject *object);
static void gnome_canvas_dot_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
static void gnome_canvas_dot_get_arg (GtkObject *object, GtkArg *arg, guint arg_id);
static void gnome_canvas_dot_update (GnomeCanvasItem *item, double *affine,
ArtSVP *clip_svp, int flags);
static void gnome_canvas_dot_realize (GnomeCanvasItem *item);
static void gnome_canvas_dot_unrealize (GnomeCanvasItem *item);
static void gnome_canvas_dot_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
int x, int y, int width, int height);
static double gnome_canvas_dot_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
GnomeCanvasItem **actual_item);
static void gnome_canvas_dot_bounds (GnomeCanvasItem *item,
double *x1, double *y1, double *x2, double *y2);
static GnomeCanvasItemClass *parent_class;
/* Private data of the GnomeCanvasDot structure */
typedef struct {
double x, y;
guint diameter;
guint fill_color;
GdkGC *gc;
guint need_shape_update : 1;
guint need_color_update : 1;
} DotPrivate;
/**
* gnome_canvas_dot_get_type:
* @void:
*
* Registers the &GnomeCanvasDot class if necessary, and returns the type ID
* associated to it.
*
* Return value: The type ID of the &GnomeCanvasDot class.
**/
GtkType
gnome_canvas_dot_get_type (void)
{
static GtkType dot_type = 0;
if (!dot_type) {
static const GtkTypeInfo dot_info = {
"GnomeCanvasDot",
sizeof (GnomeCanvasDot),
sizeof (GnomeCanvasDotClass),
(GtkClassInitFunc) gnome_canvas_dot_class_init,
(GtkObjectInitFunc) gnome_canvas_dot_init,
NULL, /* reserved_1 */
NULL, /* reserved_2 */
(GtkClassInitFunc) NULL
};
dot_type = gtk_type_unique (gnome_canvas_item_get_type (), &dot_info);
}
return dot_type;
}
/* Class initialization function for the dot item */
static void
gnome_canvas_dot_class_init (GnomeCanvasDotClass *class)
{
GtkObjectClass *object_class;
GnomeCanvasItemClass *item_class;
object_class = (GtkObjectClass *) class;
item_class = (GnomeCanvasItemClass *) class;
parent_class = gtk_type_class (gnome_canvas_item_get_type ());
gtk_object_add_arg_type ("GnomeCanvasDot::x",
GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X);
gtk_object_add_arg_type ("GnomeCanvasDot::y",
GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y);
gtk_object_add_arg_type ("GnomeCanvasDot::diameter_pixels",
GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_DIAMETER_PIXELS);
gtk_object_add_arg_type ("GnomeCanvasDot::fill_color",
GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FILL_COLOR);
object_class->destroy = gnome_canvas_dot_destroy;
object_class->set_arg = gnome_canvas_dot_set_arg;
object_class->get_arg = gnome_canvas_dot_get_arg;
item_class->update = gnome_canvas_dot_update;
item_class->realize = gnome_canvas_dot_realize;
item_class->unrealize = gnome_canvas_dot_unrealize;
item_class->draw = gnome_canvas_dot_draw;
item_class->point = gnome_canvas_dot_point;
item_class->bounds = gnome_canvas_dot_bounds;
}
/* Object initialization function for the dot item */
static void
gnome_canvas_dot_init (GnomeCanvasDot *dot)
{
DotPrivate *priv;
priv = g_new0 (DotPrivate, 1);
dot->priv = priv;
priv->x = 0.0;
priv->y = 0.0;
priv->fill_color = 0x000000ff;
priv->need_shape_update = TRUE;
priv->need_color_update = TRUE;
}
/* Destroy handler for the dot item */
static void
gnome_canvas_dot_destroy (GtkObject *object)
{
GnomeCanvasDot *dot;
GnomeCanvasItem *item;
DotPrivate *priv;
g_return_if_fail (object != NULL);
g_return_if_fail (GNOME_IS_CANVAS_DOT (object));
dot = GNOME_CANVAS_DOT (object);
item = GNOME_CANVAS_ITEM (object);
priv = dot->priv;
gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
g_free (priv);
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
/* Set_arg handler for the dot item */
static void
gnome_canvas_dot_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
GnomeCanvasItem *item;
GnomeCanvasDot *dot;
DotPrivate *priv;
char *str;
GdkColor color;
item = GNOME_CANVAS_ITEM (object);
dot = GNOME_CANVAS_DOT (object);
priv = dot->priv;
switch (arg_id) {
case ARG_X:
priv->x = GTK_VALUE_DOUBLE (*arg);
priv->need_shape_update = TRUE;
gnome_canvas_item_request_update (item);
break;
case ARG_Y:
priv->y = GTK_VALUE_DOUBLE (*arg);
priv->need_shape_update = TRUE;
gnome_canvas_item_request_update (item);
break;
case ARG_DIAMETER_PIXELS:
priv->diameter = GTK_VALUE_UINT (*arg);
priv->need_shape_update = TRUE;
gnome_canvas_item_request_update (item);
break;
case ARG_FILL_COLOR:
str = GTK_VALUE_STRING (*arg);
g_return_if_fail (str != NULL);
if (gdk_color_parse (str, &color)) {
priv->fill_color = ((color.red & 0xff00) << 16 |
(color.green & 0xff00) << 8 |
(color.blue & 0xff00) |
0xff);
priv->need_color_update = TRUE;
gnome_canvas_item_request_update (item);
}
break;
default:
break;
}
}
/* Get_arg handler for the dot item */
static void
gnome_canvas_dot_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
GnomeCanvasDot *dot;
DotPrivate *priv;
dot = GNOME_CANVAS_DOT (object);
priv = dot->priv;
switch (arg_id) {
case ARG_X:
GTK_VALUE_DOUBLE (*arg) = priv->x;
break;
case ARG_Y:
GTK_VALUE_DOUBLE (*arg) = priv->y;
break;
case ARG_DIAMETER_PIXELS:
GTK_VALUE_UINT (*arg) = priv->diameter;
break;
default:
arg->type = GTK_TYPE_INVALID;
break;
}
}
/* Update handler for the dot item */
static void
gnome_canvas_dot_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_svp, int flags)
{
GnomeCanvasDot *dot;
DotPrivate *priv;
dot = GNOME_CANVAS_DOT (item);
priv = dot->priv;
if (parent_class->update)
(* parent_class->update) (item, affine, clip_svp, flags);
/* Redraw old area if necessary */
if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
&& !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE))
|| (flags & GNOME_CANVAS_UPDATE_AFFINE)
|| priv->need_shape_update)
gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
/* Update color if needed */
if (priv->need_color_update) {
GdkGCValues values;
int mask;
if (priv->gc)
gtk_gc_release (priv->gc);
values.foreground.pixel = gnome_canvas_get_color_pixel (item->canvas,
priv->fill_color);
mask = GDK_GC_FOREGROUND;
priv->gc = gtk_gc_get (gtk_widget_get_visual (GTK_WIDGET (item->canvas))->depth,
gtk_widget_get_colormap (GTK_WIDGET (item->canvas)),
&values,
mask);
priv->need_color_update = FALSE;
}
/* If we need a shape update, or if the item changed visibility
* to shown, recompute the bounding box.
*/
if (priv->need_shape_update
|| ((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
&& (GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE))
|| (flags & GNOME_CANVAS_UPDATE_AFFINE)) {
ArtPoint i1, i2, c1, c2;
double d;
double i2c[6];
d = priv->diameter / item->canvas->pixels_per_unit;
i1.x = priv->x - d / 2;
i1.y = priv->y - d / 2;
i2.x = priv->x + d / 2;
i2.y = priv->y + d / 2;
gnome_canvas_item_i2c_affine (item, i2c);
art_affine_point (&c1, &i1, i2c);
art_affine_point (&c2, &i2, i2c);
item->x1 = c1.x;
item->y1 = c1.y;
item->x2 = c2.x + 1;
item->y2 = c2.y + 1;
priv->need_shape_update = FALSE;
}
/* If the fill our outline changed, we need to redraw, anyways */
gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
}
/* Realize handler for the dot item */
static void
gnome_canvas_dot_realize (GnomeCanvasItem *item)
{
GnomeCanvasDot *dot;
DotPrivate *priv;
dot = GNOME_CANVAS_DOT (item);
priv = dot->priv;
if (parent_class->realize)
(* parent_class->realize) (item);
priv->need_color_update = TRUE;
gnome_canvas_item_request_update (item);
}
/* Unrealize handler for the dot item */
static void
gnome_canvas_dot_unrealize (GnomeCanvasItem *item)
{
GnomeCanvasDot *dot;
DotPrivate *priv;
dot = GNOME_CANVAS_DOT (item);
priv = dot->priv;
if (priv->gc) {
gtk_gc_release (priv->gc);
priv->gc = NULL;
}
if (parent_class->unrealize)
(* parent_class->unrealize) (item);
}
/* Draw handler for the dot item */
static void
gnome_canvas_dot_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
int x, int y, int width, int height)
{
GnomeCanvasDot *dot;
DotPrivate *priv;
double i2c[6];
ArtPoint i, c;
int x1, y1;
dot = GNOME_CANVAS_DOT (item);
priv = dot->priv;
i.x = priv->x;
i.y = priv->y;
gnome_canvas_item_i2c_affine (item, i2c);
art_affine_point (&c, &i, i2c);
x1 = floor (c.x - priv->diameter / 2.0 + 0.5) - x;
y1 = floor (c.y - priv->diameter / 2.0 + 0.5) - y;
gdk_draw_arc (drawable,
priv->gc,
TRUE,
x1, y1,
priv->diameter,
priv->diameter,
0 * 64,
360 * 64);
gdk_draw_arc (drawable,
priv->gc,
FALSE,
x1, y1,
priv->diameter,
priv->diameter,
0 * 64,
360 * 64);
}
/* Point handler for the dot item */
static double
gnome_canvas_dot_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
GnomeCanvasItem **actual_item)
{
GnomeCanvasDot *dot;
DotPrivate *priv;
double d;
double dx, dy, dist;
dot = GNOME_CANVAS_DOT (item);
priv = dot->priv;
*actual_item = item;
d = priv->diameter / item->canvas->pixels_per_unit;
dx = x - priv->x;
dy = y - priv->y;
dist = sqrt (dx * dx + dy * dy);
if (dist <= d / 2.0)
return 0.0;
else
return dist - d / 2.0;
}
/* Bounds handler for the dot item */
static void
gnome_canvas_dot_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
{
GnomeCanvasDot *dot;
DotPrivate *priv;
double d;
dot = GNOME_CANVAS_DOT (item);
priv = dot->priv;
d = priv->diameter / item->canvas->pixels_per_unit;
*x1 = priv->x - d / 2.0;
*y1 = priv->y - d / 2.0;
*x2 = priv->x + d / 2.0;
*y2 = priv->y + d / 2.0;
}