Приложение 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; }