Шаг 51.
Библиотека OWL.
Более подробно о классе TDC

    На этом шаге мы рассмотрим программу, использующую класс TDC.

    Поскольку среда Windows является графическим пользовательским интерфейсом, нет, вероятно, ничего удивительного в том, что она насыщена графическими функциями. Можно использовать их не только для рисования линий и простых фигур вроде квадрата или овала, но и создавать сложные растровые изображения, закрашивать любую область на экране любым цветом, отображать пиктограммы, печатать текст и тому подобное. Множеством графических функций среды Windows обеспечивается GDI (Graphics Device Interface - интерфейс графического устройства); разнообразие функций таково, что можно выводить графическую информацию на любое устройство вывода, будь то экран монитора или какой угодно принтер. Библиотека OWL инкапсулирует большую часть функций интерфейса GDI в класс TDC (класс контекста устройства), представляя программистам свободный и удобный доступ к возможностям этой графической библиотеки.

    На предыдущих шагах вы уже встречались с этим классом функций, когда изучали, как описать информационные структуры, относящиеся к контексту устройств (Device Context), как распечатать текст, как нарисовать линию или контур определенной формы. Мы не собираемся полностью задокументировать весь гигантский класс TDC библиотеки OWL, но мы постараемся материалом, изложенным в этих шагах, хотя бы немного расширить круг познанного вами и познакомить вас с некоторыми новыми графическими функциями и приемами программирования.

    Следующий пример является демонстрационной программой, показывающей, как можно использовать многие функции интерфейса GDI, инкапсулированные в OWL-класс TDC.

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\dc.h>
#include "pr51_1.rc"

// Режимы рисования фигур.
enum TShapeMode {mLine, mPie, mEllipse, mRndRect,
mIcon, mBitmap, mFill, mPixel};

// Режим выполнения программы.
enum TOptionMode {manual, automatic};

//Класс приложения.
class TApp: public TApplication
{
  public:
	 TApp():TApplication() {}
	 void InitMainWindow();
};

// Класс главного окна.
class TWndw : public  TFrameWindow
{
  public:
	 TWndw (TWindow *parent, const char far *title);
  protected:
			TShapeMode shape;
			TOptionMode operation;
			TClientDC *clientDC;
			TBrush *brush;
			TBitmap *bitmapl;
			TIcon *GDIIcon;
			int timer;

			void SetupWindow();
			void CleanUpWindow();
			void Paint(TDC&, BOOL, TRect&);
			void CmChangeShape(WPARAM Id);
			void CmManual() ;
			void CmAuto();
			void EvLButtonDown (UINT, TPoint &point);
			void EvRButtonDown (UINT, TPoint&);
			void EvTimer(UINT);
			void EvChar(UINT, UINT, UINT);

			// Переключатели доступности команд.
			void CmEnableShapes (TCommandEnabler &commandEnabler);
			void CmEnableManual (TCommandEnabler &commandEnabler);
			void CmEnableAuto (TCommandEnabler &commandEnabler);
  private:
			void DrawObject (TPoint &point);

			DECLARE_RESPONSE_TABLE(TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
	  EV_WM_LBUTTONDOWN,
	  EV_WM_RBUTTONDOWN,
	  EV_WM_TIMER,
	  EV_WM_CHAR,

	  EV_COMMAND_AND_ID (CM_PIXEL, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_LINES, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_PIE, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_ELLIPSE, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_RNDRECT, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_ICON, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_BITMAP, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_FILL, CmChangeShape),

	  EV_COMMAND (CM_MANUAL, CmManual),
	  EV_COMMAND (CM_AUTO, CmAuto),

	  EV_COMMAND_ENABLE (CM_PIXEL, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_LINES, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_PIE, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_ELLIPSE, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_RNDRECT, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_ICON, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_BITMAP, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_FILL, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_MANUAL, CmEnableManual),
	  EV_COMMAND_ENABLE (CM_AUTO, CmEnableAuto),
END_RESPONSE_TABLE;

// TWndw::TWndw()
// Это конструктор главного окна.
TWndw::TWndw(TWindow *parent, const char far *title):
			 TFrameWindow (parent, title)
{
  // Определить расположение и размеры окна.
  Attr.X = 20;
  Attr.Y = 20;
  Attr.W = 500;
  Attr.H = 400;
  // Добавить меню к окну.
  AssignMenu(MENU_1);
  // Установить пиктограмму приложения.
  SetIcon (GetApplication(), GDIICON);
  // Инициализировать режимы рисования
  // фигуры и выполнения программы.
  shape = mLine;
  operation = manual;
  // Инициализировать генератор случайных чисел.
  randomize();
}

// TWndw::SetupWindow()
// Эта функция, которая переопределяет функцию,
// унаследованную от класса TFrameWindow,
// завершает установку окна.
void TWndw::SetupWindow()
{
  // Всегда вызывать функцию SetupWindow() базового класса. 
  TFrameWindow::SetupWindow();
  // Получить DC (контекст устройства) для окна-
  // клиента. Как правило, вам не следует
  // создавать DC в этом месте программы и затем
  // обращаться к нему из любого места в программе.
  // Однако, для удобства в настоящей программе это
  // важное правило нарушается.
  clientDC = new TClientDC(HWindow);
  // Установить для кисти контекста устройства 
  // светло-зеленый цвет.
  brush = new TBrush(TColor::LtGreen);
  clientDC->SelectObject (*brush);
  // Получить дескриптор экземпляра приложения.
  HINSTANCE instance = *GetApplication();
  // Построить объекты растрового изображения и пиктограммы.
  bitmapl = new TBitmap (instance, BITMAP_1);
  GDIIcon = new TIcon (instance, (TResId)GDIICON);
  // Установить Windows-таймер.
  timer = SetTimer (1, 10, 0);
  if (timer == 0)
		MessageBox ("Нет таймера!", "Ошибка", MB_OK);
}

// TWndw::CleanUpWindow()
// Эта функция переопределяет функцию, унаследованную
// от TFrameWindow. Она выполняет заключительную
// очистку этого окна.
void TWndw::CleanUpWindow()
{
  // Удалить GDI-объекты и DC.
  delete brush;
  delete bitmapl;
  delete GDIIcon;
  delete clientDC;
  // Избавиться от таймера игры. 
  KillTimer(timer);
}

// TWndw::Paint()
// Эта функция, которая реагирует на сообщение
// WM_PAINT, вызывается каждый раз, когда
// необходима перекраска окна пользователя.
void TWndw::Paint(TDC&, BOOL, TRect&)
{
  // Сохранить текущее состояние DC.
  clientDC->SaveDC();
  // Выбрать серую кисть.
  clientDC->SelectStockObject(GRAY_BRUSH);
  // Получить размер области пользователя. 
  TRect rect = GetClientRect();
  // Заполнить область пользователя серым прямоугольником. 
  clientDC->Rectangle(rect);
  // Восстановить DC в исходное состояние.
  clientDC->RestoreDC();
}

// TWndw::CmChangeShape()
// Эта функция реагирует на любую команду в меню Shape,
// заменяя текущий режим рисования фигуры режимом, 
// выбранным пользователем.
void TWndw::CmChangeShape(WPARAM Id)
{
  // Установить текущий режим фигуры на 
  // основании выбора пользователя.
  switch (Id)
  {
    case CM_PIXEL  : shape = mPixel; break;
    case CM_LINES  : shape = mLine;  break;
    case CM_PIE    : shape = mPie;   break;
    case CM_ELLIPSE: shape = mEllipse; break;
    case CM_RNDRECT: shape = mRndRect; break;
    case CM_ICON   : shape = mIcon;  break;
    case CM_BITMAP : shape = mBitmap;break;
    case CM_FILL   : shape = mFill;  break;
  }
}

// TWndw::CmManual()
// Эта функция вызывается, когда пользователь выбирает 
// команду Manual из меню Option, устанавливая 
// ручной режим выполнения программы.
void TWndw::CmManual()
{
  operation = manual;
}

// TWndw::CmAuto()
// Эта функция вызывается, когда пользователь 
// выбирает команду Automatic из меню Option, 
// устанавливая автоматический режим выполнения программы.
void TWndw::CmAuto()
{
  operation = automatic;
}

// TWndw::EvLButtonDown()
// Эта функция реагирует на нажатия левой клавиши 
// мыши при рисовании текущей фигуры при 
// выполнении программы в ручном режиме.
void TWndw::EvLButtonDown(UINT, TPoint &point)
{
  if (operation == manual) DrawObject (point);
}

// TWndw::EvRButtonDown()
// Эта функция реагирует на нажатие правой клавиши 
// мыши, вызывая Invalidate для стирания всего 
// пользовательского окна.
void TWndw::EvRButtonDown(UINT, TPoint&)
{
  Invalidate();
}

// TWndw::EvChar()
// Эта функция реагирует на сообщение WM_CHAR,
// которое Windows посылает в окно при нажатии
// пользователем любой клавиши.
void TWndw::EvChar(UINT, UINT, UINT)
{
  Invalidate ();
}

// TWndw::EvTimer()
// Эта функция реагирует на сообщение WM_TIMER.
void TWndw::EvTimer(UINT)
{
  // Рисует объект только при выполнении программы
  // в автоматическом режиме, и если окно не минимизировано.
  if ((operation == automatic) && (!IsIconic()))
  {
    // Получить размер рабочей области окна.
	 TRect rect = GetClientRect();
    // Вычислить случайное положение на экране. 
    int xpos = random(rect.right); 
    int ypos = random(rect.bottom);
    // Рисует объект.
    DrawObject (TPoint(xpos, ypos));
  }
}

// TWndw::DrawObject()
// Эта функция рисует текущую выбранную фигуру в
// точке с координатами, заданными в объекте TPoint.
void TWndw::DrawObject(TPoint  &point) 
{
  BITMAP  bm;
  TColor color;
  switch (shape) 
  {
    case mPixel   : // Окрасить пиксель в данной точке. 
      clientDC->SetPixel (point, TColor::LtRed); break;
	 case mLine    : // Провести линию к точке.
      clientDC->LineTo (point); break;
    case mPie     : // Рисовать в данной точке круговую диаграмму. 
      clientDC->Pie (point.x, point.y, point.x+40, point.y+40, 
              point.x+40, point.y+20, point.x+20, point.y+40); break;
    case mEllipse : // Рисовать в данной точке эллипс. 
      clientDC->Ellipse (point.x, point.y, point.x+40, point.y+30); break;
    case mRndRect : // Рисовать в данной точке
                    // прямоугольник со скругленными углами. 
      clientDC->RoundRect (point.x, point.y, point.x+40, 
                         point.y+30, 20, 20); break;
    case mFill    :   //  Получить цвет в данной точке.
      color  = clientDC->GetPixel (point); 
                      // Закрасить область вокруг точки. 
      clientDC->ExtFloodFill (point, color, FLOODFILLSURFACE); break;
    case mIcon    :   //  Рисовать  пиктограмму в данной точке.
      clientDC->DrawIcon(point, *GDIIcon); break;
    case mBitmap  :  // Прочитать контекст устройства 
                     // для памяти, основанный на
                     // контексте окна пользователя. 
      TMemoryDC memDC (*clientDC); 
                     // Выбрать растровое изображение 
                     // в контекст устройства памяти. 
      memDC.SelectObject (*bitmapl); 
                     // Получить атрибуты растрового изображения. 
      bitmapl->GetObject (bm); 
                     // Отобразить растровое 
                     // изображение в данной точке. 
      clientDC->BitBlt(point.x, point.y, bm.bmWidth, 
                bm.bmHeight, memDC, 0,0, SRCCOPY);
  }
}

// TWwndw::CmEnableShapes()
// Это переключатель доступности команд для всех пунктов
// меню Shapes. Он обеспечивает маркер проверки для
// правильных пунктов меню.
void TWndw::CmEnableShapes (TCommandEnabler &commandEnabler)
{
  TShapeMode shapeMode;
  switch (commandEnabler.Id)
  {
    case CM_PIXEL  : shapeMode = mPixel; break;
    case CM_LINES  : shapeMode = mLine;  break;
    case CM_PIE    : shapeMode = mPie;   break;
    case CM_ELLIPSE: shapeMode = mEllipse; break;
    case CM_RNDRECT: shapeMode = mRndRect; break;
    case CM_ICON   : shapeMode = mIcon;  break;
    case CM_BITMAP : shapeMode = mBitmap;break;
    case CM_FILL   : shapeMode = mFill;  break;
  }
  if (shape == shapeMode)
      commandEnabler.SetCheck(TCommandEnabler::Checked); 
  else 
      commandEnabler.SetCheck(TCommandEnabler::Unchecked);
}

// TWwndw::CmEnableManual()
// Это переключатель доступности для команды Manual из 
// меню Option. Он обеспечивает маркер проверки пункту 
// Manual, когда программа выполняется в ручном режиме. 
void TWndw::CmEnableManual (TCommandEnabler &commandEnabler)
{
  if (operation == manual)
        commandEnabler.SetCheck(TCommandEnabler::Checked); 
  else
        commandEnabler.SetCheck(TCommandEnabler::Unchecked);
} 

// TWwndw::CmEnableAuto()
// Это переключатель доступности для команды Automatic
// из меню Option. Он обеспечивает маркер проверки
// пункту Automatic, когда программа выполняется
// в автоматическом режиме.
void TWndw::CmEnableAuto (TCommandEnabler &commandEnabler)
{
  if (operation == automatic)
       commandEnabler.SetCheck(TCommandEnabler::Checked); 
  else commandEnabler.SetCheck(TCommandEnabler::Unchecked);
}

void TApp::InitMainWindow()
{
  TFrameWindow *wndw = new TWndw (0,"Приложение Демо-GDI");
  SetMainWindow(wndw);
}

int OwlMain(int,char *[])
{
  return TApp().Run();
}

    Файл ресурсов:

#ifndef WORKSHOP_INVOKED
#include "windows.h"
#endif

#define GDIICON         300
#define BITMAP_1        301
#define MENU_1          100
#define CM_PIXEL        201
#define CM_LINES        202
#define CM_PIE          203
#define CM_ELLIPSE      204
#define CM_RNDRECT      205
#define CM_ICON         206
#define CM_BITMAP       207
#define CM_FILL         208
#define CM_MANUAL       301
#define CM_AUTO         302
#define CM_EXIT         24310

#ifdef RC_INVOKED


MENU_1 MENU
{
  POPUP "&File"
  {
	  MENUITEM "E&xit", CM_EXIT
  }
  POPUP "&Shape"
  {  
          MENUITEM "&Pixel", CM_PIXEL 
          MENUITEM "&Lines", CM_LINES 
          MENUITEM "P&ie",   CM_PIE 
          MENUITEM "&Ellipse", CM_ELLIPSE 
          MENUITEM "&Round Rect", CM_RNDRECT 
          MENUITEM "&Icon", CM_ICON 
          MENUITEM "&Bitmap", CM_BITMAP 
          MENUITEM "&Fill", CM_FILL
  }
  POPUP "&Options"
  {
          MENUITEM "&Manual", CM_MANUAL 
          MENUITEM "&Automatic", CM_AUTO
  }
}

BITMAP_1 BITMAP "100130.bmp" 
GDIICON ICON "doc.ico"

#endif
Текст этого приложения вместе с дополнительными файлами можно взять здесь.

    Когда вы запустите эту программу, перед вами появится окно, показанное на рисунке 1.


Рис.1. Результат работы приложенияя

    В этом окне содержатся три пункта меню. В меню File содержится только команда Exit. В меню Shape содержатся команды для изменения формы фигуры, которую будет рисовать программа. Вы можете также использовать это меню для включения режима Fill (заливка). В ручном режиме для того, чтобы нарисовать фигуру или выполнить ее заливку, вам надо нажать кнопку мыши внутри окна. В автоматическом режиме программа постоянно отображает текущий выбранный контур в случайном месте внутри окна. (На рисунке 2 показано выполнение программы в ручном режиме, а на рисунке 3 программа выполняется в автоматическом режиме.)


Рис.2. Программа в ручном режиме


Рис.3. Программа в автоматическом режиме

    Содержимое окна можно стереть в любой момент, нажав правую кнопку мыши внутри окна, или нажав на любую клавишу клавиатуры.

    Посмотрев на текст программы, вы заметите, что, как обычно, программа начинается с включения заголовочных файлов. Затем программа определяет два перечисляемых типа, которые представляют текущий тип фигуры и режим работы программы. Тип фигуры определяет, какую фигуру следует рисовать, а режим работы программы указывает, какой режим выполнения установлен - ручной (manual) или автоматический (automatic).

    Эта программа включает довольно большой класс главного окна. Этот класс начинается объявлением некоторых защищенных переменных.

 			TShapeMode shape;
			TOptionMode operation;
			TClientDC *clientDC;
			TBrush *brush;
			TBitmap *bitmapl;
			TIcon *GDIIcon;
			int timer;

где: shape - содержит текущий тип фигуры, operation - содержит текущий режим выполнения программы, clientDC - указатель на контекст устройства для этой программы, brush - указатель на стандартную кисть для настоящей программы, bitmapl - указатель на объект растрового изображения настоящей программы, GDIICON - указатель на пиктограммы этой программы и timer - идентификатор таймера Windows.

    Помимо конструктора класс главного окна этой программы содержит несколько защищенных функций-членов:

  public:
	 TWndw (TWindow *parent, const char far *title);
  protected:
			void SetupWindow();
			void CleanUpWindow();
			void Paint(TDC&, BOOL, TRect&);
			void CmChangeShape(WPARAM Id);
			void CmManual() ;
			void CmAuto();
			void EvLButtonDown (UINT, TPoint &point);
			void EvRButtonDown (UINT, TPoint&);
			void EvTimer(UINT);
			void EvChar(UINT, UINT, UINT);

    С функциями SetupWindow() и Paint() вы неоднократно встречались ранее. Согласно этому CleanUpWindow () является зеркальным отражением SetupWindow(). Точно так же, как вы используете SetupWindow() для выполнения задачи инициализации, которая должна быть выполнена, когда окно получает корректный дескриптор, вы используете CleanUpWindow() для выполнения процедур очистки окна.

    К другим защищенным функциям-членам этого класса относятся функциия отклика CmChangeShape(), CmManual(), CmAuto(), которые реагируют на команды меню Shape и Options. Также класс включает функции отклика на сообщения WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_TIMER и WM_CHAR.

    Для того, чтобы к выбранным пунктам меню добавить индикаторы состояния, в классе TWndw имеются три переключателя доступности команд:

			void CmEnableShapes (TCommandEnabler &commandEnabler);
			void CmEnableManual (TCommandEnabler &commandEnabler);
			void CmEnableAuto (TCommandEnabler &commandEnabler);

    Первый переключатель доступности команд управляет всеми пунктами меню Shape. Предыдущие переключатели доступности управляли только одним-единственным пунктом меню; это касается и переключателей доступности данного класса CmEnableManual() и CmEnableAuto().

    Далее TWndw объявляет частную (private) функцию-член DrawObject() и таблицу откликов класса. Функция DrawObject() рисует текущую выбранную фигуру:

  private:
			void DrawObject (TPoint &point);

			DECLARE_RESPONSE_TABLE(TWndw);

    Наконец, программа определяет таблицу откликов TWndw, которая сама по себе внушительна; в самом деле:

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
	  EV_WM_LBUTTONDOWN,
	  EV_WM_RBUTTONDOWN,
	  EV_WM_TIMER,
	  EV_WM_CHAR,

	  EV_COMMAND_AND_ID (CM_PIXEL, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_LINES, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_PIE, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_ELLIPSE, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_RNDRECT, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_ICON, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_BITMAP, CmChangeShape),
	  EV_COMMAND_AND_ID (CM_FILL, CmChangeShape),

	  EV_COMMAND (CM_MANUAL, CmManual),
	  EV_COMMAND (CM_AUTO, CmAuto),

	  EV_COMMAND_ENABLE (CM_PIXEL, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_LINES, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_PIE, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_ELLIPSE, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_RNDRECT, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_ICON, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_BITMAP, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_FILL, CmEnableShapes),
	  EV_COMMAND_ENABLE (CM_MANUAL, CmEnableManual),
	  EV_COMMAND_ENABLE (CM_AUTO, CmEnableAuto),
END_RESPONSE_TABLE;

    Эта таблица начинается с элементов EV_WM_LBUTTONDOWN, EV_WM_RBUTTONDOWN и EV_WM_TIMER, котрые, если вы помните, автоматически связывают Windows-сообщения WM_LBUTTONDOWN, WM_RBUTTONDOWN и WM_TIMER c функциями отклика на сообщения EvLButtonDown(), EvRButtonDown() и EvTimer(). Элемент таблицы EV_WM_CHAR является новым и сообщает OWL о том, что каждый раз, когда пользователь нажимает на клавишу клавиатуры, должна вызываться функция EvChar().

    После этих элементов вы видите нечто новое: последовательность элементов таблицы откликов вида EV_COMMAND_AND_ID. Элемент EV_COMMAND_AND_ID работает так же, как и элемент EV_COMMAND, за тем исключением, что он передает в функцию отклика слово WPARAM сообщения. В данном случае WPARAM содержит идентификатор команды. Используя элементы EV_COMMAND_AND_ID таблицы откликов, вы можете располагать единственной функцией, которая отвечает на многие команды. В этой программе все команды в меню Shape обрабатываются функцией отклика на команду CmChangeShape() . Восемь элементов таблицы EV_COMMAND_AND_ID связывают идентификаторы пунктов меню с функцией CmChangeShape().

    Следующая группа элементов таблицы представляет собой совокупность стандартных элементов EV_COMMAND_ENABLED для пунктов меню Options. После них следуют элементы EV_COMMAND_ENABLED для каждой команды в меню Shape и Options. Отметим, что все команды в меню Shape управляются одним и тем же переключателем доступности команд. Немного далее вы увидите, как все это работает. Команды CM_MANUAL и CM_AUTO имеют свои собственные переключатели доступности.

    Достаточно для краткого обзора класса главного окна. Теперь рассмотрим конструктор класса.

TWndw::TWndw(TWindow *parent, const char far *title):
			 TFrameWindow (parent, title)
{
  // Определить расположение и размеры окна.
  Attr.X = 20;
  Attr.Y = 20;
  Attr.W = 500;
  Attr.H = 400;
  // Добавить меню к окну.
  AssignMenu(MENU_1);
  // Установить пиктограмму приложения.
  SetIcon (GetApplication(), GDIICON);
  // Инициализировать режимы рисования
  // фигуры и выполнения программы.
  shape = mLine;
  operation = manual;
  // Инициализировать генератор случайных чисел.
  randomize();
}

    После установки размеров окна и его позиционирования конструктор присваивает этому окну меню и устанавливает пиктограмму. Эта пиктограмма появляется на рабочем столе Windows, когда пользователь минимизирует приложение.

    Далее программа устанавливает тип фигуры и режим выполнения программы таким образом, что программа запускается в ручном режиме и рисует линии. Наконец, инициализируется генератор случайных чисел, обеспечивая создание последовательностей случайных чисел при каждом запуске программы пользователем (это не говорит о том, что последовательность чисел много значит для программы).

    Функция рассматриваемого класса SetupWindow(), которая переопределяет функцию SetupWindow() базового класса, производит окончательную установку:

void TWndw::SetupWindow()
{
  // Всегда вызывать функцию SetupWindow() базового класса. 
  TFrameWindow::SetupWindow();
  // Получить DC (контекст устройства) для окна-
  // клиента. Как правило, вам не следует
  // создавать DC в этом месте программы и затем
  // обращаться к нему из любого места в программе.
  // Однако, для удобства в настоящей программе это
  // важное правило нарушается.
  clientDC = new TClientDC(HWindow);
  // Установить для кисти контекста устройства 
  // светло-зеленый цвет.
  brush = new TBrush(TColor::LtGreen);
  clientDC->SelectObject (*brush);
  // Получить дескриптор экземпляра приложения.
  HINSTANCE instance = *GetApplication();
  // Построить объекты растрового изображения и пиктограммы.
  bitmapl = new TBitmap (instance, BITMAP_1);
  GDIIcon = new TIcon (instance, (TResId)GDIICON);
  // Установить Windows-таймер.
  timer = SetTimer (1, 10, 0);
  if (timer == 0)
		MessageBox ("Нет таймера!", "Ошибка", MB_OK);
}

    Сначала эта функция вызывает функцию базового класса SetupWindow(), чтобы обеспечить соответствующую иницилизацию родительского окна. Затем SetupWindow() создает контекст устройства для окна пользователя и устанавливает для кисти этого контекста устройства светло-зеленый цвет. На следующем этапе SetupWindow() конструирует объекты растрового изображения и пиктограммы программы, разрешая их раскрашивание в окне.

    OWL обеспечивает классы как для растровых изображений, так и для пиктограмм. В классе TBitmap содержатся десять различного рода конструкторов, позволяющих вам создавать объект растрового изображения из уже загруженного растрового изображения, из уже существующего растрового объекта, из какого-либо объекта в буфере обмена (Clipboard), из структуры BITMAP, заданных ширины и высоты, из растрового изображения, из аппаратно-независимого растрового изображения, из идентификатора ресурса растрового изображения и т.д.

    В OWL-классе TIcon также имеется аналогичный набор конструкторов. В рассмотренном выше фрагменте текста программы первым аргументом является дескриптор приложения, а вторым - идентификатор ресурса пиктограммы. Помимо конструкторов в классах TBitmap и TIcon имеются функции-члены для обработки объектов. Например, в классе TBitmap имеются функции для поиска ширины растровых изображений и их высоты, (Width() и Height() соответственно). В классе TIcon содержатся функция GetIconInfo(), которая обеспечивает возврат информации о пиктограмме, и оператор HICON() для преобразования дескриптора объекта к дескриптору типа HICON.

    Последнее, что делает функция SetupWindow() - это инициализация таймера Windows. Этот таймер в текущей программе определяет частоту, с которой программа рисует контуры при ее установке в автоматический режим.

    Как отмечалось выше при описании класса TWndw, функция CleanUpWindow() является зеркальным отражением SetupWindow():

void TWndw::CleanUpWindow()
{
  // Удалить GDI-объекты и DC.
  delete brush;
  delete bitmapl;
  delete GDIIcon;
  delete clientDC;
  // Избавиться от таймера игры. 
  KillTimer(timer);
}

    Когда вызывается функция CleanUpWindow(), дескриптор окна все еще остается в силе, поэтому этот момент является весьма подходящим для выполнения очистки этого окна; в то же время деструктор окна лучше всего использовать для удаления С++-объектов. В настоящей версии CleanUpWindow() программа удаляет свою кисть, растровое изображение, пиктограмму и контекст устройства, а также удаляет таймер Windows. Вам нет необходимости вызывать функцию базового класса CleanUpWindow(), так как она не оказывает вашему окну никаких услуг. Эта функция - всего лишь место для хранения производных классов, которые будут переопределяться. Функция Paint() в этой программе несколько необычна:

void TWndw::Paint(TDC&, BOOL, TRect&)
{
  // Сохранить текущее состояние DC.
  clientDC->SaveDC();
  // Выбрать серую кисть.
  clientDC->SelectStockObject(GRAY_BRUSH);
  // Получить размер области пользователя. 
  TRect rect = GetClientRect();
  // Заполнить область пользователя серым прямоугольником. 
  clientDC->Rectangle(rect);
  // Восстановить DC в исходное состояние.
  clientDC->RestoreDC();
}

    Как правило, функция Paint() использует свой собственный контекст устройства, который OWL автоматически посылает в качестве первого параметра этой функции. Однако в контексте устройства раскрашивания уже установлен отсекающий прямоугольник; другими словами, при использовании такого контекста программа может выполнять рисование только внутри этого отсекающего прямоугольника. В этой программе функция Paint() получает вызов не только тогда, когда окно меняет размер, но также и тогда, когда пользователь активизирует какое-либо меню при выполнении программы в автоматическом режиме. Это происходит из-за того, что, хотя меню и блокирует часть области пользователя окна, фигуры все равно можно поместить в эту область. Если фигура помещается в область позади меню, когда пользователь закрывает это меню, Windows формирует сообщение WM_PAINT. Однако, так как отсекающий прямоугольник уже задан и существует в тот момент, когда программа перерисовывает задний план в том месте, в котором находилось меню, то в результате вы получите на экране пустой прямоугольник. Чтобы избежать этой неприятности, программа должна перерисовывать весь задний план, когда происходит перекрашивание рабочей области. Чтобы проделать это, не заботясь об отсекающем прямоугольнике, функция Paint() использует контекст clientDC настоящей программы, а не тот DC, который был послан функции Paint().

    Сначала Paint() вызывает функцию объекта контекста устройства DC SaveDC(), которая сохраняет текущее состояние контекста в стеке. Затем функция DC-объекта SelectStockObject() выбирает в контексте устройства системную серую кисть. Обращение к функции GetClienTRect(), унаследованной от TWindow, получает размер рабочей области окна.

    Затем функция Rectangle() выполняет рисование прямоугольника, который перекрывает всю область пользователя, стирая все внутри окна. Наконец, функция объекта контекста RestoreDC(), зеркальное отражение функции SaveDC(), восстанавливает контекст устройства в его исходное состояние. Другими словами, кисть снова становится зеленой.


    Замечание. Windows обеспечивает набор готовых GDI-объектов для использования в Windows. Эти готовые объекты, которые представлены константами BLACK_BRUSH, DKGRAY_BRUSH, GRAY_BRUSH, HOLLOW_BRUSH, LTGRAY_BRUSH, NULL_BRUSH, WHITE_BRUSH, BLACK_BRUSH, NULL_PEN, WHITE_PEN, ANSI_FIXED_FONT, ANSI_VAR_FONT, DEVICE_DEFAULT_FONT, OEM_FIXED_FONT и SYSTEM_FONT, полностью готовы для использования в вашем приложении. Вам нет необходимости создавать эти объекты, вам также не надо пытаться их удалять. В программе Windows к любому из этих объектов доступ обеспечивается путем обращения к функции GetStockObject(), чтобы получить дескриптор объекта, и последующего обращения к функции SelectObject() для выделения объекта в контексте устройства. Однако, ObjectWindows объединяет оба этих обращения к функциям в функции SelectStockObject() класса TDC.

    Когда пользователь выбирает из меню Shape какую-нибудь команду, функция CmChangeShape() класса TWndw получает вызов:

void TWndw::CmChangeShape(WPARAM Id)
{
  // Установить текущий режим фигуры на 
  // основании выбора пользователя.
  switch (Id)
  {
    case CM_PIXEL  : shape = mPixel; break;
    case CM_LINES  : shape = mLine;  break;
    case CM_PIE    : shape = mPie;   break;
    case CM_ELLIPSE: shape = mEllipse; break;
    case CM_RNDRECT: shape = mRndRect; break;
    case CM_ICON   : shape = mIcon;  break;
    case CM_BITMAP : shape = mBitmap;break;
    case CM_FILL   : shape = mFill;  break;
  }
}

    Эта функция - не что иное, как оператор switch, который для установки программы в подходящий режим рисования контура использует идентификаторы команд меню. Этот режим - один из режимов TShapeMode, перечисленных в начале текста программы. Для каждого из контуров, который может рисовать программа, существует один режим, включая и операцию заливки.

    Аналогично устанавливается режим выполнения программы; исключением являются две команды из меню Options, каждая из которых имеет свою собственную функцию ответа на команду:

void TWndw::CmManual()
{
  operation = manual;
}

void TWndw::CmAuto()
{
  operation = automatic;
}

    Режимы выполнения программы, так же как и режимы рисования, находятся в начале текста программы. Когда программа установлена в ручной режим выполнения, пользователь, для того, чтобы нарисовать контур в окне, должен нажать кнопку мыши. В автоматическом режиме программа рисует выбранный контур в случайных местах экрана, в ответ на каждое посылаемое программе событие WM_TIMER.

    Когда пользователь нажимает левую кнопку мыши, OWL обращается к функции EvLButtonDown():

void TWndw::EvLButtonDown(UINT, TPoint &point)
{
  if (operation == manual) DrawObject (point);
}

    Эта функция сначала проверяет установку программы в режим ручного управления. Если программа в этот режим не установлена, то функция EvLButtonDown() бездействует. Если ручной режим установлен, то эта функция вызывает функцию DrawObject() для рисования нужного контура по координатам, хранящимся в point; point содержит координаты точки, в которой пользователь нажимает клавишу мыши.

    Правая клавиша мыши выполняет совсем другую задачу. Когда пользователь нажимает правую клавишу мыши, EvRButtonDown() начинает действовать:

void TWndw::EvRButtonDown(UINT, TPoint&)
{
  Invalidate();
}

    Эта функция объявляет всю рабочую область в окне областью обновления, вызывая функцию Invalidate(). При этом Windows посылает приложению сообщение WM_PAINT, на которое OWL реагирует вызовом функции окна Paint() для стирания всей области пользователя.

    Пользователь может также стереть свою область путем нажатия любой клавиши на клавиатуре; так как нажатие клавиатурной клавиши формирует сообщение WM_CHAR из Windows, программа может выполнить очистку окна с помощью функции EvChar(), которую OWL автоматически вызывает, если в таблице откликов окна содержится элемент таблицы EV_WH_CHAR:

void TWndw::EvChar(UINT, UINT, UINT)
{
  Invalidate ();
}

    Как видно, функция EvChar() проделывает в точности такие же действия, что и функция EvRButtonDown(), аннулируя область пользователя окна; это приводит к вызову OWL-функции окна Paint(). Однако, функция EvChar() получает иной набор аргументов, чем функция Paint(). Этими аргументами являются нажатая клавиша, количество нажатий этой клавиши и флаги этой клавиши.

    На следующем шаге мы продолжим разбирать приведенную на этом шаге программу.




Предыдущий шаг Содержание Следующий шаг