Шаг 63.
Библиотека OWL.
Работа с растровыми изображениями с помощью буфера обмена

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

    Несмотря на то, что в буфер обмена и из него передаются в основном данные в формате CF_TEXT, над растровыми изображениями, имеющими формат CF_BITMAP также можно выполнять операции cut и paste. И работа с растровыми изображениями с помощью буфера обмена ничуть не сложнее, чем с текстом, особенно когда вы используете OWL. Основы те же самые. Вы просто должны уяснить, как обращаться с растровыми изображениями в памяти. Здесь мы вспомним о том, что узнали раньше, а также узнаем кое-что новое об объектах TBitmap.

    В следующем примере приведен текст программы, в основу которой положена программа для рисования, рассмотренная на шаге 58. Программа позволяет вам рисовать на экране контуры, а затем вырезать из них участки и помещать их в буфер обмена.

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\editfile.h> 
#include <owl\dc.h> 
#include "pr63_1.rc"
 
// Класс приложения.
class TApp: public TApplication
{
  public:
	 TApp():TApplication() {}
	 void InitMainWindow();
};

// Класс окна-клиента.
class TCWndw : public TWindow
{
  public:
          TCWndw (TWindow *parent, const char far *title);
  protected:
          BOOL bitmapInClip;
          int lButton, rButton, paste;
          TPen *newPen;
          TClientDC *lineDC, *rectDC;
          TRect rect;

          void EvSetFocus (HWND);
          void EvLButtonDown (UINT, TPoint &point);
          void EvLButtonUp (UINT, TPoint&);
          void EvMouseMove (UINT, TPoint &point);
          void EvRButtonDown (UINT, TPoint &point);
          void EvRButtonUp (UINT, TPoint&);
          void CmEditPaste();
  private:
          void PasteBitmap (TPoint point);
          void CmEnableEditPaste (TCommandEnabler &commandEnabler);

	  DECLARE_RESPONSE_TABLE (TCWndw);
};

DEFINE_RESPONSE_TABLE1 (TCWndw, TWindow)
          EV_WM_SETFOCUS, 
          EV_WM_LBUTTONDOWN, 
          EV_WM_LBUTTONUP, 
          EV_WM_RBUTTONDOWN, 
          EV_WM_RBUTTONUP,
          EV_WM_MOUSEMOVE,
          EV_COMMAND (CM_EDITPASTE, CmEditPaste), 
          EV_COMMAND_ENABLE (CM_EDITPASTE, CmEnableEditPaste), 
END_RESPONSE_TABLE;

//***********************************
// Реализация окна TCWndw.
//***********************************
// TCWndw::TCWndw()
// Это конструктор окна-клиента.
TCWndw::TCWndw (TWindow *parent, const char far *title) :
         TWindow (parent, title) 
{
  // Инициализировать переменные.
  lButton = FALSE;
  rButton = FALSE;
  paste = FALSE;
  rect.SetEmpty();
}

// TCWndw::EvSetFocus()
// Эта функция вызывается, когда окно-клиент 
// этой программы получает фокус.
void TCWndw::EvSetFocus (HWND)
{
  // Проверить, находится ли в буфере обмена растровое изображение.
  if (IsClipboardFormatAvailable (CF_BITMAP))
    // Если в буфере обмена имеется растровое
    // изображение, включить пункт меню Paste.
    bitmapInClip = TRUE; 
  else
    // Если растровое изображение отсутствует,
    // заблокировать пункт меню Paste.
    bitmapInClip = FALSE;
  // Выполнить обычную обработку сообщения WM_SETFOCUS.
  DefaultProcessing();
}

// TCWndw::EvLButtonDown()
// Эта  функция  реагирует  на  сообщение  WM_LBUTTONDOWN. 
void  TCWndw::EvLButtonDown (UINT, TPoint &point) 
{
  // Если  программа  работает   в  режиме   "вставить"...
  if (paste) PasteBitmap (point);
  // Если это новое нажатие на левую клавишу... 
  else if (!lButton)
       { 
         // Получить контекст устройства и перо. 
         lineDC = new TClientDC (HWindow); 
         newPen = new TPen (TColor::Black, 2, PS_SOLID); 
         lineDC->SelectObject (*newPen);
         // Направить все вводимое мышью в это окно. 
         SetCapture ();
         // Привязать начало линии к координатам мыши. 
         lineDC->MoveTo (point);
         // Установить флаг кнопки мыши. 
         lButton = TRUE;
       }
}

// TCWndw::EvLButtonUp()
// Эта функция реагирует на сообщение WM_LBUTTONUP. 
void TCWndw::EvLButtonUp (UINT, TPoint&)
{
  if (lButton)
  {
    // Освободить контекст устройства. 
    delete lineDC;
    // Удалить объект стандартного пера. 
    delete newPen;
    // Сбросить флаг клавиши. 
    lButton = FALSE;
    // Освсободить захват ввода от мыши. 
    ReleaseCapture();
  }
}

// TCWndw::EvMouseMove()
// Эта функция реагирует на сообщение WM_MOUSEHOVE.
void TCWndw::EvMouseMove (UINT, TPoint &point)
{
  // Если нажата левая кнопка, рисовать линию.
  if (lButton) lineDC->LineTo (point);
  // Если нажата правая кнопка...
  else if (rButton)
       {
         // Установить режим рисования ИСКЛЮЧАЮЩЕЕ ИЛИ.
         rectDC->SetROP2 (R2_XORPEN);
         // Стереть старый прямоугольник. 
         rectDC->DrawFocusRect (rect);
         // Установить новый прямоугольник в новые координаты.
         rect.Set (rect.left, rect.top, point.x, point.y);
         // Рисовать новый прямоугольник. 
         rectDC->DrawFocusRect (rect);
       }
}

// TCWndw::EvRButtonDown()
// Эта функция реагирует на сообщение WM_RBUTTONDOWN.
void TCWndw::EvRButtonDown (UINT, TPoint &point) 
{
  if (!rButton) 
  {
    // Установить флаг правой кнопки. 
    rButton = TRUE;
    // Получить контекст устройства и привязать
    // начальную точку прямоугольника и координаты мыши.
    rectDC = new TClientDC (HWindow);
    rect.Set (point.x, point.y, point.x, point.y);
    // Направить все вводимое мышью в это окно. 
    SetCapture ();
  }
}

// TCWndw::EvRButtonUp()
// Эта функция реагирует на сообщение WM_RBUTTONUP.
void TCWndw::EvRButtonUp (UINT, TPoint&) 
{
  // Сбросить флаг правой кнопки.
  rButton = FALSE;
  // Стереть очерчивающий прямоугольник. 
  rectDC->SetROP2 (R2_XORPEN); 
  rectDC->DrawFocusRect (rect);
  // Освободить контекстные устройства и захват мыши. 
  delete rectDC; 
  ReleaseCapture();
  // Вычислить  ширину и  высоту  выделенного  участка. 
  int width = rect.right - rect.left; 
  int height = rect.bottom - rect.top;
  //  Если растровое изображение что-либо содержит...
  if   ((width > 0) && (height > 0))
  {
    // Получить DC (контексты устройства) для окна и памяти.
    TClientDC clientDC (HWindow);
    TMemoryDC memoryDC (clientDC);
    // Создать растровое изображение.
    TBitmap bitmap(clientDC, width, height, FALSE);
    // Выбрать растровое изображение в DC памяти. 
    memoryDC.SelectObject (bitmap);
    // Скопировать растровое изображение в DC памяти. 
    memoryDC.BitBlt (0, 0, width, height, clientDC, rect.left, 
       rect.top, SRCCOPY);
    // Открыть и очистить буфер обмена. 
    TClipboard &clipboard = OpenClipboard(); 
    clipboard.EmptyClipboard();
    // Передать растровое изображение в буфер обмена. 
    bitmap.ToClipboard (clipboard);
    // Закрыть буфер обмена Clipboard. 
    clipboard.CloseClipboard();
    // Известить пользователя, что все прошло хорошо. 
    MessageBox ("Изображение помещено в буфер обмена", "Копирование", MB_OK);
    // Включить пункт Paste меню. 
    bitmapInClip = TRUE;
  }
}

// TCWndw::CmEditPaste()
// Эта функция вызывается, когда пользователь
// выбирает команду Paste из меню Edit.
void TCWndw::CmEditPaste() 
{
  // Установить режим вставки и изменить курсор.
  paste = TRUE;
  HCURSOR hCursor = LoadCursor(NULL, IDC_CROSS);
  SetClassWord (GCW_HCURSOR, (WORD) hCursor);
}

// TCWndw::PasteBitmap()
// Эта функция вставляет растровое изображение на
// экран из буфера обмена.
void TCWndw::PasteBitmap(TPoint point) 
{
  // Получить контекст устройства окна.
  TClientDC clientDC (HWindow);
  // Открыть буфер обмена.
  TClipboard &clipboard = OpenClipboard();
  // Создать растровое изображение.
  TBitmap bitmap(clipboard);
  // Закрыть буфер обмена Clipboard. 
  clipboard.CloseClipboard();
  // Создать контекст устройства памяти и 
  // выбрать в него растровое изображение.
  TMemoryDC memoryDC (clientDC);
  memoryDC.SelectObject (bitmap);
  // Найти ширину и высоту растрового изображения.
  int width = bitmap.Width();
  int height = bitmap.Height();
  // Скопировать растровое изображение на экран. 
  clientDC.BitBlt (point.x, point.y, width, height, 
            memoryDC, 0, 0, SRCCOPY);
  // Сбросить флаг вставки. 
  paste = FALSE;
  // Восстановить курсор в виде стрелки. 
  HCURSOR hCursor = LoadCursor(NULL,IDC_ARROW); 
  SetClassWord (GCW_HCURSOR, (WORD) hCursor);
}

// TCWndw::CmEnableEditPaste()
// Эта функция является переключателем доступности
// команды Edit/Paste и определяет, включен или
// блокирован пункт меню.
void TCWndw::CmEnableEditPaste (TCommandEnabler &commandEnabler)
{
  commandEnabler.Enable (bitmapInClip);
}

//***********************************
// Реализация класса TApp.
//***********************************
// TApp::InitMainWindow()
// Эта функция создает главное окно приложения.
void TApp::InitMainWindow()
{
  // Создать окно-клиент.
  TWindow *client = new TCWndw(0, 0);
  // Создать окно обрамления.
  TFrameWindow *wndw = new TFrameWindow 
     (0, "Буфер обмена - 2", client);
  // Добавить меню к главному окну. 
  wndw->AssignMenu (MENU_1);
  // Определить расположение и размеры окна.
  wndw->Attr.X = 40;
  wndw->Attr.Y = 40;
  wndw->Attr.W = GetSystemMetrics (SM_CXSCREEN) / 1.5;
  wndw->Attr.H = GetSystemMetrics (SM_CYSCREEN) / 1.5;
  // Установить указатель окна. 
  SetMainWindow(wndw);
} 

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

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

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

#include <owl\editfile.rh>

#define MENU_1         100 
#define CM_EXIT        24310
#define CM_EDITPASTE   24324

#ifdef RC_INVOKED

MENU_1 MENU
{
  POPUP "&File"
  {
	  MENUITEM "E&xit", CM_EXIT
  }
  POPUP "&Edit" 
  {
          MENUITEM "&Paste", CM_EDITPASTE 
  }
}

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


    Замечание. Перед компиляцией программы нужно установить в окне TargetExpert значение параметра Platform в Windows 3.x (16):


Рис.1. Окно TargetExpert


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


Рис.2. Результат работы приложения (фрагмент уже скопирован)

    Чтобы начать работу с программой, сначала поместите курсор мыши в рабочую область окна, удерживая нажатой левую кнопку, нарисуйте какую-нибудь фигуру. Затем поместите курсор мыши в левый верхний угол фигуры, которую вы хотите захватить и, удерживая правую кнопку мыши нажатой, переместите курсор в правый нижний угол фигуры. За курсором следует прямоугольник из пунктирных линий, очерчивая фигуру, которую вы выбираете.

    Чтобы скопировать содержимое прямоугольника, отпустите правую кнопку мыши. Появляется окно сообщений, информируя вас, что фигура захвачена.

    Чтобы вставить захваченное изображение, выберите опцию Paste в меню Edit. Курсор мыши превратится в крестик. Поместите крестик в то место, в которое вы хотите вставить 'фигуру, и нажмите левую кнопку мыши. Наконец, чтобы убедиться в правильном функционировании буфера обмена, активизируйте Windows Paint и его пункт Paste. Фигура, которую вы выбрали, появляется в рабочей области Paint. (Вы можете также просмотреть растровое изображение, используя просмотрщик буфера обмена Windows.) Если хотите, вы можете нарисовать фигуру в окне Paint, скопировать ее в буфер, используя команду Сору из меню Paint, которая находится в меню Edit), а затем вставить растровое изображение в окно программы.

    Копирование растрового изображения в буфер обмена не сложнее, чем копирование текста. Основное различие заключается в способе подготовки данных, то есть, если текст надо только лишь скопировать в память, то для растрового изображения надо получить контексты устройств, создать в памяти совместимые с устройствами растровые изображения и произвести "блиттинг" растрового изображения из одного контекста устройства в другой ("блиттинг" (blitting) - это процесс быстрой передачи данных между разделами памяти, обычно из ОЗУ в экранную память).

    Рассмотрим объявление окна пользователя:

class TCWndw : public TWindow
{
  public:
          TCWndw (TWindow *parent, const char far *title);
  protected:
          BOOL bitmapInClip;
          int lButton, rButton, paste;
          TPen *newPen;
          TClientDC *lineDC, *rectDC;
          TRect rect;

          void EvSetFocus (HWND);
          void EvLButtonDown (UINT, TPoint &point);
          void EvLButtonUp (UINT, TPoint&);
          void EvMouseMove (UINT, TPoint &point);
          void EvRButtonDown (UINT, TPoint &point);
          void EvRButtonUp (UINT, TPoint&);
          void CmEditPaste();
  private:
          void PasteBitmap (TPoint point);
          void CmEnableEditPaste (TCommandEnabler &commandEnabler);

	  DECLARE_RESPONSE_TABLE (TCWndw);
};

DEFINE_RESPONSE_TABLE1 (TCWndw, TWindow)
          EV_WM_SETFOCUS, 
          EV_WM_LBUTTONDOWN, 
          EV_WM_LBUTTONUP, 
          EV_WM_RBUTTONDOWN, 
          EV_WM_RBUTTONUP,
          EV_WM_MOUSEMOVE,
          EV_COMMAND (CM_EDITPASTE, CmEditPaste), 
          EV_COMMAND_ENABLE (CM_EDITPASTE, CmEnableEditPaste), 
END_RESPONSE_TABLE;

    Сложность класса окна-клиента обусловлена тем, что OWL не имеет класса редактирования графики, поэтому программа должна сама обеспечить редактирование графики. Заметим, что этот новый класс TCWndw содержит целый ряд защищенных членов-данных. Ниже вы увидите, как в программе используются эти переменные. Возможности программы обеспечиваются функциями EvLButtonDown() и EvLButtonUp(), EvMouseMove(), EvRButtonDown() и EvRButtonUp(). (Некоторые фрагменты кода в этих функциях заимствованы из программы, демонстрирующей панель инструментов TOOLBOX, приведенной на шаге 58.) Функция EvSetFocus() обеспечивает тот же сервис, что и в программе шага 62, за тем исключением, что теперь она проверяет буфер обмена Clipboard на нахождение в нем данных формата CF_BITMAP, а не формата CF_TEXT. Наконец, частная функция - PasteBitmap() копирует растровое изображение из буфера обмена на экран. Теперь рассмотрим конструктор класса:

TCWndw::TCWndw (TWindow *parent, const char far *title) :
         TWindow (parent, title) 
{
  // Инициализировать переменные.
  lButton = FALSE;
  rButton = FALSE;
  paste = FALSE;
  rect.SetEmpty();
}

    Здесь, как и в программе шага 62, инициализируется несколько важных переменных. Булева переменная lButton указывает, нажата или нет левая клавиша мыши. Булева переменная rButton означает то же самое для правой клавиши мыши. Булева переменная paste указывает, работает ли программа в режиме paste в данный момент. Объект rect OWL-класса TRect содержит координаты прямоугольника, который пользователь выделил на экране. Наконец, функция SetEmpty() класса TRect устанавливает координаты прямоугольника в нуль.


    Замечание. В Borland C++ к OWL добавляется целый ряд полезных классов поддержки, включая класс TRect, который используется в рассматриваемой программе. К другим классам поддержки относятся TColor, TPoint, TSize и TResId. Каждый из этих классов, а также другие, не упомянутые здесь, классы, обеспечивают не только хранение данных соответствующего типа, но также и несколько функций, которые помогают вам работать с этими данными.

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

void  TCWndw::EvLButtonDown (UINT, TPoint &point) 
{
  // Если  программа  работает   в  режиме   "вставить"...
  if (paste) PasteBitmap (point);
  // Если это новое нажатие на левую клавишу... 
  else if (!lButton)
       { 
         // Получить контекст устройства и перо. 
         lineDC = new TClientDC (HWindow); 
         newPen = new TPen (TColor::Black, 2, PS_SOLID); 
         lineDC->SelectObject (*newPen);
         // Направить все вводимое мышью в это окно. 
         SetCapture ();
         // Привязать начало линии к координатам мыши. 
         lineDC->MoveTo (point);
         // Установить флаг кнопки мыши. 
         lButton = TRUE;
       }
}

    Эта функция почти идентична ее аналогу в программе создания панели инструментов на шаге 58. Самое существенное различие заключается в том, что программа проверяет булево значение параметра paste. Если значение paste равно TRUE, то программа находится в режиме вставки; это означает, что пользователь не пытается рисовать линию. Как раз наоборот, пользователь пытается вывести содержимое буфера обмена Clipboard на экран. Если значение paste - FALSE, программа начинает рисование. Рисование завершается, когда пользователь отпускает левую клавишу мыши, при этом генерируется сообщение WM_LBUTTONUP. Это сообщение обрабатывается функцией EvLButtonUp() точно так же, как и прежде.

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

void TCWndw::EvRButtonDown (UINT, TPoint &point) 
{
  if (!rButton) 
  {
    // Установить флаг правой кнопки. 
    rButton = TRUE;
    // Получить контекст устройства и привязать
    // начальную точку прямоугольника и координаты мыши.
    rectDC = new TClientDC (HWindow);
    rect.Set (point.x, point.y, point.x, point.y);
    // Направить все вводимое мышью в это окно. 
    SetCapture ();
  }
}

    Здесь программа проверяет, что клавиша мыши еще не нажата, после чего производит установку rButton в TRUE. Затем, как и при начале операции рисования, программа получает контекст устройства окна. Так как нажатие правой кнопки отмечает левый верхний угол прямоугольника, который пользователь хочет захватить, то в вызове функции Set() объекта прямоугольника программа использует текущие координаты мыши; эта функция связывает rect с координатами мыши.

    Устанавливая одинаковые значения левой и правой координат прямоугольника и одинаковые значения верхней и нижней координат, программа создает прямоугольник с шириной и высотой, равными нулю. Наконец, после инициализации прямоугольника программа вызывает SetCapture(), чтобы направить все вводимое мышью в окно, независимо от того, где находится мышь. Когда пользователь перемещает мышь, она генерирует последовательность сообщений WM_M0USEMOVE, которые обрабатываются функцией EvMouseMove():

void TCWndw::EvMouseMove (UINT, TPoint &point)
{
  // Если нажата левая кнопка, рисовать линию.
  if (lButton) lineDC->LineTo (point);
  // Если нажата правая кнопка...
  else if (rButton)
       {
         // Установить режим рисования ИСКЛЮЧАЮЩЕЕ ИЛИ.
         rectDC->SetROP2 (R2_XORPEN);
         // Стереть старый прямоугольник. 
         rectDC->DrawFocusRect (rect);
         // Установить новый прямоугольник в новые координаты.
         rect.Set (rect.left, rect.top, point.x, point.y);
         // Рисовать новый прямоугольник. 
         rectDC->DrawFocusRect (rect);
       }
}

    Если пользователь не нажимал левую или правую кнопку, эта функция бездействует. Если пользователь нажимал левую кнопку, эта функция рисует новую линию точно так же, как и в программе, реализующей панель инструментов.

    Однако, если нажимается правая кнопка, чтобы выделить контур прямоугольника, нужно проделать кое-какую работу.

    Сначала программа вызывает функцию SetROP2 объекта rectDC, чтобы изменить режим рисования на ИСКЛЮЧАЩЕЕ ИЛИ (XOR). Такой режим рисования является идеальным для выделения прямоугольника, так как позволяет программе рисовать на экране прямоугольник, не изменяя имеющееся изображение. Это достигается путем двукратного рисования одного и того же контура в одних и тех же координатах. Сначала, когда программа чертит контур, она высвечивает те пиксели, поверх которых этот контур чертится. На следующем этапе, когда программа чертит контур, эти пиксели гасятся, оставляя экран в том виде, в каком он был перед тем, как был нарисован прямоугольник.

    После переключения режима рисования программа удаляет старый прямоугольник, перерисовывая его в режиме ИСКЛЮЧАЮЩЕГО ИЛИ. В начальный момент rect имеет нулевую ширину и высоту, поэтому не чертится никакой фигуры. Функция контекста устройства объекта DrawFocusRect() задает идеальную форму и толщину линии для черчения прямоугольника выделения, при этом программе нет необходимости создавать и вводить стандартное перо в контекст устройства. (Функция DrawFocusRect() является OWL-версией одноименной функции Windows API.)

    После удаления "старого" прямоугольника (если таковой имеется), программа вызывает функцию Set() объекта прямоугольника для копирования в rect новых координат прямоугольника. Координатами левого верхнего угла прямоугольника всегда являются координаты, изначально занесенные в элементы left и top объекта rect.

    Однако координаты right и bottom являются новыми координатами мыши. Наконец, программа рисует новый прямоугольник с помощью очередного вызова DrawFocusRect(). Так как программа все еще работает в режиме рисования ИСКЛЮЧАЮЩЕЕ ИЛИ, прямоугольник высвечивает те пиксели, по которым он чертится.

    Поскольку пользователь удерживает кнопку мыши нажатой при ее перемещении, она продолжает генерировать сообщения WM_MOUSEMOVE, которые удаляют и рисуют прямоугольники. Это создает иллюзию расширения и сжатия охватывающей области. Когда пользователь наконец отпускает кнопку мыши, программа должна создать выбранное растровое изображение. Она проделывает это в функции EvRButtonUp().

    Программа сначала присваивает rButton значение FALSE - это указывает на то, что кнопка больше не нажата. Затем стирается прямоугольник:

         rectDC->SetROP2 (R2_XORPEN);
         rectDC->DrawFocusRect (rect);

и освобождаются контексты устройства и мыши:

  delete rectDC; 
  ReleaseCapture();

    Чтобы вычислить размер выбранного растрового изображения, программа производит вычитание левой координты из правой, определяя ее ширину, и вычитание верхней координаты из нижней, определяя ее высоту:

  int width = rect.right - rect.left; 
  int height = rect.bottom - rect.top;

    Если ширина и высота больше нуля, то пользователь выбрал правильный прямоугольник, поэтому программа получает новый контекст устройства для окна-клиента и создает совместимый с ним контекст устройства памяти:

    TClientDC clientDC (HWindow);
    TMemoryDC memoryDC (clientDC);

    Затем программа создает объект растрового изображения с использованием контекста устройства окна-клиента, а также ширины и высоты этого изображения, при обращении к конструктору TBitmap. Четвертым аргументом в обращении к конструктору является булево значение, указывающее, какой функцией должно создаваться растровое изображение - либо функцией Windows CreateCompatibleBitmap() (FALSE), либо функцией Windows CreateDiscardableBitmap() (TRUE):

    TBitmap bitmap(clientDC, width, height, FALSE);

    После построения объекта растрового изображения программа выбирает растровое изображение в контекст устройства памяти, вызывая функцию SelectObject() объекта DC:

    memoryDC.SelectObject (bitmap);

    Затем она вызывает функцию BitBlt() для копирования выделенной области экрана в растровое изображение:

    memoryDC.BitBlt (0, 0, width, height, clientDC, rect.left, 
       rect.top, SRCCOPY);

    Теперь в памяти существует копия выбранного растрового изображения. Программа теперь может передать изображение в буфер обмена, предварительно открыв его и очистив:

    TClipboard &clipboard = OpenClipboard(); 
    clipboard.EmptyClipboard();

а затем вызывая функцию ToClipboard() объекта растрового изображения, которая передает его в буфер обмена.

    bitmap.ToClipboard (clipboard);

    Единственным аргументом этой функции является ссылка на объект TClipboard(). Функция TClipboard() передает это растровое изображение в буфер обмена, вызывая функцию объекта Clipboard с именем SetClipboardData() из Windows. Наконец, программа закрывает буфер обмена и выводит на экран окно сообщений, информирующее пользователя о том, что растровое изображение скопировано:

    clipboard.CloseClipboard();
    MessageBox ("Изображение помещено в буфер обмена", "Копирование", MB_OK);

    Последняя задача этой функции заключается в том, чтобы включить команду Paste, присваивая флагу BitmapInClip значение TRUE.

    Рассмотрим процесс вставки изображения. Когда пользователь хочет вставить изображение, он пользуется командой Paste из меню Edit, которая генерирует сообщение CM_EDITPASTE. Это сообщение обрабатывается функцией ответа на сообщения CmEditPaste():

void TCWndw::CmEditPaste() 
{
  // Установить режим вставки и изменить курсор.
  paste = TRUE;
  HCURSOR hCursor = LoadCursor(NULL, IDC_CROSS);
  SetClassWord (GCW_HCURSOR, (WORD) hCursor);
}

    Здесь программа присваивает переменной paste значение TRUE, указывающее, что теперь она работает в режиме вставки. Затем вызывается функция LoadCursor() из Windows, чтобы получить дескриптор системного курсора, имеющего форму перекрестья. Наконец, API-функция SetClassWord() меняет форму курсора на перекрестье.

    Теперь, когда пользователь работает в режиме вставки, он перемещает перекрестье курсора в то место, куда хочет вставить растровое изображение, и нажимает на левую клавишу мыши. При этом генерируется сообщение WM_LBUTTONDOWN, которое вызывает функцию EvLButtonDown(). Как отмечалось выше, в режиме вставки функция EvLButtonDown() вызывает PasteBitmap(), чтобы выполнить запрос пользователя:

void TCWndw::PasteBitmap(TPoint point) 
{
  // Получить контекст устройства окна.
  TClientDC clientDC (HWindow);
  // Открыть буфер обмена.
  TClipboard &clipboard = OpenClipboard();
  // Создать растровое изображение.
  TBitmap bitmap(clipboard);
  // Закрыть буфер обмена Clipboard. 
  clipboard.CloseClipboard();
  // Создать контекст устройства памяти и 
  // выбрать в него растровое изображение.
  TMemoryDC memoryDC (clientDC);
  memoryDC.SelectObject (bitmap);
  // Найти ширину и высоту растрового изображения.
  int width = bitmap.Width();
  int height = bitmap.Height();
  // Скопировать растровое изображение на экран. 
  clientDC.BitBlt (point.x, point.y, width, height, 
            memoryDC, 0, 0, SRCCOPY);
  // Сбросить флаг вставки. 
  paste = FALSE;
  // Восстановить курсор в виде стрелки. 
  HCURSOR hCursor = LoadCursor(NULL,IDC_ARROW); 
  SetClassWord (GCW_HCURSOR, (WORD) hCursor);
}

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

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

    Так как буфер обмена является ресурсом, который совместно используется каждым приложением, при работе в Windows необходимо, чтобы вы соблюдали установленные правила пользования им. Несоблюдение этих правил может привести к неправильному поведению любого другого приложения, работающего в данный момент в Windows. Например, если вы забыли закрыть буфер обмена Clipboard, Windows считает, что вы все еще им пользуетесь. Это не позволяет открыть Clipboard другим приложениям до тех пор, пока ваше является активным. (Когда приложение закрывается, Windows забирает буфер обмена Clipboard.)


    Замечание. Закрывайте буфер обмена как можно скорее после его открытия. Не удерживайте контроль над ним дольше, чем это абсолютно необходимо. Пока вы работаете с буфером Clipboard, он недоступен для других приложений.

    Со следующего шага мы начнем рассматривать библиотеки динамической компоновки (DLL).




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