CTreeDoc* GetDocument()
{
	return dynamic_cast<CTreeDoc*>(m_pDocument);
}


class CTreeFrame : public CMDIChildWnd
{
	DECLARE_DYNCREATE(CTreeFrame)
public:
	CTreeFrame();
	virtual ~CTreeFrame();
	//====== Создание панелей расщепленного (split) окна  
	virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs,
								CCreateContext* pContext);
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
	//====== Объект для управления расщепленным окном
	CSplitterWnd m_wndSplitter;
	DECLARE_MESSAGE_MAP()
};


#pragma once
class CTreeDoc;				// Упреждающее объявление

class CLeftView : public CTreeView
{
protected:
	//====== Ссылка на объект элемета управления деревом
	CTreeCtrl& m_Tree;
	//====== Список значков узлов дерева
	CImageList *m_pImgList;
	
	CLeftView();
	virtual void OnInitialUpdate();

	DECLARE_DYNCREATE(CLeftView)
public:
	virtual ~CLeftView();
	CTreeDoc* GetDocument()  
	{
		return dynamic_cast<CTreeDoc*>(m_pDocument);
	}

	//====== Выбор системных значков
	void GetSysImgList();
	//====== Вставка нового узла (ветви)
	void AddItem(HTREEITEM h, LPCTSTR s);
	//====== Поиск своих документов
	void SearchForDocs(CString s);
	//====== Проверка отсутствия файлов
	bool NotEmpty(CString s);
	//====== Вычисляет полный путь текущего узла дерева
	CString GetPath (HTREEITEM hCur);
	DECLARE_MESSAGE_MAP()
};



IMPLEMENT_DYNCREATE(CLeftView, CTreeView)

BEGIN_MESSAGE_MAP(CLeftView, CTreeView)
END_MESSAGE_MAP()

CLeftView::CLeftView(){}
CLeftView::~CLeftView(){}

void CLeftView::OnInitialUpdate()
{
	CTreeView::OnInitialUpdate();
}



CLeftView::CLeftView()
	: m_Tree(GetTreeCtrl()) {}


::SetWindowLongPtr (m_Tree.m_hWnd, GWL_STYLE,
		::GetWindowLong(m_Tree.m_hWnd, GWL_STYLE)
			| TVS_HASBUTTONS | TVS_HASLINES
			| TVS_LINESATROOT | TVS_SHOWSELALWAYS);



m_Tree.InsertItem("Item",0,0);



//====== Традиционный для MS двухступенчатый способ
//====== создания нового объекта - списка изображений
m_pImgList = new CImageList;
m_pImgList->Create(16, 16, ILC_MASK, 0, 32);

for (UINT nID = IDB_1; nID <= IDB_3; nID++)
{
	//====== Временный объект
	CBitmap bitmap;
	//====== Загрузка из ресурсов
	bitmap.LoadBitmap(nID);	

	//====== Добавление в конец списка изображений
	m_pImgList->Add(&bitmap, (COLORREF)0xFFFFFF);

	//====== Освобождаем память
	bitmap.DeleteObject();
}

//=== Связывание списка изображений с объектом CTreeCtrl
m_Tree.SetImageList(m_pImgList, TVSIL_NORMAL);



TVINSERTSTRUCT gtv;		// Глобальная структура



//====== Вставляем узел верхнего уровня иерархии
gtv.hParent = TVI_ROOT;
//====== Вставляем в конец списка
gtv.hInsertAfter = TVI_LAST;
//====== Формат узла - два изображения и текст
gtv.item.mask= TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
//====== Индекс изображения для узла в обычном состоянии
gtv.item.iImage = 0;
//=== Индекс изображения для узла в выбранном состоянии
gtv.item.iSelectedImage = 1;
//====== Текст, именующий узел
gtv.item.pszText = "First";
		
//====== Описатели трех ветвей
HTREEITEM h1, h2, h3;
//====== Вставка первого узла
h1 = m_Tree.InsertItem(&gtv);

//====== Первый узел будет родителем второго
gtv.hParent = h1;
//====== Атрибуты второго узла
gtv.item.iImage = 1;
gtv.item.pszText = "Second";
//====== Вставка второго узла	
h2 = m_Tree.InsertItem(&gtv);

//====== Второй узел будет родителем третьего
gtv.hParent = h2;
gtv.item.iImage = 2;
gtv.item.pszText = "Third";
//====== Вставка третьего узла
h3 = m_Tree.InsertItem(&gtv);



void CLeftView::OnInitialUpdate()
{
	CTreeView::OnInitialUpdate();

	::SetWindowLongPtr(m_Tree.m_hWnd, GWL_STYLE,
		GetWindowLong(m_Tree.m_hWnd, GWL_STYLE)|TVS_HASLINES
		| TVS_HASBUTTONS|TVS_LINESATROOT|TVS_SHOWSELALWAYS);
	
	//====== Создаем новый список изображений
	m_pImgList = new CImageList;

	//====== Связываем его с системным списком изображений
	GetSysImgList();
	
	char s [1024];
	//====== Получаем имена логических дисков
	DWORD size = ::GetLogicalDriveStrings (1024, s);

	if (!size)		// В случае отказа
		return;		// уходим молча

	//=== Сканируем текст и вставляем новые узлы дерева
	for (char *pName = s; *pName; pName += strlen(pName) + 1)
		AddItem (TVI_ROOT, pName);		
}



void CLeftView::GetSysImgList()
{
	SHFILEINFO info;

	// Попытка получить описатель системного списка значков
	HIMAGELIST hImg = (HIMAGELIST)::SHGetFileInfo("C:\\",0,
							&info,sizeof(info),
							SHGFI_SYSICONINDEX | SHGFI_SMALLICON);

	//=== Приписываем описатель системного списка
	//=== изображений объекту CImageList
	if (!hImg || !m_pImgList->Attach(hImg))
	{
		MessageBox(0,"Не могу получить System Image List!");
		return;
	}

	//=== Связывание списка с элементом управления деревом
	m_Tree.SetImageList(m_pImgList, TVSIL_NORMAL);
}



void CLeftView::AddItem (HTREEITEM h, LPCTSTR s)
{
	SHFILEINFO Info;
	int len = sizeof(Info);

	//=== Добываем изображение (маленький значок)
	::SHGetFileInfo (s, 0, &Info, len, SHGFI_ICON
							| SHGFI_SMALLICON);
	int id = Info.iIcon;

	//=== Добываем изображение в выбранном состоянии
	::SHGetFileInfo (s,0,&Info,len,
				SHGFI_ICON | SHGFI_OPENICON | SHGFI_SMALLICON);
	int idSel = Info.iIcon;

	//====== Копируем параметр в рабочую строку
	CString sName(s);

	//=== Отсекаем лишние символы (сначала в конце строки)
	if (sName.Right(1) == '\\')
		sName.SetAt (sName.GetLength() - 1, '\0');

	//====== Затем в начале
	int iPos = sName.ReverseFind('\\');
	if (iPos != -1)
		sName = sName.Mid(iPos + 1);
	
	//=== Вставляем узел в дерево
	HTREEITEM hNew = m_Tree.InsertItem(sName, id, idSel, h);

	//====== Вставляем пустой узел
	if (NotEmpty(s))
		m_Tree.InsertItem("", 0, 0, hNew);
}



bool CLeftView::NotEmpty(CString s)
{
	//====== Параметр s содержит текущий файловый путь
	//====== Объект класса, умеющего искать нечто в папке
	CFileFind cff;

	//====== Дополняем путь маской *.* или \*.*	
	s += s.Right(1) == '\\' ? "*.*" : "\\*.*";

	BOOL bFound = cff.FindFile(s);
	
	//====== Цикл поиска настоящих объектов
	while (bFound)
	{
		bFound = cff.FindNextFile();
			
		//====== Это папка?
		if (cff.IsDirectory() && !cff.IsDots())
			return true;

		//====== Это файл?
		if (!cff.IsDirectory() && !cff.IsHidden())
			return true;
	}
	//====== Не найдены объекты, достойные внимания
	return false;
}


void CLeftView::OnItemexpanding (NMHDR* pNMHDR,
											LRESULT* pResult) 
{
	//====== Преобразование типа указателя
	NM_TREEVIEW* p = (NM_TREEVIEW*)pNMHDR;

	//====== Если узел не раскрыт
	if ( !(p->itemNew.state & TVIS_EXPANDED) )
		//====== тормозим перерисовку
		SetRedraw(FALSE);
	*pResult = 0;
}



void CLeftView::OnItemexpanded (NMHDR* pNMHDR,
											LRESULT* pResult)
{
	NM_TREEVIEW* p = (NM_TREEVIEW*)pNMHDR;
	
	//====== Создаем курсор ожидания
	CWaitCursor wait;

	//====== Признак раскрытия узла (а не его закрытия)
	if (p->itemNew.state & TVIS_EXPANDED)
	{	
		// Описатели раскрываемого и первого вложенного узла	
		HTREEITEM	hCur = p->itemNew.hItem,
					h = m_Tree.GetChildItem(hCur);

		//====== Если имя вложенного узла пусто,
		//====== то ветвь еще не раскрывалась
		if (m_Tree.GetItemText(h) == "")
		{
			//====== Удаляем муляж
			m_Tree.DeleteItem(h);
			//====== Вычисляем полный путь
			CString s = GetPath(hCur) + "*.*";

			//====== Наполнение раскрытой ветви
			CFileFind cff;
			BOOL bFound = cff.FindFile(s);
	
			while (bFound)
			{
				bFound = cff.FindNextFile();
				if (cff.IsDirectory() && !cff.IsDots())
					AddItem(hCur, cff.GetFilePath());
			}
		}
		//====== Разрешаем перерисовку
		SetRedraw(TRUE);
	}
	*pResult = 0;
}



CString CLeftView::GetPath (HTREEITEM hCur)
{
	//====== Вычисляет полный файловый путь узла hCur
	CString s = "";
	for (HTREEITEM h=hCur;  h;  h=m_Tree.GetParentItem(h))
		s = m_Tree.GetItemText(h) + '\\' + s;
	return s;
}



void CLeftView::OnSelchanged (NMHDR *pNMHDR,
										LRESULT *pResult)
{
	NM_TREEVIEW* p = (NM_TREEVIEW*)pNMHDR;

	//====== Освобождение контейнера текущих файлов
	GetDocument()->FreeDocs();

	//====== Поиск нужных файлов
	SearchForDocs (GetPath(p->itemNew.hItem));

	//====== Генерация картинок и демонстрация их в окне
	//====== правого Вида
	GetDocument()->ProcessDocs();
	*pResult = 0;
}



void CLeftView::SearchForDocs (CString s)
{
	//====== Дополняем файловый путь маской поиска
	s += "*.mgn";
	
	CFileFind cff;
	BOOL bFound = cff.FindFile(s);
	
	while (bFound)
	{
		bFound = cff.FindNextFile();
		//====== Запоминаем файловые пути в контейнере строк
		GetDocument()->m_sFiles.push_back(cff.GetFilePath());
	}
}



typedef vector<CDPoint, allocator<CDPoint> > VECPTS;



class CPolygon: public CObject
{
	DECLARE_SERIAL(CPolygon)
public:
	CTreeDoc *m_pDoc;		// Обратный указатель
	VECPTS m_Points;			// Контейнер вещественных точек
	UINT m_nPenWidth;		// Толщина пера
	COLORREF m_PenColor;	// Цвет пера
	COLORREF m_BrushColor;	// Цвет кисти
	CDPoint m_ptLT;			// Координата левого верхнего угла
	CDPoint m_ptRB;			// Координата правого нижнего угла

	//====== Конструктор по умолчанию
	CPolygon();
	//====== Конструктор копирования
	CPolygon(const CPolygon& poly);

	//====== Операция присвоения
	CPolygon& operator= (const CPolygon& poly);
	//====== Операция выбора i-той точки
	CDPoint& operator[] (UINT i);
	//====== Вычисление обрамляющего прямоугольника
	void GetRect(CDPoint& ptLT, CDPoint& ptRB);
	//====== Установка обратного указателя
	void Set (CTreeDoc *p);	
	//====== Изменение атрибутов
	void Set(CTreeDoc *p,COLORREF bCl,COLORREF pCl,UINT pen);

	//====== Создание трех простых заготовок
	void MakeStar();			// Звезда
	void MakeTria();			// Треугольник
	void MakePent();			// Пятиугольник

	//====== Изображение в контексте устройства
	virtual void Draw (CDC *pDC, bool bContour);

	//====== Сохранение и восстановление данных
	virtual void Serialize(CArchive& ar);
	virtual ~CPolygon();		// Деструктор
};

//====== Новый тип данных: контейнер полигонов
typedef vector<CPolygon, allocator<CPolygon> > VECPOLY;


IMPLEMENT_SERIAL(CPolygon, CObject, 1)

//====== Конструктор по умолчанию
CPolygon::CPolygon()
{
	m_pDoc = 0;			// Пока не знаем обратного	адреса
	MakeStar();			// Зададим полигон в виде звезды
}



CPolygon& CPolygon::operator=(const CPolygon& poly)
{
	//====== Копируем все данные
	m_pDoc = poly.m_pDoc;
	m_nPenWidth = poly.m_nPenWidth;
	m_PenColor = poly.m_PenColor;
	m_BrushColor = poly.m_BrushColor;
		
	m_ptLT = poly.m_ptLT;
	m_ptRB = poly.m_ptRB;
	
	//====== Освобождаем контейнер точек
	if (!m_Points.empty())
		m_Points.clear();

	//====== Копируем все точки
	for (UINT i=0;  i<poly.m_Points.size();  i++)
		m_Points.push_back(poly.m_Points[i]);

	//====== Возвращаем собственный объект
	return *this;
}

//====== Конструктор копирования пользуется уже
//====== существующей реализацией операции присвоения
CPolygon::CPolygon(const CPolygon& poly)
{
	*this = poly;
}



CDPoint& CPolygon::operator[](UINT i)
{
	if (0 <= i && i < m_Points.size())
		return m_Points[i];
	return m_ptLT;
}



//====== Установка обратного указателя
void CPolygon::Set (CTreeDoc *p)
{
	m_pDoc = p;
}

//====== Совмещенная версия для изменения атрибутов
void CPolygon::Set (CTreeDoc *p, COLORREF bCl,
							COLORREF pCl, UINT pen)
{
	m_pDoc			= p;
	m_BrushColor	= bCl;
	m_PenColor	= pCl;
	m_nPenWidth	= pen;
}



CPolygon::~CPolygon() 
{
 m_Points.clear(); 
}



void CPolygon::GetRect(CDPoint& ptLT, CDPoint& ptRB)
{
	m_ptLT = m_ptRB = CDPoint(0.,0.);

	//====== Если полигон содержит точки контура
	UINT n = m_Points.size();	
	if (n > 0)
	{	
		//====== Пробег по всем его точкам
		for (UINT i=0; i<n; i++)
		{
			//====== Поиск и запоминание экстремумов
			double	x = m_Points[i].x,
					y = m_Points[i].y;
			if (x < m_ptLT.x)
				m_ptLT.x = x;
			else if (x > m_ptRB.x)
				m_ptRB.x = m_Points[i].x;
			if (y > m_ptLT.y)
				m_ptLT.y = y;
			else if (y < m_ptRB.y)
				m_ptRB.y = y;
		}
	}
	//====== Возвращаем найденные координаты (ссылками)
	ptLT = m_ptLT;
	ptRB = m_ptRB;
}



void CPolygon::Serialize(CArchive& ar)
{
	//====== Если идет запись в архив,
	if (ar.IsStoring())
	{
		//=== то последовательно переносим туда все данные
		ar	<< m_nPenWidth << m_PenColor << m_BrushColor 
			<< m_Points.size() << m_ptLT.x << m_ptLT.y
			<< m_ptRB.x << m_ptRB.y; 
		
		for (UINT i=0; i <m_Points.size(); i++)
			ar << m_Points[i].x << m_Points[i].y;
	}
	else
	{
		//=== При чтении из архива меняем направление обмена
		UINT size;
		ar	>> m_nPenWidth >> m_PenColor >> m_BrushColor
			>> size >> m_ptLT.x >> m_ptLT.y
			>> m_ptRB.x >> m_ptRB.y;

		//====== Заново создаем контейнер точек полигона
		m_Points.clear();
		while (size--)
		{
			double x, y;
			ar >> x >> y;
			m_Points.push_back(CDPoint(x,y));
		}
	}
}



void CPolygon::Draw (CDC *pDC, bool bContour)
{
	//====== Размер контейнера World-координат точек
	UINT nPoints = m_Points.size();
	if (!nPoints)
		return;

	//====== Временный массив логических координат точек
	CPoint *pts = new CPoint[nPoints];

	//====== Преобразование координат
	for (UINT i=0; i<nPoints; i++)
		pts[i] = m_pDoc->MapToLogPt(m_Points[i]);
	
	pDC->SaveDC();

	CPen pen (PS_SOLID,m_nPenWidth,m_PenColor);
	pDC->SelectObject(&pen);
	CBrush brush (bContour ? GetSysColor(COLOR_WINDOW)
				 	: m_BrushColor);
	pDC->SelectObject(&brush);

	//====== Полигон изображается в предварительно
	//====== подготовленном контексте устройства
	pDC->Polygon(pts, nPoints);

	//====== Освобождаем массив
	delete [] pts;
	pDC->RestoreDC(-1);
}



void CPolygon::MakeStar()
{
	m_Points.clear();

	//====== Вспомогательные переменные
	double	pi = 4. * atan(1.),				// Углы
			a1 = pi / 10.,
			a2 = 3. * a1,
			//====== 2 характерные точки
			x1 = cos(a1),
			y1 = sin(a1),	
			x2 = cos(a2),
			y2 = sin(a2);
	
	//=== Вещественные (World) координаты углов звезды
	m_Points.push_back(CDPoint(0., 1.));
	m_Points.push_back(CDPoint(-x2, -y2));
	m_Points.push_back(CDPoint( x1,  y1));
	m_Points.push_back(CDPoint(-x1,  y1));
	m_Points.push_back(CDPoint( x2, -y2));

	//====== Габариты звезды
	m_ptLT = CDPoint(-x1, 1.);
	m_ptRB = CDPoint( x1,-y2);
}

//====== Генерация треугольника
void CPolygon::MakeTria()
{
	m_Points.clear();
	double	pi = 4. * atan(1.),
			a = pi / 6.,
			x = cos(a),
			y = sin(a);
	
	m_Points.push_back (CDPoint(0., 1.));
	m_Points.push_back (CDPoint(-x, -y));
	m_Points.push_back (CDPoint( x, -y));
	m_ptLT = CDPoint (-x, 1.);
	m_ptRB = CDPoint ( x,-y);
}

//====== Генерация пятиугольника
void CPolygon::MakePent()
{
	m_Points.clear();

	double	pi = 4. * atan(1.),
			a1 = pi / 10.,
			a2 = 3. * a1,
			x1 = cos(a1),
			y1 = sin(a1),
			x2 = cos(a2),
			y2 = sin(a2);
	
	// Вещественные (World) координаты углов пятиугольника
	m_Points.push_back(CDPoint(0., 1.));
	m_Points.push_back(CDPoint(-x1,  y1));
	m_Points.push_back(CDPoint(-x2, -y2));
	m_Points.push_back(CDPoint( x2, -y2));
	m_Points.push_back(CDPoint( x1,  y1));

	m_ptLT = CDPoint(-x1, 1.);
	m_ptRB = CDPoint( x1,-y2);
}



class CTreeDoc : public CDocument
{
	//=== Все 3 Вида имеют право доступа к данным Документа
	friend class CLeftView;	
	friend class CRightView;
	friend class CDrawView;
protected:
	virtual ~CTreeDoc();	
	CTreeDoc();

	DECLARE_DYNCREATE(CTreeDoc)
public:
	//========== Данные документа =============
	CPolygon m_Poly;				// Дежурный полигон
	VECPOLY m_Shapes;			// Контейнер полигонов

	//====== Контейнер имен файлов
	vector<CString> m_sFiles;

	//====== Размер Документа в Page space
	CSize m_szDoc;
	//== Коэффициент увеличения при переходе World->Page
	UINT m_nLogZoom;
	//====== Флаг: открыто окно типа CTreeFrame
	bool m_bTreeExist;
	//====== Флаг: открыто окно типа CDrawFrame
	bool m_bDrawExist;

	//====== Новые методы класса Документ =======
	//====== Поиск нужного Вида
	CView* GetView(const CRuntimeClass* pViewClass);
	//====== Создание нужного Вида
	bool MakeView();
	//====== Преобразование координат World -> Page
	CPoint MapToLogPt(CDPoint& pt);
	//====== Преобразование координат Page -> World
	CDPoint MapToWorldPt(CPoint& pt);
	//====== Перерисовка вида редактирования
	void UpdateDrawView();

	//====== Чтение найденных документов и их демонстрация
	void ProcessDocs();
	//====== Освобождение контейнеров
	void FreeDocs();
	//====== Поиск выбранной точки
	int FindPoint(CDPoint& pt);

	// Overrides
	public:
	virtual BOOL OnNewDocument();
	virtual void Serialize(CArchive& ar);

// Generated message map functions
protected:
	DECLARE_MESSAGE_MAP()
};



CTreeDoc::CTreeDoc() : m_szDoc(2000,2000), m_Poly()
{
	//====== Установка обратного указателя и
	//====== атрибутов дежурного полигона
	m_Poly.Set(this, RGB(240,255,250), RGB(0,96,0), 2);
	m_nLogZoom = 700;
}



CTreeDoc::~CTreeDoc()
{
	FreeDocs();
	m_Poly.m_Points.clear();
}



void CTreeDoc::Serialize(CArchive& ar)
{
	// Просим объект выполнить сериализацию самостоятельно
	m_Poly.Serialize(ar);

	if (ar.IsStoring())
	{
		// Здесь помещается код записи "обычных" данных
	}
	else
	{
		// Здесь помещается код чтения "обычных" данных
	}
}



CPoint CTreeDoc::MapToLogPt(CDPoint& pt)
{
	//====== Растяжение и сдвиг
	int	x = m_szDoc.cx/2 + int(m_nLogZoom * pt.x),	
		y = m_szDoc.cy/2 + int(m_nLogZoom * pt.y);	
	return CPoint(x,y);
}



CDPoint CTreeDoc::MapToWorldPt(CPoint& pt)
{
	//====== Обратные операции
	double	x = double(pt.x - m_szDoc.cx/2) / m_nLogZoom,
			y = double(pt.y - m_szDoc.cy/2) / m_nLogZoom;
	return CDPoint(x, y);
}



class CTreeApp : public CWinApp
{
public:
	//====== Два шаблона документов
	CMultiDocTemplate *m_pTemplDraw, *m_pTemplTree;

	CTreeApp();
	virtual BOOL InitInstance();
	afx_msg void OnAppAbout();
	DECLARE_MESSAGE_MAP()
};



//====== Создаем первый шаблон
m_pTemplTree = new CMultiDocTemplate(IDR_TreeTYPE,
		RUNTIME_CLASS(CTreeDoc),
		RUNTIME_CLASS(CTreeFrame),
		RUNTIME_CLASS(CLeftView));

//====== Помещаем его в список
AddDocTemplate(m_pTemplTree);

//====== Создаем второй шаблон
m_pTemplDraw = new CMultiDocTemplate(IDR_DrawTYPE,
		RUNTIME_CLASS(CTreeDoc),
		RUNTIME_CLASS(CDrawFrame),
		RUNTIME_CLASS(CDrawView));

//====== Помещаем его в список
AddDocTemplate(m_pTemplDraw);



\nTree\nTree\nTree Files (*.mgn)\n.mgn\nTree.Document\nTree.Document



void CTreeApp::OnAppAbout()
{
	CDialog(IDD_ABOUTBOX).DoModal();
}



#pragma once
class CTreeDoc;				// Упреждающее объявление

class CDrawView : public CView
{
	DECLARE_DYNCREATE(CDrawView)
protected:
	CSize m_szView;		// Размеры клиетской области окна
	bool m_bNewPoints;	// Флаг режима вставки новых точек
	bool m_bReady;		// Флаг готовности захвата вершины
	bool m_bLock; 		// Флаг захвата вершины
	int m_CurID;			// Индекс полигона в массиве
	HCURSOR m_hGrab;		// Курсор захвата
	CPen m_penLine;		// Перо для изображения контура

	CDrawView();
	virtual ~CDrawView();

public:
	CTreeDoc* GetDocument()  
	{
 	return dynamic_cast<CTreeDoc*>(m_pDocument); 
	}
	virtual void OnDraw(CDC* pDC);

	//====== Настройка контекста устройства
	void SetDC(CDC* pDC);

	//====== Перерисовка контура
	void RedrawLines (CDC *pDC, CPoint& point);

	DECLARE_MESSAGE_MAP()
};



CDrawView::CDrawView()
{
	//====== Все режимы редактирования выключены
	m_bNewPoints = false;
	m_bReady = false;
	m_bLock = false;
	m_CurID = -1;
}

void CDrawView::OnDraw(CDC* pDC)
{
	CTreeDoc* pDoc = GetDocument();

	//====== Настройка контекста устройства
	SetDC(pDC);
	//====== Рисуем без заливки внутренних областей,
	//====== если вершина перемещается
	pDoc->m_Poly.Draw(pDC, m_bLock);
}



void CDrawView::SetDC(CDC* pDC)
{
	CTreeDoc* pDoc = GetDocument();

	//====== Режим преобразования без искажений пропорций
	pDC->SetMapMode(MM_ISOTROPIC);

	//====== Размеры логического окна хранит Документ
	pDC->SetWindowExt(pDoc->m_szDoc);
	pDC->SetWindowOrg(pDoc->m_szDoc.cx/2,pDoc->m_szDoc.cy/2);

	//====== Размеры физического окна хранит Вид
	pDC->SetViewportExt (m_szView.cx, -m_szView.cy);
	pDC->SetViewportOrg (m_szView.cx/2, m_szView.cy/2);
}



void CDrawView::OnSize(UINT nType, int cx, int cy) 
{
	CView::OnSize(nType, cx, cy);

	// Каркас иногда вызывает эту функцию с нулевыми cx,cy
	if (cx==0 || cy==0)
		return;

	//====== Запоминаем размеры окна Вида	
	m_szView = CSize (cx, cy);	
}



void CDrawView::OnInitialUpdate()
{
	//====== Загружаем курсор перемещения
	m_hGrab = ((CTreeApp*)AfxGetApp())->LoadCursor(IDC_MOVE);
	//=== Создаем перо перерисовки контура (при перемещении)
	m_penLine.CreatePen (PS_DOT,0,COLORREF(0));
}



#pragma once

//====== Класс для демонстрации содержимого документов
class CRightView : public CScrollView
{
	//====== Упреждающее объявление класса окна картинки
	friend class CWndGeom;
protected:
	CSize m_szView;		// Реальные размеры окна
	CSize m_szScroll;	// Размеры прокручиваемого окна
	CSize m_szItem;		// Размеры картинки
	CSize m_szMargin;	// Размеры полей
	CString m_WndClass;	// Строка регистрации окна картинки
	
	CRightView();
	DECLARE_DYNCREATE(CRightView)
public:
	//====== Контейнер окон картинок
	vector<CWndGeom*> m_pWnds;
	
	CTreeDoc* GetDocument()
	{
 	return dynamic_cast<CTreeDoc*>(m_pDocument);
	}
	virtual ~CRightView();
	void Show();					// Демонстрация картинок
	void Clear();					// Освобождение ресурсов

	// Overrides
	public:
	virtual void OnDraw(CDC* pDC);
	protected:
	virtual void OnInitialUpdate();
	DECLARE_MESSAGE_MAP()
};



IMPLEMENT_DYNCREATE(CRightView, CScrollView)

BEGIN_MESSAGE_MAP(CRightView, CScrollView)
END_MESSAGE_MAP()

CRightView::CRightView(){}
CRightView::~CRightView(){}

void CRightView::OnDraw(CDC* pDC)
{
	CTreeDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
}



void CRightView::OnSize(UINT nType, int cx, int cy) 
{
	CScrollView::OnSize(nType, cx, cy);

	if (cx==0 || cy==0)
		return;

	//====== Запоминаем размеры окна Вида
	m_szView = CSize (cx, cy);	
}

void CRightView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();

	//====== Начальные размеры окна
	m_szScroll = m_szView;
	SetScrollSizes(MM_TEXT, m_szScroll); 
}



CRightView::CRightView()
{
	m_szItem = CSize(200,150);		// Размеры картинки
	m_szMargin = CSize(20,20);		// Размеры полей
	try
	{
		//====== Попытка зарегистрировать класс окон
		m_WndClass=AfxRegisterWndClass(CS_VREDRAW|CS_HREDRAW,
		::LoadCursor(GetModuleHandle(0),(char*)IDC_MYHAND),
		(HBRUSH) CreateSolidBrush(GetSysColor(COLOR_INFOBK)));
	}
	catch (CResourceException* pEx)
	{
		AfxMessageBox(_T("Класс уже зарегистрирован"));
		pEx->Delete();
	}
}



void CRightView::Show()
{
	CTreeDoc *pDoc = GetDocument();

	//====== Количество картинок
	int nPoly = pDoc->m_Shapes.size();	
	//====== Вычисление шага, с которым выводятся картинки
	int	dx = m_szItem.cx + m_szMargin.cx,
		dy = m_szItem.cy + m_szMargin.cy,
		nCols = m_szView.cx/dx;		// Количество колонок

	//====== Коррекция
	if (nCols < 1)
		nCols = 1;
	if (nCols > nPoly)
		nCols = nPoly;

	//====== Количество рядов
	int nRows = int(double(nPoly)/nCols + .5);

	//=== Вычисление и установка размеров окна прокрутки
	m_szScroll = CSize(nCols*dx, nRows*dy);
	SetScrollSizes(MM_TEXT, m_szScroll);

	//====== Координаты и размеры первой картинки
	CRect r(CPoint(0,0),m_szItem);
	r.OffsetRect (15,15);

	//====== Стиль окна картинки
	DWORD style = WS_CHILD | WS_BORDER | WS_VISIBLE;

	//====== Цикл прохода по рядам (n - счетчик картинок)
	for (int i=0, n=0; i < nRows; i++)
	{	
		//====== Цикл прохода по столбцам
		for (int j=0; j<nCols && n<nPoly; j++, n++)
		{
			CWndGeom *pWnd = new CWndGeom(this, n);

			//====== Запоминаем окна в контейнере
			m_pWnds.push_back(pWnd);
			//====== Создаем Windows-окно
			pWnd->Create (m_WndClass, 0, style, r, this, 0);

			//====== Сдвигаем координаты окна вправо
			r.OffsetRect (dx,0);
		}
		//====== Сдвигаем координаты окна влево и вниз
		r.OffsetRect(-nCols*dx, dy);
	}
}



void CRightView::Clear()
{
	//====== Цикл прохода по всем адресам контейнера
	for (UINT i=0; i<m_pWnds.size(); i++)
	{
		//====== Уничтожение Windows-окна
		m_pWnds[i]->DestroyWindow();
	
		// Освобождение памяти, занимаемой объектами класса
		delete m_pWnds[i];
	}
	//====== Освобождение памяти, занимаемой контейнером
	m_pWnds.clear();
}

		// Деструктор класса вызывает Clear
CRightView::~CRightView()
{
	Clear();
}



class CWndGeom : public CWnd
{
public:
	CTreeDoc *m_pDoc;	// Адрес Документа (для удобства)
	CRightView *m_pView;// Адрес родительского окна
	int m_ID; 			// Индекс окна документа в массиве
	CRect m_Rect;			// Координаты в окне правого Вида

	//====== Удобный для нас конструктор
	CWndGeom(CRightView *p, int id);
	~CWndGeom();
protected:
	DECLARE_MESSAGE_MAP()
};



CWndGeom::CWndGeom(CRightView *p, int id) 
{
	//====== Запоминаем адрес родительского окна
	m_pView = p;
	//====== Запоминаем адрес Документа
	m_pDoc = p->GetDocument();
	//====== и индекс окна в массиве
	m_ID = id;
}

void CWndGeom::OnPaint() 
{
	CPaintDC dc(this);
	
	dc.SetMapMode(MM_ISOTROPIC);

	//====== Настраиваем логическое окно
	dc.SetWindowOrg (m_pDoc->m_szDoc.cx/2,
						m_pDoc->m_szDoc.cy/2);
	dc.SetWindowExt(m_pDoc->m_szDoc);

	//====== Узнаем текущие размеры окна
	GetClientRect(&m_Rect);
	int w = m_Rect.Width(),
		 h = m_Rect.Height();

	//====== Настраиваем аппаратное окно
	dc.SetViewportOrg (w/2, h/2);
	dc.SetViewportExt (w, -h);

	//=== Выбираем в контейнере нужный полигон и просим
	//=== его изобразить себя в подготовленном контексте
	m_pDoc->m_Shapes[m_ID].Draw(&dc);
}

void CWndGeom::OnLButtonDown(UINT nFlags, CPoint point)
{
	//====== Изменяемем дежурный полигон
	m_pDoc->m_Poly = m_pDoc->m_Shapes[m_ID];

	//====== Если не было вида CDrawView, то создаем его
	if (m_pDoc->MakeView())
		return;

	//=== Если он был, то находим его и делаем активным 
	CView *pView=m_pDoc->GetView(&CDrawView::classCDrawView);
	((CMDIChildWnd*)pView->GetParentFrame())->MDIActivate();

	//====== Перерисовка с учетом изменений
	pView->Invalidate();
}



void CWndGeom::OnMouseMove(UINT nFlags, CPoint point) 
{
	//====== Если указатель мыши в пределах окна,
	if (m_Rect.PtInRect(point))
	{
		//====== то захватываем мышь, выбираем перо
		//====== и рисуем обрамляющий прямоугольник
		SetCapture();
		CClientDC dc(this);
		CPen pen (PS_SOLID, 4, RGB(192,192,255));
		dc.SelectObject(&pen);

		dc.MoveTo(m_Rect.left+4, m_Rect.top+4);
		dc.LineTo(m_Rect.right-4, m_Rect.top+4);
		dc.LineTo(m_Rect.right-4, m_Rect.bottom-4);
		dc.LineTo(m_Rect.left+4, m_Rect.bottom-4);
		dc.LineTo(m_Rect.left+4, m_Rect.top+4);
	}
	else
	{
		ReleaseCapture();	// Освобождаем мышь
		Invalidate();			// Прямоугольник будет стерт
	}
}



CView* CTreeDoc::GetView(const CRuntimeClass* pViewClass)
{
	//====== Становимся в начало списка Видов
	POSITION pos = GetFirstViewPosition();
	//====== Пессимистический прогноз
	CView *pView = 0;

	//====== Цикл поиска нужного Вида
	while (pos)
	{
		pView = GetNextView(pos);
		//=== Если Вид нужного типа, то возвращаем его адрес
		if (pView->IsKindOf(pViewClass))
			break;
	}

	//====== Возвращаем результат поиска
	return pView;
}



bool CTreeDoc::MakeView()
{
	//====== Если недостает какого-либо из видов документа
	if (!m_bDrawExist || !m_bTreeExist)
	{
		//====== Добываем адрес приложения
		CTreeApp* pApp = dynamic_cast<CTreeApp*>(AfxGetApp());
		CDocTemplate *pTempl;
				
		//====== Выбираем шаблон недостающего типа
		if (!m_bDrawExist)
		{
			pTempl = pApp->m_pTemplDraw;
			m_bDrawExist = true;
		}
		else
		{
			pTempl = pApp->m_pTemplTree;
			m_bTreeExist = true;
		}
		
		//=== Создаем окно документа и помещаем в него Вид
		//=== Тип рамки и вида определяется шаблоном
		CFrameWnd *pFrame = pTempl->CreateNewFrame(this, 0);
		pTempl->InitialUpdateFrame(pFrame, this);
		return true;
	}
	return false;
}



BOOL CTreeDoc::OnNewDocument()
{
	//====== При создании нового документа
	if (!CDocument::OnNewDocument())
		return FALSE;

	//====== Документ знает свой шаблон
	CDocTemplate* pTempl = GetDocTemplate();
	CString s;

	//====== Выясняем его тип из строкового ресурса
	pTempl->GetDocString(s, CDocTemplate::fileNewName);
	
	m_bDrawExist = s == "Draw";
	m_bTreeExist = !m_bDrawExist;

	return TRUE;
}



void CTreeDoc::FreeDocs()
{
	m_sFiles.clear();
	m_Shapes.clear();

	//====== Выясняем адрес правого вида		
	CRightView *pView = dynamic_cast<CRightView*>
						(GetView(RUNTIME_CLASS(CRightView)));
	//====== Вид освобождает окна-картинки
	if (pView)
		pView->Clear();
}



void CTreeDoc::ProcessDocs()
{
	UINT nFiles = m_sFiles.size();
	//====== Если документы не обнаружены
	if (!nFiles)
		return;
	
	for (UINT i=0; i < nFiles; i++)
	{	
		//====== Читаем все документы
		CFile file; 				// Класс, управляющий файлами
		CFileException e;		// Класс для обработки сбоев
		CString fn = m_sFiles[i];	// Имя файла
		
		if (!file.Open (fn, CFile::modeRead 	|
								CFile::shareDenyWrite, &e))
		{
			//=== В случае сбоя в зависимости от причины
			//=== выдаем то или иное сообщение
			CString msg = 
				e.m_cause == CFileException::fileNotFound ?
				"Файл: " + fn + "  не найден"
				:	"Невозможно открыть " + fn;
			AfxMessageBox(msg);
			return;
		}
		//====== Связываем архив с файлом
		CArchive ar (&file, CArchive::load);
		CPolygon poly;				// Временный полигон
		poly.Set(this);				// Обратный указатель
		poly.Serialize (ar); 		// Читаем данные
		m_Shapes.push_back(poly);	// Запоминаем в массиве
	}
	
	//====== Отображаем результат в окне правого Вида	
	CRightView *pView = dynamic_cast<CRightView*>
						(GetView(RUNTIME_CLASS(CRightView)));
	pView->Show();
}



void CTreeFrame::OnClose() 
{
	//====== Добываем адрес активного документа
	CTreeDoc *pDoc = dynamic_cast<CTreeDoc*>
										(GetActiveDocument());
	pDoc->m_bTreeExist = false;
	CMDIChildWnd::OnClose();
}

void CDrawFrame::OnClose() 
{
	CTreeDoc *pDoc = dynamic_cast<CTreeDoc*>
										(GetActiveDocument());
	pDoc->m_bDrawExist = false;
	CMDIChildWnd::OnClose();
}



void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	//====== В режиме создания нового полигона
	if (m_bNewPoints)
	{
		CTreeDoc *pDoc = GetDocument();
		//====== Ссылка на массив точек текущего полигона
		VECPTS& pts = pDoc->m_Poly.m_Points;

		//=== Получаем адрес текущего контекста устройства
		CDC *pDC = GetDC();
		//====== Настраиваем его с учетом размеров окна
		SetDC(pDC);

		//=== Преобразуем аппаратные координаты в логические
		pDC->DPtoLP(&point);

		//=== Преобразуем Page-координаты в World-координаты
		CDPoint pt = pDoc->MapToWorldPt(point);
		//====== Запоминаем в контейнере
		pts.push_back(pt);
	}
	//====== В режиме готовности к захвату
	else if (m_bReady)
	{
		m_bLock = true;		// Запоминаем состояние захвата
		m_bReady = false;	// Снимаем флаг готовности
	}
	//====== В режиме повторного нажатия
	else if (m_bLock)
		m_bLock = false;		// Снимаем флаг захвата
	else						// В случае бездумного нажатия
		return;				// уходим

	Invalidate();				// Просим перерисовать
}

void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
	//=== В режиме создания нового полигона не участвуем
	if (m_bNewPoints)
		return;
	
	//====== Получаем и настраиваем контекст
	CDC *pDC = GetDC();
	SetDC(pDC);

	//=== Преобразуем аппаратные координаты в логические
	pDC->DPtoLP(&point);

	//=== Преобразуем Page-координаты в World-координаты
	CTreeDoc *pDoc = GetDocument();
	CDPoint pt = pDoc->MapToWorldPt(point);

	//====== Если был захват, то перерисовываем
	//====== контуры двух соседних с узлом линий
	if (m_bLock)
	{
		// Курсор должен показывать операцию перемещения
		SetCursor(m_hGrab);
		//====== Установка режима 
		pDC->SetROP2(R2_XORPEN);
		
		//====== Двойное рисование
		//====== Сначала стираем старые линии
		RedrawLines(pDC,	pDoc->MapToLogPt(pDoc->
								m_Poly.m_Points[m_CurID]));

		//====== Затем рисуем новые
		RedrawLines(pDC, point);

		//====== Запоминаем новое положение вершины
		pDoc->m_Poly.m_Points[m_CurID] = pt;
	}
	//====== Обычный режим поиска близости к вершине
	else
	{
		m_CurID = pDoc->FindPoint(pt);

		//=== Если близко, то m_CurID получит индекс вершины
		//=== Если далеко, то индекс будет равен -1
		m_bReady = m_CurID >= 0;

		//=== Если близко, то меняем курсор
		if (m_bReady)
			SetCursor(m_hGrab);
	}
}

//====== Перерисовка двух линий, соединяющих
//====== перемещаемую вершину с двумя соседними
void CDrawView::RedrawLines (CDC *pDC, CPoint& point)
{
	CTreeDoc *pDoc = GetDocument();

	//====== Ссылка на массив точек текущего полигона
	VECPTS& pts = pDoc->m_Poly.m_Points;
	UINT size = pts.size();

	//====== Если полигон вырожден, уходим
	if (size < 2)
		return;
	
	//====== Индексы соседних вершин
	int i1 = m_CurID == 0 ? size - 1 : m_CurID - 1;
	int i2 = m_CurID == size - 1 ? 0 : m_CurID + 1;
	
	//====== Берем перо и рисуем две линии
	pDC->SelectObject(&m_penLine);
	pDC->MoveTo(pDoc->MapToLogPt(pts[i1]));
	pDC->LineTo(point);
	pDC->LineTo(pDoc->MapToLogPt(pts[i2]));
}


int CTreeDoc::FindPoint(CDPoint& pt)
{
	//====== Пессимистический прогноз
	int id = -1;
	//====== Поиск среди точек дежуоного полигона
	for (UINT i=0; i<m_Poly.m_Points.size(); i++)
	{
		//=== Степень близости в World-пространстве.
		//=== Здесь мы используем операцию взятия нормы
		//=== вектора, которую определили в классе CDPoint
		if ( !(m_Poly.m_Points[i] - pt) <= 5e-2)
		{
			id = i;
			break;			// Нашли
		}
	}
	//====== Возвращаем результат
	return id;
}



void CDrawView::OnEditNewpoly(void)
{
	//====== Включаем/Выключаем режим ввода вершин
	m_bNewPoints = !m_bNewPoints;
	//=== Снимаем флаги редактирования перетаскиванием
	m_bReady = false;
	m_bLock = false;

	//====== Если режим включен, то уничтожаем вершины
	if (m_bNewPoints)
	{
		GetDocument()->m_Poly.m_Points.clear();
		Invalidate();
	}
}



void CDrawView::OnUpdateEditNewpoly(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bNewPoints);
}



void CMainFrame::ChangeToolbar(UINT tb)
{ 
	//=== В параметре tb будет передан идентификатор панели
	m_wndToolBar.LoadToolBar (tb);
	//=== Перерисовка toolbar
	RecalcLayout();
}



void CTreeFrame::OnSetFocus(CWnd* pOldWnd)
{
	//====== Родитель делает свое дело,
	CMDIChildWnd::OnSetFocus(pOldWnd);
	
	//====== а мы делаем свое
	((CMainFrame*)GetParentFrame())->
							ChangeToolbar(IDR_TreeTYPE);
}

void CDrawFrame::OnSetFocus(CWnd* pOldWnd)
{
	CMDIChildWnd::OnSetFocus(pOldWnd);
	((CMainFrame*)AfxGetMainWnd())->
								ChangeToolbar(IDR_DrawTYPE);
}



void CTreeDoc::OnViewRefresh(void)
{
	//====== Получаем адрес левого Вида
	CLeftView *pView = dynamic_cast<CLeftView*>
					(GetView(RUNTIME_CLASS(CLeftView)));

	//====== Запускаем цепочку действий для освежения
	//====== содержимого окна правого Вида
	FreeDocs();
	pView->SearchForDocs(pView->GetPath(pView->
									m_Tree.GetSelectedItem()));
	ProcessDocs();
}



void CWndGeom::OnMouseMove(UINT nFlags, CPoint point) 
{
	//====== Два прямоугольника (CWndGeom и CRightView)
	CRect rChild, rParent;
	//=== Определяем экранные координаты (не клиентские!)
	GetWindowRect(rChild);
	m_pView->GetWindowRect(rParent);

	//=== Если есть полосы прокрутки, то уменьшаем
	//=== прямоугольник окна на 14 пиксел (толщина полосы)
	if (m_pView->m_szScroll.cx - m_pView->m_szView.cx > 0)
		rParent.right -= 14;
	if (m_pView->m_szScroll.cy - m_pView->m_szView.cy > 0)
		rParent.bottom -= 14;
	
	//=== Ищем пересечение прямоугольников, обрезая rChild
	rChild.IntersectRect (rChild, rParent);
	//=== Приводим к экранным координаты указателя мыши	
	ClientToScreen(&point);

	//=== Если мышь попала в усеченный прямоугольник,	
	if (rChild.PtInRect(point))
	{
		//=== то демонстрируем активное состояние,
		// изображая рамку внутри прямоугольника CWndGeom
		SetCapture();

		//=== Здесь координаты относительные (клиентские)
		CRect r(m_Rect);
		r.DeflateRect(4,4);
		CClientDC dc(this);
		//====== Обрамляем выбранный рисунок
		dc.FrameRect(&r, &CBrush(RGB(192,192,255)));
	}
	else
	{
		ReleaseCapture();
		Invalidate();
	}
}	


if (rChild.PtInRect(point))
{
	SetCapture();
	CPen pen (PS_SOLID, 4, RGB(192,192,255));
	CClientDC dc(this);
	dc.SelectObject(&pen);
	CRect r(m_Rect);
	//====== Уменьшаем прямоугольник
	r.DeflateRect(4,4);

	//=== Выбираем прозрачную кисть для того, чтобы
	//=== не закрасить его содержимое
	dc.SelectObject(GetStockObject(NULL_BRUSH));	
	dc.Rectangle(r);
}



Элемент
Идентификатор
Диалог
IDD_POLYCOLOR
Окно редактирования Size:
IDC_PEN
Кнопка TRI
IDC_TRI
Кнопка PENT
IDC_ PENT
Кнопка STAR
IDC_ STAR
Кнопка Close
IDOK
Окно редактирования Red
IDC_RED
Окно редактирования Green
IDC_GREEN
Окно редактирования Blue
IDC_BLUE
Ползунок (Slider)
IDC_RSLIDER
Slider
IDC_GSLIDER
Slider
IDC_BSLIDER
Окно редактирования Color
IDC_COLOR
Диалог
IDD_POLYCOLOR
Окно редактирования Size:
IDC_PEN
Кнопка TRI
IDC_TRI
Кнопка PENT
IDC_ PENT
Кнопка STAR
IDC_ STAR
Кнопка Close
IDOK
Окно редактирования Red
IDC_RED
Окно редактирования Green
IDC_GREEN
Окно редактирования Blue
IDC_BLUE
Ползунок (Slider)
IDC_RSLIDER
Slider
IDC_GSLIDER





#pragma once

//===== Класс нестандартного окна редактирования
class CClrEdit : public CEdit
{
	DECLARE_DYNAMIC(CClrEdit)

public:
	CClrEdit();
	virtual ~CClrEdit();
	void ChangeColor(COLORREF clr);	// Изменяем цвета

protected:
	DECLARE_MESSAGE_MAP()

private:
	COLORREF m_clrText; 	// Цвет текста
	COLORREF m_clrBk;		// Цвет фона
	CBrush m_brBk; 			// Кисть для закраски фона
};

//====== Класс для управления немодальным диалогом
class CPolyDlg : public CDialog
{
	friend class CClrEdit;
	DECLARE_DYNAMIC(CPolyDlg)

public:
	enum { IDD = IDD_POLYCOLOR };

	//====== Удобный для нас конструктор
	CPolyDlg(CTreeDoc* p);
	virtual ~CPolyDlg();

	//====== Отслеживание цвета
	void UpdateColor();
	
protected:
	virtual void DoDataExchange(CDataExchange* pDX);

	DECLARE_MESSAGE_MAP()
private:
	CTreeDoc* m_pDoc;		// Обратный указатель
	CBitmapButton m_cTri;	// Кнопки с изображениями
	CBitmapButton m_cPent;	
	CBitmapButton m_cStar; 
	bool m_bScroll;			// Флаг использования ползунка
};



HBRUSH CClrEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
	pDC->SetTextColor (m_clrText);	// Цвет текста
	pDC->SetBkColor (m_clrBk);			// Цвет подложки текста
	return m_brBk;						// Возвращаем кисть
}



void CClrEdit::ChangeColor(COLORREF clr)
{
	//====== Цвет текста - инвертированый цвет фона
	m_clrText = ~clr & 0xffffff;
	m_clrBk = clr;
	//====== Создаем кисть цвета фона
	m_brBk.DeleteObject();
	m_brBk.CreateSolidBrush (clr);
	Invalidate();
}



void CPolyDlg::DoDataExchange(CDataExchange* pDX)
{
	//====== Связывание Control-переменных с ползунками
	DDX_Control(pDX, IDC_BSLIDER, m_bSlider);
	DDX_Control(pDX, IDC_GSLIDER, m_gSlider);
	DDX_Control(pDX, IDC_RSLIDER, m_rSlider);

	//==== Связывание Control-переменных с нестандартными
	//==== окнами редактирования
	DDX_Control(pDX, IDC_COLOR, m_cColor);
	DDX_Control(pDX, IDC_BLUE, m_cBlue);
	DDX_Control(pDX, IDC_GREEN, m_cGreen);
	DDX_Control(pDX, IDC_RED, m_cRed);

	//==== Связывание Value-переменных с нестандартными
	//==== окнами редактирования и проверка данных
	DDX_Text(pDX, IDC_BLUE, m_nBlue);
	DDV_MinMaxUInt(pDX, m_nBlue, 0, 255);
	DDX_Text(pDX, IDC_GREEN, m_nGreen);
	DDV_MinMaxUInt(pDX, m_nGreen, 0, 255);
	DDX_Text(pDX, IDC_RED, m_nRed);
	DDV_MinMaxUInt(pDX, m_nRed, 0, 255);
	DDX_Text(pDX, IDC_PEN, m_nPen);
	DDV_MinMaxUInt(pDX, m_nPen, 1, 100);	

	//==== Вызов родительской версии функции обмена	
	CDialog::DoDataExchange(pDX);
}



void CPolyDlg::OnHScroll(UINT nSBCode, UINT nPos,
									CScrollBar* pScrollBar) 
{
	//====== Неинтересное для нас сообщение
	if (nSBCode==SB_ENDSCROLL)
		return;

	//====== Устанавливаем флаг сообщений от ползунков
	m_bScroll = true;

	//====== Узнаем идентификатор активного ползунка
	switch(GetFocus()->GetDlgCtrlID())
	{
	case IDC_RSLIDER:
		//====== Считываем текущую позицию движка
		m_nRed = m_rSlider.GetPos();
		//====== Синхронизируем поле редактирования
		SetDlgItemInt(IDC_RED, m_nRed);
		break;
	case IDC_GSLIDER:
		m_nGreen = m_gSlider.GetPos();
		SetDlgItemInt(IDC_GREEN, m_nGreen);
		break;
	case IDC_BSLIDER:
		m_nBlue = m_bSlider.GetPos();
		SetDlgItemInt(IDC_BLUE, m_nBlue);
		break;
	}
	//====== Снимаем флаг сообщений от ползунков
	m_bScroll = false;
}



void CPolyDlg::OnChangePen(void)
{
	BOOL bSuccess;
	//====== Попытка преобразовать в число
	UINT nSize = GetDlgItemInt(IDC_PEN, &bSuccess, FALSE);
	if (bSuccess && nSize < 101)
	{
		m_nPen = nSize;
		m_pDoc->m_Poly.m_nPenWidth = m_nPen;
		m_pDoc->UpdateDrawView();
	}
}



void CPolyDlg::OnChangeRed(void)
{
	//====== Если сообщение спровоцировано ползунком,
	//====== то обходим коды его синхронизации
	if (!m_bScroll)
	{
		m_nRed = GetDlgItemInt(IDC_RED, 0, FALSE);
		m_rSlider.SetPos(m_nRed);
	}

	//====== Изменяем цвет фона окна редактирования
	m_cRed.ChangeColor(RGB(m_nRed, 0, 0));
	//====== Корректируем интегральный  цвет
	UpdateColor();
}

void CPolyDlg::OnChangeGreen(void)
{
	if (!m_bScroll)
	{
		m_nGreen = GetDlgItemInt(IDC_GREEN, 0, FALSE);
		m_gSlider.SetPos(m_nGreen);
	}
	m_cGreen.ChangeColor(RGB(0, m_nGreen, 0));
	UpdateColor();
}

void CPolyDlg::OnChangeBlue(void)
{
	if (!m_bScroll)
	{
		m_nBlue = GetDlgItemInt(IDC_BLUE, 0, FALSE);
		m_bSlider.SetPos(m_nBlue);
	}
	m_cBlue.ChangeColor(RGB(0, 0, m_nBlue));
	UpdateColor();
}



void CPolyDlg::UpdateColor()
{
	COLORREF clr = RGB(m_nRed,m_nGreen,m_nBlue);
	m_cColor.ChangeColor(clr);
	m_pDoc->m_Poly.m_BrushColor = clr;
	m_pDoc->UpdateDrawView();
}
void CPolyDlg::OnClickedTri(void)
{
	m_pDoc->m_Poly.MakeTria();
	m_pDoc->UpdateDrawView();
}

void CPolyDlg::OnClickedPent(void)
{
	m_pDoc->m_Poly.MakePent();
	m_pDoc->UpdateDrawView();
}

void CPolyDlg::OnClickedStar(void)
{
	m_pDoc->m_Poly.MakeStar();
	m_pDoc->UpdateDrawView();
}



CPolyDlg::CPolyDlg(CTreeDoc* p): CDialog(CPolyDlg::IDD,0)
{
	m_pDoc = p;
	m_nPen = p->m_Poly.m_nPenWidth;

	//====== Расщепляем цвет фона текущего полигона
	COLORREF brush = p->m_Poly.m_BrushColor;
	m_nRed		= GetRValue(brush); 	// на три компоненты
	m_nGreen	= GetGValue(brush);
	m_nBlue	= GetBValue(brush);
	m_bScroll	= false;					// Ползунки в покое
}



BOOL CPolyDlg::OnInitDialog()
{
	//====== Загрузка из ресурсов изображений кнопок
	m_cTri.AutoLoad(IDC_TRI,this);
	m_cPent.AutoLoad(IDC_PENT,this);
	m_cStar.AutoLoad(IDC_STAR,this);

	CDialog::OnInitDialog();

	//====== Установка диапазона ползунков
	m_rSlider.SetRange(0,255);
	m_gSlider.SetRange(0,255);
	m_bSlider.SetRange(0,255);

	//====== Установка цены деления ползунков
	m_rSlider.SetTicFreq(50);
	m_gSlider.SetTicFreq(50);
	m_bSlider.SetTicFreq(50);

	//=== Вызов обработчиков для начальной
	//=== закраски окон и установки ползунков
	OnChangeRed();
	OnChangeGreen();
	OnChangeBlue();
	return TRUE;
}



void CPolyDlg::OnClickedOk(void) 
{
	//=== Запоминаем факт отсутствия диалога в Документе
	m_pDoc->m_pPolyDlg = 0;

	//====== Родительская версия вызовет DestroyWindow
	CDialog::OnOK();

	//====== Мы освобождаем память
	delete this;
}



void CTreeDoc::OnEditPolycolor(void)
{
	//====== Если диалог отсутствует
	if (!m_pPolyDlg)
	{
		//====== Создаем его в две ступени
		m_pPolyDlg = new CPolyDlg(this);
		m_pPolyDlg->Create(IDD_POLYCOLOR);
	}
	else
		//===== Иначе делаем активным его окно
		m_pPolyDlg->SetActiveWindow();
}



void CTreeDoc::UpdateDrawView()
{
	//====== Добываем адрес нужного Вида
	CDrawView *pView = dynamic_cast<CDrawView*>
			(GetView(&CDrawView::classCDrawView));
	//====== и просим его перерисоваться с учетом изменений
	if (pView)
		pView->Invalidate();
}




