/* DiaCanvas -- A technical drawing canvas.
 * Copyright (C) 1999, Arjan Molenaar
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/* Original license:
 * Dia -- an diagram creation/manipulation program
 * Copyright (C) 1998 Alexander Larsson
 *
 * 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.
 */

/* diaobject.h
 * -----------
 * base class for all objects that can be displayed in a DiaCanvas
 */

#ifndef __DIA_OBJECT_H__
#define __DIA_OBJECT_H__

#include <diacanvas/geometry.h>
#include <diacanvas/diarenderer.h>
#include <diacanvas/diadefs.h>

DIA_OPEN

#define DIA_OBJECT(x) ((DiaObject*) x)

typedef struct _DiaObject DiaObject;
typedef struct _DiaObjectOps DiaObjectOps;
typedef struct _DiaObjectType DiaObjectType;
typedef struct _DiaConnectionPoint DiaConnectionPoint;
typedef struct _DiaHandle DiaHandle;

#include <diacanvas/dialayer.h>
#include <diacanvas/diaevent.h> 

/* An object can have 4 states: */
typedef enum
{
  DIA_OBJECT_STATE_NORMAL = 0,        /* 0000 nothing special */
  DIA_OBJECT_STATE_ACTIVE = 1,        /* 0001 the object is placed in
				              the active layer */
  DIA_OBJECT_STATE_SELECTED = 1 << 1, /* 0010 the object is selected */
  DIA_OBJECT_STATE_FOCUSED = 1 << 2,  /* 0100 The object has focus */
  DIA_OBJECT_STATE_GRABBED = 1 << 3,  /* 1000 The object has grab */
  DIA_OBJECT_STATE_MASK = 0x0F
} DiaObjectFlags;

/* The states are defined by the environment (mostly DiaDisplay).
 * For example: an object can be selected in one Display but doesn't have to
 * be selected in another display. The display sets the appropriate state
 * when it is relevant (when drawing the canvas).
 */

/* bits 4-31 (16..MAX) are still free to use. */
#ifdef DIA_FLAG_CHECK

#ifdef __GNUC__

#define DIA_OBJECT_SET_FLAGS(obj,flag) \
G_STMT_START{ \
		DIA_OBJECT (obj)->flags |=  (flag); \
		g_log (G_LOG_DOMAIN, \
		       G_LOG_LEVEL_DEBUG, \
		       "file %s: line %d (%s): flag set to 0x%x.", \
		       __FILE__, \
		       __LINE__, \
		       __PRETTY_FUNCTION__, \
		       DIA_OBJECT (obj)->flags); \
}G_STMT_END
						   
#define DIA_OBJECT_UNSET_FLAGS(obj,flag) \
G_STMT_START{ \
		DIA_OBJECT (obj)->flags &= ~(flag); \
		g_log (G_LOG_DOMAIN, \
		       G_LOG_LEVEL_DEBUG, \
		       "file %s: line %d (%s): flag UNset to 0x%x.", \
		       __FILE__, \
		       __LINE__, \
		       __PRETTY_FUNCTION__, \
		       DIA_OBJECT (obj)->flags); \
}G_STMT_END

#else /* !__GNUC__ */

#define DIA_OBJECT_SET_FLAGS(obj,flag)    (DIA_OBJECT (obj)->flags |=  (flag))
#define DIA_OBJECT_UNSET_FLAGS(obj,flag)  (DIA_OBJECT (obj)->flags &= ~(flag))

#endif /* __GNUC__ */

#else /* !DIA_FLAG_CHECK */

#define DIA_OBJECT_SET_FLAGS(obj,flag)    (DIA_OBJECT (obj)->flags |=  (flag))
#define DIA_OBJECT_UNSET_FLAGS(obj,flag)  (DIA_OBJECT (obj)->flags &= ~(flag))

#endif /* DIA_FLAG_CHECK */

#define DIA_OBJECT_IS_SET(obj, flag)      ((DIA_OBJECT (obj)->flags & (flag)) == (flag))

/* Requests are created by objects as a result of user actions.
 */
typedef enum
{
  DIA_REQUEST_NOTHING  = 0,
  /* Redraw the object. the UPDATE_BOX field in DiaObject specifies the
   * region that has to be updated. */
  DIA_REQUEST_REDRAW   = 1,
  /* update the bounding boxes of the layer and the diagram. */
  DIA_REQUEST_EXTENTS  = 1 << 1,
  /* unselect and unfocus the object. */
  DIA_REQUEST_NORMAL   = 1 << 2,
  /* select the object. This will let it draw its handles. */
  DIA_REQUEST_SELECT   = 1 << 3,
  DIA_REQUEST_UNSELECT = 1 << 4,
  /* only sets focus, not added to selection list!!! */
  DIA_REQUEST_FOCUS    = 1 << 5, 
  DIA_REQUEST_UNFOCUS  = 1 << 6,
  /* send events always only to this object */
  DIA_REQUEST_GRAB     = 1 << 7, 
  DIA_REQUEST_UNGRAB   = 1 << 8, 
  /* Ask the environment to update all objects connected to this object. */
  DIA_REQUEST_UPDATE_CONNECTIONS = 1 << 9,
  /* Ask the environment to update the connections of the objects that
   * are connected by a handle. This is the counterpart of 
   * DIA_REQUEST_UPDATE_CONNECTIONS and will mainly be used by DiaGraphs who
   * wish to be placed correct (e.i. the line always points to the center
   * of the element it is connected to). */
  DIA_REQUEST_UPDATE_HANDLES = 1 << 10,
  /* request a (standard) cursor symbol. Don't forget to set the CURSOR
   * field in DiaObject to an appropriate value. */
  DIA_REQUEST_CURSOR = 1 << 11, 
  /* "Please give me a NORMAL cursor!" */
  DIA_REQUEST_CURSOR_RESET = 1 << 12, 
  /* Do not free yourself, instead let the environment clean you up. */
  DIA_REQUEST_DESTROY = 1 << 13,
  /* Execute the event loop again after the objects events have been
   * handled. Nice for objects that do not ungrab at a BUTTON_RELEASE. */
  DIA_REQUEST_REENTER = 1 << 14,
  /* Stop emitting of signal. */
  DIA_REQUEST_STOP_EMIT = 1 << 15,

  /* For future expantion: */
  DIA_REQUEST_RESERVED_1 = 1 << 16,
  DIA_REQUEST_RESERVED_2 = 1 << 17,
  DIA_REQUEST_RESERVED_3 = 1 << 18,
  DIA_REQUEST_RESERVED_4 = 1 << 19,
  DIA_REQUEST_RESERVED_5 = 1 << 20,

  /* custom requests:
   * NOTE: you have to create a new modify tool to use extra requests. */
  DIA_REQUEST_CUSTOM_1 = 1 << 20,
  DIA_REQUEST_CUSTOM_2 = 1 << 21,
  DIA_REQUEST_CUSTOM_3 = 1 << 22,
  DIA_REQUEST_CUSTOM_4 = 1 << 23,
  DIA_REQUEST_CUSTOM_5 = 1 << 24,
  DIA_REQUEST_CUSTOM_6 = 1 << 25,
  DIA_REQUEST_CUSTOM_7 = 1 << 26,
  DIA_REQUEST_CUSTOM_8 = 1 << 27,
  DIA_REQUEST_CUSTOM_9 = 1 << 28,
} DiaRequest;

#define DIA_OBJECT_SET_REQUEST(obj,flag)    (DIA_OBJECT (obj)->request |=  (flag))
#define DIA_OBJECT_UNSET_REQUEST(obj,flag)  (DIA_OBJECT (obj)->request &= ~(flag))

/* This function is used for initializing an object.
 */
typedef void (*DiaInitFunc) (DiaObject* obj);

/* Function called before an object is deleted.
 * This function must call the parent class's DestroyFunc, and then free
 * the memory associated with the object
 *
 * Must also unconnect itself from all other objects.
 * (This is by calling object_destroy, or letting the super-class call it)
 */
typedef void (*DiaDestroyFunc) (DiaObject* obj);

/* Function responsible for drawing the object.
 * Every drawing must be done through the use of the Renderer, so that we
 * can render the picture on screen, in an eps file, ...
 * Don't forget to set the state before calling the drawing function!!!
 */
typedef void (*DiaDrawFunc) (DiaObject* obj, DiaRenderer* renderer);

/* This function must return the distance between the Object and the Point.
 * Several functions are provided in geometry.h to facilitate this calculus.
 * POINT is a value relative to the objects origin.
 */
typedef gfloat (*DiaDistanceFunc) (DiaObject* obj, Point* point);

/* Function called to move the entire object.
 * The new position is given by pos.
 * It's exact definition depends on the object. It's the point on the
 * object that 'snaps' to the grid if that is enabled. (generally it
 * is the upper left corner). After the move DiaObject::update_box contains
 * the region to be updated.
 */
typedef void (*DiaMoveFunc) (DiaObject *obj, gfloat dx, gfloat dy);

/* Function called to move one of the handles associated with the
 * object. Its new position is given by pos. After the move
 * DiaObject::update_box contains the region to be updated.
 */
typedef void (*DiaMoveHandleFunc) (DiaObject *obj, DiaHandle *handle,
				   gfloat dx, gfloat dy);

/* Returns a copy of Object.
 * This must be an depth-copy (pointers must be duplicated and so on)
 * as the initial object can be deleted any time.
 * NOTE: cp's connections to handles and visa versa are not copied, neither
 *       is the data pointer
 */
typedef void (*DiaCopyFunc) (DiaObject* from, DiaObject *to);

/* Return TRUE if object does not contain anything
 * (i.e. is empty on the screen)
 * Then it will be removed, because the user cannot select it to
 * remove it himself.
 *
 * This is mainly used by TextObj.
 */
typedef gint (*DiaIsEmptyFunc) (DiaObject *obj);

/* Calculate the bounding box for the object.
 */
typedef void (*DiaCalcBoundingBoxFunc) (DiaObject *obj);

/* Return TRUE if the event is handled and/or no default handler
 * should be invoked. This handler is only called if the active tool
 * is doing it. DIAEVENT *EVENT is a mapping from the GTKEVENT, since lots
 * of event are not supported by the objects in the canvas. This solution
 * increases portability. LAYER is the layer the object is part
 * of. This parameter can be used for handles to find the nearest connection
 * point. Of course you're free to use it for whatever you want.
 */
typedef gint (*DiaEventFunc) (DiaObject *obj, DiaEvent *event,
			      DiaLayer *layer);

/* Ask the object to return the minimal distance between a point POS (relative
 * to the object) and a connection point (existing or not). This function is
 * used to determine the closest connection point.
 * *object: the "owning object". must be <> NULL.
 * *pos: the position of which the distance should be calculated. POS is
 *       relative to OBJECT. must be <> NULL.
 * *con_pos: The position to which the handle will be connected, if you call
 *       the cp_connect () function. This point is the same as (*CP)->POS in
 *       case a CP is found. This value is handy for the "glue" effect.
 * **cp: If the connection point is already existing, it will be assigned to
 *       this parameter, NULL otherwise.
 * returns: distance between POS and the nearest connection point.
 * NOTE: Since CP's can be craeted on the fly, the distance could as well be
 * calculated from POS to the closest point where a CP could be created.
 */
typedef gfloat (*DiaConnectionPointDistanceFunc) (DiaObject *object,
						  Point *pos, Point *con_pos,
						  DiaConnectionPoint **cp);

/* Ask the object to connect a handle to it. 
 * *obj: may not be NULL
 * *h: handle to be connected  (<> NULL)
 * *pos: the position (relative to OBJ) where the CP should be located/created.
 *       If POS == NULL then the point should be determined first.
 * returns: a pointer to the CP that the handle is connected to or NULL if
 *          no conection is established.
 * NOTE: The distance from pos to the object is not evaluated!
 */
typedef DiaConnectionPoint* (*DiaConnectionPointConnectFunc) (DiaObject *obj,
							      DiaHandle *h,
							      Point *pos);

/* This function can be used for removal of connectionpoints.
 * DiaObject *obj: The object the connection point belongs to;
 * DiaConnectionPoint *cp: the connection point, connected to OBJ;
 * DiaHandle *h: The handle that is to be disconnected. May be NULL, this
 *               will disconnect all handles.
 * NOTES:
 *  Use the wrapper function dia_handle_disconnect () to disconnect a handle.
 *  This function should do the REAL disconnection 
 *  (DiaObject::ops::cp_disconnect () does that, so don't worry...).
 */
typedef void (*DiaConnectionPointDisconnectFunc) (DiaObject *obj,
						  DiaConnectionPoint *cp,
						  DiaHandle *h);

/* This function is a prototype for all functions that are able to
 * create new objects. It is used by the create tool. All new functions should
 * be conform this prototype.
 */
typedef DiaObject* (*DiaNewObjectFunc) (Point *pos);

#define DIA_CONNECTION_POINT_SIZE 5
#define DIA_CONNECTION_POINT_TRESHOLD 0.1

/* Connection point -- the point another object can "glue" on the object
 */
struct _DiaConnectionPoint
{
  Point pos;         /* position of this connection point */

  DiaObject *object; /* pointer to the object having this point */

  GList *connected;  /* list of handles connected to this point */

  gpointer data;     /* if you want to add data to a CP, do it here */
};

extern DiaColor dia_handle_colors[];
/* A handle can have eight (visible) states (nota that the connectable flag
 * is represented by a cross inside the handle):
 * 0: selected, unmovable
 * 1: selected, movable
 * 2: selected, movable, can be connected (e.g. on a mouse button release)
 * 3: selected, movable, connected
 * 4: focused, unmovable
 * 5: focused, movable
 * 6: focused, movable, can be connected
 * 7: focused, movable, connected
 */

#define DIA_HANDLE_SIZE 7 /* make it an odd number */

/* Handles are the points on objects that will let it perform a resize action,
 * a connect action or whatever you think is nessesary.
 * Handles are basically used to connect to other objects
 * (by ConnectionPoints), you can see if a handle is connected by its color.
 * Handles will move with the object the handle is connected to.
 */
struct _DiaHandle
{
  Point pos;
  DiaObject *object; /* pointer to the object having this handle */
 
  gint keep_on_move: 1; /* should a connection break when the object the
			 * handle belongs to moves? */
  gint is_connectable: 1; /* can the handle be attached to a connectionpoint */
  gint is_movable: 1; /* can a handle move independant from the object */

  gint could_be_connected: 1; /* if a CP is found close to the handle,
			       * this bit has to be set. */
  
  DiaConnectionPoint *connected_to; /* NULL if not connected */

  gpointer data; /* if you want to add data to a handle, do it here. */
};

/* OBJ -- DiaObject (or a child);
 * nr  -- index number.
 */
#define DIA_OBJECT_GET_HANDLE(obj, nr) ((DiaHandle*)g_ptr_array_index ( \
                                        DIA_OBJECT (obj)->handles, nr))
#define DIA_OBJECT_GET_CP(obj, nr) ((DiaConnectionPoint*)g_ptr_array_index ( \
                                    DIA_OBJECT (obj)->connections, nr))

typedef DiaObjectType* (*DiaGetTypeFunc) ();

/* simple type descriptor... 
 */
struct _DiaObjectType
{
  gchar          *name;
  guint           size; /* size of the object in bytes. */
  DiaInitFunc     init;
  DiaObjectOps   *ops;
  DiaGetTypeFunc  parent_type;
};


/* This structure gives access to the functions used to manipulate an object
 * See information above on the use of the functions
 * Create one (static) instance of this struct to every object (will not be
 * freed by dia_object_destroy).
 * These functions should be called through their dia_object_* counterparts.
 */
struct _DiaObjectOps
{
  DiaDestroyFunc         destroy;       /* called to clean up an object */
  DiaDrawFunc            draw;          /* draw the object */
  DiaDrawFunc            draw_handles;  /* draw the handles */
  DiaDrawFunc            draw_cps;      /* draw the connection points */
  DiaDistanceFunc        distance;      /* distance from a point calculation */
  DiaMoveFunc            move;          /* move the object */
  DiaMoveHandleFunc      move_handle;   /* move one handle of the object */
  DiaCopyFunc            copy;          /* duplicate the object */
  DiaEventFunc           event;         /* GDK like event handler */
  DiaIsEmptyFunc         is_empty;      /* check if an object is empty */
  DiaCalcBoundingBoxFunc calc_bounding_box;
  
  /* Connection Point handling: */
  DiaConnectionPointDistanceFunc   cp_distance;
  DiaConnectionPointConnectFunc    cp_connect;
  DiaConnectionPointDisconnectFunc cp_disconnect;

  /* For future use (?): */
  gpointer reserved[4];
};   

/* The base class in the Object hierarcy.
 * All information in this structure read-only
 * from the application point of view.
 */
struct _DiaObject
{
  /* If you are making a new object, you should overwrite those two
   * declarations...
   */
  DiaObjectType    *object_type;
  DiaObjectOps     *ops;

  /* FLAGS: set state of an object. Note that bit 0 and 1 (value 0..3)
   * are used by the displays to set the object display-state */
  guint32           flags;
  
  Rectangle         bounding_box;   
  Point             snap_pos; /* This is the position where the pointer is
				 snapped to the object (like with move ) */
 
  /* request section: */
  /* Ask the owner of the object to do something. This  */
  DiaRequest        request;
  
  /* If there is a redraw request, define the rectangle to be redrawn here.
     This is a box relative to the POSITION! */
  Rectangle         update_box;
  /* Set a cursor. */
  gint              cursor;
  
  /* and the rest of the data: */
  GPtrArray        *handles; /* List of DiaHandle's */
  GPtrArray        *connections; /* List of DiaConnectionPoints */
  
  gpointer          data; /* just to apply some extra info to the object */
};

/* check if a cast is valid */
gboolean dia_object_check_type (DiaObject *obj, DiaObjectType *typ);

DiaObjectType* dia_object_get_type ();

void        dia_object_init (DiaObject *object);

/* This is a generic function for creating objects: */
DiaObject*  dia_object_create (DiaObjectType *typ);

/* These functions should be used when calling an OPS function instead
 * of calling the function directly. */
void        dia_object_destroy (DiaObject *object);
void        dia_object_draw (DiaObject* obj, DiaRenderer* renderer);
void        dia_object_draw_handles (DiaObject* obj, DiaRenderer* renderer); 
void        dia_object_draw_cps (DiaObject* obj, DiaRenderer* renderer); 
gfloat      dia_object_distance (DiaObject* obj, Point* point);
void        dia_object_move (DiaObject *obj, gfloat dx, gfloat dy);
void        dia_object_move_handle (DiaObject *obj, DiaHandle *handle,
				    gfloat dx, gfloat dy);
void        dia_object_move_handle_abs (DiaObject *obj, DiaHandle *handle,
					gfloat x, gfloat y);
DiaObject*  dia_object_copy (DiaObject* from);
gint        dia_object_event (DiaObject *obj, DiaEvent *event,
			      DiaLayer *layer);
gint        dia_object_is_empty (DiaObject *obj);
void        dia_object_calc_bounding_box (DiaObject *obj);

void        dia_object_add_update (DiaObject *obj, Rectangle *update_box);

void        dia_object_calc_bounding_box_update (DiaObject *obj);

gfloat      dia_object_cp_distance (DiaObject *object, Point *pos,
				    Point *con_pos, DiaConnectionPoint **cp);
DiaConnectionPoint* dia_object_cp_connect (DiaObject *obj, DiaHandle *h,
					   Point *pos);

/* Instead of this function, use dia_handle_disconnect () where posible. */
void        dia_object_cp_disconnect (DiaObject *obj, DiaConnectionPoint *p,
				      DiaHandle *h);

/* Move the object and update the OBJ->UPDATE_BOX field. If REINIT is TRUE
 * the UPDATE_BOX field will be set, otherwise it will only be updated.
 */
void        dia_object_destroy_object_list (GList *list_to_be_destroyed);

/* remove all connections to an object */
void        dia_object_unconnect_all (DiaObject *object);

/* return the newly created handle */
DiaHandle*  dia_object_add_handle (DiaObject *object, gfloat x, gfloat y);
DiaHandle*  dia_object_insert_handle (DiaObject *object, gfloat x,
				      gfloat y, gint index);
void  dia_object_remove_handle (DiaObject *object, DiaHandle *handle);

gfloat dia_object_find_closest_handle (DiaObject *object, Point *pos,
				       DiaHandle **handle);

/* convenience function: find the closest handle from a list of objects
 * and return the distance. */
gfloat dia_find_closest_handle_from_list (GList *list, Point *pos, 
					  DiaHandle **handle);

/* returns the newly created Connectionpoint */
DiaConnectionPoint*  dia_object_add_connection_point (DiaObject *object,
						     gfloat x, gfloat y);

/* check if one of CONN_OBJ handles is connected to OBJECT.
 * This is a recursive function that will go through the whole connection
 * tree. This guarantees that no dead-lock (resulting in a stack overflow)
 * will occur. */
gboolean dia_object_is_connected_to (DiaObject *object, DiaObject *conn_obj);

void dia_object_draw_connection_points (DiaObject* obj, DiaRenderer* renderer);
void dia_object_draw_handles (DiaObject* obj, DiaRenderer* renderer);

/* Handle specific functions
 */
void        dia_handle_init (DiaHandle *handle);
/* Returns TRUE if the connection is successful, FALSE otherwise. */
gint        dia_handle_connect (DiaHandle *handle,
				DiaConnectionPoint *conpoint);
/* NOTE Do not call this function from DiaObject::ops::cp_disconnect, 'cause
 *      this function calls that one. */
void        dia_handle_disconnect (DiaHandle *handle);
void        dia_handle_free (DiaHandle *handle);

/* Connection Point Functions
 */
/* connection point can not connect: the handle must connect to the CP */
void  dia_connection_point_unconnect (DiaConnectionPoint *conpoint);
void  dia_connection_point_free (DiaConnectionPoint *conpoint);
void  dia_connection_point_move (DiaConnectionPoint *conpoint,
				 gfloat dx, gfloat dy, DiaRequest *request);

DIA_CLOSE

#endif /* __DIA_OBJECT_H__ */


