/*
 * Skel.java
 * egy szkeleton megjelenítője: application és applet is, JDK1.2.2 szükséges
 * by pts@fazekas.hu at Wed Mar 21 18:25:59 CET 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.io.StringWriter;
import java.io.PrintWriter;
import java.applet.Applet;
import java.util.StringTokenizer;
import java.util.Vector;
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.event.WindowAdapter;
import java.awt.Label;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Toolkit;
import java.awt.IllegalComponentStateException;
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.awt.event.FocusEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyListener;
import eNTitanok.gfx.OkosAWT.OkosApplet;
import eNTitanok.gfx.OkosAWT.OkosFrame;
import eNTitanok.gfx.OkosAWT.OkosCanvas;

/**
 * Ez a file tartalmazza egy (akármilyen) szkeleton program megjelenítéséért
 * felelős részeit. A futtatáshoz JDK1.2.2 kell, mert a JDK1.1.7-ben teljesen
 * hibás (bugos, deadlock-os) a java.awt.TextField widget. Az itt megvalósított
 * szkeleton modell az eljárások neveit a Java metódushívási fa elemzésével
 * állapítja meg, amibe nagyon bekavarnak az öröklésék és a JIT optimalizáció.
 * Érdemes tehát <code>javac -g</code>-vel fordítani.
 *
 * <P>Próbáltam a Linux-os JDK1.1.7-tel, de a program végtelen ciklusba került
 * ha TextField.setEditable()-t vagy TextField.getText()-et hívtam. JDK1.2-vel
 * pedig tökéletesen futott. Ezen a ponton feladtam a JDK1.1-gyel való
 * kompatibilitást -- ha annyira bugos egy implementáció, hogy egy
 * stringlekérdező függvényre lefagy a program, arra nem lehet fejleszteni.
 * Ki tudja, hogy még mennyi felfedezetlen bug várt volna rám.
 */
public class Skel extends OkosApplet implements Runnable {
  public static class StackKeret {
    public static final String CONSTRUCTOR="<constructor>";
    protected String klassz, metodus, filenev;
    protected int sorszam;
    StackKeret(String klassz, String metodus) { this.klassz=klassz; this.metodus=metodus; }
    public String getKlassz() { return klassz; }
    public String getMetodus() { return metodus; }
    public String getFilenev() { return filenev; }
    public int getSorszam() { return sorszam; }
    public void setFilenev(String filenev) { this.filenev=filenev; }
    public void setMetodus(String metodus) { this.metodus=metodus; }
    public void setSorszam(int sorszam) { this.sorszam=sorszam; }
    public String toString() { return "[at "+klassz+"."+metodus+"("+filenev+":"+sorszam+")]"}
    public String toString2() { return klassz+"."+metodus+" ["+filenev+":"+sorszam+"]"}
  }
  public static StackKeret[] honnanHivtak(int kezd) {
    /* @param kezd: a tetejéről számított hányadik indextől adjuk vissza.
     *   ha kezd==0, akkor egy honnanHivtak-os sor lesz a 0. visszadott elem.
     * @return null, ha a Java nem támogatja a sorszám-debug-ot
     */
    StringWriter sw=new StringWriter();
    PrintWriter pw=new PrintWriter(sw,true);
    try { throw new Exception("honnanTeszt")}
    catch (Exception e) { e.printStackTrace(pw)}
    String ss=sw.toString();
    // sw.toString() tipikus kimenete (Sun JDK1.1.7):
    //   java.lang.Exception: ...
    //    at Skel.honnanHivtak(Skel.java:16)
    //    at Skel.hejho(Skel.java:22)
    //    at Skel.init(Skel.java:26)
    //    at sun.applet.AppletPanel.run(AppletPanel.java:281)
    //    at java.lang.Thread.run(Thread.java)
    // vvv Most pedig foggal-körömmel-kisbaltával feldolgozzuk ss-et, és
    //     jól kezelhető StackKeret objektumokat hozunk létre. Ez különösen
    //     fáradságos, méltatlan, hálátlan, pilinszázós feladat, mivel
    //     Java-ban nincsenek regexp-ek (miért is lennének?? miért is kapjon
    //     a programozó végre egy hatékony eszközt a kezébe??).
    if (ss.startsWith("java.lang.Exception")) {
      int i=ss.indexOf('\n');
      if (i==-1) return null;
      ss=ss.substring(i+1);
    }
    // System.out.println(ss);
    StringTokenizer st=new StringTokenizer(ss, "\010\011\012\013\014\015\000\n\r\t \"~!@#%^&*()+|{}:<>?`-=[];',",true);
    // ^^^ kimaradtak: . / \ $ _
    Vector v=new Vector()// elemei: StackKeret
    final int KLASSZ=1, FILENEV=2, SORSZAM=3, FILENEV_NYIT=4, SORSZAM_NYIT=5, SOREMELES=6;
    int varjuk=KLASSZ;
    StackKeret k=null;
    while (st.hasMoreTokens()) {
      String s=st.nextToken();
      char c=s.charAt(0);
      if (c==010 || c==011 || c==040 || c==0 || c=='\t') continue;
      // ^^^ nem sortörő white space kihagyása
      if (c>=010 && c<=015 || c=='\n' || c=='\r') c='\n';
      // ^^^ sortörő white space konvertálása '\n'-né
      if (c=='\\') c='/';
      if (c=='\n') {
        if (k!=null) { v.addElement(k); k=null}
        varjuk=KLASSZ;
        continue;
      }
      boolean nev_e=Character.isJavaIdentifierStart(c);
      //System.out.println(varjuk);
      //System.out.println(nev_e);
      if (varjuk==KLASSZ && nev_e) {
        if (s.equals("at") || s.equals("in") || s.equals("from")) continue;
        int i=s.lastIndexOf('.');
        if (i==-1) { varjuk=SOREMELES; continue}
        varjuk=FILENEV_NYIT;
        k=new StackKeret(s.substring(0,i), s.substring(i+1));
        // System.out.println("klassz,metodus: "+s);
      } else if (varjuk==FILENEV_NYIT && c=='<') {
        k.setMetodus(StackKeret.CONSTRUCTOR);
      } else if (varjuk==FILENEV_NYIT && c=='(') {
        varjuk=FILENEV;
      } else if (varjuk==FILENEV && (nev_e||c=='/')) {
        if (s.equals("Compiled")) {
          k=null;
          varjuk=SOREMELES;
        } else {
          varjuk=SORSZAM_NYIT;
          // System.out.println("filenev: "+s);
          k.setFilenev(s);
        }
      } else if (varjuk==SORSZAM_NYIT && (c==')' || c==',')) {
        varjuk=SOREMELES;
        v.addElement(k); k=null;
      } else if (varjuk==SORSZAM_NYIT && c==':') {
        varjuk=SORSZAM;
      } else if (varjuk==SORSZAM && Character.isDigit(c)) {
        varjuk=SOREMELES;
        int i=0;
        try { i=Integer.parseInt(s)} catch (NumberFormatException e) {}
        // System.out.println("sorszam="+i);
        k.setSorszam(i);
        v.addElement(k); k=null;
      // } else { System.out.println(">"+s+"<");
      }
    }

    { // hack, hogy NT-s Java-n is menjen...
      int i=0;
      while (i<v.size() && !((StackKeret)v.elementAt(i)).getKlassz().equals("java.lang.Exception")) i++;
      if (i!=v.size()) {
        Vector ujv=new Vector();
        for (i+=1;i<v.size();i++) ujv.addElement(v.elementAt(i));
        v=ujv;
      }
    }

    if (v.size()<=kezd) return null;
    StackKeret t[]=new StackKeret[v.size()-kezd];
    for (int i=0;i<t.length;i++) t[i]=(StackKeret)v.elementAt(i+kezd);
    // for (int i=0;i<t.length;i++) System.out.println(">>>"+t[i]);
    return t;
  }
  public static StackKeret[] honnanHivtak() { return honnanHivtak(2)}

  // --- szekvenciadiagram rajzolása
  //     by pts@fazekas.hu at Sunday, March 25, 2001

  public static class SzekvenciaFejlec {
    public String klassz, klassz_kiir;
    public int idx;
    public int x; // középvonal x koordinátája megjelenítéshez
    public int van; // hány objektumpéldány létezik az adott klassz-ból
    public int fut; // hány metódus fut éppen az adott klassz objektumpéldányaiból
  }

  public static class SzekvenciaBejegyzes {
    public static final int TAVOLSAG=69; // 9*69==621
    public static final int SORTAV=30;
    private SzekvenciaFejlec fejlec;
    private int van, volt; // hány objektumpéldány létezik az adott klassz-ból
    private int fut, futott; // hány metódus fut éppen az adott klassz objektumpéldányaiból
    private boolean aktiv;
    public SzekvenciaBejegyzes(String klassz, String klassz_kiir, int idx) {
      fejlec=new SzekvenciaFejlec();
      fejlec.klassz=klassz;
      fejlec.klassz_kiir=klassz_kiir;
      fejlec.idx=idx;
      fejlec.x=TAVOLSAG/2+TAVOLSAG*idx;
      fejlec.van=this.van=this.volt=0;
      fejlec.fut=this.fut=this.futott=0;
      aktiv=false;
    }
    public SzekvenciaBejegyzes(SzekvenciaBejegyzes regi) {
      /* az új sorhoz tartozó bejegyzést állítjuk elő a régi sorhoz tartozóból */
      fejlec=regi.fejlec;
      volt=van=regi.van;
      fut=futott=regi.fut;
      aktiv=regi.aktiv;
    }
    public int getX() { return fejlec.x; }
    public int setVan(int van) { return this.van=fejlec.van=van; }
    public int setFut(int fut) { return this.fut=fejlec.fut=fut; }
    public int addVan(int van) { return this.van=fejlec.van+=van; }
    public int addFut(int fut) { return this.fut=fejlec.fut+=fut; }
    public boolean setAktiv(boolean aktiv) { return this.aktiv=aktiv; }
    public boolean isKlassz(String klassz) { return this.fejlec.klassz.equals(klassz)}

    public void kirajzol(SzekvenciaCanvas sc, int y) {
      Graphics g=sc.getGraphics2();
      g.setColor(Color.cyan);
      if (futott<1) g.drawLine(fejlec.x, y-SORTAV+1, fejlec.x, y-7);
               else g.fillRect(fejlec.x-1, y-SORTAV+1, 3, SORTAV-7);
      if (fut<1)    g.drawLine(fejlec.x, y-6, fejlec.x, y);
               else g.fillRect(fejlec.x-1, y-6, 3, 7);
    }
    public void fejlec_kirajzol(SzekvenciaCanvas sc, int y) {
      sc.drawStringKeret(fejlec.klassz_kiir+"*"+van, fejlec.x, y);
    }
  } /// class SzekvenciaBejegyzes

  public static class SzekvenciaSor {
    public static class MarHivtakError extends Error {}
    public static class HibasTipusError extends Error {}
    public static final int T_SEMMI=0, T_HIVAS=1, T_VISSZATERES=2, T_KONSTRUKTOR=3;
    private SzekvenciaBejegyzes bejegyzesek[];
    private int aktiv_idx;
    private int aktiv_volt_idx;
    private int mindenes_idx; // melyik bejegyzéshez kerül az ismeretlen klassz-ú metódushívás

    private int tipus;
    private String metodus; // metódus neve
    private int honnan_idx; // melyik bejegyzés hívta a metódust
    private int hova_idx; // melyik bejegyzés metódusát hívtuk
    public SzekvenciaSor(SzekvenciaBejegyzes bejegyzesek[]int mindenes_idx) {
      this.bejegyzesek=bejegyzesek;
      this.mindenes_idx=mindenes_idx;
      this.aktiv_idx=this.aktiv_volt_idx=mindenes_idx;
      this.tipus=T_SEMMI;
      bejegyzesek[mindenes_idx].addFut(1);
    }
    public SzekvenciaSor(SzekvenciaSor regi) {
      /* az új sort hozzuk létre a régiből */
      bejegyzesek=new SzekvenciaBejegyzes[regi.bejegyzesek.length];
      for (int i=0;i<regi.bejegyzesek.length;i++) bejegyzesek[i]=new SzekvenciaBejegyzes(regi.bejegyzesek[i]);
      aktiv_volt_idx=aktiv_idx=regi.aktiv_idx;
      tipus=T_SEMMI;
    }

    private int klassz_keres(String klassz) {
      /* -1, ha nincs olyan klassz */
      int i=bejegyzesek.length-1;
      while (i>=0 && !bejegyzesek[i].isKlassz(klassz)) i--;
      return i;
    }
    private int klassz_keres_mindenes(String klassz) {
      int j=klassz.lastIndexOf('$');
      if (j!=-1) klassz=klassz.substring(j+1);
      int i=klassz_keres(klassz);
      return i==-1?mindenes_idx:i;
    }
    private void setAktiv(int idx) {
      if (aktiv_idx>=0) bejegyzesek[aktiv_idx].setAktiv(false);
      bejegyzesek[idx].setAktiv(true);
      aktiv_idx=idx;
    }

    public void esemeny(StackKeret honnan, StackKeret hova, int tipus) {
      if (this.tipus!=T_SEMMI) throw new MarHivtakError();
      this.tipus=tipus;
      metodus=hova.getMetodus();
      honnan_idx=klassz_keres_mindenes(honnan.getKlassz());
      hova_idx=klassz_keres_mindenes(hova.getKlassz());
      if (tipus==T_HIVAS) {
        bejegyzesek[hova_idx].addFut(1);
      } else if (tipus==T_KONSTRUKTOR) {
        bejegyzesek[hova_idx].addVan(1);
        bejegyzesek[hova_idx].addFut(1);
      } else if (tipus==T_VISSZATERES) {
        bejegyzesek[hova_idx].addFut(-1);
      } else throw new HibasTipusError();
      setAktiv(hova_idx);
    }

    public void kirajzol(SzekvenciaCanvas sc, int y) {
      for (int i=0;i<bejegyzesek.length;i++) bejegyzesek[i].kirajzol(sc,y);
      if (tipus==T_HIVAS) {
        sc.drawStringNyil(metodus, bejegyzesek[honnan_idx].getX(), y-7, bejegyzesek[hova_idx].getX());
      } else if (tipus==T_KONSTRUKTOR) {
        sc.drawStringNyil("<c.tor>", bejegyzesek[honnan_idx].getX(), y-7, bejegyzesek[hova_idx].getX());
      } else if (tipus==T_VISSZATERES) {
        sc.drawStringNyil("<vissza>", bejegyzesek[hova_idx].getX(), y-7, bejegyzesek[honnan_idx].getX());
      }
    }
    public void fejlec_kirajzol(SzekvenciaCanvas sc, int y) {
      for (int i=0;i<bejegyzesek.length;i++) bejegyzesek[i].fejlec_kirajzol(sc,y);
    }
  } /// class SzekvenciaSor

  public static class SzekvenciaCanvas extends OkosCanvas {
    private FontMetrics fm;
    private Graphics g;
    protected SzekvenciaSor sorok[];
    protected int utso; // első már nem használt index a sorok-ban, mindig >=1
    public SzekvenciaCanvas() {
      font2=new Font("sansserif",Font.PLAIN,12);
      setBackground(new Color(255,240,208));
    }
    public void ujsor() {
      int kifer=hanySorFerKi();
      if (utso>=kifer) { System.arraycopy(sorok, utso-kifer+1, sorok, 0, kifer-1); utso=kifer-1; }
      sorok[utso]=new SzekvenciaSor(sorok[utso-1]);
      utso++;
    }
    public void esemeny(StackKeret honnan, StackKeret hova, int tipus) {
      ujsor();
      sorok[utso-1].esemeny(honnan, hova, tipus);
      repaint();
      // Imp: scrolling
    }
    public int hanySorFerKi() {
      int kifer=(this.getSize().height-18)/SzekvenciaBejegyzes.SORTAV;
      if (kifer<2) kifer=2;
      if (sorok==null || kifer>sorok.length) {
        SzekvenciaSor ujsorok[]=new SzekvenciaSor[kifer];
        if (sorok!=null) for (int i=0;i<utso;i++) ujsorok[i]=sorok[i];
        sorok=ujsorok;
      }
      return kifer;
    }
    public Graphics getGraphics2() { return g; }
    public synchronized void paint(Graphics g) { // synchronized fontos!
      int kifer=hanySorFerKi();
      this.g=g;
      g.setFont(font2);
      fm=g.getFontMetrics();
      int y=18;
      sorok[utso-1].fejlec_kirajzol(this,y);
      for (int i=utso-kifer;i<utso;i++) {
        if (i<0) continue;
        y+=SzekvenciaBejegyzes.SORTAV;
        sorok[i].kirajzol(this,y);
      }
    }
    public void drawStringKozep(String str, int x, int y) {
      // x: középső pixel
      // y: baseline
      int w=fm.stringWidth(str);
      g.drawString(str, x-w/2, y);
    }
    public void drawStringKeret(String str, int x, int y) {
      // x: középső pixel
      // y: keret alsó vonala
      int w=fm.stringWidth(str);
      int a=fm.getAscent();
      int d=fm.getDescent();
      g.setColor(Color.white);
      g.fillRect(x-w/2-1, y-a-d+1, w+2, a+d-1);
      g.setColor(Color.black);
      g.drawString(str, x-w/2, y-d);
      g.setColor(Color.blue);
      g.drawRect(x-w/2-2, y-a-d-1, w+4, a+d+1);
      g.drawLine(x,y+1,x,y+1);
    }
    public void drawStringNyil(String str, int x1, int y, int x2) {
      // nyíl: (x1,y)->(x2,y)
      int w=fm.stringWidth(str);
      g.setColor(Color.red);
      int sx;
      if (x1==x2) {
        sx=x1+5;
        y-=5;
        g.drawLine(x1,y  ,sx+w/2, y  );
        g.drawLine(x1,y+5,sx+w/2, y+5);
        g.drawLine(sx+w/2,y,sx+w/2,y+5);
        // vvv nyíl hegye
        g.drawLine(x2+3,y+5+3,x2,y+5);
        g.drawLine(x2+3,y+5-3,x2,y+5);
      } else {
        sx=(x1+x2-w)/2;
        g.drawLine(x1,y,x2,y);
        if (x2>x1) { g.drawLine(x2-3,y-3,x2,y); g.drawLine(x2-3,y+3,x2,y)}
              else { g.drawLine(x2+3,y-3,x2,y); g.drawLine(x2+3,y+3,x2,y)}
      }
      g.drawString(str, sx, y-2);
    }
  } /// class SzekvenciaCanvas

  // ---

  /*public static void hejho() {
    StackKeret t[]=honnanHivtak();
    for (int i=0;i<t.length;i++) System.out.println(t[i]);
  }*/

  private static class JobbTextArea extends TextArea {
    protected int position2;
    protected boolean maradt;
    public void setCaretPosition2(int position) {
      // Ismét egy hülyesége a Java AWT-nek: csak kirajzolt TextArea
      // kurzorpozícióját lehet megváltoztatni. Őrület.
      position2=position;
      try {
        super.setCaretPosition(position);
      } catch (IllegalComponentStateException e) {}
    }
    public int getCaretPosition2() { return position2; }
    public synchronized void print(String s) {
      if (maradt) { append("\n"); maradt=false}
      if (s.endsWith("\n")) {
        append(s.substring(0,s.length()-1));
        maradt=true;
      } else {
        append(s)// setText(getText()+s);
      }
      setCaretPosition2(getText().length());
      // ^^^ azért nem ez, mert az utolsó sor soremelése miatt nem szeretnénk
      //     folyton üres sort látni
    }
    public synchronized void println(String s) { print(s+"\n")}
    public void kurzortIgazit() {
      setCaretPosition2(position2);
    }
  } /// class JobbTextArea

  public static class JobbTextField extends TextField implements KeyListener {
    private static class Monitor {}
    protected volatile String sor;
    protected JobbTextArea jta;
    protected Monitor monitor;
    public JobbTextField(JobbTextArea jta) {
      monitor=new Monitor();
      this.jta=jta;
      addKeyListener(this);
    }
    public String getSor() { return sor; }
    public String xreadln() {
      // ezt csak this.thread-ből szabad hívni
      setEditable(true); setText("");
      synchronized(monitor) { try { monitor.wait()} catch (InterruptedException e) {} }
      // sor=null;
      //while (sor==null)
      //  try { Thread.sleep(100); } catch (InterruptedException e) {}

//      if (true) return "";
      //synchronized(monitor) {
      //  try { monitor.wait(); } catch (InterruptedException e) {} // Thread.currentThread().stop(); }
      //}
//      Thread.yield();
//      synchronized(this) {}
      // try { Thread.sleep(2000); } catch (InterruptedException e) {}
      setEditable(false);
      // ^^^ ez a sor a vacak JDK1.1.7-ben deadlock-ot okozna. De miért?
      //     Miért nem állíthatom át a flag-eket akkor, amikor akarom??
      //     Most csak a Java bugos, az implementáció bugos, vagy én nem értek
      //     valamit?? A kék könyv és a Sun-féle API doksi semmit nem ír
      //     erről. Igazán nem nagy kérés, hogy akkor állíthassam
      //     szerkeszthetőre a mezőt amikor én akarom. De vannak olyan
      //     programnyelvek, akiknek nagy kérés...
      // sor=getText();
      // ^^^ ez is deadlock. No comment.
      // System.out.println("GEM");
      // jta.println(">> "+sor);
      return sor;
    }
    public String readln() {
      xreadln();
      jta.println("$$ "+sor);
      return sor;
    }
    public void keyPressed(KeyEvent e) {
      int i=e.getKeyCode();
      // System.out.println("KT"+KeyEvent.VK_ENTER);
      if (i==KeyEvent.VK_ENTER) {
        synchronized(monitor) { sor=getText(); monitor.notifyAll()}
      }
    }
    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {
      // System.out.println("BE");
      char c=e.getKeyChar();
      if (c=='\n' || c=='\r') {
        synchronized(monitor) { sor=getText(); monitor.notifyAll()}
        // System.out.println("gt");
        // sor=getText();
        //synchronized(monitor) {
        //  System.out.println("monig");
        //  // sor=getText();
        //  System.out.println("gott");
        //  monitor.notifyAll();
        //}
        // thread.interrupt();
        // System.out.println("opp");
      }
      // System.out.println("KI");
      // ta.setCaretPosition2(Integer.MAX_VALUE);
    }
  }

  public static Skel skel;
  public void skel_main() {
  }

  private static final String sok_plussz="++++++++++++++++++++++++++++++++++++++++++++++++";
  public static String n_db_plussz(int n) {
    /* n db '+'-ot tartalmazó String-et ad vissza */
    if (n<=sok_plussz.length()) return sok_plussz.substring(0,n);
    return sok_plussz+n_db_plussz(n-sok_plussz.length());
  }
  public static String[] str_split(char c, String s) {
    /* felvágja s-et c mentén */
    // Még ezt az egyszerű függvényt is nekem kell megírnom.
    int n=0;
    int i=0;
    while (-1!=(i=s.indexOf(c,i))) { n++; i++; }
    String t[]=new String[n+1];
    i=0;
    n=0;
    int j=0;
    while (-1!=(j=s.indexOf(c,i))) { t[n++]=s.substring(i,j); i=j+1; }
    t[n]=s.substring(i);
    return t;
  }

  private int alapPlussz;
  private String mostPlussz;
  public void println_plussz(String s) {
    ta.println(mostPlussz+s);
  }
  public void enter() {
    StackKeret t[]=honnanHivtak(2);
    // for (int i=0;i<t.length;i++) ta.println(t[i].toString());
    if (t.length-alapPlussz>mostPlussz.length()+1) return;
    // ^^^ túl mélyre kerültünk (pl. szülő osztály konstruktoraiba)
    ta.println((mostPlussz=n_db_plussz(t.length-alapPlussz))+" >>"+t[0].toString2());
    if (t.length>=2) c.esemeny(t[1],t[0],SzekvenciaSor.T_HIVAS);
  }
  public void constructor() {
    StackKeret t[]=honnanHivtak(2);
    if (t.length-alapPlussz>mostPlussz.length()+1) return;
    // ^^^ túl mélyre kerültünk (pl. szülő osztály konstruktoraiba)
    ta.println((mostPlussz=n_db_plussz(t.length-alapPlussz))+" +>"+t[0].toString2());
    if (t.length>=2) c.esemeny(t[1],t[0],SzekvenciaSor.T_KONSTRUKTOR);
  }
  public void leave() {
    StackKeret t[]=honnanHivtak(2);
    if (t.length-alapPlussz>mostPlussz.length()) return;
    // ^^^ túl mélyre kerültünk (pl. szülő osztály konstruktoraiba)
    ta.println((mostPlussz=n_db_plussz(t.length-alapPlussz-1))+"+ <<"+t[0].toString2());
    if (t.length>=2) c.esemeny(t[1],t[0],SzekvenciaSor.T_VISSZATERES);
  }
  public int ask(String kerdes, String valaszok[]) {
    ta.println(mostPlussz+" ?? "+kerdes);
    ta.print(mostPlussz+" |: ");
    for (int i=0;i<valaszok.length;i++) {
      if (i!=0) ta.print(" | ");
      ta.print(i+"="+valaszok[i]);
    }
    ta.println("");
    int i;
    while (true) {
      String valasz=tf.xreadln();
      i=0; while (i<valaszok.length && !valasz.equals(valaszok[i])) i++;
      if (i<valaszok.length) break;
      try { i=Integer.parseInt(valasz)} catch (NumberFormatException e) {}
      if (i>=0 && i<valaszok.length) break;
      Toolkit.getDefaultToolkit().beep();
    }
    ta.println(mostPlussz+" $$ "+valaszok[i]);
    return i;
  }
  public int ask_int(String kerdes, int legalabb, int legfeljebb) {
    ta.println(mostPlussz+" ?? "+kerdes);
    ta.println(mostPlussz+" |: egy "+legalabb+" és "+legfeljebb+" közötti egész számot várok.");
    int i;
    while (true) {
      String valasz=tf.xreadln();
      try {
        i=Integer.parseInt(valasz);
        if (i>=legalabb && i<=legfeljebb) break;
      } catch (NumberFormatException e) {}
      Toolkit.getDefaultToolkit().beep();
    }
    ta.println(mostPlussz+" $$ "+Integer.toString(i));
    return i;
  }
  public String ask_string(String kerdes) {
    ta.println(mostPlussz+" ?? "+kerdes);
    int i;
    String valasz=tf.xreadln();
    ta.println(mostPlussz+" $$ "+valasz);
    return valasz;
  }
  public int ask(String kerdes, String valaszok) { return ask(kerdes, str_split('/', valaszok))}
  private static final String ask_boolean_s[]={"nem","igen"};
  public boolean ask_boolean(String kerdes) { return ask(kerdes, ask_boolean_s)==0?false:true}

  Thread thread;
  public void run() {
    /* a szkeleton szál főfüggvénye, ez fut napestig this.thread()-ben */
    ta.println("Ez a Kincskereső Kisgömböc szkeletonja.");
    ta.println("Felül látható a szekvenciadiagram, középen (itt) párbeszéd-mező, alulra pedig a válaszokat lehet írni.");
    ta.println("A választ a `??' megjelenése után kell beírni, majd Enter-t kell ütni.");
    ta.println("A szkeletonból az ablak bezárásával lehet kilépni.");
    // alapPlussz=honnanHivtak(2).length+1;
    alapPlussz=honnanHivtak(2).length+1;
    mostPlussz="";
    tf.requestFocus();
    skel_main();
    tf.setText("");
    ta.println("A szkeleton futása véget ért, a programból ki lehet lépni");
  }

  protected SzekvenciaCanvas c;
  private JobbTextField tf;
  private JobbTextArea ta;

  public void initFont2() {
    if (font2==null) font2=new Font("sansserif",Font.PLAIN,12);
    // ^^^ ha nemlétező Font-ot kérünk, akkor próbál valamit helyettesíteni,
    //     ami lehet, hogy nagyon vacak. Hát igen, a fontkezelést sem sikerült
    //     megoldaniuk a Java AWT programozóinak. Különböző platformokon
    //     teljesen másmilyen (és más méretű!) betűk jelennek meg. Azt meg, hogy
    //     a .class-hoz a programozó mellékeli a neki tetsző betűtípust, nem
    //     lehet megoldani. Miért is lehetne??...
  }

  public void init() {
    // Imp: Csak egy egyszerű egymás alá pakolást akarok, fix szélességgel.
    //      hát olyan nagy kérés ez??
    skel=this;
    setSize(620,440);
    initFont2();
    // hejho();
    int width=this.getPreferredSize().width;
    // setBackground(Color.blue);
    this.setLayout(new GridBagLayout());
    GridBagConstraints gc=new GridBagConstraints();
    gc.gridx=0;
    gc.gridy=0;
    gc.gridwidth=1;
    gc.gridheight=1; // GridBagConstraints.REMAINDER;
    gc.weighty=2;
    gc.weightx=1;
    gc.fill=GridBagConstraints.BOTH; // GridBagConstraints.NONE;
    gc.anchor=GridBagConstraints.NORTH;

    if (c==null) {
      c=new SzekvenciaCanvas();
      c.setBackground(Color.red);
    }
    c.setFont2(this.font2);
    // c=new KkkgSzekvenciaCanvas(font2);
    // c.setSize(width,20); // muszáj, mert különben 0 lesz. Ez ilyen buta
    // c=new OkosCanvas(); c.setBackground(Color.red);
    ta=new JobbTextArea(); ta.setColumns(1); ta.setRows(1)// 0-t nem szereti
    ta.setBackground(Color.pink)// :-)
    ta.setFont(font2);
    ta.addFocusListener(new FocusAdapter() {
      public void focusGained(FocusEvent e) {
        ta.transferFocus();
      }
    });
    // ^^^ csak így tudjuk kikapcsolni a Tab-bejárást a komponensre.
    ta.setEditable(false)// azért a bill. fókuszt megkaphatja
    // ta.disableEvents(AWTEvent.FOCUS_EVENT_MASK);
    // ^^^ protected :-((
    // ta.setEnabled(false);
    // ^^^ ez használhatatlan, mert elrondítja az egészet, és scrollozni sem
    //     lehet
    ta.setText("");
    // ta.setText("próba\nalma\n1\n2\n\3\n4\n5\n6\n7\n8\n9\nnegyvenkettő\n");
    ta.setCaretPosition2(Integer.MAX_VALUE);
    tf=new JobbTextField(ta);
    //tf.addKeyListener(new KeyAdapter() {
    //  public void keyTyped(KeyEvent e) {
    //    char c=e.getKeyChar();
    //    if (c=='\n' || c=='\r') ta.setCaretPosition2(Integer.MAX_VALUE);
    //  }
    //});

    // tf.setSize(width,300); // hatástalan
    add(c,gc);
    gc.weighty=1;
    gc.gridy=1; // GridBagConstraints.RELATIVE;
    add(ta,gc);
    // ta.setCaretPosition(Integer.MAX_VALUE);
    gc.weighty=0;
    gc.gridy=2; // GridBagConstraints.RELATIVE;
    add(tf,gc);
    thread=new Thread(this);
  }
  public void destroy() {
    // Imp: nem sokat segít..., xreadln még mindig nullpointer-t ad vissza
    if (thread.isAlive()) { thread.interrupt()}
  }
  public void stop() {
    // thread.stop(); -- deprecated
    // System.out.println("Stopp!");
  }
  public void start() { if (!thread.isAlive()) thread.start()}

} /// class Skel