UNIVERSIDAD DEL ISTMO Ingeniería en computación Compiladores PROFESOR M. en C. J. Jesús Arellano Pimentel NÚMERO DE PRÁCTICA 7 NOMBRE DE LA PRÁCTICA Un simple editor de texto. OBJETIVO GENERAL Crear un simple editor de texto Win32 con funcionalidades básicas. OBJETIVOS ESPECÍFICOS EQUIPO REQUERIDO SOFTWARE REQUERIDO - Crear una aplicación para la edición de texto plano. Abrir y guardar archivos de texto plano. Formatear un par de palabras en el texto. Computadora personal con 512 MB de RAM mínimo. - Windows 7/8. - Microsoft Visual Studio Express 2012 para escritorio de Windows. 1.- Fundamentos. 1.1.- Los controles de edición Win32 Uno de los controles de edición más habituales en aplicaciones Win32 son los tipo clase edit. Estos controles se pueden configurar para que sean de línea única o de múltiples líneas [Petzold95]. Los controles de múltiples líneas permiten realizar operaciones básicas como: mover el cursor de edición, seleccionar porciones de texto usando el ratón o teclado, mover el texto seleccionado al portapapeles (Ctrl+x), copiar texto (Ctrl+c), e incluso insertar texto desde el portapapeles (Ctrl+v). También existe una versión mejorada de estos controles clase edit y son conocidos como richedit (edición enriquecida). Este tipo de control es más potente y permite, entre otras muchas cosas, formatear el texto utilizando diferentes fuentes y colores. 2.- Desarrollo 2.1 Crear un Proyecto Win32 con un control de edición RichEdit Paso 1 Iniciar el MVS Express 2012. Paso 2 Crear un nuevo proyecto Win32. Seleccionar la opción del menú Archivo->Nuevo Proyecto. Del cuadro de diálogo Nuevo proyecto en Plantillas seleccionar Visual C++ y Win32; en tipo de aplicación Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 1 seleccionar Proyecto Win32 Visual C++ (ver Figura 1). El nombre del proyecto o solución podría ser EdtWin32. Presionar el botón Aceptar. Figura 1. Cuadro de diálogo Nuevo Proyecto para la aplicación EdtWin32. Paso 3 Configurar el Proyecto Actual. En el Asistente para aplicaciones Win32 presionar el botón siguiente; después deshabilitar la casilla de verificación de Comprobación del ciclo de vida de desarrollo de seguridad(SDL); finalmente oprimir el botón Finalizar. Paso 4 Editar el código para crear una ventana con un control RichEdit. En la función WndProc agregar las siguientes variables: static HWND hWndEdit; CHARFORMAT2 cf; DWORD dwEVM; HFONT hFont; //Manejador de ventana //Formato del texto //Evento de captura //Manejador de fuente Después, agregar el procesamiento del mensaje WM_CREATE con el siguiente código: case WM_CREATE: LoadLibrary(L"riched20.dll"); hWndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, L"", WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL, 0, 0, 0, 0, hWnd, (HMENU) ID_EDITRICH, hInst, NULL); hFont = CreateFont( 18, 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, L"Arial" ); SendMessage( hWndEdit, WM_SETFONT, ( WPARAM ) hFont, 0 ); SetFocus(hWndEdit); break; Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 2 Además de cargar la librería (LoadLibrary(L"riched20.dll")), también es necesario incluir el archivo de encabezado correspondiente en el archivo stdafx.h, conjuntamente con las librerías commdlg.h y stdio.h, de la siguiente forma: // TODO: #include #include #include mencionar aquí los encabezados adicionales que el programa necesita <richedit.h> <commdlg.h> <stdio.h> Aparte, habrá que definir la constante simbólica asociada a este control, el cuál es el antepenúltimo parámetro de la función CreateWindowEx. Esta debería estar en el archivo Resource.h de la siguiente forma: // Valores predeterminados siguientes para nuevos objetos // #define ID_EDITRICH 200 Ya por último, es necesario redimensionar el control de edición para que tome todo el tamaño del área cliente de la ventana principal, esto se logra procesando el mensaje WM_SIZE, el cual se debe agregar después del WM_CREATE con el siguiente código: case WM_SIZE: MoveWindow(hWndEdit, 0, 0, LOWORD (lParam), HIWORD(lParam), TRUE); break; Hecho lo anterior esta todo listo para realizar una primera prueba. Paso 5 Guardar todo, compilar y probar. Para cerciorarse que todo va bien, se deben guardar todos los cambios, compilar y ejecutar la aplicación, el resultado deberá ser el de la Figura 2. Esta aplicación ya puede procesar mensajes de forma predefinida para seleccionar, copiar/cortar y pegar texto, así como la opción de deshacer. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 3 Figura 2. Ejecución de la aplicación EdtWin32. Paso 6 Agregar las opciones del menú para Abrir y Guardar un archivo de texto plano. Las funcionalidades de Abrir y Guardar archivo estarán asociadas a dos nuevos elementos del menú principal, por lo cual será necesario agregar sus constantes simbólicas en el archivo Resource.h y sus elementos de menú en el archivo EdtWin32.rc. El código para las constantes simbólicas en Resource.h es el siguiente: #define IDM_ABRIR #define IDM_GUARDAR 201 202 El código para los elementos del menú en el archivo EdtWin32.rc es el siguiente: MENUITEM "&Abrir", MENUITEM "&Guardar", IDM_ABRIR IDM_GUARDAR Recuérdese que el código anterior va dentro del menú archivo correspondiente al menú principal, el cuál inicia aproximadamente en la línea 38 del archivo EdtWin32.rc. Paso 7 Procesar los mensajes (comandos) para abrir y guardar un archivo de texto plano. El procesamiento de los mensajes IDM_ABRIR e IDM_GUARDAR debe hacerse dentro del switch correspondiente al procesamiento del mensaje WM_COMMAND de la función WndProc, previo al caso default de la siguiente manera: case IDM_ABRIR: { TCHAR szFile[MAX_PATH], szCaption[64 + _MAX_FNAME + _MAX_EXT]; ZeroMemory(szFile, MAX_PATH); OPENFILENAME ofn; ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_CREATEPROMPT; ofn.hwndOwner = hWnd; ofn.lpstrFilter = _T("Tipos de formatos soportados(*.txt)\0*.txt\0Texto (*.txt)\0\0"); ofn.lpstrTitle = _T("Abrir archivo de texto"); ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; if (IDOK == GetOpenFileName(&ofn)){ wsprintf(szCaption,_T("%s - %s"),szTitle, szFile[0] ? szFile : _T("Sin archivo abierto")); SetWindowText(hWnd, szCaption); FILE *file ; int iLength ; PSTR pstrBuffer ; char cFile[MAX_PATH]; TCHAR *ptchBuffer; wcstombs(cFile,szFile,MAX_PATH); if (NULL == (file = fopen (cFile, "rb"))){ MessageBox(hWnd,L"Error al leer el archivo", L"Error", MB_OK | MB_ICONERROR); Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 4 }else{ iLength = PopFileLength (file) ; if (NULL == (pstrBuffer = (PSTR) malloc (sizeof(char)*(iLength+1))) || NULL == (ptchBuffer = (TCHAR*) malloc (sizeof(TCHAR)*(iLength+1)))){ fclose (file) ; MessageBox(hWnd,L"Error al reservar memoria", L"Error", MB_OK | MB_ICONERROR); }else{ fread (pstrBuffer, 1, iLength, file) ; fclose (file) ; pstrBuffer[iLength] = '\0' ; mbstowcs(ptchBuffer,pstrBuffer,iLength+1); SetWindowText (hWndEdit, ptchBuffer) ; free (pstrBuffer) ; free (ptchBuffer) ; } } } //El siguiente segmento de código da formato reemplazando texto //normal por texto con formato memset( &cf, 0, sizeof cf ); //Se limpia la estructura del formato cf.cbSize = sizeof (CHARFORMAT2); //Se fija el tamaño de la estructura // Se establece la mascara para que sea posible aplicar color al texto cf.dwMask = CFM_COLOR; //| CFM_BACKCOLOR ; cf.crTextColor = RGB(255,0,0); //Se establece el color del texto //cf.crBackColor = RGB(0,0,255); //Se establece un rango de texto a seleccionar SendMessage( hWndEdit, EM_SETSEL, (WPARAM)5, (LPARAM)9); //Se aplica el formato al rango seleccionado SendMessage( hWndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf); //Se reemplaza el rango seleccionado con el nuevo texto y formato SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) L"cara"); } break; case IDM_GUARDAR: { TCHAR szFile[MAX_PATH]; ZeroMemory(szFile, MAX_PATH); OPENFILENAME ofn; ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT ; ofn.hwndOwner = hWnd; ofn.lpstrFilter = _T("Tipos de formatos soportados(*.txt)\0*.txt\0Texto (*.txt)\0\0"); ofn.lpstrTitle = _T("Guardar archivo de texto"); ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; if (IDOK == GetSaveFileName(&ofn)){ FILE *file ; int iLength ; PSTR pstrBuffer ; char cFile[MAX_PATH]; TCHAR *ptchBuffer; Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 5 wcstombs(cFile,szFile,MAX_PATH); if (NULL == (file = fopen (cFile, "wb"))){ MessageBox(hWnd,L"Error al crear el archivo", L"Error", MB_OK | MB_ICONERROR); }else{ iLength = GetWindowTextLength (hWndEdit) ; if (NULL == (pstrBuffer = (PSTR) malloc (sizeof(char) * (iLength+1))) || NULL == (ptchBuffer = (TCHAR*) malloc(sizeof(TCHAR)* (iLength+1)))) { MessageBox(hWnd,L"Error al reservar memoria", L"Error", MB_OK | MB_ICONERROR); fclose (file) ; } GetWindowText (hWndEdit, ptchBuffer, iLength+1) ; wcstombs(pstrBuffer, ptchBuffer, iLength+1); fwrite (pstrBuffer, 1, iLength+1, file); fclose (file) ; free (pstrBuffer) ; free (ptchBuffer); } } } break; El procesamiento del mensaje IDM_ABRIR utiliza una función PopFileLength función debe definirse previo a la función WndProc con el siguiente código: (file), esta long PopFileLength (FILE *file) { int iCurrentPos, iFileLength ; iCurrentPos = ftell (file) ; fseek (file, 0, SEEK_END) ; iFileLength = ftell (file) ; fseek (file, iCurrentPos, SEEK_SET) ; return iFileLength ; } Paso 8 Guardar todo, compilar y probar. Guardar todos los cambios, compilar y probar. El resultado deberá ser que cada que se abra un archivo el texto de la posición 5 a la posición 9 será remplazado por la palabra cara en color rojo. 4.- Referencias [Petzold96] Petzold Charles, et al. Programación en Windows 95. Mc Graw Hill – Microsoft Press. 1996. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 6