Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование для IBM OS/2

© Александр Фролов, Григорий Фролов
Том 25, М.: Диалог-МИФИ, 1993, 286 стр.

[Назад] [Содеожание] [Дальше]

8.6. Приложение SCROLL

В приложении SCROLL мы создаем в главном окне приложения две полосы просмотра - вертикальную и горизонтальную, указывая соответствующие флаги при вызове функции WinCreateStdWindow. Эти полосы используются для просмотра метрик шрифта с названием Courier (рис. 8.5).

Рис. 8.5. Просмотра метрик шрифта Courier в окне приложения SCROLL

В одной из следующих наших книг, посвященной программированию для операционной системы IBM OS/2 Warp, мы изучим эти метрики. А пока вы можете убедиться, что кроме высоты и ширины символов для описания шрифта используется дополнительно несколько десятков других параметров.

Исходные тексты приложения SCROLL приведены в листинге 8.9.

Листинг 8.9. Файл scroll\scroll.c

// =================================================
// Определения
// =================================================

#define INCL_WIN
#define INCL_GPI
#define INCL_WINDIALOGS
#include <os2.h>
#include <string.h>
#include <stdio.h>
#include "scroll.h"

#define YSIZE 50
#define XSIZE 50

// Прототип функции окна приложения
MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM);

// =================================================
// Глобальные переменные
// =================================================

HAB  hab;
HWND hWndFrame;
HWND hWndClient;

CHAR szAppTitle[] = "Scroll Demo";

// Размеры окна Client Window
SHORT cxClient;
SHORT cyClient;

// Размеры символов выбранного шрифта
SHORT cxChar, cyChar, cyDesc;

// Структура для записи метрик шрифта
FONTMETRICS fm;

// Идентификаторы полос просмотра
HWND hwndXScroll;
HWND hwndYScroll;

// Текущие координаты движков
INT nXScrollPos;
INT nYScrollPos;

// Текущие координаты для вывода текста
LONG cxCurrentPosition;
LONG cyCurrentPosition;

// =================================================
// Главная функция приложения main
// =================================================

int main()
{
  HMQ   hmq;
  QMSG  qmsg;
  BOOL  fRc;
  HPS hps;

  // Флаги для создания окна Frame Window
  // Добавляем флаги FCF_VERTSCROLL  и FCF_HORZSCROLL ,
  // в результате чего в главном окне будут созданы
  // вертикальная и горизонтальная полосы просмотра
  ULONG flFrameFlags =
    FCF_SYSMENU    | FCF_TITLEBAR      | FCF_MINMAX   |
    FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST |
    FCF_ICON       | FCF_VERTSCROLL   | FCF_HORZSCROLL ;

  // Имя класса главного окна
  CHAR  szWndClass[] = "SCRDEMO";

  hab = WinInitialize(0);
  if(hab == NULLHANDLE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка инициализации",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    return(-1);
  }

  // Создаем очередь сообщений
  hmq = WinCreateMsgQueue(hab, 0);
  if(hmq == NULLHANDLE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка при создании очереди сообщений",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    WinTerminate(hab);
    return(-1);
  }

  // Регистрация главного окна приложения
  fRc = WinRegisterClass(hab, szWndClass,
    (PFNWP)WndProc, 0, 0);
  if(fRc == FALSE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка при регистрации класса главного окна",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    WinDestroyMsgQueue(hmq);
    WinTerminate(hab);

    return(-1);
  }

  // Создаем главное окно приложения
  hWndFrame = WinCreateStdWindow (HWND_DESKTOP,
    WS_VISIBLE,
    &flFrameFlags, szWndClass, szAppTitle,
    0, 0, ID_APP_FRAMEWND, &hWndClient);
  if(hWndFrame == NULLHANDLE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка при создании главного окна",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    WinDestroyMsgQueue(hmq);
    WinTerminate(hab);

    return(-1);
  }

  // Получаем пространство отображения
  hps = WinGetPS(hWndFrame);

  // Выбираем в пространство отображения шрифт
  // с фиксированной шириной символов
  SetCourierFont(hps);

  // Определяем метрики шрифта
  GpiQueryFontMetrics(hps, (LONG)sizeof(fm), &fm);

  cxChar = fm.lAveCharWidth;
  cyChar = fm.lMaxBaselineExt;
  cyDesc = fm.lMaxDescender;

  // Устанавливаем шрифт, выбранный в пространство
  // отображения по умолчанию
  ResetFont(hps);

  // Возвращаем пространство отображения
  WinReleasePS(hps);

  // Запускаем цикл обработки сообщений
  while(WinGetMsg(hab, &qmsg, 0, 0, 0))
    WinDispatchMsg(hab, &qmsg);

  WinDestroyWindow(hWndFrame);
  WinDestroyMsgQueue(hmq);
  WinTerminate(hab);
  return(0);
}

// =================================================
// Функция главного окна приложения
// =================================================

MRESULT EXPENTRY
WndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
  HPS hps;
  RECTL rec;

  switch (msg)
  {
    case WM_CREATE:
    {
      // Начальные координаты движков
      nYScrollPos = 0;
      nXScrollPos = 0;

      // Начальные координаты для вывода текста
      cyCurrentPosition = cyClient;
      cxCurrentPosition =
        nXScrollPos * cxChar + cxChar;

      // Определяем идентификатор окна для
      // вертикальной полосы просмотра
      hwndYScroll = WinWindowFromID(
        WinQueryWindow(hWnd, QW_PARENT),
        FID_VERTSCROLL),

      // Устанавливаем диапазон изменений координат
      // движка и начальное положение для
      // вертикальной полосы просмотра
      WinSendMsg(hwndYScroll,
        SBM_SETSCROLLBAR, (MPARAM)0,
        MPFROM2SHORT(0, YSIZE));

      // Выполняем аналогичные действия для
      // горизонтальной полосы просмотра
      hwndXScroll = WinWindowFromID(
        WinQueryWindow(hWnd, QW_PARENT), 
        FID_HORZSCROLL),

      WinSendMsg(hwndXScroll,
        SBM_SETSCROLLBAR, (MPARAM)0,
        MPFROM2SHORT(0, XSIZE));

      return FALSE;
    }

    case WM_SIZE:
    {
      // получаем и сохраняем размеры главного окна
      cxClient = SHORT1FROMMP(mp2);
      cyClient = SHORT2FROMMP(mp2);

      // Перерисовываем окно приложения
      WinInvalidateRect(hWnd, NULL, TRUE);
      return 0;
    }

    case WM_PAINT:
    {
      // Получаем пространство отображения
      hps = WinBeginPaint(hWnd, NULLHANDLE, &rec);

      // Закрашиваем область, требующую обновление
      WinFillRect(hps, &rec, CLR_WHITE);

      // Выбираем в пространство отображения шрифт
      // с фиксированной шириной символов
      SetCourierFont(hps);

      // Устанавливаем начальные координаты для
      // вывода текста
      cxCurrentPosition = -nXScrollPos * cxChar 
        + cxChar;
      cyCurrentPosition = cyClient;

      // Выводим метрики шрифта
      PrintString(hps, fm.szFamilyname,
        "szFamilyname");
      PrintString(hps, fm.szFacename,       
        "szFacename");

      PrintLong(hps, fm.idRegistry,         
        "idRegistry");
      PrintLong(hps, fm.usCodePage,         
        "usCodePage");
      PrintLong(hps, fm.lEmHeight,          
        "lEmHeight");
      PrintLong(hps, fm.lXHeight,           
        "lXHeight");
      PrintLong(hps, fm.lMaxAscender,       
        "lMaxAscender");
      PrintLong(hps, fm.lMaxDescender,      
        "lMaxDescender");
      PrintLong(hps, fm.lLowerCaseAscent,   
        "lLowerCaseAscent");
      PrintLong(hps, fm.lLowerCaseDescent,  
        "lLowerCaseDescent");
      PrintLong(hps, fm.lInternalLeading,   
        "lInternalLeading");
      PrintLong(hps, fm.lExternalLeading,   
        "lExternalLeading");
      PrintLong(hps, fm.lAveCharWidth,      
        "lAveCharWidth");
      PrintLong(hps, fm.lMaxCharInc,        
        "lMaxCharInc");
      PrintLong(hps, fm.lEmInc, "lEmInc");
      PrintLong(hps, fm.lMaxBaselineExt,    
         "lMaxBaselineExt");
      PrintLong(hps, fm.sCharSlope,         
         "sCharSlope");
      PrintLong(hps, fm.sInlineDir,         
         "sInlineDir");
      PrintLong(hps, fm.sCharRot,           
         "sCharRot");
      PrintLong(hps, fm.usWeightClass,      
         "usWeightClass");
      PrintLong(hps, fm.usWidthClass,       
         "usWidthClass");
      PrintLong(hps, fm.sXDeviceRes,        
         "sXDeviceRes");
      PrintLong(hps, fm.sYDeviceRes,        
         "sYDeviceRes");
      PrintLong(hps, fm.sFirstChar,         
         "sFirstChar");
      PrintLong(hps, fm.sLastChar,          
         "sLastChar");
      PrintLong(hps, fm.sDefaultChar,       
         "sDefaultChar");
      PrintLong(hps, fm.sBreakChar,         
         "sBreakChar");
      PrintLong(hps, fm.sNominalPointSize,  
         "sNominalPointSize");
      PrintLong(hps, fm.sMinimumPointSize,  
         "sMinimumPointSize");
      PrintLong(hps, fm.sMaximumPointSize,  
         "sMaximumPointSize");
      PrintLong(hps, fm.fsType, "fsType");
      PrintLong(hps, fm.fsDefn, "fsDefn");
      PrintLong(hps, fm.fsSelection,        
         "fsSelection");
      PrintLong(hps, fm.fsCapabilities,     
         "fsCapabilities");
      PrintLong(hps, fm.lSubscriptXSize,    
         "lSubscriptXSize");
      PrintLong(hps, fm.lSubscriptYSize,    
         "lSubscriptYSize");
      PrintLong(hps, fm.lSubscriptXOffset,  
         "lSubscriptXOffset");
      PrintLong(hps, fm.lSubscriptYOffset,  
         "lSubscriptYOffset");
      PrintLong(hps, fm.lSuperscriptXSize,  
         "lSuperscriptXSize");
      PrintLong(hps, fm.lSuperscriptYSize,  
         "lSuperscriptYSize");
      PrintLong(hps, fm.lSuperscriptXOffset,
         "lSuperscriptXOffset");
      PrintLong(hps, fm.lSuperscriptYOffset,
         "lSuperscriptYOffset");
      PrintLong(hps, fm.lUnderscoreSize,    
         "lUnderscoreSize");
      PrintLong(hps, fm.lUnderscorePosition,
         "lUnderscorePosition");
      PrintLong(hps, fm.lStrikeoutSize,     
         "lStrikeoutSize");
      PrintLong(hps, fm.lStrikeoutPosition, 
         "lStrikeoutPosition");
      PrintLong(hps, fm.sKerningPairs,      
         "sKerningPairs");
      PrintLong(hps, fm.sFamilyClass,       
         "sFamilyClass");
      PrintLong(hps, fm.lMatch, "lMatch");
      PrintLong(hps, fm.FamilyNameAtom,     
         "FamilyNameAtom");
      PrintLong(hps, fm.FaceNameAtom,       
         "FaceNameAtom");

      // Устанавливаем шрифт, выбранный в пространство
      // отображения по умолчанию
      ResetFont(hps);

      // Возвращаем пространство отображения
      WinEndPaint(hps);
      return 0;
    }

    case WM_ERASEBACKGROUND:
      return(MRFROMLONG(1L));

    // Это сообщение приходит от вертикальной
    // полосы просмотра
    case WM_VSCROLL:
    {
      // В зависимости от кода извещения
      // изменяем содержимое переменной, в которой
      // хранится координата движка
      switch (SHORT2FROMMP(mp2))
      {
        case SB_LINEDOWN:
        {
          nYScrollPos += 1;
          break;
        }
        case SB_LINEUP:
        {
          nYScrollPos -= 1;
          break;
        }
        case SB_PAGEDOWN:
        {
          nYScrollPos += cyClient / cyChar;
          break;
        }
        case SB_PAGEUP:
        {
          nYScrollPos -= cyClient / cyChar;
          break;
        }
        case SB_SLIDERTRACK:
        {
          nYScrollPos = SHORT1FROMMP(mp2);
          break;
        }

        default:
          break;
      }

      // Ограничиваем диапазон изменения
      // координаты движка
      if(nYScrollPos > YSIZE) nYScrollPos = YSIZE;
      if(nYScrollPos < 0)  nYScrollPos = 0;

      // Устанавливаем новую позицию движка
      WinSendMsg(hwndYScroll, SBM_SETPOS,
        (MPARAM)nYScrollPos, NULL);

      // Перерисовываем окно приложения
      WinInvalidateRect(hWnd, NULL, TRUE);

      return 0;
    }

    // Это сообщение приходит от горизонтальной
    // полосы просмотра
    case WM_HSCROLL:
    {
      // В зависимости от кода извещения
      // изменяем содержимое переменной, в которой
      // хранится координата движка
      switch (SHORT2FROMMP(mp2))
      {
        case SB_LINELEFT:
        {
          nXScrollPos -= 1;
          break;
        }
        case SB_LINERIGHT:
        {
          nXScrollPos += 1;
          break;
        }
        case SB_PAGERIGHT:
        {
          nXScrollPos += 10;
          break;
        }
        case SB_PAGELEFT:
        {
          nXScrollPos -= 10;
          break;
        }
        case SB_SLIDERTRACK:
        {
          nXScrollPos = SHORT1FROMMP(mp2);
          break;
        }

        default:
          break;
      }

      // Ограничиваем диапазон изменения
      // координаты движка
      if(nXScrollPos < 0)  nXScrollPos = 0;

      // Устанавливаем новую позицию движка
      WinSendMsg(hwndXScroll, SBM_SETPOS,
        (MPARAM)nXScrollPos, NULL);

      // Перерисовываем окно приложения
      WinInvalidateRect(hWnd, NULL, TRUE);

      return 0;
    }

    // Это сообщение появляется, когда пользователь
    // нажимает или отжимает клавишу
    case WM_CHAR:
    {
      // Пропускаем только виртуальные клавиши
      if(!(CHARMSG(&msg) ->fs & KC_VIRTUALKEY))
        return 0;

      // Фильтруем сообщения, поступающие при
      // отжатии клавиш
      if(!(CHARMSG(&msg) ->fs & KC_KEYUP))
        return 0;

      // В зависимости от виртуального кода клавиши
      // посылаем окну нашего прилоджения
      // сообщения WM_HSCROLL или WM_HSCROLL для
      // работы с полосами просмотра при помощи
      // клавиатуры
      switch(CHARMSG(&msg) -> vkey)
      {
        case VK_LEFT:
        {
           WinSendMsg(hWnd, WM_HSCROLL,
             NULL, MPFROM2SHORT(0, SB_LINELEFT));
          break;
        }
        case VK_RIGHT:
        {
           WinSendMsg(hWnd, WM_HSCROLL,
             NULL, MPFROM2SHORT(0, SB_LINERIGHT));
          break;
        }
        case VK_UP:
        {
           WinSendMsg(hWnd, WM_VSCROLL,
             NULL, MPFROM2SHORT(0, SB_LINEUP));
          break;
        }
        case VK_DOWN:
        {
           WinSendMsg(hWnd, WM_VSCROLL,
             NULL, MPFROM2SHORT(0, SB_LINEDOWN));
          break;
        }
        case VK_PAGEUP:
        {
           WinSendMsg(hWnd, WM_VSCROLL,
             NULL, MPFROM2SHORT(0, SB_PAGEUP));
          break;
        }
        case VK_PAGEDOWN:
        {
           WinSendMsg(hWnd, WM_VSCROLL,
             NULL, MPFROM2SHORT(0, SB_PAGEDOWN));
          break;
        }
        default:
          break;
      }

      return 0;
    }

    default:
      return(WinDefWindowProc(hWnd, msg, mp1, mp2));
  }
}

// =================================================
// Выбор шрифта с фиксированной шириной символов
// =================================================

void SetCourierFont(HPS hps)
{
  FATTRS fat;

  // Заполняем структуру описанием нужного
  // нам шрифта
  fat.usRecordLength = sizeof(FATTRS);
  strcpy(fat.szFacename ,"Courier");
  fat.fsSelection = 0;
  fat.lMatch = 0L;
  fat.idRegistry = 0;
  fat.usCodePage = 850;
  fat.lMaxBaselineExt = 12L;
  fat.lAveCharWidth = 12L;
  fat.fsType = 0;
  fat.fsFontUse = FATTR_FONTUSE_NOMIX;

  // Создаем логический шрифт, имеющий идентификатор 1L
  GpiCreateLogFont(hps, NULL, 1L, &fat);

  // Выбираем созданный шрифт 
  // в пространство отображения
  GpiSetCharSet(hps, 1L);
}

// =================================================
// Установка шрифта, выбранного в пространство
// отображения по умолчанию
// =================================================
void ResetFont(HPS hps)
{
  GpiSetCharSet(hps, LCID_DEFAULT);
  GpiDeleteSetId(hps, 1L);
}

// =================================================
// Вывод в окне значения строчной переменной
// =================================================

void PrintString(HPS hps, PSZ pszValue, PSZ pszName)
{
  int i;
  CHAR szBuf[80];
  POINTL ptl;

  // Выводим название поля структуры
  sprintf(szBuf, "%s", pszName);
  i = strlen(szBuf);

  ptl.x = cxCurrentPosition;
  ptl.y = cyCurrentPosition -
    cyChar * (1 - nYScrollPos);
  GpiCharStringAt(hps, &ptl, i, szBuf);

  // Выводим значение, записанное в этом поле
  sprintf(szBuf, "= %s", pszValue);
  i = strlen(szBuf);

  ptl.x = cxCurrentPosition + 20 * cxChar;
  ptl.y = cyCurrentPosition -
    cyChar * (1 - nYScrollPos);
  GpiCharStringAt(hps, &ptl, i, szBuf);

  // Изменяем текущую позицию для вывода
  cyCurrentPosition -= cyChar;
}

// =================================================
// Вывод в окне значения переменной типа LONG
// =================================================

void PrintLong(HPS hps, LONG lValue, PSZ pszName)
{
  int i;
  CHAR szBuf[80];
  POINTL ptl;

  sprintf(szBuf, "%s", pszName);
  i = strlen(szBuf);

  ptl.x = cxCurrentPosition;
  ptl.y = cyCurrentPosition - 
     cyChar * (1 - nYScrollPos);
  GpiCharStringAt(hps, &ptl, i, szBuf);

  sprintf(szBuf, "= %ld", lValue);
  i = strlen(szBuf);

  ptl.x = cxCurrentPosition + 20 * cxChar;
  ptl.y = cyCurrentPosition - 
    cyChar * (1 - nYScrollPos);
  GpiCharStringAt(hps, &ptl, i, szBuf);

  cyCurrentPosition -= cyChar;
}

Определения и глобальные переменные

В программе определены две константы с именами XSIZE и YSIZE, которые задают верхнюю границу изменений значений, соответственно, горизонтальной и вертикальной полосы просмотра.

Обработчик сообщения WM_SIZE записывает в переменные cxClient и cyClient размеры окна Client Window.

В переменных cxChar, cyChar и cyDesc хранятся основные размеры символов для шрифта Courier. Мы выбрали этот шрифт для отображения информации в главном окне приложения.

Структура fm содержит метрики шрифта, которые вы будете просматривать в окне Client Window.

Для того чтобы посылать сообщения окнам полос просмотра, необходимо знать их идентификаторы. Эти идентификаторы хранятся в переменных hwndXScroll и hwndYScroll (соответственно, для горизонтальной и вертикальной полосы просмотра).

В переменных nXScrollPos и nYScrollPos хранятся текущие координаты движков полос просмотра. Эти координаты нужны для определения текущих координат, начиная с которых в окне Client Window будет выводиться текст. Начальные координаты для вывода текста записаны в переменных cxCurrentPosition, cyCurrentPosition и, разумеется, зависят от положения движков полос просмотра.

Функция main

Особенностью функции main является то, что при создании главного окна приложения используются флаги FCF_VERTSCROLL и FCF_HORZSCROLL :

ULONG flFrameFlags =
  FCF_SYSMENU    | FCF_TITLEBAR      | FCF_MINMAX   |
  FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST |
  FCF_ICON       | FCF_VERTSCROLL     | FCF_HORZSCROLL ;

Для определения отображаемых в своем главном окне метрик шрифта функция main получает пространство отображения, выбирает в него шрифт Courier и с помощью функции GpiQueryFontMetrics заполняет структуру fm. Затем функция main восстанавливает шрифт, выбранный в пространство отображения по умолчанию, и возвращает пространство отображения. После этого запускается цикл обработки сообщений.

Функция окна WndProc

Рассмотрим обработку сообщений функцией окна WndProc.

Сообщение WM_CREATE

В процессе создания окна приложения начальные координаты движков, хранящиеся в переменных nXScrollPos и nYScrollPos, устанавливаются в нулевое значение. Это соответствует начальному положению движков, в которое они устанавливаются при создании этих полос просмотра.

Начальные координаты для вывода текста в окне Client Window устанавливаются следующим образом:

cyCurrentPosition = cyClient;
cxCurrentPosition = nXScrollPos * cxChar + cxChar;

Так как начальная координата по оси Y устанавливается равной высоте окна, вывод текста начнется в верхней части экрана. Начальная позиция по оси X устанавливается равной средней ширине символа выбранного шрифта, поэтому первый символ первой строки текста будет нарисован в верхнем левом углу экрана с небольшим отступом слева.

Далее обработчик сообщения WM_CREATE определяет идентификаторы окон полос просмотра, сохраняя их в глобальных переменных hwndYScroll и hwndXScroll. Для определения этих идентификаторов используется функция WinWindowFromID.

Так как через первый параметр этой функции необходимо передать идентификатор окна, которое является родительским для полос просмотра, мы получаем идентификатор родительского окна при помощи функции WinQueryWindow. В качестве первого параметра этой функции передается идентификатор окна Client Window, а в качестве второго - константу QW_PARENT. Так как окно Client Window и окна полос просмотра имеют одно и то же родительское окно, функция WinQueryWindow вернет нужный идентификатор окна, который можно передать функции WinWindowFromID.

На следующем этапе обработчик сообщения WM_CREATE, пользуясь полученными идентификаторами окон полос просмотра, устанавливает начальное положение движка и диапазон изменения значений полос просмотра. Для этого окнам полос просмотра передается сообщение SBM_SETSCROLLBAR:

WinSendMsg(hwndYScroll, SBM_SETSCROLLBAR, (MPARAM)0,
  MPFROM2SHORT(0, YSIZE));
WinSendMsg(hwndXScroll, SBM_SETSCROLLBAR, (MPARAM)0,
  MPFROM2SHORT(0, XSIZE));

Сообщение WM_SIZE

Обработчик сообщения WM_SIZE сохраняет размеры окна Client Window в глобальных переменных cxClient и cyClient, а затем объявляет, что окно требует перерисовки. Последнее выполняется с помощью функции WinInvalidateRect.

Сообщение WM_PAINT

При обработке сообщения WM_PAINT функция окна нашего приложения рисует в окне Client Window строки значений параметров метрик шрифта.

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

Затем начальные координаты для вывода текста устанавливаются так, как это показано ниже:

cxCurrentPosition = -nXScrollPos * cxChar + cxChar;
cyCurrentPosition = cyClient;

Вывод текста всегда начинается в верхней части окна, так как в переменную cyCurrentPosition записывается значение высоты окна cyClient. Что же касается оси X, то текущая позиция по этой оси определяется с учетом текущей позиции движка горизонтальной полосы просмотра. Чем эта позиция больше (т. е. чем правее расположен движок), тем меньше значение позиции вывода текста.

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

Далее обработчик сообщения WM_PAINT несколько раз вызывает функции PrintString и PrintLong, отображающие, соответственно, текстовые и числовые параметры метрик шрифта. Эти функции после рисования одной строки текста изменяют значение текущей позиции вывода по оси Y.

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

Сообщение WM_VSCROLL

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

Анализируя значение старшего слова параметра mp2, в котором хранится код команды, обработчик сообщения WM_VSCROLL изменяет соответствующим образом значение глобальной переменной nYScrollPos. Напомним, что в этой переменной хранится текуще положение движка вертикальной полосы просмотра.

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

WinSendMsg(hwndYScroll, SBM_SETPOS,
  (MPARAM)nYScrollPos, NULL);

Для того чтобы указанные изменения позиции движка отразились на содержимом окна Client Window, его необходимо перерисовать. Поэтому перед возвращением управления обработчик сообщения WM_VSCROLL вызывает функцию WinInvalidateRect, перерисовывая окно приложения.

Сообщение WM_HSCROLL

Сообщение WM_HSCROLL поступает в функцию окна, когда пользователь работает с горизонтальной полосой просмотра или клавишами, предназначенными для свертки текста по горизонтали. Обработчик этого сообщения выполняет действия, аналогичные действиям обработчика сообщения WM_VSCROLL, однако он изменяет содержимое глобальной переменной nXScrollPos (позиция движка горизонтальной полосы просмотра).

Сообщение WM_CHAR

Обычно в приложениях Presentation Manager вы можете работать с полосами просмотра не только при помощи мыши, но и при помощи клавиатуры. Для обеспечения такой возможности в нашем приложении предусмотрен обработчик клавиатурного сообщения WM_CHAR.

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

if(!(CHARMSG(&msg) ->fs & KC_VIRTUALKEY))
  return 0;
if(!(CHARMSG(&msg) ->fs & KC_KEYUP))
  return 0;

Затем анализируется код виртуальной клавиши и в зависимости от него окну Client Window посылаются сообщения WM_HSCROLL или WM_VSCROLL с соответствующим кодом команды, например:

switch(CHARMSG(&msg) -> vkey)
{
  case VK_LEFT:
  {
    WinSendMsg(hWnd, WM_HSCROLL,
      NULL, MPFROM2SHORT(0, SB_LINELEFT));
    break;
  }
  case VK_RIGHT:
  {
    WinSendMsg(hWnd, WM_HSCROLL,
      NULL, MPFROM2SHORT(0, SB_LINERIGHT));
    break;
   }
   . . .
}

Функция PrintString

Функция PrintString, определенная в нашем приложении, используется для ввода в окно приложения текстовых параметров метрик шрифта.

Вначале эта функция подготавливает в буфере szBuf название поля шрифта, а переменной i - размер соответствующей строки символов. Затем в структуру ptl типа POINTL записываются начальные координаты вывода строки. Рисование строки выполняется функцией GpiCharStringAt :

ptl.x = cxCurrentPosition;
ptl.y = cyCurrentPosition - cyChar * (1 - nYScrollPos);
GpiCharStringAt(hps, &ptl, i, szBuf);

Заметьте, что текущая координата по оси Y зависит от положения движка вертикальной полосы просмотра.

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

Перед возвращением управления функция уменьшает текущую позицию вывода по оси Y на высоту символа, чтобы следующая строка была нарисована ниже:

cyCurrentPosition -= cyChar;

Функция PrintLong

Функция PrintLong аналогична только что описанной функции PrintString, и отличается от последней только тем, что используется для отображения значения типа LONG.

Файл scroll.h

Файл scroll.h содержит определения константы ID_APP_FRAMEWND, а также прототипы функций, определенных в приложении SCROLL (листинг 8.10).

Листинг 8.10. Файл scroll\scroll.h

#define ID_APP_FRAMEWND 1
void SetCourierFont(HPS hps);
void ResetFont(HPS hps);
void PrintString(HPS hps, PSZ pszValue, PSZ pszName);
void PrintLong(HPS hps, LONG lValue, PSZ pszName);

Файл scroll.rc

Файл описания ресурсов приложения scroll.rc представлен в листинге 8.11.

Листинг 8.11. Файл scroll\scroll.rc

#include <os2.h>
#include "scroll.h"
ICON ID_APP_FRAMEWND SCROLL.ICO

Файл scroll.def

Файл определения модуля scroll.def приведен в листинге 8.12.

Листинг 8.12. Файл scroll\scroll.def

NAME        SCROLL   WINDOWAPI
DESCRIPTION 'Scroll Application (C) Frolov A., 1996'
HEAPSIZE    4096
STACKSIZE   32768
EXPORTS     WndProc
[Назад] [Содеожание] [Дальше]