Разработка полноценных Windows-приложений
2. Параллельный перенос и масштабирование плоскости
Создадим пункт меню Tools (инструменты). В нем создадим подпункт Parallel Shift (параллельный перенос) и связанную с ним кнопку на панели инструментов, которые переводят программу в режим ожидания параллельного переноса или выводят ее из него. Если программа находится в режиме ожидания параллельного переноса, то нажатие левой кнопки активирует его и программа начинает обрабатывать событие WM_MOUSEMOVE (движение мыши). В соответствии с перемещением курсора, перемещается точка наблюдения. Отпускание левой кнопки мыши деактивирует перенос и возвращает программу в режим ожидания. В данном пункте необходимо добавить обработчики движения мыши, нажатия левой кнопки мыши и ее отпускания. Обработчик движения мыши (важные для этого пункта строки выделены жирным шрифтом):
void CGpView::OnMouseMove(UINT nFlags, CPoint point)
{
CRect cr; //Переменная для хранения размеров клиентского прямоугольника
CString str; //Строка для записи и вывода координат курсора
GetClientRect(&cr); //Получение размеров клиентского прямоугольника
CMainFrame *pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd; //Указатель на
основную рамку (необходимо для обращения к строке состояния)
CStatusBar *pStatus = &pFrame->m_wndStatusBar; //Указатель на строку состояния
(будет отображать координаты курсора)
if(cr.PtInRect(point)) //Если курсор в клиентском прямоугольнике то...
{
SetCapture(); //Захватываем мышь
if(parallel_shift) //Если в режиме ожидания параллельного переноса то...
if(move_camera) //Если перенос актвирован
{
//Сдвигается точка наблюдения на разность между текущим и предыдущим положениями курсора
CameraPoint.x += (static_cast<double>(point.x) - static_cast<double>(MousePosPoint.x))/ScaleXY;
CameraPoint.y -= (static_cast<double>(point.y) - static_cast<double>(MousePosPoint.y))/ScaleXY;
Invalidate(); //Обновление клиентсой области
MousePosPoint = point; //Сохранение положения курсора
}
else SetCursor(AfxGetApp()->LoadCursorA(IDC_CURSOR_PALM)); //Если перенос
не активирован то устанавливается нужный курсор
if(mark_points) SetCursor(LoadCursor(NULL,IDC_CROSS)); //Если в режиме визуального редактирования
то устанавливается соответствующий курсор
}
else //Если курсор вне клиентского прямоугольника то...
{
ReleaseCapture(); //Освобождается мышь
move_camera = false; //Деактивируется перенос
}
if(pStatus) //Если указатель на строку состояния верный то...
{
str.Format("x = %f", SysToLog(point).x); //Запись во вспомогательную строку координаты x
pStatus->SetPaneText(0,str); //Вывод в строку состояния
str.Format("y = %f", SysToLog(point).y); //Запись во вспомогательную строку координаты y
pStatus->SetPaneText(1,str); //Вывод в строку соостояния
}
CView::OnMouseMove(nFlags, point);
}
Листинг
5.2.
MousePosPoint - это переменная типа CPoint, которая служит для хранения координат курсора и объявлена в прототипе класса CGpView с модификатором private. С тем же модификатором объявлены переменные parallel_shift и move_camera типа bool. Первая переменная сигнализирует, нажата ли кнопка параллельного переноса, а вторая - активирован ли перенос (нажата или отпущена левая кнопка мыши). Функция SetCapture() перенаправляет все сообщения мыши на обработку окну, которое ее вызвало. Функция ReleaseCapture() восстанавливает стандартный путь сообщений мыши в операционной системе. Функция SetCursor(...) меняет изображение курсора. Функция LoadCursorA(...), член класса приложения, загружает курсор из ресурсов приложения, вызвавшего ее, и возвращает его описатель, в качестве входного параметра требует его идентификатор. Функция AfxGetApp() возвращает указатель на экземпляр приложения.
Обработчик нажатия левой кнопки мыши:
void CGpView::OnLButtonDown(UINT nFlags, CPoint point) //Обработчик нажатия левой кнопки мыши
{
CRect cr; //Переменная для хранения размеров клиентского прямоугольника
GetClientRect(&cr); //Получение размеров клиентского прямоугольника
if(parallel_shift && cr.PtInRect(point)) //Если в режиме ожидания переноса и курсор в клиетском прямоугольник...
{
MousePosPoint = point; //Сохранение координат нажатия
move_camera = true; //Активация переноса
SetCursor(AfxGetApp()->LoadCursorA(IDC_CURSOR_FIST)); //Устанавливается соответствующий курсор
}
CView::OnLButtonDown(nFlags, point);
}Обработчик отпускания левой кнопки мыши:
void CGpView::OnLButtonUp(UINT nFlags, CPoint point) //Обработчик отпускания левой кнопки мыши
{
CRect cr; //Переменная для хранения размеров клиентского прямоугольника
GetClientRect(&cr); //Получение размеров клиентского прямоугольника
if(parallel_shift && cr.PtInRect(point)) //Если в режиме ожидания переноса и курсор в клиетском прямоугольник ...
{
move_camera = false; //Деактивация переноса
SetCursor(AfxGetApp()->LoadCursorA(IDC_CURSOR_PALM)); //Устанавливается соответствующий курсор
}
CView::OnLButtonUp(nFlags, point);
}Для корректной работы программы создадим и отредактируем соответствующие обработчики для кнопки Parallel Shift. В рамках данного пункта важны строки, выделенные жирным шрифтом. Обработчик пункта меню Parallel Shift и связанной с ним кнопки на панели инструментов:
void CGpView::OnToolsParallelshift()
{
mark_points = false; //Если в режиме визуального редактирования узлов то выход из него
parallel_shift = !parallel_shift; //Вход или выход в режим ожидания переноса
}Обработчик изменения внешнего вида пункта меню Parallel Shift и связанной с ним кнопки на панели инструментов:
void CGpView::OnUpdateToolsParallelshift(CCmdUI *pCmdUI) //Обрабатывает изменение внешнего вида кнопки (пункта меню)
{
pCmdUI->SetCheck(parallel_shift); //Смена статуса (активна или не активна) в зависимости от флага
}Для масштабирования следует обрабатывать прокрутку колеса мыши. В зависимости от направления вращения будет меняться масштабный коэффициент. Обработчик колесика мыши:
BOOL CGpView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
CString str("Updating coordinates. Please move cursor."); //Информативная строка
CMainFrame *pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd; //Получение указатель на основную рамку
CStatusBar *pStatus = &pFrame->m_wndStatusBar; //Получение указатель на строку состояния
if(zDelta > 0) {if(ScaleXY < 200) ScaleXY += 5;} //Если вращение вперед, то увеличивается массштабный коэффициент
else if(ScaleXY > 40) ScaleXY -= 5; //Если вращение назад, то уменьшается масштабный коэффициент
Invalidate(); //Перерисовываем
if(pStatus) //Если указатель на строку состояния истина, то
{
//Вывод информативной строки в строку состояния
pStatus->SetPaneText(0,str);
pStatus->SetPaneText(1,str);
}
return CView::OnMouseWheel(nFlags, zDelta, pt);
}