Volviendo al AWT

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);
  }
}


Menú a la Java

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:

Si eligió Archivo/Salir (o alt-F4 o lo que sea) salgo del programa
Si eligió Archivo/Abrir, llamo al método CargarArchivo
Si eligió Archivo/Grabar, llamo al método GrabarArchivo
Si eligió Edit/Cortar copio el texto seleccionado a mi clipboard y borro la selección
Si eligió Edit/Copiar sólo copio el texto seleccionado a mi clipboard
Si eligió Edit/Pegar borro el texto seleccionado e inserto el de mi clipboard
Si eligió Edit/Seleccionar_todo marco todo el texto

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:

getSelectionStart() y getSelectionEnd() marcan los límites del texto seleccionado (si no lo hay, son iguales).
getSelectedText() devuelve el texto seleccionado en el TextArea.
replaceText() reemplaza una parte (o todo) del TextArea por un String.
insertText() inserta un String en un lugar determinado del TextArea.
selectAll() selecciona todo el texto del TextArea.
MenuItem.enable() habilita un ítem de menú. Lo utilizo para habilitar Edit/Pegar sólo luego de cortar o copiar algo a mi clipboard.
En el caso del ítem Archivo/Grabar, lo habilito o no dependiendo de la variable editado, utilizando la otra forma de enable: MenuItem.enable(boolean).


Diálogos

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

 

<> Página de tutoriales <> Página Anterior <> Siguiente Capitulo <>