/**
@author Martin Klossek, Martin Meedt und Fabian Wleklinski
letzte Bearbeitung: 29.05.99 | 14:00 
*/

import java.awt.*;
import java.io.*;
import java.util.*;


/** Beinhaltet den Arbeitsbereich und auch die Programmeinstellungen des
Zeichenprogrammes */
public class TPaint {
  
  public TPaint() {
    FPaintWorkplace = new TPaintWorkplace();
    FPaintSettings = new TPaintSettings();
  }

  /** Liefert die Instanz der Programmeinstellungen zurück */
  public TPaintSettings PaintSettings() {
    return FPaintSettings;
  }

  /** Liefert die Instanz des Arbeitsbereiches zurück */
  public TPaintWorkplace PaintWorkplace() {
    return FPaintWorkplace;
  }

  /** Serialisierung: Speichert den Arbeitsbereich und die Programmeinstellungen in einer Datei */
  public void saveObjectsToFile( String filename ) throws java.io.FileNotFoundException, java.io.IOException {
    FileOutputStream fileOutputStream = new FileOutputStream( filename );
    ObjectOutputStream objectOutputStream = new ObjectOutputStream( fileOutputStream );

    objectOutputStream.writeObject( FPaintWorkplace );
    objectOutputStream.writeObject( FPaintSettings );
    objectOutputStream.flush();

    fileOutputStream.close();
  }

  /** Textformat: Speichert den Arbeitsbereich und die Programmeinstellungen in einer Datei */
  public void saveToFile( String filename ) throws java.io.FileNotFoundException, java.io.IOException {
    FileOutputStream fileOutputStream = new FileOutputStream( filename );
    PrintWriter ps = new PrintWriter( fileOutputStream );

   // Umgebungsinformationen abspeichern
    ps.println( FPaintSettings.getSaveString() );
  
   // Die Grafik-Objekte abspeichern
    TGraphicContainer thisContainer = null; 
    int j = 0;

    for (int i = 0; i < FPaintWorkplace.count(); i++) {
       if (FPaintWorkplace.items[i] instanceof TGraphicContainer) { // Grafikcontainer speichern
          thisContainer = (TGraphicContainer)FPaintWorkplace.items[i];

          if (FPaintWorkplace.items[i] instanceof TTree) { // Ein Baum wird zunächst in einen Graphen umgewandelt...
             thisContainer = new TGraph(FPaintSettings); // ... und danach wie ein Graph gespeichert
             ((TGraph)thisContainer).add ( (TTree)FPaintWorkplace.items[i] ); 
          }

         // Die Container-Nodes durchlaufen und abspeichern
          thisContainer.setIndex();  // Nodes vor Speicherung indizieren
          for (j = 0; j < thisContainer.count(); j++) {
             ps.println ( thisContainer.container(j).getSaveString() ); 
          }

       } else { // normales Objekt
          ps.println ( ((TGraphic)FPaintWorkplace.items[i]).getSaveString() );
       }  
    }

   // Schreiben beendet, Streams schließen
    ps.flush();
    fileOutputStream.close();
  }

  /** Serialisierung: Restauriert den Arbeitsbereich und die Programmeinstellungen aus einer Datei */
  public void loadObjectsFromFile( String filename ) throws java.io.StreamCorruptedException, java.io.IOException, java.lang.ClassNotFoundException {
    FileInputStream fileInputStream = new FileInputStream( filename );
    ObjectInputStream objectInputStream = new ObjectInputStream( fileInputStream );

    FPaintWorkplace = (TPaintWorkplace) objectInputStream.readObject();
    FPaintSettings = (TPaintSettings) objectInputStream.readObject();

    fileInputStream.close();
  }

  /** Textformat: Restauriert den Arbeitsbereich und die Programmeinstellungen aus einer Datei */
  public void loadFromFile( String filename ) throws java.io.StreamCorruptedException, java.io.IOException, java.lang.ClassNotFoundException {
    FileReader fileReader = new FileReader ( filename );
    LineNumberReader lr = new LineNumberReader( fileReader );

    String currentLine, lineType, itemType;  
    int currentItem = 0;    int lastItem = -1;
    int graphCount = 0;
    TGraph newGraph = new TGraph(FPaintSettings);  
    TList newGraphRelations = new TList ();
    TGraphic newGraphic = null; 
    
   // Datei Zeilenweise auslesen   
    currentLine = lr.readLine();
    if (currentLine != null) { FPaintWorkplace.clear(); } // Workplace löschen

    while (currentLine != null) {

      // Dei einzelnen Zeilen parsen
       lineType = getLineType(currentLine);
       if (!lineType.equals ("")) {
          graphCount++;
          if (lineType.equals ("SETTINGS")) { FPaintSettings.setFromString(currentLine); }
          else if (lineType.equals ("LINE")) { FPaintWorkplace.add ( new TLine (currentLine, FPaintSettings) ); }
          else if (lineType.equals ("RECTANGLE")) { FPaintWorkplace.add ( new TRectangle (currentLine, FPaintSettings) ); }
          else if (lineType.equals ("CIRCLE")) { FPaintWorkplace.add ( new TCircle (currentLine, FPaintSettings) ); }
          else if (lineType.equals ("TEXTBOX")) { FPaintWorkplace.add ( new TTextbox (currentLine, FPaintSettings) ); }
          else {
            // Hier werden jetzt die Graphen und ihre Verknüpfungen behandelt
             lastItem = currentItem; // Für Graphen-Sequenz-Wechsel
             currentItem = (new Integer (lineType)).intValue();

             // System.out.println ( currentLine );

             graphCount--; // Variable dient zum Prüfen, ob Graph-Sequenz beendet ist (beendet, bei > 0) 
             if ((graphCount>0) || (currentItem <= lastItem)) { // Graphsequenz wurde unterbrochen, bzw. erste Graphensequenz wird betreten
               // Wenn bereits eine Graph-Sequenz vorgelegen hat, dann jetzt Items verbinden
               // und im Workplace speichern
                if (newGraph!=null) { 
                   createRelations ( newGraph, newGraphRelations ); // Spannt die Verknüpfungen auf
                   FPaintWorkplace.add ( newGraph ); 
                }
               // Zurücksetzen der Variabeln (so ne Art Semaphore...)
                graphCount = 0;  
                newGraph = new TGraph(FPaintSettings);  // ... und neuen Graph erstellen (prophylaktisch)
                newGraphRelations = new TList(); // Die Relationsliste nimmt die Relationstrings auf
             }

             newGraphic = getGraphicFromLine ( currentLine );
             if (newGraphic != null) {
                newGraph.add (newGraphic);
                newGraphRelations.add ( getRelations ( currentLine ) ); // Holt die Verbindungen und speichert sie in Liste
                System.out.println ( newGraphic.getSaveString() );
                System.out.println ( getRelations ( currentLine ) );
             }
             
          }

          // System.out.println (lineType);
       }

      // nächste Zeile einlesen
       currentLine = lr.readLine();
    }

    // Falls das letzte Element noch zu einem Graphen gehört hat, müssen wir diesen Graphen hier in Workplace einfügen 
    if (newGraph!=null) { if (newGraph.count()>0) { 
       createRelations ( newGraph, newGraphRelations ); // Spannt die Verknüpfungen auf
       FPaintWorkplace.add ( newGraph ); 
    } }


    fileReader.close();
  }

  /** Stellt den Typ einer Zeile fest (abhängig vom ersten Wort)
    * @param String Die ungeparste Eingabezeile
    * @return String Der Typ (LINE, RECTANGLE, CIRCLE, TEXTBOX, SETTINGS) oder die Elementnummer (0..n)
    */
  private String getLineType ( String ts ) {

    // Einzelelemente checken -> Zeile nach Keyword checken
     if ( ts.indexOf ("LINE")==0 ) { return ( "LINE" ); }
     if ( ts.indexOf ("RECTANGLE")==0 ) { return ( "RECTANGLE" ); }
     if ( ts.indexOf ("CIRCLE")==0 ) { return ( "CIRCLE" ); }
     if ( ts.indexOf ("TEXTBOX")==0 ) { return ( "TEXTBOX" ); }
     if ( ts.indexOf ("SETTINGS")==0 ) { return ( "SETTINGS" ); }

    // Graphenelemente detecten
     if ( ts.indexOf (" ") != -1) { // wenn ein Leerzeichen drin, dann...
        String os = ts.substring ( 0, ts.indexOf (" ") );

        long number = 0;  
        try {
           number = new Integer(os).intValue();
        } catch (NumberFormatException ex) { os = ""; } // wenn keine Zahl, dann wird Zeile für ungülig erklärt

        return (os);        
     }

    // Wenn weder Keyword noch Elementnummer, dann leer zurück
     return ("");
  }

  /** Untersucht die Eingabezeile, die zu einem Graphen gehört
    * @param String Die ungeparste Graphen-Eingabezeile
    * @return TGraphic Das aus der Eingabezeile erzeugte Objekt
    */
  private TGraphic getGraphicFromLine ( String data ) {
     if (data.indexOf(" ")==-1) { return(null); } // Ein Leerzeichen als Feldtrenner brauchen wir mindestens

     int first = 0;  String ts;  Double d;  TGraphic newGraphic = null;
     first = data.indexOf (" ") +1;

    // Im ersten Teil das Grafikelement bestimmen und anlegen
     ts = data.substring(first, data.indexOf (" ", first));
     first = data.indexOf (" ", first) +1;
     if (getLineType (ts).equals("LINE")) { newGraphic = new TLine (ts, FPaintSettings); }
     else if (getLineType (ts).equals ("RECTANGLE")) { newGraphic = new TRectangle (ts, FPaintSettings); }
     else if (getLineType (ts).equals ("CIRCLE")) { newGraphic = new TCircle (ts, FPaintSettings); }
     else if (getLineType (ts).equals ("TEXTBOX")) { newGraphic = new TTextbox (ts, FPaintSettings); }
     else { // Dann muß es ja ne Standard-Textbox sein:
       
        String text = ts; // Text für Textbox retten
 
        ts = data.substring(first, data.indexOf (" ", first));
        first = data.indexOf (" ", first) +1;  
        double xPos = (new Double (ts)).doubleValue(); 

        int end = data.indexOf (" ", first); // Wenn Nachfolgerknoten kommen
        if (end == -1) { end = data.length(); } // Wenn keine Nachfolgerknoten kommen
        ts = data.substring(first, end);
        double yPos = (new Double (ts)).doubleValue();

        newGraphic = new TTextbox (text, 0, 0, FPaintSettings);
        newGraphic.setTop (yPos);  newGraphic.setLeft (xPos);
     }

    // Und zurückgeben
     return (newGraphic);
  }             

  /** Holt den Relation-Teil der Eingabezeile, die zu einem Graphen gehört
    * @param String Die ungeparste Graphen-Eingabezeile
    * @return String Der Teil des Eingabestrings, der die Relationindizes enthält
    */
  private String getRelations ( String data ) {
     if (data.indexOf(" ")==-1) { return (null); } // Ein Leerzeichen als Feldtrenner brauchen wir mindestens

     int first = -1;
          
     first = data.indexOf (" ") +1;  // first ist nach Index
     first = data.indexOf (" ", first) +1;  // first ist nach Text
     first = data.indexOf (" ", first) +1;  // first ist nach xPos 
     first = data.indexOf (" ", first);  // first ist nach yPos 

     if (first != -1) {
        return ( data.substring (first +1, data.length() ) ); // Der Relationspart der Eingabezeile
     }
   
     return (""); // Kein sinnvolles Ergebnis extrahierbar oder keine Relationen
  }

  /** Spannt die Relationen zwischen den einzelnen Objekten des Graphen
    * @param TGraph Der zu bearbeitenden Graph
    * @param TList Die Liste der Relationen
    */
  private void createRelations ( TGraph gra, TList rel ) {
     StringTokenizer thisTokenizer; 
     int second = 0;
    
    // Alle Graph-Items durchlaufen und bei Bedarf verknüpfen
     for ( int i = 0; i < gra.count(); i++ ) {

         if (rel.item(i)!=null) { if (!((String)rel.item(i)).equals("")) {

            thisTokenizer = new StringTokenizer((String)rel.item(i));
            while (thisTokenizer.hasMoreTokens()) {
               second = (new Integer (thisTokenizer.nextToken())).intValue();
               gra.connect ( gra.item(i), gra.item(second) );
               
// System.out.println ( thisTokenizer.nextToken() );
            }
         } }

     }
  }

  // Der Arbeitsbereich
  private TPaintWorkplace FPaintWorkplace;

  // Die Programmeinstellungen
  private TPaintSettings FPaintSettings;
}

/** Beinhaltet einen Arbeitsbereich für beliebig viele Zeichenobjekte */
class TPaintWorkplace implements java.io.Serializable{

  /** Erzeugt einen Arbeitsbereich */
  public TPaintWorkplace() {
    // vererbten Konstruktor aufrufen
    super();

    // Den Container für die Elemente anlegen. Für das erste benutzen wir mal 10 Elemente
    items = new Object[ 10 ];
  }

  /** Markiert / Demarkiert alle Elemente */
  public void setSelected( boolean isSelected ) {
    for (int i = 0; i < FCount; i++) {
      ((TGraphic) items[ i ]).setSelected( isSelected );
    }
  }

  /** löscht alle Elemente */
  public void clear(  ) {
    for (int itemI=0; itemI < FCount; itemI++) {
      items[ itemI ] = null;
    }
    FCount = 0;
  }

  /** Fügt ein Grafikelement hinzu.
  @param item Das hinzuzufügende Grafikelement
  */
  public void add( TGraphic item ) {
    // sicherstellen, daß genügend Platz vorhanden ist
    ensureCapacity( FCount + 1 );
    // Das neue Element aufnehmen
    items[ FCount ] = item;
    // Elementzähler erhöhen
    FCount++;
  }

  /** Liefert ein Grafikelement zurück
  @param index Der Index des Grafikelementes
  */
  public TGraphic getItem( int index ) {
    TGraphic result = null;
    if (index < FCount) {
      result = (TGraphic) items[ index ];
    }

    return result;
  }

  /** Liefert den Index eines bestimmten Grafikelementes zurück
  @param item Das Grafikelement, für das der Index gesucht wird
  */
  public int indexOf( TGraphic graphic ) {
    int result = -1;
    for (int itemI=0; itemI < FCount; itemI++) {
      if ( ((TGraphic) items[ itemI ]) == graphic ) {
        result = itemI;
        break;
      }
    }
    return result;
  }

  /** löscht ein bestimmtes Grafikelement
  @param index Der Index des zu löschenden Elementes
  */
  public void delete( int index ) {
    // wenn Index außerhalb derGrenzen -> raus
    if ((index < 0) || (index >= FCount)) {
      return;
    }

    // Alle folgenden Elemente aufrücken
    for (int itemI = index; itemI < FCount-1; itemI++) {
      items[ itemI ] = items[ itemI+1 ];
    }

    FCount--;
  }

  /** löscht das angegebene Grafikelement
  @param Graphic Das zu löschende Elemente
  */
  public void delete( TGraphic Graphic ) {
    // Index des Elements bestimmen:
    int delResult = 0;

    for ( int itemI = 0; itemI < FCount; itemI++) {
      if (items[ itemI ] == Graphic) {
         delete (itemI);
         break; 
      } else if (items[ itemI ] instanceof TGraphicContainer) {
         delResult = ((TGraphicContainer) items[itemI]).delete(Graphic);
         if (delResult==0) { break; } // Ein Element in Container wurde gelöscht
         if (delResult==1) { delete (itemI); break; } // Das letzte Element des Containers wurde gelöscht
                                 // Container ohne Elemente werden nicht zugelassen, daher auch diesen löschen! 
      }
    }
 
  }

  /** Liefert die Anzahl der Elemente zurück, die im Moment vorhanden sind */
  public int count() {
    return FCount;
  }

  /* Stellt sicher, daß Platz für die übergebene Anzahl an Elementen ist. Wenn nicht,
  wird Platz geschaffen */
  private void ensureCapacity( int count ) {
    // Wenn genügend Platz ist -> raus
    if (items.length >= count) {
      return;
    }

    // Ein neues Array anlegen, mit der doppelten, gewünschten Größe
    Object[] tempArray = new Object[ count * 2 ];
    // Die bestehenden Elemente dort hineinkopieren
    System.arraycopy( items, 0, tempArray, 0, items.length );
    // Das temporäre Array übernehmen
    items = tempArray;
  }

  /* Hält die Anzahl der Elemente, die momentan zugewiesen sind */
  private int FCount;

  /* Hält alle Grafikelemente */
  public Object[] items;
}

/** Beinhaltet die Programmoptionen */
class TPaintSettings implements java.io.Serializable {
  /** Die Größe des Workplace */
  public double maxX, maxY; 
  /** Die Startkoordinaten des sichtbaren Workplace-Ausschnittes */
  public double mapStartX, mapStartY;
  /** Die Endkoordinaten des sichtbaren Workplace-Ausschnittes */
  public double Xratio, Yratio;
  /** Die Größe des Fensters */
  public int frameWidth, frameHeight;  
  /** Aktuelle Zeichenfarbe */
  public Color currentColor;
  /** Aktuelle Hintergrundfarbe */
  public Color currentBGColor;

  /** Aktuelle Markerfarbe */
  private Color FMarkerColor;
  /** Toleranz beim Selektieren */
  private double FToleranz;

  /** Das Bild für den Lauf des Handlungsreisenden */
  private Image FTSPimage;


 /** Liefert die aktuelle Objektzeichenfarbe zurück
   * @return Color Die aktuelle Objektzeichenfarbe
   */
 public Color getCurrentColor () {
    return (currentColor);
 }

 /** Liefert die aktuelle Markerfarbe zurück
   * @return Color Die aktuelle Markefarbe
   */
 public Color getMarkerColor() {
    return (FMarkerColor);
 }
 /** Setzt die aktuelle Markerfarbe 
   * @param Color Die aktuelle Markefarbe
   */
 public void setMarkerColor(Color MarkerColor) {
    FMarkerColor = MarkerColor;
 }

 /** Liefert die aktuelle Selektiertoleranz zurück
   * @return int Die aktuelle Toleranz als double
   */
 public double getToleranz() {
    return (FToleranz);
 }
 /** Setzt die aktuelle Selektiertoleranz
   * @param Color Die Toleranz in Pixel ausgedrückt
   */
 public void setToleranz(int Toleranz) {
    FToleranz = double2pixWidth (Toleranz);
 }


 /** Rechnet Reele Zahl aus Koordinatensystem in X-Pixel um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixX ( double x ) {
    int w = (int) ( (x - mapStartX) * Xratio );
    return (w);
 }

 /** Rechnet Reele Zahl aus Koordinatensystem in Y-Pixel um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixY ( double y ) {
    int w = (int) ( (y - mapStartY) * Yratio );
    return (w);
 }

 /** Rechnet Breite als Reele Zahl aus Koordinatensystem in Pixelanzahl um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixWidth ( double x ) {
    int w = (int) ( x * Xratio );
    return (w);
 }
 /** Rechnet Reele Zahl aus Koordinatensystem in Y-Pixel um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixHeight ( double y ) {
    int w = (int) ( y * Yratio );
    return (w);
 }

 /** Rechnet X-Pixelangabe in Reele Zahl des Koordinatensystems um
   * @param int Der Pixel, der in die reele Zahl umgerechnet werdeb soll
   */
 public double pix2doubleX ( int x ) {
    double w = (double) ( (x / Xratio) + mapStartX );
    return (w);
 }
 /** Rechnet Y-Pixelangabe in Reele Zahl des Koordinatensystems um
   * @param int Der Pixel, der in die reele Zahl umgerechnet werdeb soll
   */
 public double pix2doubleY ( int y ) {
    double w = (double) ( (y / Yratio) + mapStartY );
    return (w);
 }

 /** Rechnet X-Pixel in reelen Abstand im Koordinatensystems um
   * @param int Die Pixel, die umgerechnet werden sollen
   */
 public double pix2doubleWidth ( int x ) {
    double w = (double) (x / Xratio);
    return (w);
 } 
 /** Rechnet Y-Pixel in reelen Abstand im Koordinatensystems um
   * @param int Die Pixel, die umgerechnet werden sollen
   */
 public double pix2doubleHeight ( int y ) {
    double w = (double) (y / Yratio);
    return (w);
 } 

 /** Vergrößert oder verkleinert den sichtbaren Zeichen-Ausschnitt
   * @param double Vergrößerung für Werte > 1, Verkleinerung < 1
   */
 public void zoom ( double ZoomFaktor ) {
    Xratio = Xratio * ZoomFaktor;
    if ( double2pixX (maxX)+1 < frameWidth -1 ) {
       mapStartX = 0;
    }

    Yratio = Yratio * ZoomFaktor;
    if ( double2pixY (maxY)+1 < frameHeight -1 ) {
       mapStartY = 0;
    }
 }

 /** Bewegt den sichtbaren Zeichen-Ausschnitt
   * @param int Bewegung in X-Richtung
   * @param int Bewegung in Y-Richtung
   */   
 public void move ( int xMove, int yMove ) {

    if ( double2pixX (maxX)+1 > frameWidth -1 ) {
       mapStartX += (xMove) / Xratio;
       if (mapStartX < 0) { mapStartX = 0; }
       if (mapStartX + 1 + (frameWidth / Xratio) > maxX) { 
          mapStartX = maxX - (frameWidth / Xratio);
       }
    }
         
    if ( double2pixY (maxY)+1 > frameHeight -1 ) {
       mapStartY += (yMove) / Yratio;
       if (mapStartY < 0) { mapStartY = 0; }
       if (mapStartY + 1 + (frameHeight / Yratio) > maxY) { 
          mapStartY = maxY - (frameHeight / Yratio);
       }
    }  

 }

 /** Liefert den größten X-Pixel zurück, denn ein Objekt im 
   * sichtbaren Ausschnitt belegen kann
   * @return int Wert für X in Pixeln 
   */
 public int getPixelMaxX () {
   return ( double2pixX (maxX) );
 }

 /** Liefert den größten Y-Pixel zurück, denn ein Objekt im 
   * sichtbaren Ausschnitt belegen kann
   * @return int Wert für Y in Pixeln 
   */
 public int getPixelMaxY () {
   return ( double2pixY (maxY) );
 }

 /** Setzt das Bild für den Lauf des Handlungsreisenden (optional)
   * @param Image Ein bereits in den Speicher geladene sImageobjekt
   */
 public void setTSPimage ( Image img ) {
    FTSPimage = img;
 }

 /** Gibt das Bild für den Lauf des Handlungsreisenden zurück
   * @return Image Das Bild für den Lauf des Handlungsreisenden 
   */
 public Image getTSPimage ( ) {
    return ( FTSPimage );
 }

 /** getSaveString() liefert die Werte des Objekts zusammengepackt in einem String
   * @return String Gepackte Information
   */
 public String getSaveString() {
    String ts = new String();
    ts = "SETTINGS{";

   // Hier Werte in ts einfügen 
    ts += (new Double (maxX)).toString();
    ts += "|";
    ts += (new Double (maxY)).toString();
    ts += "|";
    ts += (new Double (mapStartX)).toString();
    ts += "|";
    ts += (new Double (mapStartY)).toString();
    ts += "|";
    ts += (new Double (Xratio)).toString();
    ts += "|";
    ts += (new Double (Yratio)).toString();
    ts += "|";
    ts += (new Long (currentColor.getRGB())).toString();
    ts += "|";
    ts += (new Long (currentBGColor.getRGB())).toString();

    ts += "}";
    return (ts);
 }

 /** loadFromString(String) setzt die Werte des Objekts entsprechend des Stringinhalts 
   * @param String Einstellungen der Zeichenfläche zusammengepackt in String
   */
 public void setFromString (String data) { 
    if (data.indexOf("SETTINGS{")!=0) { return; }

    int first = 0;  String ts;  Double d;
    first = data.indexOf ("{") +1;

    ts = data.substring(first, data.indexOf ("|", first));
    first = data.indexOf ("|", first) +1;  
    maxX = (new Double (ts)).doubleValue();

    ts = data.substring(first, data.indexOf ("|", first));
    first = data.indexOf ("|", first) +1;  
    maxY = (new Double (ts)).doubleValue();

    ts = data.substring(first, data.indexOf ("|", first));
    first = data.indexOf ("|", first) +1;  
    mapStartX = (new Double (ts)).doubleValue();

    ts = data.substring(first, data.indexOf ("|", first));
    first = data.indexOf ("|", first) +1;  
    mapStartY = (new Double (ts)).doubleValue();

    ts = data.substring(first, data.indexOf ("|", first));
    first = data.indexOf ("|", first) +1;  
    Xratio = (new Double (ts)).doubleValue();

    ts = data.substring(first, data.indexOf ("|", first));
    first = data.indexOf ("|", first) +1;  
    Yratio = (new Double (ts)).doubleValue();

    ts = data.substring(first, data.indexOf ("|", first));
    first = data.indexOf ("|", first) +1;  
    currentColor = new Color ( (new Integer (ts)).intValue() );

    ts = data.substring(first,data.length()-1);
    currentBGColor = new Color ( (new Integer (ts)).intValue() );

    return;
 }

} 