Операционная система Windows 95 для программиста

       

В этом разделе мы опишем


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

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


В переменной hInst хранится идентификатор приложения, полученный функцией WinMain. Строчные массивы szAppName и szAppTitle хранят, соответственно, имя и заголовок приложения.
Переменная hwndEdit используется для хранения идентификатора созданного органа управления Rich Edit.
Для инициализации DLL-библиотеки, отвечающей за работу органа управления Rich Edit, мы используем переменную hRTFLib (в нее записывается идентификатор загруженной библиотеки RICHED32.DLL).

WinMain


Функция WinMain не имеет никаких особенностей, за исключением того что в ней выполняется явная загрузка библиотеки RICHED32.DLL . Для этого мы используем функцию LoadLibrary :
hRTFLib = LoadLibrary("RICHED32.DLL"); if(!hRTFLib) return FALSE;

WndProc


Функция WndProc обрабатывает следующие сообщения: WM_CREATE, WM_DESTROY, WM_COMMAND, WM_SIZE и WM_SETFOCUS. Обработка выполняется с использованием макрокоманды HANDLE_MSG.

WndProc_OnCreate


Обработчик сообщения WM_CREATE создает орган управления Rich Edit. Размеры окна органа управления устанавливаются равными размерам внутренней области главного окна приложения и в дальнейшем изменяются обработчиком сообщения WM_SIZE.
После создания окно органа управления Rich Edit получает фокус ввода, для чего вызывается функция SetFocus , описанная нами в 12 томе "Библиотеки системного программиста":
SetFocus(hwndEdit);

WndProc_OnDestroy


Функция WndProc_OnDestroy вызывается, когда пользователь завершает работу приложения. Она уничтожает окно органа управления Rich Edit и освобождает загруженную при инициализации приложения библиотеку RICHED32.DLL, вызывая функцию FreeLibrary :


if(hRTFLib) FreeLibrary(hRTFLib);
Затем функция WndProc_OnDestroy останавливает цикл обработки сообщений, вызывая функцию PostQuitMessage.

WndProc_OnCommand




Функция WndProc_OnCommand обрабатывает сообщение WM_COMMAND, поступающее от главного меню приложения. Рассмотрим процедуры обработки сообщений для каждой строки меню. Название строки мы будем отделять от названия меню символом "/".
  • Format/Bold

  • Когда пользователь выделяет текст и выбирает из меню Format строку Bold, выделенный текст будет оформлен жирным шрифтом. Если же выбрать эту строку без предварительного выделения текста, указанное оформление получат символы, введенные после выполнения этой операции.
    После повторного выбора строки оформление жирным шрифтом отменяется.
    Соответствующий обработчик вначале определяет текущее оформление символов, посылая окну органа управления сообщение EM_GETCHARFORMAT :
    cf.cbSize = sizeof(cf); SendMessage(hwndEdit, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf);
    Ниже приведены параметры этого сообщения:
    wParam = (WPARAM)(BOOL)fSelection; lParam = (LPARAM)(CHARFORMAT FAR *)lpFmt;
    Параметр fSelection может принимать значения TRUE или FALSE. В первом случае будет определено оформление символов, принятое по умолчанию, во втором - оформление для выделенного текста.
    Параметр lpFmt должен содержать указатель на структуру типа CHARFORMAT , в которую будут записаны атрибуты форматирования. Эта структура имеет следующий формат:
    typedef struct _charformat { UINT cbSize; // размер структуры в байтах _WPAD _wPad1; // зарезервировано DWORD dwMask; // маски полей атрибутов DWORD dwEffects;// эффекты, использованные при оформлении LONG yHeight; // высота символов LONG yOffset; // смещение от базовой линии COLORREF crTextColor; // цвет текста BYTE bCharSet; // набор символов BYTE bPitchAndFamily; // семейство шрифтов TCHAR szFaceName[LF_FACESIZE]; // название шрифта _WPAD _wPad2; // зарезервировано } CHARFORMAT;
    Перед использованием структуры CHARFORMAT в поле cbSize следует записать размер структуры, как мы это сделали в приведенном выше примере.
    Если структура CHARFORMAT используется для установки формата, в поле dwMask следует записать маски, соответствующие устанавливаемым атрибутам оформления (сведения об этих атрибутах будут записаны в поле dwEffects, рассмотренное ниже, и в другие поля структуры CHARFORMAT).



    Значение маски Поля структуры CHARFORMAT
    CFM_BOLD Значение CFE_BOLD поля dwEffects
    CFM_COLOR Поле crTextColor и значение CFE_AUTOCOLOR в поле dwEffects
    CFM_FACE Поле szFaceName
    CFM_ITALIC Значение CFE_ITALIC поля dwEffects
    CFM_OFFSET Поле yOffset
    CFM_PROTECTED Значение CFE_PROTECTED поля dwEffects
    CFM_SIZE Поле yHeight
    CFM_STRIKEOUT Значение CFE_STRIKEOUT поля dwEffects
    CFM_UNDERLINE . Значение CFE_UNDERLINE поля dwEffects

    Если же вы применяете структуру CHARFORMAT для определения форматирования, в поле dwMask будут записаны маски для тех полей, в которых были занесены полученные значения.
    В поле dwEffects может находиться комбинация следующих значений (объединенных при помощи логической операции ИЛИ):


    Значение Описание
    CFE_AUTOCOLOR Для отображения текста используется системный цвет COLOR_WINDOWTEXT
    CFE_BOLD Символы выделены жирным шрифтом (bold)
    CFE_ITALIC Символы выделены наклоном (italic)
    CFE_STRIKEOUT Символы перечеркнуты
    CFE_UNDERLINE Символы выделены подчеркиванием
    CFE_PROTECTED Данная группа символов защищена от изменения. Если пользователь пытается их изменить, родительское окно получит извещение с кодом EN_PROTECTED

    Заметьте, что установив атрибут оформления CFE_PROTECTED, вы можете защитить часть текста от изменений со стороны пользователя, что может быть удобно при создании специализированных редакторов текста.
    Опишем кратко остальные поля структуры CHARFORMAT.
    Поле yHeight содержит высоту символов в логических единицах, соответствующих выбранному режиму отображения.
    Поле yOffset содержит смещение символов от базовой линии. Смещение может быть положительное (например, для надстрочных индексов) или отрицательное (для подстрочных индексов).
    В поле crTextColor заносится цвет символов. Подробное обсуждение структуры COLORREF и структуры LOGFONT вы сможете найти в 14 томе "Библиотеки системного программиста", который называется "Графический интерфейс GDI в Microsoft Windows".


    В поле bCharSet может находиться одно из значений, которое определено для поля lfCharSet структуры LOGFONT :


    Константа Описание
    ANSI_CHARSET Набор символов в кодировке ANSI
    DEFAULT_CHARSET Не используется при отображении шрифтов. Определяется при необходимости запросить шрифт с заданным именем и размером шрифта. Следует использовать с осторожностью, так как если указанного шрифта нет, GDI может выделить шрифт с любым набором символов
    SYMBOL_CHARSET Символьный шрифт, такой как, например, Wingdings
    SHIFTJIS_CHARSET Шрифт, в котором для представления символов используется двухбайтовая кодировка. Нужен для работы с японской версией Windows
    OEM_CHARSET Набор символов в кодировке OEM

    С помощью поля bPitchAndFamily можно определить, используется ли фиксированная или переменна ширина символов. Кроме этого, можно определить семейство, к которому должен принадлежать полученный шрифт.
    Фиксированная или переменная ширина символов задается при помощи следующих констант:


    Константа Описание
    DEFAULT_PITCH Не имеет значения, будет ли шрифт иметь фиксированную или переменную ширину символов
    FIXED_PITCH Нужен шрифт с фиксированной шириной символов
    VARIABLE_PITCH Нужен шрифт с переменной шириной символов

    Вы можете объединить при помощи логической операции ИЛИ эти константы со следующими константами, соответствующими семейству шрифта:


    Константа Описание
    FF_DECORATIVE Шрифт, содержащий маленькие рисунки (пиктограммы). Примером такого шрифта может послужить шрифт Wingdings, поставляемый в составе Windows
    FF_DONTCARE Семейство шрифта не имеет значения
    FF_MODERN Семейство Modern. Фиксированная ширина символов, могут быть засечки (но могут и не быть)
    FF_ROMAN Семейство Roman. Переменная ширина букв, есть засечки
    FF_SCRIPT Семейство Script. Рукописный шрифт
    FF_SWISS Семейство Swiss. Переменная ширина букв, нет засечек

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


    В нашем приложении после определения текущего оформления обработчик сообщения, поступающего от строки Bold меню Format, изменяет на противоположное содержимое бита CFE_BOLD в поле dwEffects структуры CHARFORMAT. Перед этим мы устанавливаем маску CFM_BOLD в поле dwMask этой же структуры:
    cf.dwMask = CFM_BOLD; cf.dwEffects ^= CFE_BOLD; SendMessage(hwndEdit,EM_SETCHARFORMAT,SCF_SELECTION, (LPARAM)&cf);
    Установка формата выполняется с помощью сообщения EM_SETCHARFORMAT. Это сообщение имеет следующие параметры:
    wParam = (WPARAM)(UINT)uFlags; lParam = (LPARAM)(CHARFORMAT FAR *)lpFmt;
    Параметр uFlags может принимать значения SCF_SELECTION или (SCF_SELECTION | SCF_WORD). В первом случае будет установлен формат выделенного фрагмента текста или формат, используемый по умолчанию (если выделение отсутствует). Если же дополнительно указано значение SCF_WORD, будет изменен формат слова, в позиции которого находится курсор, или формат выделенной группы слов.
    Параметр lpFmt должен указывать на предварительно подготовленную структуру типа CHARFORMAT, которая только что была нами описана.
  • Format/Italic

  • При выборе строки Italic из меню Format выполняется оформление с использованием наклонного начертания символов. При этом применяются только что описанные сообщения и структуры данных.
  • Format/Underline

  • Эта строка меню выделяет символы подчеркиванием. Соответствующий обработчик аналогичен тому, что вызывается для предыдущих двух строк меню Format.
  • Format/Font

  • Замечательной особенностью органа управления Rich Edit является возможность выбора для оформления символов любого шрифта. В нашем приложении пользователь может при помощи строки Font меню Format вызвать на экран стандартную диалоговую панель, с помощью которой можно выбрать шрифт.
    Вначале соответствующий обработчик определяет текущее оформление символов, посылая органу управления Rich Edit сообщение EM_GETCHARFORMAT.
    Затем определяется текущий контекст отображения и заполняется структура chfnt типа CHOOSEFONT, которая нужна для функции вызова стандартной диалоговой панели ChooseFont. Эта функция была описана в 14 томе "Библиотеки системного программиста", поэтому для экономии места мы не будем к ней возвращаться.


    После выбора шрифта его атрибуты копируются в структуру cf типа CHARFORMAT. Адрес этой структуры передается в качестве последнего параметра функции SendMessage, посылающей окну органа управления Rich Edit сообщение EM_SETCHARFORMAT :
    SendMessage(hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
    После установки нового шрифта обработчик освобождает полученный ранее контекст отображения.
  • Format/Paragraph/Left

  • Для изменения оформления параграфа наше приложение посылает окну органа управления Rich Edit сообщение EM_SETPARAFORMAT :
    pf.cbSize = sizeof(pf); pf.dwMask = PFM_ALIGNMENT; pf.wAlignment = PFA_LEFT; SendMessage(hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
    Параметр wParam этого сообщения не используется и должен быть равен нулю. Через параметр lParam передается адрес предварительно заполненной структуры типа PARAFORMAT:
    lParam = (LPARAM)(PARAFORMAT FAR *)lpFmt;
    Структура PARAFORMAT определена следующим образом:
    typedef struct _paraformat { UINT cbSize; // размер структуры в байтах _WPAD _wPad1; // зарезервировано DWORD dwMask; // поле масок WORD wNumbering; // порядок нумерации WORD wReserved; // зарезервировано LONG dxStartIndent; // отступ для первой строки параграфа LONG dxRightIndent; // отступ от правой границы листа LONG dxOffset; // отступ второй и следующих // строк параграфа WORD wAlignment; // выравнивание параграфа SHORT cTabCount; // количество символов табуляции LONG rgxTabs[MAX_TAB_STOPS];// массив абсолютных позиций // для символов табуляции } PARAFORMAT;
    Поле маски dwMask определяет, какие из остальных полей структуры PARAFORMAT будут использованы для установки формата параграфа. В этом поле могут находиться следующие значения:


    Значение маски Поля структуры PARAFORMAT
    PFM_ALIGNMENT wAlignment
    PFM_NUMBERING wNumbering
    PFM_OFFSET dxOffset
    PFM_OFFSETINDENT dxStartIndent
    PFM_RIGHTINDENT dxRightIndent
    PFM_STARTINDENT dxStartIndent
    PFM_TABSTOPS cTabStobs и rgxTabStops

    Для поля wNumbering вы можете указать нулевое значение или константу PFN_BULLET .


    Поле wAlignment структуры PARAFORMAT задает выравнивание параграфа. Вы можете указать здесь следующие значения:


    Значение Тип выравнивания параграфа
    PFA_LEFT По левой границе окна редактирования
    PFA_RIGHT По правой границе окна редактирования
    PFA_CENTER Центрирование

  • Format/Paragraph/Right

  • Эта строка меню предназначена для установки выравнивания параграфа по правой границе. Примененный в нашем приложении способ выполнения выравнивания был только что описан.
  • Format/Paragraph/Center

  • Выравнивание по центру выполняется аналогично выравниванию по левой и правой границе окна редактирования.
  • Edit/Undo

  • Edit/Cut

  • Edit/Copy

  • Edit/Paste

  • Edit/Delete

  • Обработка сообщений от строк Undo, Cut, Copy, Paste и Delete выполняется очень просто. Органу управления Rich Edit посылается соответствующее сообщение: EM_UNDO , WM_CUT , WM_COPY , WM_PASTE или WM_CLEAR . Например:
    case ID_EDIT_UNDO: SendMessage(hwndEdit, EM_UNDO, 0, 0L); return 0L; break;
  • Edit/Select all

  • В некоторых случаях удобно выполнять какую-либо операцию со всем текстом сразу. Для того чтобы можно было выделить весь текст, органу управления Rich Text посылается сообщение EM_EXSETSEL :
    CHARRANGE charr; charr.cpMin = 0; // от начала... charr.cpMax = -1; // ... и до конца текста SendMessage(hwndEdit, EM_EXSETSEL, 0, (LPARAM)&charr);
    Через параметр lParam этого сообщения необходимо передать адрес заполненной структуры типа CHARRANGE. Формат этой структуры приведен ниже:
    typedef struct _charrange { LONG cpMin; // номер первого выделяемого символа LONG cpMax; // номер последнего выделяемого символа } CHARRANGE;
    Для того чтобы выделить весь текст, в поле cpMin необходимо записать нулевое значение, а в поле cpMax - значение -1.
  • File/New

  • Когда пользователь выбирает строку New из меню File, содержимое редактора удаляется простейшим способом - при помощи функции SetWindowText :
    SetWindowText(hwndEdit,"\0");
    Отметим, что в нашем приложении не выполняется проверка, было ли предварительно выполнено сохранение редактируемого текста. При необходимости вы сможете доработать исходные тексты приложения, сделав его более "безопасным". Например, вы можете организовать обработку извещения EN_CHANGE , которое посылается, если пользователь изменяет содержимое редактируемого текста.


  • File/Open

  • Обработчик сообщения от строки Open меню File вызывает функцию FileOpen, определенную в нашем приложении. Эта функция позволяет загружать для редактирования обычные текстовые файлы или файлы в формате RTF .
    Функция FileOpen будет описана немного позже.
  • File/Save as

  • Аналогично, при выборе пользователем строки Save as из меню File, вызывается функция FileSaveAs, которая позволяет сохранить файл в обычном текстовом формате или в формате RTF. Позже мы рассмотрим исходный текст этой функции.
  • File/Print

  • Строка Print меню File позволяет пользователю распечатать содержимое редактируемого текста. Печать выполняется функцией FilePrint, определенной в нашем приложении. О ней мы расскажем позже.
  • File/Exit

  • С помощью строки Exit меню File пользователь может завершить работу приложения.
  • Help/About

  • Эта строка выдает некоторую информацию о приложении RtfPad.

    WndProc_OnSize


    Приложение изменяет размеры органа управления Rich Edit таким образом, чтобы они всегда соответствовали размерам внутренней области главного окна приложения. Для этого оно обрабатывает сообщение WM_SIZE , изменяя размеры окна органа управления при помощи функции MoveWindow.

    WndProc_OnSetFocus


    Когда главное окно приложения RtfPad получает фокус ввода, она передает его органу управления Rich Edit, вызывая для этого функцию SetFocus :
    SetFocus(hwndEdit);

    FileSaveAs


    Когда пользователь сохраняет редактируемый текст в файле, приложение вызывает функцию FileSaveAs. Эта функция выводит на экран стандартную диалоговую панель Save as, вызывая для этого функцию GetSaveFileName . Функцией GetSaveFileName мы уже пользовались, например, в приложении TEDIT, описанном в 12 томе "Библиотеки системного программиста".
    Когда пользователь выберет файл, путь к нему будет записан в переменную ofn.lpstrFile. Затем этот файл будет открыт для записи функцией OpenFile . Хотя в Microsoft Windows 95 и в Microsoft Windows NT существуют специальные средства для работы с файлами, пока мы используем этот хорошо знакомый вам способ, который тоже работает.


    После того как файл будет открыт, начнется процесс записи. Он несколько необычен, так как использует механизм обратного вызова функции, выполняющей запись данных в файл.
    Инициирование процесса записи выполняется посылкой сообщения EM_STREAMOUT , причем в зависимости от нужного формата выходного файла (текстовый формат или формат RTF) для этого сообщения указывается разное значение параметра wParam:
    _strupr(&ofn.lpstrFile[ofn.nFileExtension]); if(!strncmp(&ofn.lpstrFile[ofn.nFileExtension], "RTF", 3)) SendMessage(hwndEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es); else SendMessage(hwndEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
    В любом случае через параметр lParam необходимо передать адрес предварительно подготовленной структуры типа EDITSTREAM , которая определена следующим образом:
    typedef struct _editstream { DWORD dwCookie; DWORD dwError; EDITSTREAMCALLBACK pfnCallback; } EDITSTREAM;
    Через параметр dwCookie можно передать функции обратного вызова любое 32-разрядное значение. Так как в нашем случае функция обратного вызова будет выполнять запись в файл, через этот параметр мы передаем идентификатор открытого файла:
    es.dwCookie = (DWORD)hFile; es.dwError = 0; es.pfnCallback = SaveCallback;
    Через поле dwError передается код ошибки, поэтому перед началом процесса записи мы записываем в него нулевое значение.
    И, наконец, через поле pfnCallback необходимо передать адрес функции обратного вызова, которая будет выполнять запись данных в файл. Наша функция называется SaveCallback; вы можете выбрать любое другое имя. Функция обратного вызова SaveCallback будет описана ниже.
    После того как функция SendMessage возвратит управление, выходной файл следует закрыть.
    Дополнительно мы сбрасываем признак модификации редактируемого файла, так как все изменения были только что сохранены. Для этого мы посылаем органу управления Rich Edit сообщение EM_SETMODIFY :
    SendMessage(hwndEdit, EM_SETMODIFY, FALSE, 0L);
    Если параметр wParam этого сообщения имеет значение FALSE, флаг модификации сбрасывается, если TRUE - устанавливается.


    В любой момент времени вы можете определить значение флага модификации, послав органу управления Rich Edit сообщение EM_GETMODIFY . Если текст был изменен, функция SendMessage вернет значение TRUE, если нет - FALSE.

    SaveCallback


    Функция обратного вызова SaveCallback, которая используется для сохранения текста в файле, имеет следующий прототип:
    DWORD CALLBACK SaveCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
    Через первый параметр этой функции передается значение, которое было записано в поле dwCookie структуры EDITSTREAM, подготовленной для сообщения EM_STREAMOUT.
    Через параметр pbBuff передается адрес буфера, откуда необходимо взять данные для записи в файл. Размер этого буфера указывается параметром cb. Количество действительно записанных байт следует записать по адресу, который передается через последний параметр pcb.
    Если все нужные данные были записаны, функция обратного вызова должна вернуть нулевое значение, в противном случае для продолжения процесса записи следует возвратить значение, отличное от нуля.

    FileOpen


    Функция FileOpen вызывается в том случае, когда пользователь загружает новый файл для редактирования.
    Выбор файла выполняется с помощью стандартной диалоговой панели Open, которая отображается на экране функцией GetOpenFileName . Эта функция использовалась нами в 12 томе "Библиотеки системного программиста".
    После того как файл выбран, он открывается функцией OpenFile для чтения. Далее мы подготавливаем структуру типа EDITSTREAM для функции обратного вызова OpenCallback, которая будет выполнять чтение файла.
    Затем, в зависимости от расширения имени выбранного файла, мы загружаем этот файл как текстовый или как файл, имеющий формат RTF. Для загрузки органу управления Rich Edit посылается сообщение EM_STREAMIN .
    После выполнения чтения файл закрывается, а флаг модификации содержимого редактора текста сбрасывается при помощи сообщения EM_SETMODIFY .

    OpenCallback


    Функция обратного вызова OpenCallback выполняет чтение файла в память органа управления Rich Edit. Прототип этой функции уже был описан, однако теперь назначение некоторых ее параметров и возвращаемое значение изменились.


    Через параметр dwCookie передается идентификатор файла, из которого будет выполняться чтение. Чтение данных необходимо выполнять в буфер pbBuff, размер которого передается через параметр cb. По адресу pcb необходимо записать количество действительно прочитанных байт или 0, если достигнут конец файла.
    Функция обратного вызова должна возвратить количество прочитанных байт или 0, если был достигнут конец файла. Так как мы читаем весь файл за один вызов функции _lread, то функция возвращает нулевое значение.

    FilePrint


    Функция FilePrint, как видно из ее названия, выполняет печать файла. При этом в приложении RtfPad используется упрощенный вариант технологии, описанной нами в 14 томе "Библиотеки системного программиста".
    Функция выводит на экран стандартную диалоговую панель Print, вызывая для этого функцию PrintDlg . Кроме всего прочего, эта функция получает контекст устройства для выбранного принтера, который сохраняется в переменной hPrintDC.
    Как вы знаете, процесс печати из приложений Microsoft Windows полностью отличается от того, к чему вы, возможно, привыкли в DOS. Печать выполняется теми же функциями GDI , с помощью которых вы рисуете изображение на экране видеомонитора.
    Орган управления Rich Edit способен самостоятельно выполнять печать, если известен контекст устройства печати (принтера). Для этого ему достаточно переслать сообщение EM_FORMATRANGE . Это сообщение имеет следующие параметры:
    wParam = (WPARAM)(BOOL)fRender; lParam = (LPARAM)(FORMATRANGE FAR *)lpFmt;
    Если значение параметра fRender равно TRUE, орган управления Rich Edit выполняет форматирование текста и его печать, если же FALSE - выполняется только форматирование с целью определения геометрических размеров, которые будут получены при печати.
    Через параметр lpFmt должен передаваться указатель на предварительно подготовленную структуру типа FORMATRANGE , определенную следующим образом:
    typedef struct _formatrange { HDC hdc; HDC hdcTarget; RECT rc; RECT rcPage; CHARRANGE chrg; } FORMATRANGE;
    В поле hdc необходимо записать идентификатор контекста устройства, на котором будет выполняться отображение (печать). Поле hdcTarget должно содержать идентификатор контекста устройства, для которого необходимо выполнить форматирование. Наше приложение записывает в эти поля идентификатор контекста принтера, полученный при вызове функции PrintDlg .
    В структуру rc следует записать размеры области, в которую будет выполняться вывод, а в структуру rcPage - размеры листа бумаги. Наше приложение оставляет небольшие поля по краям листа. Размеры должны быть указаны в TWIPS -ах (один TWIPS составляет 1/1440 часть дюйма).
    Структура chrg описывает фрагмент текста, который должен быть выведен на печать. Приложение RtfPad выводит текст целиком, однако при необходимости вы сможете организовать печать только выделенного фрагмента или текущего листа. Оставляем вам для упражнения реализацию этих возможностей.

    Содержание раздела