Modificación del texto

Windows mantiene una bandera para cada control edit que indica si su contenido ha sido modificado por el usuario o no.

Esta bandera nos permite tomar decisiones en función de si el contenido de un control edit ha sido o no modificado. Por ejemplo, si el contenido de un control correspondiente a un editor de texto no se ha modificado, no tendrá sentido leerlo y actualizar el fichero original.

Windows desactiva esta bandera automáticametne al crear el control, y la activa cada vez que el usuario edita el contenido del control.

Para leer el valor actual de esta bandera se usa el mensaje EM_GETMODIFY:

   BOOL modif;
   
   modif = SendMessage(hctrl, EM_GETMODIFY, 0, 0);
   if(modif) strcpy(mensaje, "Texto modificado");
   else strcpy(mensaje, "Texto no modificado");
   MessageBox(hwnd, mensaje, "Ejemplo de control edit", MB_OK);

En ocasiones nos interesará volver a desactivar esta bandera, por ejemplo, cuando hemos guardado el contenido actual de nuestro editor en disco, consideraremos que el contenido actual no ha sido modificado, puesto que es el mismo que en el fichero. Para modificar el valor de la bandera se usa el mensaje EM_SETMODIFY.

También podemos combinar el estado de esta bandera con el mensaje de notificación EN_CHANGE. Esto es lo que se suele hacer para mostrar el estado de modificación de un texto en un editor. Cada vez que se recibe un mensaje de notificación EN_CHANGE, se consulta la bandera de modificación, y en función de su valor, se pone la marca que indica si es necesario guardar el contenido o no. También se pueden inhibir las opciones de guardar si la bandera de modificación no está activa.

      case WM_COMMAND:
         switch(LOWORD(wParam)) {
            case CM_GUARDAR:
               Guardar(hctrl, "texto.txt");
               SendMessage(hctrl, EM_SETMODIFY, FALSE, 0);
               ActualizarMenu(hwnd, hctrl);
               break;
            case ID_TEXTO:
               if(HIWORD(wParam) == EN_CHANGE)
                  if(EnableMenuItem(hctrl, EM_GETMODIFY, 0, 0))
                     EnableMenuItem(GetMenu(hwnd), CM_GUARDAR, MF_BYCOMMAND | MF_ENABLED);
               break;
            ...
              
void ActualizarMenu(HWND hwnd, HWND hctrl) {
   if(SendMessage(hctrl, EM_GETMODIFY, 0, 0))
      EnableMenuItem(GetMenu(hwnd), CM_GUARDAR, MF_BYCOMMAND | MF_ENABLED);
   else
      EnableMenuItem(GetMenu(hwnd), CM_GUARDAR, MF_BYCOMMAND | MF_GRAYED);
}

En este fragmento vemos cómo manipular el mensaje de notificación EN_CHANGE para verificar la bandera de modificación, y activar la opción de menú de "guardar" cuando el contenido del control haya sido modificado.

El procesamiento del mensaje de "guardar" guarda el contenido del control edit, y a continuación elimina la bandera de modificación y actualiza el estado del menú.

Márgenes y tabuladores

Si no indicamos nada, el control edit usará toda la superficie de su área de cliente para mostrar el texto. Pero podemos cambiar esto de varias formas, definiendo los márgenes izquierdo y derecho, o especificando un rectángulo, dentro del área de cliente, que se usará para mostrar el texto.

El mensaje EM_SETMARGINS nos permite fijar los márgenes izquierdo y/o derecho del texto dentro del control edit. En el parámetro wParam indicamos qué márgenes vamos a definir, y en qué unidades se expresan. Para ello podemos combinar los valores EC_LEFTMARGIN, EC_RIGHTMARGIN y EC_USEFONTINFO. El primero para definir el margen izquierdo, el segundo para definir el derecho, y el tercero para indicar que usaremos la anchura del carácter "A" de la fuente actual para el margen izquierdo, y el del carácter "C" para el derecho. Si no usamos este valor, el parámetro lParam indicará la anchura en pixels.

El parámetro lParam indica el márgen izquierdo en la palabra de menor peso, y el derecho en la de mayor peso. Para combinar estos valores se puede usar la macro MAKELONG.

   SendMessage(hctrl, EM_SETMARGINS, 
      EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(50, 30));

El mensaje EM_GETMARGINS se puede usar para recuperar los valores de los márgenes actuales de un control edit.

Otra opción es usar el mensaje EM_SETRECT para definir el rectángulo que se usará para delimitar el texto. En este mensaje se usa el parámetro lParam para indicar un puntero a una estructura RECT que define el rectángulo delimitador.

El mensaje EM_SETRECTNP es idéntico, con la diferencia de que no se actualiza el control edit para reflejar el nuevo aspecto del control.

   GetClientRect(hctrl, &re);
   re.left += 60;
   re.top += 20;
   re.right -= 40;
   re.bottom -= 40;
   SendMessage(hctrl, EM_SETRECT, 0, (LPARAM)&re);

La ventaja de este método es que nos permite definir los márgenes superior e inferior, además del derecho e izquierdo. La desventaja es que estas definiciones no son permanentes, al contrario que en el caso del mensaje EM_SETMARGINS. De modo que si cambiamos el tamaño de la ventana del control, deberemos volver a definir los márgenes.

Para recuperar el rectángulo delimitador actual de un control edit se puede usar el mensaje EM_GETRECT.

Por último, hablaremos de los tabuladores. En los controles edit multilínea, los tabuladores no se despliegan como un número fijo de espacios, sino que corresponden a distancias fijas con respecto al borde izquierdo de la ventana del control. Cada vez que se introduce un carácter tabulador, se añade el espacio necesario para desplazar el caret a la siguiente posición del tabulador. Recordemos que los controles edit pueden usar fuentes de espacio proporcional, y que el uso principal de los tabuladores es crear tablas, por lo tanto, este es el comportamiento más lógico.

Podemos fijar las marcas de tabulación mediante el mensaje EM_SETTABSTOPS. Estas distancias se miden con respecto al borde izquierdo, y se expresan en unidades de diálogo.

Si sólo indicamos una marca de tabulación, todas las marcas se situarán a distancias iguales, si indicamos más, las primeras n marcas se situarán a las distancias indicadas, y el resto serán equidistantes.

   DWORD lista[10] = {10,25,40,65,95,130,160,200,250,360};
   SendMessage(hctrl, EM_SETTABSTOPS, 10, (LPARAM)&lista);