CTreeDoc* GetDocument() { return dynamic_cast(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(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(>v); //====== Первый узел будет родителем второго gtv.hParent = h1; //====== Атрибуты второго узла gtv.item.iImage = 1; gtv.item.pszText = "Second"; //====== Вставка второго узла h2 = m_Tree.InsertItem(>v); //====== Второй узел будет родителем третьего gtv.hParent = h2; gtv.item.iImage = 2; gtv.item.pszText = "Third"; //====== Вставка третьего узла h3 = m_Tree.InsertItem(>v); 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 > 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 > 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 0) { //====== Пробег по всем его точкам for (UINT i=0; i 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_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; iMapToLogPt(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 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(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 m_pWnds; CTreeDoc* GetDocument() { return dynamic_cast(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; jCreate (m_WndClass, 0, style, r, this, 0); //====== Сдвигаем координаты окна вправо r.OffsetRect (dx,0); } //====== Сдвигаем координаты окна влево и вниз r.OffsetRect(-nCols*dx, dy); } } void CRightView::Clear() { //====== Цикл прохода по всем адресам контейнера for (UINT i=0; iDestroyWindow(); // Освобождение памяти, занимаемой объектами класса 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(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 (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 (GetView(RUNTIME_CLASS(CRightView))); pView->Show(); } void CTreeFrame::OnClose() { //====== Добываем адрес активного документа CTreeDoc *pDoc = dynamic_cast (GetActiveDocument()); pDoc->m_bTreeExist = false; CMDIChildWnd::OnClose(); } void CDrawFrame::OnClose() { CTreeDoc *pDoc = dynamic_cast (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; im_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 (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 (GetView(&CDrawView::classCDrawView)); //====== и просим его перерисоваться с учетом изменений if (pView) pView->Invalidate(); }