Шаг 82.
Библиотека OWL.
Выбор шрифта

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

    Студент 4 курса (2010-11 уч.год) Шепелин Александр создал приложение, позволяющее изменять характеристики шрифта средствами бибилиотеки OWL.

    Иногда бывает необходимо посмотреть, как будет выглядеть шрифт в работе. Windows умеет отображать вид шрифта. Однако в утилите просмотра шрифтов Windows нет возможности проверить его в работе со своим текстом.

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

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


Рис.1. Внешний вид приложения

    Предусмотрена корректная перерисовка при изменении параметров шрифта.

    Приведем текст программы.

// Изменение шрифта текста и его характеристик.
// Разработать приложение, содержащее произвольный текст (предусмотреть
// возможность загрузки текста из файла). Реализовать возможность изменения
// шрифта и его характеристик с помощью стандартного диалогового окна "Шрифт".

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\scroller.h>
#include <stdio.h>
#include <owl\dc.h>
#include <owl\choosefo.h>
#include <owl\opensave.h>
#include <string.h>
#include "tz.rc"

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

//Класс основного окна
class TWndw : public TFrameWindow
{
  public:
	 TWndw(TWindow *parent, const char far *title);
         ~TWndw();
  protected:
         TFont *font;
         TColor fontColor;
         char filen [255];
         //переменная для хранения размеров текущего шрифта
         int fheight;
         void CmChooseFont();
         void CmFileOpen();
         void  Paint (TDC &paintDC, BOOL, TRect&);

         DECLARE_RESPONSE_TABLE (TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
  EV_COMMAND(CM_CHOOSEFONT, CmChooseFont),
  EV_COMMAND(CM_FILEOPEN, CmFileOpen),
END_RESPONSE_TABLE;

TWndw::TWndw(TWindow *parent, const char far *title):
		  TFrameWindow(parent,title)
{
	// Добавить полосы прокрутки к главному окну
	Attr.Style |= WS_VSCROLL;
	Scroller = new TScroller (this, 1, 1, 640, 480);
	// Добавить меню к главному окну.
	AssignMenu("MENU_1");
	// Определить расположение и размеры окна.
	Attr.W=GetSystemMetrics(SM_CXSCREEN);
	Attr.H=GetSystemMetrics(SM_CXSCREEN)-1000;
	// Инициализировать указатель шрифта, цвет и название.
	font = 0;
	fontColor = TColor::Black;
	strcpy(filen,"");
	fheight =-15;
}

// TWndw::~TWndw()
// Это деструктор главного окна.
TWndw::~TWndw()
{
  // Если объект шрифта существует, удалить его.
  if (font) delete font;
}

// TWndw::CmChooseFont()
// Эта функция реагирует на команду Choose Font,
// отображая диалоговое окно "Шрифт".
void TWndw::CmChooseFont()
{
  // Определить массив для сообщения об ошибке.
  char errorMsg[81];
  // Создать объект TData диалогового окна Font.
  TChooseFontDialog::TData fontData;
  // Инициализировать объект TData. 
  fontData.Flags = CF_EFFECTS | CF_TTONLY | 
     CF_FORCEFONTEXIST | CF_SCREENFONTS;
  fontData.Color = fontColor;
  // Создать диалоговое окно Font.
  TChooseFontDialog *dialog =
       new TChooseFontDialog (this, fontData);
  // Выполнить диалоговое окно Font. 
  int result = dialog->Execute();
  // Ответить на кнопку "OK" диалогового окна.
  if (result == IDOK)
  {
      // Удалить старый шрифт, если он существует.
      if (font) delete font;
      // Создать новый шрифт по выбору пользователя. 
      font = new TFont(&fontData.LogFont);
		// Получить цвет выбранного шрифта.
		fontColor = fontData.Color;
		fheight = fontData.LogFont.lfHeight;
      // Заставить окно перерисоваться. 
		Invalidate();
  }
  // Если произошла ошибка... 
  else if (fontData.Error != 0) 
         {
           // Вывести окно сообщений об ошибках. 
           wsprintf(errorMsg,"Ошибка #%ld.", fontData.Error); 
           MessageBox(errorMsg, "Ошибка",  MB_OK | MB_ICONEXCLAMATION);
         }
}


// TWndw::CmFileOpen()
// Эта функция реагирует на команду Open,
// отображая диалоговое окно "Открытие файла".
void TWndw::CmFileOpen()
{
  char errorMsg[81];
  // Создать объект TData диалогового окна. 
  TOpenSaveDialog::TData fileData (OFN_FILEMUSTEXIST | OFN_HIDEREADONLY
      | OFN_PATHMUSTEXIST, 
     "Все файлы (*.*)|*.*|Текстовые файлы (*.txt)|*.txt|", 0, 0, "*");
  // Создать диалоговое окно Open. 
  TFileOpenDialog *dialog = new TFileOpenDialog(this, fileData);
  // Отобразить диалоговое окно Open. 
  int result = dialog->Execute();
  // Ответить на кнопку "OK" диалогового окна. 
  if (result == IDOK) {
		 strcpy(filen,fileData.FileName);
  }
  else if (fileData.Error != 0)
       {
		wsprintf(errorMsg,"Возникла ошибка #%ld.", fileData.Error);
		MessageBox(errorMsg, "Ошибка",  MB_OK | MB_ICONEXCLAMATION);
		 }
  Invalidate();
}

//TWndw::Paint()
// Эта функция переопределяет функцию Paint() из 
// TWindow и реагирует на сообщение WM_PAINT, которое 
// Windows посылает окну, если оно должно быть 
// повторно перерисовано.
void TWndw::Paint(TDC &paintDC, BOOL, TRect&)
{
  // Если имеется выбранный шрифт...
  if (font)
        // ... поместить его в контекст устройства.
        paintDC.SelectObject (*font); 
  // Установить цвет шрифта.
  paintDC.SetTextColor (fontColor);  
  // Вывести название шрифта в окне.
  //MessageBox(filen, "Ошибка",  MB_OK | MB_ICONEXCLAMATION);
  int tm=1;
  if (strcmp(filen,"")==0) { paintDC.TextOut(10, 10, "Произвольный текст"); }
  else {
    FILE *ReadF;
    ReadF=fopen(filen, "r");
    char  rline[300];
    do{
        fgets(rline,300,ReadF);
        if (feof(ReadF)) break;
        if (rline[strlen(rline)-1]== '\n') rline[strlen(rline)-1]=0;
        paintDC.TextOut(10, 10-fheight*tm, rline);
        tm++;
     } while(true);
    fclose(ReadF);
  }
//установить новые значения полосы прокрутки
Scroller->SetRange(0, (-1)*fheight*tm);

}

void TApp::InitMainWindow()
{
  TFrameWindow *wndw= new TWndw(0,"Шрифт-тестер");
  SetMainWindow(wndw);
}

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

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

#define CM_FILEOPEN    102
#define CM_FILEEXIT    24310
#define CM_CHOOSEFONT  101

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

#ifdef RC_INVOKED

MENU_1 MENU 
{
 POPUP "&File"
 {
  MENUITEM "&Open", CM_FILEOPEN
  MENUITEM "E&xit", CM_FILEEXIT
 }

 POPUP "&Font"
 {
  MENUITEM "&Choose Font...", CM_CHOOSEFONT
 }

}

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

    Класс главного окна примера включает три защищенных члена-данных: font, fontcolor filen:

  TFont *font;
  TColor fontColor;
  char filen [255];
которые являются соответственно объектом, цветом шрифта и именем файла с заданным текстом. Программа инициализирует эти поля в конструкторе главного окна, устанавливая указатель шрифта в нуль, цвет шрифта в черный. Название текущего файла устанавливается в пустую строку. Это необходимо для случая, когда пользователь удовлетворится строкой для теста по умолчанию.

    Деструктор отвечает за удаление объекта текущего выбранного шрифта, если он существует. Это освобождает ресурсы для других Windows-приложений.

    Большинство действий программы осуществляется в функциях CmChooseFont() и Paint(). CmChooseFont() реагирует на команду Choose Font из пункта меню Font. Эта функция сначала объявляет объект TChooseFontDialog::TData с именем fontData, затем инициализирует соответствующие члены-данные объекта fontData.

    После создания и инициализации объекта TData, CmChooseFont() создает диалоговое окно TChooseFontDialog с помощью вызова конструктора класса с двумя аргументами: указателем на родительское окно и ссылкой на объект TChooseFontDialog::TData:

// TWndw::CmChooseFont()
// Эта функция реагирует на команду Choose Font,
// отображая диалоговое окно "Шрифт".
void TWndw::CmChooseFont()
{
  // Определить массив для сообщения об ошибке.
  char errorMsg[81];
  // Создать объект TData диалогового окна Font.
  TChooseFontDialog::TData fontData;
  // Инициализировать объект TData. 
  fontData.Flags = CF_EFFECTS | CF_TTONLY | 
     CF_FORCEFONTEXIST | CF_SCREENFONTS;
  fontData.Color = fontColor;
  // Создать диалоговое окно Font.
  TChooseFontDialog *dialog =
       new TChooseFontDialog (this, fontData);
  // Выполнить диалоговое окно Font. 
  int result = dialog->Execute();
  // Ответить на кнопку "OK" диалогового окна.
  if (result == IDOK)
  {
      // Удалить старый шрифт, если он существует.
      if (font) delete font;
      // Создать новый шрифт по выбору пользователя. 
      font = new TFont(&fontData.LogFont);
      // Получить цвет выбранного шрифта.
      fontColor = fontData.Color;
      fheight = fontData.LogFont.lfHeight;
      // Заставить окно перерисоваться. 
      Invalidate();
  }
  // Если произошла ошибка... 
  else if (fontData.Error != 0) 
         {
           // Вывести окно сообщений об ошибках. 
           wsprintf(errorMsg,"Ошибка #%ld.", fontData.Error); 
           MessageBox(errorMsg, "Ошибка",  MB_OK | MB_ICONEXCLAMATION);
         }
}

    Если пользователь завершает диалог по кнопке ОК, это означает, что он выбрал новый шрифт. В этом случае CmChooseFont() удаляет старый шрифт (если он существовал), создает новый объект TFont из члена-данного LogFont объекта TData и его параметры (цвет, размер и т.п.).

    Собрав вместе все данные, необходимые для нового шрифта, CmChooseFont() вызывает Invalidate() для того, чтобы послать в окно сообщение WM_PAINT, которое заставит OWL вызвать функцию Paint() главного окна.

    Функция CmFileOpen открывает диалог выбора файла:

// TWndw::CmFileOpent()
// Эта функция реагирует на команду Open,
// отображая диалоговое окно "Открытие файла".
void TWndw::CmFileOpen()
{
  char errorMsg[81];
  // Создать объект TData диалогового окна. 
  TOpenSaveDialog::TData fileData (OFN_FILEMUSTEXIST | OFN_HIDEREADONLY
      | OFN_PATHMUSTEXIST, 
     "Все файлы (*.*)|*.*|Текстовые файлы (*.txt)|*.txt|", 0, 0, "*");
  // Создать диалоговое окно Open. 
  TFileOpenDialog *dialog = new TFileOpenDialog(this, fileData);
  // Отобразить диалоговое окно Open. 
  int result = dialog->Execute();
  // Ответить на кнопку "OK" диалогового окна. 
  if (result == IDOK) {
		 strcpy(filen,fileData.FileName);
  }
  else if (fileData.Error != 0)
       {
		wsprintf(errorMsg,"Возникла ошибка #%ld.", fileData.Error);
		MessageBox(errorMsg, "Ошибка",  MB_OK | MB_ICONEXCLAMATION);
		 }
  Invalidate();
}

    Эта функция сначала конструирует объект TData для диалогового окна. Версия объекта TData для класса TOpenSaveDialog имеет конструктор, требующий пять аргументов. Первым является набор флагов, которые определяют, как диалоговое окно будет выглядеть и работать. Чтобы придать диалоговому окну Open такой вид, какой вы хотите, вы должны произвести операцию логического сложения (OR) над соответствующими флагами. В приведенном примере флаги указывают, что пользователь не может выбрать (создать) несуществующие файлы и пути, и кнопка-переключатель "только чтение" ("read-only") не будет показана в этом диалоговом окне.

    После того, как функция создала объект TData, возвратимся к CmFileOpen(). Она конструирует диалоговое окно Open. Для этого программа вызывает конструктор класса TOpenFileDialog, который требует в качестве аргументов указатель родительского окна создаваемого диалогового окна и ссылку на объект TData диалогового окна. Вызов функции Execute() диалогового окна активизирует окно диалога и разрешает пользователю использовать его управляющие элементы для выбора файла.

    Пользователь может выйти из диалогового окна Open с помощью кнопок ОК или Cancel. Если пользователь выходит по кнопке ОК, то он выбрал файл, который надо открыть и имя которого хранится в элементе TData.FileName.

//TWndw::Paint()
// Эта функция переопределяет функцию Paint() из 
// TWindow и реагирует на сообщение WM_PAINT, которое 
// Windows посылает окну, если оно должно быть 
// повторно перерисовано.
void TWndw::Paint(TDC &paintDC, BOOL, TRect&)
{
  // Если имеется выбранный шрифт...
  if (font)
        // ... поместить его в контекст устройства.
        paintDC.SelectObject (*font); 
  // Установить цвет шрифта.
  paintDC.SetTextColor (fontColor);  
  // Вывести название шрифта в окне.
  //MessageBox(filen, "Ошибка",  MB_OK | MB_ICONEXCLAMATION);
  int tm=1;
  if (strcmp(filen,"")==0) { paintDC.TextOut(10, 10, "Произвольный текст"); }
  else {
    FILE *ReadF;
    ReadF=fopen(filen, "r");
    char  rline[300];
    do{
        fgets(rline,300,ReadF);
        if (feof(ReadF)) break;
        if (rline[strlen(rline)-1]== '\n') rline[strlen(rline)-1]=0;
        paintDC.TextOut(10, 10-fheight*tm, rline);
        tm++;
    } while(true);
    fclose(ReadF);
  }
//установить новые значения полосы прокрутки
Scroller->SetRange(0, (-1)*fheight*tm);
}

    Функция Paint() сначала проверяет, указывает ли font на объект нового шрифта (помните, что при запуске программы значение font равно нулю). Если новый шрифт существует, Paint() выбирает его в контекст устройства. В любом случае функция вызывает SetTextColor() вместе с fontColor, чтобы установить цвет шрифта, а затем вызывает TextOut() для вывода текста файла, либо строки по умолчанию.

    На следующем шаге мы рассмотрим реализацию игры Баше.




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