Editor de Textos II

Vamos a continuar con el Editor de Textos. Nuestro programa es capaz de Cortar, Copiar y Pegar, desde el portapapeles, por medio del menú contextual que tiene asociado el componente Memo. Pero vamos a poner un menú por el cual se pueda hacer lo mismo. Así que crea un menú llamado Editar y añade tres entradas que se llamen Copiar, Cortar, Pegar, también puedes poner unos atajos de teclado para darle un toque más "elegante". Si decides colocar los atajos de teclado te recomiendo que coloques los estandar de Windows, simplemente porque así tu programa será más amigable con el usuario de windows, al no tener este que aprenderse nuevos atajos.

Añade las siguientes líneas de código a los eventos de los menús que acabas de crear.

procedure TForm1.Copiar1Click(Sender: TObject);
begin
Memo1.CopyToClipboard;
end;
procedure TForm1.Cortar1Click(Sender: TObject);
begin
Memo1.CutToClipboard;
end;
procedure TForm1.Pegar1Click(Sender: TObject);
begin
Memo1.PasteFromClipboard;
end;

Ahora prueba el programa, y verás que todo va bien. Pero el menú Editar siempre muestra sus submenús disponíbles independientemente que tengas texto seleccionado o no para copiar o pegar. Tambíen la opción Pegar no deberia mostrarse si el portapapeles no contiene texto, ya que nuestro editor solo puede pegar texto desde el portapapeles.
La manera de hacerlo es activar o desactivar estas opciones según tengamos texto o no seleccionado, y estado el portapapeles. Así que selecciona el menú Editar en el Inspector Objetos, y luego en la página de eventos de este selecciona el evento OnClick del menú. Este evento se produce siempre que pulses con el ratón en el menu Editar, antes de mostrar los submenús que depende de él. Pues escribe esto:

procedure TForm1.Editar1Click(Sender: TObject);
Var
Seleccion : boolean;
begin
Pegar1.Enabled := ClipBoard.HasFormat (CF_TEXT);
Seleccion := Memo1.SelLength <> 0;
Cortar1.Enabled := Seleccion;
Copiar1.Enabled := Seleccion;

end;
La prime línea lo que hace es activar o desactivar el menú Pegar según el estado del portapapeles. La siguiente línea coloca verdadero o falso (True o False) en una variable lógica (boleana) que he declarado al inicio del procedimiento. Quizas te sorprenda si ves la ayuda de Delphi y compruebas que la propiedad SelLenght de un componente Tmemo devuelve un entero, el cual lo estamos asignando a una variable lógica. Es muy sencillo si el entero delvuelto es cero la variable Seleccion toma el valor False, en caso contrario vale Verdadero, esto es posible por el operado distinto de cero que hay al final de la línea.Los menús Cortar y Pegar estarán activos según el estado de la variable Seleccion.

Usando métodos del componente Memo, podemos implementar las dos opciones más, una es seleccionar todo, y la otra es borrar todo. La primera la podemos poner en un memú que se llame Seleccionar Todo, y que esté dentro del menú Editar. La segunda opción nos va a servir para hacer un menú que se llame Nuevo y que este dentro del menú Archivo. Hacer esto es muy sencillo, solo hay que llamar a un par de métodos del componente Tmemo. Así en el evento del menú Seleccionar Todo coloca esta línea:
Memo1.SelectAll;

Y para el evento de menú Nuevo coloca esta otra:
Memo1.Clear;

También podemos colocar un menú para suprimir el texto seleccionado, para ello crea un menú que se llame Suprimir y coloca está línea dentro de su evento:
Memo1.ClearSelection;

Y para que este menú no este activo cuando no hay texto seleccionado coloca esta línea en el evento del menú Editar junta a las otras que activas o desactivan los menús:
Suprimir1.Enabled := Seleccion;

Hay detalle que se escapa al control de nuestro editor, que cuando salimos no guarda los cambios. Así que vamos a hacer algo que nos permita guardar el fichero según queramos o no. A lo que me refiero es que vamos a sacar el clásico mensage diciendo si deseamos guardar el fichero o no. Para ello lo primero que tenemos que saber si el texto ha sido modificado o no. Para ello el componente Tmemo tiene un propiedad que solo puede ser leido y en tiempo de ejecución, que se llama Modified, la cual es de tipo boleano.

Ahora solo nos queda preguntar por el estado de esta propiedad antes de cerrar el programa, y según su estado tomar un decisión. El momento ideal para realizar está operación es cuando se ejecuta el evento OnCloseQuery del formulario. Este evento se produce el primero cuando se solicita cerrar el formulario, y además tiene la ventaja que nos permite decidir si deseamos cerrar el formulario o no. Os recuerdo que cerrar un formulario equivale a cerrar el programa si el formulario cerrado es el principal (el primero que se creo al ejecutarse la aplicación). Así que este evento pondremos un líneas de código como las que siguen:
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
Var
Boton : Integer;
begin

If Memo1.Modified = True Then
Begin
CanClose := False;
Boton := Application.MessageBox ('¿Desea guardar los cambios?','Atencion',
Mb_YesNoCancel+Mb_IconInformation);
If Boton = IDYes Then
Begin
Guardar1Click (Sender);
CanClose := True;
End
Else
If Boton = IDNo Then
CanClose := True
Else
If Boton = IDCancel then
CanClose := False;
End;
end;

Lo que conseguimos con estas líneas es que si el texto se ha modificado lo primero que hacemos es prohibir que se cierre el formulario, eso se consigue con el variable CanClose. La cual no he declarado, pero es parte del procedimiento, fijate en la primera línea, donde esta declarado el procedimiento. Después saco el mensaje advirtiendo el usuario, su contestación la guardo en una variable. Fijate que la variable es de tipo integer, ya que ApplicationMessageBox devuelve un entero aunque nosotros lo comparemos con texto, pero este texto son constantes numéricas declaradas por Delphi. Según botón pulsado tomamos un decisión, y cambiamos el valor de Canclose.

Quizas para que el procesador fuera más "elegante" estaria bien que cuando pulsas el menú Nuevo este preguntara si quieres guardar los cambios en el caso que el texto estuviera modificado, pero eso os lo dejo a vosotros, ya sabeis que teneis que consultar la propiedad Modified del componente Memo.

Ya para acabar este ejemplo vamos a darle la capacidad de impresión a nuestro editor y la opción al usario de cambiar el tipo de letra. Empecemos por el tipo de letra, elige el cuadro de dialogo de Fuentes en la paleta Dialogs de Delphi, es ese que tiene unas letras F. Echalo donde más te guste sobre tu formulario, desde un menú que crees para ello (por ejemplo dentro del menú Editar, pon uno que se llame Fuentes), escribe estas líneas para su evento Onclick:
procedure TForm1.Fuentes1Click(Sender: TObject);
begin
If FontDialog1.Execute Then
Memo1.Font := FontDialog1.Font;
end;

Lo que consigues con estas líneas es que cuando se ejecuta el diálogo y el usuario sale pulsando Aceptar, es asignar la fuente que ha elegido el usuario al tu editor de textos.

Si quieres que tu diálogo de fuentes muestre la fuente que tiene el texto cuando es invocado, entonces debes colocar estas líneas en el evento OnShow del cuadro de diálogo. Para localizar este evento, selecciona el cuadro de diálogo y luego vete a la página de eventos del Inspector de Objetos. Este evento se produce cuando se va a mostrar el cuadro, momento que aprovechamos para asignar al cuadro de dialogo la fuente que tiene el componente Memo:
procedure TForm1.FontDialog1Show(Sender: TObject; Wnd: Integer);
begin
FontDialog1.Font := Memo1.Font;
end;

Para los usuarios de Delphi 2.0 o superior, el cuadro de dialogo de fuentes tiene dentro de su propiedad Options una subpropiedad que se llama fdApplyButton, la cual por defecto está en falso, si activas esta opción, tu cuadro de dialogo tendrá un botón más, que se llama Aplicar, al estilo Windows 95. Si activas esta opción aparece un evento más que se llama OnApply, y en el debes colocar una línea idéntica a esta:
Memo1.Font := FontDialog1.Font;

Para imprimir es muy sencillo también. En el evento Onclik de un un menú coloca estas líneas. Acuerdate de poner un cuadro de impresoras, lo tienes en la misma página que el cuadro de fuentes.
procedure TForm1.Imprimir1Click(Sender: TObject);
Var
Impresora : TextFile;
N : Integer;
begin
If PrintDialog1.Execute Then
Begin
AssignPrn (Impresora);
Rewrite (Impresora);
Printer.Canvas.Font := Memo1.Font;
For N := 0 to Memo1.Lines.Count -1 Do
Writeln (Impresora,Memo1.Lines [n]);
CloseFile (impresora);
End;
end;

Acuertate de añadir la cláusula Printers en la línea Uses de tu formulario.

Esto es todo por hoy. Hemos visto como con muy poco de trabajo se puede hacer un editor de textos. Aunque este no sea muy bonito, es funcional. Así los detalles, como un barra de herramientes, la opción Guardar Como, etc os lo dejo a vosotros.