SlideShare una empresa de Scribd logo
1 de 114
Descargar para leer sin conexión
EL ARCHIPIÉLAGO ECLIPSE
(PARTE 4 DE 4)
Miguel Ángel Abián
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 2 de 114
EL ARCHIPIÉLAGO ECLIPSE
(PARTE 4 DE 4)
Fecha de última revisión: 27.05.2014
Miguel Ángel Abián
mabian ARROBA aidima PUNTO es
Conozco máquinas que son más complicadas que la gente.
Thomas Pynchon. V
La gente suele preguntarme: “¿Por qué escribe usted en una lengua moribunda?”. Esa
pregunta tiene varias respuestas. En primer lugar, me gusta escribir historias de fantasmas,
y para eso nada hay más adecuado que una lengua moribunda. Cuanto más muerta está una
lengua, más vivo está el fantasma. A los fantasmas les encanta el yiddish y, por lo que sé,
todos lo hablan. [...] Por otra parte, el yiddish puede ser una lengua moribunda, pero es la
única que conozco bien. Fue la lengua de mi madre, y ustedes saben que una madre nunca
está realmente muerta.
I. B. Singer. Discurso en la ceremonia de entrega de los premios Nobel (1978)
En 1968, los Beatles publicaron el doble disco conocido como el Álbum blanco.
Visto en retrospectiva, el disco es el testimonio de un grupo que había dejado atrás los
alaridos de las fans y que estaba buscando su propia voz, lejos de la psicodelia y del
rock and roll. En el Álbum blanco hay de todo, como en un bazar: confusión (musical y
personal), pop, rock'n'roll, distorsión, experimentación (musical y comercial), baladas,
aullidos. No obstante, el disco no es un caos informe ni un revoltijo desconcertante: hay
un cierto orden interno, una cierta coherencia. Los Beatles no siguieron el camino
esbozado en este disco, bien porque los conflictos personales estaban destrozando a la
banda, bien porque ese camino no coincidía con lo que la gente esperaba de un grupo
tan conocido.
Eclipse me recuerda mucho al Álbum blanco. La plataforma Eclipse tiene de todo:
uno puede trabajar, entre otros lenguajes, con Java, C/C++, PHP, JavaScript, Ruby,
Pascal, Eiffel, Python y COBOL (el no muerto, el Nosferatu de los lenguajes informáticos:
espero que nunca le muerda). Nadie le impide agregar a Eclipse los plug-ins
(extensiones) que desee y trabajar con un magnífico IDE, hecho a medida, para ASP,
JSP, XML, XHTML o HTML. Es más, puede escribir sus propios plug-ins y
comercializarlos. Tantas posibilidades no se traducen –por ahora– en un Helter Skelter:
la comunidad de desarrolladores y los publicistas que rodean a Eclipse están trabajando
mucho y muy bien, con metas muy claras. Con todo, hay mucho de experimentación, en
este caso informática y comercial, dentro y alrededor de Eclipse.
Copyright (c) 2005-2014, Miguel Ángel Abián. Este documento puede ser distribuido sólo
bajo los términos y condiciones de la licencia de Documentación de javaHispano v1.0 o
posterior (la última versión se encuentra en http://www.javahispano.org/licencias/).
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 3 de 114
Al proyecto Eclipse le deseo mejor suerte que los Beatles tras el Álbum blanco. Si el
proyecto se fragmenta en exceso, es posible que a los subproyectos les ocurra lo mismo
que a los componentes del grupo. A saber: que ninguno alcanzó la popularidad y el
reconocimiento que habían tenido cuando estaban unidos.
John Lennon dijo en 1980, poco antes de morir: “Si los años sesenta o los Beatles
tenían un mensaje, era aprender a nadar. Y una vez has aprendido, nadar”. Esta serie de
artículos es una rápida lección de natación, impartida con un solo fin: invitarle, si no lo ha
hecho ya, a zambullirse en las aguas del océano Eclipse. No tiene nada que perder, y sí
mucho que ganar.
14. SWT: el nuevo componente gráfico para Java
El Standard Widget Toolkit (SWT) de la plataforma Eclipse ha sido el aspecto de
Eclipse que mayores rechazos, adhesiones y controversias ha provocado en la
comunidad de Java. SWT constituye la respuesta de IBM, para el desarrollo de interfaces
de usuario, a AWT y a Swing (la historia de SWT y su papel en la estrategia comercial de
IBM se explicarán en la próxima parte de esta serie de artículos). De acuerdo con la
documentación oficial de Eclipse, he aquí la descripción del componente Standard
Widget Toolkit: "El componente SWT está diseñado para proporcionar acceso eficaz y
transportable a los servicios de interfaz de usuario del sistema operativo sobre el cual se
implementa". Dicho de otra manera, también oficial: “[SWT es] un conjunto de
componentes gráficos y una biblioteca gráfica integrada con el sistema de ventanas
específico del sistema operativo, pero con una API independiente del SO”. Tanto SWT
como JFace son bibliotecas estructuradas en paquetes con clases e interfaces escritas
en Java.
Widget es una palabra inglesa que se usa para referirse a un dispositivo cuyo
nombre no se conoce o no importa, o bien a un dispositivo que todavía no existe; una
traducción aproximada sería cachivache o trasto. Chocaría mucho leer frases como “el
cachivache pequeño se coloca en el centro del cachivache grande” en un artículo técnico
en español; en cambio, en inglés suenan incluso simpáticas.
La documentación oficial de Eclipse (http://www.eclipse.org) establece que un
widget es “cualquier objeto de la interfaz gráfica de usuario que puede colocarse dentro
de otro widget” (versión tecnológica de la frase “Una gallina es lo que usan los huevos
para producir otros huevos”; el ser humano no ha avanzado mucho explicando las cosas,
la verdad). Veamos una definición más aclaratoria: “Un widget es un objeto de la interfaz
gráfica de usuario, responsable de la interacción con el usuario”.
En la documentación y la bibliografía de Eclipse suelen usarse de manera
intercambiable las palabras widget y control (elemento de la interfaz gráfica con
contrapartida en el sistema operativo; es decir, gestionado por el SO). Esta equivalencia
no resulta del todo cierta, pues no todos los widgets son controles. Consideremos, vaya
por caso, un control de tipo árbol: sus nodos y subnodos son widgets (elementos de la
interfaz gráfica), pero no controles. La razón para esto es práctica: si el árbol se encarga
de dibujar los nodos y subnodos, se evita consumir los recursos del sistema operativo
que se necesitarían si cada nodo fuera un control.
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 4 de 114
Suele decirse que Swing es un componente de “peso ligero” (lightweight), mientras
que SWT y AWT son de “peso pesado” (heavyweight). Aunque el significado de estos
términos dista mucho de ser unánime, suele aceptarse que “peso ligero” significa que
hay una asociación o correspondencia uno a uno (one-to-one mapping) entre los widgets
del componente y los de la plataforma (controles). Un componente de peso pesado
simplifica mucho la programación gráfica: todas las implementaciones de los sucesos
(apretar una tecla, mover el ratón...) vienen dadas por el sistema operativo, mejor dicho,
por el gestor de ventanas del sistema operativo. Su principal desventaja radica que
consume muchos recursos del sistema operativo (memoria, punteros, ciclos de CPU).
Consideremos, por ejemplo, que creamos un objeto java.awt.Button: al instante, el gestor
de ventanas del SO creará su propio botón. Cuando el usuario pulse el botón, el suceso
irá, primero, de la biblioteca gráfica del SO a la máquina virtual de Java; luego, irá a la
lógica vinculada al objeto Button de AWT, que decidirá qué hay que hacer.
En un componente de peso ligero, los widgets son dibujados por el componente, no
por el SO. En consecuencia, cada componente debe encargarse de implementar
sucesos como mover el ratón o apretar una tecla. A cambio, su rendimiento resulta, en
general, mejor que el de uno pesado (en cuanto a recursos, es más facil y menos
exigente dibujar widgets que dejar que los cree el SO). Consideremos, por ejemplo, una
ventana con cien o doscientas etiquetas. En Swing, la ventana (un JFrame) se
comportará mejor que en AWT o SWT, pues las etiquetas se dibujan sobre la ventana y,
por tanto, no hay que preocuparse por liberar sus recursos (son dibujos). En estos dos
últimos componentes gráficos, las etiquetas serán creadas por el SO, que tendrá que
conservarlas en memoria en todo momento y supervisar sus sucesos y el estado de cada
una.
Otro ejemplo: consideremos una tabla con un millón de filas. En Swing, el tiempo
que se tardaría en dibujarla apenas diferiría del empleado para dibujar una tabla de diez
mil filas. En SWT, el tiempo que se tardaría en dibujar la tabla de un millón de filas sería
de dos órdenes de magnitud superior al necesario para dibujar la tabla de diez mil filas,
pues cada fila sería un widget mantenido por el sistema operativo (omito aquí cualquier
consideración sobre la memoria que emplearía SWT). Por regla general, la escalabilidad
de los componentes de peso ligero es mucho mayor que la de los componentes de peso
pesado.
¿Es SWT un AWT con muchos más widgets? Por fuera, sí; pero por dentro hay
muchas diferencias. Tanto AWT como SWT usan las funciones de la API gráfica de las
plataformas donde se ejecutan, pero lo hacen de modo muy distinto. AWT usa un
sistema de componentes “gemelos” donde cada widget AWT tiene asociado un
componente en el gestor de ventanas del sistema operativo. Cada widget AWT tiene
asociado código nativo, escrito en C o C++, que hace llamadas a las bibliotecas gráficas
de la plataforma (en Windows, Win32 o MFC) y que controla el comportamiento de los
widgets. En cada JDK (Java Distribution Kit) se incluyen las clases Java de AWT y el
código máquina resultante de compilar el código nativo (el código máquina cambia para
cada plataforma, pero no las clases Java).
De resultas del diseño basado en componentes gemelos, AWT tiene un número
muy limitado de widgets, pues sólo puede incluir componentes presentes en todas las
plataformas donde se implementa. Por ejemplo, no hay un widget árbol en AWT, pues no
existe en Motif y, por tanto, en esa plataforma no hay un componente gemelo árbol al
que AWT puede llamar.
En SWT, los widgets están escritos en Java, no en código nativo; y los métodos
gráficos de la plataforma se llaman a través de una “capa” que se explicará más
adelante. Para cada plataforma, esta capa expone los métodos de la API (interfaz de
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 5 de 114
programación de aplicaciones) gráfica de la plataforma, de suerte que las clases SWT
(escritas en Java) pueden llamar a los métodos de la API gráfica. A diferencia de AWT,
las clases SWT cambian en cada plataforma. Aparte de que hay muchos más widgets en
SWT que en AWT, la gran ventaja de SWT es que toda la lógica de los widgets está
escrita en Java (en AWT, la lógica está mezclada con código nativo de cada plataforma).
El propósito original de Swing consistía en proporcionar a las aplicaciones escritas
en Java el mismo aspecto en todas las plataformas (con la posibilidad de imitar hasta
cierto punto la apariencia propia de cada una). Aunque con Swing pueden producirse,
con esfuerzo, aplicaciones en Java de aspecto similar a las aplicaciones propias de cada
plataforma, su rendimiento siempre ha sido pobre. Resulta destacable que Swing haya
mejorado bastante su eficacia en las versiones 1.4 y 5.0 (antes 1.5) de Java.
Posiblemente, la competencia que supone SWT ha influido beneficiosamente en el
desarrollo de Swing.
El motivo del pobre rendimiento con Swing radica en que el código que interacciona
directamente con los widgets de cada plataforma está escrito en C o C++ (forma parte de
las bibliotecas de AWT específicas de cada plataforma) y, en consecuencia, no está
disponible para Swing, que está escrito en Java. También la API que proporcionan los
widgets propios de cada plataforma está escrita en C o C++, y Swing no puede acceder
directamente a ella. Por estos motivos, Swing trabaja con la API gráfica que le
proporciona AWT. En Swing, cada componente es dibujado como una imagen en el
componente que lo contiene, lo que requiere que Swing trabaje bit a bit con las interfaces
gráficas. Cuando se quiere mostrar una interfaz al usuario, Swing –a través de AWT–
envía el mapa de bits correspondiente al gestor de ventanas propio de la plataforma
donde se trabaja. Como puede suponerse, este planteamiento dista mucho de ser eficaz;
sus limitaciones se aprecian enseguida en dispositivos como agendas electrónicas o
teléfonos móviles, donde la memoria está muy limitada.
En cuanto a la personalización de los controles Swing para cada plataforma (lo que
se conoce como pluggable look and feel o pluggable L&F), los resultados han sido
sumamente mediocres: en parte porque la apariencia no coincide exactamente con la
que deberían tener, y en parte porque Sun no ha ido actualizando Swing a las nuevas
versiones de los sistemas operativos. Así, por ejemplo, la apariencia y sensación de
Windows (Windows L&F) que proporciona Swing ha sido (hasta la versión 1.4.2 de Java)
la misma para Windows 95, Windows 98, Windows 2000, Windows Millenium (curioso
experimento fallido, por cierto) y Windows XP. Como usted mismo habrá comprobado, la
apariencia de las aplicaciones para Windows cambiaba, y mucho, de una versión del
sistema operativo a otro.
En comparación con Swing, el SWT de Eclipse ha roto las reglas del juego
mediante el empleo de una estrategia sumamente ingeniosa y eficaz: usa la API JNI de
Java (Java Native Interface, interfaz nativa de Java). JNI permite que los programas en
Java llamen a métodos nativos (métodos propios de la plataforma, generalmente escritos
en C o C++) y que reciban los resultados que éstos devuelven. Cuando se compila un
archivo con código JNI-C (código en C donde se implementan métodos declarados de
tipo JNI), se genera una biblioteca compartida –en Windows, una DLL o biblioteca de
enlace dinámico–, que permite acceder desde Java a los métodos declarados en el
código JNI-C.
Para ilustrar cómo se trabaja con JNI, considero aquí el ejemplo de un método
nativo que calcula números de Fibonacci.
En primer lugar, el método nativo se declara en una clase llamada MetodoNativo:
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 6 de 114
public class MetodoNativo {
// declaración Java del método
public native int fibonacci(int n);
// Carga la biblioteca compartida llamada native.
static {
System.loadLibrary("native");
}
}
En segundo lugar, se compila la clase anterior y, luego, se usa la herramienta javah
de la siguiente forma (omito cualquier consideración sobre el CLASSPATH):
javah –jni MetodoNativo
Con el paso anterior, se crea un archivo MetodoNativo.h que contiene lo siguiente:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MetodoNativo */
#ifndef _Included_MetodoNativo
#define _Included_MetodoNativo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MetodoNativo
* Method: fibonacci
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_MetodoNativo_fibonacci
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
En tercer lugar, la implementación en C del método fibonacci() se guarda en un
archivo ImplementacionMetodoNativo.c:
#include <jni.h>
#include “MetodoNativo.h”
/* implementación en C de la función que calcula el énesimo número de Fibonacci */
int fibonacci(int n) {
if (n <= 1)
return 0;
if (n == 2)
return 1;
Declaración del método Java_MetodoNativo_fibonacci().
El argumento JNIEnv* es un puntero al entorno de
ejecución del método.
El argumento jobject es una referencia a la clase donde se
especifica el método (MetodoNativo.java)
El argumento jint es el tipo C que corresponde al tipo
Java int, declarado como tipo de retorno del método
fibonacci ().
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 7 de 114
return (fibonacci(n-1) + fibonacci(n –2));
}
/* implementación de Java_MetodoNativo_fibonacci() */
JNIEXPORT jint JNICALL
Java_MetodoNativo_fibonacci(JNIEnv *env, jobject obj, jint n) {
return fibonacci(n);
}
Por último, el archivo anterior se compila con un compilador de C o C++, que creará
un biblioteca compartida (en Windows, una DLL). A partir de ese momento, el método
nativo fibonacci() podrá usarse desde cualquier clase de Java.
La técnica que usa SWT para llamar –mediante JNI– a las API gráficas de cada
sistema operativo (escritas en C o C++) consiste en establecer, siempre que sea posible,
una correspondencia uno a uno entre los métodos en Java de cada widget SWT y las
llamadas a la API gráfica propia de cada sistema operativo. Más adelante, veremos
código JNI-C de SWT donde se aprecia esa correspondencia.
Las razones por las que SWT se basa en dicha estrategia son tres: eficacia,
velocidad e integración con la plataforma. Un widget SWT llama directamente al gestor
de ventanas específico de la plataforma; así se evita que Java tenga que enviar,
mediante AWT, las interfaces en forma de mapas de bits. Con SWT se logra una
integración casi completa con cada plataforma. No olvidemos que la integración con la
plataforma no se reduce a la apariencia y la sensación (L&F): características como
“copiar y pegar” o “arrastrar y soltar” son automáticas si se usa el SWT, pero no lo son
tanto si se usa Swing. Pese al tiempo transcurrido desde que salieron las primeras
versiones de Swing, aún existen acciones que no producen en algunas plataformas los
resultados que esperaría el usuario habituado a éstas.
Tal es la integración de SWT con la plataforma, que refleja inmediatamente los
cambios en la apariencia y sensación de la interfaz de usuario del sistema operativo
subyacente. Así, un cambio de piel (skin) de Windows XP provocará que todas las
interfaces de usuario construidas con componentes SWT cambien al instante su
apariencia y sensación. Una piel o skin es un conjunto de gráficos que incluye todas las
piezas necesarias para cambiar el aspecto y sensación de la interfaz gráfica. Los gráficos
pueden incluir imágenes para botones, barras, menús, etc. Skinning es la acción de
aplicar una piel. Cuando se aplica una piel a Windows, se altera su apariencia y
sensación: cambian los colores, los botones, etc. (El término skinning se usó
originalmente en el videojuego Quake, juego tremendamente popular por su carácter
intimista y conciliador. Para dar variedad al juego se inventaron varias pieles, que
permitían al usuario cargar una serie de gráficos que cambiaban la apariencia del
personaje principal, un pacífico y sosegado ciudadano.)
La integración no se detiene en la apariencia y la sensación de Windows: SWT
permite trabajar con los objetos binarios reutilizables de Microsoft (COM). De hecho, con
unas pocas llamadas a métodos SWT se puede incluir controles ActiveX y documentos
OLE en las aplicaciones SWT.
La declaración del método Java_MetodoNativo_fibonacci() en
ImplementacionMetodoNativo.c debe coincidir con la del método
Java_MetodoNativo_fibonacci() en MetodoNativo.h.
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 8 de 114
Figura 14. Bloc de notas visto con distintas pieles de Windows. Se ha utilizado
la herramienta WindowBlinds 3, disponible en http://www.windowblinds.net
SWT usa los widgets propios de cada plataforma siempre que sea posible, excepto
cuando uno no existe en todas las plataformas. En ese caso, SWT lo simula en las
plataformas que no cuentan con él. Dicho de otro modo: SWT usa para los widgets un
enfoque basado en el mínimo común denominador. Por ejemplo, Motif no proporciona
por omisión un widget de tipo árbol; consecuentemente, el SWT proporciona un widget
árbol con la misma API que la implementación propia de Windows. Ningún usuario de
Motif notará nada raro cuando use un componente SWT árbol: como éste no existe de
serie en Motif, el usuario carecerá de ideas preconcebidas sobre cómo debe comportarse
(salvo que provenga de Windows). A diferencia de AWT, SWT proporciona todos los
controles que se suponen imprescindibles en cualquier interfaz gráfica moderna.
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 9 de 114
Una vez obtenida la correspondencia uno a uno entre los métodos en Java y los
métodos propios de la API gráfica de la plataforma, el desarrollador puede olvidarse del
código JNI-C y de los detalles de bajo nivel del sistema operativo, y limitarse a utilizar los
métodos públicos que proporcionan las clases de SWT. Por así decirlo, simplificando un
poco, SWT encapsula de modo transparente el sistema operativo mediante JNI,
permitiendo así utilizar todas las características de los widgets propios de cada
plataforma. Actúa, en definitiva, como una fina capa entre Java y las bibliotecas de la API
gráfica específica de cada SO. En comparación con Swing, SWT evita el uso de una
capa gruesa y ralentizadora como la de AWT (veáse la figura 15).
Casi todo el código de las clases SWT se limita a actuar como “puente” entre el
mundo de Java y el de la plataforma: excepto en el caso de los componentes SWT sin
equivalente en la plataforma, apenas se almacena información sobre los componentes
SWT. No hace falta mucho más: SWT usa, siempre que puede, la información que
proporciona la API gráfica de cada plataforma.
Cualquier aplicación escrita en Java podría utilizar la JNI para establecer una
correspondencia entre los métodos Java y las capacidades gráficas propias de la
plataforma usada. La ventaja de usar SWT radica en que evita a los programadores
escribir su propio código JNI (tarea que, desde luego, dista mucho de ser trivial y
requiere un buen conocimiento de la plataforma con la cual se trabaja).
Fig. 15. Funcionamiento interno de los dos componentes gráficos
Componente
gráfico Swing AWT
Biblioteca
gráfica del SO
Swing
Componente
gráfico SWT
Biblioteca
gráfica del SO
JNI-C
SWT
Miguel Ángel Abián, marzo de 2005
escrito en C escrita en Cescrito en Java
escrito en Java escrita en C
r
FUNCIONAMIENTO INTERNO DE SWING y SWT
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 10 de 114
Para ver cómo funcionan en SWT los métodos JNI, podemos considerar el método
setMenu() de la clase Java OS (clase que representa el sistema operativo de la
plataforma y que forma parte de SWT):
public static final native boolean SetMenu (int hWnd, int hMenu);
Este método asigna un menú a una ventana. La palabra reservada native indica al
compilador de Java dos cosas: a) la implementación del código no está escrita en Java;
b) la implementación se guarda en un archivo (os.c) que no es el de la clase OS
(os.java). En Windows, el código JNI-C correspondiente al método setFocus() es
JNIEXPORT jboolean JNICALL OS_NATIVE(SetMenu)
(JNIEnv *env, jclass that, jint arg0, jint arg1) {
jboolean rc = 0;
OS_NATIVE_ENTER(env, that, SetMenu_FUNC);
rc = (jboolean)SetMenu((HWND)arg0, (HMENU)arg1);
OS_NATIVE_EXIT(env, that, SetMenu_FUNC);
return rc;
}
Como puede verse, la implementación en Java del método SetMenu() de la clase
OS llama a un método del sistema operativo también llamado SetMenu(). (Tal como se
adelantó, hay una relación uno a uno entre los métodos de SWT y los métodos gráficos
de la plataforma.)
Cada plataforma con una implementación del SWT tiene una biblioteca compartida
(en Windows, una biblioteca dinámica o DLL) y uno o más archivos JAR. La biblioteca
compartida, específica de cada plataforma, resulta de compilar el código JNI-C que
establece una correspondencia uno a uno entre los métodos Java y los métodos de la
API gráfica propia del SO. En Windows, esta biblioteca contiene, entre otro mucho
código, el código máquina resultante de compilar el código JNI-C del método setMenu()
que se ha usado antes como ejemplo.
Los archivos JAR contienen las clases Java de SWT –Button, Tree, List, Text,
Label, etc.–, que llaman a los métodos JNI-C correspondientes (los cuales, a su vez,
llaman a los métodos nativos de la plataforma). Estas clases comunes, por tener las
mismas declaraciones de sus métodos públicos para todas las plataformas,
permiten que el código Java que use SWT se ejecute sin necesidad de cambios en
cualquier otra plataforma donde exista una implementación de SWT. En ese sentido,
SWT resulta a la par transportable y específico de la plataforma, por contradictorio que
parezca (en SWT, todas las interfaces directas a la API gráfica específica del SO están
escritas en Java, excepto la de más bajo nivel). Tal y como dice IBM en su página web:
“Un importante beneficio de usar SWT es que, una vez se desarrolla una interfaz de
usuario en Linux, es también una interfaz de primera clase cuando se aplica a Windows.
Lo inverso también es cierto”.
Un ejemplo completo aclarará el porqué del contradictorio carácter del SWT:
consideremos un widget SWT Button, subclase de la clase abstracta Control, y
supongamos que trabajamos con implementaciones de SWT en Windows y Solaris
(olvidemos temporalmente que hay más plataformas).
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 11 de 114
En Windows, la clase Button tiene el siguiente código (por brevedad, omito todos los
comentarios, salvo el de copyright, y algunos de los métodos):
package org.eclipse.swt.widgets;
/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*/
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.events.*;
public class Button extends Control {
Image image;
static final int ButtonProc;
static final TCHAR ButtonClass = new TCHAR (0,"BUTTON", true);
static final int CheckWidth, CheckHeight;
static {
int hBitmap = OS.LoadBitmap (0, OS.OBM_CHECKBOXES);
if (hBitmap == 0) {
CheckWidth = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CXSMICON :
OS.SM_CXVSCROLL);
CheckHeight = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CYSMICON :
OS.SM_CYVSCROLL);
} else {
BITMAP bitmap = new BITMAP ();
OS.GetObject (hBitmap, BITMAP.sizeof, bitmap);
OS.DeleteObject (hBitmap);
CheckWidth = bitmap.bmWidth / 4;
CheckHeight = bitmap.bmHeight / 3;
}
WNDCLASS lpWndClass = new WNDCLASS ();
OS.GetClassInfo (0, ButtonClass, lpWndClass);
ButtonProc = lpWndClass.lpfnWndProc;
}
public Button (Composite parent, int style) {
super (parent, checkStyle (style));
}
public void addSelectionListener (SelectionListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener (listener);
addListener (SWT.Selection,typedListener);
addListener (SWT.DefaultSelection,typedListener);
}
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 12 de 114
int callWindowProc (int msg, int wParam, int lParam) {
if (handle == 0) return 0;
return OS.CallWindowProc (ButtonProc, handle, msg, wParam, lParam);
}
void click () {
OS.SendMessage (handle, OS.BM_CLICK, 0, 0);
}
public Point computeSize (int wHint, int hHint, boolean changed) {
checkWidget ();
int border = getBorderWidth ();
int width = border * 2, height = border * 2;
if ((style & SWT.ARROW) != 0) {
if ((style & (SWT.UP | SWT.DOWN)) != 0) {
width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
height += OS.GetSystemMetrics (OS.SM_CYVSCROLL);
} else {
width += OS.GetSystemMetrics (OS.SM_CXHSCROLL);
height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
}
if (wHint != SWT.DEFAULT) width = wHint + (border * 2);
if (hHint != SWT.DEFAULT) height = hHint + (border * 2);
return new Point (width, height);
}
int extra = 0;
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & (OS.BS_BITMAP | OS.BS_ICON)) == 0) {
int oldFont = 0;
int hDC = OS.GetDC (handle);
int newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
TEXTMETRIC lptm = new TEXTMETRIC ();
OS.GetTextMetrics (hDC, lptm);
int length = OS.GetWindowTextLength (handle);
if (length == 0) {
height += lptm.tmHeight;
} else {
extra = Math.max (8, lptm.tmAveCharWidth);
TCHAR buffer = new TCHAR (getCodePage (), length + 1);
OS.GetWindowText (handle, buffer, buffer.length ());
RECT rect = new RECT ();
int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE;
OS.DrawText (hDC, buffer, length, rect, flags);
width += rect.right - rect.left;
height += rect.bottom - rect.top;
}
if (newFont != 0) OS.SelectObject (hDC, oldFont);
OS.ReleaseDC (handle, hDC);
} else {
if (image != null) {
Rectangle rect = image.getBounds ();
width = rect.width;
height = rect.height;
extra = 8;
}
}
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 13 de 114
if ((style & (SWT.CHECK | SWT.RADIO)) != 0) {
width += CheckWidth + extra;
height = Math.max (height, CheckHeight + 3);
}
if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
width += 10; height += 7;
}
if (wHint != SWT.DEFAULT) width = wHint + (border * 2);
if (hHint != SWT.DEFAULT) height = hHint + (border * 2);
return new Point (width, height);
}
int defaultBackground () {
if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
return OS.GetSysColor (OS.COLOR_BTNFACE);
}
return super.defaultBackground ();
}
int defaultForeground () {
return OS.GetSysColor (OS.COLOR_BTNTEXT);
}
public int getAlignment () {
checkWidget ();
if ((style & SWT.ARROW) != 0) {
if ((style & SWT.UP) != 0) return SWT.UP;
if ((style & SWT.DOWN) != 0) return SWT.DOWN;
if ((style & SWT.LEFT) != 0) return SWT.LEFT;
if ((style & SWT.RIGHT) != 0) return SWT.RIGHT;
return SWT.UP;
}
if ((style & SWT.LEFT) != 0) return SWT.LEFT;
if ((style & SWT.CENTER) != 0) return SWT.CENTER;
if ((style & SWT.RIGHT) != 0) return SWT.RIGHT;
return SWT.LEFT;
}
String getNameText () {
return getText ();
}
public String getText () {
checkWidget ();
int length = OS.GetWindowTextLength (handle);
if (length == 0) return "";
TCHAR buffer = new TCHAR (getCodePage (), length + 1);
OS.GetWindowText (handle, buffer, length + 1);
return buffer.toString (0, length);
}
public void setText (String string) {
checkWidget ();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
int oldBits = newBits;
newBits &= ~(OS.BS_BITMAP | OS.BS_ICON);
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 14 de 114
if (newBits != oldBits) {
OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
}
TCHAR buffer = new TCHAR (getCodePage (), string, true);
OS.SetWindowText (handle, buffer);
}
LRESULT WM_GETDLGCODE (int wParam, int lParam) {
LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
if (result != null) return result;
if ((style & SWT.ARROW) != 0) {
return new LRESULT (OS.DLGC_STATIC);
}
return result;
}
LRESULT WM_KILLFOCUS (int wParam, int lParam) {
LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
if ((style & SWT.PUSH) != 0 && getDefault ()) {
menuShell ().setDefaultButton (null, false);
}
return result;
}
}
En Solaris 8, la clase Button presenta este código (por brevedad, omito todos los
comentarios, salvo el de copyright, y algunos de los métodos):
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.widgets;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.motif.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.events.*;
public class Button extends Control {
String text = "";
Image image, bitmap, disabled;
static final byte [] ARM_AND_ACTIVATE;
static {
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 15 de 114
String name = "ArmAndActivate";
int length = name.length();
char [] unicode = new char [length];
name.getChars (0, length, unicode, 0);
byte [] buffer = new byte [length + 1];
for (int i = 0; i < length; i++) {
buffer[i] = (byte) unicode[i];
}
ARM_AND_ACTIVATE = buffer;
}
public Button (Composite parent, int style) {
super (parent, checkStyle (style));
}
public void addSelectionListener(SelectionListener listener) {
checkWidget();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener(listener);
addListener(SWT.Selection,typedListener);
addListener(SWT.DefaultSelection,typedListener);
}
public Point computeSize (int wHint, int hHint, boolean changed) {
checkWidget();
int border = getBorderWidth ();
int width = border * 2, height = border * 2;
if ((style & SWT.ARROW) != 0) {
width += display.scrolledMarginX;
height += display.scrolledMarginY;
if (wHint != SWT.DEFAULT) width = wHint + (border * 2);
if (hHint != SWT.DEFAULT) height = hHint + (border * 2);
return new Point (width, height);
}
XtWidgetGeometry result = new XtWidgetGeometry ();
result.request_mode = OS.CWWidth | OS.CWHeight;
int [] argList2 = {OS.XmNrecomputeSize, 1};
OS.XtSetValues(handle, argList2, argList2.length / 2);
OS.XtQueryGeometry (handle, null, result);
int [] argList3 = {OS.XmNrecomputeSize, 0};
OS.XtSetValues(handle, argList3, argList3.length / 2);
width += result.width;
height += result.height;
int [] argList = {OS.XmNlabelType, 0};
OS.XtGetValues (handle, argList, argList.length / 2);
if (argList [1] == OS.XmSTRING) {
int [] argList1 = {OS.XmNlabelString, 0};
OS.XtGetValues (handle, argList1, argList1.length / 2);
int xmString = argList1 [1];
if (OS.XmStringEmpty (xmString)) height += getFontHeight (font.handle);
if (xmString != 0) OS.XmStringFree (xmString);
}
if (wHint != SWT.DEFAULT || hHint != SWT.DEFAULT) {
int [] argList4 = new int [] {OS.XmNmarginLeft, 0, OS.XmNmarginRight, 0,
OS.XmNmarginTop, 0, OS.XmNmarginBottom, 0};
OS.XtGetValues (handle, argList4, argList4.length / 2);
if (wHint != SWT.DEFAULT) width = wHint + argList4 [1] + argList4 [3] + (border * 2);
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 16 de 114
if (hHint != SWT.DEFAULT) height = hHint + argList4 [5] + argList4 [7] + (border * 2);
}
return new Point (width, height);
}
public int getAlignment () {
checkWidget();
if ((style & SWT.ARROW) != 0) {
int [] argList = {OS.XmNarrowDirection, 0};
OS.XtGetValues (handle, argList, argList.length / 2);
int direction = argList [1];
if (direction == OS.XmARROW_UP) return SWT.UP;
if (direction == OS.XmARROW_DOWN) return SWT.DOWN;
if (direction == OS.XmARROW_LEFT) return SWT.LEFT;
if (direction == OS.XmARROW_RIGHT) return SWT.RIGHT;
return SWT.UP;
}
int [] argList = {OS.XmNalignment, 0};
OS.XtGetValues (handle, argList, argList.length / 2);
int alignment = argList [1];
if (alignment == OS.XmALIGNMENT_BEGINNING) return SWT.LEFT;
if (alignment == OS.XmALIGNMENT_CENTER) return SWT.CENTER;
if (alignment == OS.XmALIGNMENT_END)return SWT.RIGHT;
return SWT.CENTER;
}
boolean getDefault () {
if ((style & SWT.PUSH) == 0) return false;
return this == menuShell ().defaultButton;
}
String getNameText () {
return getText ();
}
public String getText () {
checkWidget();
if ((style & SWT.ARROW) != 0) return "";
return text;
}
public void setAlignment (int alignment) {
checkWidget();
if ((style & SWT.ARROW) != 0) {
int [] argList = {OS.XmNarrowDirection, OS.XmARROW_UP};
if ((alignment & SWT.UP) != 0) argList [1] = OS.XmARROW_UP;
if ((alignment & SWT.DOWN) != 0) argList [1] = OS.XmARROW_DOWN;
if ((alignment & SWT.LEFT) != 0) argList [1] = OS.XmARROW_LEFT;
if ((alignment & SWT.RIGHT) != 0) argList [1] = OS.XmARROW_RIGHT;
OS.XtSetValues (handle, argList, argList.length / 2);
return;
}
if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return;
int [] argList = {OS.XmNalignment, OS.XmALIGNMENT_BEGINNING};
if ((alignment & SWT.CENTER) != 0) argList [1] = OS.XmALIGNMENT_CENTER;
if ((alignment & SWT.RIGHT) != 0) argList [1] = OS.XmALIGNMENT_END;
OS.XtSetValues (handle, argList, argList.length / 2);
}
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 17 de 114
public void setText (String string) {
checkWidget();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
if ((style & SWT.ARROW) != 0) return;
text = string;
char [] text = new char [string.length ()];
string.getChars (0, text.length, text, 0);
int mnemonic = fixMnemonic (text);
byte [] buffer = Converter.wcsToMbcs (getCodePage (), text, true);
int xmString = OS.XmStringParseText (
buffer,
0,
OS.XmFONTLIST_DEFAULT_TAG,
OS.XmCHARSET_TEXT,
null,
0,
0);
if (xmString == 0) error (SWT.ERROR_CANNOT_SET_TEXT);
if (mnemonic == 0) mnemonic = OS.XK_VoidSymbol;
int [] argList = {
OS.XmNlabelType, OS.XmSTRING,
OS.XmNlabelString, xmString,
OS.XmNmnemonic, mnemonic,
};
OS.XtSetValues (handle, argList, argList.length / 2);
if (xmString != 0) OS.XmStringFree (xmString);
}
int xFocusIn (XFocusChangeEvent xEvent) {
super.xFocusIn (xEvent);
if (handle == 0) return 0;
if ((style & SWT.PUSH) != 0) {
menuShell ().setDefaultButton (this, false);
}
return 0;
}
}
Como puede observarse, la clase Button en Solaris presenta un código muy distinto
del correspondiente a la misma clase en Windows, pero ambas mantienen la misma
declaración –es decir, los mismos argumentos y el mismo tipo de retorno– para todos los
métodos públicos. Por ejemplo, el método público setText() de Button mantiene la misma
declaración tanto en Solaris como en Windows: public void setText (String string). (Lo
mismo sucede en todas las demás plataformas para las que está disponible SWT.)
En cuanto a la implementación de los métodos públicos, varía completamente de
una a otra cada plataforma. En el caso de setText(), se usan en Windows llamadas a
métodos como OS.GetWindowLong(), que llama, a su vez, al método GetWindowLong()
de la biblioteca gráfica Win32; en Solaris se usan llamadas a métodos como
OS.XmStringParseText(), que llama, a su vez, al método XmStringParseText() de la
bibliotecas gráfica Motif. Tal como se dijo antes, la clase OS representa el sistema
operativo de la plataforma.
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 18 de 114
Los métodos que no son públicos gozan de completa libertad en cuanto a
declaración. Generalmente, no tienen por qué existir en todas las plataformas. Por
ejemplo, no hay un método con una declaración int callWindowProc(int p0, int p1, int p2)
en la clase Button de Solaris, método que goza de buena salud en Windows.
Como ya se adelantó hace unas páginas, que los métodos públicos de los widgets
SWT se declaren igual en todas las plataformas causa que el SWT sea transportable. En
el ejemplo de Button, la implementación de cada método en Solaris no será transportable
a Windows, y viceversa; pero el código Java que utilice los métodos públicos de Button sí
lo será, puesto que ambas plataformas tienen implementaciones del SWT.
Continuando con el ejemplo de setText(), la sentencia miBoton.setText("Soy un
botón SWT") hará llamadas en Windows y Solaris, a través de JNI, a métodos nativos
completamente diferentes; pero funcionará en ambas plataformas (dando distintas
resultados gráficos, claro está). El código Java que llama a SWT no necesita conocer la
implementación de las clases SWT ni el código JNI-C encargado de llamar a la API
gráfica correspondiente. Más aún: si lo hiciera se incumpliría el principio de encapsulado,
fundamental en la programación orientada a objetos.
Las bibliotecas JNI-SWT deben compilarse para cada plataforma, lo cual crea un
archivo con extensión .dll en Windows y otro con extensión .so en Solaris. En la página
web de Eclipse se puede descargar el código JNI-C de la biblioteca JNI-SWT para cada
plataforma, así como el código compilado para cada plataforma. Un programa que utilice
SWT empleará las bibliotecas Win32 o MFC (propias de Windows) cuando se ejecuta en
Windows (98/ME/2000/XP), y las bibliotecas de Motif cuando se ejecuta en Solaris.
La correspondencia casi exacta entre los objetos gráficos de SWT –widgets, tipos
de letra, colores, imágenes– y los del sistema operativo hace que el recolector de basura
de Java no sirva para liberar los recursos asociados a esos objetos; pues la tarea natural
del recolector es liberar los recursos asociados a objetos que “viven” en la máquina
virtual de Java y que ya no se utilizan, no liberar recursos del sistema operativo donde se
ejecuta la MVJ. Por esto, la liberación de los recursos asociados a objetos SWT debe
establecerla el programador. Por ejemplo, una ventana repleta de controles puede
cerrarse de varias maneras –uso “cerrar” en el sentido de hacerla desaparecer de la vista
del usuario–; pero el programador deberá llamar explícitamente al método dispose() del
display correspondiente si realmente quiere liberar los recursos de la ventana y de sus
controles. En caso contrario, puede suceder que la aplicación se cuelgue o funcione muy
lentamente si contiene muchas ventanas que ya no se necesitan y cuyos recursos no se
han liberado. Para evitar fallos de memoria y la disminución del rendimiento, se
recomienda liberar los recursos asociados a un objeto SWT en cuanto se deje de
necesitarlo.
SWT sigue la estrategia de que liberar los recursos de un control “padre” conlleva la
liberación de los recursos de todos los controles “hijos”. Dicho de otro modo: al llamar al
método dispose() del padre, se llama a cada método dispose() de los hijos.
Métodos como dispose() resultan necesarios cuando se trabaja con componentes
gráficos o con sockets, pues llaman a métodos nativos que saben cómo liberar los
recursos asociados del sistema operativo. Si desea más información sobre el uso de
dispose() con sockets, puede consultar el apartado 3.3 del tutorial java.net y java.nio.
Cómo hacer un chat en Java, http://www.javahispano.org/tutorials.item.action?id=7).
Esas llamadas a métodos nativos son inevitables, ya que los componentes gráficos de
peso pesado y las conexiones de red dependen por completo del SO.
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 19 de 114
En el caso de los objetos SWT correspondientes a colores (Color), imágenes
(Image) y tipos de letra (Font), el programador debe ser cuidadoso. De un lado, dichos
objetos carecen de objetos “padre”. De otro, los recursos que consumen no se liberan
cuando se llama al método dispose() de los componentes donde se usan tales objetos.
Estas dos características no se deben a ningún error de diseño. Antes al contrario:
responden a un entendimiento cabal de la naturaleza de esos objetos. Si, por ejemplo,
las imágenes tuvieran que crearse asociadas a componentes padre (ventanas, botones,
listas, menús...), todos los componentes con una misma imagen necesitarían sendas
instancias de la clase Image. Esta situación sería muy incómoda para el programador
(quien tendría que escribir la instaciación una y otra vez) y para el sistema operativo
(imaginémoslo como un gruñón que se preguntara por qué demonios le obligan a
mantener tantos objetos Image, cuando en realidad corresponden a un misma imagen, y
que amenazara con colgar el sistema como protesta). A consecuencia de la “libertad” de
la que gozan esos objetos, resulta obligatorio que no se destruyan cuando se destruyen
los componentes que los usan. Si varios componentes usaran un mismo objeto Image y
al destruir uno se destruyera el objeto Image, ¿en qué estado se encontrarían los
restantes componentes? Sin imagen, que ya estaría en el cubo de la basura.
Como los objetos Color, Image y Font no se destruyen cuando desaparecen los
componentes que los usan, el programador debe liberarlos explícitamente llamando a
dispose(). En el apartado dedicado a JFace veremos cómo se puede simplificar la
liberación de los recursos que consumen estos objetos.
Toda la interfaz gráfica de Eclipse se basa en SWT. Ahora bien, ello no significa que
este componente gráfico se pueda usar sólo con Eclipse: las bibliotecas SWT se pueden
descargar independientemente de Eclipse y pueden usarse para construir aplicaciones
independientes. En la última versión de Eclipse (3.0.2), lanzada el 11 de marzo de 2005
(la versión 3.1M5a, aún no definitiva, salió en febrero de este año), los componentes
SWT y JFace se hallan disponibles para las siguientes plataformas:
Windows 98/ME/2000/XP
Windows PocketPC 2002 (Strong ARM)
Linux (x86/Motif)
Linux (x86/GTK 2)
Linux (AMD 64/GTK 2)
Linux (IA 64/GTK 2)
Solaris 8 (SPARC/Motif)
AIX (PPC/Motif)
HP-UX (HP9000/Motif)
HP-UX (IA 64/Motif)
Como la licencia de SWT coincide con la de Eclipse (Common Public License, ya
explicada en el primer artículo de esta serie:
http://www.javahispano.org/articles.article.action?id=75), cualquier programador puede
usar este componente sin pagar regalías. Para ello, debe configurar el CLASSPATH de
su aplicación para que incluya el archivo jar del SWT (dependiendo de la plataforma,
pueden ser varios) y la propiedad java.library.path para que incluya la biblioteca
compartida JNI-SWT propia de la plataforma usada (esta biblioteca resulta de compilar
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 20 de 114
todo el código JNI-C que usan las clases SWT; en Windows tiene un nombre del tipo swt-
win32.xxxx.dll).
En las siguientes figuras se muestra el aspecto de Eclipse en varias plataformas.
Como Eclipse usa exclusivamente componentes SWT, su aspecto cambia en cada
plataforma.
Fig. 16. SWT en acción: Eclipse en Linux-Motif. Extraído de la documentación
oficial de Eclipse.
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 21 de 114
Fig. 17. SWT en acción: Eclipse en Linux/Gnome2
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 22 de 114
Fig. 18. SWT en acción: Eclipse en Windows XP
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 23 de 114
Fig. 19. SWT en acción: Eclipse en Mac OS X
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 24 de 114
15. Ejemplos de uso del Standard Widget Tool (SWT)
Esta sección no pretende, ni mucho menos, ser una introducción completa a SWT.
Sus objetivos son mucho más modestos: explicar qué se necesita para programar con
este componente gráfico, presentar algunos aspectos de su uso y dar unos cuantos
ejemplos representativos de cómo se usan los widgets SWT. Dar una explicación
completa del funcionamiento de SWT queda fuera de las metas de esta serie de artículos
(definidas en el primero, http://www.javahispano.org/articles.article.action?id=75).
Usando SWT, el típico programa HolaMundo quedaría así:
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
public class Saludos {
public static void main(String args[]){
// Se crea un display que servirá como contenedor para el shell.
Display display = new Display();
// Se coloca el display dentro del shell.
Shell shell = new Shell(display);
// Se establece el tamaño y el texto del shell.
shell.setSize(250, 120);
shell.setText("Mi primera aplicación con SWT");
// Se crea una etiqueta centrada en la ventana y se le da un texto.
Label etiqueta= new Label(shell, SWT.CENTER);
etiqueta.setText("Saludos a los lectores de javaHispano");
etiqueta.setBounds(shell.getClientArea());
// Se muestra el shell; un shell es invisible salvo que se llame a open().
shell.open ();
// Se entra en un bucle para leer y enviar los sucesos del usuario.
// Si no hay ningún suceso que tratar, el método sleep() hace que el
// programa espere el suceso siguiente sin consumir ciclos de CPU.
// Se sale del bucle cuando el usuario cierra el shell.
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
// Se cierra el display y se liberan los recursos que empleaba del sistema operativo
// Siempre es conveniente liberar explícitamente los recursos
// que no se van a necesitar. En este caso no es necesario llamar a dispose(),
// pues si se llega a aquí es porque el programa va a terminar, lo que implica que
// se liberarán los recursos asociados; pero es una buena práctica acostumbrarse
// a hacerlo.
Ejemplo 1: Saludos.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 25 de 114
display.dispose ();
}
}
Para que un programa con SWT pueda compilarse y ejecutarse en el entorno de
Eclipse, se necesitan dos pasos previos.
El primero consiste en indicar al compilador dónde está el código Java de SWT; es
decir, dónde se encuentran las clases SWT. Éstas se almacenan en el archivo swt.jar.
Para indicar a Eclipse dónde debe buscar el archivo swt.jar hay que seguir varios pasos.
En primer lugar, hay que apretar, sobre el nombre del proyecto de Java, el botón derecho
del ratón (véase la figura 20). A continuación, hay que seleccionar la opción Properties.
En la ventana que aparece debe seleccionarse Java Build Path (panel de la derecha) y
la pestaña Libraries (véase la figura 21). Acto seguido, debe incluirse swt.jar con el
botón Add External JARs... (véase la figura 22).
Según el FAQ más actualizado de Eclipse
(http://www.eclipse.org/eclipse/faq/eclipse-faq.html), swt.jar se encuentra –dependiendo
de la plataforma– en
• win32: INSTALLDIReclipsepluginsorg.eclipse.swt.win32_3.0.1wswin32
• Linux GTK: INSTALLDIR/eclipse/plugins/org.eclipse.swt.gtk_3.0.1/ws/gtk/
• Linux Motif: INSTALLDIR/eclipse/plugins/org.eclipse.swt.motif_3.0.1/ws/motif/
• Photon QNX: INSTALLDIR/eclipse/plugins/org.eclipse.swt.photon_3.0.1/ws/photon/
• Mac OS X: INSTALLDIR/eclipse/plugins/org.eclipse.swt.carbon_3.0.1/ws/carbon/
INSTALLDIR es el directorio donde se ha instalado Eclipse. Dependiendo de la
versión de SWT que se use, cambiará el número separado por puntos que aparece en
las rutas (en vez de 3.0.1, podría ser 3.1.0, por ejemplo).
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 26 de 114
Figura 20. Nótese que la vista que se usa es la de paquetes
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 27 de 114
Figura 21. Hay que apretar el botón Add External JARs...
Figura 22. Hay que seleccionar el archivo swt.jar (en otra plataforma, se encontrará
en otro directorio)
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 28 de 114
El segundo paso que se requiere para ejecutar el programa consiste en indicar a
Eclipse dónde está la biblioteca compartida que requiere swt.jar. Una manera consiste en
introducir, cuando se arranca la máquina virtual de Java, la ubicación de la biblioteca
compartida como argumento de ejecución. Para ello, basta elegir el menú Run, escoger
Run... (véase la figura 23), elegir la pestaña Arguments en la parte derecha de la
pantalla e introducir en el área de texto VM arguments lo siguiente (véase la figura 24a):
-Djava.library.path=<directorio que contiene la biblioteca compartida para SWT>
Tal y como sucedía con swt.jar, la biblioteca se encontrará en una ubicación dependiente
de la plataforma. Según el FAQ de Eclipse, las ubicaciones serán del estilo (la lista de
plataformas no es exhaustiva):
• Windows: INSTALLDIReclipsepluginsorg.eclipse.swt.win32_3.0.1oswin32x86
• Linux GTK: INSTALLDIR/eclipse/plugins/org.eclipse.swt.gtk_3.0.1/os/linux/x86
• Linux Motif: INSTALLDIR/eclipse/plugins/org.eclipse.swt.motif_3.0.1/os/linux/x86
• Photon QNX: INSTALLDIR/eclipse/plugins/org.eclipse.swt.photon_3.0.1/os/qnx/x86
• Mac OS X: INSTALLDIR/eclipse/plugins/org.eclipse.swt.carbon_3.0.1/os/macosx/ppc
Figura 23. Se escoge Run...
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 29 de 114
Figura 24a. La versión de Eclipse de la captura de pantalla es la 3.1M4
El nombre exacto de la biblioteca JNI–SWT varía en cada versión de Eclipse (a
veces, también varía con la plataforma, aunque no varíe la versión). En la versión 3.1M3
de Eclipse para Windows 98/ME/2000/XP, el nombre de la biblioteca es swt-win-32-
3111.dll (véase la figura 24b).
Figura 24b. Biblioteca JNI-SWT correspondiente a Eclipse 3.1M3
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 30 de 114
Completados todos los pasos anteriores, la ejecución de la clase Saludos en
Windows XP producirá una ventana similar a la de la figura 25a.
Figura 25a. Resultado de ejecutar la clase Saludos
Figura 25b. Manteniendo en ejecución la clase Saludos, la ventana cambia de
aspecto cuando se cambia el estilo de Windows XP por el clásico de Windows.
Como ya se señaló en el apartado anterior, para desarrollar o ejecutar aplicaciones
SWT no se necesita el entorno de desarrollo Eclipse. Para ejecutar en Windows una
aplicación SWT sin Eclipse, hay que añadir el archivo swt.jar al CLASSPATH e incluir la
biblioteca dinámica swt-win-32-xxxx.dll en el java.library.path. Tanto swt.jar como la DLL
se pueden descargar sin el resto de Eclipse.
Por ejemplo, en el caso de que ambos archivos estén en C:SWT, la siguiente orden
compilará la aplicación Saludos desde la línea de órdenes:
javac -classpath C:SWTswt.jar Saludos.java
La siguiente orden ejecutará Saludos desde la línea de órdenes:
java -classpath C:SWTswt.jar -Djava.library.path=C:SWT Saludos
Para el resto de las plataformas, el proceso resulta similar a éste.
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 31 de 114
La clase Saludos, si bien muy simple, alberga la estructura de cualquier aplicación SWT:
1) Se crea un objeto Display.
2) Se crea un objeto Shell que sirve como ventana principal de la
aplicación.
3) Se crean dentro del shell los widgets (componentes gráficos) que se
deseen.
4) Se configuran los tamaños de los widgets (normalmente, en este paso
se registran los sucesos a los cuales atenderán los widgets).
5) Se abre la ventana del shell.
6) Se entra en un bucle donde se comprueban los sucesos que se van
produciendo.
7) Se cierra el objeto Display y se liberan los recursos asociados.
La clase Display (contenida en el paquete org.eclipse.swt.widgets) actúa como
“puente” entre el SWT y las operaciones del sistema de interfaz gráfica de la plataforma.
Según la documentación de esta clase, su principal función es implementar el bucle de
sucesos de SWT mediante el modelo de sucesos de la plataforma. Además, proporciona
métodos para acceder a información sobre el sistema operativo y para controlar y
gestionar los recursos del sistema donde se ejecuta SWT. En resumen, un objeto Display
se comporta como un intermediario entre la interfaz de usuario y el componente o
componentes que lo implementan en una plataforma.
Por lo general, el programador –salvo que escriba aplicaciones multihilo– debe
preocuparse solamente de: a) crear un objeto Display antes de crear ninguna ventana; y
b) cerrarlo cuando ya no se necesite.
El método dispose() se encarga de liberar los recursos que el display consumía del
sistema de ventanas específico de la plataforma (memoria, punteros). Al llamarlo, se
eliminan todos los shells que tenía el display.
Las instancias de la clase Shell (contenida en el paquete org.eclipse.swt.widgets)
vienen a ser ventanas gestionadas por el gestor de ventanas propio de la plataforma.
Cuando el usuario mueve o cambia el tamaño de una ventana, SWT pasa el tratamiento
de estas acciones a la API gráfica del SO. Esta clase tiene varios constructores que
admiten como argumento un objeto shell: con ellos se pueden generan shells de diálogo
o secundarios (shell significa cáscara).
El método dispose() de esta clase actúa de forma similar al método homónimo de la
clase Display.
Nota: La manera más sencilla de evitar la configuración del
CLASSPATH y del argumento -Djava.library.path desde la línea de
órdenes es colocar el fichero o los ficheros JAR en el directorio lib/ext
del JRE (Java Runtime Environment), y la biblioteca compartida JNI-
SWT en el directorio bin del JRE.
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 32 de 114
Si comparamos SWT con Swing, los componentes de Swing serían equivalentes a
los widgets del SWT, y los marcos y ventanas (frames and windows), a los shells.
En el constructor de la etiqueta habrá visto que hay un segundo argumento en el
constructor:
Label etiqueta= new Label(shell, SWT.CENTER);
SWT.CENTER (o CENTER, por abreviar) es un estilo. Los estilos son constantes
enteras que se emplean para establecer el aspecto y comportamiento de los widgets. En
este ejemplo, el estilo CENTER indica que el texto de la etiqueta debe centrarse respecto
a ella. Todos los estilos están definidos en la clase org.eclipse.swt.SWT. Una vez se
asigna un estilo a un widget, aquél no puede cambiarse. La clase Shell que acabamos de
ver admite los siguientes estilos: BORDER, H_SCROLL, V_SCROLL, CLOSE, MIN,
MAX, RESIZE, TITLE, SHELL_TRIM y DIALOG_TRIM.
Un widget puede aceptar varios estilos:
Label etiqueta = new Label(shell, SWT.CENTER | SWT.HORIZONTAL | SWT.SEPARATOR);
Figura 26. Ejemplo de distintos estilos de un mensaje de diálogo
El paquete org.eclipse.swt.widgets incluye un conjunto de widgets o componentes
SWT formado, entre otros, por estos elementos:
- Button. SWT no tiene una clase individual para un botón redondo, de flecha, etc.:
todos son instancias de Button.
- Label. Una etiqueta muestra una cadena de texto, una imagen o una línea –vertical
u horizontal– separadora.
- Text. Muestra un texto susceptible de ser editado, ya sea de una línea o de varias.
- Slider. Control que representa un intervalo de valores numéricos.
- ProgressBar. Control que indica el porcentaje cumplido de una tarea (descargar
un archivo, cargar una aplicación, etc.).
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 33 de 114
- Combo. Control que permite seleccionar un valor de una lista desplegable o
introducir directamente el valor deseado.
- List. Control que permite seleccionar (de forma simple o múltiple) elementos de
una lista de cadenas de texto.
- Composite. Control que permite agrupar otros controles. Equivaldría a un
contenedor (container) de Swing.
- Group. Control formado a partir de un Composite con título y borde.
- Canvas. Control que permite operaciones gráficas de todo tipo (dibujar un
octógono azul, por ejemplo)
- Menu. Control que contiene elementos seleccionables de tipo menú.
- MenuItem. Elemento seleccionable que representa un elemento en un menú.
- Table. Control seleccionable que muestra una lista de elementos de una tabla.
- TableColumn. Elemento seleccionable que representa una columna de una tabla.
- TableItem. Objeto seleccionable que representa un elemento de una tabla.
- ToolBar. Control compuesto que permite diseñar elementos de barras de
herramientas.
- ToolItem. Objeto seleccionable que representa un elemento de una barra de
herramientas.
- Tree. Control seleccionable en forma de árbol que muestra una lista jerárquica de
elementos.
- TreeItem. Objeto que representa una jerarquía de objetos en un control Tree.
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 34 de 114
Figura 27. Funcionamiento interno de SWT
Antes de dar algunos ejemplos del uso de estos componentes, creo pertinente
incluir aquí algunos comentarios sobre el tratamiento de sucesos en SWT. El tratamiento
de los sucesos en el SWT resulta muy similar al de Swing y al de AWT: a cada widget se
le añade una clase Listener mediante un método del estilo addXXXListener(), cuyo
argumento es la clase que implementa la correspondiente interfaz de tipo Listener.
Asimismo, existen clases adaptadoras –clases que implementan por omisión las
interfaces Listener–, de manera que el programador necesite escribir sólo el código de
los sucesos que verdaderamente le interesan.
Plataforma
Display Shell
Composite
Widget
Widget
Widget
Widget
Bibliotecas
gráficas
JNI
FUNCIONAMIENTO INTERNO DEL COMPONENTE
SWT
Miguel Ángel Abián, 2005
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 35 de 114
Nota: Si no tiene experiencia con el tratamiento de sucesos en
Swing o AWT, le resultará útil saber lo siguiente:
1) Cada suceso se asocia con una acción del usuario (pulsar una
tecla, mover el ratón...).
2) Cada suceso tiene asociado una interfaz de Java llamada
Listener (oyente).
3) Es tarea del programador crear una clase Listener que
implemente la interfaz Listener correspondiente y en la que se
especifique qué respuesta debe darse cuando se produzca un suceso.
4) Para añadir una clase Listener a un control se usan métodos
del estilo addXXXListener().
5) Las clases Adapters (adaptadoras) son clases que
implementan las interfaces Listener con una implementación por
omisión para cada método. En vez de una clase Listener, el
programador puede asociar un Adapter a un componente, de manera
que sólo tenga que implementar los métodos que necesite. Por
ejemplo, a la interfaz FocusListener le corresponde la clase
adaptadora FocusAdapter.
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 36 de 114
Figura 28. Tabla con los sucesos y las clases Listener más importantes
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 37 de 114
He aquí un ejemplo de código para tratar sucesos (corresponde a un componente
SWT de tipo Button):
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
/**
* Esta clase genera un botón que inicialmente permanece apretado.
*
* La clase Button admite los siguientes estilos: BORDER, CHECK, PUSH,
* RADIO, TOGGLE, FLAT, ARROW (con UP, DOWN), LEFT, RIGHT y CENTER.
*
* BORDER crea un botón con borde.
* CHECK crea un botón de casilla de verificación.
* PUSH crea un botón estándar (valor por omisión).
* RADIO crea un botón de opción.
* TOGGLE crea un botón que mantiene su estado pulsado o no pulsado.
* FLAT crea un botón sin efectos de relieve 3D (plano).
* ARROW crea un botón en forma de flecha (UP y DOWN marcan el sentido de ésta).
* LEFT, RIGHT, CENTER alinean el texto asociado al botón.
*/
public class EjemploBoton {
public static void main(String args[]) {
// Se crea un display que servirá como contenedor para el shell.
Display display = new Display();
// Se coloca el display dentro del shell.
Shell shell = new Shell(display);
// Se establece el tamaño y el texto del shell.
shell.setSize(250, 120);
shell.setText("Ventana hecha con el SWT");
// Se establece el tipo, eltamaño y el texto del botón.
// Un botón TOGGLE permanece presionado tras pulsarlo, hasta que se vuelve a
// pulsar.
Button boton = new Button(shell, SWT.TOGGLE);
boton.setSize(150, 50);
boton.setText("Soy un botón de tipo TOGGLE");
// Tratamiento de sucesos: muy similar a como se hace en Swing.
// Nótese que hay que implementar todos los métodos de cada interfaz
// Listener.
boton.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Se ha seleccionado el botón");
}
public void widgetDefaultSelected(SelectionEvent e) {
System.out.println("Se ha seleccionado el botón");
Ejemplo 2: EjemploBoton.java
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 38 de 114
}
});
boton.addMouseListener(new MouseListener() {
public void mouseDown(MouseEvent e) {
System.out.println("Se ha movido el ratón hacia abajo");
}
public void mouseUp(MouseEvent e) {
System.out.println("Se ha movido el ratón hacia arriba");
}
public void mouseDoubleClick(MouseEvent e) {
System.out.println("Se ha hecho doble click en el botón");
}
});
// Se muestra el shell; un shell es invisible salvo que se llame a open().
shell.open();
// Se entra en un bucle para leer y enviar los sucesos del usuario.
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
// Se cierra el display y se liberan los recursos del sistema operativo
// asociados.
display.dispose();
}
}
Figura 29. Resultado de ejecutar la clase EjemploBoton
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 39 de 114
Si en la clase EjemploBoton interesara procesar sólo el suceso Selection, sería
mucho más rápido usar la correspondiente clase adaptadora:
// Tratamiento del suceso Selection con adaptadores.
boton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Se ha seleccionado el botón");
}
});
El ejemplo anterior quedaría así:
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
public class EjemploBoton2 {
public static void main(String args[]){
// Se crea un display que servirá como contenedor para el shell.
Display display = new Display();
// Se coloca el display dentro del shell.
Shell shell = new Shell(display);
// Se establece el tamaño y el texto del shell.
shell.setSize(250, 120);
shell.setText("Ventana hecha con el SWT");
// Se establece el tipo, el tamaño y el texto del botón.
Button boton = new Button(shell, SWT.TOGGLE);
boton.setSize(150, 50);
boton.setText("Soy un botón de tipo TOGGLE");
// Tratamiento del suceso Selection con adaptadores.
boton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Se ha seleccionado el botón");
}
});
shell.open ();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose ();
}
Ejemplo 3: EjemploBoton2.java
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 40 de 114
}
Con todo, existe una relevante diferencia entre el SWT y Swing: un programa que
use SWT y no disponga de algún bucle que se encargue de leer y procesar los sucesos
del sistema operativo terminará en cuanto se llegue al final del método main(). Por
ejemplo, el usuario sólo verá una ventana que se cierra al momento si ejecuta las clases
anteriores sin este código (si su máquina es lo bastante rápida, puede que ni siquiera
llegue a ver la ventana):
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
Asociada a esta diferencia entre SWT y Swing, está el hecho de que los sucesos de
SWT sólo se entregan al programa (y, en consecuencia, sólo pueden procesarse)
cuando se llama a un método SWT. En los ejemplos anteriores, el método
readAndDispatch() se encarga de entregar al programa en ejecución los sucesos
generados por el usuario. Sin él, el comportamiento del botón sería anómalo: no
respondería a las acciones del usuario (defecto, dicho sea de paso, que los usuarios
desprecian).
A continuación, incluyo unos cuantos ejemplos de uso de los principales controles
SWT. Cuando se especifican coordenadas, hay que tener en cuenta que (0, 0)
corresponde a la esquina superior izquierda, que los puntos situados por debajo del
origen tienen su componente y positiva y que los puntos a la derecha del origen tiene su
componente x positiva.
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.*;
/**
* Esta clase genera una ventana con una imagen y una barra de menús
* Se necesita incluir el paquete org.eclips.swt.graphics.Image
* para poder trabajar con imágenes en SWT.
*
* La clase Menu actúa como un contenedor para los objetos MenuItem.
* La clase Menu admite los siguientes estilos: BAR, DROP_DOWN y POP_UP.
*
* BAR crea una barra de menús.
* DROP_DOWN crea un menú desplegable.
* POP_UP crea un menú contextual.
*
*
* La clase MenuItem admite los siguientes estilos: CHECK, CASCADE, PUSH, RADIO y
Ejemplo 4: EjemploMenu1.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 41 de 114
* SEPARATOR.
*
* CHECK crea un menú de casilla de verificación.
* CASCADE crea un menú en cascada con un submenú.
* PUSH crea un elemento estándar de menú.
* RADIO crea un menú de opción.
* SEPARATOR crea un separador de elementos de menú.
*
*/
public class EjemploMenu1{
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(200, 200);
shell.setText("Ejemplo 1 de menús");
shell.setImage(new Image(display,"c:eclipse.jpg"));
Menu menu = new Menu(shell, SWT.BAR);
shell.setMenuBar(menu);
shell.open();
while (!shell.isDisposed()){
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
Figura 30. Resultado de ejecutar la clase EjemploMenu1
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 42 de 114
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Image;
/**
* Esta clase genera una ventana una barra de menús que tiene dos menús con submenús.
* Se necesita incluir el paquete org.eclips.swt.graphics.Image
* para poder trabajar con imágenes en SWT.
*
*/
public class EjemploMenu2 {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 300);
shell.setText("Ejemplo 2 de menús");
shell.setImage(new Image(display, "c:Eclipse.jpg"));
// Se crea la barra de menús. Se pueden crear más, pero sólo una
// puede ser visible en un instante dado.
Menu barra = new Menu(shell, SWT.BAR);
// Se crea un MenuItem llamado archivo.
MenuItem archivo = new MenuItem(barra, SWT.CASCADE);
archivo.setText("Archivo");
// Se crea el menú de archivos y se asocia al MenuItem archivo de la barra
// de menús.
Menu menuArchivo = new Menu(shell, SWT.DROP_DOWN);
archivo.setMenu(menuArchivo);
MenuItem abrir = new MenuItem(menuArchivo, SWT.RADIO);
abrir.setText("Abrir");
// Se añade al menú de archivos una línea separadora.
MenuItem separador = new MenuItem(menuArchivo, SWT.SEPARATOR);
// Se añade al menú Archivo un elemento Salir.
MenuItem salir = new MenuItem(menuArchivo, SWT.PUSH);
salir.setText("Salir");
// Se crea un MenuItem llamado editar.
MenuItem editar = new MenuItem(barra, SWT.CASCADE);
editar.setText("Edición");
Ejemplo 5: EjemploMenu2.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 43 de 114
// Se crea el menú de edición y se asocia al MenuItem editar de la barra de
// menús.
Menu menuEditar = new Menu(shell, SWT.DROP_DOWN);
editar.setMenu(menuEditar);
MenuItem cortar = new MenuItem(menuEditar, SWT.PUSH);
cortar.setText("Cortar");
// Se añade al menú de edición un elemento Copiar.
MenuItem copiar = new MenuItem(menuEditar, SWT.PUSH);
copiar.setText("Copiar");
// Se añade al menú de edición un elemento Pegar.
MenuItem pegar = new MenuItem(menuEditar, SWT.PUSH);
pegar.setText("Pegar");
// Tratamiento de los sucesos del usuario. Es obligatorio
// incluir el método widgetDefaultSelected, aunque esté
// vacío.
abrir.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Apretó Abrir");
}
public void widgetDefaultSelected(SelectionEvent e) { }
});
salir.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.exit(0); // se sale de la aplicación
}
public void widgetDefaultSelected(SelectionEvent e) { }
});
cortar.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Apretó Cortar");
}
public void widgetDefaultSelected(SelectionEvent e) { }
});
copiar.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Apretó Copiar");
}
public void widgetDefaultSelected(SelectionEvent e) { }
});
pegar.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Apretó Pegar");
}
public void widgetDefaultSelected(SelectionEvent e) { }
});
shell.setMenuBar(barra);
shell.open();
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 44 de 114
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
Figura 31. Resultado de ejecutar la clase EjemploMenu2
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 45 de 114
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.*;
/**
* Esta clase genera un cuadro de texto multilínea.
* Se necesita incluir el paquete org.eclips.swt.graphics.Image
* para poder trabajar con imágenes en SWT.
*
* La clase Label admite los siguientes estilos: BORDER, CENTER,
* LEFT, RIGHT, WRAP y SEPARATOR (con HORIZONTAL, SHADOW_IN,
* SHADOW_OUT, SHADOW_NONE y VERTICAL).
*
* BORDER crea una etiqueta con borde.
* CENTER, LEFT y RIGHT alinean el texto de la etiqueta con respecto a la etiqueta.
* WRAP hace que el texto dentro de la etiqueta se pueda dividir en varias líneas, si
* resulta necesario.
* SHADOW_IN (usado con SEPARATOR) crea un separador que parece empotrado
* en la ventana.
* SHADOW_OUT (usado con SEPARATOR) crea un separador que parece sobresalir
* de la ventana.
* SHADOW_NONE (usado con SEPARATOR) crea un separador sin sombra.
* SEPARATOR y VERTICAL generan una línea vertical.
* SEPARATOR y HORIZONTAL generan una línea horizontal.
*
*
* La clase Text admite los siguientes estilos: BORDER, SINGLE,
* READ_ONLY, LEFT, CENTER, RIGHT, WRAP, PASSWORD y MULTI
* (con H_SCROLL, V_SCROLL)
*
* BORDER crea un cuadro de texto con borde.
* SINGLE crea un cuadro de texto que sólo permite una línea.
* MULTI crea un cuadro de texto que admite varias líneas.
* READ_ONLY crea un cuadro de texto cuyo contenido no se puede editar.
* CENTER, LEFT y RIGHT alinean el texto del cuadro de texto con respecto a él.
* WRAP hace que el texto dentro del cuadro de texto se pueda dividir en varias líneas, si
* resulta necesario.
* PASSWORD crea un cuadro de texto que muestra asteriscos cuando se escribe en él.
* H_SCROLL crea una barra de desplazamiento horizontal.
* V_SCROLL crea una barra de desplazamiento vertical.
*/
public class EjemploCuadroTexto1 {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(280, 230);
shell.setText("Ejemplo 1 de cuadro de texto");
Ejemplo 6: EjemploCuadroTexto1.java
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 46 de 114
// Se crean los componentes gráficos.
Label l1 = new Label(shell, SWT.NONE);
l1.setText("Nombre del usuario:");
l1.setBounds(90, 10, 100, 25);
final Text nombreUsuario = new Text(shell, SWT.BORDER);
nombreUsuario.setBounds(90, 35, 100, 25);
Label etiqueta2 = new Label(shell, SWT.NULL);
etiqueta2.setText("Contraseña:");
etiqueta2.setBounds(90, 70, 100, 25);
final Text contrasenya = new Text(shell, SWT.BORDER);
contrasenya.setEchoChar('*');
contrasenya.setBounds(90, 95, 100, 25);
Button validarUsuario = new Button(shell, SWT.PUSH);
validarUsuario.setText("Validar usuario");
validarUsuario.setBounds(100, 150, 85, 28);
// Tratamiento de sucesos: muy similar a como se hace en Swing.
Listener listener = new Listener() {
public void handleEvent(Event suceso) {
System.out.println(
"El nombre del usuario es: " + nombreUsuario.getText());
System.out.println(
"La contraseña es: " + contrasenya.getText());
}
};
validarUsuario.addListener(SWT.Selection, listener);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 47 de 114
Figura 32. Resultado de ejecutar la clase EjemploCuadroTexto1
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.Image;
/**
* Esta clase genera un cuadro de texto multilínea.
* Se necesita incluir el paquete org.eclips.swt.graphics.Image
* para poder trabajar con imágenes en SWT.
*
*/
public class EjemploCuadroTexto2 {
public static void main(String args []) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 300);
shell.setImage(new Image(display, "c:Eclipse.jpg"));
shell.setText("Ejemplo 2 de cuadro de texto");
// Se crea el cuadro de texto multílinea.
Text texto = new Text(shell, SWT.MULTI);
// El primer argumento indica la coordenada x del extremo inferior izquierdo del cuadro de
// texto; el segundo, la coordenada y del extremo superior izquierdo; los otros dos indican la
// anchura y la altura del cuadro de texto, respectivamente.
Ejemplo 7: EjemploCuadroTexto2.java
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 48 de 114
texto.setBounds(10, 10, 200, 28);
texto.setText( "Línea 1" + System.getProperty("line.separator") + "Línea 2");
shell.open();
while (!shell.isDisposed()){
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
Figura 33. Resultado de ejecutar la clase EjemploCuadroTexto2
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
/**
* Esta clase genera tres cuadros de texto y muestra por consola el texto seleccionado
* cuando se pasa de uno a otro.
*
*/
Ejemplo 8: EjemploCuadroTexto3.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 49 de 114
public class EjemploCuadroTexto3 {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(200, 200);
shell.setText("Ejemplo 3 de cuadro de texto");
// primer cuadro de texto
Text texto1 = new Text(shell, SWT.SINGLE | SWT.BORDER);
texto1.setBounds(30, 10, 100, 20);
texto1.setTextLimit(11);
texto1.setText("javaHispano");
// segundo cuadro de texto
Text texto2 = new Text(shell, SWT.SINGLE | SWT.BORDER);
texto2.setBounds(30, 40, 100, 20);
texto2.setTextLimit(11);
// tercer cuadro de texto
Text texto3 = new Text(shell, SWT.SINGLE | SWT.BORDER);
texto3.setBounds(30, 70, 100, 20);
texto3.setTextLimit(11);
// gestión de eventos
FocusListener focusListener = new FocusListener() {
public void focusGained(FocusEvent e) {
// Si obtiene el foco se selecciona todo lo que hay en el cuadro de texto.
Text texto = (Text) e.widget;
texto.selectAll();
}
public void focusLost(FocusEvent e) {
// Si pierde el foco, muestra el texto seleccionado o un mensaje que
// indica que el cuadro de texto estaba vacío.
Text texto = (Text) e.widget;
if ( texto.getSelectionCount() > 0 ) {
if ( texto.getLocation().y == 10 ) {
System.out.println("Ha seleccionado en el cuadro de texto 1: " + texto.getText());
}
if ( texto.getLocation().y == 40 ) {
System.out.println("Ha seleccionado en el cuadro de texto 2: " + texto.getText());
}
if ( texto.getLocation().y == 70 ) {
System.out.println("Ha seleccionado en el cuadro de texto 3: " + texto.getText());
}
}
else {
System.out.println("No ha seleccionado nada: el cuadro de texto estaba vacío.");
}
}
}; // fin focusListener
texto1.addFocusListener(focusListener);
texto2.addFocusListener(focusListener);
texto3.addFocusListener(focusListener);
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 50 de 114
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
Figura 34. Resultado de ejecutar la clase EjemploCuadroTexto3
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.*;
/**
* Esta clase muestra cómo se usan las etiquetas con SWT.
*
* La clase Label admite los siguientes estilos: BORDER, CENTER,
* LEFT, RIGHT, WRAP y SEPARATOR (con HORIZONTAL, SHADOW_IN,
* SHADOW_OUT, SHADOW_NONE y VERTICAL).
*
* BORDER crea una etiqueta con borde.
* CENTER, LEFT y RIGHT alinean el texto de la etiqueta con respecto a la etiqueta.
* WRAP hace que el texto dentro de la etiqueta se pueda dividir en varias líneas, si
* resulta necesario.
* SHADOW_IN (usado con SEPARATOR) crea un separador que parece empotrado
* en la ventana.
Ejemplo 9: EjemploEtiqueta.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 51 de 114
* SHADOW_OUT (usado con SEPARATOR) crea un separador que parece sobresalir
* de la ventana.
* SHADOW_NONE (usado con SEPARATOR) crea un separador sin sombra.
* SEPARATOR y VERTICAL generan una línea vertical.
* SEPARATOR y HORIZONTAL generan una línea horizontal.
*
*/
public class EjemploEtiqueta {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell();
shell.setLayout(new GridLayout(1, false));
shell.setText("Ejemplo de etiquetas");
// Se crea una etiqueta.
Label etiqueta1 = new Label(shell, SWT.NONE);
etiqueta1.setText("Etiqueta plana");
// Se crea una etiqueta con borde
Label etiqueta2 = new Label(shell, SWT.BORDER);
etiqueta2.setText("Etiqueta con borde");
// Se crea un separador.
Label etiqueta3 = new Label(shell, SWT.SEPARATOR);
// Se crea un separador horizontal con sombra.
Label etiqueta4= new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL |
SWT.SHADOW_IN);
etiqueta4.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
// Se crea una etiqueta con el logotipo de Eclipse.
Image imagen = new Image(display, "c:Eclipse.jpg");
Label etiqueta5 = new Label(shell, SWT.NONE);
etiqueta5.setImage(imagen);
shell.open();
shell.pack();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 52 de 114
Figura 35. Resultado de ejecutar la clase EjemploEtiqueta
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
/**
* Esta clase genera una lista con tres elementos.
*
* La clase List admite los siguientes estilos: BORDER, H_SCROLL,
* V_SCROLL, SINGLE y MULTI.
*
* BORDER crea un borde alrededor de la lista.
Ejemplo 10: EjemploLista1.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 53 de 114
* H_SCROLL crea una barra de desplazamiento horizontal para la lista.
* V_SCROLL crea una barra de desplazamiento vertical para la lista.
* MULTI permite seleccionar a la vez varios elementos.
*/
public class EjemploLista1 {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 300);
shell.setText("Ejemplo 1 de lista");
List lista = new List(shell, SWT.SINGLE | SWT.BORDER);
lista.setBounds(40, 40, 80, 60);
lista.add("Bolígrafo");
lista.add("Lápiz");
lista.add("Calculadora");
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
Figura 36. Resultado de ejecutar la clase EjemploLista1
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 54 de 114
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
/**
* Esta clase genera una lista con tres elementos que admite selecciones
* múltiples.
*/
public class EjemploLista2 {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 250);
shell.setText("Ejemplo 2 de lista");
// El objeto Lista debe definirse como final si se quiere acceder a él desde
// el método addSelectionListener(). El argumento SWT.MULTI hace que
// se puedan seleccionar varios elementos a la par.
final List lista = new List(shell, SWT.MULTI | SWT.BORDER);
lista.setBounds(40, 40, 80, 60);
lista.add("Bolígrafo");
lista.add("Lápiz");
lista.add("Calculadora");
Button boton = new Button(shell, SWT.PUSH | SWT.BORDER);
boton.setBounds(50, 150, 180, 25);
boton.setText("Seleccione un elemento de la lista");
// Gestión de sucesos: si se presiona el botón se muestran los
// elementos seleccionados. El método getSelection() devuelve
// un vector de Strings.
boton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
String elementosSeleccionados[] = lista.getSelection();
for (int i = 0; i< elementosSeleccionados.length; i++) {
System.out.println(elementosSeleccionados[i]);
}
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
Ejemplo 11: EjemploLista2.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 55 de 114
Figura 37. Resultado de ejecutar la clase EjemploLista2
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
/**
* Esta clase genera una lista con un menú contextual (pop-up) que almacena dos
* menús, uno de ellos con dos submenús.
*
*/
public class EjemploLista3 {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 250);
shell.setText("Ejemplo 3 de lista");
List lista = new List(shell, SWT.SINGLE | SWT.BORDER);
lista.setBounds(80, 40, 100, 60);
lista.add("Elemento 1");
lista.add("Elemento 2");
lista.add("Elemento 3");
// Se crea el menú y se añade a la lista.
Menu menu = new Menu(shell, SWT.POP_UP);
lista.setMenu(menu);
Ejemplo 12: EjemploLista3.java
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 56 de 114
// Se crea el menú Archivo.
MenuItem archivo = new MenuItem(menu, SWT.CASCADE);
archivo.setText("Archivo");
Menu menuArchivo = new Menu(menu);
archivo.setMenu(menuArchivo);
// Se añaden al menú Archivo los submenús Nuevo y Cerrar
MenuItem nuevo = new MenuItem(menuArchivo, SWT.CHECK);
nuevo.setText("Nuevo");
MenuItem cerrar = new MenuItem(menuArchivo, SWT.CHECK);
cerrar.setText("Cerrar");
// Se crea el menú Salir
MenuItem salir = new MenuItem(menu, SWT.PUSH);
salir.setText("Salir");
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
Figura 38. Resultado de ejecutar la clase EjemploLista3
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 57 de 114
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
/**
* Esta clase muestra un cuadro combinado en que el usuario puede introducir
* elementos hasta alcanzar el valor límite LIMITE. Una vez alcanzado el límite,
* por cada elemento que se añada se borrará uno de los que ya había.
*
* La clase Combo admite los siguientes estilos: BORDER, DROP_DOWN,
* READ_ONLY y SIMPLE.
*
* BORDER crea un borde alrededor del cuadro combinado.
* DROP_DOWN crea un cuadro combinado desplegable.
* READ_ONLY crea un cuadro combinado que no permite que el usuario introduzca nuevos
* elementos o modifique los ya existentes.
* SIMPLE crea un cuadro de texto en el que los elementos del cuadro siempre se muestran
* como una lista.
*
*/
public class EjemploCuadroCombinado {
// Número máximo de elementos que admite el cuadro combinado.
final static int LIMITE = 5;
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(210, 100);
shell.setText("Ejemplo de cuadro combinado");
final Combo cuadroComb = new Combo(shell, SWT.BORDER | SWT.DROP_DOWN);
cuadroComb.setSize( 200, 50);
// Tratamiento de sucesos: mientras no se alcance LIMITE, el texto que
// introduce el usuario se van añadiendo a la lista de elementos
// susceptibles de ser seleccionados.
cuadroComb.addListener(SWT.DefaultSelection,new Listener() {
public void handleEvent(Event event) {
String texto = cuadroComb.getText();
if ( (cuadroComb.indexOf(texto) == -1) && (cuadroComb.getItemCount() >= LIMITE) ) {
cuadroComb.remove(0);
}
cuadroComb.add(texto);
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
Ejemplo 13: EjemploCuadroCombinado.java
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 58 de 114
}
}
}
}
Figura 39. Resultado de ejecutar la clase EjemploCuadroCombinado
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
/**
* Esta clase muestra una tabla con cuatro ciudades europeas y su distancia a Madrid.
*
* La clase Table admite los siguientes estilos: BORDER, H_SCROLL, V_SCROLL, SINGLE,
* MULTI, CHECK, FULL_SELECTION y HIDE_SELECTION.
*
* BORDER crea una tabla con borde.
* H_SCROLL crea una tabla con una barra de desplazamiento horizontal.
* V_SCROLL crea una tabla con una barra de desplazamiento vertical.
* SINGLE crea una tabla que no permite seleccionar a la vez más de una celda.
* MULTI crea una tabla que permite la selección de varias celdas a la vez.
* CHECK crea una tabla con cuadros de verificación.
* FULL_SELECTION crea una tabla en la que, al seleccionar una celda, se selecciona
* toda la fila.
* HIDE_SELECTION crea una tabla que esconde las celdas seleccionadas cuando la
* la tabla pierde el foco.
*
* La clase TableColum representa una columna de una tabla y admite tres estilos: LEFT,
* RIGHT y CENTER, que alinean la columna a la izquierda, a la derecha o al centro.
*
* La clase TableItem representa una fila de una tabla y no admite ningún estilo.
*/
public class EjemploTabla1 {
public static void main(String args[]) {
Ejemplo 14: EjemploTabla1.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 59 de 114
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 250);
shell.setText("Ejemplo 1 de tabla");
// Una objeto Table es un widget compuesto. Por ello, conviene usar
// algún tipo de esquema (layout). En este caso se usará
// el esquema de tipo FillLayout, que distribuye
// equitativamente entre los componentes el espacio disponible.
shell.setLayout(new FillLayout());
// Se crea la tabla, que permite la selección múltiple de filas.
Table tabla = new Table(shell, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
tabla.setHeaderVisible(true);
tabla.setLinesVisible(true);
// Se crean las columnas.
TableColumn columna1 = new TableColumn(tabla, SWT.CENTER);
TableColumn columna2 = new TableColumn(tabla, SWT.CENTER);
// Se establece el ancho y el nombre de cada columna.
columna1.setText("Ciudad");
columna1.setWidth(90);
columna2.setText("Distancia");
columna2.setWidth(90);
// Se crean y se rellenan las filas.
TableItem fila1 = new TableItem(tabla, SWT.NONE);
String tmp1[] = {"París", "1100 km"};
fila1.setText(tmp1);
TableItem fila2 = new TableItem(tabla, SWT.NONE);
String tmp2[] = {"Luxemburgo", "1600 km"};
fila2.setText(tmp2);
TableItem fila3 = new TableItem(tabla, SWT.NONE);
String tmp3[] = {"Berlín","1800 km"};
fila3.setText(tmp3);
// Otra manera de rellenar las filas.
TableItem fila4 = new TableItem(tabla, SWT.NONE);
fila4.setText(0, "Atenas");
fila4.setText(1, "3500 km");
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 60 de 114
Figura 40. Resultado de ejecutar la clase EjemploTabla1
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
/**
* Esta clase muestra por consola el contenido de todas las celdas de la fila seleccionada por
* el usuario.
*
*/
public class EjemploTabla2 {
public static void main (String [] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 300);
shell.setText("Ejemplo 2 de tabla");
// Tabla que admite la selección simultánea de varios elementos.
final Table tabla = new Table (shell, SWT.BORDER | SWT.V_SCROLL |
SWT.FULL_SELECTION);
tabla.setLinesVisible (true);
tabla.setHeaderVisible (true);
// Se da nombre a las columnas.
for (int i = 1; i < 3; i++) {
Ejemplo 15: EjemploTabla2.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 61 de 114
TableColumn columna = new TableColumn (tabla, SWT.NULL);
columna.setText ("Columna " + i);
}
// Se rellena la tabla.
for (int i = 1; i < 11; i++) {
TableItem celda = new TableItem (tabla, SWT.NONE);
celda.setText (0, "celda (" + i + " ,1)");
celda.setText (1, "celda (" + i + " ,2)");
}
// Se vuelve a calcular el ancho preferido de cada columna.
for (int i = 0; i < 2; i++) {
tabla.getColumn (i).pack();
}
tabla.setSize (800, 800);
// Gestión de sucesos: al seleccionar una celda se muestra por consola su texto.
tabla.addListener (SWT.Selection, new Listener () {
public void handleEvent (Event suceso) {
String celda = null;
TableItem celdas[] = tabla.getSelection();
if ( (celdas != null) && (celdas.length >= 1) ) {
System.out.println("Se han seleccionado las celdas: ");
System.out.println (celdas[0].getText(0));
System.out.println (celdas[0].getText(1));
}
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 62 de 114
Figura 41. Resultado de ejecutar la clase EjemploTabla2
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
/**
* Esta clase muestra un panel de lengüetas (TabFolder) con tres lengüetas. Una contiene
* un cuadro desplegable (Combo); otra, una etiqueta (Label), y la otra un cuadro de texto (Text).
*/
public class EjemploTabFolder {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 300);
shell.setText("Ejemplo de panel de lengüetas (TabFolder)");
shell.setLayout(new FillLayout());
// Se crea el panel de lengüetas
TabFolder panelLengueta = new TabFolder(shell, SWT.NONE);
Ejemplo 16: EjemploTabFolder.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 63 de 114
// Se crea una lengüeta y se llena con un cuadro desplegable.
TabItem lengueta1 = new TabItem(panelLengueta, SWT.NONE);
lengueta1.setText("Lengüeta 1");
Combo desplegable = new Combo(panelLengueta, SWT.BORDER);
desplegable.add("Soy un cuadro desplegable");
lengueta1.setControl(desplegable);
// Se crea una lengüeta y se llena con una etiqueta.
TabItem lengueta2 = new TabItem(panelLengueta, SWT.NONE);
lengueta2.setText("Lengüeta 2");
Label etiqueta = new Label(panelLengueta, SWT.BORDER);
etiqueta.setText("Soy una etiqueta");
lengueta2.setControl(etiqueta);
// Se crea una lengüeta y se llena con un cuadro de texto.
TabItem lengueta3 = new TabItem(panelLengueta, SWT.NONE);
lengueta3.setText("Lengüeta 3");
Text cuadroTexto = new Text(panelLengueta, SWT.NONE);
cuadroTexto.setText("Soy un cuadro de texto");
lengueta3.setControl(cuadroTexto);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
Figura 42. Resultado de ejecutar la clase EjemploTabFolder
El archipiélago Eclipse (parte 4 de 4)
© Miguel Ángel Abián, 2005-2014 Página 64 de 114
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
/**
* Esta clase genera un árbol del que cuelgan varios nodos.
*
* La clase Tree actúa como un contenedor de objetos TreeItem.
* La clase Tree admite los siguientes estilos: BORDER,
* H_SCROLL, V_SCROLL, SINGLE, MULTI y CHECK.
*
* BORDER crea un árbol con borde.
* H_SCROLL y V_SCROLL crean un árbol con una barra de desplazamiento horizontal o
* vertical, respectivamente
* SINGLE crea un árbol que sólo permite seleccionar un nodo o elemento a la vez.
* MULTI crea un árbol que permite que se seleccionen a la vez varios nodos o elementos.
* CHECK crea un árbol con cuadros de verificación.
*
* Los métodos más usados de Tree son:
* 1) getSelection() devuelve un vector con los TreeItems que están seleccionados.
* 2) getSelectionCount() devuelve el número de elementos seleccionados.
* 3) removeAll() borra todos los elementos del árbol.
* 4) selectAll() selecciona todos los elementos del árbol.
* 5) deselectAll() quita la selección de todos los elementos seleccionados del árbol.
* 6) setSelection(TreeItem[]) Seleccionan los elementos establecidos en el vector pasado como
* argumento.
*
* Los métodos más usados de TreeItem son:
* 1) getChecked() devuelve true si el elemento está marcado, y false en caso contrario.
* 2) getExpanded () devuelve true si el elemento está expandido, y false en caso contrario.
* 3) setChecked (boolean) establece si el elemento está marcado o no.
* 4) setExpanded(boolean) establece si el elemento está expandido o no.
* 5) setImage(Image) establece una imagen para el elemento. Si el argumento es null, no se
* mostrará ninguna imagen.
* 4) setText(String) establece el texto del elemento.
*/
public class EjemploArbol {
public static void main(String args[]) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 300);
shell.setText("Ejemplo de árbol");
shell.setLayout(new FillLayout());
// Se crea el árbol.
Tree arbol = new Tree(shell, SWT.BORDER);
// Se crea el nodo 1 y sus subnodos.
TreeItem nodo1 = new TreeItem(arbol, SWT.NONE);
Ejemplo 17: EjemploArbol.java
http://www.javahispano.org
© Miguel Ángel Abián, 2005-2014 Página 65 de 114
nodo1.setText("Nodo 1");
TreeItem nodo11 = new TreeItem(nodo1, SWT.NONE);
nodo11.setText("Nodo 1.1");
TreeItem nodo111 = new TreeItem(nodo11, SWT.NONE);
nodo111.setText("Nodo 1.1.1");
// Se crea el nodo 2 y sus subnodos.
TreeItem nodo2 = new TreeItem(arbol, SWT.NONE);
nodo2.setText("Nodo 2");
TreeItem nodo21 = new TreeItem(arbol, SWT.NONE);
nodo21.setText("Nodo 2.1");
TreeItem nodo22 = new TreeItem(arbol, SWT.NONE);
nodo22.setText("Nodo 2.2");
// Se expande el nodo 1 y su subnodo.
nodo1.setExpanded( true);
nodo11.setExpanded(true);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
Figura 43. Resultado de ejecutar la clase EjemploArbol
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT
Artículo 4 sobre la plataforma ECLIPSE. Especial SWT

Más contenido relacionado

La actualidad más candente

Introduction to Java
Introduction to Java Introduction to Java
Introduction to Java Hitesh-Java
 
Implementing jsp tag extensions
Implementing jsp tag extensionsImplementing jsp tag extensions
Implementing jsp tag extensionsSoujanya V
 
JDBC Java Database Connectivity
JDBC Java Database ConnectivityJDBC Java Database Connectivity
JDBC Java Database ConnectivityRanjan Kumar
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickHermann Hueck
 
Herramientas de eclipse
Herramientas de eclipseHerramientas de eclipse
Herramientas de eclipseJacqueline98
 
Programacion interpretada (scripting)
Programacion interpretada (scripting)Programacion interpretada (scripting)
Programacion interpretada (scripting)alexand3r1
 
JavaScript for ABAP Programmers - 1/7 Introduction
JavaScript for ABAP Programmers - 1/7 IntroductionJavaScript for ABAP Programmers - 1/7 Introduction
JavaScript for ABAP Programmers - 1/7 IntroductionChris Whealy
 
Lecture 3: Servlets - Session Management
Lecture 3:  Servlets - Session ManagementLecture 3:  Servlets - Session Management
Lecture 3: Servlets - Session ManagementFahad Golra
 
F5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_korea
F5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_koreaF5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_korea
F5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_koreaInfraEngineer
 
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...Edureka!
 

La actualidad más candente (18)

Introduction to Java
Introduction to Java Introduction to Java
Introduction to Java
 
Implementing jsp tag extensions
Implementing jsp tag extensionsImplementing jsp tag extensions
Implementing jsp tag extensions
 
JDBC Java Database Connectivity
JDBC Java Database ConnectivityJDBC Java Database Connectivity
JDBC Java Database Connectivity
 
Tracing in ASp.Net
Tracing in ASp.NetTracing in ASp.Net
Tracing in ASp.Net
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with Slick
 
Herramientas de eclipse
Herramientas de eclipseHerramientas de eclipse
Herramientas de eclipse
 
Programacion interpretada (scripting)
Programacion interpretada (scripting)Programacion interpretada (scripting)
Programacion interpretada (scripting)
 
Jdbc complete
Jdbc completeJdbc complete
Jdbc complete
 
Jdbc connectivity in java
Jdbc connectivity in javaJdbc connectivity in java
Jdbc connectivity in java
 
Testing Spring Applications
Testing Spring ApplicationsTesting Spring Applications
Testing Spring Applications
 
JavaScript for ABAP Programmers - 1/7 Introduction
JavaScript for ABAP Programmers - 1/7 IntroductionJavaScript for ABAP Programmers - 1/7 Introduction
JavaScript for ABAP Programmers - 1/7 Introduction
 
Lecture 3: Servlets - Session Management
Lecture 3:  Servlets - Session ManagementLecture 3:  Servlets - Session Management
Lecture 3: Servlets - Session Management
 
F5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_korea
F5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_koreaF5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_korea
F5 container ingress_service_in_kuernetes_with_calico_cni_by_duck_in_korea
 
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
Java Interview Questions and Answers | Spring and Hibernate Interview Questio...
 
Applet in java
Applet in javaApplet in java
Applet in java
 
Hibernate tutorial
Hibernate tutorialHibernate tutorial
Hibernate tutorial
 
Android MVVM
Android MVVMAndroid MVVM
Android MVVM
 
Jsp
JspJsp
Jsp
 

Destacado

Practica 12 toxicologia
Practica 12 toxicologiaPractica 12 toxicologia
Practica 12 toxicologiaXavier Pineda
 
Estadísticas Campeonato. Nal. Infantil Actualizadas al 4 agosto
Estadísticas Campeonato. Nal. Infantil Actualizadas al 4 agostoEstadísticas Campeonato. Nal. Infantil Actualizadas al 4 agosto
Estadísticas Campeonato. Nal. Infantil Actualizadas al 4 agostofmcharrera
 
Dental hygienist schools in florida
Dental hygienist schools in floridaDental hygienist schools in florida
Dental hygienist schools in floridadhsray
 
110405 human & technologies corp presentation
110405  human & technologies corp presentation110405  human & technologies corp presentation
110405 human & technologies corp presentation동우 이
 
Prot. 2146 16 pl altera e revoga dispositivos da lei municipal nº 5.450-201...
Prot. 2146 16   pl altera e revoga dispositivos da lei municipal nº 5.450-201...Prot. 2146 16   pl altera e revoga dispositivos da lei municipal nº 5.450-201...
Prot. 2146 16 pl altera e revoga dispositivos da lei municipal nº 5.450-201...Claudio Figueiredo
 
Sistema de atención ciudadana municipal con opcion de outsourcing
Sistema de atención ciudadana municipal con opcion de outsourcing Sistema de atención ciudadana municipal con opcion de outsourcing
Sistema de atención ciudadana municipal con opcion de outsourcing clocsc
 
Bni 6 minute presentation
Bni 6 minute presentationBni 6 minute presentation
Bni 6 minute presentationlorieva
 
Al Fazl International 10th June 2016 (Weekly UK)
Al Fazl International 10th June 2016 (Weekly UK)Al Fazl International 10th June 2016 (Weekly UK)
Al Fazl International 10th June 2016 (Weekly UK)muzaffertahir9
 
OUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIK
OUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIKOUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIK
OUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIKHommel Etamic (Jenoptik)
 
Estandares ingles parte ii
Estandares ingles parte iiEstandares ingles parte ii
Estandares ingles parte iiDamaris Garcia
 
Keranigonj text part 4
Keranigonj text part 4Keranigonj text part 4
Keranigonj text part 4Nasrul Hamid
 
Digital Marketing portfolio
Digital Marketing portfolioDigital Marketing portfolio
Digital Marketing portfolioAD Technosys
 
M2M Perspective of New World Health Paradigm at Bio-IT World Asia 2013
M2M Perspective of  New World Health Paradigm at Bio-IT World Asia 2013M2M Perspective of  New World Health Paradigm at Bio-IT World Asia 2013
M2M Perspective of New World Health Paradigm at Bio-IT World Asia 2013Narendra K Saini
 
Putusan permohonan PK PT.Langgeng
Putusan permohonan PK PT.LanggengPutusan permohonan PK PT.Langgeng
Putusan permohonan PK PT.Langgengsischayank
 
Email Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slides
Email Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slidesEmail Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slides
Email Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slidesPallab Kakoty
 
Cehv8 - Module 12: Hacking Webservers
Cehv8 - Module 12: Hacking WebserversCehv8 - Module 12: Hacking Webservers
Cehv8 - Module 12: Hacking WebserversVuz Dở Hơi
 
06 reglamento de seguridad e higiene(pemex)
06 reglamento de seguridad e higiene(pemex)06 reglamento de seguridad e higiene(pemex)
06 reglamento de seguridad e higiene(pemex)Ismael Domínguez
 
Diseño Estratégico - Strategic Design - Presentacion Corfo
Diseño Estratégico - Strategic Design - Presentacion CorfoDiseño Estratégico - Strategic Design - Presentacion Corfo
Diseño Estratégico - Strategic Design - Presentacion CorfoDiego Rodriguez Bastias
 

Destacado (20)

Practica 12 toxicologia
Practica 12 toxicologiaPractica 12 toxicologia
Practica 12 toxicologia
 
Estadísticas Campeonato. Nal. Infantil Actualizadas al 4 agosto
Estadísticas Campeonato. Nal. Infantil Actualizadas al 4 agostoEstadísticas Campeonato. Nal. Infantil Actualizadas al 4 agosto
Estadísticas Campeonato. Nal. Infantil Actualizadas al 4 agosto
 
Dental hygienist schools in florida
Dental hygienist schools in floridaDental hygienist schools in florida
Dental hygienist schools in florida
 
110405 human & technologies corp presentation
110405  human & technologies corp presentation110405  human & technologies corp presentation
110405 human & technologies corp presentation
 
Prot. 2146 16 pl altera e revoga dispositivos da lei municipal nº 5.450-201...
Prot. 2146 16   pl altera e revoga dispositivos da lei municipal nº 5.450-201...Prot. 2146 16   pl altera e revoga dispositivos da lei municipal nº 5.450-201...
Prot. 2146 16 pl altera e revoga dispositivos da lei municipal nº 5.450-201...
 
brochure_A4_2013
brochure_A4_2013brochure_A4_2013
brochure_A4_2013
 
Sistema de atención ciudadana municipal con opcion de outsourcing
Sistema de atención ciudadana municipal con opcion de outsourcing Sistema de atención ciudadana municipal con opcion de outsourcing
Sistema de atención ciudadana municipal con opcion de outsourcing
 
Bni 6 minute presentation
Bni 6 minute presentationBni 6 minute presentation
Bni 6 minute presentation
 
Al Fazl International 10th June 2016 (Weekly UK)
Al Fazl International 10th June 2016 (Weekly UK)Al Fazl International 10th June 2016 (Weekly UK)
Al Fazl International 10th June 2016 (Weekly UK)
 
OUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIK
OUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIKOUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIK
OUR OFFER TO INDUSTRIAL DEVELOPMENT, BY JENOPTIK
 
Estandares ingles parte ii
Estandares ingles parte iiEstandares ingles parte ii
Estandares ingles parte ii
 
Keranigonj text part 4
Keranigonj text part 4Keranigonj text part 4
Keranigonj text part 4
 
Digital Marketing portfolio
Digital Marketing portfolioDigital Marketing portfolio
Digital Marketing portfolio
 
Hoja de vida de jorge palacios
Hoja de vida de jorge palaciosHoja de vida de jorge palacios
Hoja de vida de jorge palacios
 
M2M Perspective of New World Health Paradigm at Bio-IT World Asia 2013
M2M Perspective of  New World Health Paradigm at Bio-IT World Asia 2013M2M Perspective of  New World Health Paradigm at Bio-IT World Asia 2013
M2M Perspective of New World Health Paradigm at Bio-IT World Asia 2013
 
Putusan permohonan PK PT.Langgeng
Putusan permohonan PK PT.LanggengPutusan permohonan PK PT.Langgeng
Putusan permohonan PK PT.Langgeng
 
Email Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slides
Email Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slidesEmail Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slides
Email Marketing Statistics, Quotes and Tips Shared by Brands via #pallab_slides
 
Cehv8 - Module 12: Hacking Webservers
Cehv8 - Module 12: Hacking WebserversCehv8 - Module 12: Hacking Webservers
Cehv8 - Module 12: Hacking Webservers
 
06 reglamento de seguridad e higiene(pemex)
06 reglamento de seguridad e higiene(pemex)06 reglamento de seguridad e higiene(pemex)
06 reglamento de seguridad e higiene(pemex)
 
Diseño Estratégico - Strategic Design - Presentacion Corfo
Diseño Estratégico - Strategic Design - Presentacion CorfoDiseño Estratégico - Strategic Design - Presentacion Corfo
Diseño Estratégico - Strategic Design - Presentacion Corfo
 

Similar a Artículo 4 sobre la plataforma ECLIPSE. Especial SWT

Similar a Artículo 4 sobre la plataforma ECLIPSE. Especial SWT (20)

Artículo 3 sobre la plataforma ECLIPSE
Artículo 3 sobre la plataforma ECLIPSEArtículo 3 sobre la plataforma ECLIPSE
Artículo 3 sobre la plataforma ECLIPSE
 
Java con eclipse
Java con eclipseJava con eclipse
Java con eclipse
 
Eclipse java en_espanol
Eclipse java en_espanolEclipse java en_espanol
Eclipse java en_espanol
 
Tutorial 3
Tutorial 3Tutorial 3
Tutorial 3
 
Mi primera-hora-con-eclipse Tutorial
Mi primera-hora-con-eclipse TutorialMi primera-hora-con-eclipse Tutorial
Mi primera-hora-con-eclipse Tutorial
 
Tutorial 3
Tutorial 3Tutorial 3
Tutorial 3
 
Mi primera-hora-con-eclipse
Mi primera-hora-con-eclipseMi primera-hora-con-eclipse
Mi primera-hora-con-eclipse
 
Mi primera-hora-con-eclipse
Mi primera-hora-con-eclipseMi primera-hora-con-eclipse
Mi primera-hora-con-eclipse
 
Mi primera-hora-con-eclipse
Mi primera-hora-con-eclipseMi primera-hora-con-eclipse
Mi primera-hora-con-eclipse
 
Mi primera-hora-con-eclipse
Mi primera-hora-con-eclipseMi primera-hora-con-eclipse
Mi primera-hora-con-eclipse
 
Tutorial de eclipse
Tutorial de eclipseTutorial de eclipse
Tutorial de eclipse
 
Mi primera-hora-con-eclipse
Mi primera-hora-con-eclipseMi primera-hora-con-eclipse
Mi primera-hora-con-eclipse
 
Tutorial 2
Tutorial 2Tutorial 2
Tutorial 2
 
Mi primera-hora-con-eclipse
Mi primera-hora-con-eclipseMi primera-hora-con-eclipse
Mi primera-hora-con-eclipse
 
Tutorial 2
Tutorial 2Tutorial 2
Tutorial 2
 
Tutorial Eclipse #1
Tutorial Eclipse #1Tutorial Eclipse #1
Tutorial Eclipse #1
 
Tutorial de Eclipse 3
Tutorial de Eclipse 3Tutorial de Eclipse 3
Tutorial de Eclipse 3
 
eclipse
eclipseeclipse
eclipse
 
Mi primera hora con eclipse
Mi primera hora con eclipseMi primera hora con eclipse
Mi primera hora con eclipse
 
Tutorial 3
Tutorial 3Tutorial 3
Tutorial 3
 

Más de torrubia

Presentación proyecto NODOS_TURISMO
Presentación proyecto NODOS_TURISMOPresentación proyecto NODOS_TURISMO
Presentación proyecto NODOS_TURISMOtorrubia
 
Presentación Proyecto SHCity
Presentación Proyecto SHCityPresentación Proyecto SHCity
Presentación Proyecto SHCitytorrubia
 
Artículo resultados Anualidad 1 proyecto NODOS-TURISMO
Artículo resultados Anualidad 1 proyecto NODOS-TURISMOArtículo resultados Anualidad 1 proyecto NODOS-TURISMO
Artículo resultados Anualidad 1 proyecto NODOS-TURISMOtorrubia
 
Newsletter 2 proyecto NODOS-TURISMO
Newsletter 2 proyecto NODOS-TURISMONewsletter 2 proyecto NODOS-TURISMO
Newsletter 2 proyecto NODOS-TURISMOtorrubia
 
Artículo proyecto NODOS-TURISMO
Artículo  proyecto NODOS-TURISMOArtículo  proyecto NODOS-TURISMO
Artículo proyecto NODOS-TURISMOtorrubia
 
Newsletter 1 proyecto Nodos-Turismo
Newsletter 1 proyecto Nodos-TurismoNewsletter 1 proyecto Nodos-Turismo
Newsletter 1 proyecto Nodos-Turismotorrubia
 
Wokshop proyecto nodos turismo
Wokshop proyecto nodos turismoWokshop proyecto nodos turismo
Wokshop proyecto nodos turismotorrubia
 
Circular 2 proyecto PROINNOMADERA
Circular 2 proyecto PROINNOMADERACircular 2 proyecto PROINNOMADERA
Circular 2 proyecto PROINNOMADERAtorrubia
 
Circular 1 proyecto PROINNOMADERA
Circular 1 proyecto PROINNOMADERACircular 1 proyecto PROINNOMADERA
Circular 1 proyecto PROINNOMADERAtorrubia
 
Jornada AIDIMA peritaje madera para arquitectos
Jornada AIDIMA peritaje madera para arquitectosJornada AIDIMA peritaje madera para arquitectos
Jornada AIDIMA peritaje madera para arquitectostorrubia
 
Conductividad termica en la edificación
Conductividad termica en la edificaciónConductividad termica en la edificación
Conductividad termica en la edificacióntorrubia
 
Resumen primer año proyecto PROINNOMADERA
Resumen primer año proyecto PROINNOMADERAResumen primer año proyecto PROINNOMADERA
Resumen primer año proyecto PROINNOMADERAtorrubia
 
Articulo 2015 fin proyecto europeo CELLUWOOD
Articulo 2015  fin proyecto europeo CELLUWOODArticulo 2015  fin proyecto europeo CELLUWOOD
Articulo 2015 fin proyecto europeo CELLUWOODtorrubia
 
Artículo 2015 proyecto IDANMAD
Artículo 2015 proyecto IDANMADArtículo 2015 proyecto IDANMAD
Artículo 2015 proyecto IDANMADtorrubia
 
Noticia jornadas técnicas MADERALIA SELECCIÓN 2015
Noticia jornadas técnicas MADERALIA SELECCIÓN 2015Noticia jornadas técnicas MADERALIA SELECCIÓN 2015
Noticia jornadas técnicas MADERALIA SELECCIÓN 2015torrubia
 
Ficha difusion 2014 proyecto IDANMAD
Ficha difusion 2014 proyecto IDANMADFicha difusion 2014 proyecto IDANMAD
Ficha difusion 2014 proyecto IDANMADtorrubia
 
Ficha tecnica final del proyecto IDANMAD
Ficha tecnica final del proyecto IDANMADFicha tecnica final del proyecto IDANMAD
Ficha tecnica final del proyecto IDANMADtorrubia
 
Noticia sobre proyecto CELLUWOOD
Noticia sobre proyecto CELLUWOODNoticia sobre proyecto CELLUWOOD
Noticia sobre proyecto CELLUWOODtorrubia
 
CELLUWOOD Project Presentation Outcomes
CELLUWOOD Project Presentation OutcomesCELLUWOOD Project Presentation Outcomes
CELLUWOOD Project Presentation Outcomestorrubia
 
CelluNews September 2014
CelluNews September 2014CelluNews September 2014
CelluNews September 2014torrubia
 

Más de torrubia (20)

Presentación proyecto NODOS_TURISMO
Presentación proyecto NODOS_TURISMOPresentación proyecto NODOS_TURISMO
Presentación proyecto NODOS_TURISMO
 
Presentación Proyecto SHCity
Presentación Proyecto SHCityPresentación Proyecto SHCity
Presentación Proyecto SHCity
 
Artículo resultados Anualidad 1 proyecto NODOS-TURISMO
Artículo resultados Anualidad 1 proyecto NODOS-TURISMOArtículo resultados Anualidad 1 proyecto NODOS-TURISMO
Artículo resultados Anualidad 1 proyecto NODOS-TURISMO
 
Newsletter 2 proyecto NODOS-TURISMO
Newsletter 2 proyecto NODOS-TURISMONewsletter 2 proyecto NODOS-TURISMO
Newsletter 2 proyecto NODOS-TURISMO
 
Artículo proyecto NODOS-TURISMO
Artículo  proyecto NODOS-TURISMOArtículo  proyecto NODOS-TURISMO
Artículo proyecto NODOS-TURISMO
 
Newsletter 1 proyecto Nodos-Turismo
Newsletter 1 proyecto Nodos-TurismoNewsletter 1 proyecto Nodos-Turismo
Newsletter 1 proyecto Nodos-Turismo
 
Wokshop proyecto nodos turismo
Wokshop proyecto nodos turismoWokshop proyecto nodos turismo
Wokshop proyecto nodos turismo
 
Circular 2 proyecto PROINNOMADERA
Circular 2 proyecto PROINNOMADERACircular 2 proyecto PROINNOMADERA
Circular 2 proyecto PROINNOMADERA
 
Circular 1 proyecto PROINNOMADERA
Circular 1 proyecto PROINNOMADERACircular 1 proyecto PROINNOMADERA
Circular 1 proyecto PROINNOMADERA
 
Jornada AIDIMA peritaje madera para arquitectos
Jornada AIDIMA peritaje madera para arquitectosJornada AIDIMA peritaje madera para arquitectos
Jornada AIDIMA peritaje madera para arquitectos
 
Conductividad termica en la edificación
Conductividad termica en la edificaciónConductividad termica en la edificación
Conductividad termica en la edificación
 
Resumen primer año proyecto PROINNOMADERA
Resumen primer año proyecto PROINNOMADERAResumen primer año proyecto PROINNOMADERA
Resumen primer año proyecto PROINNOMADERA
 
Articulo 2015 fin proyecto europeo CELLUWOOD
Articulo 2015  fin proyecto europeo CELLUWOODArticulo 2015  fin proyecto europeo CELLUWOOD
Articulo 2015 fin proyecto europeo CELLUWOOD
 
Artículo 2015 proyecto IDANMAD
Artículo 2015 proyecto IDANMADArtículo 2015 proyecto IDANMAD
Artículo 2015 proyecto IDANMAD
 
Noticia jornadas técnicas MADERALIA SELECCIÓN 2015
Noticia jornadas técnicas MADERALIA SELECCIÓN 2015Noticia jornadas técnicas MADERALIA SELECCIÓN 2015
Noticia jornadas técnicas MADERALIA SELECCIÓN 2015
 
Ficha difusion 2014 proyecto IDANMAD
Ficha difusion 2014 proyecto IDANMADFicha difusion 2014 proyecto IDANMAD
Ficha difusion 2014 proyecto IDANMAD
 
Ficha tecnica final del proyecto IDANMAD
Ficha tecnica final del proyecto IDANMADFicha tecnica final del proyecto IDANMAD
Ficha tecnica final del proyecto IDANMAD
 
Noticia sobre proyecto CELLUWOOD
Noticia sobre proyecto CELLUWOODNoticia sobre proyecto CELLUWOOD
Noticia sobre proyecto CELLUWOOD
 
CELLUWOOD Project Presentation Outcomes
CELLUWOOD Project Presentation OutcomesCELLUWOOD Project Presentation Outcomes
CELLUWOOD Project Presentation Outcomes
 
CelluNews September 2014
CelluNews September 2014CelluNews September 2014
CelluNews September 2014
 

Artículo 4 sobre la plataforma ECLIPSE. Especial SWT

  • 1. EL ARCHIPIÉLAGO ECLIPSE (PARTE 4 DE 4) Miguel Ángel Abián
  • 2. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 2 de 114 EL ARCHIPIÉLAGO ECLIPSE (PARTE 4 DE 4) Fecha de última revisión: 27.05.2014 Miguel Ángel Abián mabian ARROBA aidima PUNTO es Conozco máquinas que son más complicadas que la gente. Thomas Pynchon. V La gente suele preguntarme: “¿Por qué escribe usted en una lengua moribunda?”. Esa pregunta tiene varias respuestas. En primer lugar, me gusta escribir historias de fantasmas, y para eso nada hay más adecuado que una lengua moribunda. Cuanto más muerta está una lengua, más vivo está el fantasma. A los fantasmas les encanta el yiddish y, por lo que sé, todos lo hablan. [...] Por otra parte, el yiddish puede ser una lengua moribunda, pero es la única que conozco bien. Fue la lengua de mi madre, y ustedes saben que una madre nunca está realmente muerta. I. B. Singer. Discurso en la ceremonia de entrega de los premios Nobel (1978) En 1968, los Beatles publicaron el doble disco conocido como el Álbum blanco. Visto en retrospectiva, el disco es el testimonio de un grupo que había dejado atrás los alaridos de las fans y que estaba buscando su propia voz, lejos de la psicodelia y del rock and roll. En el Álbum blanco hay de todo, como en un bazar: confusión (musical y personal), pop, rock'n'roll, distorsión, experimentación (musical y comercial), baladas, aullidos. No obstante, el disco no es un caos informe ni un revoltijo desconcertante: hay un cierto orden interno, una cierta coherencia. Los Beatles no siguieron el camino esbozado en este disco, bien porque los conflictos personales estaban destrozando a la banda, bien porque ese camino no coincidía con lo que la gente esperaba de un grupo tan conocido. Eclipse me recuerda mucho al Álbum blanco. La plataforma Eclipse tiene de todo: uno puede trabajar, entre otros lenguajes, con Java, C/C++, PHP, JavaScript, Ruby, Pascal, Eiffel, Python y COBOL (el no muerto, el Nosferatu de los lenguajes informáticos: espero que nunca le muerda). Nadie le impide agregar a Eclipse los plug-ins (extensiones) que desee y trabajar con un magnífico IDE, hecho a medida, para ASP, JSP, XML, XHTML o HTML. Es más, puede escribir sus propios plug-ins y comercializarlos. Tantas posibilidades no se traducen –por ahora– en un Helter Skelter: la comunidad de desarrolladores y los publicistas que rodean a Eclipse están trabajando mucho y muy bien, con metas muy claras. Con todo, hay mucho de experimentación, en este caso informática y comercial, dentro y alrededor de Eclipse. Copyright (c) 2005-2014, Miguel Ángel Abián. Este documento puede ser distribuido sólo bajo los términos y condiciones de la licencia de Documentación de javaHispano v1.0 o posterior (la última versión se encuentra en http://www.javahispano.org/licencias/).
  • 3. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 3 de 114 Al proyecto Eclipse le deseo mejor suerte que los Beatles tras el Álbum blanco. Si el proyecto se fragmenta en exceso, es posible que a los subproyectos les ocurra lo mismo que a los componentes del grupo. A saber: que ninguno alcanzó la popularidad y el reconocimiento que habían tenido cuando estaban unidos. John Lennon dijo en 1980, poco antes de morir: “Si los años sesenta o los Beatles tenían un mensaje, era aprender a nadar. Y una vez has aprendido, nadar”. Esta serie de artículos es una rápida lección de natación, impartida con un solo fin: invitarle, si no lo ha hecho ya, a zambullirse en las aguas del océano Eclipse. No tiene nada que perder, y sí mucho que ganar. 14. SWT: el nuevo componente gráfico para Java El Standard Widget Toolkit (SWT) de la plataforma Eclipse ha sido el aspecto de Eclipse que mayores rechazos, adhesiones y controversias ha provocado en la comunidad de Java. SWT constituye la respuesta de IBM, para el desarrollo de interfaces de usuario, a AWT y a Swing (la historia de SWT y su papel en la estrategia comercial de IBM se explicarán en la próxima parte de esta serie de artículos). De acuerdo con la documentación oficial de Eclipse, he aquí la descripción del componente Standard Widget Toolkit: "El componente SWT está diseñado para proporcionar acceso eficaz y transportable a los servicios de interfaz de usuario del sistema operativo sobre el cual se implementa". Dicho de otra manera, también oficial: “[SWT es] un conjunto de componentes gráficos y una biblioteca gráfica integrada con el sistema de ventanas específico del sistema operativo, pero con una API independiente del SO”. Tanto SWT como JFace son bibliotecas estructuradas en paquetes con clases e interfaces escritas en Java. Widget es una palabra inglesa que se usa para referirse a un dispositivo cuyo nombre no se conoce o no importa, o bien a un dispositivo que todavía no existe; una traducción aproximada sería cachivache o trasto. Chocaría mucho leer frases como “el cachivache pequeño se coloca en el centro del cachivache grande” en un artículo técnico en español; en cambio, en inglés suenan incluso simpáticas. La documentación oficial de Eclipse (http://www.eclipse.org) establece que un widget es “cualquier objeto de la interfaz gráfica de usuario que puede colocarse dentro de otro widget” (versión tecnológica de la frase “Una gallina es lo que usan los huevos para producir otros huevos”; el ser humano no ha avanzado mucho explicando las cosas, la verdad). Veamos una definición más aclaratoria: “Un widget es un objeto de la interfaz gráfica de usuario, responsable de la interacción con el usuario”. En la documentación y la bibliografía de Eclipse suelen usarse de manera intercambiable las palabras widget y control (elemento de la interfaz gráfica con contrapartida en el sistema operativo; es decir, gestionado por el SO). Esta equivalencia no resulta del todo cierta, pues no todos los widgets son controles. Consideremos, vaya por caso, un control de tipo árbol: sus nodos y subnodos son widgets (elementos de la interfaz gráfica), pero no controles. La razón para esto es práctica: si el árbol se encarga de dibujar los nodos y subnodos, se evita consumir los recursos del sistema operativo que se necesitarían si cada nodo fuera un control.
  • 4. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 4 de 114 Suele decirse que Swing es un componente de “peso ligero” (lightweight), mientras que SWT y AWT son de “peso pesado” (heavyweight). Aunque el significado de estos términos dista mucho de ser unánime, suele aceptarse que “peso ligero” significa que hay una asociación o correspondencia uno a uno (one-to-one mapping) entre los widgets del componente y los de la plataforma (controles). Un componente de peso pesado simplifica mucho la programación gráfica: todas las implementaciones de los sucesos (apretar una tecla, mover el ratón...) vienen dadas por el sistema operativo, mejor dicho, por el gestor de ventanas del sistema operativo. Su principal desventaja radica que consume muchos recursos del sistema operativo (memoria, punteros, ciclos de CPU). Consideremos, por ejemplo, que creamos un objeto java.awt.Button: al instante, el gestor de ventanas del SO creará su propio botón. Cuando el usuario pulse el botón, el suceso irá, primero, de la biblioteca gráfica del SO a la máquina virtual de Java; luego, irá a la lógica vinculada al objeto Button de AWT, que decidirá qué hay que hacer. En un componente de peso ligero, los widgets son dibujados por el componente, no por el SO. En consecuencia, cada componente debe encargarse de implementar sucesos como mover el ratón o apretar una tecla. A cambio, su rendimiento resulta, en general, mejor que el de uno pesado (en cuanto a recursos, es más facil y menos exigente dibujar widgets que dejar que los cree el SO). Consideremos, por ejemplo, una ventana con cien o doscientas etiquetas. En Swing, la ventana (un JFrame) se comportará mejor que en AWT o SWT, pues las etiquetas se dibujan sobre la ventana y, por tanto, no hay que preocuparse por liberar sus recursos (son dibujos). En estos dos últimos componentes gráficos, las etiquetas serán creadas por el SO, que tendrá que conservarlas en memoria en todo momento y supervisar sus sucesos y el estado de cada una. Otro ejemplo: consideremos una tabla con un millón de filas. En Swing, el tiempo que se tardaría en dibujarla apenas diferiría del empleado para dibujar una tabla de diez mil filas. En SWT, el tiempo que se tardaría en dibujar la tabla de un millón de filas sería de dos órdenes de magnitud superior al necesario para dibujar la tabla de diez mil filas, pues cada fila sería un widget mantenido por el sistema operativo (omito aquí cualquier consideración sobre la memoria que emplearía SWT). Por regla general, la escalabilidad de los componentes de peso ligero es mucho mayor que la de los componentes de peso pesado. ¿Es SWT un AWT con muchos más widgets? Por fuera, sí; pero por dentro hay muchas diferencias. Tanto AWT como SWT usan las funciones de la API gráfica de las plataformas donde se ejecutan, pero lo hacen de modo muy distinto. AWT usa un sistema de componentes “gemelos” donde cada widget AWT tiene asociado un componente en el gestor de ventanas del sistema operativo. Cada widget AWT tiene asociado código nativo, escrito en C o C++, que hace llamadas a las bibliotecas gráficas de la plataforma (en Windows, Win32 o MFC) y que controla el comportamiento de los widgets. En cada JDK (Java Distribution Kit) se incluyen las clases Java de AWT y el código máquina resultante de compilar el código nativo (el código máquina cambia para cada plataforma, pero no las clases Java). De resultas del diseño basado en componentes gemelos, AWT tiene un número muy limitado de widgets, pues sólo puede incluir componentes presentes en todas las plataformas donde se implementa. Por ejemplo, no hay un widget árbol en AWT, pues no existe en Motif y, por tanto, en esa plataforma no hay un componente gemelo árbol al que AWT puede llamar. En SWT, los widgets están escritos en Java, no en código nativo; y los métodos gráficos de la plataforma se llaman a través de una “capa” que se explicará más adelante. Para cada plataforma, esta capa expone los métodos de la API (interfaz de
  • 5. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 5 de 114 programación de aplicaciones) gráfica de la plataforma, de suerte que las clases SWT (escritas en Java) pueden llamar a los métodos de la API gráfica. A diferencia de AWT, las clases SWT cambian en cada plataforma. Aparte de que hay muchos más widgets en SWT que en AWT, la gran ventaja de SWT es que toda la lógica de los widgets está escrita en Java (en AWT, la lógica está mezclada con código nativo de cada plataforma). El propósito original de Swing consistía en proporcionar a las aplicaciones escritas en Java el mismo aspecto en todas las plataformas (con la posibilidad de imitar hasta cierto punto la apariencia propia de cada una). Aunque con Swing pueden producirse, con esfuerzo, aplicaciones en Java de aspecto similar a las aplicaciones propias de cada plataforma, su rendimiento siempre ha sido pobre. Resulta destacable que Swing haya mejorado bastante su eficacia en las versiones 1.4 y 5.0 (antes 1.5) de Java. Posiblemente, la competencia que supone SWT ha influido beneficiosamente en el desarrollo de Swing. El motivo del pobre rendimiento con Swing radica en que el código que interacciona directamente con los widgets de cada plataforma está escrito en C o C++ (forma parte de las bibliotecas de AWT específicas de cada plataforma) y, en consecuencia, no está disponible para Swing, que está escrito en Java. También la API que proporcionan los widgets propios de cada plataforma está escrita en C o C++, y Swing no puede acceder directamente a ella. Por estos motivos, Swing trabaja con la API gráfica que le proporciona AWT. En Swing, cada componente es dibujado como una imagen en el componente que lo contiene, lo que requiere que Swing trabaje bit a bit con las interfaces gráficas. Cuando se quiere mostrar una interfaz al usuario, Swing –a través de AWT– envía el mapa de bits correspondiente al gestor de ventanas propio de la plataforma donde se trabaja. Como puede suponerse, este planteamiento dista mucho de ser eficaz; sus limitaciones se aprecian enseguida en dispositivos como agendas electrónicas o teléfonos móviles, donde la memoria está muy limitada. En cuanto a la personalización de los controles Swing para cada plataforma (lo que se conoce como pluggable look and feel o pluggable L&F), los resultados han sido sumamente mediocres: en parte porque la apariencia no coincide exactamente con la que deberían tener, y en parte porque Sun no ha ido actualizando Swing a las nuevas versiones de los sistemas operativos. Así, por ejemplo, la apariencia y sensación de Windows (Windows L&F) que proporciona Swing ha sido (hasta la versión 1.4.2 de Java) la misma para Windows 95, Windows 98, Windows 2000, Windows Millenium (curioso experimento fallido, por cierto) y Windows XP. Como usted mismo habrá comprobado, la apariencia de las aplicaciones para Windows cambiaba, y mucho, de una versión del sistema operativo a otro. En comparación con Swing, el SWT de Eclipse ha roto las reglas del juego mediante el empleo de una estrategia sumamente ingeniosa y eficaz: usa la API JNI de Java (Java Native Interface, interfaz nativa de Java). JNI permite que los programas en Java llamen a métodos nativos (métodos propios de la plataforma, generalmente escritos en C o C++) y que reciban los resultados que éstos devuelven. Cuando se compila un archivo con código JNI-C (código en C donde se implementan métodos declarados de tipo JNI), se genera una biblioteca compartida –en Windows, una DLL o biblioteca de enlace dinámico–, que permite acceder desde Java a los métodos declarados en el código JNI-C. Para ilustrar cómo se trabaja con JNI, considero aquí el ejemplo de un método nativo que calcula números de Fibonacci. En primer lugar, el método nativo se declara en una clase llamada MetodoNativo:
  • 6. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 6 de 114 public class MetodoNativo { // declaración Java del método public native int fibonacci(int n); // Carga la biblioteca compartida llamada native. static { System.loadLibrary("native"); } } En segundo lugar, se compila la clase anterior y, luego, se usa la herramienta javah de la siguiente forma (omito cualquier consideración sobre el CLASSPATH): javah –jni MetodoNativo Con el paso anterior, se crea un archivo MetodoNativo.h que contiene lo siguiente: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class MetodoNativo */ #ifndef _Included_MetodoNativo #define _Included_MetodoNativo #ifdef __cplusplus extern "C" { #endif /* * Class: MetodoNativo * Method: fibonacci * Signature: (I)I */ JNIEXPORT jint JNICALL Java_MetodoNativo_fibonacci (JNIEnv *, jobject, jint); #ifdef __cplusplus } #endif #endif En tercer lugar, la implementación en C del método fibonacci() se guarda en un archivo ImplementacionMetodoNativo.c: #include <jni.h> #include “MetodoNativo.h” /* implementación en C de la función que calcula el énesimo número de Fibonacci */ int fibonacci(int n) { if (n <= 1) return 0; if (n == 2) return 1; Declaración del método Java_MetodoNativo_fibonacci(). El argumento JNIEnv* es un puntero al entorno de ejecución del método. El argumento jobject es una referencia a la clase donde se especifica el método (MetodoNativo.java) El argumento jint es el tipo C que corresponde al tipo Java int, declarado como tipo de retorno del método fibonacci ().
  • 7. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 7 de 114 return (fibonacci(n-1) + fibonacci(n –2)); } /* implementación de Java_MetodoNativo_fibonacci() */ JNIEXPORT jint JNICALL Java_MetodoNativo_fibonacci(JNIEnv *env, jobject obj, jint n) { return fibonacci(n); } Por último, el archivo anterior se compila con un compilador de C o C++, que creará un biblioteca compartida (en Windows, una DLL). A partir de ese momento, el método nativo fibonacci() podrá usarse desde cualquier clase de Java. La técnica que usa SWT para llamar –mediante JNI– a las API gráficas de cada sistema operativo (escritas en C o C++) consiste en establecer, siempre que sea posible, una correspondencia uno a uno entre los métodos en Java de cada widget SWT y las llamadas a la API gráfica propia de cada sistema operativo. Más adelante, veremos código JNI-C de SWT donde se aprecia esa correspondencia. Las razones por las que SWT se basa en dicha estrategia son tres: eficacia, velocidad e integración con la plataforma. Un widget SWT llama directamente al gestor de ventanas específico de la plataforma; así se evita que Java tenga que enviar, mediante AWT, las interfaces en forma de mapas de bits. Con SWT se logra una integración casi completa con cada plataforma. No olvidemos que la integración con la plataforma no se reduce a la apariencia y la sensación (L&F): características como “copiar y pegar” o “arrastrar y soltar” son automáticas si se usa el SWT, pero no lo son tanto si se usa Swing. Pese al tiempo transcurrido desde que salieron las primeras versiones de Swing, aún existen acciones que no producen en algunas plataformas los resultados que esperaría el usuario habituado a éstas. Tal es la integración de SWT con la plataforma, que refleja inmediatamente los cambios en la apariencia y sensación de la interfaz de usuario del sistema operativo subyacente. Así, un cambio de piel (skin) de Windows XP provocará que todas las interfaces de usuario construidas con componentes SWT cambien al instante su apariencia y sensación. Una piel o skin es un conjunto de gráficos que incluye todas las piezas necesarias para cambiar el aspecto y sensación de la interfaz gráfica. Los gráficos pueden incluir imágenes para botones, barras, menús, etc. Skinning es la acción de aplicar una piel. Cuando se aplica una piel a Windows, se altera su apariencia y sensación: cambian los colores, los botones, etc. (El término skinning se usó originalmente en el videojuego Quake, juego tremendamente popular por su carácter intimista y conciliador. Para dar variedad al juego se inventaron varias pieles, que permitían al usuario cargar una serie de gráficos que cambiaban la apariencia del personaje principal, un pacífico y sosegado ciudadano.) La integración no se detiene en la apariencia y la sensación de Windows: SWT permite trabajar con los objetos binarios reutilizables de Microsoft (COM). De hecho, con unas pocas llamadas a métodos SWT se puede incluir controles ActiveX y documentos OLE en las aplicaciones SWT. La declaración del método Java_MetodoNativo_fibonacci() en ImplementacionMetodoNativo.c debe coincidir con la del método Java_MetodoNativo_fibonacci() en MetodoNativo.h.
  • 8. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 8 de 114 Figura 14. Bloc de notas visto con distintas pieles de Windows. Se ha utilizado la herramienta WindowBlinds 3, disponible en http://www.windowblinds.net SWT usa los widgets propios de cada plataforma siempre que sea posible, excepto cuando uno no existe en todas las plataformas. En ese caso, SWT lo simula en las plataformas que no cuentan con él. Dicho de otro modo: SWT usa para los widgets un enfoque basado en el mínimo común denominador. Por ejemplo, Motif no proporciona por omisión un widget de tipo árbol; consecuentemente, el SWT proporciona un widget árbol con la misma API que la implementación propia de Windows. Ningún usuario de Motif notará nada raro cuando use un componente SWT árbol: como éste no existe de serie en Motif, el usuario carecerá de ideas preconcebidas sobre cómo debe comportarse (salvo que provenga de Windows). A diferencia de AWT, SWT proporciona todos los controles que se suponen imprescindibles en cualquier interfaz gráfica moderna.
  • 9. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 9 de 114 Una vez obtenida la correspondencia uno a uno entre los métodos en Java y los métodos propios de la API gráfica de la plataforma, el desarrollador puede olvidarse del código JNI-C y de los detalles de bajo nivel del sistema operativo, y limitarse a utilizar los métodos públicos que proporcionan las clases de SWT. Por así decirlo, simplificando un poco, SWT encapsula de modo transparente el sistema operativo mediante JNI, permitiendo así utilizar todas las características de los widgets propios de cada plataforma. Actúa, en definitiva, como una fina capa entre Java y las bibliotecas de la API gráfica específica de cada SO. En comparación con Swing, SWT evita el uso de una capa gruesa y ralentizadora como la de AWT (veáse la figura 15). Casi todo el código de las clases SWT se limita a actuar como “puente” entre el mundo de Java y el de la plataforma: excepto en el caso de los componentes SWT sin equivalente en la plataforma, apenas se almacena información sobre los componentes SWT. No hace falta mucho más: SWT usa, siempre que puede, la información que proporciona la API gráfica de cada plataforma. Cualquier aplicación escrita en Java podría utilizar la JNI para establecer una correspondencia entre los métodos Java y las capacidades gráficas propias de la plataforma usada. La ventaja de usar SWT radica en que evita a los programadores escribir su propio código JNI (tarea que, desde luego, dista mucho de ser trivial y requiere un buen conocimiento de la plataforma con la cual se trabaja). Fig. 15. Funcionamiento interno de los dos componentes gráficos Componente gráfico Swing AWT Biblioteca gráfica del SO Swing Componente gráfico SWT Biblioteca gráfica del SO JNI-C SWT Miguel Ángel Abián, marzo de 2005 escrito en C escrita en Cescrito en Java escrito en Java escrita en C r FUNCIONAMIENTO INTERNO DE SWING y SWT
  • 10. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 10 de 114 Para ver cómo funcionan en SWT los métodos JNI, podemos considerar el método setMenu() de la clase Java OS (clase que representa el sistema operativo de la plataforma y que forma parte de SWT): public static final native boolean SetMenu (int hWnd, int hMenu); Este método asigna un menú a una ventana. La palabra reservada native indica al compilador de Java dos cosas: a) la implementación del código no está escrita en Java; b) la implementación se guarda en un archivo (os.c) que no es el de la clase OS (os.java). En Windows, el código JNI-C correspondiente al método setFocus() es JNIEXPORT jboolean JNICALL OS_NATIVE(SetMenu) (JNIEnv *env, jclass that, jint arg0, jint arg1) { jboolean rc = 0; OS_NATIVE_ENTER(env, that, SetMenu_FUNC); rc = (jboolean)SetMenu((HWND)arg0, (HMENU)arg1); OS_NATIVE_EXIT(env, that, SetMenu_FUNC); return rc; } Como puede verse, la implementación en Java del método SetMenu() de la clase OS llama a un método del sistema operativo también llamado SetMenu(). (Tal como se adelantó, hay una relación uno a uno entre los métodos de SWT y los métodos gráficos de la plataforma.) Cada plataforma con una implementación del SWT tiene una biblioteca compartida (en Windows, una biblioteca dinámica o DLL) y uno o más archivos JAR. La biblioteca compartida, específica de cada plataforma, resulta de compilar el código JNI-C que establece una correspondencia uno a uno entre los métodos Java y los métodos de la API gráfica propia del SO. En Windows, esta biblioteca contiene, entre otro mucho código, el código máquina resultante de compilar el código JNI-C del método setMenu() que se ha usado antes como ejemplo. Los archivos JAR contienen las clases Java de SWT –Button, Tree, List, Text, Label, etc.–, que llaman a los métodos JNI-C correspondientes (los cuales, a su vez, llaman a los métodos nativos de la plataforma). Estas clases comunes, por tener las mismas declaraciones de sus métodos públicos para todas las plataformas, permiten que el código Java que use SWT se ejecute sin necesidad de cambios en cualquier otra plataforma donde exista una implementación de SWT. En ese sentido, SWT resulta a la par transportable y específico de la plataforma, por contradictorio que parezca (en SWT, todas las interfaces directas a la API gráfica específica del SO están escritas en Java, excepto la de más bajo nivel). Tal y como dice IBM en su página web: “Un importante beneficio de usar SWT es que, una vez se desarrolla una interfaz de usuario en Linux, es también una interfaz de primera clase cuando se aplica a Windows. Lo inverso también es cierto”. Un ejemplo completo aclarará el porqué del contradictorio carácter del SWT: consideremos un widget SWT Button, subclase de la clase abstracta Control, y supongamos que trabajamos con implementaciones de SWT en Windows y Solaris (olvidemos temporalmente que hay más plataformas).
  • 11. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 11 de 114 En Windows, la clase Button tiene el siguiente código (por brevedad, omito todos los comentarios, salvo el de copyright, y algunos de los métodos): package org.eclipse.swt.widgets; /* * Copyright (c) 2000, 2002 IBM Corp. All rights reserved. * This file is made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html */ import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.win32.*; import org.eclipse.swt.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.events.*; public class Button extends Control { Image image; static final int ButtonProc; static final TCHAR ButtonClass = new TCHAR (0,"BUTTON", true); static final int CheckWidth, CheckHeight; static { int hBitmap = OS.LoadBitmap (0, OS.OBM_CHECKBOXES); if (hBitmap == 0) { CheckWidth = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CXSMICON : OS.SM_CXVSCROLL); CheckHeight = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CYSMICON : OS.SM_CYVSCROLL); } else { BITMAP bitmap = new BITMAP (); OS.GetObject (hBitmap, BITMAP.sizeof, bitmap); OS.DeleteObject (hBitmap); CheckWidth = bitmap.bmWidth / 4; CheckHeight = bitmap.bmHeight / 3; } WNDCLASS lpWndClass = new WNDCLASS (); OS.GetClassInfo (0, ButtonClass, lpWndClass); ButtonProc = lpWndClass.lpfnWndProc; } public Button (Composite parent, int style) { super (parent, checkStyle (style)); } public void addSelectionListener (SelectionListener listener) { checkWidget (); if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); TypedListener typedListener = new TypedListener (listener); addListener (SWT.Selection,typedListener); addListener (SWT.DefaultSelection,typedListener); }
  • 12. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 12 de 114 int callWindowProc (int msg, int wParam, int lParam) { if (handle == 0) return 0; return OS.CallWindowProc (ButtonProc, handle, msg, wParam, lParam); } void click () { OS.SendMessage (handle, OS.BM_CLICK, 0, 0); } public Point computeSize (int wHint, int hHint, boolean changed) { checkWidget (); int border = getBorderWidth (); int width = border * 2, height = border * 2; if ((style & SWT.ARROW) != 0) { if ((style & (SWT.UP | SWT.DOWN)) != 0) { width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); height += OS.GetSystemMetrics (OS.SM_CYVSCROLL); } else { width += OS.GetSystemMetrics (OS.SM_CXHSCROLL); height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); } if (wHint != SWT.DEFAULT) width = wHint + (border * 2); if (hHint != SWT.DEFAULT) height = hHint + (border * 2); return new Point (width, height); } int extra = 0; int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & (OS.BS_BITMAP | OS.BS_ICON)) == 0) { int oldFont = 0; int hDC = OS.GetDC (handle); int newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); TEXTMETRIC lptm = new TEXTMETRIC (); OS.GetTextMetrics (hDC, lptm); int length = OS.GetWindowTextLength (handle); if (length == 0) { height += lptm.tmHeight; } else { extra = Math.max (8, lptm.tmAveCharWidth); TCHAR buffer = new TCHAR (getCodePage (), length + 1); OS.GetWindowText (handle, buffer, buffer.length ()); RECT rect = new RECT (); int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE; OS.DrawText (hDC, buffer, length, rect, flags); width += rect.right - rect.left; height += rect.bottom - rect.top; } if (newFont != 0) OS.SelectObject (hDC, oldFont); OS.ReleaseDC (handle, hDC); } else { if (image != null) { Rectangle rect = image.getBounds (); width = rect.width; height = rect.height; extra = 8; } }
  • 13. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 13 de 114 if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { width += CheckWidth + extra; height = Math.max (height, CheckHeight + 3); } if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { width += 10; height += 7; } if (wHint != SWT.DEFAULT) width = wHint + (border * 2); if (hHint != SWT.DEFAULT) height = hHint + (border * 2); return new Point (width, height); } int defaultBackground () { if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { return OS.GetSysColor (OS.COLOR_BTNFACE); } return super.defaultBackground (); } int defaultForeground () { return OS.GetSysColor (OS.COLOR_BTNTEXT); } public int getAlignment () { checkWidget (); if ((style & SWT.ARROW) != 0) { if ((style & SWT.UP) != 0) return SWT.UP; if ((style & SWT.DOWN) != 0) return SWT.DOWN; if ((style & SWT.LEFT) != 0) return SWT.LEFT; if ((style & SWT.RIGHT) != 0) return SWT.RIGHT; return SWT.UP; } if ((style & SWT.LEFT) != 0) return SWT.LEFT; if ((style & SWT.CENTER) != 0) return SWT.CENTER; if ((style & SWT.RIGHT) != 0) return SWT.RIGHT; return SWT.LEFT; } String getNameText () { return getText (); } public String getText () { checkWidget (); int length = OS.GetWindowTextLength (handle); if (length == 0) return ""; TCHAR buffer = new TCHAR (getCodePage (), length + 1); OS.GetWindowText (handle, buffer, length + 1); return buffer.toString (0, length); } public void setText (String string) { checkWidget (); if (string == null) error (SWT.ERROR_NULL_ARGUMENT); int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE); int oldBits = newBits; newBits &= ~(OS.BS_BITMAP | OS.BS_ICON);
  • 14. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 14 de 114 if (newBits != oldBits) { OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); } TCHAR buffer = new TCHAR (getCodePage (), string, true); OS.SetWindowText (handle, buffer); } LRESULT WM_GETDLGCODE (int wParam, int lParam) { LRESULT result = super.WM_GETDLGCODE (wParam, lParam); if (result != null) return result; if ((style & SWT.ARROW) != 0) { return new LRESULT (OS.DLGC_STATIC); } return result; } LRESULT WM_KILLFOCUS (int wParam, int lParam) { LRESULT result = super.WM_KILLFOCUS (wParam, lParam); if ((style & SWT.PUSH) != 0 && getDefault ()) { menuShell ().setDefaultButton (null, false); } return result; } } En Solaris 8, la clase Button presenta este código (por brevedad, omito todos los comentarios, salvo el de copyright, y algunos de los métodos): /******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.widgets; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.motif.*; import org.eclipse.swt.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.events.*; public class Button extends Control { String text = ""; Image image, bitmap, disabled; static final byte [] ARM_AND_ACTIVATE; static {
  • 15. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 15 de 114 String name = "ArmAndActivate"; int length = name.length(); char [] unicode = new char [length]; name.getChars (0, length, unicode, 0); byte [] buffer = new byte [length + 1]; for (int i = 0; i < length; i++) { buffer[i] = (byte) unicode[i]; } ARM_AND_ACTIVATE = buffer; } public Button (Composite parent, int style) { super (parent, checkStyle (style)); } public void addSelectionListener(SelectionListener listener) { checkWidget(); if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); TypedListener typedListener = new TypedListener(listener); addListener(SWT.Selection,typedListener); addListener(SWT.DefaultSelection,typedListener); } public Point computeSize (int wHint, int hHint, boolean changed) { checkWidget(); int border = getBorderWidth (); int width = border * 2, height = border * 2; if ((style & SWT.ARROW) != 0) { width += display.scrolledMarginX; height += display.scrolledMarginY; if (wHint != SWT.DEFAULT) width = wHint + (border * 2); if (hHint != SWT.DEFAULT) height = hHint + (border * 2); return new Point (width, height); } XtWidgetGeometry result = new XtWidgetGeometry (); result.request_mode = OS.CWWidth | OS.CWHeight; int [] argList2 = {OS.XmNrecomputeSize, 1}; OS.XtSetValues(handle, argList2, argList2.length / 2); OS.XtQueryGeometry (handle, null, result); int [] argList3 = {OS.XmNrecomputeSize, 0}; OS.XtSetValues(handle, argList3, argList3.length / 2); width += result.width; height += result.height; int [] argList = {OS.XmNlabelType, 0}; OS.XtGetValues (handle, argList, argList.length / 2); if (argList [1] == OS.XmSTRING) { int [] argList1 = {OS.XmNlabelString, 0}; OS.XtGetValues (handle, argList1, argList1.length / 2); int xmString = argList1 [1]; if (OS.XmStringEmpty (xmString)) height += getFontHeight (font.handle); if (xmString != 0) OS.XmStringFree (xmString); } if (wHint != SWT.DEFAULT || hHint != SWT.DEFAULT) { int [] argList4 = new int [] {OS.XmNmarginLeft, 0, OS.XmNmarginRight, 0, OS.XmNmarginTop, 0, OS.XmNmarginBottom, 0}; OS.XtGetValues (handle, argList4, argList4.length / 2); if (wHint != SWT.DEFAULT) width = wHint + argList4 [1] + argList4 [3] + (border * 2);
  • 16. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 16 de 114 if (hHint != SWT.DEFAULT) height = hHint + argList4 [5] + argList4 [7] + (border * 2); } return new Point (width, height); } public int getAlignment () { checkWidget(); if ((style & SWT.ARROW) != 0) { int [] argList = {OS.XmNarrowDirection, 0}; OS.XtGetValues (handle, argList, argList.length / 2); int direction = argList [1]; if (direction == OS.XmARROW_UP) return SWT.UP; if (direction == OS.XmARROW_DOWN) return SWT.DOWN; if (direction == OS.XmARROW_LEFT) return SWT.LEFT; if (direction == OS.XmARROW_RIGHT) return SWT.RIGHT; return SWT.UP; } int [] argList = {OS.XmNalignment, 0}; OS.XtGetValues (handle, argList, argList.length / 2); int alignment = argList [1]; if (alignment == OS.XmALIGNMENT_BEGINNING) return SWT.LEFT; if (alignment == OS.XmALIGNMENT_CENTER) return SWT.CENTER; if (alignment == OS.XmALIGNMENT_END)return SWT.RIGHT; return SWT.CENTER; } boolean getDefault () { if ((style & SWT.PUSH) == 0) return false; return this == menuShell ().defaultButton; } String getNameText () { return getText (); } public String getText () { checkWidget(); if ((style & SWT.ARROW) != 0) return ""; return text; } public void setAlignment (int alignment) { checkWidget(); if ((style & SWT.ARROW) != 0) { int [] argList = {OS.XmNarrowDirection, OS.XmARROW_UP}; if ((alignment & SWT.UP) != 0) argList [1] = OS.XmARROW_UP; if ((alignment & SWT.DOWN) != 0) argList [1] = OS.XmARROW_DOWN; if ((alignment & SWT.LEFT) != 0) argList [1] = OS.XmARROW_LEFT; if ((alignment & SWT.RIGHT) != 0) argList [1] = OS.XmARROW_RIGHT; OS.XtSetValues (handle, argList, argList.length / 2); return; } if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return; int [] argList = {OS.XmNalignment, OS.XmALIGNMENT_BEGINNING}; if ((alignment & SWT.CENTER) != 0) argList [1] = OS.XmALIGNMENT_CENTER; if ((alignment & SWT.RIGHT) != 0) argList [1] = OS.XmALIGNMENT_END; OS.XtSetValues (handle, argList, argList.length / 2); }
  • 17. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 17 de 114 public void setText (String string) { checkWidget(); if (string == null) error (SWT.ERROR_NULL_ARGUMENT); if ((style & SWT.ARROW) != 0) return; text = string; char [] text = new char [string.length ()]; string.getChars (0, text.length, text, 0); int mnemonic = fixMnemonic (text); byte [] buffer = Converter.wcsToMbcs (getCodePage (), text, true); int xmString = OS.XmStringParseText ( buffer, 0, OS.XmFONTLIST_DEFAULT_TAG, OS.XmCHARSET_TEXT, null, 0, 0); if (xmString == 0) error (SWT.ERROR_CANNOT_SET_TEXT); if (mnemonic == 0) mnemonic = OS.XK_VoidSymbol; int [] argList = { OS.XmNlabelType, OS.XmSTRING, OS.XmNlabelString, xmString, OS.XmNmnemonic, mnemonic, }; OS.XtSetValues (handle, argList, argList.length / 2); if (xmString != 0) OS.XmStringFree (xmString); } int xFocusIn (XFocusChangeEvent xEvent) { super.xFocusIn (xEvent); if (handle == 0) return 0; if ((style & SWT.PUSH) != 0) { menuShell ().setDefaultButton (this, false); } return 0; } } Como puede observarse, la clase Button en Solaris presenta un código muy distinto del correspondiente a la misma clase en Windows, pero ambas mantienen la misma declaración –es decir, los mismos argumentos y el mismo tipo de retorno– para todos los métodos públicos. Por ejemplo, el método público setText() de Button mantiene la misma declaración tanto en Solaris como en Windows: public void setText (String string). (Lo mismo sucede en todas las demás plataformas para las que está disponible SWT.) En cuanto a la implementación de los métodos públicos, varía completamente de una a otra cada plataforma. En el caso de setText(), se usan en Windows llamadas a métodos como OS.GetWindowLong(), que llama, a su vez, al método GetWindowLong() de la biblioteca gráfica Win32; en Solaris se usan llamadas a métodos como OS.XmStringParseText(), que llama, a su vez, al método XmStringParseText() de la bibliotecas gráfica Motif. Tal como se dijo antes, la clase OS representa el sistema operativo de la plataforma.
  • 18. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 18 de 114 Los métodos que no son públicos gozan de completa libertad en cuanto a declaración. Generalmente, no tienen por qué existir en todas las plataformas. Por ejemplo, no hay un método con una declaración int callWindowProc(int p0, int p1, int p2) en la clase Button de Solaris, método que goza de buena salud en Windows. Como ya se adelantó hace unas páginas, que los métodos públicos de los widgets SWT se declaren igual en todas las plataformas causa que el SWT sea transportable. En el ejemplo de Button, la implementación de cada método en Solaris no será transportable a Windows, y viceversa; pero el código Java que utilice los métodos públicos de Button sí lo será, puesto que ambas plataformas tienen implementaciones del SWT. Continuando con el ejemplo de setText(), la sentencia miBoton.setText("Soy un botón SWT") hará llamadas en Windows y Solaris, a través de JNI, a métodos nativos completamente diferentes; pero funcionará en ambas plataformas (dando distintas resultados gráficos, claro está). El código Java que llama a SWT no necesita conocer la implementación de las clases SWT ni el código JNI-C encargado de llamar a la API gráfica correspondiente. Más aún: si lo hiciera se incumpliría el principio de encapsulado, fundamental en la programación orientada a objetos. Las bibliotecas JNI-SWT deben compilarse para cada plataforma, lo cual crea un archivo con extensión .dll en Windows y otro con extensión .so en Solaris. En la página web de Eclipse se puede descargar el código JNI-C de la biblioteca JNI-SWT para cada plataforma, así como el código compilado para cada plataforma. Un programa que utilice SWT empleará las bibliotecas Win32 o MFC (propias de Windows) cuando se ejecuta en Windows (98/ME/2000/XP), y las bibliotecas de Motif cuando se ejecuta en Solaris. La correspondencia casi exacta entre los objetos gráficos de SWT –widgets, tipos de letra, colores, imágenes– y los del sistema operativo hace que el recolector de basura de Java no sirva para liberar los recursos asociados a esos objetos; pues la tarea natural del recolector es liberar los recursos asociados a objetos que “viven” en la máquina virtual de Java y que ya no se utilizan, no liberar recursos del sistema operativo donde se ejecuta la MVJ. Por esto, la liberación de los recursos asociados a objetos SWT debe establecerla el programador. Por ejemplo, una ventana repleta de controles puede cerrarse de varias maneras –uso “cerrar” en el sentido de hacerla desaparecer de la vista del usuario–; pero el programador deberá llamar explícitamente al método dispose() del display correspondiente si realmente quiere liberar los recursos de la ventana y de sus controles. En caso contrario, puede suceder que la aplicación se cuelgue o funcione muy lentamente si contiene muchas ventanas que ya no se necesitan y cuyos recursos no se han liberado. Para evitar fallos de memoria y la disminución del rendimiento, se recomienda liberar los recursos asociados a un objeto SWT en cuanto se deje de necesitarlo. SWT sigue la estrategia de que liberar los recursos de un control “padre” conlleva la liberación de los recursos de todos los controles “hijos”. Dicho de otro modo: al llamar al método dispose() del padre, se llama a cada método dispose() de los hijos. Métodos como dispose() resultan necesarios cuando se trabaja con componentes gráficos o con sockets, pues llaman a métodos nativos que saben cómo liberar los recursos asociados del sistema operativo. Si desea más información sobre el uso de dispose() con sockets, puede consultar el apartado 3.3 del tutorial java.net y java.nio. Cómo hacer un chat en Java, http://www.javahispano.org/tutorials.item.action?id=7). Esas llamadas a métodos nativos son inevitables, ya que los componentes gráficos de peso pesado y las conexiones de red dependen por completo del SO.
  • 19. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 19 de 114 En el caso de los objetos SWT correspondientes a colores (Color), imágenes (Image) y tipos de letra (Font), el programador debe ser cuidadoso. De un lado, dichos objetos carecen de objetos “padre”. De otro, los recursos que consumen no se liberan cuando se llama al método dispose() de los componentes donde se usan tales objetos. Estas dos características no se deben a ningún error de diseño. Antes al contrario: responden a un entendimiento cabal de la naturaleza de esos objetos. Si, por ejemplo, las imágenes tuvieran que crearse asociadas a componentes padre (ventanas, botones, listas, menús...), todos los componentes con una misma imagen necesitarían sendas instancias de la clase Image. Esta situación sería muy incómoda para el programador (quien tendría que escribir la instaciación una y otra vez) y para el sistema operativo (imaginémoslo como un gruñón que se preguntara por qué demonios le obligan a mantener tantos objetos Image, cuando en realidad corresponden a un misma imagen, y que amenazara con colgar el sistema como protesta). A consecuencia de la “libertad” de la que gozan esos objetos, resulta obligatorio que no se destruyan cuando se destruyen los componentes que los usan. Si varios componentes usaran un mismo objeto Image y al destruir uno se destruyera el objeto Image, ¿en qué estado se encontrarían los restantes componentes? Sin imagen, que ya estaría en el cubo de la basura. Como los objetos Color, Image y Font no se destruyen cuando desaparecen los componentes que los usan, el programador debe liberarlos explícitamente llamando a dispose(). En el apartado dedicado a JFace veremos cómo se puede simplificar la liberación de los recursos que consumen estos objetos. Toda la interfaz gráfica de Eclipse se basa en SWT. Ahora bien, ello no significa que este componente gráfico se pueda usar sólo con Eclipse: las bibliotecas SWT se pueden descargar independientemente de Eclipse y pueden usarse para construir aplicaciones independientes. En la última versión de Eclipse (3.0.2), lanzada el 11 de marzo de 2005 (la versión 3.1M5a, aún no definitiva, salió en febrero de este año), los componentes SWT y JFace se hallan disponibles para las siguientes plataformas: Windows 98/ME/2000/XP Windows PocketPC 2002 (Strong ARM) Linux (x86/Motif) Linux (x86/GTK 2) Linux (AMD 64/GTK 2) Linux (IA 64/GTK 2) Solaris 8 (SPARC/Motif) AIX (PPC/Motif) HP-UX (HP9000/Motif) HP-UX (IA 64/Motif) Como la licencia de SWT coincide con la de Eclipse (Common Public License, ya explicada en el primer artículo de esta serie: http://www.javahispano.org/articles.article.action?id=75), cualquier programador puede usar este componente sin pagar regalías. Para ello, debe configurar el CLASSPATH de su aplicación para que incluya el archivo jar del SWT (dependiendo de la plataforma, pueden ser varios) y la propiedad java.library.path para que incluya la biblioteca compartida JNI-SWT propia de la plataforma usada (esta biblioteca resulta de compilar
  • 20. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 20 de 114 todo el código JNI-C que usan las clases SWT; en Windows tiene un nombre del tipo swt- win32.xxxx.dll). En las siguientes figuras se muestra el aspecto de Eclipse en varias plataformas. Como Eclipse usa exclusivamente componentes SWT, su aspecto cambia en cada plataforma. Fig. 16. SWT en acción: Eclipse en Linux-Motif. Extraído de la documentación oficial de Eclipse.
  • 21. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 21 de 114 Fig. 17. SWT en acción: Eclipse en Linux/Gnome2
  • 22. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 22 de 114 Fig. 18. SWT en acción: Eclipse en Windows XP
  • 23. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 23 de 114 Fig. 19. SWT en acción: Eclipse en Mac OS X
  • 24. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 24 de 114 15. Ejemplos de uso del Standard Widget Tool (SWT) Esta sección no pretende, ni mucho menos, ser una introducción completa a SWT. Sus objetivos son mucho más modestos: explicar qué se necesita para programar con este componente gráfico, presentar algunos aspectos de su uso y dar unos cuantos ejemplos representativos de cómo se usan los widgets SWT. Dar una explicación completa del funcionamiento de SWT queda fuera de las metas de esta serie de artículos (definidas en el primero, http://www.javahispano.org/articles.article.action?id=75). Usando SWT, el típico programa HolaMundo quedaría así: import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; public class Saludos { public static void main(String args[]){ // Se crea un display que servirá como contenedor para el shell. Display display = new Display(); // Se coloca el display dentro del shell. Shell shell = new Shell(display); // Se establece el tamaño y el texto del shell. shell.setSize(250, 120); shell.setText("Mi primera aplicación con SWT"); // Se crea una etiqueta centrada en la ventana y se le da un texto. Label etiqueta= new Label(shell, SWT.CENTER); etiqueta.setText("Saludos a los lectores de javaHispano"); etiqueta.setBounds(shell.getClientArea()); // Se muestra el shell; un shell es invisible salvo que se llame a open(). shell.open (); // Se entra en un bucle para leer y enviar los sucesos del usuario. // Si no hay ningún suceso que tratar, el método sleep() hace que el // programa espere el suceso siguiente sin consumir ciclos de CPU. // Se sale del bucle cuando el usuario cierra el shell. while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } // Se cierra el display y se liberan los recursos que empleaba del sistema operativo // Siempre es conveniente liberar explícitamente los recursos // que no se van a necesitar. En este caso no es necesario llamar a dispose(), // pues si se llega a aquí es porque el programa va a terminar, lo que implica que // se liberarán los recursos asociados; pero es una buena práctica acostumbrarse // a hacerlo. Ejemplo 1: Saludos.java
  • 25. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 25 de 114 display.dispose (); } } Para que un programa con SWT pueda compilarse y ejecutarse en el entorno de Eclipse, se necesitan dos pasos previos. El primero consiste en indicar al compilador dónde está el código Java de SWT; es decir, dónde se encuentran las clases SWT. Éstas se almacenan en el archivo swt.jar. Para indicar a Eclipse dónde debe buscar el archivo swt.jar hay que seguir varios pasos. En primer lugar, hay que apretar, sobre el nombre del proyecto de Java, el botón derecho del ratón (véase la figura 20). A continuación, hay que seleccionar la opción Properties. En la ventana que aparece debe seleccionarse Java Build Path (panel de la derecha) y la pestaña Libraries (véase la figura 21). Acto seguido, debe incluirse swt.jar con el botón Add External JARs... (véase la figura 22). Según el FAQ más actualizado de Eclipse (http://www.eclipse.org/eclipse/faq/eclipse-faq.html), swt.jar se encuentra –dependiendo de la plataforma– en • win32: INSTALLDIReclipsepluginsorg.eclipse.swt.win32_3.0.1wswin32 • Linux GTK: INSTALLDIR/eclipse/plugins/org.eclipse.swt.gtk_3.0.1/ws/gtk/ • Linux Motif: INSTALLDIR/eclipse/plugins/org.eclipse.swt.motif_3.0.1/ws/motif/ • Photon QNX: INSTALLDIR/eclipse/plugins/org.eclipse.swt.photon_3.0.1/ws/photon/ • Mac OS X: INSTALLDIR/eclipse/plugins/org.eclipse.swt.carbon_3.0.1/ws/carbon/ INSTALLDIR es el directorio donde se ha instalado Eclipse. Dependiendo de la versión de SWT que se use, cambiará el número separado por puntos que aparece en las rutas (en vez de 3.0.1, podría ser 3.1.0, por ejemplo).
  • 26. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 26 de 114 Figura 20. Nótese que la vista que se usa es la de paquetes
  • 27. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 27 de 114 Figura 21. Hay que apretar el botón Add External JARs... Figura 22. Hay que seleccionar el archivo swt.jar (en otra plataforma, se encontrará en otro directorio)
  • 28. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 28 de 114 El segundo paso que se requiere para ejecutar el programa consiste en indicar a Eclipse dónde está la biblioteca compartida que requiere swt.jar. Una manera consiste en introducir, cuando se arranca la máquina virtual de Java, la ubicación de la biblioteca compartida como argumento de ejecución. Para ello, basta elegir el menú Run, escoger Run... (véase la figura 23), elegir la pestaña Arguments en la parte derecha de la pantalla e introducir en el área de texto VM arguments lo siguiente (véase la figura 24a): -Djava.library.path=<directorio que contiene la biblioteca compartida para SWT> Tal y como sucedía con swt.jar, la biblioteca se encontrará en una ubicación dependiente de la plataforma. Según el FAQ de Eclipse, las ubicaciones serán del estilo (la lista de plataformas no es exhaustiva): • Windows: INSTALLDIReclipsepluginsorg.eclipse.swt.win32_3.0.1oswin32x86 • Linux GTK: INSTALLDIR/eclipse/plugins/org.eclipse.swt.gtk_3.0.1/os/linux/x86 • Linux Motif: INSTALLDIR/eclipse/plugins/org.eclipse.swt.motif_3.0.1/os/linux/x86 • Photon QNX: INSTALLDIR/eclipse/plugins/org.eclipse.swt.photon_3.0.1/os/qnx/x86 • Mac OS X: INSTALLDIR/eclipse/plugins/org.eclipse.swt.carbon_3.0.1/os/macosx/ppc Figura 23. Se escoge Run...
  • 29. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 29 de 114 Figura 24a. La versión de Eclipse de la captura de pantalla es la 3.1M4 El nombre exacto de la biblioteca JNI–SWT varía en cada versión de Eclipse (a veces, también varía con la plataforma, aunque no varíe la versión). En la versión 3.1M3 de Eclipse para Windows 98/ME/2000/XP, el nombre de la biblioteca es swt-win-32- 3111.dll (véase la figura 24b). Figura 24b. Biblioteca JNI-SWT correspondiente a Eclipse 3.1M3
  • 30. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 30 de 114 Completados todos los pasos anteriores, la ejecución de la clase Saludos en Windows XP producirá una ventana similar a la de la figura 25a. Figura 25a. Resultado de ejecutar la clase Saludos Figura 25b. Manteniendo en ejecución la clase Saludos, la ventana cambia de aspecto cuando se cambia el estilo de Windows XP por el clásico de Windows. Como ya se señaló en el apartado anterior, para desarrollar o ejecutar aplicaciones SWT no se necesita el entorno de desarrollo Eclipse. Para ejecutar en Windows una aplicación SWT sin Eclipse, hay que añadir el archivo swt.jar al CLASSPATH e incluir la biblioteca dinámica swt-win-32-xxxx.dll en el java.library.path. Tanto swt.jar como la DLL se pueden descargar sin el resto de Eclipse. Por ejemplo, en el caso de que ambos archivos estén en C:SWT, la siguiente orden compilará la aplicación Saludos desde la línea de órdenes: javac -classpath C:SWTswt.jar Saludos.java La siguiente orden ejecutará Saludos desde la línea de órdenes: java -classpath C:SWTswt.jar -Djava.library.path=C:SWT Saludos Para el resto de las plataformas, el proceso resulta similar a éste.
  • 31. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 31 de 114 La clase Saludos, si bien muy simple, alberga la estructura de cualquier aplicación SWT: 1) Se crea un objeto Display. 2) Se crea un objeto Shell que sirve como ventana principal de la aplicación. 3) Se crean dentro del shell los widgets (componentes gráficos) que se deseen. 4) Se configuran los tamaños de los widgets (normalmente, en este paso se registran los sucesos a los cuales atenderán los widgets). 5) Se abre la ventana del shell. 6) Se entra en un bucle donde se comprueban los sucesos que se van produciendo. 7) Se cierra el objeto Display y se liberan los recursos asociados. La clase Display (contenida en el paquete org.eclipse.swt.widgets) actúa como “puente” entre el SWT y las operaciones del sistema de interfaz gráfica de la plataforma. Según la documentación de esta clase, su principal función es implementar el bucle de sucesos de SWT mediante el modelo de sucesos de la plataforma. Además, proporciona métodos para acceder a información sobre el sistema operativo y para controlar y gestionar los recursos del sistema donde se ejecuta SWT. En resumen, un objeto Display se comporta como un intermediario entre la interfaz de usuario y el componente o componentes que lo implementan en una plataforma. Por lo general, el programador –salvo que escriba aplicaciones multihilo– debe preocuparse solamente de: a) crear un objeto Display antes de crear ninguna ventana; y b) cerrarlo cuando ya no se necesite. El método dispose() se encarga de liberar los recursos que el display consumía del sistema de ventanas específico de la plataforma (memoria, punteros). Al llamarlo, se eliminan todos los shells que tenía el display. Las instancias de la clase Shell (contenida en el paquete org.eclipse.swt.widgets) vienen a ser ventanas gestionadas por el gestor de ventanas propio de la plataforma. Cuando el usuario mueve o cambia el tamaño de una ventana, SWT pasa el tratamiento de estas acciones a la API gráfica del SO. Esta clase tiene varios constructores que admiten como argumento un objeto shell: con ellos se pueden generan shells de diálogo o secundarios (shell significa cáscara). El método dispose() de esta clase actúa de forma similar al método homónimo de la clase Display. Nota: La manera más sencilla de evitar la configuración del CLASSPATH y del argumento -Djava.library.path desde la línea de órdenes es colocar el fichero o los ficheros JAR en el directorio lib/ext del JRE (Java Runtime Environment), y la biblioteca compartida JNI- SWT en el directorio bin del JRE.
  • 32. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 32 de 114 Si comparamos SWT con Swing, los componentes de Swing serían equivalentes a los widgets del SWT, y los marcos y ventanas (frames and windows), a los shells. En el constructor de la etiqueta habrá visto que hay un segundo argumento en el constructor: Label etiqueta= new Label(shell, SWT.CENTER); SWT.CENTER (o CENTER, por abreviar) es un estilo. Los estilos son constantes enteras que se emplean para establecer el aspecto y comportamiento de los widgets. En este ejemplo, el estilo CENTER indica que el texto de la etiqueta debe centrarse respecto a ella. Todos los estilos están definidos en la clase org.eclipse.swt.SWT. Una vez se asigna un estilo a un widget, aquél no puede cambiarse. La clase Shell que acabamos de ver admite los siguientes estilos: BORDER, H_SCROLL, V_SCROLL, CLOSE, MIN, MAX, RESIZE, TITLE, SHELL_TRIM y DIALOG_TRIM. Un widget puede aceptar varios estilos: Label etiqueta = new Label(shell, SWT.CENTER | SWT.HORIZONTAL | SWT.SEPARATOR); Figura 26. Ejemplo de distintos estilos de un mensaje de diálogo El paquete org.eclipse.swt.widgets incluye un conjunto de widgets o componentes SWT formado, entre otros, por estos elementos: - Button. SWT no tiene una clase individual para un botón redondo, de flecha, etc.: todos son instancias de Button. - Label. Una etiqueta muestra una cadena de texto, una imagen o una línea –vertical u horizontal– separadora. - Text. Muestra un texto susceptible de ser editado, ya sea de una línea o de varias. - Slider. Control que representa un intervalo de valores numéricos. - ProgressBar. Control que indica el porcentaje cumplido de una tarea (descargar un archivo, cargar una aplicación, etc.).
  • 33. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 33 de 114 - Combo. Control que permite seleccionar un valor de una lista desplegable o introducir directamente el valor deseado. - List. Control que permite seleccionar (de forma simple o múltiple) elementos de una lista de cadenas de texto. - Composite. Control que permite agrupar otros controles. Equivaldría a un contenedor (container) de Swing. - Group. Control formado a partir de un Composite con título y borde. - Canvas. Control que permite operaciones gráficas de todo tipo (dibujar un octógono azul, por ejemplo) - Menu. Control que contiene elementos seleccionables de tipo menú. - MenuItem. Elemento seleccionable que representa un elemento en un menú. - Table. Control seleccionable que muestra una lista de elementos de una tabla. - TableColumn. Elemento seleccionable que representa una columna de una tabla. - TableItem. Objeto seleccionable que representa un elemento de una tabla. - ToolBar. Control compuesto que permite diseñar elementos de barras de herramientas. - ToolItem. Objeto seleccionable que representa un elemento de una barra de herramientas. - Tree. Control seleccionable en forma de árbol que muestra una lista jerárquica de elementos. - TreeItem. Objeto que representa una jerarquía de objetos en un control Tree.
  • 34. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 34 de 114 Figura 27. Funcionamiento interno de SWT Antes de dar algunos ejemplos del uso de estos componentes, creo pertinente incluir aquí algunos comentarios sobre el tratamiento de sucesos en SWT. El tratamiento de los sucesos en el SWT resulta muy similar al de Swing y al de AWT: a cada widget se le añade una clase Listener mediante un método del estilo addXXXListener(), cuyo argumento es la clase que implementa la correspondiente interfaz de tipo Listener. Asimismo, existen clases adaptadoras –clases que implementan por omisión las interfaces Listener–, de manera que el programador necesite escribir sólo el código de los sucesos que verdaderamente le interesan. Plataforma Display Shell Composite Widget Widget Widget Widget Bibliotecas gráficas JNI FUNCIONAMIENTO INTERNO DEL COMPONENTE SWT Miguel Ángel Abián, 2005
  • 35. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 35 de 114 Nota: Si no tiene experiencia con el tratamiento de sucesos en Swing o AWT, le resultará útil saber lo siguiente: 1) Cada suceso se asocia con una acción del usuario (pulsar una tecla, mover el ratón...). 2) Cada suceso tiene asociado una interfaz de Java llamada Listener (oyente). 3) Es tarea del programador crear una clase Listener que implemente la interfaz Listener correspondiente y en la que se especifique qué respuesta debe darse cuando se produzca un suceso. 4) Para añadir una clase Listener a un control se usan métodos del estilo addXXXListener(). 5) Las clases Adapters (adaptadoras) son clases que implementan las interfaces Listener con una implementación por omisión para cada método. En vez de una clase Listener, el programador puede asociar un Adapter a un componente, de manera que sólo tenga que implementar los métodos que necesite. Por ejemplo, a la interfaz FocusListener le corresponde la clase adaptadora FocusAdapter.
  • 36. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 36 de 114 Figura 28. Tabla con los sucesos y las clases Listener más importantes
  • 37. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 37 de 114 He aquí un ejemplo de código para tratar sucesos (corresponde a un componente SWT de tipo Button): import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; /** * Esta clase genera un botón que inicialmente permanece apretado. * * La clase Button admite los siguientes estilos: BORDER, CHECK, PUSH, * RADIO, TOGGLE, FLAT, ARROW (con UP, DOWN), LEFT, RIGHT y CENTER. * * BORDER crea un botón con borde. * CHECK crea un botón de casilla de verificación. * PUSH crea un botón estándar (valor por omisión). * RADIO crea un botón de opción. * TOGGLE crea un botón que mantiene su estado pulsado o no pulsado. * FLAT crea un botón sin efectos de relieve 3D (plano). * ARROW crea un botón en forma de flecha (UP y DOWN marcan el sentido de ésta). * LEFT, RIGHT, CENTER alinean el texto asociado al botón. */ public class EjemploBoton { public static void main(String args[]) { // Se crea un display que servirá como contenedor para el shell. Display display = new Display(); // Se coloca el display dentro del shell. Shell shell = new Shell(display); // Se establece el tamaño y el texto del shell. shell.setSize(250, 120); shell.setText("Ventana hecha con el SWT"); // Se establece el tipo, eltamaño y el texto del botón. // Un botón TOGGLE permanece presionado tras pulsarlo, hasta que se vuelve a // pulsar. Button boton = new Button(shell, SWT.TOGGLE); boton.setSize(150, 50); boton.setText("Soy un botón de tipo TOGGLE"); // Tratamiento de sucesos: muy similar a como se hace en Swing. // Nótese que hay que implementar todos los métodos de cada interfaz // Listener. boton.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { System.out.println("Se ha seleccionado el botón"); } public void widgetDefaultSelected(SelectionEvent e) { System.out.println("Se ha seleccionado el botón"); Ejemplo 2: EjemploBoton.java
  • 38. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 38 de 114 } }); boton.addMouseListener(new MouseListener() { public void mouseDown(MouseEvent e) { System.out.println("Se ha movido el ratón hacia abajo"); } public void mouseUp(MouseEvent e) { System.out.println("Se ha movido el ratón hacia arriba"); } public void mouseDoubleClick(MouseEvent e) { System.out.println("Se ha hecho doble click en el botón"); } }); // Se muestra el shell; un shell es invisible salvo que se llame a open(). shell.open(); // Se entra en un bucle para leer y enviar los sucesos del usuario. while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } // Se cierra el display y se liberan los recursos del sistema operativo // asociados. display.dispose(); } } Figura 29. Resultado de ejecutar la clase EjemploBoton
  • 39. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 39 de 114 Si en la clase EjemploBoton interesara procesar sólo el suceso Selection, sería mucho más rápido usar la correspondiente clase adaptadora: // Tratamiento del suceso Selection con adaptadores. boton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { System.out.println("Se ha seleccionado el botón"); } }); El ejemplo anterior quedaría así: import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; public class EjemploBoton2 { public static void main(String args[]){ // Se crea un display que servirá como contenedor para el shell. Display display = new Display(); // Se coloca el display dentro del shell. Shell shell = new Shell(display); // Se establece el tamaño y el texto del shell. shell.setSize(250, 120); shell.setText("Ventana hecha con el SWT"); // Se establece el tipo, el tamaño y el texto del botón. Button boton = new Button(shell, SWT.TOGGLE); boton.setSize(150, 50); boton.setText("Soy un botón de tipo TOGGLE"); // Tratamiento del suceso Selection con adaptadores. boton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { System.out.println("Se ha seleccionado el botón"); } }); shell.open (); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose (); } Ejemplo 3: EjemploBoton2.java
  • 40. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 40 de 114 } Con todo, existe una relevante diferencia entre el SWT y Swing: un programa que use SWT y no disponga de algún bucle que se encargue de leer y procesar los sucesos del sistema operativo terminará en cuanto se llegue al final del método main(). Por ejemplo, el usuario sólo verá una ventana que se cierra al momento si ejecuta las clases anteriores sin este código (si su máquina es lo bastante rápida, puede que ni siquiera llegue a ver la ventana): while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } Asociada a esta diferencia entre SWT y Swing, está el hecho de que los sucesos de SWT sólo se entregan al programa (y, en consecuencia, sólo pueden procesarse) cuando se llama a un método SWT. En los ejemplos anteriores, el método readAndDispatch() se encarga de entregar al programa en ejecución los sucesos generados por el usuario. Sin él, el comportamiento del botón sería anómalo: no respondería a las acciones del usuario (defecto, dicho sea de paso, que los usuarios desprecian). A continuación, incluyo unos cuantos ejemplos de uso de los principales controles SWT. Cuando se especifican coordenadas, hay que tener en cuenta que (0, 0) corresponde a la esquina superior izquierda, que los puntos situados por debajo del origen tienen su componente y positiva y que los puntos a la derecha del origen tiene su componente x positiva. import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.*; /** * Esta clase genera una ventana con una imagen y una barra de menús * Se necesita incluir el paquete org.eclips.swt.graphics.Image * para poder trabajar con imágenes en SWT. * * La clase Menu actúa como un contenedor para los objetos MenuItem. * La clase Menu admite los siguientes estilos: BAR, DROP_DOWN y POP_UP. * * BAR crea una barra de menús. * DROP_DOWN crea un menú desplegable. * POP_UP crea un menú contextual. * * * La clase MenuItem admite los siguientes estilos: CHECK, CASCADE, PUSH, RADIO y Ejemplo 4: EjemploMenu1.java
  • 41. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 41 de 114 * SEPARATOR. * * CHECK crea un menú de casilla de verificación. * CASCADE crea un menú en cascada con un submenú. * PUSH crea un elemento estándar de menú. * RADIO crea un menú de opción. * SEPARATOR crea un separador de elementos de menú. * */ public class EjemploMenu1{ public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(200, 200); shell.setText("Ejemplo 1 de menús"); shell.setImage(new Image(display,"c:eclipse.jpg")); Menu menu = new Menu(shell, SWT.BAR); shell.setMenuBar(menu); shell.open(); while (!shell.isDisposed()){ if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } } Figura 30. Resultado de ejecutar la clase EjemploMenu1
  • 42. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 42 de 114 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.Image; /** * Esta clase genera una ventana una barra de menús que tiene dos menús con submenús. * Se necesita incluir el paquete org.eclips.swt.graphics.Image * para poder trabajar con imágenes en SWT. * */ public class EjemploMenu2 { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 300); shell.setText("Ejemplo 2 de menús"); shell.setImage(new Image(display, "c:Eclipse.jpg")); // Se crea la barra de menús. Se pueden crear más, pero sólo una // puede ser visible en un instante dado. Menu barra = new Menu(shell, SWT.BAR); // Se crea un MenuItem llamado archivo. MenuItem archivo = new MenuItem(barra, SWT.CASCADE); archivo.setText("Archivo"); // Se crea el menú de archivos y se asocia al MenuItem archivo de la barra // de menús. Menu menuArchivo = new Menu(shell, SWT.DROP_DOWN); archivo.setMenu(menuArchivo); MenuItem abrir = new MenuItem(menuArchivo, SWT.RADIO); abrir.setText("Abrir"); // Se añade al menú de archivos una línea separadora. MenuItem separador = new MenuItem(menuArchivo, SWT.SEPARATOR); // Se añade al menú Archivo un elemento Salir. MenuItem salir = new MenuItem(menuArchivo, SWT.PUSH); salir.setText("Salir"); // Se crea un MenuItem llamado editar. MenuItem editar = new MenuItem(barra, SWT.CASCADE); editar.setText("Edición"); Ejemplo 5: EjemploMenu2.java
  • 43. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 43 de 114 // Se crea el menú de edición y se asocia al MenuItem editar de la barra de // menús. Menu menuEditar = new Menu(shell, SWT.DROP_DOWN); editar.setMenu(menuEditar); MenuItem cortar = new MenuItem(menuEditar, SWT.PUSH); cortar.setText("Cortar"); // Se añade al menú de edición un elemento Copiar. MenuItem copiar = new MenuItem(menuEditar, SWT.PUSH); copiar.setText("Copiar"); // Se añade al menú de edición un elemento Pegar. MenuItem pegar = new MenuItem(menuEditar, SWT.PUSH); pegar.setText("Pegar"); // Tratamiento de los sucesos del usuario. Es obligatorio // incluir el método widgetDefaultSelected, aunque esté // vacío. abrir.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { System.out.println("Apretó Abrir"); } public void widgetDefaultSelected(SelectionEvent e) { } }); salir.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { System.exit(0); // se sale de la aplicación } public void widgetDefaultSelected(SelectionEvent e) { } }); cortar.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { System.out.println("Apretó Cortar"); } public void widgetDefaultSelected(SelectionEvent e) { } }); copiar.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { System.out.println("Apretó Copiar"); } public void widgetDefaultSelected(SelectionEvent e) { } }); pegar.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { System.out.println("Apretó Pegar"); } public void widgetDefaultSelected(SelectionEvent e) { } }); shell.setMenuBar(barra); shell.open();
  • 44. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 44 de 114 while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } } Figura 31. Resultado de ejecutar la clase EjemploMenu2
  • 45. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 45 de 114 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.*; /** * Esta clase genera un cuadro de texto multilínea. * Se necesita incluir el paquete org.eclips.swt.graphics.Image * para poder trabajar con imágenes en SWT. * * La clase Label admite los siguientes estilos: BORDER, CENTER, * LEFT, RIGHT, WRAP y SEPARATOR (con HORIZONTAL, SHADOW_IN, * SHADOW_OUT, SHADOW_NONE y VERTICAL). * * BORDER crea una etiqueta con borde. * CENTER, LEFT y RIGHT alinean el texto de la etiqueta con respecto a la etiqueta. * WRAP hace que el texto dentro de la etiqueta se pueda dividir en varias líneas, si * resulta necesario. * SHADOW_IN (usado con SEPARATOR) crea un separador que parece empotrado * en la ventana. * SHADOW_OUT (usado con SEPARATOR) crea un separador que parece sobresalir * de la ventana. * SHADOW_NONE (usado con SEPARATOR) crea un separador sin sombra. * SEPARATOR y VERTICAL generan una línea vertical. * SEPARATOR y HORIZONTAL generan una línea horizontal. * * * La clase Text admite los siguientes estilos: BORDER, SINGLE, * READ_ONLY, LEFT, CENTER, RIGHT, WRAP, PASSWORD y MULTI * (con H_SCROLL, V_SCROLL) * * BORDER crea un cuadro de texto con borde. * SINGLE crea un cuadro de texto que sólo permite una línea. * MULTI crea un cuadro de texto que admite varias líneas. * READ_ONLY crea un cuadro de texto cuyo contenido no se puede editar. * CENTER, LEFT y RIGHT alinean el texto del cuadro de texto con respecto a él. * WRAP hace que el texto dentro del cuadro de texto se pueda dividir en varias líneas, si * resulta necesario. * PASSWORD crea un cuadro de texto que muestra asteriscos cuando se escribe en él. * H_SCROLL crea una barra de desplazamiento horizontal. * V_SCROLL crea una barra de desplazamiento vertical. */ public class EjemploCuadroTexto1 { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(280, 230); shell.setText("Ejemplo 1 de cuadro de texto"); Ejemplo 6: EjemploCuadroTexto1.java
  • 46. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 46 de 114 // Se crean los componentes gráficos. Label l1 = new Label(shell, SWT.NONE); l1.setText("Nombre del usuario:"); l1.setBounds(90, 10, 100, 25); final Text nombreUsuario = new Text(shell, SWT.BORDER); nombreUsuario.setBounds(90, 35, 100, 25); Label etiqueta2 = new Label(shell, SWT.NULL); etiqueta2.setText("Contraseña:"); etiqueta2.setBounds(90, 70, 100, 25); final Text contrasenya = new Text(shell, SWT.BORDER); contrasenya.setEchoChar('*'); contrasenya.setBounds(90, 95, 100, 25); Button validarUsuario = new Button(shell, SWT.PUSH); validarUsuario.setText("Validar usuario"); validarUsuario.setBounds(100, 150, 85, 28); // Tratamiento de sucesos: muy similar a como se hace en Swing. Listener listener = new Listener() { public void handleEvent(Event suceso) { System.out.println( "El nombre del usuario es: " + nombreUsuario.getText()); System.out.println( "La contraseña es: " + contrasenya.getText()); } }; validarUsuario.addListener(SWT.Selection, listener); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } }
  • 47. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 47 de 114 Figura 32. Resultado de ejecutar la clase EjemploCuadroTexto1 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.graphics.Image; /** * Esta clase genera un cuadro de texto multilínea. * Se necesita incluir el paquete org.eclips.swt.graphics.Image * para poder trabajar con imágenes en SWT. * */ public class EjemploCuadroTexto2 { public static void main(String args []) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 300); shell.setImage(new Image(display, "c:Eclipse.jpg")); shell.setText("Ejemplo 2 de cuadro de texto"); // Se crea el cuadro de texto multílinea. Text texto = new Text(shell, SWT.MULTI); // El primer argumento indica la coordenada x del extremo inferior izquierdo del cuadro de // texto; el segundo, la coordenada y del extremo superior izquierdo; los otros dos indican la // anchura y la altura del cuadro de texto, respectivamente. Ejemplo 7: EjemploCuadroTexto2.java
  • 48. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 48 de 114 texto.setBounds(10, 10, 200, 28); texto.setText( "Línea 1" + System.getProperty("line.separator") + "Línea 2"); shell.open(); while (!shell.isDisposed()){ if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } } Figura 33. Resultado de ejecutar la clase EjemploCuadroTexto2 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; /** * Esta clase genera tres cuadros de texto y muestra por consola el texto seleccionado * cuando se pasa de uno a otro. * */ Ejemplo 8: EjemploCuadroTexto3.java
  • 49. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 49 de 114 public class EjemploCuadroTexto3 { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(200, 200); shell.setText("Ejemplo 3 de cuadro de texto"); // primer cuadro de texto Text texto1 = new Text(shell, SWT.SINGLE | SWT.BORDER); texto1.setBounds(30, 10, 100, 20); texto1.setTextLimit(11); texto1.setText("javaHispano"); // segundo cuadro de texto Text texto2 = new Text(shell, SWT.SINGLE | SWT.BORDER); texto2.setBounds(30, 40, 100, 20); texto2.setTextLimit(11); // tercer cuadro de texto Text texto3 = new Text(shell, SWT.SINGLE | SWT.BORDER); texto3.setBounds(30, 70, 100, 20); texto3.setTextLimit(11); // gestión de eventos FocusListener focusListener = new FocusListener() { public void focusGained(FocusEvent e) { // Si obtiene el foco se selecciona todo lo que hay en el cuadro de texto. Text texto = (Text) e.widget; texto.selectAll(); } public void focusLost(FocusEvent e) { // Si pierde el foco, muestra el texto seleccionado o un mensaje que // indica que el cuadro de texto estaba vacío. Text texto = (Text) e.widget; if ( texto.getSelectionCount() > 0 ) { if ( texto.getLocation().y == 10 ) { System.out.println("Ha seleccionado en el cuadro de texto 1: " + texto.getText()); } if ( texto.getLocation().y == 40 ) { System.out.println("Ha seleccionado en el cuadro de texto 2: " + texto.getText()); } if ( texto.getLocation().y == 70 ) { System.out.println("Ha seleccionado en el cuadro de texto 3: " + texto.getText()); } } else { System.out.println("No ha seleccionado nada: el cuadro de texto estaba vacío."); } } }; // fin focusListener texto1.addFocusListener(focusListener); texto2.addFocusListener(focusListener); texto3.addFocusListener(focusListener);
  • 50. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 50 de 114 shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } } Figura 34. Resultado de ejecutar la clase EjemploCuadroTexto3 import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.*; /** * Esta clase muestra cómo se usan las etiquetas con SWT. * * La clase Label admite los siguientes estilos: BORDER, CENTER, * LEFT, RIGHT, WRAP y SEPARATOR (con HORIZONTAL, SHADOW_IN, * SHADOW_OUT, SHADOW_NONE y VERTICAL). * * BORDER crea una etiqueta con borde. * CENTER, LEFT y RIGHT alinean el texto de la etiqueta con respecto a la etiqueta. * WRAP hace que el texto dentro de la etiqueta se pueda dividir en varias líneas, si * resulta necesario. * SHADOW_IN (usado con SEPARATOR) crea un separador que parece empotrado * en la ventana. Ejemplo 9: EjemploEtiqueta.java
  • 51. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 51 de 114 * SHADOW_OUT (usado con SEPARATOR) crea un separador que parece sobresalir * de la ventana. * SHADOW_NONE (usado con SEPARATOR) crea un separador sin sombra. * SEPARATOR y VERTICAL generan una línea vertical. * SEPARATOR y HORIZONTAL generan una línea horizontal. * */ public class EjemploEtiqueta { public static void main(String[] args) { Display display = new Display(); Shell shell = new Shell(); shell.setLayout(new GridLayout(1, false)); shell.setText("Ejemplo de etiquetas"); // Se crea una etiqueta. Label etiqueta1 = new Label(shell, SWT.NONE); etiqueta1.setText("Etiqueta plana"); // Se crea una etiqueta con borde Label etiqueta2 = new Label(shell, SWT.BORDER); etiqueta2.setText("Etiqueta con borde"); // Se crea un separador. Label etiqueta3 = new Label(shell, SWT.SEPARATOR); // Se crea un separador horizontal con sombra. Label etiqueta4= new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.SHADOW_IN); etiqueta4.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); // Se crea una etiqueta con el logotipo de Eclipse. Image imagen = new Image(display, "c:Eclipse.jpg"); Label etiqueta5 = new Label(shell, SWT.NONE); etiqueta5.setImage(imagen); shell.open(); shell.pack(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } }
  • 52. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 52 de 114 Figura 35. Resultado de ejecutar la clase EjemploEtiqueta import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; /** * Esta clase genera una lista con tres elementos. * * La clase List admite los siguientes estilos: BORDER, H_SCROLL, * V_SCROLL, SINGLE y MULTI. * * BORDER crea un borde alrededor de la lista. Ejemplo 10: EjemploLista1.java
  • 53. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 53 de 114 * H_SCROLL crea una barra de desplazamiento horizontal para la lista. * V_SCROLL crea una barra de desplazamiento vertical para la lista. * MULTI permite seleccionar a la vez varios elementos. */ public class EjemploLista1 { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 300); shell.setText("Ejemplo 1 de lista"); List lista = new List(shell, SWT.SINGLE | SWT.BORDER); lista.setBounds(40, 40, 80, 60); lista.add("Bolígrafo"); lista.add("Lápiz"); lista.add("Calculadora"); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } } Figura 36. Resultado de ejecutar la clase EjemploLista1
  • 54. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 54 de 114 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; /** * Esta clase genera una lista con tres elementos que admite selecciones * múltiples. */ public class EjemploLista2 { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 250); shell.setText("Ejemplo 2 de lista"); // El objeto Lista debe definirse como final si se quiere acceder a él desde // el método addSelectionListener(). El argumento SWT.MULTI hace que // se puedan seleccionar varios elementos a la par. final List lista = new List(shell, SWT.MULTI | SWT.BORDER); lista.setBounds(40, 40, 80, 60); lista.add("Bolígrafo"); lista.add("Lápiz"); lista.add("Calculadora"); Button boton = new Button(shell, SWT.PUSH | SWT.BORDER); boton.setBounds(50, 150, 180, 25); boton.setText("Seleccione un elemento de la lista"); // Gestión de sucesos: si se presiona el botón se muestran los // elementos seleccionados. El método getSelection() devuelve // un vector de Strings. boton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { String elementosSeleccionados[] = lista.getSelection(); for (int i = 0; i< elementosSeleccionados.length; i++) { System.out.println(elementosSeleccionados[i]); } } }); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } } Ejemplo 11: EjemploLista2.java
  • 55. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 55 de 114 Figura 37. Resultado de ejecutar la clase EjemploLista2 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; /** * Esta clase genera una lista con un menú contextual (pop-up) que almacena dos * menús, uno de ellos con dos submenús. * */ public class EjemploLista3 { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 250); shell.setText("Ejemplo 3 de lista"); List lista = new List(shell, SWT.SINGLE | SWT.BORDER); lista.setBounds(80, 40, 100, 60); lista.add("Elemento 1"); lista.add("Elemento 2"); lista.add("Elemento 3"); // Se crea el menú y se añade a la lista. Menu menu = new Menu(shell, SWT.POP_UP); lista.setMenu(menu); Ejemplo 12: EjemploLista3.java
  • 56. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 56 de 114 // Se crea el menú Archivo. MenuItem archivo = new MenuItem(menu, SWT.CASCADE); archivo.setText("Archivo"); Menu menuArchivo = new Menu(menu); archivo.setMenu(menuArchivo); // Se añaden al menú Archivo los submenús Nuevo y Cerrar MenuItem nuevo = new MenuItem(menuArchivo, SWT.CHECK); nuevo.setText("Nuevo"); MenuItem cerrar = new MenuItem(menuArchivo, SWT.CHECK); cerrar.setText("Cerrar"); // Se crea el menú Salir MenuItem salir = new MenuItem(menu, SWT.PUSH); salir.setText("Salir"); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } } Figura 38. Resultado de ejecutar la clase EjemploLista3
  • 57. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 57 de 114 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; /** * Esta clase muestra un cuadro combinado en que el usuario puede introducir * elementos hasta alcanzar el valor límite LIMITE. Una vez alcanzado el límite, * por cada elemento que se añada se borrará uno de los que ya había. * * La clase Combo admite los siguientes estilos: BORDER, DROP_DOWN, * READ_ONLY y SIMPLE. * * BORDER crea un borde alrededor del cuadro combinado. * DROP_DOWN crea un cuadro combinado desplegable. * READ_ONLY crea un cuadro combinado que no permite que el usuario introduzca nuevos * elementos o modifique los ya existentes. * SIMPLE crea un cuadro de texto en el que los elementos del cuadro siempre se muestran * como una lista. * */ public class EjemploCuadroCombinado { // Número máximo de elementos que admite el cuadro combinado. final static int LIMITE = 5; public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(210, 100); shell.setText("Ejemplo de cuadro combinado"); final Combo cuadroComb = new Combo(shell, SWT.BORDER | SWT.DROP_DOWN); cuadroComb.setSize( 200, 50); // Tratamiento de sucesos: mientras no se alcance LIMITE, el texto que // introduce el usuario se van añadiendo a la lista de elementos // susceptibles de ser seleccionados. cuadroComb.addListener(SWT.DefaultSelection,new Listener() { public void handleEvent(Event event) { String texto = cuadroComb.getText(); if ( (cuadroComb.indexOf(texto) == -1) && (cuadroComb.getItemCount() >= LIMITE) ) { cuadroComb.remove(0); } cuadroComb.add(texto); } }); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); Ejemplo 13: EjemploCuadroCombinado.java
  • 58. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 58 de 114 } } } } Figura 39. Resultado de ejecutar la clase EjemploCuadroCombinado import org.eclipse.swt.SWT; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; /** * Esta clase muestra una tabla con cuatro ciudades europeas y su distancia a Madrid. * * La clase Table admite los siguientes estilos: BORDER, H_SCROLL, V_SCROLL, SINGLE, * MULTI, CHECK, FULL_SELECTION y HIDE_SELECTION. * * BORDER crea una tabla con borde. * H_SCROLL crea una tabla con una barra de desplazamiento horizontal. * V_SCROLL crea una tabla con una barra de desplazamiento vertical. * SINGLE crea una tabla que no permite seleccionar a la vez más de una celda. * MULTI crea una tabla que permite la selección de varias celdas a la vez. * CHECK crea una tabla con cuadros de verificación. * FULL_SELECTION crea una tabla en la que, al seleccionar una celda, se selecciona * toda la fila. * HIDE_SELECTION crea una tabla que esconde las celdas seleccionadas cuando la * la tabla pierde el foco. * * La clase TableColum representa una columna de una tabla y admite tres estilos: LEFT, * RIGHT y CENTER, que alinean la columna a la izquierda, a la derecha o al centro. * * La clase TableItem representa una fila de una tabla y no admite ningún estilo. */ public class EjemploTabla1 { public static void main(String args[]) { Ejemplo 14: EjemploTabla1.java
  • 59. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 59 de 114 Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 250); shell.setText("Ejemplo 1 de tabla"); // Una objeto Table es un widget compuesto. Por ello, conviene usar // algún tipo de esquema (layout). En este caso se usará // el esquema de tipo FillLayout, que distribuye // equitativamente entre los componentes el espacio disponible. shell.setLayout(new FillLayout()); // Se crea la tabla, que permite la selección múltiple de filas. Table tabla = new Table(shell, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); tabla.setHeaderVisible(true); tabla.setLinesVisible(true); // Se crean las columnas. TableColumn columna1 = new TableColumn(tabla, SWT.CENTER); TableColumn columna2 = new TableColumn(tabla, SWT.CENTER); // Se establece el ancho y el nombre de cada columna. columna1.setText("Ciudad"); columna1.setWidth(90); columna2.setText("Distancia"); columna2.setWidth(90); // Se crean y se rellenan las filas. TableItem fila1 = new TableItem(tabla, SWT.NONE); String tmp1[] = {"París", "1100 km"}; fila1.setText(tmp1); TableItem fila2 = new TableItem(tabla, SWT.NONE); String tmp2[] = {"Luxemburgo", "1600 km"}; fila2.setText(tmp2); TableItem fila3 = new TableItem(tabla, SWT.NONE); String tmp3[] = {"Berlín","1800 km"}; fila3.setText(tmp3); // Otra manera de rellenar las filas. TableItem fila4 = new TableItem(tabla, SWT.NONE); fila4.setText(0, "Atenas"); fila4.setText(1, "3500 km"); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } }
  • 60. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 60 de 114 Figura 40. Resultado de ejecutar la clase EjemploTabla1 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.layout.*; /** * Esta clase muestra por consola el contenido de todas las celdas de la fila seleccionada por * el usuario. * */ public class EjemploTabla2 { public static void main (String [] args) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 300); shell.setText("Ejemplo 2 de tabla"); // Tabla que admite la selección simultánea de varios elementos. final Table tabla = new Table (shell, SWT.BORDER | SWT.V_SCROLL | SWT.FULL_SELECTION); tabla.setLinesVisible (true); tabla.setHeaderVisible (true); // Se da nombre a las columnas. for (int i = 1; i < 3; i++) { Ejemplo 15: EjemploTabla2.java
  • 61. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 61 de 114 TableColumn columna = new TableColumn (tabla, SWT.NULL); columna.setText ("Columna " + i); } // Se rellena la tabla. for (int i = 1; i < 11; i++) { TableItem celda = new TableItem (tabla, SWT.NONE); celda.setText (0, "celda (" + i + " ,1)"); celda.setText (1, "celda (" + i + " ,2)"); } // Se vuelve a calcular el ancho preferido de cada columna. for (int i = 0; i < 2; i++) { tabla.getColumn (i).pack(); } tabla.setSize (800, 800); // Gestión de sucesos: al seleccionar una celda se muestra por consola su texto. tabla.addListener (SWT.Selection, new Listener () { public void handleEvent (Event suceso) { String celda = null; TableItem celdas[] = tabla.getSelection(); if ( (celdas != null) && (celdas.length >= 1) ) { System.out.println("Se han seleccionado las celdas: "); System.out.println (celdas[0].getText(0)); System.out.println (celdas[0].getText(1)); } } }); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } }
  • 62. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 62 de 114 Figura 41. Resultado de ejecutar la clase EjemploTabla2 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.layout.*; /** * Esta clase muestra un panel de lengüetas (TabFolder) con tres lengüetas. Una contiene * un cuadro desplegable (Combo); otra, una etiqueta (Label), y la otra un cuadro de texto (Text). */ public class EjemploTabFolder { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 300); shell.setText("Ejemplo de panel de lengüetas (TabFolder)"); shell.setLayout(new FillLayout()); // Se crea el panel de lengüetas TabFolder panelLengueta = new TabFolder(shell, SWT.NONE); Ejemplo 16: EjemploTabFolder.java
  • 63. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 63 de 114 // Se crea una lengüeta y se llena con un cuadro desplegable. TabItem lengueta1 = new TabItem(panelLengueta, SWT.NONE); lengueta1.setText("Lengüeta 1"); Combo desplegable = new Combo(panelLengueta, SWT.BORDER); desplegable.add("Soy un cuadro desplegable"); lengueta1.setControl(desplegable); // Se crea una lengüeta y se llena con una etiqueta. TabItem lengueta2 = new TabItem(panelLengueta, SWT.NONE); lengueta2.setText("Lengüeta 2"); Label etiqueta = new Label(panelLengueta, SWT.BORDER); etiqueta.setText("Soy una etiqueta"); lengueta2.setControl(etiqueta); // Se crea una lengüeta y se llena con un cuadro de texto. TabItem lengueta3 = new TabItem(panelLengueta, SWT.NONE); lengueta3.setText("Lengüeta 3"); Text cuadroTexto = new Text(panelLengueta, SWT.NONE); cuadroTexto.setText("Soy un cuadro de texto"); lengueta3.setControl(cuadroTexto); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } } Figura 42. Resultado de ejecutar la clase EjemploTabFolder
  • 64. El archipiélago Eclipse (parte 4 de 4) © Miguel Ángel Abián, 2005-2014 Página 64 de 114 import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.layout.*; /** * Esta clase genera un árbol del que cuelgan varios nodos. * * La clase Tree actúa como un contenedor de objetos TreeItem. * La clase Tree admite los siguientes estilos: BORDER, * H_SCROLL, V_SCROLL, SINGLE, MULTI y CHECK. * * BORDER crea un árbol con borde. * H_SCROLL y V_SCROLL crean un árbol con una barra de desplazamiento horizontal o * vertical, respectivamente * SINGLE crea un árbol que sólo permite seleccionar un nodo o elemento a la vez. * MULTI crea un árbol que permite que se seleccionen a la vez varios nodos o elementos. * CHECK crea un árbol con cuadros de verificación. * * Los métodos más usados de Tree son: * 1) getSelection() devuelve un vector con los TreeItems que están seleccionados. * 2) getSelectionCount() devuelve el número de elementos seleccionados. * 3) removeAll() borra todos los elementos del árbol. * 4) selectAll() selecciona todos los elementos del árbol. * 5) deselectAll() quita la selección de todos los elementos seleccionados del árbol. * 6) setSelection(TreeItem[]) Seleccionan los elementos establecidos en el vector pasado como * argumento. * * Los métodos más usados de TreeItem son: * 1) getChecked() devuelve true si el elemento está marcado, y false en caso contrario. * 2) getExpanded () devuelve true si el elemento está expandido, y false en caso contrario. * 3) setChecked (boolean) establece si el elemento está marcado o no. * 4) setExpanded(boolean) establece si el elemento está expandido o no. * 5) setImage(Image) establece una imagen para el elemento. Si el argumento es null, no se * mostrará ninguna imagen. * 4) setText(String) establece el texto del elemento. */ public class EjemploArbol { public static void main(String args[]) { Display display = new Display(); Shell shell = new Shell(display); shell.setSize(300, 300); shell.setText("Ejemplo de árbol"); shell.setLayout(new FillLayout()); // Se crea el árbol. Tree arbol = new Tree(shell, SWT.BORDER); // Se crea el nodo 1 y sus subnodos. TreeItem nodo1 = new TreeItem(arbol, SWT.NONE); Ejemplo 17: EjemploArbol.java
  • 65. http://www.javahispano.org © Miguel Ángel Abián, 2005-2014 Página 65 de 114 nodo1.setText("Nodo 1"); TreeItem nodo11 = new TreeItem(nodo1, SWT.NONE); nodo11.setText("Nodo 1.1"); TreeItem nodo111 = new TreeItem(nodo11, SWT.NONE); nodo111.setText("Nodo 1.1.1"); // Se crea el nodo 2 y sus subnodos. TreeItem nodo2 = new TreeItem(arbol, SWT.NONE); nodo2.setText("Nodo 2"); TreeItem nodo21 = new TreeItem(arbol, SWT.NONE); nodo21.setText("Nodo 2.1"); TreeItem nodo22 = new TreeItem(arbol, SWT.NONE); nodo22.setText("Nodo 2.2"); // Se expande el nodo 1 y su subnodo. nodo1.setExpanded( true); nodo11.setExpanded(true); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } } Figura 43. Resultado de ejecutar la clase EjemploArbol