// BWECtl.cpp : Implementation of the CBWECtrl ActiveX Control class.

#include "stdafx.h"
#include "BWE.h"
#include "BWECtl.h"
#include "BWEPpg.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CBWECtrl, COleControl)


/////////////////////////////////////////////////////////////////////////////
// Message map

BEGIN_MESSAGE_MAP(CBWECtrl, COleControl)
	//{{AFX_MSG_MAP(CBWECtrl)
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_TIMER()
	ON_WM_ACTIVATE()
	ON_WM_SETCURSOR()
	ON_WM_VSCROLL()
	ON_WM_HSCROLL()
	//}}AFX_MSG_MAP
	ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// Dispatch map

BEGIN_DISPATCH_MAP(CBWECtrl, COleControl)
	//{{AFX_DISPATCH_MAP(CBWECtrl)
	DISP_PROPERTY_EX(CBWECtrl, "SizeX", GetSizeX, SetSizeX, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "SizeY", GetSizeY, SetSizeY, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "DotSize", GetDotSize, SetDotSize, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "FromX", GetFromX, SetFromX, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "FromY", GetFromY, SetFromY, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "DotGap", GetDotGap, SetDotGap, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "SelSet", GetSelSet, SetSelSet, VT_BOOL)
	DISP_PROPERTY_EX(CBWECtrl, "SelPosX1", GetSelPosX1, SetSelPosX1, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "SelPosY1", GetSelPosY1, SetSelPosY1, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "SelPosX2", GetSelPosX2, SetSelPosX2, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "SelPosY2", GetSelPosY2, SetSelPosY2, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "DrawMode", GetDrawMode, SetDrawMode, VT_I2)
	DISP_PROPERTY_EX(CBWECtrl, "MouseX", GetMouseX, SetMouseX, VT_I4)
	DISP_PROPERTY_EX(CBWECtrl, "Mode", GetMode, SetMode, VT_BOOL)
	DISP_PROPERTY_EX(CBWECtrl, "TextAlignment", GetTextAlignment, SetTextAlignment, VT_I2)
	DISP_FUNCTION(CBWECtrl, "PaintSelection", PaintSelection, VT_EMPTY, VTS_I2)
	DISP_FUNCTION(CBWECtrl, "Escape", Escape, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "Copy", Copy, VT_BOOL, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "Paste", Paste, VT_BOOL, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "GetData", GetData, VT_BSTR, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "SetData", SetData, VT_BOOL, VTS_BSTR)
	DISP_FUNCTION(CBWECtrl, "GetOvl", GetOvl, VT_BSTR, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "SetOvl", SetOvl, VT_BOOL, VTS_BSTR)
	DISP_FUNCTION(CBWECtrl, "SetText", SetText, VT_EMPTY, VTS_BSTR)
	DISP_FUNCTION(CBWECtrl, "MirrorSelection", MirrorSelection, VT_EMPTY, VTS_BOOL)
	DISP_FUNCTION(CBWECtrl, "ImportPicture", ImportPicture, VT_EMPTY, VTS_PICTURE)
	DISP_FUNCTION(CBWECtrl, "TightBound", TightBound, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "RotateSelection", RotateSelection, VT_BOOL, VTS_I2)
	DISP_FUNCTION(CBWECtrl, "Undo", Undo, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "ResetUndo", ResetUndo, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "SaveUndo", SaveUndo, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "Redo", Redo, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CBWECtrl, "Repaint", Repaint, VT_EMPTY, VTS_NONE)
	DISP_STOCKPROP_FONT()
	//}}AFX_DISPATCH_MAP
	DISP_FUNCTION_ID(CBWECtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()


/////////////////////////////////////////////////////////////////////////////
// Event map

BEGIN_EVENT_MAP(CBWECtrl, COleControl)
	//{{AFX_EVENT_MAP(CBWECtrl)
	EVENT_CUSTOM("MouseBtnUp", FireMouseBtnUp, VTS_XPOS_PIXELS  VTS_YPOS_PIXELS)
	//}}AFX_EVENT_MAP
END_EVENT_MAP()


/////////////////////////////////////////////////////////////////////////////
// Property pages

// TODO: Add more property pages as needed.  Remember to increase the count!
BEGIN_PROPPAGEIDS(CBWECtrl, 1)
	PROPPAGEID(CBWEPropPage::guid)
END_PROPPAGEIDS(CBWECtrl)


/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CBWECtrl, "BWE.BWECtrl.1",
	0xe1e72b77, 0xbec3, 0x11d3, 0x84, 0x85, 0, 0x80, 0xc8, 0x90, 0xae, 0xf9)


/////////////////////////////////////////////////////////////////////////////
// Type library ID and version

IMPLEMENT_OLETYPELIB(CBWECtrl, _tlid, _wVerMajor, _wVerMinor)


/////////////////////////////////////////////////////////////////////////////
// Interface IDs

const IID BASED_CODE IID_DBWE =
		{ 0xe1e72b92, 0xbec3, 0x11d3, { 0x84, 0x85, 0, 0x80, 0xc8, 0x90, 0xae, 0xf9 } };
const IID BASED_CODE IID_DBWEEvents =
		{ 0xe1e72b93, 0xbec3, 0x11d3, { 0x84, 0x85, 0, 0x80, 0xc8, 0x90, 0xae, 0xf9 } };


/////////////////////////////////////////////////////////////////////////////
// Control type information

static const DWORD BASED_CODE _dwBWEOleMisc =
	OLEMISC_ACTIVATEWHENVISIBLE |
	OLEMISC_SETCLIENTSITEFIRST |
	OLEMISC_INSIDEOUT |
	OLEMISC_CANTLINKINSIDE |
	OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CBWECtrl, IDS_BWE, _dwBWEOleMisc)


/////////////////////////////////////////////////////////////////////////////
// CBWECtrl::CBWECtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CBWECtrl

BOOL CBWECtrl::CBWECtrlFactory::UpdateRegistry(BOOL bRegister)
{
	// TODO: Verify that your control follows apartment-model threading rules.
	// Refer to MFC TechNote 64 for more information.
	// If your control does not conform to the apartment-model rules, then
	// you must modify the code below, changing the 6th parameter from
	// afxRegApartmentThreading to 0.

	if (bRegister)
		return AfxOleRegisterControlClass(
			AfxGetInstanceHandle(),
			m_clsid,
			m_lpszProgID,
			IDS_BWE,
			IDB_BWE,
			afxRegApartmentThreading,
			_dwBWEOleMisc,
			_tlid,
			_wVerMajor,
			_wVerMinor);
	else
		return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}


/////////////////////////////////////////////////////////////////////////////
// CBWECtrl::CBWECtrl - Constructor

CBWECtrl::CBWECtrl()
{
	InitializeIIDs(&IID_DBWE, &IID_DBWEEvents);
	
}


/////////////////////////////////////////////////////////////////////////////
// CBWECtrl::~CBWECtrl - Destructor

CBWECtrl::~CBWECtrl()
{
	// TODO: Cleanup your control's instance data here.
}


/////////////////////////////////////////////////////////////////////////////
BOOL CBWECtrl::PreCreateWindow( CREATESTRUCT& cs )
{
    //cs.lpszClass = _T("_afxWnd");
	cs.style|=WS_HSCROLL;
	cs.style|=WS_VSCROLL;
	//cs.cx=100;
	
    return COleControl::PreCreateWindow(cs);
}


// CBWECtrl::OnDraw - Drawing function

void CBWECtrl::OnDraw(
			CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
	CRect CurrRect;
	short fx,fy; //counters 
	short nx,ny; //how many dots can be displayed
	short x1,y1,x2,y2,ofs; // for overlay (selection) borders
	COLORREF BkColor=SetBkColor(*pdc,0);
	SCROLLINFO si;

	CurrBounds=rcBounds;
	//Calculate how many dots can be displayed
	ny=(rcBounds.bottom-rcBounds.top-m_dotGap)/(m_dotSize+m_dotGap);
	nx=(rcBounds.right-rcBounds.left-m_dotGap)/(m_dotSize+m_dotGap);
	
	
	//repaint gaps with background color (one big rectangle cannot be used since it causes
	//a very unpleasant "flickering" to appear during re-painting 
	CurrRect.left=rcBounds.left;
	CurrRect.right=rcBounds.right;
	for(fy=m_fromY;fy<m_sizeY && fy<(m_fromY+ny);fy++){
		CurrRect.top=rcBounds.top+(fy-m_fromY)*(m_dotSize+m_dotGap);
		CurrRect.bottom=CurrRect.top+m_dotGap;
		pdc->FillSolidRect(CurrRect,COLOR_BK);
	}
	
	CurrRect.top=rcBounds.top+(fy-m_fromY)*(m_dotSize+m_dotGap);
	CurrRect.bottom=rcBounds.bottom;
	pdc->FillSolidRect(CurrRect,COLOR_BK);
	
	CurrRect.top=rcBounds.top;
	CurrRect.bottom=rcBounds.bottom;
	for(fx=m_fromX;fx<m_sizeX && fx<(m_fromX+nx);fx++){
		CurrRect.left=rcBounds.left+(fx-m_fromX)*(m_dotSize+m_dotGap);
		CurrRect.right=CurrRect.left+m_dotGap;
		pdc->FillSolidRect(CurrRect,COLOR_BK);
	}

	CurrRect.left=rcBounds.left+(fx-m_fromX)*(m_dotSize+m_dotGap);
	CurrRect.right=rcBounds.right;
	pdc->FillSolidRect(CurrRect,COLOR_BK);


	//calculate the offset of the overlay layer
	if(m_selPosX2>m_selPosX1){
		x1=m_selPosX1;
		x2=m_selPosX2;
	}
	else{
		x1=m_selPosX2;
		x2=m_selPosX1;
	}
	
	if(m_selPosY2>m_selPosY1){
		y1=m_selPosY1;
		y2=m_selPosY2;
	}
	else{
		y1=m_selPosY2;
		y2=m_selPosY1;
	}

	ofs=y1*MAX_WIDTH+x1;
	
	// display the dots (overlay+main layers)
	for(fy=m_fromY;fy<m_sizeY && fy<(m_fromY+ny);fy++){
		CurrRect.top=rcBounds.top+(fy-m_fromY)*(m_dotSize+m_dotGap)+m_dotGap;
		CurrRect.bottom=CurrRect.top+m_dotSize;
		for(fx=m_fromX;fx<m_sizeX && fx<(m_fromX+nx);fx++){
			CurrRect.left=rcBounds.left+(fx-m_fromX)*(m_dotSize+m_dotGap)+m_dotGap;
			CurrRect.right=CurrRect.left+m_dotSize;
			//check if selection is enabled and this dot is within current overlay rectangle
			if(m_selSet && fx>=x1 && fx<=x2 && fy>=y1 && fy<=y2){
				//within current selection rectangle
				if(m_ovlArr[fy*MAX_WIDTH+fx-ofs]==2){
					//overlay dot is "transparent"
					if(m_dataArr[fy*MAX_WIDTH+fx]!=0)
						pdc->FillSolidRect(CurrRect,COLOR_DOT_ON_THRU_OVL);
					else
						pdc->FillSolidRect(CurrRect,COLOR_DOT_OFF);
				}
				else{
					//overlay dot is not transparent
					if(m_ovlArr[fy*MAX_WIDTH+fx-ofs]!=0)
						pdc->FillSolidRect(CurrRect,COLOR_DOT_ON_OVL);
					else
						pdc->FillSolidRect(CurrRect,COLOR_DOT_OFF);
				}
			}
			else{
				//outside of current selection rectangle
				if(m_dataArr[fy*MAX_WIDTH+fx]!=0)
					pdc->FillSolidRect(CurrRect,COLOR_DOT_ON);
				else
					pdc->FillSolidRect(CurrRect,COLOR_DOT_OFF);
			}
		}
	}
	
	// Draw Selection (if any)
	if(m_selSet){
		if(m_selState==SEL_RESIZE_OR_DRAG || m_selState==SEL_RESIZING)
			drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,TRUE,TRUE);
		else
			drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,TRUE,FALSE);
	}
	
	//refresh scroll-bars
	si.fMask=SIF_PAGE|SIF_POS|SIF_RANGE;
	si.nPage=nx;
	si.nMin=0;
	si.nMax=m_sizeX;
	si.nPos=m_fromX;
	SetScrollInfo(SB_HORZ,&si,TRUE);
	si.nPage=ny;
	si.nMin=0;
	si.nMax=m_sizeY;
	si.nPos=m_fromY;
	SetScrollInfo(SB_VERT,&si,TRUE);

	//restore backcolor
	SetBkColor(*pdc,BkColor);
}


/////////////////////////////////////////////////////////////////////////////
// CBWECtrl::DoPropExchange - Persistence support

void CBWECtrl::DoPropExchange(CPropExchange* pPX)
{
	unsigned int f;
	
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);

	// TODO: Call PX_ functions for each persistent custom property.
	PX_Short(pPX, _T("SizeX"), m_sizeX, DEF_WIDTH);
	PX_Short(pPX, _T("SizeY"), m_sizeY, DEF_HEIGHT);
	PX_Short(pPX, _T("DotSize"), m_dotSize, DEF_DOT_SIZE);
	PX_Short(pPX, _T("DotGap"), m_dotGap, DEF_DOT_GAP);
	PX_Short(pPX, _T("FromX"), m_fromX, 0);
	PX_Short(pPX, _T("FromY"), m_fromY, 0);
	PX_Bool(pPX, _T("SelSet"), m_selSet, FALSE);
	PX_Short(pPX, _T("SelPosX1"), m_selPosX1, 0);
	PX_Short(pPX, _T("SelPosY1"), m_selPosY1, 0);
	PX_Short(pPX, _T("SelPosX2"), m_selPosX2, 0);
	PX_Short(pPX, _T("SelPosY2"), m_selPosY2, 0);
	PX_Bool(pPX, _T("Mode"), m_mode, FALSE);
	PX_Short(pPX, _T("DrawMode"), m_drawMode, 0);
	PX_Short(pPX, _T("TextAlignment"), m_textAlignment, 0);
	//Reset the graphical data to its default state
	for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++){
		m_dataArr[f]=0; //main data array to "all white"
		m_ovlArr[f]=2; //overlay data array to "all transparent"
	}
	//Reset other stuff
	m_noDot=FALSE;
	m_btnState=FALSE;
	m_panInProg=FALSE;
	m_selState=SEL_IDLE;
	m_cfPrivate=NULL;
	m_undoCtr=0;
	m_undoPtr=0;
}


/////////////////////////////////////////////////////////////////////////////
// CBWECtrl::OnResetState - Reset control to default state

void CBWECtrl::OnResetState()
{
	COleControl::OnResetState();  // Resets defaults found in DoPropExchange

	// TODO: Reset any other control state here.
}


/////////////////////////////////////////////////////////////////////////////
// CBWECtrl::AboutBox - Display an "About" box to the user

void CBWECtrl::AboutBox()
{
	CDialog dlgAbout(IDD_ABOUTBOX_BWE);
	dlgAbout.DoModal();
}


/////////////////////////////////////////////////////////////////////////////
// CBWECtrl message handlers

short CBWECtrl::GetSizeX() 
{
	return m_sizeX;
}

void CBWECtrl::SetSizeX(short nNewValue) 
{
	if(nNewValue>MAX_WIDTH)
		m_sizeX=MAX_WIDTH;
	else if(nNewValue<1)
		m_sizeX=1;
	else
		m_sizeX=nNewValue;

	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetSizeY() 
{
	return m_sizeY;
}

void CBWECtrl::SetSizeY(short nNewValue) 
{
	if(nNewValue>MAX_HEIGHT)
		m_sizeY=MAX_HEIGHT;
	else if(nNewValue<1)
		m_sizeY=1;
	else
		m_sizeY=nNewValue;
	
	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetDotSize() 
{
	return m_dotSize;
}

void CBWECtrl::SetDotSize(short nNewValue) 
{
	if(nNewValue>MAX_DOT_SIZE)
		m_dotSize=MAX_DOT_SIZE;
	else if(nNewValue<1)
		m_dotSize=1;
	else
		m_dotSize=nNewValue;

	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetFromX() 
{
	return m_fromX;
}

void CBWECtrl::SetFromX(short nNewValue) 
{
	if(nNewValue>MAX_WIDTH)
		m_fromX=MAX_WIDTH;
	else if(nNewValue<0)
		m_fromX=0;
	else
		m_fromX=nNewValue;

	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetFromY() 
{
	return m_fromY;
}

void CBWECtrl::SetFromY(short nNewValue) 
{
	if(nNewValue>MAX_HEIGHT)
		m_fromY=MAX_HEIGHT;
	else if(nNewValue<0)
		m_fromY=0;
	else
		m_fromY=nNewValue;


	SetModifiedFlag();
	InvalidateControl();
}

//////////////////////////////////////////////////////////////////////////////////////////////
void CBWECtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
	
	short f;
	CRect CurrRect;
	CClientDC dc( this );
	COLORREF BkColor=SetBkColor(dc,0);
	
	//check mode
	if(m_mode){
		//DRAWING MODE
	}
	else{
		//SELECTION MODE
		
		if(m_selState==SEL_IDLE){
			//idle, ready to start selecting

		}

		else if(m_selState==SEL_SELECTING){
			//initial selection has been finished
			m_selState=SEL_RESIZE_OR_DRAG;
			for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++)
				m_ovlArr[f]=2; //transparent
		}

		else if(m_selState==SEL_RESIZE_OR_DRAG){
			//initial selection completed, can resize or start dragging
			//clear overaly data
		}

		else if(m_selState==SEL_RESIZING){
			//resizing
		}
		else{
			//dragging
		}		
	}
	
	ReleaseCapture();
	m_panInProg=FALSE;
		
	m_btnState=FALSE;
	SetBkColor(dc,BkColor);
	InvalidateControl();
	COleControl::OnLButtonUp(nFlags, point);

}

////////////////////////////////////////////////////////////////////////////////////////////
short CBWECtrl::GetDotGap() 
{
	return m_dotGap;
}

void CBWECtrl::SetDotGap(short nNewValue) 
{
	
	if(nNewValue>MAX_DOT_GAP)
		m_dotGap=MAX_DOT_GAP;
	else if(nNewValue<1)
		m_dotGap=1;
	else
		m_dotGap=nNewValue;

	SetModifiedFlag();
	InvalidateControl();
}

void CBWECtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	short x,y,b;
	CClientDC dc( this );
	CRect CurrRect;

	//required to track mouse and buttons even when it is outside of the client window
	SetCapture();
	
	//check the Mode: 0- selection, 1- drawing
	
	if(m_mode){
		//DRAWING MODE
		if(dotFromMouseCoord(point,&x,&y,FALSE)){
			//click was made over a point, corresponding dot was returned in x,y
			CurrRect.left=(x-m_fromX)*(m_dotSize+m_dotGap)+m_dotGap+CurrBounds.left;
			CurrRect.right=CurrRect.left+m_dotSize;
			CurrRect.top=(y-m_fromY)*(m_dotSize+m_dotGap)+m_dotGap+CurrBounds.top;
			CurrRect.bottom=CurrRect.top+m_dotSize;
			//memorize this dot's coordinate
			m_drawX=x;
			m_drawY=y;
			//update data array (depending on the Draw mode)
			if(m_drawMode==0){
				//to black
				m_dataArr[y*MAX_WIDTH+x]=1;
				dc.FillSolidRect(CurrRect,COLOR_DOT_ON);
			}
			else if(m_drawMode==1){
				//to white
				m_dataArr[y*MAX_WIDTH+x]=0;
				dc.FillSolidRect(CurrRect,COLOR_DOT_OFF);
			}
			else{
				//invert
				if(m_dataArr[y*MAX_WIDTH+x]==0){
					m_dataArr[y*MAX_WIDTH+x]=1;
					dc.FillSolidRect(CurrRect,COLOR_DOT_ON);
				}
				else{
					m_dataArr[y*MAX_WIDTH+x]=0;
					dc.FillSolidRect(CurrRect,COLOR_DOT_OFF);
				}
			}
		}	
	}
	else{
		// SELECTION MODE

		if(m_selState==SEL_IDLE){
			//idle, ready to start selecting
		}

		else if(m_selState==SEL_SELECTING){
			//XY1 point has been selected, selecting XY2
		}

		else if(m_selState==SEL_RESIZE_OR_DRAG){
			//initial selection completed, can resize or start dragging or reset selection
			//decide what will it be
			b=BorderTest(point);
			if(b==B_NIL){
				//missed selected area completely: reset selection
				m_selSet=FALSE;
				m_selState=SEL_IDLE;
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,FALSE,FALSE);
			}
			else if(b==B_I){
				//inside area has been clicked- advance to drag mode

				//if CTRL key is not pressed- erase the area under the selection
				//if pressed- just copy it
				//if SHIFT key is not pressed- copy both white and black dots, else- 
				//copy white dots as transparent
				if(!(nFlags&MK_CONTROL) && !(nFlags&MK_SHIFT))
					CopyOvl(TRUE,FALSE);
				else if(nFlags&MK_CONTROL && !(nFlags&MK_SHIFT))
					CopyOvl(FALSE,FALSE);
				else if(!(nFlags&MK_CONTROL) && nFlags&MK_SHIFT )
					CopyOvl(TRUE,TRUE);
				else
					CopyOvl(FALSE,TRUE);
				
				//memorize current pointer position with respect to the left-top corner of
				//selection rectangle
				dotFromMouseCoord(point,&m_dragPtrOfsX,&m_dragPtrOfsY,TRUE);
				if(m_selPosX2>m_selPosX1)
					m_dragPtrOfsX=m_dragPtrOfsX-m_selPosX1;
				else
					m_dragPtrOfsX=m_dragPtrOfsX-m_selPosX2;
				
				if(m_selPosY2>m_selPosY1)
					m_dragPtrOfsY=m_dragPtrOfsY-m_selPosY1;
				else
					m_dragPtrOfsY=m_dragPtrOfsY-m_selPosY2;

				m_selState=SEL_DRAGGING;
			}

			else{
				//border has been hit- switch into resizing
				m_selState=SEL_RESIZING;
				//memorize hit point (to calculate relative mouse movement)
				m_resizeDir=b;
				DisplCursor(b);
			}
		}

		else if(m_selState==SEL_RESIZING){
			//resizing
			b=BorderTest(point);
			if(b==B_NIL){
				//missed selected area completely: reset selection
				m_selSet=FALSE;
				m_selState=SEL_IDLE;
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,FALSE,FALSE);
			}
			else if(b==B_I){
				//clicked inside- switch into drag mode
				//if CTRL key is not pressed- erase the area under the selection
				//if pressed- just copy it
				//if SHIFT key is not pressed- copy both white and black dots, else- 
				//copy white dots as transparent
				if(!(nFlags&MK_CONTROL) && !(nFlags&MK_SHIFT))
					CopyOvl(TRUE,FALSE);
				else if(nFlags&MK_CONTROL && !(nFlags&MK_SHIFT))
					CopyOvl(FALSE,FALSE);
				else if(!(nFlags&MK_CONTROL) && nFlags&MK_SHIFT )
					CopyOvl(TRUE,TRUE);
				else
					CopyOvl(FALSE,TRUE);
				
				//memorize current pointer position with respect to the left-top corner of
				//selection rectangle
				dotFromMouseCoord(point,&m_dragPtrOfsX,&m_dragPtrOfsY,TRUE);
				if(m_selPosX2>m_selPosX1)
					m_dragPtrOfsX=m_dragPtrOfsX-m_selPosX1;
				else
					m_dragPtrOfsX=m_dragPtrOfsX-m_selPosX2;
				
				if(m_selPosY2>m_selPosY1)
					m_dragPtrOfsY=m_dragPtrOfsY-m_selPosY1;
				else
					m_dragPtrOfsY=m_dragPtrOfsY-m_selPosY2;

				m_selState=SEL_DRAGGING;
			}
			else{
				//border has been hit- continue resizing
				//memorize hit point (to calculate relative mouse movement)
				m_resizeDir=b;
			}
		}

		else{
			//dragging already- check if clicked inside of selected area (this time including
			//borders since there will be no resizing after the system has entered drag mode
			b=BorderTest(point);
			if(b==B_NIL){
				//missed selected area completely: reset selection, paste data
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,FALSE,FALSE);
				PasteOvl();
				m_selSet=FALSE;
				m_selState=SEL_IDLE;
			}
			else{
				//inside of selected area
				//memorize current pointer position with respect to the left-top corner of
				//selection rectangle
				dotFromMouseCoord(point,&m_dragPtrOfsX,&m_dragPtrOfsY,TRUE);
				if(m_selPosX2>m_selPosX1)
					m_dragPtrOfsX=m_dragPtrOfsX-m_selPosX1;
				else
					m_dragPtrOfsX=m_dragPtrOfsX-m_selPosX2;
				
				if(m_selPosY2>m_selPosY1)
					m_dragPtrOfsY=m_dragPtrOfsY-m_selPosY1;
				else
					m_dragPtrOfsY=m_dragPtrOfsY-m_selPosY2;
				//if CTRL key is pressed- copy the data onto the main level (but do not
				//destroy the selection or drag mode
				if(nFlags&MK_CONTROL)
					PasteOvl();				
			}
		}
	}
	m_btnState=TRUE;
	COleControl::OnLButtonDown(nFlags, point);
}

/////////////////////////////////////////////////////////////////////////////////////////////
void CBWECtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
	short x,y;
	short sx,sy,cx,cy;
	CClientDC dc( this );
	CRect CurrRect;
	m_mouseX=point.x;	//<<<<<<<<<<<<<<<<<<<<
	

	//check button status
	if(m_btnState){
		//BUTTON IS PRESSED, check mode
		if(m_mode){
			//DRAWING MODE
			//Determine current dot
			if(dotFromMouseCoord(point,&x,&y,FALSE)){
				//click was made over a point, corresponding dot was returned in x,y
				//update the point only if it haven't been updated yet
				if(x!=m_drawX || y!=m_drawY){
					CurrRect.left=(x-m_fromX)*(m_dotSize+m_dotGap)+m_dotGap+CurrBounds.left;
					CurrRect.right=CurrRect.left+m_dotSize;
					CurrRect.top=(y-m_fromY)*(m_dotSize+m_dotGap)+m_dotGap+CurrBounds.top;
					CurrRect.bottom=CurrRect.top+m_dotSize;
					//memorize this dot's coordinate
					m_drawX=x;
					m_drawY=y;
					//update data array (depending on the Draw mode)
					if(m_drawMode==0){
						//to black
						m_dataArr[y*MAX_WIDTH+x]=1;
						dc.FillSolidRect(CurrRect,COLOR_DOT_ON);
					}
					else if(m_drawMode==1){
						//to white
						m_dataArr[y*MAX_WIDTH+x]=0;
						dc.FillSolidRect(CurrRect,COLOR_DOT_OFF);
					}
					else{
						//invert
						if(m_dataArr[y*MAX_WIDTH+x]==0){
							m_dataArr[y*MAX_WIDTH+x]=1;
							dc.FillSolidRect(CurrRect,COLOR_DOT_ON);
						}
						else{
						m_dataArr[y*MAX_WIDTH+x]=0;
						dc.FillSolidRect(CurrRect,COLOR_DOT_OFF);
						}
					}
				}
			}
		}
		else{
			//SELECTION MODE
			//check if mouse pointer went out of control's window- perform panning if so
			if(point.x<0 || point.y<0 || point.x>=CurrBounds.right-CurrBounds.left || point.y>=CurrBounds.bottom-CurrBounds.top){
				//out of window, decide on the panning direction
				if(point.x<0)
					m_panLeft=TRUE;
				else
					m_panLeft=FALSE;
				if(point.y<0)
					m_panUp=TRUE;
				else
					m_panUp=FALSE;
				if(point.x>=CurrBounds.right-CurrBounds.left)
					m_panRight=TRUE;
				else
					m_panRight=FALSE;
				if(point.y>=CurrBounds.bottom-CurrBounds.top)
					m_panDown=TRUE;
				else
					m_panDown=FALSE;

				if(!m_panInProg){
					m_panInProg=TRUE;
					SetTimer(0,100,0);
				}
			}
			else{
				//within window
				if(m_panInProg){
					m_panInProg=FALSE;
					KillTimer(0);
				}
			}

			
			//branch according to different select/drag states
			if(m_selState==SEL_IDLE){
				//idle, ready to start selecting
				//check if clicking on any dot (incl. left and top gaps)
				if(dotFromMouseCoord(point,&x,&y,TRUE)){
					//clicked on a point, initially the selection is around one dot
					m_selPosX2=m_selPosX1=x;
					m_selPosY2=m_selPosY1=y;
					m_selSet=TRUE; //selection process has been initialized
					drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,TRUE,FALSE);
					m_selState=SEL_SELECTING;
				}
			}

			else if(m_selState==SEL_SELECTING){
				//XY1 point has been selected, selecting XY2
				//update XY2 position
				dotFromMouseCoord(point,&x,&y,TRUE);
				//erase old selection rectangle first
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,FALSE,FALSE);
				//draw new one
				m_selPosX2=x;
				m_selPosY2=y;
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,TRUE,FALSE);
			}

			else if(m_selState==SEL_RESIZE_OR_DRAG){
				//initial selection completed, can resize or start dragging
			}
		
			else if(m_selState==SEL_RESIZING){
				//resizing
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,FALSE,FALSE);
				dotFromMouseCoord(point,&x,&y,TRUE);
				switch(m_resizeDir){
				case B_R:
					if(m_selPosX2>=m_selPosX1){
						if(x>=m_selPosX1)
							m_selPosX2=x;
						else
							m_selPosX2=m_selPosX1;
					}
					else{
						if(x>=m_selPosX2)
							m_selPosX1=x;
						else
							m_selPosX1=m_selPosX2;
					}
					break;
				
				case B_L:
					if(m_selPosX2>=m_selPosX1){
						if(x<=m_selPosX2)
							m_selPosX1=x;
						else
							m_selPosX1=m_selPosX2;
					}
					else{
						if(x<=m_selPosX1)
							m_selPosX2=x;
						else
							m_selPosX2=m_selPosX1;
					}
					
					break;
				
				case B_B:
					if(m_selPosY2>=m_selPosY1){
						if(y>=m_selPosY1)
							m_selPosY2=y;
						else
							m_selPosY2=m_selPosY1;
					}
					else{
						if(y>=m_selPosY2)
							m_selPosY1=y;
						else
							m_selPosY1=m_selPosY2;
					}
					break;

				case B_T:
					if(m_selPosY2>=m_selPosY1){
						if(y<=m_selPosY2)
							m_selPosY1=y;
						else
							m_selPosY1=m_selPosY2;
					}
					else{
						if(y<=m_selPosY1)
							m_selPosY2=y;
						else
							m_selPosY2=m_selPosY1;
					}
					break;

				case B_TL:
					if(m_selPosY2>=m_selPosY1){
						if(y<=m_selPosY2)
							m_selPosY1=y;
						else
							m_selPosY1=m_selPosY2;
					}
					else{
						if(y<=m_selPosY1)
							m_selPosY2=y;
						else
							m_selPosY2=m_selPosY1;
					}
					
					if(m_selPosX2>=m_selPosX1){
						if(x<=m_selPosX2)
							m_selPosX1=x;
						else
							m_selPosX1=m_selPosX2;
					}
					else{
						if(x<=m_selPosX1)
							m_selPosX2=x;
						else
							m_selPosX2=m_selPosX1;
					}
					
					break;

				case B_TR:
					if(m_selPosY2>=m_selPosY1){
						if(y<=m_selPosY2)
							m_selPosY1=y;
						else
							m_selPosY1=m_selPosY2;
					}
					else{
						if(y<=m_selPosY1)
							m_selPosY2=y;
						else
							m_selPosY2=m_selPosY1;
					}
					
					if(m_selPosX2>=m_selPosX1){
						if(x>=m_selPosX1)
							m_selPosX2=x;
						else
							m_selPosX2=m_selPosX1;
					}
					else{
						if(x>=m_selPosX2)
							m_selPosX1=x;
						else
							m_selPosX1=m_selPosX2;
					}
					
					break;

				case B_BL:
					if(m_selPosY2>=m_selPosY1){
						if(y>=m_selPosY1)
							m_selPosY2=y;
						else
							m_selPosY2=m_selPosY1;
					}
					else{
						if(y>=m_selPosY2)
							m_selPosY1=y;
						else
							m_selPosY1=m_selPosY2;
					}
					
					if(m_selPosX2>=m_selPosX1){
						if(x<=m_selPosX2)
							m_selPosX1=x;
						else
							m_selPosX1=m_selPosX2;
					}
					else{
						if(x<=m_selPosX1)
							m_selPosX2=x;
						else
							m_selPosX2=m_selPosX1;
					}
					
					break;

				case B_BR:
					if(m_selPosY2>=m_selPosY1){
						if(y>=m_selPosY1)
							m_selPosY2=y;
						else
							m_selPosY2=m_selPosY1;
					}
					else{
						if(y>=m_selPosY2)
							m_selPosY1=y;
						else
							m_selPosY1=m_selPosY2;
					}
					if(m_selPosX2>=m_selPosX1){
						if(x>=m_selPosX1)
							m_selPosX2=x;
						else
							m_selPosX2=m_selPosX1;
					}
					else{
						if(x>=m_selPosX2)
							m_selPosX1=x;
						else
							m_selPosX1=m_selPosX2;
					}
					break;
				
				default:
					break;
							}
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,TRUE,TRUE);
			}
		
			else{
				//dragging
				dotFromMouseCoord(point,&cx,&cy,TRUE);
				if(m_selPosX2>m_selPosX1)
					sx=m_selPosX2-m_selPosX1;
				else
					sx=m_selPosX1-m_selPosX2;
				if(m_selPosY2>m_selPosY1)
					sy=m_selPosY2-m_selPosY1;
				else
					sy=m_selPosY1-m_selPosY2;
				
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,FALSE,FALSE);
				m_selPosX1=cx-m_dragPtrOfsX;
				m_selPosY1=cy-m_dragPtrOfsY;
				m_selPosX2=m_selPosX1+sx;
				m_selPosY2=m_selPosY1+sy;
				
				//make sure new rectangle position is within range
				if(m_selPosX1<0){
					m_selPosX1=0;
					m_selPosX2=sx;
				}
				if(m_selPosY1<0){
					m_selPosY1=0;
					m_selPosY2=sy;
				}
				if(m_selPosX2>=m_sizeX){
					m_selPosX2=m_sizeX-1;
					m_selPosX1=m_selPosX2-sx;
				}
				if(m_selPosY2>=m_sizeY){
					m_selPosY2=m_sizeY-1;
					m_selPosY1=m_selPosY2-sy;
				}
				drawSel(m_selPosX1,m_selPosY1,m_selPosX2,m_selPosY2,TRUE,FALSE);
			}			
		}	
	}
	else{
		//BUTTON IS NOT PRESSED
		//display appropriate cursor
		DisplCursor(BorderTest(point));

	}
	COleControl::OnMouseMove(nFlags, point);
}

////////////////////////////////////////////////////////////////////////////////////////
BOOL CBWECtrl::GetSelSet() 
{
	return m_selSet;
}

void CBWECtrl::SetSelSet(BOOL bNewValue) 
{
	m_selSet=bNewValue;
	m_selState=SEL_IDLE;
	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetSelPosX1() 
{
	return m_selPosX1;
}

void CBWECtrl::SetSelPosX1(short nNewValue) 
{
	if(nNewValue>=m_sizeX)
		m_selPosX1=m_sizeX;
	else if(nNewValue<0)
		m_selPosX1=0;
	else
		m_selPosX1=nNewValue;

	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetSelPosY1() 
{
	return m_selPosY1;
}

void CBWECtrl::SetSelPosY1(short nNewValue) 
{
	if(nNewValue>=m_sizeY)
		m_selPosY1=m_sizeY;
	else if(nNewValue<0)
		m_selPosY1=0;
	else
		m_selPosY1=nNewValue;
	
	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetSelPosX2() 
{
	return m_selPosX2;
}

void CBWECtrl::SetSelPosX2(short nNewValue) 
{
	if(nNewValue>=m_sizeX)
		m_selPosX2=m_sizeX;
	else if(nNewValue<0)
		m_selPosX2=0;
	else
		m_selPosX2=nNewValue;
	
	SetModifiedFlag();
	InvalidateControl();
}

short CBWECtrl::GetSelPosY2() 
{
	return m_selPosY2;
}

void CBWECtrl::SetSelPosY2(short nNewValue) 
{
	if(nNewValue>=m_sizeY)
		m_selPosY2=m_sizeY;
	else if(nNewValue<0)
		m_selPosY2=0;
	else
		m_selPosY2=nNewValue;
	
	SetModifiedFlag();
	InvalidateControl();
}


/////////////////////////////////////////////////////////////////////////////////////
void CBWECtrl::drawSel(short x1, short y1, short x2, short y2, bool fl, bool rubber)
{
	//draws/removes selection depending on the fl: FALSE- remove, TRUE- draw
	//if rubber flag is set- rectangle will be dotted
	//Selection is inclusive. If X1=Y1=X2=Y2=0 then this means that rectangle should
	//be around dot0,0
	
	short nx,ny; //remembers how many dots can be displays within current screen
	long f;
	short lx1,ly1,lx2,ly2;
	CRect CurrRect,RRect;
	CClientDC dc( this );			
	
	COLORREF BkColor=SetBkColor(dc,0);
	COLORREF CurrColor;

	if(fl)
		CurrColor=COLOR_SEL_RECT;
	else
		CurrColor=COLOR_BK;

	ny=(CurrBounds.bottom-CurrBounds.top-m_dotGap)/(m_dotSize+m_dotGap);
	nx=(CurrBounds.right-CurrBounds.left-m_dotGap)/(m_dotSize+m_dotGap);
	
	// make sure that the first point is "higher and to the left"
	if(m_selPosX2>=m_selPosX1){
		x1=m_selPosX1;
		x2=m_selPosX2+1;
	}
	else{
		x1=m_selPosX2;
		x2=m_selPosX1+1;
	}
	
	if(m_selPosY2>=m_selPosY1){
		y1=m_selPosY1;
		y2=m_selPosY2+1;
	}
	else{
		y1=m_selPosY2;
		y2=m_selPosY1+1;
	}	
	
	//selection rectangle cannot fall outside of current image size
	if(x1<0)
		x1=0;
	if(y1<0)
		y1=0;
	if(x2>m_sizeX)
		x2=m_sizeX;
	if(y2>m_sizeY)
		y2=m_sizeY;
	
	//selection may not be completely visible, process each size separately
	
	//LEFT AND RIGHT SIDES, check if there is a line to draw 
	if(y1>=m_fromY || y2<m_fromY+ny || (y1<m_fromY && y2>=m_fromY+ny)){
		
		if(y1>=m_fromY)
			ly1=y1;
		else
			ly1=m_fromY;
		
		if(y2<=m_fromY+ny)
			ly2=y2;
		else
			ly2=m_fromY+ny;
		CurrRect.top=CurrBounds.top + (ly1-m_fromY)*(m_dotSize+m_dotGap);
		CurrRect.bottom=CurrBounds.top + (ly2-m_fromY)*(m_dotSize+m_dotGap)+m_dotGap;
		
		//left side is visible?
		if(x1>=m_fromX){
			CurrRect.left=CurrBounds.left + (x1-m_fromX)*(m_dotSize+m_dotGap);
			CurrRect.right=CurrRect.left+m_dotGap;
			if(!rubber)
				dc.FillSolidRect(CurrRect,CurrColor);
			else{
				RRect.left=CurrRect.left;
				RRect.right=CurrRect.right;
				for(f=CurrRect.top;f<CurrRect.bottom;f=f+m_dotGap*3){
					RRect.top=f;
					RRect.bottom=f+m_dotGap*2;
					if(RRect.bottom>CurrRect.bottom)
						RRect.bottom=CurrRect.bottom;
					dc.FillSolidRect(RRect,CurrColor);
				}
			}
		}

		//right side is visible?
		if(x2<m_fromX+nx){
			CurrRect.right=CurrBounds.left + (x2-m_fromX)*(m_dotSize+m_dotGap)+m_dotGap;	
			CurrRect.left=CurrRect.right-m_dotGap;
			if(!rubber)
				dc.FillSolidRect(CurrRect,CurrColor);
			else{
				RRect.left=CurrRect.left;
				RRect.right=CurrRect.right;
				for(f=CurrRect.top;f<CurrRect.bottom;f=f+m_dotGap*3){
					RRect.top=f;
					RRect.bottom=f+m_dotGap*2;
					if(RRect.bottom>CurrRect.bottom)
						RRect.bottom=CurrRect.bottom;
					dc.FillSolidRect(RRect,CurrColor);
				}
			}
		}
	}
	
	//TOP AND BOTTOM SIDES, check if there is a line to draw 
	if(x1>=m_fromX || x2<m_fromX+nx || (x1<m_fromX && x2>=m_fromX+nx)){
		
		if(x1>=m_fromX)
			lx1=x1;
		else
			lx1=m_fromX;
		
		if(x2<=m_fromX+nx)
			lx2=x2;
		else
			lx2=m_fromX+nx;
		CurrRect.left=CurrBounds.left + (lx1-m_fromX)*(m_dotSize+m_dotGap);
		CurrRect.right=CurrBounds.left + (lx2-m_fromX)*(m_dotSize+m_dotGap)+m_dotGap;
		
		//top side is visible?
		if(y1>=m_fromY){
			CurrRect.top=CurrBounds.top + (y1-m_fromY)*(m_dotSize+m_dotGap);
			CurrRect.bottom=CurrRect.top+m_dotGap;
			if(!rubber)
				dc.FillSolidRect(CurrRect,CurrColor);
			else{
				RRect.top=CurrRect.top;
				RRect.bottom=CurrRect.bottom;
				for(f=CurrRect.left;f<CurrRect.right;f=f+m_dotGap*3){
					RRect.left=f;
					RRect.right=f+m_dotGap*2;
					if(RRect.right>CurrRect.right)
						RRect.right=CurrRect.right;
					dc.FillSolidRect(RRect,CurrColor);
				}
			}
		}

		//right side is visible?
		if(y2<m_fromY+ny){
			CurrRect.top=CurrBounds.top + (y2-m_fromY)*(m_dotSize+m_dotGap)+m_dotGap;	
			CurrRect.bottom=CurrRect.top-m_dotGap;
			if(!rubber)
				dc.FillSolidRect(CurrRect,CurrColor);
			else{
			RRect.top=CurrRect.top;
				RRect.bottom=CurrRect.bottom;
				for(f=CurrRect.left;f<CurrRect.right;f=f+m_dotGap*3){
					RRect.left=f;
					RRect.right=f+m_dotGap*2;
					if(RRect.right>CurrRect.right)
						RRect.right=CurrRect.right;
					dc.FillSolidRect(RRect,CurrColor);
				}
			}
		}
	}

	//restore background color
	SetBkColor(dc,BkColor);

}


short CBWECtrl::GetDrawMode() 
{
	return m_drawMode;
}

void CBWECtrl::SetDrawMode(short nNewValue) 
{
	if(nNewValue>2)
		m_drawMode=2;
	else if(nNewValue<0)
		m_drawMode=0;
	else
		m_drawMode=nNewValue;

	SetModifiedFlag();
	InvalidateControl();
}



bool CBWECtrl::dotFromMouseCoord(CPoint point, short *px, short *py, bool gap)
{
	//transforms actual mouse coordinates supplied into corresponding grid coordinate. "gap" flag
	//specifies whether dot's left and top border should be considered as belonging to this
	//dot (gap=TRUE) or not (gap=FALSE). This call returns FALSE if click missed the dot or
	//true if there was a dot under the mouse pointer. IN ANY CASE, this call transforms mouse
	//coords into closest valid grid coordinates 
	
	long x,y,x2,y2,x3,y3;
	bool bf=TRUE;

	// calc maximum valid co-ord that we should pay attention at
	y2=((CurrBounds.bottom-CurrBounds.top-m_dotGap)/(m_dotSize+m_dotGap)) * (m_dotSize+m_dotGap);
	x2=((CurrBounds.right-CurrBounds.left-m_dotGap)/(m_dotSize+m_dotGap)) * (m_dotSize+m_dotGap);
	

	y3=point.y;
	x3=point.x;
	//if any mouse co-ord is outside of valid area, then set it to maximum and reset bf
	if(point.x>=x2){
		x3=x2;
		bf=FALSE;
	}
	if(point.y>=y2){
		y3=y2;
		bf=FALSE;
	}

	// calculate which dot this screen point belonds to. Note that this time it
	//will include the dot's left and top gap
	x=x3/(m_dotSize+m_dotGap);
	y=y3/(m_dotSize+m_dotGap);
	// if gap=FALSE then test if the mouse was pressed over the dot (and not gap)
	x2=x*(m_dotSize+m_dotGap)+m_dotGap;
	y2=y*(m_dotSize+m_dotGap)+m_dotGap;
	x3=(x+1)*(m_dotSize+m_dotGap);
	y3=(y+1)*(m_dotSize+m_dotGap);

	if(!gap && (point.x<x2 || point.x>=x3 || point.y<y2 || point.y>=y3))
		bf=FALSE;
		
	//check if out of grid range
	if(x+m_fromX>=m_sizeX){
		x=m_sizeX-1; //sizeX cannot be <1
		bf=FALSE;
	}
	
	if(y+m_fromY>=m_sizeY){
		y=m_sizeY-1;
		bf=FALSE; //size Y cannot be <1
	}
	
	if(x+m_fromX<0){ //mouse coordinates can become negative
		x=0;
		bf=FALSE;
	}
	
	if(y+m_fromY<0){ //mouse coordinates can become negative
		y=0;
		bf=FALSE;
	}
	
	*px=x+m_fromX;
	*py=y+m_fromY;
	
	return bf;
}



long CBWECtrl::GetMouseX() 
{
	// TODO: Add your property handler here

	return m_mouseX;
}

void CBWECtrl::SetMouseX(long nNewValue) 
{
	// TODO: Add your property handler here

	SetModifiedFlag();
}

void CBWECtrl::OnTimer(UINT nIDEvent) 
{
	short nx,ny;
	
	//calculate how many dots can be displayed
	ny=(CurrBounds.bottom-CurrBounds.top-m_dotGap)/(m_dotSize+m_dotGap);
	nx=(CurrBounds.right-CurrBounds.left-m_dotGap)/(m_dotSize+m_dotGap);	
	
	if(m_panInProg){
		if(m_panLeft && m_fromX>0)
			m_fromX--;

		if(m_panUp && m_fromY>0)
			m_fromY--;

		if(m_panRight && m_fromX+nx<m_sizeX)
			m_fromX++;

		if(m_panDown && m_fromY+ny<m_sizeY)
			m_fromY++;

		InvalidateControl();
	}
	
	COleControl::OnTimer(nIDEvent);
}

void CBWECtrl::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) 
{
	COleControl::OnActivate(nState, pWndOther, bMinimized);
	
	// TODO: Add your message handler code here
	
}

BOOL CBWECtrl::GetMode() 
{
	return m_mode;
}

void CBWECtrl::SetMode(BOOL bNewValue) 
{
	m_mode=bNewValue;
	if(m_mode){
		//drawing mode now
		//if dragging was in progress- paste the data
		if(m_selState==SEL_DRAGGING)
			PasteOvl();
		m_selSet=FALSE;
		m_selState=SEL_IDLE;
		//save undo data
		SaveUndo();
	}
	InvalidateControl();
	SetModifiedFlag();
}

void CBWECtrl::CopyOvl(bool cut, bool blonly)
{
	short fx,fy;
	short x1,x2,y1,y2,ofs;
	//copies the area under selection rectangle into the overlay, updates the screen
	//this call corrects selection borders if they fall outside of exisiting graphic area
	//if cut=0- just copy the data, =1- also erase (cut) the "copy from" area
	//if blonly=0- both white and black dots are copied, =1- black dots are copied as is, while
	//white dots are copied as "transparent" 
	if(m_selSet){
		//make sure that selection border are within limits
		if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2+1;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1+1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2+1;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1+1;
		}

		if(x1<0)
			x1=0;
		if(y1<0)
			y1=0;
		if(x1>=m_sizeX)
			x1=m_sizeX-1;
		if(y1>=m_sizeY)
			y1=m_sizeY-1;
		
		
		//calc offset: beginning of selection area with respect to 0,0 of main
		ofs=y1*MAX_WIDTH+x1;
		//copy the area
		for(fy=0;fy<y2-y1;fy++){
			for(fx=0;fx<x2-x1;fx++){
				if(m_dataArr[fy*MAX_WIDTH+fx+ofs]!=0)
					m_ovlArr[fy*MAX_WIDTH+fx]=1;
				else{
					if(blonly)
						m_ovlArr[fy*MAX_WIDTH+fx]=2;
					else
						m_ovlArr[fy*MAX_WIDTH+fx]=0;
				}
			}
		}
		//need to erase the area?
		if(cut){
			for(fy=0;fy<y2-y1;fy++){
				for(fx=0;fx<x2-x1;fx++){
					m_dataArr[fy*MAX_WIDTH+fx+ofs]=0;
				}
			}
		}
	}
	InvalidateControl();
}

void CBWECtrl::PasteOvl()
{
	short x1,y1,x2,y2,fx,fy,ofs;
	
	//pastes the overlay data (the size and position of selection rectangle
	
	if(m_selSet){
		//make sure that selection border are within limits
		if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2+1;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1+1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2+1;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1+1;
		}

		if(x1<0)
			x1=0;
		if(y1<0)
			y1=0;
		if(x1>=m_sizeX)
			x1=m_sizeX-1;
		if(y1>=m_sizeY)
			y1=m_sizeY-1;
	
		//calculate offset
		ofs=y1*MAX_WIDTH+x1;
		//copy the data
		for(fy=0;fy<y2-y1;fy++){
			for(fx=0;fx<x2-x1;fx++){
				//ignore transparent dots
				if(m_ovlArr[fy*MAX_WIDTH+fx]==0)
					m_dataArr[ofs+fy*MAX_WIDTH+fx]=0;
				if(m_ovlArr[fy*MAX_WIDTH+fx]==1)
					m_dataArr[ofs+fy*MAX_WIDTH+fx]=1;
			}
		}
		InvalidateControl();
	}

}

void CBWECtrl::PaintSelection(short PaintMode) 
{
	//Paints selected area basing on the mode specified: 0- all white, 1-all black, 
	//other- invert 
	short x1,y1,x2,y2,fx,fy,ofs;
	
	if(m_selSet){
		//make sure that selection border are within limits
		if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2+1;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1+1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2+1;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1+1;
		}

		if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
			//work on the main level

			ofs=y1*MAX_WIDTH+x1;
			for(fy=0;fy<y2-y1;fy++){
				for(fx=0;fx<x2-x1;fx++){
					if(PaintMode==0)
						m_dataArr[ofs+fy*MAX_WIDTH+fx]=0;
					else if(PaintMode==1)
						m_dataArr[ofs+fy*MAX_WIDTH+fx]=1;
					else{
						if(m_dataArr[ofs+fy*MAX_WIDTH+fx]==0)
							m_dataArr[ofs+fy*MAX_WIDTH+fx]=1;
						else
						m_dataArr[ofs+fy*MAX_WIDTH+fx]=0;
					}
				}
			}
		}
		else{
			//work on the overlay level

			for(fy=0;fy<y2-y1;fy++){
				for(fx=0;fx<x2-x1;fx++){
					if(PaintMode==0)
						m_ovlArr[fy*MAX_WIDTH+fx]=0;
					else if(PaintMode==1)
						m_ovlArr[fy*MAX_WIDTH+fx]=1;
					else{
						if(m_ovlArr[fy*MAX_WIDTH+fx]==0)
							m_ovlArr[fy*MAX_WIDTH+fx]=1;
						else if(m_ovlArr[fy*MAX_WIDTH+fx]==1)
							m_ovlArr[fy*MAX_WIDTH+fx]=0;
						else{} //ignore transparent color
					}
				}
			}
		}

		InvalidateControl();
	}
}

void CBWECtrl::Escape() 
{
	
	CClientDC dc( this );
	//Abort whichever was in progress: selection or painting. If was in drag mode- paste
	m_mode=FALSE;	//returned to selection mode
	if(m_selState!=SEL_IDLE){
		m_selState=SEL_IDLE;
		PasteOvl();
	}
	m_selSet=FALSE;
	InvalidateControl();
	DisplCursor(B_NIL);
}

short CBWECtrl::BorderTest(CPoint point)
{
	//Tests position of cursor with relation to selection borders. Returns values for
	//inside (B_I) , border (B_T/L/R/B/TL/TR/BL/BR) and outside (NIL)

	short x1,y1,x2,y2, bl,br,bt,bb;

	if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1;
		}	

	//make sure that the mouse pointer is within client area
	if(point.x<0 || point.y<0 || point.x>(CurrBounds.right-CurrBounds.left) || point.y>(CurrBounds.bottom-CurrBounds.top))
		return B_NIL;
		
	//test if the pointer is outside of selection rectangle
	//calculate left,right,top and bottom outer borders 
	bl=(x1-m_fromX)*(m_dotSize+m_dotGap);
	bt=(y1-m_fromY)*(m_dotSize+m_dotGap);
	br=(x2-m_fromX+1)*(m_dotSize+m_dotGap);
	bb=(y2-m_fromY+1)*(m_dotSize+m_dotGap);
	if(point.x<bl || point.x>=br || point.y<bt || point.y>=bb)
		return B_NIL;

	//test if the pointer is inside if selection rectangle
	bl=bl+m_dotGap+m_dotSize/2;
	br=br-m_dotGap-m_dotSize/2;
	bt=bt+m_dotGap+m_dotSize/2;
	bb=bb-m_dotGap-m_dotSize/2;
	if(point.x>=bl && point.x<br && point.y>=bt && point.y<bb)
		return B_I;
	
	//the pointer is definitely on borders
	if(point.x<bl && !(point.y<bt) && !(point.y>=bb))
		return B_L;

	if(point.x>=br && !(point.y<bt) && !(point.y>=bb))
		return B_R;


	if(point.y<bt){
		if(point.x<bl)
			return B_TL;
		else if(point.x>=br)
			return B_TR;
		else
			return B_T;
	}

	if(point.y>=bb){
		if(point.x<bl)
			return B_BL;
		else if(point.x>=br)
			return B_BR;
		else
			return B_B;
	}
	return B_NIL;
}

void CBWECtrl::DisplCursor(short b)
{
	if(!m_mode){	
		
		if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
			//display cursor, including resize choices
			switch(b){
			case B_I:
				SetCursor(LoadCursor(NULL,IDC_SIZEALL));
				break;
			case B_L:
			case B_R:
				SetCursor(LoadCursor(NULL,IDC_SIZEWE));
				break;
			case B_T:
			case B_B:
				SetCursor(LoadCursor(NULL,IDC_SIZENS));
				break;
			case B_TL:
			case B_BR:
				SetCursor(LoadCursor(NULL,IDC_SIZENWSE));
				break;
			case B_TR:
			case B_BL:
				SetCursor(LoadCursor(NULL,IDC_SIZENESW));
				break;
			default:
				SetCursor(LoadCursor(NULL,IDC_ARROW));
				break;
			}
		}
		else if(m_selState==SEL_DRAGGING){
			//display cursor, but this time substitue all resize shoices for drag
		switch(b){
			case B_I:
			case B_L:
			case B_R:
			case B_T:
			case B_B:
			case B_TL:
			case B_BR:	
			case B_TR:
			case B_BL:	
				SetCursor(LoadCursor(NULL,IDC_SIZEALL));
				break;
			default:
				SetCursor(LoadCursor(NULL,IDC_ARROW));
				break;
			}
		}
		else{
			//display standard cursor
			SetCursor(LoadCursor(NULL,IDC_ARROW));
		}
	}
	else
		SetCursor(LoadCursor(NULL,IDC_CROSS));
}

BOOL CBWECtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	// prevent OLE Control's respont code from executing
	return TRUE; //def. impl: return COleControl::OnSetCursor(pWnd, nHitTest, message);
}



BOOL CBWECtrl::Copy() 
{
	short x1,y1,x2,y2;
	short fx,fy,fb,my;
	unsigned char d;
	COleDataSource* sourcePtr= new COleDataSource;
	 
	
	//Check if there is any selection
	if(m_selSet){

		//copy selection onto the clipboard
		if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2+1;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1+1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2+1;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1+1;
		}	
	
		//make sure that selection is within limits
		if(x2>m_sizeX)
			x2=m_sizeX;
		if(y2>m_sizeY)
			y2=m_sizeY;
		if(x1<0)
			x1=0;
		if(y1<0)
			y1=0;
		
		//check selection state: if not dragging yet- copy the dato to the ovl layer
		if(m_selState!=SEL_DRAGGING){
			CopyOvl(FALSE,FALSE);
			m_selState=SEL_DRAGGING;
		}
		
		//register private clipboard format if necessary
		if(m_cfPrivate==NULL){
			m_cfPrivate=(CLIPFORMAT)::RegisterClipboardFormat(_T("BWEData"));
		}
	
		//create archive for temporary data storage
		CSharedFile file;
		CArchive ar(&file,CArchive::store);
		
		//save array size into the archive;
		fx=x2-x1;
		fy=y2-y1;
		ar<<fx;
		ar<<fy;
		
		//prepare to copy data array, calculate how many 8-bit-high rows we have
		my=(y2-y1)/8;
		if((y2-y1)-my*8>0)
			my++;	//bottom byte-wide row can be not fully used		

		//copy the data into the archive
		for(fy=0;fy<my;fy++){
			for(fx=0;fx<x2-x1;fx++){
				d=0;
				for(fb=0;fb<8;fb++){
					//no transparent color for clipboard operations (transparent becomes white)
					if(m_ovlArr[(fy*8+fb)*MAX_WIDTH+fx]==1)
						d=d+(1<<fb);
				}
				ar<<d;
			}
		}

		//copy the data
		ar.Close();
		sourcePtr->CacheGlobalData(m_cfPrivate,file.Detach());
		sourcePtr->SetClipboard();
		InvalidateControl();
		
	}
	else{
		return FALSE;
	}

	return TRUE;
}



BOOL CBWECtrl::Paste() 
{
	short f,fx,fy,fb,sx,sy,my;
	unsigned char d;
	COleDataObject dDest;
	unsigned char temp[MAX_WIDTH*MAX_HEIGHT]; //the data is first retrieved into it

	//register private clipboard format if necessary
	if(m_cfPrivate==NULL){
		m_cfPrivate=(CLIPFORMAT)::RegisterClipboardFormat(_T("BWEData"));
	}
	
	//check if there is some clipboard data available
	dDest.AttachClipboard();
	if(dDest.IsDataAvailable(m_cfPrivate,NULL)!=0){
		//get that data
		CFile *pfile = dDest.GetFileData(m_cfPrivate,NULL);
		if (pfile == NULL)
			return FALSE;
		CArchive ar(pfile, CArchive::load);
		
		//get array size
		ar>>sx;
		ar>>sy;
		
		//both values must be >0 and <MAX_WIDTH/HEIGHT. We deal with the actual image size later
		if(sx<=0 || sy<=0 || sx>MAX_WIDTH || sy>MAX_HEIGHT){
			ar.Close();
			delete pfile;
			return FALSE;
		}
		
		//calculate how many bytes to process
		my=sy/8;
		if(sy-my*8>0)
			my++;
		
		//size is in range, extract the data (no "transparent color" for )
		for(fy=0;fy<my;fy++){
			for(fx=0;fx<sx;fx++){
				ar>>d;
				for(fb=0;fb<8;fb++){
					if((d&(1<<fb))!=0)
						temp[(fy*8+fb)*MAX_WIDTH+fx]=1;
					else
						temp[(fy*8+fb)*MAX_WIDTH+fx]=0;
				}
			}
		}
		
		//all data extracted in order, copy into overlay
		for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++)
			m_ovlArr[f]=temp[f];
		//Paste selection always starts from 0,0
		m_selPosX1=0;
		m_selPosY1=0;
		//limit selection to the current image size
		if(sx>m_sizeX)
			m_selPosX2=m_sizeX-1;
		else
			m_selPosX2=sx-1;
		if(sy>m_sizeY)
			m_selPosY2=m_sizeY-1;
		else
			m_selPosY2=sy-1;

		//enable selection
		m_selSet=TRUE;
		m_selState=SEL_DRAGGING;
		InvalidateControl();

		ar.Close();
		delete pfile;
		return TRUE;
	}
	
	return FALSE;
}

void CBWECtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	short ny,step;
	//Calculate how many dots can be displayed
	ny=(CurrBounds.bottom-CurrBounds.top-m_dotGap)/(m_dotSize+m_dotGap);
	//step is 1/4 of visible number of dots or at least 1
	step=ny/4;
	if(step==0)
		step=1;

	switch(nSBCode){
	case SB_LINEUP:
		if(m_fromY-step<0)
			m_fromY=0;
		else
			m_fromY=m_fromY-step;
		InvalidateControl();
		break;

	case SB_LINEDOWN:
		if(m_fromY+step>=m_sizeY-ny)
			m_fromY=m_sizeY-ny+1;
		else
			m_fromY=m_fromY+step;
		InvalidateControl();
		break;

	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		m_fromY=nPos;
		InvalidateControl();
		break;

	default:
		break;
	}
	
	COleControl::OnVScroll(nSBCode, nPos, pScrollBar);
}



void CBWECtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	short nx,step;
	//Calculate how many dots can be displayed
	nx=(CurrBounds.right-CurrBounds.left-m_dotGap)/(m_dotSize+m_dotGap);
	//step is 1/4 of visible number of dots or at least 1
	step=nx/4;
	if(step==0)
		step=1;

	switch(nSBCode){
	case SB_LINEUP:
		if(m_fromX-step<0)
			m_fromX=0;
		else
			m_fromX=m_fromX-step;
		InvalidateControl();
		break;

	case SB_LINEDOWN:
		if(m_fromX+step>=m_sizeX-nx)
			m_fromX=m_sizeX-nx+1;
		else
			m_fromX=m_fromX+step;
		InvalidateControl();
		break;

	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		m_fromX=nPos;
		InvalidateControl();
		break;

	default:
		break;
	}
	
	COleControl::OnHScroll(nSBCode, nPos, pScrollBar);
}





BSTR CBWECtrl::GetData() 
{
	short s,fy,fx,fb;
	unsigned char d,yb;
	CString str="";
	
	//calculate required string size (the data is byte-packed)
	s=m_sizeY/8;
	if(m_sizeY-s*8>0)
		s++;

	//pack the size
	yb=m_sizeY/8;
	if(m_sizeY-yb*8>0)
		yb++;
	
	str+=byteToStr((unsigned char)m_sizeX);
	str+=',';
	str+=byteToStr((unsigned char)yb);

	//pack the data
	for(fy=0;fy<s;fy++){
		for(fx=0;fx<m_sizeX;fx++){
			d=0;
			for(fb=0;fb<8;fb++){
				if(m_dataArr[(fy*8+fb)*MAX_WIDTH+fx]==1)
					d=d+(1<<fb);
			}
			str+=',';
			str+=byteToStr(d);
		}
	}
	
	return str.AllocSysString();
}


BOOL CBWECtrl::SetData(LPCTSTR str) 
{
	unsigned short s,p;
	unsigned char sx,sy,fy,fx,fb,d; 
	
	//string creation might throw an exception
	try{
		
		CString* strptr=new CString(str);
		//CComBSTR* strptr =new CComBSTR(str);
		//extract picture sizes
		p=0;
		sx=strToByte(strptr,&p);
		sy=strToByte(strptr,&p)*8;
		
		//check the image size for correctness: must be >0 and <MAX
		if(sx>MAX_WIDTH || sy>MAX_HEIGHT || sx<1 || sy<1)
			return FALSE;

		//Undo data
		SaveUndo();
		
		//set new image size, reset selection
		m_sizeX=sx;
		m_sizeY=sy;
		m_selSet=FALSE;
		m_selState=SEL_IDLE;

		//we are dealing with packed data
		s=sy/8;
		if(sy-s*8>0)
			s++;
		
		//place the data into the data array
		for(fy=0;fy<s;fy++){
			for(fx=0;fx<sx;fx++){
				d=strToByte(strptr,&p);
				for(fb=0;fb<8;fb++){
					if((d&(1<<fb))!=0)
						m_dataArr[(fy*8+fb)*MAX_WIDTH+fx]=1;
					else
						m_dataArr[(fy*8+fb)*MAX_WIDTH+fx]=0;
				}
			}
		}
		InvalidateControl();
		delete strptr;
	}
	catch(CException* e){
		e->Delete();
		return FALSE;
	}
	
	return TRUE;
}


BSTR CBWECtrl::byteToStr(unsigned char d)
{
	//returns a string representation of a byte (in decimal form)
	CString str="";
	unsigned char x;
	
	x=d/100;
	d=d-x*100;
	str+='0'+x;
	x=d/10;
	d=d-x*10;
	str+='0'+x;
	str+='0'+d;
	return str.AllocSysString();
}

unsigned char CBWECtrl::strToByte(CString* sptr, unsigned short *pos)
{
	//check string length
	unsigned short l=sptr->GetLength();
	unsigned char ca[3],c;
	
	ca[0]=ca[1]=ca[2]=0;
	while(*pos<l && (c=sptr->GetAt((*pos)++))!=','){
		if(c>='0' && c<='9'){
			ca[0]=ca[1];
			ca[1]=ca[2];
			ca[2]=c-'0';
		}
	}
	return ca[0]*100+ca[1]*10+ca[2];
	
	
	
}

BSTR CBWECtrl::GetOvl() 
{
	short x1,y1,x2,y2,sx,sy;
	short s,fy,fx,fb;
	unsigned char d;
	CString str="";
	
	//check if there is a selection
	if(!m_selSet)
		return str.AllocSysString();
	
	//selection- into dragging mode, if wasn't in dragging mode- copy data into ovl
	if(m_selState!=SEL_DRAGGING){
		CopyOvl(FALSE,FALSE);
		m_selState=SEL_DRAGGING;
		InvalidateControl();
	}

	//normalize selection range
	if(m_selPosX2>=m_selPosX1){
		x1=m_selPosX1;
		x2=m_selPosX2+1;
	}
	else{
		x1=m_selPosX2;
		x2=m_selPosX1+1;
	}
	
	if(m_selPosY2>=m_selPosY1){
		y1=m_selPosY1;
		y2=m_selPosY2+1;
	}
	else{
		y1=m_selPosY2;
		y2=m_selPosY1+1;
	}
	
	//X- and Y-dimensions
	sx=x2-x1;
	sy=y2-y1;
	
	//calculate required string size (the data is byte-packed)
	s=sy/8;
	if(sy-s*8>0)
		s++;

	//pack the size
	str+=byteToStr((unsigned char)sx);
	str+=',';
	str+=byteToStr((unsigned char)sy);

	//pack the data
	for(fy=0;fy<s;fy++){
		for(fx=0;fx<sx;fx++){
			d=0;
			for(fb=0;fb<8;fb++){
				if(m_ovlArr[(fy*8+fb)*MAX_WIDTH+fx]==1)
					d=d+(1<<fb);
			}
			str+=',';
			str+=byteToStr(d);
		}
	}
	
	return str.AllocSysString();	
}


BOOL CBWECtrl::SetOvl(LPCTSTR str) 
{
	unsigned short s,p;
	unsigned char sx,sy,fy,fx,fb,d; 
	
	//string creation might throw an exception
	try{
		
		CString* strptr=new CString(str);
		//CComBSTR* strptr =new CComBSTR(str);
		//extract picture sizes
		p=0;
		sx=strToByte(strptr,&p);
		sy=strToByte(strptr,&p);
		
		//check selection size for correctness: must be >0 and <MAX
		if(sx>MAX_WIDTH || sy>MAX_HEIGHT || sx<1 || sy<1)
			return FALSE;

		//set new selection range, switch into drag mode 
		m_selPosX1=0;
		m_selPosY1=0;
		m_selPosX2=sx-1;
		m_selPosY2=sy-1;
		m_selSet=TRUE;
		m_selState=SEL_DRAGGING;
		InvalidateControl();

		//we are dealing with packed data
		s=sy/8;
		if(sy-s*8>0)
			s++;
		
		//place the data into the data array
		for(fy=0;fy<s;fy++){
			for(fx=0;fx<sx;fx++){
				d=strToByte(strptr,&p);
				for(fb=0;fb<8;fb++){
					if((d&(1<<fb))!=0)
						m_ovlArr[(fy*8+fb)*MAX_WIDTH+fx]=1;
					else
						m_ovlArr[(fy*8+fb)*MAX_WIDTH+fx]=0;
				}
			}
		}
		delete strptr;
	}
	catch(CException* e){
		e->Delete();
		return FALSE;
	}

	return TRUE;
}

void CBWECtrl::SetText(LPCTSTR str) 
{
#define MAX_LINES 16 //maximum number of lines in the input text
	
	//this code might throw an exception becuase of several NEW statesments

	try{
		unsigned short f;
		long fx,fy,w;
		TCHAR ch;
		
		CString* strptr=new CString(_T(str));	
		CClientDC dc( this );
		CClientDC *pdc=&dc;
		CFont *pOldFont;
		
		CDC MemDC; //memory context is used for text rendering
		CSize sz; //extimated size of text line
		CRect rect; //text is output within this rectangle
		unsigned char nln; //number of lines in the input text
		struct lines{
			unsigned char from; //beginning of string in the buffer
			unsigned char len; //size of string in the buffer
			CSize sz; //rendered string width/height
		} ln[MAX_LINES];
	
		//Initialize memory context- text rendering area
		MemDC.CreateCompatibleDC(pdc);
		CBitmap *bptr=new CBitmap();
		bptr->CreateBitmap(CurrBounds.right-CurrBounds.left,CurrBounds.bottom-CurrBounds.top,1,1,NULL);
		MemDC.SelectObject(bptr);
		MemDC.FillSolidRect(CurrBounds,0x00FFFFFF);
	
		pOldFont=SelectStockFont(&MemDC);
	
		//text size is estimated using a special windows call, which, unfortunately, works
		//with single lines of text only. Therefore, input text must be subdivided into lines
		f=0;
		nln=0;
		ln[0].from=0;
		for(f=0;f<strptr->GetLength()/sizeof(TCHAR) && nln<MAX_LINES-1;f++){
			ch=strptr->GetAt(f);
			if(ch==0x0D){
				ln[nln].len=f-ln[nln].from;
				nln++;
				ln[nln].from=f+1;
			}
		}
		ln[nln].len=f-ln[nln].from;	
	
		//calculate sizes separately for every string
		for(f=0;f<=nln;f++){
			ln[f].sz=MemDC.GetTextExtent(strptr->Mid(ln[f].from,ln[f].len));
		}

		rect.left=0;
		rect.right=0;
		rect.top=0;
		rect.bottom=0;
		//total output area width is the width of a longest string
		for(f=0;f<=nln;f++){
			if(ln[f].sz.cx>rect.right)
				rect.right=ln[f].sz.cx;
		}
		//total output height is the sum of the height of all lines
		for(f=0;f<=nln;f++)
			rect.bottom=rect.bottom+ln[f].sz.cy;

		//render text now
		if(m_textAlignment==0)
			MemDC.DrawText(*strptr,rect,DT_LEFT);
		else if(m_textAlignment==1)
			MemDC.DrawText(*strptr,rect,DT_RIGHT);
		else
			MemDC.DrawText(*strptr,rect,DT_CENTER);

		//copy text into the temporary array (because will perform "tight bounding")
		w=rect.right+1; //initial text image width
		CByteArray *aptr=new CByteArray;
		aptr->SetSize((rect.right+1)*(rect.bottom+1),-1);
		for(fy=0;fy<=rect.bottom;fy++){
			for(fx=0;fx<=rect.right;fx++){
				if(MemDC.GetPixel(fx,fy)==0)
					(*aptr)[fy*w+fx]=1;
				else
					(*aptr)[fy*w+fx]=0;
			}
		}

	
		//text must be clipped to fit the image size
		if(rect.right-rect.left>=m_sizeX)
			rect.right=rect.left+m_sizeX;
		if(rect.bottom-rect.top>=m_sizeY)
			rect.bottom=rect.top+m_sizeY; 
	
		//eliminate all white gaps from the image
		
		//left side
		for(fx=0;fx<=rect.right;fx++){
			for(fy=0;fy<=rect.bottom;fy++){
				if((*aptr)[fy*w+fx]==1)
					goto g1;
			}
		}
g1:		rect.left=fx;

		//right side
		for(fx=rect.right;fx>=0;fx=fx-1){
			for(fy=0;fy<=rect.bottom;fy++){
				if((*aptr)[fy*w+fx]==1)
					goto g2;
			}
		}
g2:		rect.right=fx;

		//top side
		for(fy=0;fy<=rect.bottom;fy++){
			for(fx=0;fx<=rect.right;fx++){
				if((*aptr)[fy*w+fx]==1)
					goto g3;
			}
		}
g3:		rect.top=fy;

		//bottom side
		for(fy=rect.bottom;fy>=0;fy=fy-1){
			for(fx=0;fx<=rect.right;fx++){
				if((*aptr)[fy*w+fx]==1)
					goto g4;
			}
		}
g4:		rect.bottom=fy;

		//copy the data into the overlay array
		for(fy=rect.top;fy<=rect.bottom;fy++){
			for(fx=rect.left;fx<=rect.right;fx++)
				m_ovlArr[(fy-rect.top)*MAX_WIDTH+(fx-rect.left)]=(*aptr)[fy*w+fx];
		}

		//set selection
		m_selPosX1=0;
		m_selPosY1=0;
		m_selPosX2=(short)(rect.right-rect.left);
		m_selPosY2=(short)(rect.bottom-rect.top);
		
		//enable selection only if it is "non-empty"
		if(m_selPosX2<0 && m_selPosY2<0){
			m_selSet=FALSE;
			m_selState=SEL_DRAGGING;
		}
		else{
			m_selSet=TRUE;
			m_selState=SEL_DRAGGING;
		}
		
		InvalidateControl();

		delete bptr;
		delete strptr;
		delete aptr;
		dc.SelectObject(pOldFont);
	}
	catch(CException* e){
		e->Delete();
	}	
}



short CBWECtrl::GetTextAlignment() 
{
	return m_textAlignment;
}

void CBWECtrl::SetTextAlignment(short nNewValue) 
{
	if(nNewValue<0)
		m_textAlignment=0;
	else if(nNewValue>2)
		m_textAlignment=2;
	else
		m_textAlignment=nNewValue;

	SetModifiedFlag();
}

void CBWECtrl::MirrorSelection(BOOL hor) 
{
	short x1,y1,x2,y2,fx,fy;
	unsigned char d;
	
	if(hor){}
	if(m_selSet){
		//make sure that selection border are within limits
		if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1;
		}	
	
		//check if the data has been copied into the overlay already
		if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
			//still working on the main level
	
			if(hor){
				//mirror horizontally
				for(fx=x1;fx<=(x1+x2)/2;fx++){
					for(fy=y1;fy<=y2;fy++){
						d=m_dataArr[fy*MAX_WIDTH+fx];
						m_dataArr[fy*MAX_WIDTH+fx]=m_dataArr[fy*MAX_WIDTH+(x2+x1-fx)];
						m_dataArr[fy*MAX_WIDTH+(x2+x1-fx)]=d;
					}
				}
			}
			else{
				//mirror vertically
				for(fy=y1;fy<=(y1+y2)/2;fy++){
					for(fx=x1;fx<=x2;fx++){
						d=m_dataArr[fy*MAX_WIDTH+fx];
						m_dataArr[fy*MAX_WIDTH+fx]=m_dataArr[(y2+y1-fy)*MAX_WIDTH+fx];
						m_dataArr[(y2+y1-fy)*MAX_WIDTH+fx]=d;
					}
				}
			}
		}

		else{
			//working on the overlay

			if(hor){
				//mirror horizontally
				for(fx=0;fx<=(x2-x1)/2;fx++){
					for(fy=0;fy<=y2-y1;fy++){
						d=m_ovlArr[fy*MAX_WIDTH+fx];
						m_ovlArr[fy*MAX_WIDTH+fx]=m_ovlArr[fy*MAX_WIDTH+(x2-x1-fx)];
						m_ovlArr[fy*MAX_WIDTH+(x2-x1-fx)]=d;
					}
				}
			}
			else{
				//mirror vertically
				for(fy=0;fy<=(y2-y1)/2;fy++){
					for(fx=0;fx<=x2-x1;fx++){
						d=m_ovlArr[fy*MAX_WIDTH+fx];
						m_ovlArr[fy*MAX_WIDTH+fx]=m_ovlArr[(y2-y1-fy)*MAX_WIDTH+fx];
						m_ovlArr[(y2-y1-fy)*MAX_WIDTH+fx]=d;
					}
				}
			}
		}
		
	InvalidateControl();
	}
}

void CBWECtrl::ImportPicture(LPPICTUREDISP pic) 
{
	long fx,fy;
	CRect rect,rect2;
	rect.left=0;
	rect.top=0;
	rect.right=m_sizeX;
	rect.bottom=m_sizeY;
	rect2.left=0;
	rect2.top=0;
	CPoint p;

	CPictureHolder ph;
	ph.CreateEmpty();
	ph.SetPictureDispatch(pic);
	
	CClientDC dc( this ); //current screen context
	CClientDC *pdc=&dc; //pointer to current screen context

	//get picture size
	dc.SetMapMode(MM_HIMETRIC);
	ph.m_pPict->get_Width(&p.x);
	ph.m_pPict->get_Height(&p.y);
	dc.LPtoDP(&p);
	p.y=p.y*(-1);
	rect2.right=p.x;
	rect2.bottom=p.y;
	dc.SetMapMode(MM_TEXT);
	
	//code below might throw an exception (becuase of NEW statement)
	try{
		//create memory context of necessary size
		CDC MemDC;
		MemDC.CreateCompatibleDC(pdc); //memory context is compatible with screen
		CBitmap *bptr=new CBitmap(); //bitmap to associate with memory context
		bptr->CreateBitmap(rect2.right-rect2.left,rect2.bottom-rect2.top,1,1,NULL);
		MemDC.SelectObject(bptr); 
		MemDC.FillSolidRect(CurrBounds,0x00FFFFFF);

		//render image
		ph.Render(&MemDC,rect2,rect2);

		//clip image size is necessary
		if(rect2.right>m_sizeX)
			rect2.right=m_sizeX;
		if(rect2.bottom>m_sizeY)
			rect2.bottom=m_sizeY;

		//copy the data into the overlay layer
		for(fx=0;fx<rect2.right;fx++){
			for(fy=0;fy<rect2.bottom;fy++){
				if(MemDC.GetPixel(fx,fy)==0)
					m_ovlArr[fy*MAX_WIDTH+fx]=1;
				else
					m_ovlArr[fy*MAX_WIDTH+fx]=0;	
			}
		}
	
		m_selSet=TRUE;
		m_selState=SEL_DRAGGING;
		m_selPosX1=0;
		m_selPosY1=0;
		m_selPosX2=(short)rect2.right-1;
		m_selPosY2=(short)rect2.bottom-1;
		InvalidateControl();
		delete bptr;
	}
	catch(CException* e){
		e->Delete();
	}
}

void CBWECtrl::TightBound() 
{
	short x1,y1,x2,y2,fx,fy,oldX1,oldY1;
	//takes current selection and shrinks it to fit tightly around selected objects
	//"objects" means black dots within original selection
	if(m_selSet){
		if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1;
		}
		
		//check selection state
		if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
			//work with data on main level
			
			//left side
			for(fx=x1;fx<=x2;fx++){
				for(fy=y1;fy<=y2;fy++){
					if(m_dataArr[fy*MAX_WIDTH+fx]==1)
						goto g1;
				}
			}
g1:			m_selPosX1=fx;			
			
			//right side
			for(fx=x2;fx>=x1;fx=fx-1){
				for(fy=y1;fy<=y2;fy++){
					if(m_dataArr[fy*MAX_WIDTH+fx]==1)
						goto g2;
				}
			}
g2:			m_selPosX2=fx;

			//top side
			for(fy=y1;fy<=y2;fy++){
				for(fx=x1;fx<=x2;fx++){
					if(m_dataArr[fy*MAX_WIDTH+fx]==1)
						goto g3;
				}
			}
g3:			m_selPosY1=fy;

			//bottom side
			for(fy=y2;fy>=y1;fy=fy-1){
				for(fx=x1;fx<=x2;fx++){
					if(m_dataArr[fy*MAX_WIDTH+fx]==1)
						goto g4;
				}
			}
g4:			m_selPosY2=fy;

			//what if there were no objects at all
			if(m_selPosX1>m_selPosX2 || m_selPosY1>m_selPosY2){
				m_selSet=FALSE;
				m_selState=SEL_IDLE;
			}

		}
		else{
			//work with data on the overlay
			//will have to remember old X1,Y1
			oldX1=m_selPosX1;
			oldY1=m_selPosY1;

			//left side
			for(fx=0;fx<=x2-x1;fx++){
				for(fy=0;fy<=y2-y1;fy++){
					if(m_ovlArr[fy*MAX_WIDTH+fx]==1)
						goto gg1;
				}
			}
gg1:			m_selPosX1=x1+fx;			

			//right side
			for(fx=x2-x1;fx>=0;fx=fx-1){
				for(fy=0;fy<=y2-y1;fy++){
					if(m_ovlArr[fy*MAX_WIDTH+fx]==1)
						goto gg2;
				}
			}
gg2:		m_selPosX2=x1+fx;

			//top side
			for(fy=0;fy<=y2-y1;fy++){
				for(fx=0;fx<=x2-x1;fx++){
					if(m_ovlArr[fy*MAX_WIDTH+fx]==1)
						goto gg3;
				}
			}
gg3:		m_selPosY1=y1+fy;

			//bottom side
			for(fy=y2-y1;fy>=0;fy=fy-1){
				for(fx=0;fx<=x2-x1;fx++){
					if(m_ovlArr[fy*MAX_WIDTH+fx]==1)
						goto gg4;
				}
			}
gg4:		m_selPosY2=y1+fy;
				
			//is there any selection still?
			if(m_selPosX1>m_selPosX2 || m_selPosY1>m_selPosY2){
				//no selection
				m_selSet=FALSE;
				m_selState=SEL_IDLE;
			}
			else{
				//there is a selection
				//"shrinked" data must be copied at the top left corner of the overlay
				for(fy=0;fy<=m_selPosY2-m_selPosY1;fy++){
					for(fx=0;fx<=m_selPosX2-m_selPosX1;fx++)
						m_ovlArr[fy*MAX_WIDTH+fx]=m_ovlArr[(fy+m_selPosY1-oldY1)*MAX_WIDTH+(fx+m_selPosX1-oldX1)];
				}
			}
		}
	
		InvalidateControl();
	}
}

BOOL CBWECtrl::RotateSelection(short angle) 
{
	short x1,x2,y1,y2,fx,fy;
	short centerX,centerY,newX1,newX2,newY1,newY2;
	unsigned char d;

	//rotates selection: angle=0- 90, 1- 180, 2- 270 degress counterclockwise
	if(m_selSet){
		if(m_selPosX2>=m_selPosX1){
			x1=m_selPosX1;
			x2=m_selPosX2;
		}
		else{
			x1=m_selPosX2;
			x2=m_selPosX1;
		}
	
		if(m_selPosY2>=m_selPosY1){
			y1=m_selPosY1;
			y2=m_selPosY2;
		}
		else{
			y1=m_selPosY2;
			y2=m_selPosY1;
		}		
		
		if(angle==1){
			//rotate 180 degrees. Selection will fit in the same frame, so there are no concerns
			//of whether it will fit or not
			if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
				//work on the main level
				for(fx=x1;fx<=(x1+x2)/2;fx++){
					for(fy=y1;fy<=y2;fy++){
						d=m_dataArr[fy*MAX_WIDTH+fx];
						m_dataArr[fy*MAX_WIDTH+fx]=m_dataArr[(y2+y1-fy)*MAX_WIDTH+(x2+x1-fx)];
						m_dataArr[(y2+y1-fy)*MAX_WIDTH+(x2+x1-fx)]=d;
					}
				}
			}
			else{
				//work on the overlay
				for(fx=0;fx<=(x2-x1)/2;fx++){
					for(fy=0;fy<=y2-y1;fy++){
						d=m_ovlArr[fy*MAX_WIDTH+fx];
						m_ovlArr[fy*MAX_WIDTH+fx]=m_ovlArr[(y2-y1-fy)*MAX_WIDTH+(x2-x1-fx)];
						m_ovlArr[(y2-y1-fy)*MAX_WIDTH+(x2-x1-fx)]=d;
					}
				}
			}
		}
		else{
			//rotate 90/270 degrees. This rotation can lead to a new selection that won't fit into
			//the image. Check this
			if(m_selPosX2-m_selPosX1>=m_sizeY || m_selPosY2-m_selPosY1>=m_sizeX)
				return FALSE;
			//ideally, image should be rotated around its center, calculate new selection co-ords
			centerX=(m_selPosX2+m_selPosX1)/2;
			centerY=(m_selPosY2+m_selPosY1)/2;
			//"strange code" for centerY compensates for selection shifting in case repeated
			//rotations are perfromed. Without this code, selection will be sliding left and up
			//gradually (for selections with even number of dots in either direction)
			if(m_selPosY2+m_selPosY1>centerY*2)
				centerY++;
			
			newY1=centerY-(m_selPosX2-centerX);
			newY2=centerY+(centerX-m_selPosX1);
			newX1=centerX-(m_selPosY2-centerY);
			newX2=centerX+(centerY-m_selPosY1);
			//rotation around center can lead to selection falling outside of image boundaries
			//however, only one side can fall out becuase we have already proved that selection
			//fits in general
			if(newX1<0){
				newX2=newX2-newX1;
				newX1=0;
			}
			if(newY1<0){
				newY2=newY2-newY1;
				newY1=0;
			}
			if(newX2>=m_sizeX){
				newX1=newX1-(newX2-m_sizeX+1);
				newX2=m_sizeX-1;
			}
			if(newY2>=m_sizeY){
				newY1=newY1-(newY2-m_sizeY+1);
				newY2=m_sizeY-1;
			}

			//dynamic memory allocation might throw an exception
			try{
				// Create a temporary array for the data
				CByteArray *aptr=new CByteArray;
				aptr->SetSize((x2-x1+1)*(y2-y1+1),-1);
				
				//copy the data depending on the angle (90/270) and selection mode
				if(angle==0){
					//rotate 90
					if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
						//work on the main level
						for(fy=y1;fy<=y2;fy++){
							for(fx=x1;fx<=x2;fx++)
								(*aptr)[(x2-fx)*(y2-y1+1)+(fy-y1)]=m_dataArr[fy*MAX_WIDTH+fx];
						}
					}
					else{
						//work on the overalay level
						for(fy=0;fy<=y2-y1;fy++){
							for(fx=0;fx<=x2-x1;fx++)
								(*aptr)[(x2-x1-fx)*(y2-y1+1)+fy]=m_ovlArr[fy*MAX_WIDTH+fx];
						}
					}
				}
				else{
					//rotate 270
					if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
						//work on the main level
						for(fy=y1;fy<=y2;fy++){
							for(fx=x1;fx<=x2;fx++)
								(*aptr)[(fx-x1)*(y2-y1+1)+(y2-fy)]=m_dataArr[fy*MAX_WIDTH+fx];
						}
					}
					else{
						//work on the overalay level
						for(fy=0;fy<=y2-y1;fy++){
							for(fx=0;fx<=x2-x1;fx++)
								(*aptr)[fx*(y2-y1+1)+(y2-y1-fy)]=m_ovlArr[fy*MAX_WIDTH+fx];
						}
					}
				}

				//copy data from temp array back into main or overaly
				if(m_selState==SEL_RESIZING || m_selState==SEL_RESIZE_OR_DRAG){
					//clear the area of a previous selection
					for(fy=y1;fy<=y2;fy++){
						for(fx=x1;fx<=x2;fx++)
							m_dataArr[fy*MAX_WIDTH+fx]=0;
					}
					
					//copy into main level
					for(fy=newY1;fy<=newY2;fy++){
						for(fx=newX1;fx<=newX2;fx++)
							m_dataArr[fy*MAX_WIDTH+fx]=(*aptr)[(fy-newY1)*(y2-y1+1)+(fx-newX1)];
					}
				}
				else{
					//copy into overlay
					for(fy=0;fy<=newY2-newY1;fy++){
						for(fx=0;fx<=newX2-newX1;fx++)
							m_ovlArr[fy*MAX_WIDTH+fx]=(*aptr)[fy*(y2-y1+1)+fx];
					}
				}
				
				delete aptr;
			}
			catch(CException* e){
				e->Delete();
				return FALSE;
			}
			
			//new selection is now official!
			m_selPosX1=newX1;
			m_selPosX2=newX2;
			m_selPosY1=newY1;
			m_selPosY2=newY2;
		
		}
	InvalidateControl();
	}
	return TRUE;
}

void CBWECtrl::Undo() 
{
	short f;
	if(m_undoPtr<m_undoCtr-1){

		if(m_undoPtr==0)
			SaveUndo();
		
		m_undoPtr++;
		
		for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++){
			m_dataArr[f]=m_undo[m_undoPtr].m_dataArr[f];
			m_ovlArr[f]=m_undo[m_undoPtr].m_ovlArr[f];
		}
		m_sizeX=m_undo[m_undoPtr].m_sizeX;
		m_sizeY=m_undo[m_undoPtr].m_sizeY;
		m_selSet=m_undo[m_undoPtr].m_selSet;
		m_selState=m_undo[m_undoPtr].m_selState;
		m_selPosX1=m_undo[m_undoPtr].m_selPosX1;
		m_selPosX2=m_undo[m_undoPtr].m_selPosX2;
		m_selPosY1=m_undo[m_undoPtr].m_selPosY1;
		m_selPosY2=m_undo[m_undoPtr].m_selPosY2;
		
		InvalidateControl();
	}

}

void CBWECtrl::ResetUndo() 
{
	m_undoCtr=0;
	m_undoPtr=0;

}

void CBWECtrl::SaveUndo() 
{
	short f,f2;
	if(m_undoPtr==0){
		//there were no undo operations- just push the data deeper into the stack
		for(f2=MAX_UNDO_LEVELS-1;f2>0;f2=f2-1){
			for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++){
				m_undo[f2].m_dataArr[f]=m_undo[f2-1].m_dataArr[f];
				m_undo[f2].m_ovlArr[f]=m_undo[f2-1].m_ovlArr[f];
			}
			m_undo[f2].m_sizeX=m_undo[f2-1].m_sizeX;
			m_undo[f2].m_sizeY=m_undo[f2-1].m_sizeY;
			m_undo[f2].m_selSet=m_undo[f2-1].m_selSet;
			m_undo[f2].m_selState=m_undo[f2-1].m_selState;
			m_undo[f2].m_selPosX1=m_undo[f2-1].m_selPosX1;
			m_undo[f2].m_selPosX2=m_undo[f2-1].m_selPosX2;
			m_undo[f2].m_selPosY1=m_undo[f2-1].m_selPosY1;
			m_undo[f2].m_selPosY2=m_undo[f2-1].m_selPosY2;
		}
		//take care of counter and pointer
		if(m_undoCtr<MAX_UNDO_LEVELS)
			m_undoCtr++;
	}
	else if(m_undoPtr>1){
		// move stack data closer to the exit
		for(f2=2;f2<MAX_UNDO_LEVELS;f2++){
			for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++){
				m_undo[f2].m_dataArr[f]=m_undo[f2-1].m_dataArr[f];
				m_undo[f2].m_ovlArr[f]=m_undo[f2-1].m_ovlArr[f];
			}
			m_undo[f2].m_sizeX=m_undo[f2-1].m_sizeX;
			m_undo[f2].m_sizeY=m_undo[f2-1].m_sizeY;
			m_undo[f2].m_selSet=m_undo[f2-1].m_selSet;
			m_undo[f2].m_selState=m_undo[f2-1].m_selState;
			m_undo[f2].m_selPosX1=m_undo[f2-1].m_selPosX1;
			m_undo[f2].m_selPosX2=m_undo[f2-1].m_selPosX2;
			m_undo[f2].m_selPosY1=m_undo[f2-1].m_selPosY1;
			m_undo[f2].m_selPosY2=m_undo[f2-1].m_selPosY2;
		}
	//take care of pointer and counter
	m_undoCtr=m_undoCtr-m_undoPtr+1;
	m_undoPtr=0;
	}
	else{
		//take care of pointer and counter
		m_undoPtr=0;
	}
	
	//put latest data onto the stack
	for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++){
		m_undo[0].m_dataArr[f]=m_dataArr[f];
		m_undo[0].m_ovlArr[f]=m_ovlArr[f];
	}
	m_undo[0].m_sizeX=m_sizeX;
	m_undo[0].m_sizeY=m_sizeY;
	m_undo[0].m_selSet=m_selSet;
	m_undo[0].m_selState=m_selState;
	m_undo[0].m_selPosX1=m_selPosX1;
	m_undo[0].m_selPosX2=m_selPosX2;
	m_undo[0].m_selPosY1=m_selPosY1;
	m_undo[0].m_selPosY2=m_selPosY2;

}

void CBWECtrl::Redo() 
{
	short f;
	if(m_undoPtr>0){
		//take care of Pointer
		m_undoPtr--;

		//restore relevant data
		for(f=0;f<MAX_WIDTH*MAX_HEIGHT;f++){
			m_dataArr[f]=m_undo[m_undoPtr].m_dataArr[f];
			m_ovlArr[f]=m_undo[m_undoPtr].m_ovlArr[f];
		}
		m_sizeX=m_undo[m_undoPtr].m_sizeX;
		m_sizeY=m_undo[m_undoPtr].m_sizeY;
		m_selSet=m_undo[m_undoPtr].m_selSet;
		m_selState=m_undo[m_undoPtr].m_selState;
		m_selPosX1=m_undo[m_undoPtr].m_selPosX1;
		m_selPosX2=m_undo[m_undoPtr].m_selPosX2;
		m_selPosY1=m_undo[m_undoPtr].m_selPosY1;
		m_selPosY2=m_undo[m_undoPtr].m_selPosY2;
	
		InvalidateControl();
	}
}


void CBWECtrl::Repaint() 
{
	SetModifiedFlag();
	InvalidateControl();
}
