// (C) Фролов Г.В., 1995
// Адрес E-mail: frolov@glas.apc.org

// ----------------------------------------
// Редактор текстовых файлов с возможностью печати
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <commdlg.h>
#include <mem.h>
#include <string.h>
#include <stdlib.h>

// Идентификатор редактора текста
#define ID_EDIT    1

// Идентификаторы кнопок
#define ID_NEW     2
#define ID_OPEN    3
#define ID_SAVE    4
#define ID_PRINT   5
#define ID_EXIT    6

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
HFILE OpenFile(void);
HFILE OpenSaveFile(void);
int PrintFile(HWND, NPSTR, WORD);

// Имя класса окна
char const szClassName[]   = "TEditAppClass";

// Заголовок окна
char const szWindowTitle[] = "Text Editor";

// Идентификатор копии приложения
HINSTANCE hInst;

// Флаг изменений в тексте
BOOL bUpdate;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance, 
	HINSTANCE hPrevInstance,
	LPSTR     lpszCmdLine, 
	int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  // Сохраняем идентификатор копии приложения
  // в глобальной переменной
  hInst = hInstance;

  // После успешной инициализации приложения создаем
  // главное окно приложения
  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем расположение и размеры
    CW_USEDEFAULT,       // окна, принятые по умолчанию
    CW_USEDEFAULT,       // 
    CW_USEDEFAULT,       // 
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
			 // параметры
  // Если создать окно не удалось, завершаем приложение
  if(!hwnd)
    return FALSE;

  // Рисуем главное окно
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  // Идентификатор редкатора текста
  static HWND hEdit;

  // Идентификаторы кнопок
  static HWND hButtNew;
  static HWND hButtOpen;
  static HWND hButtSave;
  static HWND hButtPrint;
  static HWND hButtExit;

  // Идентификаторы файлов
  static HFILE hfSrcFile, hfDstFile;

  switch (msg)
  {
    case WM_CREATE:
    {
      // Создаем редактор текста
      hEdit = CreateWindow("edit", NULL,
	 WS_CHILD | WS_VISIBLE | WS_BORDER |
         WS_HSCROLL | WS_VSCROLL |
	 ES_LEFT | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
	 ES_MULTILINE,
	 0, 0, 0, 0,
	 hwnd, (HMENU) ID_EDIT, hInst, NULL);

      // Устанавливаем максимальную длину
      // редактируемого текста, равную 32000 байт
      SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);

      // Сбрасываем флаг обновления текста
      bUpdate = FALSE;

      // Создаем кнопки
      hButtNew = CreateWindow("button", "New",
         WS_CHILD | WS_VISIBLE |
	 BS_PUSHBUTTON,
	 0, 0, 80, 20,
	 hwnd, (HMENU) ID_NEW, hInst, NULL);

      hButtOpen = CreateWindow("button", "Open",
         WS_CHILD | WS_VISIBLE |
	 BS_PUSHBUTTON,
	 80, 0, 80, 20,
	 hwnd, (HMENU) ID_OPEN, hInst, NULL);

      hButtSave = CreateWindow("button", "Save",
         WS_CHILD | WS_VISIBLE |
	 BS_PUSHBUTTON,
	 160, 0, 80, 20,
	 hwnd, (HMENU) ID_SAVE, hInst, NULL);

      hButtPrint = CreateWindow("button", "Print",
         WS_CHILD | WS_VISIBLE |
	 BS_PUSHBUTTON,
	 240, 0, 80, 20,
	 hwnd, (HMENU) ID_PRINT, hInst, NULL);

      hButtExit = CreateWindow("button", "Exit",
         WS_CHILD | WS_VISIBLE |
	 BS_PUSHBUTTON,
	 320, 0, 80, 20,
	 hwnd, (HMENU) ID_EXIT, hInst, NULL);

      return 0;
    }

    case WM_SIZE:
    {
      // Устанавливаем размер органа управления
      // (текстового редактора) в соотвестствии
      // с размерами главного окна приложения
      MoveWindow(hEdit, 0, 20, LOWORD(lParam),
	HIWORD(lParam) - 20, TRUE);
      return 0;
    }

    // Когда главное окно приложения получает
    // фокус ввода, отдаем фокус редактору текста
    case WM_SETFOCUS:
    {
      SetFocus(hEdit);
      return 0;
    }

    case WM_COMMAND:
    {
      // Обработка извещений текстового редактора
      if(wParam == ID_EDIT)
      {
        // Ошибка
	if(HIWORD(lParam) == EN_ERRSPACE)
	{
	   MessageBox(hwnd, "Мало памяти",
	     szWindowTitle, MB_OK);
	}

	// Произошло изменение в редактируемом
        // тексте
	else if(HIWORD(lParam) == EN_UPDATE)
	{
           // Устанавливаем флаг обновления текста
           bUpdate = TRUE;
	}
	return 0;
      }

      // Нажата кнопка сохранения текста
      else if(wParam == ID_SAVE)
      {
	WORD wSize;
	HANDLE hTxtBuf;
        NPSTR  npTextBuffer;

        // Открываем выходной файл
	hfDstFile = OpenSaveFile();
	if(!hfDstFile) return 0;

	// Определяем размер текста
	wSize = GetWindowTextLength(hEdit);

	// Получаем идентификатор блока памяти,
        // в котором находится редактируемый текст
	hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);

	// Фиксируем блок памяти и получаем указатель
        // на него
	npTextBuffer = (NPSTR)LocalLock(hTxtBuf);

        // Записываем содержимое блока памяти в файл
	if(wSize != _lwrite(hfDstFile, npTextBuffer, wSize))
	{
          // При ошибке закрываем файл и выдаем сообщение
	  _lclose(hfDstFile);
	  MessageBox(hwnd, "Ошибка при записи файла",
	     szWindowTitle, MB_OK);
	  return 0;
	}

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

        // Расфиксируем блок памяти
	LocalUnlock(hTxtBuf);

	// Так как файл был только что сохранен,
	// сбрасываем флаг обновления 
        bUpdate = FALSE;

        SetFocus(hEdit);
        return 0;
      }

      // Создание нового файла
      else if(wParam == ID_NEW)
      {
        // Проверяем флаг обновления
	if(bUpdate)
	{
	  if(IDYES == MessageBox(hwnd,
	   "Файл был изменен. Желаете сохранить?",
	   szWindowTitle, MB_YESNO | MB_ICONQUESTION))
	     return 0;
        }

        // Сбрасываем содержимое текстового редактора
	SetWindowText(hEdit, "\0");

	// Сбрасываем флаг обновления
	bUpdate = FALSE;

        SetFocus(hEdit);
	return 0;
      }

      // Загорузка файла для редактирования
      else if(wParam == ID_OPEN)
      {
	LPSTR lpTextBuffer;
	DWORD dwFileSize, dwCurrentPos;

        // Проверяем флаг обновления
	if(bUpdate)
	{
	  if(IDYES == MessageBox(hwnd,
	   "Файл был изменен. Желаете сохранить?",
	   szWindowTitle, MB_YESNO | MB_ICONQUESTION))
	     return 0;
        }

	// Открываем входной файл.
	hfSrcFile = OpenFile();
	if(!hfSrcFile) return 0;

        // Определяем размер файла
	dwCurrentPos = _llseek(hfSrcFile, 0L, 1);
	dwFileSize   = _llseek(hfSrcFile, 0L, 2);
	_llseek(hfSrcFile, dwCurrentPos, 0);

        // Размер файла не должен превосходить 32000 байт
	if(dwFileSize >= 32000)
	{
	  _lclose(hfSrcFile);
	  MessageBox(hwnd, "Размер файла больше 32000 байт",
	     szWindowTitle, MB_OK);
	  return 0;
	}

        // Заказываем память для загрузки файла
        lpTextBuffer = (LPSTR)malloc(32000);
        if(lpTextBuffer == NULL)
           return 0;

        // Загружаем текст из файла в буфер
	_lread(hfSrcFile, lpTextBuffer, dwFileSize);

        // Закрываем буфер двоичным нулем
        lpTextBuffer[(WORD)dwFileSize] = '\0';

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

	// Переносим содержимое буфера в
        // текстовый редактор
        SetWindowText(hEdit, lpTextBuffer);

        // Освобождаем буфер
        free((void *)lpTextBuffer);

        // сбрасываем флаг обновления
	bUpdate = FALSE;

        SetFocus(hEdit);
        return 0;
      }

      else if(wParam == ID_PRINT)
      {
	WORD wSize;
	HANDLE hTxtBuf;
        NPSTR  npTextBuffer;

	// Определяем размер текста
	wSize = GetWindowTextLength(hEdit);

	// Получаем идентификатор блока памяти,
        // в котором находится редактируемый текст
	hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);

	// Фиксируем блок памяти и получаем указатель
        // на него
	npTextBuffer = (NPSTR)LocalLock(hTxtBuf);

        PrintFile(hwnd, npTextBuffer, wSize);

        // Расфиксируем блок памяти
	LocalUnlock(hTxtBuf);

        SetFocus(hEdit);
        return 0;
      }

      else if(wParam == ID_EXIT)
      {
	// Проверяем флаг обновления
	if(bUpdate)
	{
	  if(IDYES == MessageBox(hwnd,
	   "Файл был изменен. Желаете сохранить?",
	   szWindowTitle, MB_YESNO | MB_ICONQUESTION))
	     return 0;
        }

	// Посылаем в функцию главного окна
        // сообщение WM_CLOSE
	SendMessage(hwnd, WM_CLOSE, 0, 0L);
        return 0;
      }
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}



// -------------------------------
// Функция OpenFile
// Сохранение файла
// -------------------------------

HFILE OpenFile(void)
{
  // Структура для выбора файла
  OPENFILENAME ofn;

  // Буфер для записи пути к выбранному файлу
  char szFile[256];

  // Буфер для записи имени выбранного файла
  char szFileTitle[256];

  // Фильтр расширений имени файлов
  char szFilter[256] =
	 "Text Files\0*.txt;*.doc\0Any Files\0*.*\0";

  // Идентификатор открываемого файла
  HFILE hf;

  // Инициализация имени выбираемого файла
  // не нужна, поэтому создаем пустую строку
  szFile[0] = '\0';

  // Записываем нулевые значения во все поля
  // структуры, которая будет использована для
  // выбора файла
  memset(&ofn, 0, sizeof(OPENFILENAME));

  // Инициализируем нужные нам поля

  // Размер структуры
  ofn.lStructSize       = sizeof(OPENFILENAME);

  // Идентификатор окна
  ofn.hwndOwner         = NULL;

  // Адрес строки фильтра
  ofn.lpstrFilter       = szFilter;

  // Номер позиции выбора
  ofn.nFilterIndex      = 1;

  // Адрес буфера для записи пути
  // выбранного файла
  ofn.lpstrFile         = szFile;

  // Размер буфера для записи пути
  // выбранного файла
  ofn.nMaxFile          = sizeof(szFile);

  // Адрес буфера для записи имени
  // выбранного файла
  ofn.lpstrFileTitle    = szFileTitle;

  // Размер буфера для записи имени
  // выбранного файла
  ofn.nMaxFileTitle     = sizeof(szFileTitle);

  // В качестве начального каталога для
  // поиска выбираем текущий каталог
  ofn.lpstrInitialDir   = NULL;

  // Определяем режимы выбора файла
  ofn.Flags =   OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST
				  | OFN_HIDEREADONLY;
  // Выбираем входной файл
  if (GetOpenFileName(&ofn)) {

    // Открываем выбранный файл
    hf = _lopen(ofn.lpstrFile, OF_READ);

    // Возвращаем идентификатор файла
    return hf;
  }
  // При отказе от выбора возвращаем
  // нулевое значение
  else return 0;
}

// -------------------------------
// Функция OpenSaveFile
// Выбор файла для редактирования
// -------------------------------

HFILE OpenSaveFile(void)
{
  OPENFILENAME ofn;

  char szFile[256];
  char szFileTitle[256];
  char szFilter[256] =
	 "Text Files\0*.txt\0Any Files\0*.*\0";

  HFILE hf;

  szFile[0] = '\0';

  memset(&ofn, 0, sizeof(OPENFILENAME));

  ofn.lStructSize       = sizeof(OPENFILENAME);
  ofn.hwndOwner         = NULL;
  ofn.lpstrFilter       = szFilter;
  ofn.nFilterIndex      = 1;
  ofn.lpstrFile         = szFile;
  ofn.nMaxFile          = sizeof(szFile);
  ofn.lpstrFileTitle    = szFileTitle;
  ofn.nMaxFileTitle     = sizeof(szFileTitle);
  ofn.lpstrInitialDir   = NULL;
  ofn.Flags             = OFN_HIDEREADONLY;

  // Выбираем выходной файл
  if (GetSaveFileName(&ofn)) {

    // При необходимости создаем файл
    hf = _lcreat(ofn.lpstrFile, 0);
    return hf;
  }
  else return 0;
}
