Операционная система Microsoft Windows 3.1 для программиста -том 3

Файл smartpad/smartpad.cpp


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

#include "toolbar.hpp" #include "smartpad.hpp"

// ====================================================== // Прототипы функций // ======================================================

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL CALLBACK _export DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

LRESULT CALLBACK _export EditWndProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam);

HFILE OpenFile(void);
HFILE OpenSaveFile(void);
int SaveFileAs(HWND hwnd);
int SaveFile(HWND hwnd);
int SelectFile(HWND hwnd);

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

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

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

// Глобальная переменная для хранения идентификатора // текущей копии приложения HINSTANCE hInst;

// Указатель на объект органа управления TOOLBAR Toolbar *Tb;

// Переменные для хранения идентификаторов меню HMENU hmenuAppMenu; HMENU hmenuSystemMenu;



// Идентификатор таблицы акселераторов HACCEL haccel;

// Идентификатор редактора текста HWND hEdit;

// Признак внесения изменений в текст BOOL bNeedSave;

// Путь к редактируемому файлу char szCurrentFileName[128];

// Временный буфер char szTempBuffer[128];

// Признак запрета редактирования BOOL bReadOnly = FALSE;

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

// Переменные для хранения адресов функций DLGPROC lpfnDlgProc; WNDPROC lpfnEditOldWndProc; WNDPROC lpfnEditWndProc;

// Идентификатор главного окна HWND hwndMain;

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




// Сохраняем идентификатор текущей копии приложения hInst = hInstance;

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

// Загружаем основное меню приложения hmenuAppMenu = LoadMenu(hInstance, "APP_MENU");

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

// Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE;

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

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

// Создаем орган управления TOOLBAR Tb = new Toolbar(hInstance, hwnd, TB_FIRST);

// Создаем кнопки в органе управления TOOLBAR Tb->
InsertButton(0, MAKEINTRESOURCE(IDB_NEWUP), MAKEINTRESOURCE(IDB_NEWDOWN), MAKEINTRESOURCE(IDB_NEWUP));
Tb->
InsertButton(1, MAKEINTRESOURCE(IDB_OPENUP), MAKEINTRESOURCE(IDB_OPENDOWN), MAKEINTRESOURCE(IDB_OPENGR));
Tb->
InsertButton(2, MAKEINTRESOURCE(IDB_SAVEUP), MAKEINTRESOURCE(IDB_SAVEDOWN), MAKEINTRESOURCE(IDB_SAVEGR));
Tb->
InsertButton(4, MAKEINTRESOURCE(IDB_CUTUP), MAKEINTRESOURCE(IDB_CUTDOWN), MAKEINTRESOURCE(IDB_CUTGR));
Tb->
InsertButton(5, MAKEINTRESOURCE(IDB_COPYUP), MAKEINTRESOURCE(IDB_COPYDOWN), MAKEINTRESOURCE(IDB_COPYGR));
Tb->
InsertButton(6, MAKEINTRESOURCE(IDB_PASTUP), MAKEINTRESOURCE(IDB_PASTDOWN), MAKEINTRESOURCE(IDB_PASTGR));
Tb->
InsertButton(7, MAKEINTRESOURCE(IDB_UNDOUP), MAKEINTRESOURCE(IDB_UNDODOWN), MAKEINTRESOURCE(IDB_UNDOGR));
Tb->
InsertButton(9, MAKEINTRESOURCE(IDB_EXITUP), MAKEINTRESOURCE(IDB_EXITDOWN), MAKEINTRESOURCE(IDB_EXITGR));
Tb->
InsertButton(10, MAKEINTRESOURCE(IDB_HELPUP), MAKEINTRESOURCE(IDB_HELPDOWN), MAKEINTRESOURCE(IDB_HELPGR));



// Загружаем таблицу акселераторов haccel = LoadAccelerators(hInstance, "APP_ACCELERATORS");

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

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

// Записываем во все поля структуры нулевые значения memset(&wc, 0, sizeof(wc));

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

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

// ====================================================== // Новая функция окна для редактора текста // ====================================================== LRESULT CALLBACK _export EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { // Если в окне редактора текста пользователь нажал // правую клавишу мыши, выводим в позиции курсора мыши // плавающее меню if(msg == WM_RBUTTONDOWN) { HMENU hmenuPopup; POINT pt;

// Преобразуем координаты курсора мыши в экранные pt = MAKEPOINT(lParam);
ClientToScreen(hwnd, &pt);

// Создаем пустое временное меню hmenuPopup = CreatePopupMenu();

// Заполняем временное меню AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED, CM_FILENEW, "&New");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED, CM_FILEOPEN, "&Open");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED, CM_FILESAVE, "&Save");
AppendMenu(hmenuPopup, MF_SEPARATOR, 0, 0);
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED, CM_FILEEXIT, "E&xit");



// Выводим плавающее меню в позиции курсора мыши TrackPopupMenu(hmenuPopup, TPM_CENTERALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, hwndMain, NULL);

// Удаляем временное меню DestroyMenu(hmenuPopup);
} // Вызываем старую функцию окна редактора текста return CallWindowProc(lpfnEditOldWndProc, hwnd, msg, wParam, lParam);
}

// ====================================================== // Функция главного окна приложения WndProc // ====================================================== LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HFONT hfont;

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, 30, 100, 100, hwnd, (HMENU) ID_EDIT, hInst, NULL);

// Задаем для редактора текста шрифт с // переменной шириной символов hfont = GetStockFont(ANSI_VAR_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM)hfont, (LPARAM)MAKELONG((WORD)TRUE, 0));

// Создаем переходник для новой функции // окна редактора текста lpfnEditWndProc = (WNDPROC)MakeProcInstance((FARPROC)EditWndProc,hInst);

// Определяем адрес старой функции // окна редактора текста lpfnEditOldWndProc = (WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC);

// Подключаем к редактору текста новую функцию окна SetWindowLong(hEdit, GWL_WNDPROC, (LONG)lpfnEditWndProc);

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

// Сбрасываем флаг обновления текста и флаг // запрета редактирования bNeedSave = FALSE; bReadOnly = FALSE;

// Так как редактируемый файл не открывался и не // сохранялся, в переменную пути к нему записываем // пустую строку lstrcpy(szCurrentFileName, "");

// Устанавливаем заголовок окна приложения SetWindowText(hwnd, "SmartPad - [UNTITLED]");

// Определяем идентификатор системного меню hmenuSystemMenu = GetSystemMenu(hwnd, FALSE);



// Добавляем в системное меню разделительную линию // и строку "About" AppendMenu(hmenuSystemMenu, MF_SEPARATOR, 0, 0);
AppendMenu(hmenuSystemMenu, MF_BYCOMMAND | MF_ENABLED, CM_SYSABOUT, "&About...");

// Блокируем в системном меню строку "Close" EnableMenuItem(hmenuSystemMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);

return 0; }

case WM_SIZE: { // Устанавливаем новую ширину дочернего окна // органа управления TOOLBAR Tb->
SetWidth(LOWORD(lParam));

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

return 0; }

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

// Это сообщение приходит от системного меню case WM_SYSCOMMAND: { // Необходимо замаскировать четыре младших бита // параметра wParam if((wParam & 0xfff0) == CM_SYSABOUT) { // Переходник для функции диалоговой панели lpfnDlgProc = (DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

// Создаем модальную диалоговую панель DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);
return 0; }

// Блокируем строку "Close". Эта строка не является // обязательной, так как мы уже заблокировали эту // строку функцией EnableMenuItem else if((wParam & 0xfff0) == SC_CLOSE) return 0; break; }

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

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

// Эти строки меню пока заблокированы, так как // соответствующие функции не реализованы case CM_HELPUSING_HELP: case CM_HELPPROCEDURES: case CM_HELPCOMMANDS: case CM_HELPKEYBOARD: case CM_HELPINDEX: case CM_FILEPRINTER_SETUP: case CM_FILEPAGE_SETUP: case CM_FILEPRINT: { MessageBox(hwnd, "В текущей версии " "редактора SmartPad данная функция" " не реализована", NULL, MB_OK);
return 0; }



// На запрос подсказки выводим диалоговую панель // с информацией о программе case TB_HELP: case CM_HELPABOUT: { // Переходник для функции диалоговой панели lpfnDlgProc = (DLGPROC)MakeProcInstance( (FARPROC)DlgProc, hInst);

// Создаем модальную диалоговую панель DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);

// Ликвидируем переходник FreeProcInstance((FARPROC) lpfnDlgProc);

return 0; }

// Переключение режима запрета редактирования case CM_EDITSETREADONLY: { // Если режим запрета редактирования выключен, // включаем его if(!bReadOnly) { // Запрещаем редактирование SendMessage(hEdit, EM_SETREADONLY, TRUE, 0L);

// Отмечаем соответствующую строку в меню CheckMenuItem(hmenuAppMenu, CM_EDITSETREADONLY, MF_BYCOMMAND | MF_CHECKED);

// Устанавливаем флаг запрета редактирования bReadOnly = TRUE; } // Если режим запрета редактирования включен, // выключаем его else { SendMessage(hEdit, EM_SETREADONLY, FALSE, 0L);
CheckMenuItem(hmenuAppMenu, CM_EDITSETREADONLY, MF_BYCOMMAND | MF_UNCHECKED);
bReadOnly = FALSE; }

// Устанавливаем фокус ввода на редактор текста SetFocus(hEdit);
return 0; }

case CM_EDITPASTE: case TB_PAST: { SendMessage(hEdit, WM_PASTE, 0, 0L);
SetFocus(hEdit);
return 0; }

case CM_EDITCOPY: case TB_COPY: { SendMessage(hEdit, WM_COPY, 0, 0L);
SetFocus(hEdit);
return 0; }

case CM_EDITCUT: case TB_CUT: { SendMessage(hEdit, WM_CUT, 0, 0L);
SetFocus(hEdit);
return 0; }

case CM_EDITCLEAR: { SendMessage(hEdit, WM_CLEAR, 0, 0L);
SetFocus(hEdit);
return 0; }

case CM_EDITSELALL: { SendMessage(hEdit, EM_SETSEL, 0, MAKELPARAM(0, -1));
SetFocus(hEdit);
return 0; }

case CM_EDITUNDO: case TB_UNDO: { SendMessage(hEdit, EM_UNDO, 0, 0L);
SetFocus(hEdit);
return 0; }

// Завершаем работу приложения case TB_EXIT: case CM_FILEEXIT: { // Проверяем флаг обновления if(bNeedSave) { // Если в тексте были изменения, // спрашиваем у пользователя, надо ли // сохранять текст в файле if(IDYES == MessageBox(hwnd, "Файл был изменен. Желаете сохранить?", szWindowTitle, MB_YESNO | MB_ICONQUESTION)) { // Если файл ни разу не сохранялся, // спрашиваем путь и имя нового файла if(szCurrentFileName[0] == '\0') { SaveFileAs(hwnd);



// Изменяем заголовок главного окна // приложения в соответствии // с именем и путем к файлу wsprintf(szTempBuffer, "SmartPad - [%s]", (LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
}

// Если файл уже сохранялся, записываем его // на прежнее место else SaveFile(hwnd);
} }

// Завершаем работу приложения DestroyWindow(hwnd);
return 0; }

case CM_FILENEW: case TB_NEW: { // Проверяем флаг обновления if(bNeedSave) { if(IDYES == MessageBox(hwnd, "Файл был изменен. Желаете сохранить?", szWindowTitle, MB_YESNO | MB_ICONQUESTION)) { if(szCurrentFileName[0] == '\0') { SaveFileAs(hwnd);
wsprintf(szTempBuffer, "SmartPad - [%s]", (LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
} else SaveFile(hwnd);
} }

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

bNeedSave = FALSE;

lstrcpy(szCurrentFileName, "");
SetWindowText(hwnd, "SmartPad - [UNTITLED]");

SetFocus(hEdit);
return 0; }

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

if(!SelectFile(hwnd)) { lstrcpy(szCurrentFileName, "");
SetWindowText(hwnd, "SmartPad - [UNTITLED]");
} else { wsprintf(szTempBuffer, "SmartPad - [%s]", (LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
}

return 0; }

case CM_FILESAVEAS: { if(SaveFileAs(hwnd)) { wsprintf(szTempBuffer, "SmartPad - [%s]", (LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
} else { lstrcpy(szCurrentFileName, "");
SetWindowText(hwnd, "SmartPad - [UNTITLED]");
} return 0; }

case CM_FILESAVE: case TB_SAVE: { if(szCurrentFileName[0] == '\0') { if(SaveFileAs(hwnd)) { wsprintf(szTempBuffer, "SmartPad - [%s]", (LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
} else { lstrcpy(szCurrentFileName, "");
SetWindowText(hwnd, "SmartPad - [UNTITLED]");
} } else SaveFile(hwnd);
return 0; }



default: break; } break; }

// Это сообщение приходит при завершении работы // операционной системы Windows. Если мы не сохранили // редактируемый текст, спрашиваем у пользователя, // надо ли это делать case WM_QUERYENDSESSION: { // Проверяем флаг обновления if(bNeedSave) { if(IDYES == MessageBox(hwnd, "Файл был изменен. Желаете сохранить?", szWindowTitle, MB_YESNO | MB_ICONQUESTION)) { if(szCurrentFileName[0] == '\0') { SaveFileAs(hwnd);
wsprintf(szTempBuffer, "SmartPad - [%s]", (LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
} else SaveFile(hwnd);
} }

// Разрешаем операционной системе Windows // завершить свою работу return 1L; }

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



// Сохраняем путь к открытому файлу lstrcpy(szCurrentFileName, ofn.lpstrFile);

// Возвращаем идентификатор файла 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);

// Сохраняем путь к файлу lstrcpy(szCurrentFileName, ofn.lpstrFile);
return hf; } else return 0; }

// ====================================================== // Функция SaveFileAs // Сохранение текста в новом файле // ====================================================== int SaveFileAs(HWND hwnd) { 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);

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

return 1; }

// ====================================================== // Функция SaveFile // Сохранение текста в старом файле // ====================================================== int SaveFile(HWND hwnd) { WORD wSize; HANDLE hTxtBuf; NPSTR npTextBuffer;

// Открываем выходной файл hfDstFile = _lopen(szCurrentFileName, OF_WRITE);
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);

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

return 1; }

// ====================================================== // Функция SelectFile // Загрузка текста из файла для редактирования // ====================================================== int SelectFile(HWND hwnd) { LPSTR lpTextBuffer; DWORD dwFileSize, dwCurrentPos;

// Открываем входной файл. 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);

// Сбрасываем флаг обновления bNeedSave = FALSE; SetFocus(hEdit);
return 1; }

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

BOOL CALLBACK _export DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { // Инициализация диалоговой панели case WM_INITDIALOG: { return TRUE; }

case WM_COMMAND: { switch(wParam) { // Сообщение от кнопки "OK" case IDOK:

// Отмена диалоговой панели. // Это сообщение приходит, когда пользователь // нажимает на клавишу <Esc>
case IDCANCEL: { // Устанавливаем флаг завершения диалога EndDialog(hdlg, 0);
return TRUE; } } } } return FALSE; }

В области глобальных переменных среди прочих определений располагается определение указателя на класс Toolbar:

Toolbar *Tb;

Через этот указатель (разумеется, после создания объекта и инициализации указателя) будет выполняться одна из операций, определенных для Toolbar, - создание кнопки в заданной позиции.

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

В массиве szCurrentFileName[128] хранится путь к редактируемому файлу. Эта информация используется для формирования заголовка главного окна приложения и для сохранения файла при помощи строки "Save" из меню "File" или кнопки в Toolbar с изображением дискеты.

С помощью строки "Read Only" меню "Options" вы можете запретить редактирование текста.


При этом в глобальную переменную bReadOnly типа BOOL записывается значение TRUE.

Три глобальные переменные используются для хранения, соответственно, адреса функции диалога, появляющегося при выборе строки "About..." (рис. 1.20), адреса стандартной функции диалога редактора текста и новой функции диалога, необходимой для вывода плавающего меню.



Рис. 1.20. Диалоговая панель "About" приложения SMARTPAD

Функция WinMain инициализирует приложение обычным образом и загружает основное меню из ресурсов приложения, вызывая функцию LoadMenu. Далее создается главное окно приложения, идентификатор которого сохраняется в глобальной переменной hwndMain.

После отображения главного окна создается орган управления Toolbar :

Tb = new Toolbar(hInstance, hwnd, TB_FIRST);

Конструктору объекта Toolbar передается идентификатор копии приложения hInstance, идентификатор окна, в котором необходимо расположить Toolbar и функция которого будет получать сообщение WM_COMMAND, и константу TB_FIRST, определяющую значение параметра wParam сообщения WM_COMMAND для самой левой кнопки в органе управления Toolbar. Для второй кнопки слева значение этого параметра будет равно TB_FIRST + 1, для третьей TB_FIRST + 2, и т. д.

Далее функция WinMain создает все необходимые кнопки, вызывая метод InsertButton, определенный в классе Toolbar:

Tb->
InsertButton(0, MAKEINTRESOURCE(IDB_NEWUP), MAKEINTRESOURCE(IDB_NEWDOWN), MAKEINTRESOURCE(IDB_NEWUP));
Tb->
InsertButton(1, MAKEINTRESOURCE(IDB_OPENUP), MAKEINTRESOURCE(IDB_OPENDOWN), MAKEINTRESOURCE(IDB_OPENGR));
Tb->
InsertButton(2, MAKEINTRESOURCE(IDB_SAVEUP), MAKEINTRESOURCE(IDB_SAVEDOWN), MAKEINTRESOURCE(IDB_SAVEGR));
Tb->
InsertButton(4, MAKEINTRESOURCE(IDB_CUTUP), MAKEINTRESOURCE(IDB_CUTDOWN), MAKEINTRESOURCE(IDB_CUTGR));
Tb->
InsertButton(5, MAKEINTRESOURCE(IDB_COPYUP), MAKEINTRESOURCE(IDB_COPYDOWN), MAKEINTRESOURCE(IDB_COPYGR));
Tb->
InsertButton(6, MAKEINTRESOURCE(IDB_PASTUP), MAKEINTRESOURCE(IDB_PASTDOWN), MAKEINTRESOURCE(IDB_PASTGR));
Tb->
InsertButton(7, MAKEINTRESOURCE(IDB_UNDOUP), MAKEINTRESOURCE(IDB_UNDODOWN), MAKEINTRESOURCE(IDB_UNDOGR));
Tb->
InsertButton(9, MAKEINTRESOURCE(IDB_EXITUP), MAKEINTRESOURCE(IDB_EXITDOWN), MAKEINTRESOURCE(IDB_EXITGR));
Tb->
InsertButton(10, MAKEINTRESOURCE(IDB_HELPUP), MAKEINTRESOURCE(IDB_HELPDOWN), MAKEINTRESOURCE(IDB_HELPGR));



Первый параметр функции InsertButton определяет расположение кнопки в области, выделенной для ToolBar. Обратите внимание, что номера кнопок увеличиваются не монотонно. После кнопки с номером 2 следует кнопка с номером 4, а после кнопки с номером 7 следует кнопка с номером 9. Пропущенным номерам соответствуют пустые позиции в окне Toolbar.

Учтите, что параметр wParam сообщения WM_COMMAND, получаемого от Toolbar, зависит от номера позиции и от значения константы TB_FIRST, переданной конструктору при создании класса. Так как мы не создали кнопки с номерами 3 и 8, функция главного окна приложения никогда не получит сообщение WM_COMMAND с параметром wParam, равным TB_FIRST + 3 и TB_FIRST + 8.

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

После создания кнопок функция окна приложения будет получать от Toolbar сообщение WM_COMMAND.

Таким образом, использование класса Toolbar предельно просто. Вы должны создать объект класса Toolbar, указав идентификатор копии приложения, идентификатор главного окна приложения (или другого окна, которое будет содержать Toolbar). Затем вам надо вставить кнопки, вызвав несколько раз функцию InsertButton, которая является методом класса Toolbar. После этого орган управления Toolbar начинает работать, посылая в функцию окна сообщение WM_COMMAND.

Для установки ширины дочернего окна Toolbar определена функция SetWidth. Вы должны вызывать эту функцию в функции главного окна приложения при обработке сообщения WM_SIZE:

case WM_SIZE: { Tb->
SetWidth(LOWORD(lParam));
..... (другие строки) ...... return 0; }

Класс Toolbar определен в файле toolbar.hpp, поэтому в исходный текст программы необходимо включить следующую строку:

#include "toolbar.hpp"

После создания Toolbar функция WinMain загружает с помощью функции LoadAccelerators таблицу акселераторов, используемую для ускоренного доступа к строкам меню, после чего запускается цикл обработки сообщений.


Для работы с акселераторами в цикле обработки сообщений вызывается функция TranslateAccelerators. Для получения символьных сообщений мы должны также вызывать в этом цикле функцию TranslateMessage.

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

Координаты курсора мыши передаются через параметр lParam. Эти координаты вычислены относительно верхнего левого угла внутренней области окна (client region). Так как функция TrackPopupMenu, создающая плавающее меню, использует экранные координаты, мы выполняем соответствующее преобразование с помощью функции ClientToScreen.

Если обработка сообщения от мыши завершена или пришло другое сообщение, предназначенное для функции окна редактора текста, новая функция окна EditWndProc передает сообщение без изменений стандартной функции окна через переходник lpfnEditOldWndProc:

return CallWindowProc(lpfnEditOldWndProc, hwnd, msg, wParam, lParam);

Теперь рассмотрим работу функции главного окна приложения, которая называется WndProc.

При создании главного окна приложения управление получает обработчик сообщения WM_CREATE. Он создает стандартный редактор текста на базе предопределенного класса окна "edit".

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

hfont = GetStockFont (ANSI_VAR_FONT);
SendMessage(hEdit, WM_SETFONT , (WPARAM)hfont, (LPARAM)MAKELONG((WORD)TRUE, 0));

Подробное описание этой операции и сообщения WM_SETFONT мы отложим до главы, посвященной шрифтам.

Далее обработчик сообщения WM_CREATE создает переходник для новой функции окна редактора текста, определяет адрес старой (т. е. стандартной) функции окна редактора текста и подключает новую функцию окна. Этот процесс был описан раньше.

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




Хотя в этом нет никакой необходимости, приложение SMARTPAD изменяет системное меню. Мы сделали это исключительно для иллюстрации методов работы с системным меню.

Сначала с помощью функции GetSystemMenu мы определяем идентификатор системного меню. Затем в системное меню добавляется горизонтальная разделительная линия и строка "About...", при выборе которой на экране появляется диалоговая панель 'About". Для добавления используется функция AppendMenu.

Мы также блокируем строку "Close" в системном меню, для чего вызываем функцию EnableMenuItem. Напомним, что все стандартные строки системного меню имеют идентификаторы, для которых в файле windows.h определены символические константы. В честности, строка "Close" имеет идентификатор SC_CLOSE.

Обработчик сообщения WM_SIZE устанавливает новую ширину дочернего окна Toolbar и новый размер редактора текста:

{ Tb->
SetWidth(LOWORD(lParam));
MoveWindow(hEdit, 0, 26, LOWORD(lParam), HIWORD(lParam) - 26, TRUE);
return 0; }

Обработчик сообщения WM_FOCUS передает фокус окну редактора текста, вызывая функцию SetFocus.

Для обработки сообщений от системного меню в функции главного окна приложения предусмотрен обработчик сообщения WM_SYSCOMMAND. Мы уже рассказывали вам об особенности этого сообщения - перед сравнением младшие четыре бита параметра wParam необходимо замаскировать.

Обработчик сообщения WM_SYSCOMMAND реагирует на добавленную нами в системное меню строку "About..." с идентификатором CM_SYSABOUT. Дополнительно мы перехватываем сообщение от строки "Close" и "изымаем" его, блокируя работу соответствующей строки.

Обработчик сообщения WM_COMMAND получился достаточно сложным, так как наше приложение имеет большое меню и Toolbar. Все основные выполняемые функции объясняются в комментариях, поэтому мы для экономии места остановимся только на некоторых моментах.

Обработка извещений от редактора текста выполняется аналогично тому, как это сделано в приложении TEDIT, описанном в предыдущем томе "Библиотеки системного программиста".




При выборе строки "Read Only" из меню "Options" инвертируется содержимое флага запрета редактирования bReadOnly. Если этот флаг находился в состоянии FALSE, редактору текста посылается сообщение EM_SETREADONLY с параметром WParam, равном TRUE, после чего содержимое флага меняется на TRUE. После этого строка "Read Only" отмечается галочкой при помощи функции CheckMenuItem.

Если выбрать эту строку еще раз, запрет редактирования отменяется, флаг запрета редактирования bReadOnly снова инвертируется, вслед за чем удаляется отметка в строке меню.

Практически после выполнения всех операций, отнимающих фокус ввода у текстового редактора, мы возвращаем фокус редактору, вызывая функцию SetFocus.

Обратите внимание на обработчик сообщения WM_QUERYENDSESSION. Это сообщение передается приложению перед завершением работы операционной системы Windows. Приложение может запретить завершение работы Windows, вернув нулевое значение, или разрешить, вернув значение 1. Наше приложение в ответ на это сообщение проверяет состояние флага bNeedSave. Если текст не был сохранен, на экран выводится соответствующий запрос. Если в ответ на этот запрос вы нажмете клавишу "Yes", текст будет сохранен в файле. После этого обработчик сообщения WM_QUERYENDSESSION возвращает 1, разрешая операционной системе завершить свою работу.

Файл smartpad.hpp содержит определения всех используемых в файле smartpad.cpp символических констант (листинг 1.9).


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