// =====================================================
// Редактор текста RTFPAD, способный работать
// с мышью Microsoft IntelliMouse
//
// (C) Фролов А.В, 1996, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================

// Это определение нужно для того, чтобы при компилляции
// файла winuser.h были подключены определения 
// идентификаторов SPI_GETWHEELSCROLLLINES
// и WM_MOUSEWHEEL

#define _WIN32_WINNT 0x0400

// Еще один способ определения этих же идентификаторов

//#ifndef SPI_GETWHEELSCROLLLINES
//#define SPI_GETWHEELSCROLLLINES   104
//#endif
//#ifndef WM_MOUSEWHEEL
//#define WM_MOUSEWHEEL WM_MOUSELAST+1
//#endif

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <richedit.h>

// Необходимо для определения значения константы 
// UINT_MAX, которая используется в файле zmouse.h
#include <limits.h>

#include "resource.h"
#include "afxres.h"

// Файл определений для IntelliMouse 
#include "zmouse.h"

#include "rtfpad.h"

// Код сообщения MSH_MOUSEEHEEL
UINT uMSH_MOUSEEHEEL   = 0;

// Код сообщения MSH_SUPPORT
UINT uMSH_SUPPORT      = 0;

// Код сообщения MSH_SCROLL_LINES
UINT uMSH_SCROLL_LINES = 0;

// Идентификатор окна для посылки сообщений
// приложению MSWheel
HWND hwndMSHWheel  = NULL;

// Флаг наличия мыши Microsoft IntelliMouse
BOOL fWheel        = FALSE;

// Количество строк свертки
UINT uiScrollLines = 3;

// Структура для определения версии
// операционной системы
OSVERSIONINFO osv;


HINSTANCE hInst;
char szAppName[]  = "RtfEditApp";
char szAppTitle[] = "Rich Text Editor RtfPad";
HWND hwndEdit;
HINSTANCE hRTFLib;

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  hInst = hInstance;
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    if(IsIconic(hWnd))
	  ShowWindow(hWnd, SW_RESTORE);
	  SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Определяем версию операционной системы
  memset(&osv, 0, sizeof(OSVERSIONINFO));
  osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx(&osv);

  // Для Windows 95 и Windows NT версии 3.51 выполняем
  // регистрацию сообщений
  if( (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) ||
     ((osv.dwPlatformId == VER_PLATFORM_WIN32_NT) && 
      (osv.dwMajorVersion < 4)))
  {
    // Регистрируем сообщение MSH_WHEELSUPPORT
    uMSH_SUPPORT = 
      RegisterWindowMessage(MSH_WHEELSUPPORT);
    
    // Определяем наличие мыши Microsoft IntelliMouse
    hwndMSHWheel = 
      FindWindow(MSH_WHEELMODULE_CLASS, 
      MSH_WHEELMODULE_TITLE);
    
    if(uMSH_SUPPORT != 0 && hwndMSHWheel != 0)
    {
      fWheel = 
        (BOOL)SendMessage(hwndMSHWheel, uMSH_SUPPORT, 0, 0);
    }

    if(!fWheel)
    {
      MessageBox(NULL, "MS Wheel not supported",
        "Error message", MB_OK);
    }
  
    // Регистрируем сообщение MSH_MOUSEWHEEL
    uMSH_MOUSEEHEEL = RegisterWindowMessage(MSH_MOUSEWHEEL);
    if(!uMSH_MOUSEEHEEL)
    {
      MessageBox(NULL, "Error: RegisterWindowMessage",
       "Error message", MB_OK);
      return FALSE;
    }

    // Регистрируем сообщение MSH_SCROLL_LINES
    uMSH_SCROLL_LINES = 
      RegisterWindowMessage(MSH_SCROLL_LINES);
    
    // Определяем количество строк свертки
    if(uMSH_SCROLL_LINES != 0 && hwndMSHWheel != 0)
    {
      uiScrollLines = 
        (BOOL)SendMessage(hwndMSHWheel, 
        uMSH_SCROLL_LINES, 0, 0);
    }
  }

  // Для Windows NT версии 4.0 применяем другую методику
  else
  {
    // Проверяем наличие мыши Microsoft IntelliPoint
    
    if(!GetSystemMetrics(SM_MOUSEWHEELPRESENT))
    {
      MessageBox(NULL, 
        "Microsoft IntelliMouse not found",
        "Error message", MB_OK);
    }

    // Определяем количество строк свертки
    SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 
      0, &uiScrollLines, 0);
  }

  hRTFLib = LoadLibrary("RICHED32.DLL");
  if(!hRTFLib)
    return FALSE;

  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
	  return FALSE;
    
  hWnd = CreateWindow(szAppName, szAppTitle, 
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 
    CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  // Изменение положения колеса
  short zDelta;

  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_CREATE,     WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY,    WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_COMMAND,    WndProc_OnCommand);
    HANDLE_MSG(hWnd, WM_SIZE,       WndProc_OnSize);
    HANDLE_MSG(hWnd, WM_SETFOCUS,   WndProc_OnSetFocus);

	  default:
    {
      if(msg == WM_MOUSEWHEEL)
      {
        zDelta = (short)HIWORD(wParam);

        if(zDelta < 0)
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0, uiScrollLines);
        
          else
            SendMessage(hwndEdit, EM_SCROLL, (WPARAM)(INT)SB_PAGEDOWN, 0);
        else
          
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0, -(LPARAM)uiScrollLines);
          
          else
            SendMessage(hwndEdit, EM_SCROLL, (WPARAM)(INT)SB_PAGEUP, 0);
        return 0L;
      }

      else if(msg == uMSH_MOUSEEHEEL)
      {
        zDelta = (short)wParam;

        if(zDelta < 0)
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0, uiScrollLines);
        
          else
            SendMessage(hwndEdit, EM_SCROLL, (WPARAM)(INT)SB_PAGEDOWN, 0);
        else
          
          if(uiScrollLines != WHEEL_PAGESCROLL)
            SendMessage(hwndEdit, EM_LINESCROLL, 0, -(LPARAM)uiScrollLines);
          
          else
            SendMessage(hwndEdit, EM_SCROLL, (WPARAM)(INT)SB_PAGEUP, 0);
        
        return 0L;
      }

      return(DefWindowProc(hWnd, msg, wParam, lParam));
    }
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
  RECT rc;

  // Определяем размеры внутренней области главного окна
  GetClientRect(hWnd, &rc);

  // Создаем орган управления Rich Edit
  hwndEdit = CreateWindowEx(0L, "RICHEDIT", "",
    WS_VISIBLE | WS_CHILD | WS_BORDER | WS_HSCROLL | WS_VSCROLL |
    ES_NOHIDESEL | ES_AUTOVSCROLL | ES_MULTILINE | 
    ES_SAVESEL | ES_SUNKEN,
    0, 0, rc.right - rc.left, rc.bottom - rc.top,
    hWnd, (HMENU) IDC_RTFEDIT, hInst, NULL);

  if(hwndEdit == NULL)
    return FALSE;

  // Передаем фокус ввода органу управления Rich Edit
  SetFocus(hwndEdit);

  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  // Уничтожаем орган управления Rich Edit
  if(hwndEdit)
    DestroyWindow(hwndEdit);

  // Освобождаем библиотеку RICHED32.DLL
  if(hRTFLib)
    FreeLibrary(hRTFLib);

  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  CHARFORMAT cf;
  CHOOSEFONT chfnt;
  LOGFONT lf;
  HDC hDC;
  PARAFORMAT pf; 

  switch (id)
  {
    // Изменяем жирность символов
	case ID_FORMAT_BOLD:
    {
	  cf.cbSize = sizeof(cf);
      
      // Определяем формат символов
      SendMessage(hwndEdit, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf);

      // Изменяем бит поля dwEffects, с помощью которого
      // можно выделить символы как bold (жирное начертание)
      cf.dwMask = CFM_BOLD;

      // Инвертируем бит, определяющий жирное начертание
      cf.dwEffects ^= CFE_BOLD;
               
      // Изменяем формат символов
      SendMessage(hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);

	  return 0L;
	  break;
    }

	// Устанавливаем или отменяем наклонное
    // начертание символов
	case ID_FORMAT_ITALIC:
    {
	  cf.cbSize = sizeof(cf);
      SendMessage(hwndEdit, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf);

      cf.dwMask = CFM_ITALIC;
      cf.dwEffects ^= CFE_ITALIC;
      SendMessage(hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);

	  return 0L;
	  break;
    }

	// Устанавливаем или отменяем выделение
    // символов подчеркиванием
	case ID_FORMAT_UNDERLINE:
    {
	  cf.cbSize = sizeof(cf);
      SendMessage(hwndEdit, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf);

      cf.dwMask = CFM_UNDERLINE;
      cf.dwEffects ^= CFE_UNDERLINE;
      SendMessage(hwndEdit, EM_SETCHARFORMAT, 
        SCF_SELECTION, (LPARAM)&cf);

	  return 0L;
	  break;
    }

	// Изменяем шрифт символов
	case ID_FORMAT_SETFONT:
    {
	  cf.cbSize = sizeof(cf);
      
      // Определяем текущий формат символов
      SendMessage(hwndEdit, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf);

      // Сбрасываем содержимое структур, которые будут
      // использоваться для выбора шрифта
      memset(&chfnt, 0, sizeof(chfnt));
      memset(&lf, 0, sizeof(lf));

      // Получаем контекст отображения
      hDC = GetDC(hWnd);

      // Если было задано выделение наклоном или жирным шрифтом,
      // подбираем шрифт с соответствующими атрибутами
      lf.lfItalic    = (BOOL)(cf.dwEffects & CFE_ITALIC);
      lf.lfUnderline = (BOOL)(cf.dwEffects & CFE_UNDERLINE);

      // Преобразуем высоту из TWIPS-ов в пикселы.
      // Устанавливаем отрицательный знак, чтобы 
      // выполнялось преобразование и использовалось абсолютное
      // значение высоты символов
      lf.lfHeight = - cf.yHeight/20;

      // Набор символов, принятый по умолчанию
      lf.lfCharSet = ANSI_CHARSET;

      // Качество символов, принятое по умолчанию
      lf.lfQuality = DEFAULT_QUALITY;

      // Выбираем семейство шрифтов
      lf.lfPitchAndFamily = cf.bPitchAndFamily;

      // Название начертания шрифта
      lstrcpy(lf.lfFaceName, cf.szFaceName);

      // Устанавливаем вес шрифта в зависимости от того,
      // было использовано выделение жирным шрифтом 
      // или нет
      if(cf.dwEffects & CFE_BOLD)
        lf.lfWeight = FW_BOLD;
      else
        lf.lfWeight = FW_NORMAL;

      // Заполняем структуру для функции выбора шрифта
      chfnt.lStructSize = sizeof(chfnt);
      chfnt.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT;
      chfnt.hDC         = hDC;
      chfnt.hwndOwner   = hWnd;
      chfnt.lpLogFont   = &lf;
      chfnt.rgbColors   = RGB(0,0,0);
      chfnt.nFontType   = SCREEN_FONTTYPE;

      // Выводим на экран диалоговую панель для
      // выбора шрифта
      if(ChooseFont(&chfnt))
      {
        // Можно использовать все биты поля dwEffects
        cf.dwMask = CFM_BOLD | CFM_FACE | CFM_ITALIC |
          CFM_UNDERLINE | CFM_SIZE | CFM_OFFSET;

        // Преобразование в TWIPS-ы
        cf.yHeight = - lf.lfHeight * 20;

        // Устанавливаем поле dwEffects 
        cf.dwEffects = 0;
        if(lf.lfUnderline)
          cf.dwEffects |= CFE_UNDERLINE;

        if(lf.lfWeight == FW_BOLD)
          cf.dwEffects |= CFE_BOLD;

        if(lf.lfItalic)
          cf.dwEffects |= CFE_ITALIC;

        // Устанавливаем семейство шрифта
        cf.bPitchAndFamily = lf.lfPitchAndFamily;

        // Устанавливаем название начертания шрифта
        lstrcpy(cf.szFaceName, lf.lfFaceName);

        // Изменяем шрифтовое оформление символов
        SendMessage(hwndEdit, EM_SETCHARFORMAT, 
          SCF_SELECTION, (LPARAM)&cf);
      }
      
      // Освобождаем контекст отображения
      ReleaseDC(hWnd, hDC);

	  return 0L;
	  break;
    }

	// Устанавливаем выравнивание параграфа по левой границе
    // окна органа управления Rich Edit
	case ID_FORMAT_PARAGRAPH_LEFT:
    {
	  pf.cbSize = sizeof(pf);
      pf.dwMask = PFM_ALIGNMENT;
      pf.wAlignment = PFA_LEFT;

      // Изменяем тип выравнивания текущего параграфа
      SendMessage(hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);

	  return 0L;
	  break;
    }

	// Устанавливаем выравнивание параграфа по правой границе
    // окна органа управления Rich Edit
	case ID_FORMAT_PARAGRAPH_RIGHT:
    {
	  pf.cbSize = sizeof(pf);
      pf.dwMask = PFM_ALIGNMENT;
      pf.wAlignment = PFA_RIGHT;
      SendMessage(hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
	  return 0L;
	  break;
    }

	// Выполняем центровку текущего параграфа
	case ID_FORMAT_PARAGRAPH_CENTER:
    {
	  pf.cbSize = sizeof(pf);
      pf.dwMask = PFM_ALIGNMENT;
      pf.wAlignment = PFA_CENTER;
      SendMessage(hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
	  return 0L;
	  break;
    }

	// Реализуем стандартные функции меню Edit
	case ID_EDIT_UNDO:
      SendMessage(hwndEdit, EM_UNDO, 0, 0L);
	  return 0L;
	  break;

	case ID_EDIT_CUT:
      SendMessage(hwndEdit, WM_CUT, 0, 0L);
	  return 0L;
	  break;

	case ID_EDIT_COPY:
      SendMessage(hwndEdit, WM_COPY, 0, 0L);
	  return 0L;
	  break;

	case ID_EDIT_PASTE:
      SendMessage(hwndEdit, WM_PASTE, 0, 0L);
	  return 0L;
	  break;

	case ID_EDIT_DELETE:
      SendMessage(hwndEdit, WM_CLEAR, 0, 0L);
	  return 0L;
	  break;

	// Выделяем весь текст, который есть в окне
    // органа управления Rich Edit
	case ID_EDIT_SELECTALL:
    {
      CHARRANGE charr;

      charr.cpMin = 0;  // от начала... 
      charr.cpMax = -1; // ... и до конца текста
      
      SendMessage(hwndEdit, EM_EXSETSEL, 0, (LPARAM)&charr);
	  return 0L;
	  break;
    }

	// При создании нового текста удаляем текущее
    // содержимое окна редактирования
	case ID_FILE_NEW:
      SetWindowText(hwndEdit,"\0");
	  return 0L;
	  break;

	case ID_FILE_OPEN:
      FileOpen(hWnd);   // загружаем файл для редактирования
	  return 0L;
	  break;
	
	case ID_FILE_SAVEAS:
      FileSaveAs(hWnd); // сохраняем текст в файле
	  return 0L;
	  break;

	case ID_FILE_PRINT:
      FilePrint();      // печатаем текст
	  return 0L;
	  break;

	case ID_FILE_EXIT:  
	  PostQuitMessage(0); // завершаем работу приложения
	  return 0L;
	  break;
	  
  	case ID_HELP_ABOUT:
      MessageBox(hWnd, 
        "Rich Text Editor RtfPad, v.1.0\n"
        "(C) Alexandr Frolov, 1995\n"
        "Email: frolov@glas.apc.org",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
	  return 0L;
	  break;

  	default:
	  break;
  }
  return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify,
    DefWindowProc);
}

// -----------------------------------------------------
// Функция WndProc_OnSize       
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
  MoveWindow(hwndEdit, 0, 0, cx, cy, TRUE);
  return FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc);
}

// -----------------------------------------------------
// Функция WndProc_OnSetFocus
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnSetFocus(HWND hwnd, HWND hwndOldFocus)
{
  // Когда главное окно нашего приложения получает 
  // фокус ввода, оно передает фокус ввода окну
  // органа управления Rich Edit
  SetFocus(hwndEdit);
  return FORWARD_WM_SETFOCUS(hwnd, hwndOldFocus, DefWindowProc);
}

// -----------------------------------------------------
// Функция FileSaveAs
// -----------------------------------------------------
void FileSaveAs(HWND hwnd)
{
  OPENFILENAME ofn;
  char szFile[256] = "untitled.rtf";
  char szDirName[512];
  char szFileTitle[256];
  
  // Фильтр допускает сохранение текста в файле с
  // расширением имени rtf, txt, или любым другим
  char szFilter[256] = 
    "Rich Text Files\0*.rtf\0Text Files\0*.txt\0"
    "Any Files\0*.*\0";

  HFILE      hFile;
  OFSTRUCT   of;
  EDITSTREAM es;

  memset(&ofn, 0, sizeof(OPENFILENAME));
    
  // Определяем путь к текущему каталогу
  GetCurrentDirectory(sizeof(szDirName), szDirName);
  
  // Заполняем структуру для выбора выходного файла
  ofn.lStructSize     = sizeof(OPENFILENAME);
  ofn.hwndOwner       = hwnd;
  ofn.lpstrFilter     = szFilter;
  ofn.lpstrInitialDir = szDirName;
  ofn.nFilterIndex    = 1;
  ofn.lpstrFile       = szFile;
  ofn.nMaxFile        = sizeof(szFile);
  ofn.lpstrFileTitle  = szFileTitle;
  ofn.nMaxFileTitle   = sizeof(szFileTitle);
  ofn.lpstrDefExt = "rtf";
  ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;

  // Выводим на экран диалоговую панель, предназначенную
  // для выбора выходного файла
  if(GetSaveFileName(&ofn))
  {
    // Если файл выбран, открываем его для записи или
    // создаем
    if (*ofn.lpstrFile)
    {
      hFile = OpenFile(ofn.lpstrFile, &of, OF_CREATE);

      // Устанавливаем параметры функции обратного вызова,
      // которая будет выполнять запись
      es.dwCookie    = (DWORD)hFile; 
      es.dwError     = 0; 
      es.pfnCallback = SaveCallback; 
            
      // Если расширение файла rtf, файл сохраняется как
      // rtf-файл. В противном случае он сохраняется как
      // обычный текстовый файл
      _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);

      // Закрываем файл
      _lclose(hFile);

      // Сбрасываем признак изменения содержимого окна
      // редактора текста
      SendMessage(hwndEdit, EM_SETMODIFY, FALSE, 0L);
    }
  }
}

// -----------------------------------------------------
// Функция SaveCallback
// -----------------------------------------------------
DWORD CALLBACK 
SaveCallback(DWORD dwCookie, LPBYTE   pbBuff,
  LONG cb, LONG *pcb)
{
  // Выполняем запись блока данных длиной cb байт
  cb = _lwrite((HFILE)dwCookie, pbBuff, cb);
  *pcb = cb;
  return 0;
}

// -----------------------------------------------------
// Функция FileOpen
// -----------------------------------------------------
void FileOpen(HWND hwnd)
{
  OPENFILENAME ofn;
  char szFile[256];
  char szDirName[256];
  char szFileTitle[256];
  char szFilter[256] = 
    "Rich Text Files\0*.rtf\0Text Files\0*.txt\0"
    "Any Files\0*.*\0";

  HFILE      hFile;
  OFSTRUCT   of;
  EDITSTREAM es;

  memset(&ofn, 0, sizeof(OPENFILENAME));
  GetCurrentDirectory(sizeof(szDirName), szDirName);
  szFile[0] = '\0';

  // Подготавливаем структуру для выбора входного файла
  ofn.lStructSize     = sizeof(OPENFILENAME);
  ofn.hwndOwner       = hwnd;
  ofn.lpstrFilter     = szFilter;
  ofn.lpstrInitialDir = szDirName;
  ofn.nFilterIndex    = 1;
  ofn.lpstrFile       = szFile;
  ofn.nMaxFile        = sizeof(szFile);
  ofn.lpstrFileTitle  = szFileTitle;
  ofn.nMaxFileTitle   = sizeof(szFileTitle);
  ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

  // Выводим на экран диалоговую панель, предназначенную
  // для выбора входного файла
  if(GetOpenFileName(&ofn))
  {
    // Если файл выбран, открываем его для чтения
    if (*ofn.lpstrFile)
    {
      hFile = OpenFile(ofn.lpstrFile, &of, OF_READ);
      
      // Устанавливаем параметры функции обратного вызова,
      // которая будет выполнять чтение
      es.dwCookie    = (DWORD)hFile; 
      es.dwError     = 0; 
      es.pfnCallback = OpenCallback; 

      // Если расширение файла rtf, файл загружается как
      // rtf-файл. В противном случае он загружается как
      // обычный текстовый файл
      _strupr(&ofn.lpstrFile[ofn.nFileExtension]); 
      if(!strncmp(&ofn.lpstrFile[ofn.nFileExtension], "RTF", 3))
        SendMessage(hwndEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
      else
        SendMessage(hwndEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);

      // Закрываем файл
      _lclose(hFile);

      // Сбрасываем признак изменения содержимого окна
      // редактора текста
      SendMessage(hwndEdit, EM_SETMODIFY, FALSE, 0L);
    }
  }
}

// -----------------------------------------------------
// Функция OpenCallback
// -----------------------------------------------------
DWORD CALLBACK 
OpenCallback(DWORD dwCookie, LPBYTE   pbBuff,
  LONG cb, LONG *pcb)
{
  // Выполняем чтение блока данных длиной cb байт
  *pcb = _lread((HFILE)dwCookie, pbBuff, cb);
  if(*pcb <= 0)
    *pcb = 0;
  return 0;
}

// -----------------------------------------------------
// Функция FilePrint
// -----------------------------------------------------
void FilePrint(void)
{
  FORMATRANGE fr;
  DOCINFO docInfo;
  LONG lLastChar, lTextSize;
  PRINTDLG pd;
  int nRc;
  HDC hPrintDC;

  // Инициализируем поля структуры PRITDLG
  memset(&pd, 0, sizeof(pd));
  pd.lStructSize = sizeof(PRINTDLG);
  pd.hwndOwner   = hwndEdit;
  pd.hInstance   = (HANDLE)hInst;
  pd.Flags       = PD_RETURNDC | PD_NOPAGENUMS | 
    PD_NOSELECTION | PD_PRINTSETUP | PD_ALLPAGES;
  pd.nFromPage   = 0xffff;
  pd.nToPage     = 0xffff;
  pd.nMinPage    = 0;
  pd.nMaxPage    = 0xffff;
  pd.nCopies     = 1;

  // Выводим на экран диалоговую панель, предназначенную
  // для печати документа
  if(PrintDlg(&pd) == TRUE)
  {
    hPrintDC = pd.hDC;

    // Инициализируем поля структуры FORMATRANGE 
    memset(&fr, 0, sizeof(fr));
  
    // Будем печатать с использованием контекста 
    // принтера, полученного от функции PrintDlg
    fr.hdc = fr.hdcTarget = hPrintDC;

    // Печатаем весь документ
    fr.chrg.cpMin = 0;     
    fr.chrg.cpMax = -1;
  
    // Устанавливаем размеры страницы в TWIPS-ах
    fr.rcPage.top = 0;
    fr.rcPage.left = 0;
    fr.rcPage.right  = MulDiv(GetDeviceCaps(hPrintDC, PHYSICALWIDTH), 
      1440, GetDeviceCaps(hPrintDC, LOGPIXELSX));

    fr.rcPage.bottom = MulDiv(GetDeviceCaps(hPrintDC, PHYSICALHEIGHT), 
      1440, GetDeviceCaps(hPrintDC, LOGPIXELSY));
    fr.rc = fr.rcPage;

    // Оставляем поля
    if(fr.rcPage.right > 2*3*1440/4+1440)
      fr.rc.right -= (fr.rc.left = 3*1440/4);
    if(fr.rcPage.bottom > 3*1440)
      fr.rc.bottom -= (fr.rc.top = 1440);
    
    // Заполняем поля структуры DOCINFO
    memset(&docInfo, 0, sizeof(DOCINFO));
    docInfo.cbSize = sizeof(DOCINFO);
    docInfo.lpszOutput = NULL;
    docInfo.lpszDocName = "RtfPad document";

    // Начинаем печать документа
    nRc = StartDoc(hPrintDC, &docInfo);
    
    // Если произошла ошибка, получаем и выводим на экран
    // код ошибки
    if(nRc < 0)
    {
      char szErr[128];
      DWORD dwErr = GetLastError();
      wsprintf(szErr, "Print Error %ld", dwErr);

      MessageBox(NULL, szErr,
        szAppTitle, MB_OK | MB_ICONEXCLAMATION);
      
      DeleteDC(hPrintDC);
      return;
    }

    // Начинаем печать страницы
    StartPage(hPrintDC);

    lLastChar = 0;

    // Определяем длину текста в байтах
    lTextSize = SendMessage(hwndEdit, WM_GETTEXTLENGTH, 0, 0);

    // Цикл по всем страницам документа
    while (lLastChar < lTextSize)
    {
      // Форматируем данные для принтера и печатаем их
      lLastChar = SendMessage(hwndEdit, EM_FORMATRANGE, TRUE, 
        (LPARAM) &fr);
        
      if(lLastChar < lTextSize)
      {
        // Завершаем печать очередной страницы
        EndPage(hPrintDC);
        
        // Начинаем новую страницу
        StartPage(hPrintDC);    
        fr.chrg.cpMin = lLastChar;
        fr.chrg.cpMax = -1;
      }
    }

    // Удаляем информацию, которая хранится в 
    // органе управления Rich Edit
    SendMessage(hwndEdit, EM_FORMATRANGE, TRUE, (LPARAM)NULL);

    // Завершаем печать страницы
    EndPage(hPrintDC);

    // Завершаем печать документа
    EndDoc(hPrintDC);

    // Удаляем контекст принтера
    DeleteDC(hPrintDC);
  }
}
