/*
* OkosAWT.java
* néhány segédosztály az AWT inkompatibilitásai egy részének kivédésére
* by pts@fazekas.hu at Sat Jun 30 12:58:54 CEST 2001
* további doksi: a README-ben és e file legkülső class-ának fejkommentjében
*
* Kincskereső Kisgömböc (C) Early May 2001 by eNTitánok (Rév Szilvia,
* Szabó Péter <pts@inf.bme.hu>, Szurdi Miklós, Weizengruber Attila).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package eNTitanok.gfx;
import java.applet.Applet;
import java.awt.Component;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.TextField;
import java.awt.TextArea;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Insets;
import java.awt.Window;
import java.awt.Dimension;
import java.awt.Panel;
import java.awt.Font;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;
import java.io.InputStream;
/**
* Néhány segédosztály az AWT hülyeségei és inkompatibilitásai egy részének
* kivédésére.
*/
public class OkosAWT {
/**
* Akkor váltódik ki, ha egy egyszeres értékadású változónak többször
* akarunk értéket adni.
*/
public static class TobbszorError extends Error {}
/**
* createImage() hívásokhoz használjuk.
*/
private static Component mainComponent;
private static Toolkit mainToolkit;
/**
* @param mainComponent Általában Applet-et szokás megadni, mert annak
* van getCodeBase() metódusa.
*/
public static void setMainComponent(Component mainComponent_) {
if (mainComponent_==null) return;
if (mainComponent!=null && mainComponent!=mainComponent)
throw new TobbszorError();
mainComponent=mainComponent_;
mainToolkit=mainComponent.getToolkit();
if (mainToolkit==null) mainToolkit=Toolkit.getDefaultToolkit();
if (mainToolkit==null) throw new NullPointerException();
}
public static Toolkit getMainToolkit() {
return mainToolkit==null?Toolkit.getDefaultToolkit():mainToolkit;
}
public static Component getMainComponent() {
return mainComponent;
}
public static ClassLoader getMainClassLoader() {
return mainComponent.getClass().getClassLoader();
}
/**
* Csak akkor változtatja meg, ha null volt.
*/
public static void setMainComponentWeak(Component mainComponent_) {
if (mainComponent==null) setMainComponent(mainComponent_);
}
/**
* mainComponent.getCodeBase()-hez relatívan egy kép betöltését kezdeményezi
* (getImage() és prepareImage()). mainComponent a hívás előtt legyen
* beállítva.
*/
public static Image getImageBase(String filenev) {
Image kep;
if (mainComponent instanceof Applet) {
Applet ap=(Applet)mainComponent;
kep=ap.getCodeBase()==null?
getMainToolkit().getImage(filenev):
ap.getImage(ap.getCodeBase(), filenev);
} else kep=getMainToolkit().getImage(filenev);
mainComponent.prepareImage(kep, mainComponent); // azonnal megkezdi a letöltést
return kep;
} // getImageBase()
/**
* A megadott, a programkód mellett található erőforrásfile-t adja vissza.
* @param eroforras / jelekkel elválasztott, kiterjesztéssel rendelkező,
* relatív filenév.
* @return <code>null</code>, ha nincs ilyen erőforrás
*/
public static InputStream getEroforrasStreamOf(ClassLoader classLoader, String eroforras) {
return (classLoader==null)?ClassLoader.getSystemResourceAsStream(eroforras)
:classLoader.getResourceAsStream(eroforras);
}
/**
* A megadott, a programkód mellett található erőforrásfile tartalmát adja
* vissza.
* @param eroforras / jelekkel elválasztott, kiterjesztéssel rendelkező,
* relatív filenév.
* @return <code>null</code>, ha nincs ilyen erőforrás
*/
public static byte[] getEroforrasByteTOf(ClassLoader classLoader, String eroforras) {
// az InputStream-ből amúgy is byte-okat lehet olvasni
byte jott[]=new byte[4096];
byte t[]=new byte[4096];
int hossz;
int len;
hossz=0;
try {
InputStream is=getEroforrasStreamOf(classLoader, eroforras);
while ((len=is.read(jott))>1) {
while (len+hossz>t.length) {
byte tregi[]=t;
t=new byte[2*tregi.length];
System.arraycopy(tregi,0,t,0,hossz);
}
System.arraycopy(jott,0,t,hossz,len);
hossz+=len;
}
is.close();
byte ret[]=new byte[hossz];
System.arraycopy(t,0,ret,0,hossz);
return ret;
} catch (Exception e) {
return null;
}
} /// getEroforrasByteT()
/**
* A megadott, a programkód mellett található erőforrásfile tartalmát adja
* vissza. A visszaadott karakterek 0..255-ig terjednek.
* @param eroforras / jelekkel elválasztott, kiterjesztéssel rendelkező,
* relatív filenév.
* @return <code>null</code>, ha nincs ilyen erőforrás
*/
public static String getEroforrasStringOf(ClassLoader classLoader, String eroforras) {
byte r[]=getEroforrasByteTOf(classLoader, eroforras);
return r==null?null:new String(r);
} /// getEroforrasString()
public static class OkosCanvas extends Canvas {
protected Dimension dim;
protected int w, h;
protected Font font2;
public void setFont2(Font font2) { this.font2=font2; }
public Font getFont2() { return font2; }
protected Dimension getDim() {
if (dim==null) dim=new Dimension(w,h);
return dim;
}
public Dimension getPreferredSize() { return getDim(); }
public void setSize(int w, int h) {
dim=new Dimension(w,h);
this.w=w; this.h=h;
super.setSize(w,h);
}
public void atmeretez(int w, int h) { setSize(w, h); }
public void setSize(Dimension d) { setSize(d.width, d.height); }
} /// class OkosCanvas
/**
* Annyival több (kevesebb), mint az OkosCanvas, hogy csak fix pixelméretben
* hajlandó megjelenni.
*/
public static class OkosFixCanvas extends OkosCanvas {
public Dimension getMinimumSize() { return getDim(); }
public Dimension getMaximumSize() { return getDim(); }
} /// class OkosFixCanvas
public static class OkosPanel extends Panel {
protected int w, h;
public Dimension getPreferredSize() {
return new Dimension(w, h);
}
public void setSize(int w, int h) {
this.w=w; this.h=h;
super.setSize(w,h);
}
public void setSize(Dimension d) { setSize(d.width, d.height); }
}
/**
* Néhány hasznos, technikai jellegű funkcióval egészíti ki az Applet
* osztályt. Támogatást nyújt Applet-ek application-ből történő létrehozáshoz.
*/
public static class OkosApplet extends Applet {
protected int w, h;
protected boolean application_e;
// ^^^ az applet-et kézzel, application-ből hoztuk-e létre?
protected Font font2;
public void setFont2(Font font2) { this.font2=font2; }
public Font getFont2() { return font2; }
public Dimension getPreferredSize() {
return new Dimension(w, h);
}
public void setSize(int w, int h) {
this.w=w; this.h=h;
super.setSize(w,h);
}
/*/ // deprecation miatt nem
public void resize(int w, int h) {
// by pts@fazekas.hu at Sun May 13 21:06:05 CEST 2001
setSize(w, h);
}
/***/
public void setSize(Dimension d) { setSize(d.width, d.height); }
public OkosApplet() { this(false); }
public OkosApplet(boolean application_e) {
OkosAWT.setMainComponentWeak(this);
this.application_e=application_e;
}
public java.net.URL getCodeBase() {
// JDK1.1 bugyuta, hibát jelezne
return application_e?null:super.getCodeBase();
}
/**
* A megadott, a programkód mellett található erőforrásfile-t adja vissza.
* @param eroforras / jelekkel elválasztott, kiterjesztéssel rendelkező,
* relatív filenév.
* @return <code>null</code>, ha nincs ilyen erőforrás
*/
public InputStream getEroforrasStream(String eroforras) {
return getEroforrasStreamOf(getClass().getClassLoader(), eroforras);
}
/**
* A megadott, a programkód mellett található erőforrásfile tartalmát adja
* vissza.
* @param eroforras / jelekkel elválasztott, kiterjesztéssel rendelkező,
* relatív filenév.
* @return <code>null</code>, ha nincs ilyen erőforrás
*/
public byte[] getEroforrasByteT(String eroforras) {
return getEroforrasByteTOf(getClass().getClassLoader(), eroforras);
} /// getEroforrasByteT()
/**
* A megadott, a programkód mellett található erőforrásfile tartalmát adja
* vissza. A visszaadott karakterek 0..255-ig terjednek.
* @param eroforras / jelekkel elválasztott, kiterjesztéssel rendelkező,
* relatív filenév.
* @return <code>null</code>, ha nincs ilyen erőforrás
*/
public String getEroforrasString(String eroforras) {
return getEroforrasStringOf(getClass().getClassLoader(), eroforras);
} /// getEroforrasString()
} /// class OkosApplet
/**
* Az OkosFrame egy olyan Frame (kerettel és esetleg menüsorral rendelkező,
* ,,toplevel'' ablak), ami egyetlen :Component-et jelenít meg önmagán
* belül, mégpedig úgy, hogy semmi (azaz 0, azaz nulla pixelnyi) helyet
* hagy a saját széle és a Component széle között. Tehát az :OkosFrame
* mérete (amit a .getSize() visszaad) épp annyival nagyobb a
* :Component méreténél, amennyit az ablak címsora és kerete elfoglal.
*
* Sajnos JDK1.3-ban ezt megváltoztatták, úgyhogy ott az OkosFrame nagyobb
* ablakot hoz létre a szükségesnél.
*
* A :Component méretét a programból az :OkosFrame setSizeC()
* metódusával érdemes állítani, mert ez a metódus a :Component méretét
* a megadott értékre változtatja, _és_ az :OkosFrame méretét ennek
* megfelelően állítja. A :Component setSize() és resize() metódusait
* érdemes elkerülni. Az :OkosFrame setSize() metódusát tilos meghívni,
* mert félreértésre adhat okot, hogy mit csinál (helyette: setSizeC()
* vagy setSizeF()). Ha a felhasználó az :OkosFrame-et (pl. az egérrel)
* átméretezi, akkor vele együtt nő a
* :Component mérete is.
*
* A :Component getPreferredSize() függvényének a kívánt méretet kell
* visszadnia. Ez teljesül például :TextField-re (setColumns() után).
* Ez :Panel, :Canvas és :Applet objektumokra nem teljesül, mert ezek
* olyan bugyuták, hogy mindig az aktuális méretük jó nekik. Tehát az
* _NEM_ fog működni, ha inicializáláskor meghívjuk a :Canvas setSize()
* függvényét, mivel a :Canvas ezt a méretet néhány röpke pillanat alatt
* elfelejti. Canvas helyett érdemes OkosCanvas-t használni.
*
* A egyszerű programozó elsőre azt gondolná, hogy az OkosFrame által
* ellátott egyszerű, alapvető feladatot a Java AWT alapból tudja, és nem
* kell rengeteg extra osztályt létrehozni... De kell.
*/
public static class OkosFrame extends Frame implements WindowListener, ComponentListener {
public static class NeeztError extends Error {}
protected int dx, dy; // mennyivel nagyobb az :OkosFrame, mint a :Component
protected boolean dok;
protected boolean nyitva;
public static final int A_SEMMI=0, A_BEZAR=1, A_KILEP=2, A_APPLETKILEP=3;
protected int autoKilep; // :OkosFrame bezárásakor program vége?
protected Component c;
public OkosFrame(String title, Component c, int autoKilep) { super(title); init2(c); this.autoKilep=autoKilep; }
public OkosFrame(String title, Component c) { super(title); init2(c); }
public OkosFrame(Component c) { super(); init2(c); }
private void init2(Component c) {
OkosAWT.setMainComponentWeak(c);
// ^^^ nem this, hanem c. (bár sehol sem használjuk ki, de így konzisztens
// az OkosApplet-tel)
this.c=c;
autoKilep=c instanceof Applet ? A_SEMMI : A_KILEP;
dok=false; dx=40; dy=40; // ha nem elég nagy, bizonyos események meg se hívódnak :-((
// ^^^ majd az első megjelenítéskor kiderül az igazi
Dimension d=getPreferredSize();
// setResizable(true);
setSizeF(d);
// setBackground(Color.pink);
// ^^^ debug célokra. Néha látszódhat (pl. TextField-nél 1 pixel vastagon)
setLayout(new BorderLayout(0,0));
/* ^^^ BorderLayout: tiszta, jól működő, kiszámítható, testreszabható
* LayoutManager. Nagy hátránya, hogy legfeljebb 5 komponenst képes
* kezelni. Csak a CENTER-be rakott komponens méreteződik át mindkét
* irányban. (Ezt az automatikus átméreteződést semelyik más
* LayoutManager-nél nem sikerült elérnem. Régi szép, TCL-es idők. Az ember
* elolvasta a pack(n) man page-et, megértette, hogy működnek a dolgok,
* kigondtolta, kipróbálta, és tényleg működtek. Most az ember elolvassa a
* 3 Java könyvet, megnézi a példaprogramokat, visszafejti a JDK-s .class
* file-okat, és még mindig nem világos, hogyan lehetne megoldani azt az
* egyszerű, mindennapos :Component-elhelyezési stratégiát, amihez TCL-ben
* mindösse 2 sort kellet írni.)
*/
addWindowListener(this);
addComponentListener(this);
//super.setSize(100,100);
add(c);
//invalidate();
// pack();
}
public Dimension getPreferredSize() {
Dimension d=c.getPreferredSize();
return new Dimension(dx+d.width, dy+d.height);
}
public Dimension getMinimumSize() {
Dimension d=c.getMinimumSize();
return new Dimension(dx+d.width, dy+d.height);
}
public Dimension getMaximumSize() {
Dimension d=c.getMaximumSize();
return new Dimension(dx+d.width, dy+d.height);
}
private void szamol() {
/* Kiszámoljuk dx-et és dy-t. A Java hülyesége miatt ezt csak akkor
* tudjuk megtenni, ha az ablak már megnyílt, és a componentResized()
* meghívdott.
*/
if (!nyitva || dok) return;
// System.out.println("wo F.w="+this.getSize().width+" F.h="+this.getSize().height);
// System.out.println("wo L.w="+c.getSize().width+" L.h="+c.getSize().height);
Dimension dc=c.getSize();
Dimension d=this.getSize();
dx=d.width-dc.width;
dy=d.height-dc.height;
// System.out.println("dx="+dx+" dy="+dy);
dok=true;
meretKovet();
}
public void meretKovet() {
Dimension dc=c.getPreferredSize();
// System.out.println(dc.width); System.out.println(dc.height);
// Brr, többszálúság.
synchronized(this) {
boolean b=isResizable();
setResizable(true);
super.setSize(dc.width+dx, dc.height+dy);
setResizable(b);
}
}
//public void setSize(int w, int h) {
// throw new NeeztError(); // "hivd setSizeF()-et vagy setSizeC()-t");
//}
public void setSize(Dimension d) { setSize(0,0); }
public void setSizeF(int w, int h) {
/* @param (w,h): az :OkosFrame új mérete */
c.setSize(w-dx, h-dy);
super.setSize(w,h);
}
public void setSizeF(Dimension d) { setSizeF(d.width,d.height); }
public void setSizeC(int w, int h) {
/* @param (w,h): a :Component új mérete */
c.setSize(w,h);
super.setSize(w+dx, h+dy);
}
public void setSizeC(Dimension d) { setSizeC(d.width,d.height); }
// validate() és invalidate() átdefiniálható, meg is hívódnak, de nem
// lehet konzisztensen (vagy csak egérrel lesz átméretezhető, vagy csak
// programból)
// public void validate() { System.out.println("VAL."); super.validate(); }
// public void invalidate() { System.out.println("INVAL."); meretKovet(); super.invalidate(); }
public void componentShown(ComponentEvent e) {}
public void componentHidden(ComponentEvent e) {}
public void componentResized(ComponentEvent e) {
// validate();
// System.out.println("RRR");
//nyitva=true; // Win32 JDK1.2.2 alatt lehet, Linux JDK1.1.7 alatt nem
szamol();
}
public void componentMoved(ComponentEvent e) {}
public void windowOpened(WindowEvent e) {
/* az ablak első megnyílása után határahozható csak meg dx és dy. Tehát
* figyeljük az eseményt, és megtesszük.
*/
nyitva=true;
super.setSize(getPreferredSize()); // Win32 JDK1.2.2 miatt kell
// validate();
// System.out.println("ZZZ");
}
public void windowClosing(WindowEvent e) {
if (autoKilep==A_BEZAR) ((Frame)e.getComponent()).setVisible(false);
else if (autoKilep==A_KILEP) System.exit(0);
else if (autoKilep==A_APPLETKILEP) {
((Applet)c).stop();
((Applet)c).destroy();
System.exit(0);
}
}
public void windowClosed(WindowEvent e) { dok=false; nyitva=false; }
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
} /// class OkosFrame
} /// class OkosAWT