El buffer de texto

Hasta ahora no nos hemos preocupado nunca del espacio de memoria necesario para almacenar y editar el contenido de un control edit. Windows se encarga de crear un buffer, y de aumentar su tamaño si es necesario, hasta cierto límite, dependiendo del tipo de control edit.

En el capítulo 7 vimos que podíamos fijar el límite máximo que el usuario podía editar mediante el mensaje EM_LIMITTEXT.

Sin embargo este mensaje no limita el tamaño del buffer. El mensaje no tiene efecto si el control ya contiene más caracteres que el límite establecido, y sigue siendo posible insertar más caracteres usando el mensaje WM_SETTEXT. De hecho, este mensaje no debería usarse, ya que ha sido sustituido por EM_SETLIMITTEXT.

Para limitar el tamaño del buffer se usa el mensaje EM_SETLIMITTEXT, y para obtener el valor del tamaño del buffer se usa el mensaje EM_GETLIMITTEXT.

En versiones de Windows de 16 bits es posible asumir, por parte de nuestra aplicación, todas las operaciones de control del buffer de memoria asociado a un control edit multilínea. Para ello, lo primero que debemos hacer es crear el control edit en una ventana que use el estilo DS_LOCALEDIT. Además disponemos de los mensajes EM_GETHANDLE y EM_SETHANDLE, para obtener un manipulador de memoria local del buffer del control, o asignar uno nuevo, respectivamente.

El proceso consiste en:

  • Obtener un manipulador del buffer local actual, mediante EM_GETHANDLE.
  • Liberar ese buffer usando la función LocalFree.
  • Crear un nuevo buffer local, usando LocalAlloc.
  • Asignar el nuevo manipulador de memoria al control, mediante el mensaje EM_SETHANDLE.

Cada vez que el buffer se quede pequeño recibiremos un mensaje de notificación EN_ERRSPACE.

Este proceso es inútil en el API de 32 bits, ya que en este caso toda la memoria pertenece al espacio de direcciones de memoria virtual, y no hay distinción entre memoria local y global.

Controles multilínea

Hasta ahora sólo hemos trabajado con controles edit de una línea, pero también es posible crear controles edit multilínea. Para ello bastará con crearlos con el estilo ES_MULTILINE. Pero estos controles tienen algunas peculiaridades que los hace algo más complicados de usar que los de una línea.

Para empezar, cuando se ejecuta un cuadro de diálogo, la tecla ENTER tiene el efecto de activar el botón por defecto. Esto nos crea un problema con los controles edit multilínea, ya que no podremos usar la tecla de [ENTER] para insertar un retorno de línea. Para evitar este comportamiento por defecto en los cuadros de diálogo se usa el estilo ES_WANTRETURN. Este estilo hace que las pulsaciones de la tecla ENTER, cuando el control tiene el foco del teclado, se conviertan en retornos de línea, y no se active el botón por defecto.

Otro detalle importante es que con frecuencia el texto no va a caber en el área visible del control, por lo que tendremos que desplazar el contenido tanto horizontal como verticalmente.

Para lograr esto disponemos, por una parte, de dos estilos propios de los controles edit: ES_AUTOHSCROLL y ES_AUTOVSCROLL. Cuando se activan estos estilos el texto se desplaza de forma automática en sentido horizontal o vertical, respectivamente, cada vez que el usuario llegue a un borde del área del control mientras escribe texto.

Además de esta posibilidad tenemos una segunda que consiste en añadir las barras de desplazamiento. Estas barras se añaden con los estilos de ventana WS_HSCROLL y WS_VSCROLL, respectivamente, y activan de forma automática los dos estilos anteriores: ES_AUTOHSCROLL y ES_AUTOVSCROLL.

La diferencia es que de esta segunda forma se muestran las barras, y de la primera no.

Iniciar controles multilínea

Otra dificultad añadida a la hora de usar estos controles es la inicialización. Las líneas dentro de un control edit multilínea se separan con dos retornos de línea y un avance de línea, es decir, dos caracteres '\r' y uno '\n', a esta secuencia se le denomina una ruptura de línea blanda. Por otra parte, si queremos convertir un retorno de línea normal en una ruptura de línea blanda, hay que saber que Windows añade de forma automática un carácter '\r' cada vez que se añade un carácter '\n', es decir, Windows sustituye el carácter '\n' por la secuencia "\r\n".

En cualquier caso, esto nos obliga a hacer un tratamiento de cada línea del texto de inicialización para sustituir las secuencias "\n" o "\r\n" por otra "\r\r\n".

Tenemos, pues, tres opciones a la hora de inicializar controles edit multilínea.

Una consiste en crear un buffer de texto con el contenido, sustituyendo los cambios de línea por rupturas blandas, y asignar el texto al control mediante un mensaje WM_SETTEXT.

void AsignarTexto(HWND hctrl, char *texto) {
   char* buffer;
   
   /* Crear buffer */
   buffer = (char *)malloc(strlen(texto)+1);
   buffer[0] = 0;

   /* Hacer la lectura */
   SustituirCambiosDeLinea(texto, buffer);

   SendMessage(hctrl, WM_SETTEXT, 0, (LPARAM)buffer);
   free(buffer);
}

Otra es usar el mismo buffer, creado con la función LocalAlloc, y asignar ese buffer al control edit directamente.

void AsignarTexto(HWND hctrl, char *texto) {
   char* buffer;
   HLOCAL hloc;
   
   /* Obtener manipulador de buffer actual: */
   hloc = (HLOCAL)SendMessage(hctrl, EM_GETHANDLE, 0, 0);
   /* Liberar buffer actual */
   LocalFree(hloc);
   /* Crear un buffer nuevo */
   hloc = LocalAlloc(LMEM_MOVEABLE, l+1);
   
   /* Bloquear el buffer para su uso */
   buffer = (char *)LocalLock(hloc);
   buffer[0] = 0;

   /* Hacer la lectura */
   SustituirCambiosDeLinea(texto, buffer);

   /* Desbloquear buffer */
   LocalUnlock(hloc);
   
   /* Asignar el nuevo buffer el control edit */
   SendMessage(hctrl, EM_SETHANDLE, (WPARAM)hloc, 0);
}

Una tercera opción consiste en enviar el contenido del control carácter a carácter, mediante mensajes WM_CHAR. La ventaja de este método es que no hay que insertar rupturas blandas, ya que el sistema lo hace por nosotros:

void AsignarTexto(HWND hctrl, char *texto) {
   int i;
   
   for(i = 0; i < strlen(texto); i++)
      SendMessage(hctrl, WM_CHAR, (WPARAM)texto[i], 0);
}