/* * 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