Визуальное редактирование данных
CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
//====== В режиме создания нового полигона
if (m_bNewPoints)
{
CTreeDoc *pDoc = GetDocument();
//====== Ссылка на массив точек текущего полигона
VECPTSS pts = pDoc->m_Poly.m_Points;
//=== Получаем адрес текущего контекста устройства
CDC *pDC = GetDC() ;
//====== Настраиваем его с учетом размеров окна
SetDC(pDC) ;
//=== Преобразуем аппаратные координаты в логические
pDC->DPtoLP(ipoint);
//=== Преобразуем Page-координаты в World-координаты
CDPoint pt = pDoc->MapToWorldPt(point);
//====== Запоминаем в контейнере
pts.push_back (pt);
}
//====== В режиме готовности к захвату
else if (m_bReady)
{
ra_bLock = true; // Запоминаем состояние захвата
m_bReady = false; // Снимаем флаг готовности
}
//====== В режиме повторного нажатия
else if (mJbLock)
m_bLock = false; // Снимаем флаг захвата
else
//В случае бездумного нажатия
return; // уходим
Invalidated; // Просим перерисовать
}
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
//=== В режиме создания нового полигона не участвуем
if (m_bNewPoints) return;
//====== Получаем и настраиваем контекст
CDC *pDC = GetDCO ;
SetDC(pDC);
//=== Преобразуем аппаратные координаты в логические
pDC->DPtoLP(Spoint);
//=== Преобразуем 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[ra_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, CPointS point)
{
CTreeDoc *pDoc = GetDocument();
//====== Ссылка на массив точек текущего полигона
VECPTS& pts = pDoc->m_Poly.m_Points;
UINT size = pts.sizeO;
//====== Если полигон вырожден, уходим
if (size < 2) return;
//====== Индексы соседних вершин
int il = m_CurID == 0 ? size - 1 : m_CurID - 1;
int 12 = m_CurID == size - 1 ? 0 : m_CurID + 1;
// ====== Берем перо и рисуем две линии
pDC->SelectObject(Sm_penLine);
pDC->MoveTo(pDoc->MapToLogPt(pts[11] ) ) ;
pDC->LineTo(point);
pDC->LineTo(pDoc->MapToLogPt(pts[12]));
}
Определение индекса вершины, к которой достаточно близко подобрался указатель мыши, производится в методе FindPoint класса документа. В случае если степень близости недостаточна, функция возвращает значение -1. Вставьте этот метод в файл реализации класса (TreeDoc.cpp):
int CTreeDoc::FindPoint(CDPointS pt)
{
//====== Пессимистический прогноз
int id = -1;
//====== Поиск среди точек дежуоного полигона
for (UINT 1=0; i<m_Poly.m_Points.size(); i++)
{
//=== Степень близости в World-пространстве.
//=== Здесь мы используем операцию взятия нормы
//=== вектора, которую определили в классе CDPoint
if ( !(m_Poly.m_Points[i) - pt) <= 5e-2)
(
id = i;
break; // Нашли
}
}
//====== Возвращаем результат
return id;
}
В этот момент вы можете запустить приложение, выбрать шаблон Draw и проверить возможности визуального редактирования, перетаскивая вершины звезды в пределах клиентской области окна документа.
Включение или выключение второго режима редактирования, служащего для создания нового полигона и ввода координат вершин с помощью мыши, потребует меньше усилий, так как логика самого режима уже реализована в обработчике нажатия левой кнопки мыши. Для включения или выключения (toggle) второго режима используется одна и та же команда. Создайте обработчик команды Edit > New Poly. Для этого:
Рис. 5.3. Редактируемый полигон
В теле обработчика следует установить флаги состояния, уничтожить все вершины дежурного полигона и перерисовать представление:
void CDrawView::OnEditNewpoly(void)
{
//====== Включаем/Выключаем режим ввода вершин
m_bNewPoints = !m_bNewPoints;
//=== Снимаем флаги редактирования перетаскиванием
m_bReady = false;
m_bLock = false;
//====== Если режим включен, то уничтожаем вершины
if (m_bNewPoints)
{
GetDocument()->m_Poly.m_Points.clear() ;
Invalidate();
}
}
Запустите приложение, выберите шаблон Draw и дайте команду Edit > New Poly. Щелкайте левой кнопкой мыши разные места клиентской области окна и наблюдайте за трансформациями полигона m_Poly при добавлении в контейнер его точек новых значений. Мысленно проследите за преобразованиями координат, которые происходят в эти моменты. Вы помните, что мышь дает аппаратные координаты, а в контейнер попадают World-координаты вершин полигона?