Развитие
класса документа
Теперь, когда
мы имеем вспомогательные классы (CDPoint и CPolygon), можно подумать о структуре
данных класса CTreeDoc. Нам понадобятся:
- массив (контейнер) полигонов,
которые соответствуют файлам документов, обнаруженных в текущем каталоге;
- массив строк текста с
файловыми путями этих документов;
- один «дежурный» полигон,
который в данный момент редактируется, то есть выбран для демонстрации в окне
третьего представления (CDrawView);
- размеры документа в логической
системе координат (Page space);
- коэффициент увеличения
размеров при переходе из World в Page-пространство.
Кроме этого,
нам понадобятся методы для управления тремя окнами: CLef tview, CRightView и
CDrawView. Последний класс будет управлять окном, в котором полигон может быть
отредактирован. Этот класс надо еще создать. Замените существующий интерфейс
класса CTreeDoc на тот, который приведен ниже. Здесь мы также провели упрощение
начальной заготовки по схеме, обсуждавшейся выше:
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
OINT
m_nLogZoom;
//======
Флаг: открыто окно типа CTreeFrame
bool
m_bTreeExist;
//=====Флаг:
открыто окно типа CDrawFrame
bool
m_bDrawExist;
//======
Новые методы класса документ =====//
//======
Поиск нужного представления
CView*
GetViewfconst CRuntimeClass* pClass);
//======
Создание нужного представления
bool
MakeViewO ;
//======
Преобразование координат World -> Page
CPoint
MapToLogPt(CDPointS pt);
//======
Преобразование координат Page -> World
CDPoint
MapToWorldPt(CPolntS pt) ;
//=====
Перерисовка окна редактирования
void
UpdateDrawView();
//
Чтение найденных документов и их демонстрация
void
ProcessDocs();
//======
Освобождение контейнеров
void
FreeDocs();
//======
Поиск выбранной точки
int
FindPoint(CDPointS pt) ;
//
Overrides
public:
virtual
BOOL OnNewDocument();
virtual
void Serialize(CArchiveS ar) ;
//
Generated message map functions
protected:
DECLARE_MESSAGE_MAP()
);
Некоторым из
данных документа можно присвоить значения по умолчанию. Обычно это делается
в конструкторе класса. Зададимся неким произвольным размером (2000 х 2000) документа
в логической (Page) системе координат. Чем больше эта область, тем точнее будут
отражены детали конструкции, так как вещественные (World) координаты претерпят
округление при приведении к целым (Page) координатам. Вспоминая, что две из
наших тестовых фигур имеют габариты в 2 единицы в пространстве World, определяем
коэффициент увеличения m_nLogZoom = 700. В этом случае габариты фигур в пространстве
Page будут равны 1400 единиц, то есть они целиком поместятся в области документа.
Выбрав произвольные начальные цвета фигуры и учтя соображения относительно установки
обратного указателя, получим следующую версию конструктора класса CTreeDoc:
CTreeDoc::CTreeDoc()
: m_szDoc(2000,2000), m_Poly()
{
//======
Установка обратного указателя и
//======
атрибутов дежурного полигона
m_Poly.Set(
this,
RGB(240,255,250), RGB(0,96,0), 2);
m_nLogZoom
= 700;
}
Деструктор
класса должен освобождать память, занимаемую динамическими структурами, входящими
в состав класса. Метод FreeDocs мы создадим позже, поэтому при проверочных компиляциях
проекта либо создайте заглушку — пустое тело функции FreeDocs, либо временно
вставляйте символы комментария в строке с вызовом отсутствующей функции:
CTreeDoc::~CTreeDoc()
{
FreeDocs
() ;
m_Poly
.m_Points . clear () ;
}
Устойчивость
данных документа обеспечивается функцией Serialize, и в стартовой заготовке
класса уже есть этот метод. Его тело содержит схему сериализа-ции, но не имеет
конкретных кодов записи или чтения данных из архива. Мы должны наполнить заготовку
кодами так, чтобы документ мог полностью сохранить или восстановить свое состояние.
Нет необходимости сохранять абсолютно все данные документа, так как некоторые
из них носят тактический (временный) характер. Они заданы по умолчанию и не
будут изменяться, например m_szDoc или m_nLogZoom. С долговременными данными
документа мы отождествляем текущий или дежурный полигон m_Poly, который по легенде
отражает выбранную и редактируемую в данный момент конструкцию. Он должен полностью
изменить свои данные при выборе пользователем одной из картинок в окне правого
представления. С этим окном мы связываем контейнер полигонов m_Shapes, который
тоже носит временный характер, так как уничтожается и вновь создается при переходе
из одной папки в другую и лишь помогает пользователю осуществить выбор. Таким
образом, сериализацию документа мы отождествляем с сериали-зацией дежурного
полигона. Поэтому тело функции Serialize выглядит весьма просто:
void
CTreeDoc: : Serialize
(CArchivei ar) {
//
Просим объект выполнить сериализацию самостоятельно
m_Poly.
Serialize (ar) ;
if
(ar.IsStoringO ) {
//
Здесь помещается код записи "обычных" данных }
else
{
//
Здесь помещается код чтения "обычных" данных
Мы могли бы
поместить в ветви условного оператора такой код: ar « m_szDoc « m_nLogZoom;
ar » m_szDoc » m_nLogZoom; но тогда для обработки документов, расположенных
в текущей папке, было бы необходимо поддерживать динамический контейнер объектов
CTreeDoc. Чтение документов сводилось бы к вызову Serialize для каждого из них.
Такое решение будет более громоздким, чем поддержка контейнера полигонов. Поэтому
мы оставляем ветви условного оператора пустыми.
Продолжая развитие
темы преобразования координат, создадим тело функции MapToLogPt, которая, получив
на входе точку с вещественными World-координатами, возвращает точку с целыми
координатами в пространстве Page. В коде этой функции мы помещаем центр симметрии
фигуры (точку с координатами
CDPoint(0,0))
в центр логической области, отведенной для документа, увеличиваем координаты
и преобразуем их к целому типу:
CPoint
CTreeDoc::MapToLogPt(CDPointS pt) {
{
//======
Растяжение и сдвиг
int
x = m_szDoc.cx/2 +
int(m_nLogZoom
* pt.x), у = m_szDoc.cy/2 +
int(m_nLogZoom
* pt.y);
return
CPoint(x,y);
+}
Введите также
функцию обратного преобразования координат, которая, получив на входе точку
с целыми координатами в пространстве Page, вычисляет соответствующую ей точку
с вещественными координатами в пространстве World:
CDPoint
CTreeDoc::MapToWorldPt(CPointS pt)
{
//======
Обратные операции
double
x = double(
pt.x - m_szDoc.cx/2) / m_nLogZoom,
у
=
double(
pt.y - m_szDoc.cy/2) / m_nLogZoom;
return
CDPoint(x, y);
}
В настоящий
момент, если закомментировать вызовы FreeDocs и ProcessDocs в теле деструктора
и функции OnSelchanged класса CLef tview, то вы можете запустить приложение,
с тем чтобы устранить возможные ошибки. Но пока никакой новой функциональности
оно не обнаружит, так как мы еще не занимались созданием и управлением других
представлений его документа. Нам вновь придется вернуться к классу документ,
но только после того, как будут разработаны классы связанных с ним представлений.