Опубликован: 10.03.2009 | Доступ: свободный | Студентов: 2296 / 280 | Оценка: 4.31 / 4.07 | Длительность: 09:23:00
Лекция 5:

Разработка полноценных 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);
}