// SpinnerDesignDlg.cpp : implementation file
//

#include "stdafx.h"
#include "SpinnerDesign.h"
#include "SpinnerDesignDlg.h"
#include "ColourPicker.h"
#include 
#include "ObjectStorage.h"
#include "storageStructs.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define DLGHEIGHT 50
#define GRIDSIZE 10
#define PI 3.141592654

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSpinnerDesignDlg dialog

CSpinnerDesignDlg::CSpinnerDesignDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CSpinnerDesignDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CSpinnerDesignDlg)
	m_scale = 1.0;
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSpinnerDesignDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSpinnerDesignDlg)
	DDX_Control(pDX, IDC_COMBO2, m_combo2);
	DDX_Control(pDX, IDC_COMBO1, m_combo1);
	DDX_Text(pDX, IDC_EDIT1, m_scale);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSpinnerDesignDlg, CDialog)
	//{{AFX_MSG_MAP(CSpinnerDesignDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
	ON_BN_CLICKED(IDC_BUTTON3, OnButton3)
	ON_WM_SIZE()
	ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1)
	ON_WM_LBUTTONDOWN()
	ON_BN_CLICKED(IDC_CLEAR, OnClear)
	ON_BN_CLICKED(IDC_GENERATE, OnGenerate)
	ON_BN_CLICKED(IDC_RESCALE, OnRescale)
	ON_WM_DRAWITEM()
	ON_BN_CLICKED(IDC_TEST, OnTest)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSpinnerDesignDlg message handlers

BOOL CSpinnerDesignDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	m_combo1.ReadFromIniSection("Background");
	m_combo2.ReadFromIniSection("Foreground");
	m_combo1.SetCurSel(0);
	m_combo2.SetCurSel(0);
	m_rot2d=0;
	m_scale=1.;
	bTestViewMode = false;
	CheckDlgButton(IDC_RADIOPOLY,1);

	m_scale = atof(AfxGetApp()->GetProfileString("Settings","Scale","1"));
	UpdateData(false);

	goImage.LoadBitmap(IDB_BITMAP1);
	clrImage.LoadBitmap(IDB_BITMAP2);
	SendDlgItemMessage(IDC_RESCALE,BM_SETIMAGE,IMAGE_BITMAP,reinterpret_cast(goImage.GetSafeHandle()));
	SendDlgItemMessage(IDC_BUTTON1,BM_SETIMAGE,IMAGE_BITMAP,reinterpret_cast(clrImage.GetSafeHandle()));
	SendDlgItemMessage(IDC_BUTTON3,BM_SETIMAGE,IMAGE_BITMAP,reinterpret_cast(clrImage.GetSafeHandle()));
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CSpinnerDesignDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}


void CSpinnerDesignDlg::DrawGrid(CDC *pDC)
{
	CRect rcClient;
	GetClientRect(rcClient);

	// make the background of the drawable area white.
	CBrush cBr(RGB(255,255,255));
	pDC->SelectObject(&cBr);
	pDC->Rectangle(0,DLGHEIGHT,rcClient.right,rcClient.bottom);

	// Draw the big line thru the center markings
	rcClient.top=DLGHEIGHT;
	CPen cPen(PS_DASHDOT,1,RGB(100,100,100));
	pDC->SelectObject(&cPen);
	pDC->SetBkMode(TRANSPARENT);
	pDC->MoveTo(rcClient.right/2,rcClient.top);
	pDC->LineTo(rcClient.right/2,rcClient.bottom);
	pDC->MoveTo(rcClient.left,(rcClient.bottom/2)+(rcClient.top/2));
	pDC->LineTo(rcClient.right,(rcClient.bottom/2)+(rcClient.top/2));
}


void CSpinnerDesignDlg::DrawItem(CDC *pDC) 
{
	// intialize the drawing objects and regions.
	int offsetdeg = GetDlgItemInt(IDC_EDIT1);
	double offsetangle = PI * ((double)offsetdeg / 180.);
	CRect rcClient;
	GetClientRect(rcClient);
	rcClient.top=DLGHEIGHT;
	CPoint centre(rcClient.right/2,(rcClient.bottom/2)+(rcClient.top/2));
	CPen solidblack(PS_SOLID,0,RGB(0,0,0));
	CPen *old = pDC->SelectObject(&solidblack);
	CBrush solidbrush(RGB(255,255,255));
	CBrush *oldbrush = pDC->SelectObject(&solidbrush);
	CRgn rgn;
	rgn.CreateRectRgn(0,DLGHEIGHT,rcClient.right,rcClient.bottom);
	pDC->SelectClipRgn(&rgn);

	// Draw the grid, only clear the screen if were in Test mode.
	if(bTestViewMode)
		pDC->Rectangle(0,DLGHEIGHT,rcClient.right,rcClient.bottom);
	else
		DrawGrid(pDC);

	// draw all objects Ive stored in my drawing list.
	POSITION pos = mylist.GetHeadPosition();
	while(pos!=NULL)
	{
		CObjectStorage *pStore = (CObjectStorage*) mylist.GetNext(pos);
		pStore->Draw(pDC,m_scale,m_rot2d,centre);
	}

	// now put the grid points on last, this will draw the grid on top
	// of all objects, which is what we require. But dont draw them in test mode.
	if(!bTestViewMode)
	{
		for(int yPos=DLGHEIGHT;yPosSetPixel(xPos,yPos,RGB(100,100,100));
			}
		}
	}

	// put old objects back into dc
	pDC->SelectClipRgn(NULL);
	pDC->SelectObject(old);
	pDC->SelectObject(oldbrush);
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CSpinnerDesignDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CPaintDC dc(this); // device context for painting
		DrawItem(&dc);
		CRect rcClient; GetClientRect(rcClient);
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CSpinnerDesignDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}


void CSpinnerDesignDlg::DrawColourList(LPDRAWITEMSTRUCT lpDraw, CComboBox& wnd)
{
}


void CSpinnerDesignDlg::OnClose() 
{
	m_combo1.WriteToIniSection("Background");
	m_combo2.WriteToIniSection("Foreground");

	CString strDouble; strDouble.Format("%f",m_scale);
	AfxGetApp()->WriteProfileString("Settings","Scale",strDouble);

	ClearItems();
	CDialog::OnClose();
}

void CSpinnerDesignDlg::ClearItems()
{
	POSITION pos = mylist.GetHeadPosition();
	while(pos)
	{
		delete mylist.GetNext(pos);
	}

	mylist.RemoveAll();
}

void CSpinnerDesignDlg::OnButton1() 
{
	CColourPicker pick;
	pick.m_comboClr = &m_combo1;
	pick.DoModal();
}

void CSpinnerDesignDlg::OnButton3() 
{
	CColourPicker pick;
	pick.m_comboClr = &m_combo2;
	pick.DoModal();
}

void CSpinnerDesignDlg::OnSize(UINT nType, int cx, int cy) 
{
	CDialog::OnSize(nType, cx, cy);
	

	CRect rcInvalid; GetClientRect(rcInvalid);
	rcInvalid.top=DLGHEIGHT;
	InvalidateRect(rcInvalid);

	//reset the window size variables here?

}

void CSpinnerDesignDlg::OnUpdateEdit1() 
{
}

void CSpinnerDesignDlg::ConvertXYtoTheta(CPoint point,double &ang,double &len)
{
	// now correct them to the screen
	CRect rcClient;
	GetClientRect(rcClient);
	rcClient.top=DLGHEIGHT;
	CPoint centre(rcClient.right/2,(rcClient.bottom/2)+(rcClient.top/2));
	point-=centre;
	point.y = -point.y; // y coords are reversed in my model

	// (line length) square root of the squares
	double xsq = pow((0. - (double)point.x),2);
	double ysq = pow((0. - (double)point.y),2);
	len = sqrt(xsq + ysq);
	len/=m_scale;

	// find the angle of the line. (TO THETA)
	double xdt = 0. - (double)point.x;
	double ydt = 0. - (double)point.y;
	ang = atan(xdt/ydt);

	// we are on the other side of the circle.
	// the above only calculates the position in 180deg
	// we find out if its on "the other side" :)
	if(ydt>0)
	{
		ang += PI;
	}
	ang-=m_rot2d;
}

void CSpinnerDesignDlg::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// correct the coordinates. to grid
	point.x = point.x-(point.x % GRIDSIZE);
	point.y = point.y-(point.y % GRIDSIZE);

	// choose what coordinate type we are currently drawing.
	AddToDiagram(point);

	// do a forced redraw.
	InvalidateRect(NULL,1);
}

void CSpinnerDesignDlg::AddToDiagram(CPoint point)
{
	int count=0;
	double angle,length;
	ConvertXYtoTheta(point,angle,length);
	CObjectStorage *pPoly;
	if(IsDlgButtonChecked(IDC_RADIOPOLY))
		pPoly= new CPolyStorage();
	else
		pPoly= new CLineStorage();
	count=1;

	BOOL bStillDrawing = TRUE;
	CPoint newPoint(0,0);
	CPoint oldpoint;
	CPoint oldendPoint;
	oldpoint.x = -99999999;
	if (this != GetCapture()) SetCapture();
	ASSERT(this == GetCapture());
	while(bStillDrawing)
	{
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST));
		if (CWnd::GetCapture() != this)
		{
			break;
		}
		/// get point, and adjust from screen coords to window coords ///
		newPoint = msg.pt;
		ScreenToClient(&newPoint);
		switch(msg.message)
		{
			case WM_LBUTTONUP:
				if(count>=MAX_POLY) 
					bStillDrawing=FALSE;
				else
				{
					if((newPoint.x % GRIDSIZE)>(GRIDSIZE/2))
						newPoint.x = newPoint.x + (GRIDSIZE-(newPoint.x % GRIDSIZE));
					else
						newPoint.x = newPoint.x - (newPoint.x % GRIDSIZE);
					if((newPoint.y % GRIDSIZE)>(GRIDSIZE/2))
						newPoint.y = newPoint.y + (GRIDSIZE-(newPoint.y % GRIDSIZE));
					else
						newPoint.y = newPoint.y - (newPoint.y % GRIDSIZE);

					newPoint.y = newPoint.y - (newPoint.y % GRIDSIZE);
					ConvertXYtoTheta(newPoint,angle,length);
					pPoly->AddPoint(angle,length);
					DrawTracked(point,newPoint);
					count++;
					point = newPoint;
				}
				break;
			case WM_MOUSEMOVE:
					if((newPoint.x % GRIDSIZE)>(GRIDSIZE/2))
						newPoint.x = newPoint.x + (GRIDSIZE-(newPoint.x % GRIDSIZE));
					else
						newPoint.x = newPoint.x - (newPoint.x % GRIDSIZE);
					if((newPoint.y % GRIDSIZE)>(GRIDSIZE/2))
						newPoint.y = newPoint.y + (GRIDSIZE-(newPoint.y % GRIDSIZE));
					else
						newPoint.y = newPoint.y - (newPoint.y % GRIDSIZE);
				if(-99999999!=oldpoint.x) DrawTracked(oldpoint,oldendPoint);
				oldpoint = point;
				oldendPoint = newPoint;
				DrawTracked(point,newPoint);
				break;
			case WM_RBUTTONDBLCLK:
			case WM_RBUTTONUP:
			case WM_RBUTTONDOWN:
				bStillDrawing=FALSE;
			case WM_LBUTTONDOWN:
				break;
			default:
				DispatchMessage(&msg);
				break;
		}
	}

	// store away as angle length pairs.
	ReleaseCapture();
	int iPos=m_combo1.GetCurSel();
	int bg = m_combo1.GetItemData(iPos);
	iPos=m_combo2.GetCurSel();
	int fg = m_combo2.GetItemData(iPos);
	pPoly->SetFgRGB(fg&0xff, (fg>>8)&0xff, (fg>>16)&0xff);
	pPoly->SetBkRGB(bg&0xff, (bg>>8)&0xff, (bg>>16)&0xff);
	mylist.AddTail(pPoly);
}

// Draw a tracker type line on the display which if drawn twice
// will "undisplay" itself.
void CSpinnerDesignDlg::DrawTracked(CPoint startPoint,CPoint endPoint)
{
	CRgn rgnNew;
	CBrush* pBrush = CDC::GetHalftoneBrush();
	CDC* pDC = GetDC();

	CRect rect(startPoint, endPoint);
	rect.NormalizeRect();

	int x_delta, y_delta;
	
	/// choose some deltas to make the tracking line look OK in each orientation ///
	if (rect.Height() > rect.Width())
	{
		 x_delta = 1;
		 y_delta = 0;
	}
	else
	{
		 x_delta = 0;
		 y_delta = 1;
	}

	CPoint newPoints[] =
	{
		CPoint(startPoint.x + x_delta, startPoint.y - y_delta),
		CPoint(startPoint.x - x_delta, startPoint.y + y_delta),
		CPoint(endPoint.x - x_delta, endPoint.y + y_delta),
		CPoint(endPoint.x + x_delta, endPoint.y - y_delta)
	};
	rgnNew.CreatePolygonRgn(newPoints, 4, ALTERNATE);
	pDC->SelectClipRgn(&rgnNew);
	pDC->GetClipBox(&rect);
	CBrush* pBrushOld = pDC->SelectObject(pBrush);
	pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
	/// restore the device context ///
	pDC->SelectObject(pBrushOld);
	pDC->SelectClipRgn(NULL);
	ReleaseDC(pDC);
}

void CSpinnerDesignDlg::OnClear() 
{
	if(mylist.GetCount()>0)
	{
		CString str;
		str.LoadString(IDS_CLEAR_ARE_YOU_SURE);
		if(MessageBox(str,"Are you sure",MB_OKCANCEL|MB_ICONQUESTION)==IDOK)
		{
			ClearItems();
		}
	}
	else ClearItems();
	Invalidate();
}

void CSpinnerDesignDlg::OnGenerate() 
{
	CFileDialog dlg(FALSE,"JVF");
	if(dlg.DoModal()==IDOK)
	{
		try
		{
			CFile file(dlg.GetFileName(),CFile::modeCreate|CFile::modeWrite);

			StorageHeader hdr;
			hdr.magic = 0xfade0ff;
			hdr.byReserved=0;
			hdr.elements=mylist.GetCount();
			hdr.byRed=255;
			hdr.byBlue=255;
			hdr.byGreen=255;
			file.Write(&hdr,sizeof StorageHeader);
	
			POSITION pos = mylist.GetHeadPosition();
			while(pos)
			{
				CObjectStorage *obj = (CObjectStorage*)mylist.GetNext(pos);
				obj->OutputCoordinates(&file);
			}

			file.Close();
		}
		catch(CFileException *e)
		{
			char szError[255];
			e->GetErrorMessage(szError,sizeof szError);
			MessageBox(CString("Problem opening file!\n")+szError);
		}

	}
}

void CSpinnerDesignDlg::OnRescale() 
{
	UpdateData();
	Invalidate();
}

void CSpinnerDesignDlg::OnTest() 
{
	// start by settting test=true, store scale and rotation.
	bTestViewMode=true;
	double scl = m_scale;
	double rot = m_rot2d;

	// work from rotation of 0 and scale of 0.1 going up in increments.
	m_scale=0.1;
	for(int i=0;i<360;i++)
	{
		m_scale+=0.004;
		m_rot2d+=0.03;
		Sleep(30); // delay between frames
		Invalidate();
		UpdateWindow();

	}

	// restore our screen..
	m_scale = scl;
	m_rot2d = rot; 
	bTestViewMode=false;
	Invalidate();
	UpdateWindow();
	
}

    Source: geocities.com/elricsgsx/java/Spinner

               ( geocities.com/elricsgsx/java)                   ( geocities.com/elricsgsx)