Шаг 34.
Библиотека OWL.
Другой подход к печати

    На этом шаге мы приведем пример программы, печатающей вкладыши для аудиокассет.

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

    Класс TPrintout библиотеки OWL распечатает все, для чего вы напишете команды в программе. Характерно, что функция PrintPage() объекта TPrintout может быть любой, в зависимости от вашего желания. Следующий пример для печати вкладышей аудиокассет демонстрирует эти возможности.

    Файл wincass.cpp - класс приложения.

#include <owl\applicat.h>
#include <owl\framewin.h>
#include "twcwnd.h"

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


void TApp::InitMainWindow()
{
  // Создать главное окно приложения, которое в
  // данной программе представляет собой в
  // действительности диалоговое окно.
  TFrameWindow *wndw = new TFrameWindow(0, "Печать вкладышей",
	  new TWCWnd(0,"DIALOG1"), TRUE);
  // Определить расположение родительского окна.
  wndw->Attr.X = 20;
  wndw->Attr.Y = 20;
  // Установить значение указателя приложения MainWindow.
  SetMainWindow(wndw);
  // Активизировать библиотеку трехмерных элементов.
  EnableBWCC();
}

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

    Файл twcwnd.h:

// Заголовочный файл для класса
// пользовательского окна WinCassette.
#ifndef __TWCWND_H
#define __TWCWND H

#include <owl\dialog.h> 
#include <owl\printer.h> 
#include <owl\static.h> 
#include <owl\button.h> 
#include <owl\edit.h> 
#include <string.h>

// Тип данных буфера обмена.
struct TTransRec
{
   char cassTitle[38] ,  // Название кассеты.
   sideTitle[38] ;       // Название для стороны А или В.
   // Названия песен.
   char edit1[38],  edit2[38],  edit3[38],  edit4[38], 
        edit5[38],  edit6[38],  edit7[38],  edit8[38], 
        edit9[38], edit10[38], edit11[38], edit12[38], 
       edit13[38], edit14[38], edit15[38], edit16[38];
};
// Класс главного окна. 
class TWCWnd : public TDialog
{
  protected:
    int sideA;
    TPrinter *printer; 
    TStatic *sideStr; 
    TButton *sideButn;
  public:
    TWCWnd(TWindow *parent, TResId resld); 
    ~TWCWnd();
  protected:
    void CmSide(); 
    void CmPrint(); 
    void CmAbout();

    DECLARE_RESPONSE_TABLE(TWCWnd);
};
#endif

    Файл twcwnd.cpp:

// Реализация  класса
// пользовательского окна WinCassette.
#include "twcwnd.h" 
#include "cassprnt.h"
#include "wincass.rc"

TTransRec sideAStrgs,  // Буфер данных стороны А.
          sideBStrgs,  // Буфер данных стороны В.
          dlgStrgs;    // Буфер передачи для  диалогового окна.
  
// Определение таблицы откликов.
DEFINE_RESPONSE_TABLE1(TWCWnd, TDialog) 
	EV_COMMAND(ID_SIDE,  CmSide),
   EV_COMMAND(ID_PRINT, CmPrint), 
   EV_COMMAND(ID_ABOUT, CmAbout),
END_RESPONSE_TABLE;

// TWCWnd::TWCWnd()
// Это конструктор пользовательского окна.
TWCWnd::TWCWnd(TWindow *parent, TResId resId):
        TDialog (parent, resId) 
{
  // Создать OWL-объекты областей ввода. 
  for (int  x = 101; x <= 119; ++x)
            new TEdit(this, x, 38);
  // Установить значения всех буферов в NULL. 
  memset(&dlgStrgs, 0, sizeof dlgStrgs); 
  memset(&sideAStrgs, 0, sizeof sideAStrgs); 
  memset(&sideBStrgs, 0, sizeof sideBStrgs);
  // Установить адрес буфера обмена для диалогового окна. 
  TransferBuffer = &dlgStrgs;
  // Создать OWL-объекты для статических строк. 
  sideStr = new TStatic(this, ID_TITLESTRING, 7); 
  sideButn = new TButton(this, ID_SIDE);
  // Начать со стороны A. 
  sideA = TRUE;
  // Создать оконный объект принтера. 
  printer = new TPrinter;
}

//TWCWnd::~TWCWnd()
// Это деструктор пользовательского окна.
TWCWnd::~TWCWnd()
{
  delete printer;
}

// TWCWnd::CmSide()
// Эта функция вызывается, когда пользователь 
// нажимает кнопку Side.
void TWCWnd::CmSide() 
{
  // Передать данные из областей ввода в буфер обмена.
  TransferData(tdGetData);
  // Если пользователь был на стороне А...
  if (sideA)
  {
    // Скопировать данные диалога в буфер стороны А.
    sideAStrgs = dlgStrgs;
    // Изменить текст в кнопке Side и в 
    // статической строке стороны. 
    sideStr->SetText("Side В:"); 
    sideButn->SetWindowText("&Side A");
    // Скопировать название кассеты, так как оно 
    // то же самое и для стороны В. 
    strcpy(sideBStrgs.cassTitle,sideAStrgs.cassTitle);
    // Скопировать буфер стороны В в буфер передачи.
    dlgStrgs = sideBStrgs;
    // Установить новый флаг стороны.
    sideA = FALSE; 
  }
  // Иначе если на стороне В... 
  else {
    // Скопировать данные диалога в буфер стороны В.
    sideBStrgs = dlgStrgs;
    // Изменить текст кнопки Side и статической строки стороны.
    sideStr->SetText("Side A:"); 
    sideButn->SetWindowText("&Side В");
    // Скопировать название кассеты, если оно изменилось.
    strcpy(sideAStrgs.cassTitle,sideBStrgs.cassTitle);
    // Скопировать буфер стороны А в буфер обмена. 
	 dlgStrgs = sideAStrgs;
    // Установить новый флаг стороны.
    sideA = TRUE; 
    }
  // Установить фокус в поле ввода названия стороны.
  HWND hControl = GetDlgItem(ID_SIDETITLE); 
  ::SetFocus(hControl);
  // Скопировать данные буфера обмена в области
  // ввода диалогового окна. 
  TransferData(tdSetData);
} 

// TWCWnd::CmPrint() 
// Эта функция вызывается, когда пользователь
// нажимает кнопку Print.
void TWCWnd::CmPrint()
{
  // Скопировать данные из областей ввода
  // диалогового окна в буфер обмена.
  TransferData(tdGetData);
  // Скопировать данные из буфера обмена е
  // соответствукщий буфер, в зависимости от текущей стороны.
  if (sideA) sideAStrgs = dlgStrgs;
  else  sideBStrgs = dlgStrgs;
  // Обработать объект  печати. 
  TCassPrintout printout("Печать вкладышей");
  // Напечатать вкладыш кассеты. 
  printer->Print(this, printout, TRUE);
}

// TWCWnd::CmAbout()
// Эта функция вызывается, когда пользователь 
// нажимает кнопку About.
void TWCWnd::CmAbout()
{
  char s[70];
  //  Построить отображаемую строку.
  strcpy(s, " Печать вкладышей 1.1 ");
  s[22] = 13;
  strcpy(&s[23] , " Автор: Клейтон Уолмен  ");
  s[47] = 13;
  s[48] = 13;
  strcpy(&s[49], "    Copyright 1994 ");
  // Использовать окно сообщений для вывода 
  // информации  "ABOUT". 
  MessageBox(s, "О программе", MB_OK);
}

    Файл cassprnt.h:

// Заголовочный файл для класса
// TPrintout печати WinCassette.
#ifndef  __CASSPRNT_H
#define  __CASSPRNT_H
#include <owl\printer.h>

// Класс печати.
class TCassPrintout : public TPrintout
{
  protected:
    // Указатели на названия песен. 
    char *sideAEdits[16],*sideBEdits[16]; 
  public:
    TCassPrintout(const char *title); 
  protected:
    void GetDialoglnfo(int &minPage, int &maxPage,
          int &selFromPage, int &selToPage); 
    void PrintPage(int, TRect&, unsigned);
  private:
    void DrawLabel(int horDots, int verDots); 
    void DrawBodyText(int horDots, int verDots); 
    void DrawTitleText(int horDots, int verDots); 
    TFont* CreateCassFont(int hSize, int vSize);
};
#endif

    Файл cassprnt.cpp:

//Реализация класса TPrintout печати WinCassette.
#include "cassprnt.h"
#include "twcwnd.h"
extern TTransRec sideAStrgs, // Буфер для данных стороны А.
                 sideBStrgs; // Буфер для данных стороны В.

// TCassPrintout::TCassPrintout()
// Это конструктор класса объекта печати.
TCassPrintout::TCassPrintout(const char *title) :
            TPrintout(title)
{
  // Сохранить адреса всех названий песен. 
  for (int x=0; x<16; ++x)
  {
	  sideAEdits[x] = sideAStrgs.edit1 + x*38;
	  sideBEdits[x] = sideBStrgs.edit1 + x*38;
  }
}

// TCassPrintout::GetDialoglnfo()
// Эта функция устанавливает значения, используемые
// в диалоговом окне обработки прерываний принтера.
void TCassPrintout::GetDialoglnfo(int &minPage,
		  int &maxPage, int &selFromPage, int &selToPage)
{
  minPage = 0;
  maxPage = 0;
  selFromPage = 0;
  selToPage = 0;
}

// TCassPrintout::PrintPage()
// Эта функция вызывается для каждой страницы
// документа. Именно в этом фрагменте программа
// создает изображение, которое появится на принтере.
void TCassPrintout::PrintPage(int, TRect&, unsigned)
{
  // Получить горизонтальное и вертикальное
  // разрешение для принтера.
  int horDots = DC->GetDeviceCaps(LOGPIXELSX);
  int verDots = DC->GetDeviceCaps(LOGPIXELSY);
  // Нарисовать чертеж вкладыша кассеты.
  DrawLabel(horDots, verDots);
  // Напечатать название стороны и названия песен.
  DrawBodyText(horDots, verDots);
  // Напечатать основные названия кассеты.
	DrawTitleText(horDots, verDots);
}

// TCassPrintout::DrawLabel()
// Эта функция рисует чертеж вкладыша кассеты.
void TCassPrintout::DrawLabel(int horDots, int verDots)
{
  DC->MoveTo(horDots/2, verDots/2);
  DC->LineTo(horDots*4.5, verDots/2);
  DC->LineTo(horDots*4.5, verDots*4.3);
  DC->LineTo(horDots/2, verDots*4.3);
  DC->LineTo(horDots/2, verDots/2);
  DC->MoveTo(horDots/2, verDots*0.75);
  DC->LineTo(horDots*4.5, verDots*0.75);
  DC->MoveTo(horDots/2, verDots*0.93);
  DC->LineTo(horDots*4.5, verDots*0.93);
  DC->MoveTo(horDots/2, verDots*3.1);
  DC->LineTo(horDots*4.5, verDots*3.1);
  DC->MoveTo(horDots/2, verDots*3.62);
  DC->LineTo(horDots*4.5, verDots*3.62);
  DC->MoveTo(horDots*2.5, verDots*0.75);
  DC->LineTo(horDots*2.5, verDots*3.1);
}

// TCassPrintout::DrawBodyText()
// Эта функция печатает название стороны и
// названия песен.
void TCassPrintout::DrawBodyText(int horDots,int verDots)
{
  TEXTMETRIC metrics;// Описание физического шрифта.
  char s[80];        // Строка выводимого текста.
  // Создать и выбрать шрифт для текста кассеты.
  TFont *newFont = CreateCassFont(horDots/30,verDots/8);
  DC->SelectObject(*newFont);
  // Получить размеры физического шрифта.
  DC->GetTextMetrics(metrics);
  // Создать и напечатать названия сторон А и В.
  strcpy(s, "Side А:");
  strcpy(&s[strlen(s)], sideAStrgs.sideTitle);
  DC->TextOut(horDots*0.6, verDots*0.77, s, strlen(s));
  strcpy(s, "Side B:");
  strcpy(&s[strlen(s)], sideBStrgs.sideTitle);
  DC->TextOut(horDots*2.6, verDots*0.77, s, strlen(s));
  // Напечатать названия песен для сторон А и В.
  for (int x=0; x<16; ++x)
  {
	 DC->TextOut((int) (horDots*0.6), (int)
		 (verDots + x*metrics.tmHeight), sideAEdits[x],
		  strlen(sideAEdits[x]));
	 DC->TextOut((int) (horDots*2.6), (int)
		 (verDots + x*metrics.tmHeight), sideBEdits[x],
		  strlen(sideBEdits[x]));
  }
  delete newFont;
}

// TCassPrintout::DrawTitleText()
// Эта функция печатает основные названия песен.
void TCassPrintout::DrawTitleText(int horDots,int verDots)
{
  int strWidth; // Ширина текстовой строки.
  // Создать новый шрифт названия.
  TFont *newFont = CreateCassFont(horDots/20, verDots/20);
  DC->SelectObject(*newFont);
  // Получить ширину и высоту строки названия.
  TSize size = DC->GetTextExtent(sideAStrgs.cassTitle,
		 strlen(sideAStrgs.cassTitle));
  // Получить ширину строки названия.
  strWidth = size.cx;
  // Напечатать основные названия кассеты.
  DC->TextOut((horDots*2.5)-(strWidth/2),verDots*0.52,
  sideAStrgs.cassTitle,strlen(sideAStrgs.cassTitle));
  DC->TextOut((horDots*2.5)-(strWidth/2),verDots*3.25,
  sideAStrgs.cassTitle,strlen(sideAStrgs.cassTitle));
  delete newFont;
}

// TCassPrintout::CreateCassFont()
// Эта функция создает шрифты для вкладыша кассеты.
// Шрифты отличаются только своими горизонтальными
// и вертикальными размерами.
TFont* TCassPrintout::CreateCassFont(int hSize, int vSize)
{
  LOGFONT cassLogFont; // Описание логического шрифта.
  // Заполнить структуру LOGFONT.
  cassLogFont.lfHeight = vSize;
  cassLogFont.lfWidth = hSize;
  cassLogFont.lfEscapement = 0;
  cassLogFont.lfOrientation = 0;
  cassLogFont.lfWeight = FW_NORMAL;
  cassLogFont.lfItalic = 0;
  cassLogFont.lfUnderline = 0;
  cassLogFont.lfStrikeOut = 0;
  cassLogFont.lfCharSet   = ANSI_CHARSET;
  cassLogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
  cassLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  cassLogFont.lfQuality = PROOF_QUALITY;
  cassLogFont.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
  strcpy(cassLogFont.lfFaceName, "Times New Roman");
  // Создать новый шрифт.
  return new TFont(&cassLogFont);
}

    Файл ресурсов wincass.rc:

//Файл ресурсов WinCassette.

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

#define ID_SIDE         201
#define ID_PRINT        202
#define ID_ABOUT        203
#define ID_SIDETITLE    102
#define ID_TITLESTRING  300

#ifdef RC_INVOKED

#include <owl\printer.rc>

DIALOG1 DIALOG 5, -24, 276, 202
STYLE DS_MODALFRAME | WS_CHILD | WS_VISIBLE |
       WS_CAPTION | WS_SYSMENU 
CLASS  "BorDlg"
CAPTION  "Печать вкладышей"
{

  EDITTEXT 101,24,6,98,12, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD |
		WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT ID_SIDETITLE, 158, 6, 97, 12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 103,23,28,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 104,23,45,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 105,23,63,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 106,23,81,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 107,23,99,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 108,23,117,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 109,23,135,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 110,23,153,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 111,155,28,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 112,155,45,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 113,155,63,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 114,155,81,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 115,155,99,100,12, ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 116,155,117,100,12,ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 117,155,135,100,12,ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  EDITTEXT 118,155,153,100,12,ES_LEFT | ES_AUTOHSCROLL |
		WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
  CONTROL  "&Side B", ID_SIDE,   "BorBtn",   BS_PUSHBUTTON   |
	  WS_CHILD  | WS_VISIBLE | WS_TABSTOP,   14,176,32,20
  CONTROL  "&Print",  ID_PRINT,  "BorBtn",   BS_PUSHBUTTON   |
	  WS_CHILD  | WS_VISIBLE | WS_TABSTOP,   57,176,32,20
  CONTROL   "&About", ID_ABOUT,  "BorBtn",   BS_PUSHBUTTON   |
	  WS_CHILD  | WS_VISIBLE | WS_TABSTOP,  100,176,32,20
  CONTROL   "Quit", 2,  "BorBtn",  BS_PUSHBUTTON   | WS_CHILD |
	  WS_VISIBLE  |   WS_TABSTOP,  230,176,34,20
  LTEXT " Side A: ", ID_TITLESTRING, 133, 8, 24, 8
  LTEXT " 1 ", - 1, 15,  30, 5, 8
  LTEXT " 2 ", - 1, 15,  47, 5, 8
  LTEXT " 3 ", - 1, 15,  65, 5, 8
  LTEXT " 4 ", - 1, 15,  83, 5, 8
  LTEXT " 5 ", - 1, 15, 101, 5, 8
  LTEXT " 6 ", - 1, 15, 119, 5, 8
  LTEXT " 7 ", - 1, 15, 137, 5, 8
  LTEXT " 8 ", - 1, 15, 155, 5, 8
  LTEXT " 9 ", - 1, 147, 30, 5, 8
  LTEXT " 10 ",	- 1, 144, 47, 8, 8
  LTEXT " 11 ",	- 1, 144, 65, 8, 8
  LTEXT " 12 ",	- 1, 144, 83, 8, 8
  LTEXT " 13 ",	- 1, 144, 101, 8, 8
  LTEXT " 14 ",	- 1, 144, 119, 8, 8
  LTEXT " 15 ",	- 1, 144, 137, 8, 8
  LTEXT " 16 ",	- 1, 144, 155, 8, 8
  LTEXT " Title : ", - 1, 6, 8,	16, 8
  CONTROL " ", 119, "BorShade", BSS_HDIP | WS_CHILD |
		  WS_VISIBLE , 4, 22, 269, 2
  CONTROL " ", 120, "BorShade", BSS_HDIP | WS_CHILD |
		  WS_VISIBLE , 4, 170, 269, 1

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

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


Рис.1. Пример работы приложения

    Напечатайте основное название аудиокассеты в первой области ввода (в верхнем левом углу). Затем нажмите Tab и напечатайте название стороны А. Далее напечатайте не более 16 названий песен для стороны А этой кассеты, а затем нажмите кнопку Side В, чтобы ввести названия песен для стороны В. Наконец, нажмите кнопку Print для того, чтобы напечатать вкладыш для кассеты.

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

    На следующем шаге мы рассмотрим принципы работы приведенной программы.




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