Para aprender un poquito más sobre la biblioteca gráfica (AWT), vamos a modificar nuestro último programa para usar menús.
Vamos a volver a poner todo el código (que ampliamos para
usar como applet o aplicación local) marcando las diferencias
más notables:
/*
// ----- Archivo: Ejemplo14.java
*/
import java.io.*;
import java.awt.*;
import java.applet.*;
public class Ejemplo14 extends Applet {
public void init() {
new Ventana14(true); // con "true" avisamos que es applet
}
public static void main(String args[]) { // para usarlo como aplicación
Ventana14 v14 = new Ventana14(false); // con "false" avisamos que no es applet
}
}
/*
// -------- Esta clase es la que en realidad hace el trabajo
*/
class Ventana14 extends Frame {
TextArea contenido;
boolean enApplet; // para indicar si lo llamamos como applet
String nombreArchivo; // para guardar el nombre del archivo abierto
MenuItem mArchivoAbrir; // ACA ESTAN LOS ITEMS DE LOS MENUS
MenuItem mArchivoGrabar; // .
MenuItem mArchivoSalir; // .
MenuItem mEditCortar; // .
MenuItem mEditCopiar; // .
MenuItem mEditPegar; // .
MenuItem mEditTodo; // v
String clipboard; // buffer para cortar y pegar
boolean editado = false; // acá indicamos si modificamos el archivo
Ventana14(boolean enApp) {
super("Ejemplo de E/S");
enApplet = enApp; // recordamos si es applet o no
Menu menuArchivo = new Menu("&Archivo"); // CREAMOS LOS MENUS!!!
mArchivoAbrir = new MenuItem("&Abrir...");
mArchivoGrabar = new MenuItem("&Grabar...");
mArchivoSalir = new MenuItem("&Salir");
menuArchivo.add(mArchivoAbrir);
menuArchivo.add(mArchivoGrabar);
menuArchivo.add(new MenuItem("-"));
menuArchivo.add(mArchivoSalir);
Menu menuEdit = new Menu("&Edit");
mEditCortar = new MenuItem("Cor&tar");
mEditCopiar = new MenuItem("&Copiar");
mEditPegar = new MenuItem("&Pegar");
mEditTodo = new MenuItem("&Seleccionar todo");
menuEdit.add(mEditCortar);
menuEdit.add(mEditCopiar);
menuEdit.add(mEditPegar);
menuEdit.add(new MenuItem("-"));
menuEdit.add(mEditTodo);
MenuBar barraMenu = new MenuBar();
barraMenu.add(menuArchivo);
barraMenu.add(menuEdit);
setMenuBar(barraMenu);
contenido = new TextArea(); // solo pongo una ventana de texto
add("Center",contenido);
pack();
show();
clipboard = new String(""); // clipboard vacío,
mEditPegar.disable(); // nada para pegar,
mArchivoGrabar.disable(); // nada para grabar
}
public boolean handleEvent(Event e) {
if ((e.id==Event.WINDOW_DESTROY)||(e.target==mArchivoSalir)) {
if (editado) System.out.println("Pedir confirmación!\n"); // debería confirmar
// si se quiere ir sin grabar!
if (enApplet) dispose();
else System.exit(0);
}
if (e.target==mArchivoAbrir) CargarArchivo(); // acá proceso selecciones
if (e.target==mArchivoGrabar) GrabarArchivo(); // de menú
if (e.target==mEditCortar) {
clipboard = contenido.getSelectedText();
mEditPegar.enable();
contenido.replaceText("",contenido.getSelectionStart(),contenido.getSelectionEnd());
editado=true;
}
if (e.target==mEditCopiar) {
clipboard = contenido.getSelectedText();
mEditPegar.enable();
}
if (e.target==mEditPegar) {
contenido.replaceText("",contenido.getSelectionStart(),contenido.getSelectionEnd());
contenido.insertText(clipboard,contenido.getSelectionStart());
editado=true;
}
if (e.target==mEditTodo) contenido.selectAll();
if ((e.id==Event.KEY_PRESS)&&(e.target==contenido)) editado=true;
mArchivoGrabar.enable(editado);
return super.handleEvent(e);
}
void CargarArchivo() {
FileInputStream fptr;
DataInputStream f;
String linea = null;
if (editado) System.out.println("Pedir confirmación!\n");
FileDialog fd = new FileDialog(this,"Abrir...",FileDialog.LOAD); // elijo archivo
fd.show(); // usando el diálogo estándar del sistema!
nombreArchivo = fd.getFile();
try {
fptr = new FileInputStream(nombreArchivo);
f = new DataInputStream(fptr);
contenido.setText(""); // vacío la ventana antes de cargar nuevo archivo
do {
linea = f.readLine();
if (linea!=null) contenido.appendText(linea+"\n");
} while (linea != null);
fptr.close();
editado=false; // archivo nuevo -> no editado
}
catch (FileNotFoundException e) {
new Error14("El archivo no existe!");
}
catch (IOException e) {
new Error14("Error leyendo archivo!");
}
catch (NullPointerException e) {
;
}
}
void GrabarArchivo() {
FileOutputStream fptr;
DataOutputStream f;
FileDialog fd = new FileDialog(this,"Grabar...",FileDialog.SAVE); // grabo archivo
fd.setFile(nombreArchivo); // usando el diálogo estándar del sistema!
fd.show();
nombreArchivo = fd.getFile();
try {
fptr = new FileOutputStream(nombreArchivo);
f = new DataOutputStream(fptr);
f.writeBytes(contenido.getText());
fptr.close();
editado=false; // recién grabado -> no editado
}
catch (IOException e) {
new Error14("Error grabando archivo!");
}
catch (NullPointerException e) {
;
}
}
}
/*
// ------- Para mostrar los errores...
*/
class Error14 extends Frame {
Error14(String error) {
add("Center",new Label(error));
add("South", new Button("Ok"));
pack();
show();
}
public boolean handleEvent(Event e) {
dispose();
return super.handleEvent(e);
}
}
Bueno, lo primero que vamos a ver son los menús.
La barra de menú está compuesta por menúes,
que a su vez están compuestos de ítems (que pueden
también ser menúes). Por ejemplo la barra de menú
la declaramos con:
MenuBar barraMenu = new MenuBar();
y le agregamos los menúes Archivo
y Edit (que habremos creado previamente)
con:
barraMenu.add(menuArchivo); barraMenu.add(menuEdit);
Finalmente la declaramos como EL menú de la ventana (Frame):
setMenuBar(barraMenu);
Cada uno de los menús los declaramos previamente:
Menu menuArchivo = new Menu("&Archivo");
...
Menu menuEdit = new Menu("&Edit");
Noten que el "&" no se visualiza, sino que la letra que le sigue aparece subrayada: Archivo, Edit. Esto permite que se pueda seleccionar el menú tanto con el mouse como con la tecla alt- o meta-, seguida de la tecla subrayada.
A su vez, el método add
está presente también en la clase Menú
y nos permite agregar los ítems:
mArchivoAbrir = new MenuItem("&Abrir...");
mArchivoGrabar = new MenuItem("&Grabar...");
mArchivoSalir = new MenuItem("&Salir");
menuArchivo.add(mArchivoAbrir);
menuArchivo.add(mArchivoGrabar);
menuArchivo.add(new MenuItem("-"));
menuArchivo.add(mArchivoSalir);
A estos ítems los hemos declarado como globales en la clase
para usarlos luego en los eventos. Noten además que
menuArchivo.add(new MenuItem("-"));
no agrega un ítem al menú sino una línea de separación, y no necesitamos crearlo como objeto permanente.
Si miramos la arquitectura de las clases, tanto MenuBar como MenuItem descienden de MenuComponent. A su vez, Menu desciende de MenuItem, por lo que implementa los mismos métodos y vamos a lo que decíamos antes: un menú puede ser un ítem de otro menú, y así sucesivamente tantos subniveles de menús como queramos.
Finalmente, en nuestro manejador de eventos simplemente necesitamos
verificar si se eligió un ítem probando si el evento
ocurrió sobre el ítem determinado:
if ((e.id==Event.WINDOW_DESTROY)||(e.target==mArchivoSalir)) {
if (editado) System.out.println("Pedir confirmación!\n");
if (enApplet) dispose();
else System.exit(0);
}
if (e.target==mArchivoAbrir) CargarArchivo();
................
if (e.target==mEditTodo) contenido.selectAll();
En resumen lo que hago es:
En todos los casos, si se modifica el texto del contenido
lo indico poniendo editado en true;
lo mismo si presiono una tecla sobre el área de edición:
if ((e.id==Event.KEY_PRESS)&&(e.target==contenido)) editado=true;
Un par de aclaraciones:
En Java disponemos de la clase Dialog para crear diálogos, es decir, ventanitas temporarias para entradas de usuario, que dependen de otra (de hecho la clase Dialog es heredera de la clase Window).
Si bien podemos crear diálogos a medida usando la clase Frame, se supone que usar diálogos debe ser más fácil. La realidad es que por ahora no se puede usar mucho más que los diálogos estándar (y el único que vale la pena es FileDialog), ya que las implementaciones actuales de Java tienen un problema: en algunas plataformas el programa que abre el diálogo sigue, en lugar de esperar que se cierre el diálogo y devuelva la respuesta.
Por eso hemos puesto solamente una indicación adonde debería
haber un diálogo de confirmación:
if (editado) System.out.println("Pedir confirmación!\n");
En ese lugar deberíamos llamar por ejemplo a un diálogo que nos permita decidir por sí o por no:
if (editado) {
sino = new ConfirmarDlg(this,"Archivo modificado!");
if (sino.getResponse()==true) ....;
else ....;
}
o algo así. Esto mismo lo podemos hacer de otras maneras, por ejemplo usando threads y comunicaciones entre procesos, pero se complica mucho para esta altura del curso. Esperemos un poco más adelante, aunque Sun me prometió que en la versión 1.1 ya va a estar corregido (sale para fines del '96).
Por lo pronto, veamos un caso simple con la clase FileDialog:
FileDialog fd = new FileDialog(this,"Abrir...",FileDialog.LOAD); fd.show(); nombreArchivo = fd.getFile();
Primero declaramos una variable de tipo FileDialog, y creamos la instancia con new. Como parámetros se pasa el padre (this, o sea "esta ventana"), el título de la ventanita de diálogo, y una constante LOAD o SAVE (son static, por lo que se denominan directamente con el nombre de la clase y no necesariamente de una instancia) que indica si el diálogo es para cargar o grabar un archivo (Obviamente la tarea en sí de cargar o grabar el archivo la tenenmos que hacer nosotros, el diálogo sólo espera que elijamos un nombre).
El método show() muestra el diálogo y espera que seleccionemos y presionemos Ok o Cancel. Aquí es donde fallan los demás diálogos ya que es programa sigue sin esperar.
Finalmente, el diálogo se cierra pero no se elimina el
objeto (posiblemente está implementado usando el método
hide(), que lo oculta de la vista pero
no se pierde hasta no salir del método que lo creó,
donde actuaría el recogedor de basura de la memoria).
Esto hace que aunque no lo veamos podamos llamar al método
getFile() sobre este objeto, que nos
devuelve el nombre del archivo seleccionado (o null
si se presionó Cancel).
Bueno, antes de meternos en otras bibliotecas, vamos a reservar
una clase más (la próxima) para
Jorge Bourdette