c6141e49

Файл tedit\tedit.cpp


// ---------------------------------------- // Редактор текстовых файлов // ----------------------------------------

#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_EXIT 5

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

// Имя класса окна 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 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, 50, 20, hwnd, (HMENU) ID_NEW, hInst, NULL);

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

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



hButtExit = CreateWindow("button", "Exit", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 150, 0, 50, 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;

return 0; }

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



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

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; }

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; }

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

Как обычно, внешний вид и функциональные возможности нашего приложения определяет функция главного окна.

По сообщению 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);

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

Пусть вас не смущает, что мы указали нулевые значения для координат и размеров редактора текста. Размеры редактора текста зависят от размеров главного окна приложения, поэтому они будут установлены при обработке сообщения WM_SIZE.

Далее обработчик сообщения WM_CREATE устанавливает максимальную длину редактируемого текста, для чего посылает редактору сообщение EM_LIMITTEXT:

SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);

После этого сбрасывается флаг обновления текста:

bUpdate = FALSE;

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

Далее в верхней части главного окна приложения создаются четыре кнопки: "New", "Open", "Save" и "Exit".

Во время обработки сообщения WM_SIZE (поступающего в функцию окна при создании окна и при изменении его размера) устанавливаются правильные размеры и расположение редактора текста:

case WM_SIZE: { MoveWindow(hEdit, 0, 20, LOWORD(lParam), HIWORD(lParam) - 20, TRUE);
return 0; }



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

Так же как и в предыдущем приложении, обработчик сообщения WM_SETFOCUS передает фокус ввода текстовому редактору, для чего вызывает функцию SetFocus.

Обработчик сообщения WM_COMMAND получает сообщения, приходящие от окна редактирования и кнопок.

Если сообщение пришло от окна редактирования, проверяется код извещения.

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

else if(HIWORD(lParam) == EN_UPDATE) { // Устанавливаем флаг обновления текста bUpdate = TRUE; }

Если нажата кнопка сохранения текста, обработчик сообщения WM_COMMAND открывает выходной файл, вызывая функцию OpenSaveFile, определенную в нашем приложении. Последняя использует стандартную диалоговую панель "Save As", с которой вы уже знакомы.

Далее обработчик определяет размер текста, находящегося в окне редактирования (в байтах), вызывая функцию GetWindowTextLength:

wSize = GetWindowTextLength(hEdit);

Далее, посылая сообщение EM_GETHANDLE, функция определяет идентификатор блока памяти, используемой редактором для хранения текста:

hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);

Управление памятью в Windows мы рассмотрим позже. Сейчас только отметим, что получив идентификатор блока памяти, мы еще не имеем к этой памяти непосредственного доступа. Для получения доступа, а заодно и адреса блока памяти, этот блок следует зафиксировать, вызвав (в нашем случае) функцию LocalLock:

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;

При создании нового файла прежде всего проверяется флаг обновления. Если он сброшен, содержимое текстового редактора сбрасывается, для чего в него записывается пустая строка:

SetWindowText(hEdit, "\0");

При загрузке файла для редактирования после проверки флага обновления вызывается функция OpenFile. Эта функция, определенная в нашем приложении, открывает файл с помощью стандартной диалоговой панели "Open", с которой вы уже знакомы.

Далее определяется размер файла, который не должен превосходить 32000 байт. Если файл имеет подходящий размер, приложение заказывает буфер для загрузки файла, вызывая функцию malloc (для приложений Windows есть и другие способы получения памяти, но этот тоже работает):

lpTextBuffer = (LPSTR)malloc(32000);

Далее файл читается в буфер, после чего буфер закрывается двоичным нулем:

_lread(hfSrcFile, lpTextBuffer, dwFileSize);
lpTextBuffer[(WORD)dwFileSize] = '\0';

Для переноса текста из буфера в редактор вызывается функция SetWindowText:

SetWindowText(hEdit, lpTextBuffer);

После этого буфер можно освободить, вызвав функцию free.

При завершении работы приложения с помощью кнопки "Exit" после проверки флага обновления в функцию главного окна приложения посылается сообщение WM_CLOSE:

SendMessage(hwnd, WM_CLOSE, 0, 0L);

Файл определения модуля приложения TEDIT приведен в листинге 2.25.


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