// Done by TheTutor -- 7/26/01 (Updated Last: 9/29/02)
// Перевод © 2005 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
/* Мы собираемся сделать очень простую версию редактора NotePad.
Тема, на которой мы будем фокусироваться, это "окно редактирования" и
загрузка/сохранение текста в окне. Мы назовем файл для загрузки и
сохранения "OurFile.txt".
*/
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include "resource.h"
#define classname "GameTutorials_notepad"
#define EDIT_BOX 0x00EB // Ссылка на окно редактирования
// Ширина и высота окна
#define WIN_WIDTH 640
#define WIN_HEIGHT 480
// Глобальные переменные
HWND edit_box = NULL; // Ссылка на окно редактирования, которое мы создадим
// Стандартная WinProc
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
// WinMain
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
HWND hwnd;
MSG msg;
WNDCLASSEX wndclassex = {0}; // Оконный класс
HMENU menu = NULL; // Ссылка на меню
// Заполняем необходимые поля
wndclassex.cbSize = sizeof(WNDCLASSEX);
wndclassex.style = CS_HREDRAW | CS_VREDRAW;
wndclassex.lpfnWndProc = WinProc;
wndclassex.hInstance = hinstance;
wndclassex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclassex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclassex.lpszClassName = classname;
// Загружаем меню
menu = LoadMenu(hinstance,MAKEINTRESOURCE(IDR_MENU1));
// Проверка ошибки
if(!menu)
return EXIT_FAILURE;
RegisterClassEx(&wndclassex);
hwnd = CreateWindowEx(WS_EX_APPWINDOW,
classname,
"EditBoxes",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
WIN_WIDTH,
WIN_HEIGHT,
NULL,
menu, // Мы создаем окно с меню
hinstance,
NULL);
// Проверка ошибки
if(!hwnd)
{
UnregisterClass(classname,hinstance); // Разрегистрируем WNDCLASSEX
DestroyMenu(menu); // Освобождаем меню
return EXIT_FAILURE;
}
// Выводим и обновляем окно
ShowWindow(hwnd, ishow);
UpdateWindow(hwnd);
// Цикл сообщений
while(1)
{
// Если есть сообщение, обрабатываем его
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
// Если выражение истинно - программа завершена
if(msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
;// Здесь проводим все сложные расчеты, если они понадобятся
}
}
UnregisterClass(classname,hinstance); // Разрегистритуем WNDCLASSEX
DestroyMenu(menu); // Освобождаем меню
return msg.wParam; // Код возврата (завершилась ли программа корректно?)
}
// WinProc
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
// Выполняем действия в зависимости от "message"
switch(message)
{
case WM_CREATE:
{
RECT rect;
// Сперва мы получаем клиент-область окна. Мы будем использовать ее
// для создания окна редактирования
GetClientRect(hwnd,&rect);
// -- Создаем окно редактирования -- //
/* Что здесь происходит? Рассмотрим шаг за шагом:
1) Первый параметр означает, что мы создаем заданный тип окна - окно редактирования.
2) Второй параметр указывает "текст по умолчанию" окна редактирования (NULL
означает, что его нет).
3) Третий параметр (WS_CHILD) указывает, что это окно редактирования будет
принадлежать родительскому окну, что означает, что они будут иметь общую
WinProc().
Значение остальных параметров:
WS_VISIBLE -- окно видимое,
ES_AUTOHSCROLL -- автоматически прокручивать вправо, когда пользователь
достигает конца строки
ES_AUTOVSCROLL -- автоматически прокручивать текст на страницу вверх,
когда пользователь нажимает "enter" в конце страницы
ES_LEFT -- выравнивать текст по левому краю,
ES_MULTILINE -- разрешить несколько строк
ES_NOHIDESEL -- запретить "поведение по умолчанию" окна редактирования,
(оно не будет спрятано, когда оно неактивно)
ES_WANTRETURN -- когда пользователь нажимает "enter", он будет функционировать
как "возврат каретки"
WS_VSCROLL - вертикальная строка прокрутки,
WSHSCROLL -- горизонтальная строка прокрутки
WS_BORDER -- у окна редактирования будет тонкая рамка
4) 0, 0 -- положение верхнего левого угла окна редактирования по отношению к
родительскому окну
5) rect.right -- ширина окна редактирования
6) rect.bottom -- высота окна редактирования
6) hwnd -- ссылка на родительское окно
7) (HMENU)EDIT_BOX -- способ определить наше окно редактирования (просто число)
8) Сначала посмотрите на CreateWindow() в WinMain(), обратите внимание как мы
передавали в функцию hinstance. Нам нужно передать это значение и здесь.
Но если вы посмотрите на WinProc(), вы увидите, что мы не передаем в нее
эту переменную, поэтому у нас нет к ней доступа. Это не совсем верно.
Если вы попробуете просто передать "hinstance", программа не будет компилироваться.
Но у нас есть значение "hinstance", спрятанное в "lparam". -- Преобразованием
типа "lparam" в LPCREATESTRUCT, мы можем извлечь из него "hinstance".
9) NULL -- Этот параметр используется для передачи "дополнительной информации", но у нас ее нет.
*/
edit_box = CreateWindow("EDIT",NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL |
ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL |
WS_VSCROLL | WS_HSCROLL | WS_BORDER | ES_WANTRETURN,
0, 0, rect.right, rect.bottom, hwnd, (HMENU)EDIT_BOX,
((LPCREATESTRUCT)lparam)->hInstance, NULL);
return 0;
}
case WM_COMMAND:
{
// Если выбран пункт меню "Load"
if(LOWORD(wparam) == LoadFile)
{
FILE *file = NULL;
fpos_t length = 0; // Используется для записи длины файла
char *buff = NULL; // Символьный буфер, используется для хранения загружаемого текста.
file = fopen("OurFile.txt","rb");
// Проверка ошибки
if(!file)
{
MessageBox(hwnd,"Couldn't load OurFile.txt","Error",MB_OK | MB_ICONERROR);
PostQuitMessage(0);
return 0;
}
// Ищем конец файла
fseek(file,0,SEEK_END);
// Эта функция получает положение файлового указателя -- Мы используем его для того,
// чтобы выделить достаточно памяти, чтобы загрузить весь файл
fgetpos(file,&length);
buff = new char[(int)length + 1]; // Выделяем память
// "+ 1" нужно для завершающего NULL
// мы хотим точно знать, что buff заканчивается символом NULL
// Проверка ошибки
if(!buff)
{
MessageBox(hwnd,"Couldn't allocate the memory","Error",MB_OK | MB_ICONERROR);
PostQuitMessage(0);
return 0;
}
// Перемещаем файловый указатель обратно в начало файла
fseek(file,0,SEEK_SET);
// Читаем весь файл в буфер buff
fread(buff,(int)length,1,file);
buff[(int)length] = NULL; // Завершаем buff символом NULL
// (Мы добавили "+ 1" к длине буфера, и теперь
// "length" это индекс последней ячейки массива buff)
// Закрываем файл
fclose(file);
// Здесь все и происходит -- Эта функция заполняет окно редактирования текстом,
// который мы прочитали.
SetWindowText(edit_box,buff);
delete[] buff; // Освобождаем "buff"
return 0;
} // конец if(LOWORD(wparam) == LoadFile)
// В другом случае, если мы записываем файл...
else if(LOWORD(wparam) == SaveFile)
{
// Создаем символьную строку для пути к каталогу, куда мы будем записывать файл
char dir_path[256] = {0};
char *buff = NULL; // Символьный буфер, используемый для хранения ВСЕГО текста,
// набранного в окне редактирования
// Так мы получаем текущий каталог, из которого запущен .exe файл
GetCurrentDirectory(256,dir_path);
// Дописываем в конец имя файла -- Помните, что необходимо два "\\"
// для представления одного "\"
strcat(dir_path,"\\OurFile.txt");
// Получаем "длину текста" (количество символов) в окне редактирования
int length = GetWindowTextLength(edit_box);
length++; // Прибавляем единицу, учитывая NULL terminator
// (нулевой завершающий символ)
// Создаем буфер достаточного размера для хранения всего текста
buff = new char[length];
// Проверка ошибки - Если мы не смогли выделить память
if(!buff)
{
MessageBox(hwnd,"Couldn't allocate the memory","Error",MB_OK | MB_ICONERROR);
PostQuitMessage(0);
return 0;
}
// Заполняем буфер текстом из окна редактирования
GetWindowText(edit_box, buff, length);
buff[length - 1] = NULL; // Для уверенности, что "buff" завершается символом NULL
// Открываем файл для записи
FILE *file_pointer = fopen(dir_path, "wb");
// Записываем содержимое окна редактирования в файл
fwrite(buff, length, 1, file_pointer);
fclose(file_pointer); // Закрываем файл
delete[] buff; // Освобождаем "buff"
return 0;
}
return 0;
} // конец case WM_COMMAND:
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wparam, lparam);
}
/* Примечание
Конечно же, если вы бы создавали более функциональную программу, вы бы
НЕ прописывали в коде имя .txt файла. Вы бы дали пользователю возможность назвать
его при записи, а также реализовали бы возможность загружать различные текстовые
файлы.
Но это выходит за предмет данной обучалки :)
Может быть в будущем, обучалка перейдет "на следующий уровень" :)
*/
/*
| TheTutor
| thetutor@gametutorials.com
| © 2000-2002 GameTutorials
*/
// Перевод © 2005 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
|