/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: XObject.h,v 1.6 2001/04/04 12:42:36 aleksandras Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////


#ifndef _XOBJECT_INCLUDED
#define _XOBJECT_INCLUDED

// ;-)

///////////////////////////////////////////////
//											 //
// All files in X-framework are Copyright    //
// of Aleksandras Gluchovas 				 //
// 											 //
// e-mail: alex@soften.ktu.lt                //
//											 //
///////////////////////////////////////////////

//... oh btw,  "XFramework" stands for - "eXtremely Simple Framework"

#include <math.h>

/*** wxWindows is used from now on (who needs MFC anyway?) ***/

#include "wx/window.h"
#include "wx/dc.h"

// #include "XWin32Canvas.h"
// #include "XWin32View.h"

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

// undefine wxUSE_TEMPLATE_STL, to use macro-based implementation for STL 

#include "wx/dynarray.h"

// serialization support (FOR NOW::uses C++ templates)
#include "XStorable.h"

typedef int    PosT;
typedef double AngleT;
typedef long   ColorT;

#define X_MIN(a,b) ( a < b ) ? a : b
#define X_MAX(a,b) ( a > b ) ? a : b

template <class T>
inline void X_SWAP(T& a, T& b) {
    T tmp = a;
    a = b;
    b = tmp;
}

#define X_DEFAULT_POS 0

#define X_RGB(R,G,B) (R | (G << 8) | ( B << 16 ) )

// These are #defined in Borland ?? - Chris Elliott
#ifdef M_PI 
#undef M_PI
#endif
#ifdef M_PI_2 
#undef M_PI_2
#endif
#ifdef M_PI_4 
#undef M_PI_4
#endif

// some handy functions for 2D-geomerty 

const AngleT M_PI   = 3.14159265358979323846;
const AngleT M_PI_2 = 1.57079632679489661923;
const AngleT M_PI_4 = 0.785398163397448309616;

inline AngleT get_true_arctan( PosT dx, PosT dy )
{
	if ((dx == 0) && (dy >= 0)) return M_PI_2;
	if ((dx == 0) && (dy <= 0)) return 3*M_PI_2;
	if ((dx >= 0) && (dy == 0)) return 0;
	if ((dx <= 0) && (dy == 0)) return M_PI;
	
	if ((dx > 0) && (dy > 0)) return   		  atan(AngleT(dy)/AngleT(dx));
	if ((dx < 0) && (dy > 0)) return   M_PI + atan(AngleT(dy)/AngleT(dx));
	if ((dx < 0) && (dy < 0)) return   M_PI + atan(AngleT(dy)/AngleT(dx));
	if ((dx > 0) && (dy < 0)) return 2*M_PI + atan(AngleT(dy)/AngleT(dx));

	return 0;
}

// 2D-geom. functions are continued further (after XDim, XPoint, etc declarations)

///////////// framework classes /////////////

class XDim
{
public:
	PosT width;
	PosT height;
public:

	// constructors

	inline XDim() 
		: width( X_DEFAULT_POS ), height( X_DEFAULT_POS )
	{}

	inline XDim( PosT width, PosT height )
		: width(width), height(height) 
	{}

	// other operations

	inline XDim Add( const XDim& other ) const
	{
		return XDim( width  + other.width,
			         height + other.height
				   );
	}

	inline XDim Sub( const XDim& other ) const 
	{
		return XDim( width  - other.width,
			         height - other.height
				   );
	}

	inline XDim Accumulate( const XDim& other ) const
	{
		return XDim( X_MAX( width,  other.width  ),
			         X_MAX( height, other.height ) 
				   );
	}

	inline bool operator==( const XDim& other )
	{
		return ( width == other.width &&
			    height == other.height );
	}
};

class XPoint
{
public:
	PosT x;
	PosT y;
public:

	// constructors

	inline XPoint() 
		: x( X_DEFAULT_POS ), y( X_DEFAULT_POS ) 
	{}

	inline XPoint( PosT x, PosT y )
		: x(x), y(y) 
	{}

	// Other operations 

	inline XPoint Add( const XPoint& other ) const
	{
		return XPoint( x + other.x, y + other.y );
	}

	inline XPoint Add( const XDim& other ) const
	{
		return XPoint( x + other.width, y + other.height );
	}

	inline XPoint Sub( const XPoint& other ) const
	{
		return XPoint( x - other.x, y - other.y );
	}

	inline void Move( PosT distance, AngleT angle )
	{
		x += PosT( double(distance) * cos(angle) );
		y += PosT( double(distance) * sin(angle) );
	}

	inline double VectorLength( const XPoint& other ) const
	{
		PosT dx = other.x - x;
		PosT dy = other.y - y;

		return sqrt( double(PosT(dx*dx + dy*dy)) );
	}

	inline void RotateAround( const XPoint& origin,
		                      AngleT        angle )
	{
		PosT dx = x - origin.x;
		PosT dy = y - origin.y;

		AngleT ang = get_true_arctan( dx, dy );

		ang += angle;

		double len = sqrt( double(PosT(dx*dx + dy*dy)) );

		x = origin.x + PosT( len * cos(ang) );
		y = origin.y + PosT( len * sin(ang) );
	}

	bool operator<( const XPoint& other )
	{
		return ( x < other.x && y < other.y );
	}

	/*** serialization ***/

	void Serialize( XSStream& stm )
	{
		stm << storeVar( x );
		stm << storeVar( y );
	}
};

inline bool operator==( const XPoint& lhs, const XPoint& rhs )
{
	return ( lhs.x == rhs.x && lhs.y == rhs.y );
}

inline bool operator!=( const XPoint& lhs, const XPoint& rhs )
{
	return ( lhs.x != rhs.x || lhs.y != rhs.y );
}

WX_DEFINE_ARRAY( XPoint*, XPointPtrListT );

// helper 2d-geomerty functions (continued)

inline double get_vector_len( const XPoint& from,
							  const XPoint& till
							)
{
	PosT dx = till.x - from.x;
	PosT dy = till.y - from.y;

	return sqrt( AngleT(dx*dx + dy*dy) );
}

inline AngleT get_ox_angle( const XPoint& from,
						    const XPoint& till
						  )
{
	return get_true_arctan( till.x - from.x,
		                    till.y - from.y  );
}

inline AngleT get_rad_angle( AngleT gradAngle )
{
	return (gradAngle*M_PI/180.0);
}

inline AngleT get_grad_angle( AngleT radAngle )
{
	return (radAngle*180.0/M_PI);
}

class XRect
{
public:
	PosT x,y;
	PosT width, height;
public:

	// constructors

	inline XRect() : x( X_DEFAULT_POS ), 
		             y( X_DEFAULT_POS ), 
				     width ( 0 ), 
				     height( 0 ) {}

	inline XRect( const XPoint& upperLeft, XDim dim )
		: x( upperLeft.x ),
		  y( upperLeft.y ),
		  width ( dim.width ),
		  height( dim.height )
	{}

	inline XRect( const XPoint& upperLeft, const XPoint& lowerRight )

		: x( upperLeft.x ),
		  y( upperLeft.y ),
		  width ( lowerRight.x - upperLeft.x ),
		  height( lowerRight.y - upperLeft.y )
	{}

	inline XRect( PosT _x, PosT _y, PosT _width, PosT _height )

		: x( _x ), y( _y ), width( _width ), height ( _height )
	{}

	// other operations

	inline XPoint UpperLeft()  const { return XPoint( x, y ); }
	inline XPoint LowerRight() const { return XPoint( x+width, y+height ); }

	inline XPoint UpperRight() const

		{ return XPoint( x+width, y ); }

	inline XPoint LowerLeft() const

		{ return XPoint( x, y+height ); }

	inline PosT Width()  const { return width;  }
	inline PosT Height() const { return height; }

	// expands the current area of this rectangle to fit
	// the given point

	void Accumulate( const XPoint& point );

	// accumulates area of the `other' recangle, the
	// resulting rectangle will contain the minimal 
	// recatangular area by which both can be surrounded

	inline void Accumulate( const XRect& other )
	{
		if ( this->IsEmpty() ) 
		{
			x = other.x;
			y = other.y;
			width = other.width;
			height = other.height;

			return;
		}
		else
		if ( other.IsEmpty() )

			return;

		PosT tx = x;
		PosT ty = y;

		x = X_MIN( x, other.x );
		y = X_MIN( y, other.y );

		PosT botX = X_MAX( tx + width,  other.x + other.width  );
		PosT botY = X_MAX( ty + height, other.y + other.height );

		width  = botX - x;
		height = botY - y;
	}

	// rearranges the upper-left and lower-right corners
	// of this recatangle if dimensions happen to become negative

	inline void Normalize()
	{
		if ( width  < 0 ) { x += width;  width  = -width;  }
		if ( height < 0 ) { y += height; height = -height; }
	}

	inline bool Contains( const XPoint& point ) const
	{
		return ( point.x >= x && point.x < x + width &&
			     point.y >= y && point.y < y + height );
	}

	inline bool Contains( const XRect& otherRect ) const
	{

		return ( x <= otherRect.x && 
			     y <= otherRect.y &&
			     x + width  >= otherRect.x + otherRect.width &&
				 y + height	>= otherRect.y + otherRect.height );
	}

	// TBD:: should be inlined

	XRect InsersectionWith( const XRect& other ) const;

	inline bool IsEmpty() const
	{
		return (width == 0 && height == 0);
	}

	inline XDim GetDim() const

		{ return XDim( width, height ); }

	inline void SetDim( const XDim& dim )
	{
		width  = dim.width;
		height = dim.height;
	}

	inline XPoint GetCenter() const
	{
		return XPoint( x + width/2, y + height/2 );
	}

	/*** serialization ***/

	void Serialize( XSStream& stm ) 
	{
		stm << storeVar( x );
		stm << storeVar( y );
		stm << storeVar( width  );
		stm << storeVar( height );
	}

	bool EqualTo( const XRect& other ) const
	{
		return ( x == other.x && 
			     y == other.y &&
			     width  == other.width && 
				 height == other.height );
	}
};

inline bool operator==( const XRect& lhs, const XRect& rhs )
{
	return ( lhs.x == rhs.x && lhs.y == rhs.y &&
		     lhs.width  == rhs.width &&
			 lhs.height == rhs.height );
}

inline bool operator!=( const XRect& lhs, const XRect& rhs )
{
	return ( lhs.x != rhs.x || lhs.y != rhs.y ||
		     lhs.width  != rhs.width ||
			 lhs.height != rhs.height );
}

class XSegment
{
public:
	XPoint mFrom;
	XPoint mTill;
public:

	XSegment() {}

	inline XSegment( const XPoint& from,
				     const XPoint& till )
		: mFrom( from ),
		  mTill( till )
	{}
	
	bool ColidesWith( const XSegment& other ) const;

	// returns intersection of two infinitelly long streight
	// lines 
	XPoint GetIntersection( const XSegment& withOther ) const;

	bool Contains( const XPoint& point ) const;

	inline double GetSlope() const
	{
		return double( mTill.y - mFrom.y ) /
			   double( mTill.x - mFrom.x );
			   
	}

	inline double GetLength() const
	{
		PosT dx = mTill.x - mFrom.x;
		PosT dy = mTill.y - mFrom.y;

		return sqrt( double(PosT(dx*dx + dy*dy)) );
	}

	double GetAngle() const
	{
		PosT dx = mTill.x - mFrom.x;
		PosT dy = mTill.y - mFrom.y;

		return get_true_arctan( dx, dy );
	}

	bool LiesNearBy( const XPoint& pnt, PosT maxDist );

	/*** serialization ***/

	void Serialize( XSStream& stm )
	{
		stm << storeObj( mFrom );
		stm << storeObj( mTill );
	}
};

// FOR NOW:: to work around absence of GetDeviceOrigin() in wxDC

extern XPoint _gCurCanvasOrigin;

class XObject;

/*
 * class used internally to reduce flicker upon wxObject::RefreshNow()
 */

class XCanvasCache : public wxObject
{
protected:
	wxMemoryDC* mpBufDc;
	wxBitmap*   mpBufBmp;

	XRect  mRegion;

	void DestroyBuffer();

public:
	XCanvasCache();

	void Create( XRect& region, XObject* pObj, wxDC& srcDc );

	virtual ~XCanvasCache();

	wxDC& GetCacheCanvas();

	void PaintCacheTo( wxDC& dc );
};

class XPoly : public wxObject
{
public:
	XPointPtrListT mPoints;
public:
	XPoly();
	XPoly( const XPoly& other );
	~XPoly();

	const XPoly& operator=( const XPoly& other );

	void  Add( const XPoint& pnt );
	bool  Contains( const XPoint& pnt );

	bool  LiesNearBy( const XPoint& pnt, PosT maxDist );

	XRect GetBoundingRect();

	void Draw( wxDC& canvas);
	void SolidDraw( wxDC& canvas);

	XPointPtrListT&  GetPoints() 
		{ return mPoints; }

	bool IsClockWise();

	void CreateFromRect( const XRect& rect );
	void Generate( const XPointPtrListT& pointString, 
				   int                width 
				 );

	XPointPtrListT& GetPointString( int width );

	void RemoveAll();

	void RotateAround( const XPoint& origin, 
		               AngleT        angle
					 );

	void Translate( const XPoint& disp );

	bool Contains( const XPoint& pnt, PosT maxDist );

	bool operator<( const XPoly& other ) { return 1; }
	bool operator==( const XPoly& other ) { return 0; }
};

class XObject;
class XSubject;
class XEditor;


WX_DEFINE_ARRAY( XObject*, XObjListT );

/*
 * base class for all "eXtremely simple objects"
 */

class XObject : public wxObject
{
protected:
	XObject*  mpParent;
	XEditor*  mpEditor;
	XSubject* mpSubject; // <- support  "subject/observer" sw-pattern

public:

	XObject();
	virtual ~XObject();

	DECLARE_DYNAMIC_CLASS( XObject )

	// notification send, when object is placed
	// into a container, i.e. becomes a parent,
	virtual void Init() {}

	virtual void SubjectChanged(bool isInitiator) {}

	virtual XSubject* GetSubject();
	virtual void SetSubject( XSubject* pSubject );

	// method useful for environments which do not support
	// run-time type identification
	virtual const char* GetTypeName() { return "XObject"; }

	virtual bool     HasParent();
	virtual XObject& GetParent();

	// override this to return an object within the
	// bounds of which this one can be manipulated

	virtual XObject* GetSurroundingObject() { return NULL; }

	// returns "this" pointer, meant for handling
	// some ugly class-cast cases with virtual base classes

	virtual void* GetThis() { return this; }

	// FIXME:: this is a work-around
	static XPoint GetCanvasOrigin();
	static void SetCanvasOrigin( const XPoint& origin );

	virtual void SetParent( XObject* pParent );

	virtual bool     HasEditor();
	virtual XEditor& GetEditor();

	// NOTE:: the given editor will be autimatically
	//        destroyed (freed) in the destructor of
	//        this object. This object will become the 
	//        parent of the editor
	//        
	virtual void SetEditor( XEditor* pEditor );

	// removes editor form this object, but instead of
	// destroying returns a pointer to editor's instance
	// Can be used to interchange editors without destroying 
	// them
	
	virtual XEditor* UnlinkEditor();    

	// NOTE:: this method SHOULD be overridden in for the instance
	//        of the top-level (parentless) object, so that objects
	//        could update the appearence without externally invoked
	//        Draw(..) method

	virtual wxDC& GetCanvas();

	// returns NULL, if window to which the top-level object
	// is placed, cannot be determined now

	virtual wxWindow* GetWindow();

	// returns true, if the object can be drawn immediately

	virtual bool HasCanvas() ;

	// messages send by framework from upper tree nodes
	// towards leaf-nodes

	virtual bool OnMouseDown( const XPoint& pos ) 
		{ return FALSE; }

	virtual bool OnMouseUp  ( const XPoint& pos )
		{ return FALSE; }

	virtual bool OnMouseMove( const XPoint& pos )
		{ return FALSE; }

	virtual bool OnKeyDown  ( const wxKeyEvent& keyInfo )
		{ return FALSE; }

	virtual bool OnDblClick  ( const XPoint& pos )
		{ return FALSE; }

	virtual bool OnRightClick( const XPoint& pos )
		{ return FALSE; }

	virtual bool OnMouseLeave()
		{ return FALSE; }

	virtual void GetMouseEventInfo( wxMouseEvent& evt );

	virtual bool IsSelectable() { return HasEditor(); }

	// called when object is selected

	virtual void OnSelected() {}

	// method should be overriden to draw appearence of
	// this object on the given canvas

	virtual void Draw( wxDC& canvas ) {}

	// notification is sent, whenever dimensions
	// of the canvas are changed (e.g. window was resized),
	// this notification is propagated down the containment tree

	virtual void CanvasChanged() {}

	///////////////////////////////////////////////////////
	// methods related to collection objects (implemented
	// in the derived collection classes)
	///////////////////////////////////////////////////////

	// NOTE:: neither insert() nor remove() update corresponding
	//        areas of the canvas, this have to be done manually

	virtual void Insert( XObject* pObject, bool refreshNow = TRUE ) {}
	virtual void Remove( XObject* pObject, bool refreshNow = TRUE  ) {}

	// returns NULL, if no objects found in the
	// given location, otherwise returns the one which
	// is on the top of z-ordered list at the given position

	virtual XObject* FindObjectAt( const XPoint& location,
								   bool ignoreMouseFocus = 0 ) 
		{ return FALSE; }

	// methods for managing current selection, setting/resetting
	// selection will automatically refresh corresponding area
	// of the canvas

	virtual void SetSelection( XObjListT& newSelection, bool refreshNow = TRUE );
	virtual void ResetSelection() {}
	virtual bool IsSelected( XObject* pObj = NULL );

	// a litte "shortcut" for SetSelection(...) which takes
	// vector of references as an argument
	void SetSelection( XObject& obj );

	virtual bool IsActive()
		{ return IsSelected( this ); }

	// the pair of methods used for capturing the mouse
	// events. After the capture is done, the events will be
	// routed along single branch in the aggregation tree

	virtual void SetMouseFocus( XObject* pForObject, bool captureMouse = FALSE );

	virtual void ResetMouseFocus( XObject* pForObject, bool releaseMouse = FALSE );

	virtual XObject* GetMouseFocus();

	// method should return bounding box of this object (SHOULD be implemented)

	virtual XRect GetBounds() { return XRect(); }

	// returns area to which the "pseudo-child"-objects should be bounded,
	// once this object is determined to be their surrounding object

	virtual XRect GetLimitingBounds() { return GetBounds(); }

	// implementation is not mandatory, but should be
	// implemented if copy & past is supported
	// (is not logged at undo-able command)

	virtual void SetBounds( const XRect& bounds ) {}

	// method should return TRUE, if the given points
	// precisely hits the inner area of this object,
	// default impl. returns TRUE if point is contained
	// within object's own bounds (not inclusive ones)

	virtual bool Contains( const XPoint& point ) ;

	virtual XRect GetInclusiveBounds();

	// notifications should be invoked to let the framework
	// "know" when the object's shape or location changes
	// the NULL parameter stands for "this" object

	virtual void NotifyModified( XObject* pObj = NULL );
	virtual void NotifyWillModify(  XObject* pObj = NULL );

	/*** support for undo-able operations ***/

	virtual void NotifyWillChange( XObject* pObj = NULL );
	virtual void NotifyWillRemove( XObject* pObj = NULL );
	virtual void NotifyAdded( XObject* pObj = NULL );

	virtual void StartBatch();
	virtual void FinishBatch();

	// method should be implemented if undo/redo operations
	// on this object can be applied

	virtual XObject* CloneCopy( bool identical = FALSE ) { return NULL; }

	// override this, if object should not be removed as a part
	// of current selection

	virtual bool CanRemove() { return TRUE; }

	// just calls editor's CloneCopy() if such present

	virtual XEditor* CloneEditorCopy( bool identical = FALSE );

	// method will mark the given area as "dirty", so that
	// subsequent call to RefeshNow() would automatically
	// refresh it. All invalidated areas are accumulated
	// until the RefreshNow() is called to update these
	// areas at once

	virtual void InvalidateRect( const XRect& area );
	virtual void RefreshNow();

	// causes immediate redrawing of this object

	virtual void RefreshThisObject();

	// places the object on the top of all these others
	// with respect to z-order, NULL pointer stands for 
	// "this" object

	virtual void MoveToFront( XObject* pObj = NULL );

	// notifications received from the editor of this
	// object, can be overridden invoke object-specifications

	virtual bool OnStartEdit()  { NotifyWillChange(); return true; }
	virtual bool OnFinishEdit() { return true; }

	// both call Get/SetBounds() by default
	virtual void SetShape( XPoly& shape );
	virtual void GetShape( XPoly& shape );

	virtual void SetFocus( XObject* pFocus, bool refreshNow = TRUE );
	virtual XObject* GetFocus();

	// notification sent by object's editor when the object
	// (or one of it's parts) is being dragged

	virtual void OnEditorDragged() {}

	static XRect GetUpdateRegion( wxDC& canvas );
	static void  SetUpdateRegion( wxDC& canvas, const XRect& region );

	/*** serialization ***/

	virtual void Serialize( XSStream& stm ) {}
};

class XEditor : public XObject
{
protected:
	bool  mAutoRepaint;
	bool  mAutoRise;
	XRect mEditArea;

public:
	XEditor();

	// returns true if the editable object is 
	// currently selected

	virtual bool IsActive();

	// set editing constraint

	virtual void SetEditArea( const XRect& area );

	XRect& GetEditArea();	

	// if auto-repaint mode is true, editor should
	// show the editable object in it's new (modified)
	// shape while the object is being edited (or dragged)

	void SetAutoRepaint( bool repaintIsOn );

	// TRUE, if object is placed on top of Z-order upon selection

	void SetAutoRise( bool riseOnSelect );

	bool GetAutoRepaint();

	// the below two methods should be implemented
	// by any derived (specific) editor

	virtual void SetShape( XPoly& shape ) {}
	virtual void GetShape( XPoly& shape ) {}

	// notification method can be overridden in the derive classes 
	// to sepcify actions when some part of the link is being dragged

	virtual void OnDragging() {}

	// NOTE:: the parent of the editor is always the editable
	//        object. Editor must propagate Draw() message to
	//        editee if the later supposed to be visible while
	//        editing

	virtual void Draw( wxDC& canvas ) 
		{ GetParent().Draw( canvas ); }

	virtual XObject& GetEditee();

	virtual bool Contains( const XPoint& point ) ;

	// adjusts shape of the editor accordingly

	virtual void NotifyObjectChanged();
};


// following "subject/observer" pattern:
// (quite abstract but can be quite useful...)

class XSubject : public wxObject
{
protected:
	XObjListT mObservers;		   

	// guard against recursive notifications upon destruction
	bool      mDestructionInProgress;

public:
	XSubject();

	// NOTE1:: destructor of the subject should never be called
	//		   directly, instead you should invoke DestroySubject() 
	//         method which will destroy subject, it's observers, and 
	//         release memory allocated for the subject
	//
	// NOTE2:: subject should always be created on the heap,
	//         it shouldn't be static or on-stack object

protected: 
	virtual ~XSubject();

public:

	virtual void DestroySubject();

	virtual const char* GetTypeName() { return "XSubject"; }

	virtual void AddObserver( XObject* pObj );
	virtual void RemoveObserver( XObject* pObj );

	// override this method to implement subject-specific
	// notification to observer, the default calls SubjectChanged(),	
	// invalidates area of it and calles RefreshNow() to observer's 
	// container

	virtual void NotifyObserver( XObject* pObserver, bool isInitiator );

	virtual void NotifySubjectChanged( XObject* pInitiator );
};


enum { X_KEYBOARD_EVENT, 
	   X_MOUSE_DOWN, X_MOUSE_UP, 
	   X_MOUSE_MOVE, X_DBL_CLICK, 
	   X_RIGHT_CLICK
     };

struct XEventInfo
{
public:
	int      mType;

	wxKeyEvent mKeyInfo;
	XPoint   mPos;
public:
	XEventInfo( int type )
		: mType(type) 
	{}
};

class XCommandManager;

// class defines abstract interface for object locators,
// which may be passed to collection objects to specify custom
// strategy for efficient search of objects in the given area

class XObjLocator
{
public:

	XObjLocator() {}
	virtual ~XObjLocator() {}
	// notifications received from collection object
	virtual void NotifyModified( XObject& obj )   = 0;
	virtual void NotifyWillModify( XObject& obj ) = 0;

	// method should place references of all objects to the given
	// container, the objects are those which are enclosed by the 
	// given rectangular area

	virtual void FindObjectsInRect( const XRect& area, XObjListT& objects ) = 0;
};

class XCollection : public XObject
{
protected:

	// contain vector of references to all objects in this container
	// the order of elements in this container corresponds to z-order
	// position of objects with respect to each other

	XObjListT    mObjects;

	// contains references to objects which a currently in the "selected"
	// state 

	XObjListT    mSelection;

	// pointers!
	XObjLocator* mpLocator;
	XObject*     mpMouseFocus;
	XObject*     mpBkObj;

	XRect        mDirtyArea;
	bool         mCachingOn;

	wxEvent*     mpEvtInfo;

	// valid only for the top-level collection object
	wxDC*     mpCanvas;
	wxWindow* mpWindow;

	XObject*     mpFocus;

	XCommandManager* mpCmdMgr;

	XObject* mpLastEventReceiver;

	XPoint       mLastInsertionPos;
	XPoint       mInsertionOfs;

	// FOR NOW:: static

	static XCanvasCache mCache;

protected:
	bool DispatchTo( XObject& target, XEventInfo& evInfo );

	bool DispatchEvent( XEventInfo& evInfo );

public:
	XCollection();
	~XCollection();

	void InvalidateGroup( XObjListT& lst );

	void MarkBackgroundObj( XObject& obj );
	bool IsBackgroundObj( XObject& obj );
	XObject* GetBackgroundObj();

	bool ObjectPresent( XObject* obj );

	virtual void SetFocus( XObject* pFocus, bool refreshNow = TRUE );
	virtual XObject* GetFocus();

	// positioning of newly inserted objects

	XPoint GetLastInsertionPos();
	XPoint GetInsetionOffset();
	XPoint GetNextInsertionPos();

	void SetLastInsertionPos( const XPoint& pos );
	void SetInsertionOfs( const XPoint& ofs );

	/*** canvas-related methods ***/

	bool HasCanvas();
	virtual wxDC& GetCanvas();

	virtual wxWindow* GetWindow();
	void SetWindow( wxWindow* pWnd );

	virtual void GetMouseEventInfo( wxMouseEvent& evt );

	void SetEventInfo( wxEvent* pEvt ) { mpEvtInfo = pEvt; }

	void SetCommandManager( XCommandManager* pMgr );
	XCommandManager* GetCommandManager() { return mpCmdMgr; }

	// canvas-change notification will propagate
	// down to each object in the collection

	virtual void CanvasChanged();

	inline XObjListT& GetContents()
		{ return mObjects; }

	// the default image caching option is on,
	// to reduce flickering effect while drawing
	// multiple objects

	void SetImageCaching( bool isOn = 0 );

	/*** undo/redo support ***/

	virtual void NotifyWillChange( XObject* pObj = 0 );
	virtual void NotifyWillRemove( XObject* pObj = 0 );
	virtual void NotifyAdded( XObject* pObj = 0 );

	virtual void StartBatch();
	virtual void FinishBatch();

	virtual bool OnMouseDown ( const XPoint& pos );
	virtual bool OnMouseUp   ( const XPoint& pos );
	virtual bool OnMouseMove ( const XPoint& pos );
	virtual bool OnDblClick  ( const XPoint& pos );
	virtual bool OnRightClick( const XPoint& pos );
	virtual bool OnKeyDown   ( const wxKeyEvent& keyInfo );

	virtual void Draw( wxDC& canvas );

	/*** methods specific only to the collection object ***/

	size_t GetObjNr( XObject* pObj );

	// returns the resulting z-order of repositioned object

	size_t SetZOrderForObject( XObject* pObj, size_t zOrderNo );


	virtual void SetMouseFocus( XObject* pForObject, bool releaseMouse = FALSE );
	virtual void ResetMouseFocus( XObject* pForObject, bool releaseMouse = FALSE );

	virtual XObject* GetMouseFocus();

	virtual XObject* FindObjectAt( const XPoint& location, 
								   bool ignoreMouseFocus    = FALSE,
								   bool ignoreBackgroundObj = FALSE
								 );

	virtual void FindObjectsInRect( const XRect& area, 
									XObjListT&   objects,
									bool         ignoreBackgroundObj = 0
								  );

	virtual void Insert( XObject* pObject, bool refreshNow = TRUE );
	virtual void Remove( XObject* pObject, bool refreshNow = TRUE );

	// removes given object form container without
	// calling it's destructor (so that later it could 
	// be inserted back) 
	//
	// NOTE: object is not removed from the current selection

	virtual void Detach( XObject* pObject );

	virtual void SetSelection( XObjListT& newSelection, bool refreshNow = TRUE );
	virtual void SelectObject( XObject& obj, bool refreshNow = TRUE );
	virtual void ResetSelection();
	virtual XObjListT& GetSelection();
	virtual bool IsSelected( XObject* pObj = 0 );
	virtual void SelectAll();
	virtual void DeleteSelection();

	virtual void BringSelectionToFront();
	virtual void SendSelectionBack();

	virtual bool HasSelection() { return mSelection.Count() != 0; }
	virtual bool HasSingleSelection() { return mSelection.Count() == 1; }
	virtual bool HasMultipleSelection() { return mSelection.Count() > 1; }

	virtual void SetLocator( XObjLocator* pLocator );

	virtual void NotifyModified( XObject* pObj = 0 );
	virtual void NotifyWillModify(  XObject* pObj = 0 );

	virtual void InvalidateRect( const XRect& area );
	virtual void RefreshNow();

	virtual void MoveToFront( XObject* pObj = 0 );

	virtual XRect GetBounds();
};


enum XCOMMAND_TYPE
{
	XCMD_CHANGE,
	XCMD_ADD,
	XCMD_REMOVE,

	XCMD_LATEST_STATE
};

// TODO:: comments for this. new stuff...

class XCommandInfo
{
public:
	XCommandInfo( int type, XObject* pRealObj, size_t batchLevel );
	~XCommandInfo();

	int GetType() { return mType; }

	bool FinishesBatch() { return mFinishesBatch; }

	int          mType;
	XObject*     mpObjCopy;
	XObject*     mpRealObj;
	bool         mRealObjIsDelted;

	XCollection* mpScene;
	size_t       mZOrder;
	bool         mFinishesBatch;

	XCommandInfo* mpLatestState;

	XCommandInfo* GetLatestState();
	void CaptureLatestState();
};

WX_DEFINE_ARRAY( XCommandInfo*, XCommandListT );

/*
 * class mangages undo/redo and clipboard operations for "XObject"s,
 * which have CloneCopy() method implemented (overridden)
 */

class XCommandManager : public wxObject
{
protected:
	XCollection*  mpScene;
	XCommandListT mCommands;
	size_t        mBatchLevel;
	size_t        mMaxCommands;
	size_t        mNextCmd;

	XObjListT     mClipboard;

	XPoint        mPasteBias;

	bool          mQueueLocked;

	static size_t mIdGenerator;

	void EncodeDeletedObject( XObject* pDeleted );

protected:
	void RelinkReferencesTo( XObject* pNewObj, XCommandInfo& cmd );

	void RemoveObject( XCommandInfo& cmd );
	void RestoreObject( XCommandInfo& cmd );
	void AddObject( XCommandInfo& cmd );
	bool HasModsAhead( size_t cmdNo );

	void Clear();
	void SetScene( XCollection* pScene );

	void CheckBuffer();
	void InsertCommand( XCommandInfo* pCmd );

	void DestroyClipboard();

public:
	XCommandManager( XCollection* pScene, size_t maxCommands = 1000000 );
	~XCommandManager();

	// gives offset, by which the pasted copies will
	// be replaced with respect to the original ones, default is (20,20)

	void SetPasteBias( const XPoint& bias );

	// notifications sent by framework

	virtual void NotifyWillChange( XObject* pObj = 0 );
	virtual void NotifyWillRemove( XObject* pObj = 0 );
	virtual void NotifyAdded( XObject* pObj = 0 );

	virtual void StartBatch();
	virtual void FinishBatch();

	// user-level commands

	virtual bool CanUndo();
	virtual bool CanRedo();
	virtual bool CanPaste();
	virtual bool CanDelete();

	virtual void Undo();
	virtual void Redo();
	virtual void Delete();

	virtual void Copy( XCollection*  pScene );
	virtual void Paste( XCollection* pScene );
};

/*
 * hooks the top-level collection to the given
 * instance of wxWindow 
 */

class XwxWindowHook : public wxEvtHandler
{
protected:
	wxWindow*    mpWnd;
	XCollection* mpScene;
	wxMouseEvent mLastMouseEvent;

	void WindowToCanvas( XPoint& pnt );

public:
	XwxWindowHook( wxWindow* pWnd, XCollection* pScene );
	~XwxWindowHook();

	void OnMouseEvent( wxMouseEvent& event );
	void OnKeyEvent( wxKeyEvent& event );
	void OnPaint( wxPaintEvent& event );
	void OnEraseBg( wxEraseEvent& event );

	inline XCollection& GetScene() { return *mpScene; }

	void SetScene( XCollection* pScene );

	DECLARE_EVENT_TABLE()
};

#endif	// _XOBJECT_INCLUDED
