/*---------------------------------------------------------------------------
File:         			Environment.java
Package:                        JFLAP Version 3.1
Author:				Magda & Octavian Procopiuc V1.0 07/15/96
                                Eric Gramond V3.0 07/22/98
				Ted Hung V3.1 07/15/99
 
Description of Contents:	Contains class Environment.
				
--------------------------------------------------------------------------*/

/* 
 * Susan H. Rodger, Magda Procopiuc, Octavian Procopiuc, Eric Gramond, Ted Hung
 * Computer Science Department
 * Duke University
 * August 1999
 * Supported by National Science Foundation DUE-9596002, DUE-9555084,
 *  and DUE-9752583
 *
 * Copyright (c) 1999
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the author.  The name of the author may not be used to
 * endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

package flap;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;
import java.util.*;

/**
 * This is the main window associated with each machine that the user
 * opens. It contains the Desktop in the middle -- where the machine 
 * is displayed and 
 * edited --, a status bar at the bottom, an input string bar  
 * above the Desktop, and a menu bar at the top. 
 * It also contains the actual machine.
 *
 * @see		flap.Desktop
 * @author	Magda & Octavian Procopiuc
 * @version	1.0, 07/15/96
 */
public class Environment extends SourceWindow implements ActionListener, WindowListener, MouseListener, KeyListener, ItemListener{
    
    /**
     * The type of the machine.
     */
    public int		type;
    /**
     * The desktop.
     */
    public Desktop	d;
    /**
     * Other desktop used by the TO_DFA FSA's to store the DFA to compare with
     * Also used by RegExptoFSAparser to convert from Regular Expression to FSA
     * and compare with
     */
    public Machine      dfa; 
    /**
     * The machine.
     */
    public Machine	a;
    /**
     * The input string field.
     */
    public TextField	inputField;
    /** 
     * the label displayed to the left of the textfield
     */
    public Label           inputName;
    /**
     * The input string field for the second tape for TM2.
     */
    public TextField	inputField2;
    /**
     * the isDone button for minimize tree
     */
    public Button isMinimizeDone;
    /**
     * the show button for minimize tree
     */
    public Button showAllMinimize;
    /** 
     * showAllMinimize NFA to DFA button for FSA
     */
    public MenuItem       minimize;
    public MenuItem       nfaToDfa;

    private MenuItem		newItem;
    private MenuItem		openItem;
    private MenuItem		saveItem;
    private MenuItem		saveAsItem;
    private MenuItem		tabularItem;
    private CheckboxMenuItem	autoRelabelItem;
    private MenuItem		undoItem;
    public  MenuItem		isDone;
    public  MenuItem		showAll;
    public  MenuItem		expandOne;
    public  MenuItem		showFinal;
    
    /**
     * The checkbox to hide or display name labels for to-dfa window
     */
    public CheckboxMenuItem showNameLabels;
    /**
     * The Environment that generated this Environment
     */
    public SourceWindow envSource;
    /**
     * the Environment that this Environment generated. When this is set the window 
     * is frozen
     */
    public Environment envTarget;
    /**
     * the environment with the tree for minimize machines
     */
    public Environment oldTree;
    /**
     * The bottom status line.
     */
    public Label		statusLine;
    /**
     * The acceptance choice for PDA.
     */
    public Choice		acceptanceChoice;

    /**
     * When isApplet is set, some items in the menu bar are disabled.
     */
    boolean 	done = false;

    /**
     *	handles the conversion from Regular Expression to FSA
     * only created if setRegExpToFSA() called
     */
    RegExptoFSAParser regexpconv;

    /**
     * The multiple input window
     */
    public MultipleInputWindow myMultiInputWindow;

  public static final String LABEL_WARNING_STR = "Labels can have only one letter when \n converting to a DFA. \n Click \"Ok\" to split them into multiple states.";
  public final static String    SHOW_GRAM               = "Convert to Grammar";
  public final static String    TO_REGEXP               = "Convert to Regular Expression";
  public final static String    DONE_STR                = "Check if done";
  public final static String    TO_DFA_STR              = "Convert to DFA";
  public final static String    SHOW_STR                = "Show";
  public final static String    EXPAND_STATE_STR        = "Expand a state";
  public final static String    SHOW_FINAL_STR          = "Show All";
  public final static String    NEXT_STEP_REGEXP_STR    = "Next Step";
  public final static String    SHOW_LABEL_STR          = "Show Name Labels";
  public final static String    MINIMIZE_STR            = "Minimize";
  public final static String    SHOW_UNREACHABLE_STR    = "Show unreachable states";
  public final static String	HLP_STATE_STR		= "States";
  public final static String	HLP_TRANSITION_STR	= "Transitions";
  public final static String	HLP_OVERVIEW_STR	= "Overview";
  public final static String	HLP_UPPERWINDOW_STR	= "Step Run Window";
  public final static String	HLP_TRACEWINDOW_STR	= "Trace Window";
  public final static String	HLP_MENUBAR_STR		= "Menu Bar";
  public final static String    HLP_NFATODFA_STR        = "NFA to DFA";
  public final static String    HLP_MINIMIZE_STR        = "Minimize a DFA";
  public final static String    AUTOMATON_IS_DETER_STR  = "The automaton is deterministic.";
  public final static String    AUTOMATON_IS_NON_DETER_STR = "The automaton is non deterministic.";
  public final static String    DFA_IS_CORRECT_STR      = " The DFA is correct.";
  public final static String    DONE_MINIMIZE_STR       = "Done";
  public final static String    SHOW_ALL_MINIMIZE_STR   = "Show All";

  final static String	SAVE_STRING		= "Save";
  final static String	NOTSAVE_STRING		= "Don't Save";
  final static String	CANCEL_STRING		= "Cancel";
  final static String[]	SAVE_BUTTONS		= {SAVE_STRING, NOTSAVE_STRING, CANCEL_STRING};

  final static int	NEW         = 0;
  final static int	OPEN        = 1;
  final static int	FASTRUN     = 2;
  final static int	PRINT       = 3;
  final static int	CLOSE       = 4;
  final static int	SHOWNONDETS = 5;
  final static int      TO_DFA      = 6;
  final static int      MINIMIZE    = 7;
  final static int      MULTI_RUN   = 8;
  
  private Panel inputLine;
  private Menu		filemenu, optionsmenu, runmenu, solvemenu, helpmenu;

//used only in starthread quit. to know if the window was enabled before the thread
// (only quit could be called while window was disabled)
  private boolean wasEnabled = true;

  public Environment(int type, String aFileName, MainMenu mainmenu, boolean isApplet)
  {
    super(mainmenu, isApplet);
    setLocation(mainmenu.getLocation().x+40, 20);
    this.type = type;
    this.addWindowListener(this);
    this.addKeyListener(this);
    this.setSize(Params._e_width, Params._e_height);
    if(type == Machine.MTC)
      d = new MinimizeDesktop(type);
    else  d = new Desktop(type);
    add("Center", d);
    setMachine(d);
    statusLine = new Label("", Label.LEFT);
    add("South", statusLine);
    isMinimizeDone = new Button(DONE_MINIMIZE_STR);
    isMinimizeDone.addActionListener(this);
    showAllMinimize = new Button(SHOW_ALL_MINIMIZE_STR);
    showAllMinimize.addActionListener(this);

    inputLine = new Panel();
    inputLine.add(inputName = new Label((type == Machine.TM2) ? "Input (tape 1):" : "Input String: "));
    inputLine.add(inputField = new TextField("", type == Machine.TM2 ? 23 : Params._e_inputfieldlength));	  
    if (type == Machine.FSA)
    {
      inputLine.add(new Label("      "));
    }
    else if (type == Machine.PDA)
    {
      inputLine.add(new Label("      Acceptance: "));
      inputLine.add(acceptanceChoice = new Choice());
      acceptanceChoice.addItem(PDA.EMPTY_STACK);
      acceptanceChoice.addItem(PDA.FINAL_STATE);
      acceptanceChoice.addItem(PDA.FINAL_AND_EMPTY);
      acceptanceChoice.select(((PDA) a).acceptanceType);
      acceptanceChoice.addItemListener(this);
    }
    else if (type == Machine.TM2)
    {
      inputLine.add(new Label("Input (tape 2):"));
      inputLine.add(inputField2 = new TextField("", 23));
    }
    inputLine.add(showAllMinimize);
    showAllMinimize.setVisible(false); 
    inputLine.add(isMinimizeDone);
    isMinimizeDone.setVisible(false);

    add("North", inputLine);
    putMenuBar();
    updateStatus("Mail any bugs or comments to: rodger@cs.duke.edu");
    if (!aFileName.equals(NO_FILE_NAME)) {
      String str = (aFileName.endsWith("."+strType())) ? aFileName: aFileName + "."+ strType();
      fileName = str;
      try {
        open(aFileName);
      }
      catch (Exception e) {
        updateStatus("Could not open file " + str + ". New file.");
      }
    }
    setIconImage(Params._img_icon);
  }

  public Dimension getPreferredSize() {
    return new Dimension(Params._e_width, Params._e_height);
  }


    public void setMachine(Desktop d) {
	switch (type) {
	case Machine.FSA:
	    a = new FSA(d);
	    break;
	case Machine.PDA:
	    a = new PDA(d);
	    break;
	case Machine.TM1:
	    a = new TM1(d);
	    break;
	case Machine.TM2:
	    a = new TM2(d);
	    break;
	case Machine.MTC:
	    a = new TreeMachine(d, this);
	    break;
	default:
	    setVisible(false);
	    System.err.println("Internal error. Please report.");
	    return;
	}
    }


  /**
   * Constructs the menu bar.
   */
  private void putMenuBar() {
    MenuBar 	mb = new MenuBar();
    Menu	m;
    MenuItem	item;

    mb.setFont(Params._m_font);
    m = new Menu("File");
    filemenu = m;
    m.setFont(Params._m_font);
    m.add(newItem = new MenuItem("New", new MenuShortcut('n')));
    newItem.addActionListener(this);
    m.add(openItem = new MenuItem("Open...", new MenuShortcut('o')));
    if (isApplet) openItem.setEnabled(false);
    openItem.addActionListener(this);
    m.add(saveItem = new MenuItem("Save", new MenuShortcut('s')));
    if (isApplet) saveItem.setEnabled(false);
    saveItem.addActionListener(this);
    m.add(saveAsItem = new MenuItem("Save as...", new MenuShortcut('S',true)));
    if (isApplet) saveAsItem.setEnabled(false);
    saveAsItem.addActionListener(this);
    m.addSeparator();
    Menu printMenu = new Menu("Print...");
    printMenu.add(tabularItem = new MenuItem("tabular", new MenuShortcut('P', true)));
    if (isApplet) tabularItem.setEnabled(false);
    tabularItem.addActionListener(this);
    printMenu.add(item = new MenuItem("postscript", new MenuShortcut('p')));
    if (isApplet) item.setEnabled(false);
    item.addActionListener(this);
    m.add(printMenu);
    m.addSeparator();
    m.add(item = new MenuItem(SourceWindow.STR_CLOSE, new MenuShortcut('c')));
    item.addActionListener(this);
    m.add(item = new MenuItem(SourceWindow.STR_QUIT, new MenuShortcut('q')));
    if (isApplet) item.setEnabled(false);
    item.addActionListener(mainmenu);
    mb.add(m);

    m = new Menu("Run");
    runmenu = m;
    m.setFont(Params._m_font);
    m.add(item = new MenuItem("Step run", new MenuShortcut('r')));
    item.addActionListener(this);
    m.add(item = new MenuItem("Fast run", new MenuShortcut('R', true)));
    item.addActionListener(this);
    m.add(item = new MenuItem("Multiple run", new MenuShortcut('M', true)));
    item.addActionListener(this);

    mb.add(m);

    m = new Menu("Options");
    optionsmenu = m;
    m.setFont(Params._m_font);
    m.add(undoItem= new MenuItem("Undo", new MenuShortcut('u')));
    undoItem.addActionListener(this);
    m.add(item = new MenuItem("Re-label states", new MenuShortcut('l')));
    item.addActionListener(this);
    m.add(item = new MenuItem("Show nondeterministic states", new MenuShortcut('w')));
    item.addActionListener(this);
    m.add(item = new MenuItem(SHOW_GRAM, new MenuShortcut('g')));
    item.addActionListener(this);
    if(type != Machine.FSA && type != Machine.PDA) item.setEnabled(false);    
    m.add(item = new MenuItem(TO_REGEXP, new MenuShortcut('e')));
    if(type != Machine.FSA) item.setEnabled(false);
    item.addActionListener(this);
    m.add(nfaToDfa = new MenuItem(TO_DFA_STR, new MenuShortcut('f')));
    nfaToDfa.addActionListener(this);
    if(type != Machine.FSA) nfaToDfa.setEnabled(false);
    m.add(item = new MenuItem(SHOW_UNREACHABLE_STR, new MenuShortcut('U', true)));
    item.addActionListener(this);    
    if(type != Machine.FSA) item.setEnabled(false);
    m.add(minimize = new MenuItem(MINIMIZE_STR, new MenuShortcut('m')));
    minimize.addActionListener(this);
    if(type != Machine.FSA) minimize.setEnabled(false);

    if (isApplet) {
      m.add(item = new MenuItem("Load example"));
      item.addActionListener(this);
    }

    m.addSeparator();
    //if(type == Machine.FSA) {
      m.add(showNameLabels = new CheckboxMenuItem(SHOW_LABEL_STR, false));
      showNameLabels.addItemListener(this);
      showNameLabels.setEnabled(true);
    //}
    m.add(autoRelabelItem = new CheckboxMenuItem("Auto re-label states"));
    autoRelabelItem.setState(d.autoRelabel);
    autoRelabelItem.addItemListener(this);

    mb.add(m);

    solvemenu = new Menu("Solve");
    solvemenu.add(isDone = new MenuItem(DONE_STR, new MenuShortcut('d')));
    isDone.addActionListener(this);
    solvemenu.add(showAll = new MenuItem(SHOW_STR, new MenuShortcut('h')));
    showAll.addActionListener(this);
    solvemenu.addSeparator();
    solvemenu.add(expandOne = new MenuItem(EXPAND_STATE_STR, new MenuShortcut('E', true)));
    expandOne.addActionListener(this);
    showFinal = new MenuItem(SHOW_FINAL_STR, new MenuShortcut('a'));
    showFinal.addActionListener(this);
    //mb.add(solvemenu);

    m = new Menu("Help");
    helpmenu = m;
    m.setFont(Params._m_font);
    m.add(item = new MenuItem("About..."));
    item.addActionListener(this);
    m.add(item = new MenuItem("Help HTML"));
    item.addActionListener(this);
    m.add(item = new MenuItem("Help Text"));
    item.addActionListener(this);
    mb.setHelpMenu(m);	// setHelpMenu is not working on all versions...


    setMenuBar(mb);
  }	// end of method putMenuBar.

  public void windowClosing(WindowEvent e) {
      startThread("quit");
  }
  public void windowOpened(WindowEvent e) 
  {}
  public void windowIconified(WindowEvent e)  
  {}
  public void windowDeiconified(WindowEvent e) 
  {}
  public void windowClosed(WindowEvent e)  
  {}
  public void windowActivated(WindowEvent e)
  {}
  public void windowDeactivated(WindowEvent e)
  {}

  public void mousePressed(MouseEvent e) 
  {}
  public void mouseReleased(MouseEvent e) 
  {
    updateStatus();
  }
  public void mouseEntered(MouseEvent e) 
  {}
  public void mouseExited(MouseEvent e) 
  {}
  public void mouseClicked(MouseEvent e)
  {}

  public void keyPressed(KeyEvent e) 
  {}
  public void keyReleased(KeyEvent e) 
  {
    updateStatus();
  }
  public void keyTyped(KeyEvent e)
  {}

  public void setAutoRelabel()
  {
    d.autoRelabel = true;
    autoRelabelItem.setState(d.autoRelabel);
  }
    
  public void itemStateChanged(ItemEvent e){
    
    if (e.getSource() instanceof Choice){
      ((PDA) a).acceptanceType = acceptanceChoice.getSelectedItem();
    } 
    else if (e.getSource() instanceof CheckboxMenuItem){
      String arg = ((CheckboxMenuItem)(e.getSource())).getLabel();
      if (arg.equals("Auto re-label states")){
        d.autoRelabel = ((CheckboxMenuItem) e.getSource()).getState();
        d.relabel();
      }
      else if (e.getSource() == showNameLabels)
      {
        d.showStateLabels = ((CheckboxMenuItem) e.getSource()).getState();
	if(dfa != null)
	   dfa.d.showStateLabels = d.showStateLabels;
	else
	   a.d.showStateLabels = d.showStateLabels;

        d.repaint();
      }
    }
  }
  
  public void actionPerformed(ActionEvent e){

    if (e.getSource() instanceof MenuItem) {
	String arg = ((MenuItem)(e.getSource())).getLabel();
	if (arg.equals("New"))
	    startThread("newone");
	else if ("Open...".equals(arg)) 
	    startThread("open");
	else if ("Save as...".equals(arg)) 
	  saveAs();
	else if ("Save".equals(arg))
	  save();
	else if (SourceWindow.STR_CLOSE.equals(arg)) 
	    startThread("quit");
	else if ("Step run".equals(arg))
	  startThread("stepRun");
	else if ("Fast run".equals(arg)) 
	    startThread("fastRun");
	else if("Multiple run".equals(arg)) {
	    if(myMultiInputWindow == null) {
		if(type == Machine.TM2)	myMultiInputWindow = new MultipleInputTwoTapeWindow(this, Params._e_backcolor);
		else myMultiInputWindow = new MultipleInputWindow(this, Params._e_backcolor);
	    }
	    myMultiInputWindow.setVisible(true);
	    myMultiInputWindow.toFront();
	}
	else if ("Re-label states".equals(arg))
	  relabel();
	else if ("Undo".equals(arg)) {
	    if(d.undoDesktop != null) {
		Desktop tempdesk = d;
		add("Center", d.undoDesktop);
		validate();
		remove(d);
		d = d.undoDesktop;
		d.undoDesktop = tempdesk;
		d.undoDesktop.undoDesktop = null;
		undoItem.setLabel("Redo");
		d.undoItem = undoItem;
		setMachine(d);
	    }
	}
	else if ("Redo".equals(arg)) {
	    if(d.undoDesktop != null) {
		Desktop tempdesk = d;
		add("Center", d.undoDesktop);
		remove(d);
		validate();
		d = d.undoDesktop;
		d.undoDesktop = tempdesk;
		d.undoDesktop.undoDesktop = null;
		undoItem.setLabel("Undo");
		setMachine(d);
	    }
	}
	else if ("Show nondeterministic states".equals(arg)) 
	  startThread("showNondets");
	else if (SHOW_UNREACHABLE_STR.equals(arg))
	  startThread("showUnreachables");
	else if (MINIMIZE_STR.equals(arg)) 
	  startThread("minimize");
	else if ("About...".equals(arg))
	  about();
	else if ("Help HTML".equals(arg))
        {
	  helpHTML();
          if(type == Machine.MTC)
             mainmenu.myHelpWindow.setCurrentTopic("The DFA Minimizing option");
        }
	else if ("Help Text".equals(arg))
        {
	  helpText();
          if(type == Machine.MTC)
             mainmenu.myHelpWindow.setCurrentTopic("The DFA Minimizing option");
        }
	else if ("Load example".equals(arg))
	  loadExample();
	else if ("tabular".equals(arg)) 
	  startThread("print");
	else if ("postscript".equals(arg)) 
	  printPostscript();
	else if(TO_DFA_STR.equals(arg))
	  startThread("to_dfa");
	else if(SHOW_GRAM.equals(arg))
	  startThread("showGrammar");
        else if(TO_REGEXP.equals(arg))
	  to_RegularExp();
	else if (e.getSource() == showAll)
	  showDfa();
	else if (e.getSource() == expandOne)
	{
	    if(type == Machine.FSA && a.mode == Machine.rextoFSA)
	    {
		if(dfa == null) return;
		String erro = ((FSA)a).checkrextoFSADone(dfa);
		if(erro.equals(FSA.FSA_CORRECT_STR))
		{
		   expandState();
		}
		else
	        {
		   startThread("checkDone");
		} 
	    }
	    else
	    {
	       expandState();
	    }
	    this.pack();
	}
	else if (e.getSource() == isDone)
          startThread("checkDone");
        else if (e.getSource() == showFinal)
	{
	  while(regexpconv.stepNext() != null);
	  showDfa();
	  inputName.setText("Input String: ");
	  inputField.setVisible(true);
	  hideSolveMenu();
	  this.validate();
	  this.pack();
	}
        else {
	 OKDialog dg = new OKDialog("This feature is not implemented", this, "Sorry...");
	 dg.show(); 
	}
    }
    else if (e.getSource() instanceof Button)
    {
	String arg = ((Button)(e.getSource())).getLabel();
	if(DONE_MINIMIZE_STR.equals(arg))
	   startThread("createMinimizeFinal");
	else if (SHOW_ALL_MINIMIZE_STR.equals(arg)) 
	   ((TreeMachine)a).showAllMinimize();
	else {
	   OKDialog dg = new OKDialog("This feature is not implemented", this, "Sorry...");
	   dg.show(); 
      }
    }
    repaint(); 
  }	// end of method actionperformed.
  
  /**
   * Relabels the states by calling relabel from the Desktop.
   */
  public void relabel()
  {
    a.d.relabel();
    a.d.repaint();
    updateStatus("Relabel completed.");
  }

  /** 
    * Pops up a menu and prints the window to a postscript file 
    */
  public void printPostscript() {
    
    try {
      PrintJob pj = this.getToolkit().getPrintJob(this, "Print Screen", null);
      Graphics pg = pj.getGraphics();
      this.printComponents(pg);
      pg.dispose();
      pj.end();
      updateStatus("Print completed.");
    } catch (Exception e) {
      updateStatus("");
      if(e.getMessage() !=null && !e.getMessage().trim().equals("")) {   //cancel button 
	OKDialog dg = new OKDialog("Could not print current machine.\n" + e.getMessage(),
				   this, strType() + " Print Error");
	dg.show();
      }
    }
  }
  
  /**
   * Shows the print dialog and prints the machine in tabular form.
   */
  public void print() {
    PrintDialog pd = new PrintDialog(this);
    String result;
    String header = "Machine type: " + strType() + "\n\n";
    String[] env = new String[1];
    env[0] = "LD_LIBRARY_PATH=" + System.getProperty("LD_LIBRARY_PATH");
 
    result = pd.findResult();
  
    if ("Print".equals(result)) {
      if (pd.printFormat == PrintDialog.TABULAR) {
        try {
          updateStatus("Printing...");
          String filename = (pd.destination == PrintDialog.PRINTER) ? 
          	"tabular.doc" : pd.getFileName();
          if (!filename.startsWith(MainMenu.fileSeparator))
            filename = MainMenu.workingDirectory + filename;
          PrintWriter out = new PrintWriter(new FileOutputStream(filename));
          out.print(header + a.d.toTabular());
          out.close();
          if (pd.destination == PrintDialog.PRINTER) {
            if (MainMenu.osName.equals(Params._sys_macos))
              throw (new Exception("Not implemented for this platform."));
            Process p = Runtime.getRuntime().exec(pd.getCommand() 
			+ " " + filename);
            int code = p.waitFor();
            if (code != 0) 
              throw (new Exception("Printing program error. Exit code "+code));
          }
          if (pd.destination == PrintDialog.PRINTER) {
            String deleteCommand = "";
            if (MainMenu.osName.equals(Params._sys_win95))
              deleteCommand = "del tabular.doc";
            else
              deleteCommand = "/usr/bin/rm tabular.doc";
            Process p = Runtime.getRuntime().exec(deleteCommand);
          }            
          updateStatus("Print completed.");
        } catch (InterruptedException e) {
        } catch (Exception e) {
          updateStatus("");
          OKDialog dg = new OKDialog("Could not print current machine.\n"
                       + e.getMessage(), this, strType() + " Print Error");
          dg.findResult();
        }
      }
    }	// end of if ("Print".equals...
    setEnabled(true);
  }

  /**
   * Opens a dialog and focuses the nondeterministic states.
   * These are found by calling showNondets from Machine.
   * @see	flap.Machine
   */
  public void  showNondets() {
    String error = a.checkMachine();
    if (error == null)
      if (a.showNondets()) {
        a.d.repaint();
        OKDialog dg = new OKDialog("\nThe nondeterministic states\nare highlighted.",
        	this, strType() + " Message");
	dg.findResult();

        a.d.unfocusAll();
        a.d.repaint();
      } else {
        OKDialog dg = new OKDialog(AUTOMATON_IS_DETER_STR, this,
        	strType() + " Message");
        dg.findResult();
      }
    else {
      OKDialog dg = new OKDialog(error, this, strType() + " Error");
      dg.findResult();
    }
    setEnabled(true);
  }

  /**
    * Checks if the machine is deterministic or has any errors and returns an 
    * appropriate String.
    */
  public String checkDeterministic() {
      String error = a.checkMachine();
      if (error != null )
	  return error;
      if (a.showNondets() == false)
	  return AUTOMATON_IS_DETER_STR;
      a.d.unfocusAll();
      a.d.repaint();
      return AUTOMATON_IS_NON_DETER_STR;
  }

  /**
    * sets the FSA to be a DFA builder
    */
  public void setToDfa(Environment NFA)
  {
    Desktop temp;
    
    setEnvSource(NFA);
    NFA.setEnvTarget(this);
    temp = new Desktop(Machine.FSA);
    dfa = new FSA(temp);
    ((FSA)dfa).buildDfa((FSA) NFA.a);
    updateStatus("Mail any bugs or comments to: rodger@cs.duke.edu");
    nfaToDfa.setEnabled(false);
    
    showSolveMenu();
    solvemenu.removeAll();
    solvemenu.add(isDone);
    solvemenu.add(showAll);
    solvemenu.addSeparator();
    solvemenu.add(expandOne);
    expandOne.setLabel(EXPAND_STATE_STR);

    showNameLabels.setEnabled(true);
    showNameLabels.setState(true);

    d.showStateLabels = true;
    d.showDfaLabels = true;
    dfa.d.showStateLabels = true;
    dfa.d.showDfaLabels = true;
    a.mode = Machine.NFAtoDFA;
    dfa.mode = Machine.NFAtoDFA;
  }

  /**
   * sets the FSA to be a Regular Expression to FSA converter/builder
   */
  public void setRegExpToFSA(String parsestr, RegExptoFSAParser parse)
  {
      a.mode = Machine.rextoFSA;
      dfa = new FSA(new Desktop(Machine.FSA));
      dfa.mode = Machine.rextoFSA;
      d.showStateLabels = false;
      d.showDfaLabels = false;
      dfa.d.showStateLabels = false;
      dfa.d.showDfaLabels = false;
      updateStatus("Mail any bugs or comments to: rodger@cs.duke.edu");

      showSolveMenu();
      solvemenu.removeAll();
      solvemenu.add(isDone);
      solvemenu.add(showAll);
      solvemenu.add(showFinal);
      solvemenu.addSeparator();
      solvemenu.add(expandOne);
      expandOne.setLabel(NEXT_STEP_REGEXP_STR);
      
      inputField.setVisible(false);
      regexpconv = parse;
      String val = regexpconv.stepNext();
      if(val != null)
      {
	 inputName.setText("Create the Regular Expression: " + val);
      }
      pack();
  }

    /**
     *  create the final minimize window if the tree is correct
     */ 
    public void createMinimizeFinal() {
	if(((TreeMachine) a).checkMinimizeDone())
        {   
	    Environment f =  new Environment(Machine.FSA, 
			   Environment.NO_FILE_NAME, mainmenu, isApplet);
	    f.setToMinimizeFinal(this);
	    f.setVisible(true);
	    f.pack();
	}
        setEnabled(true);
    }

    /**
     * sets to a final minimized DFA window
     */
  public void setToMinimizeFinal(Environment theSource) {
    Desktop temp;
    int trap;
    oldTree = theSource;
    setEnvSource((Environment) (theSource.envSource));
    ((Environment) theSource.envSource).setEnvTarget(this);
    theSource.setEnabled(false);
    theSource.setEnvSource(null);
    temp = new Desktop(Machine.FSA);
    dfa = new FSA(temp);
    ((FSA)a).putMinimizedStates((TreeMachine) theSource.a);
    ((FSA)dfa).buildMinimized((TreeMachine) theSource.a, (FSA) ((Environment)envSource).a);
    updateStatus("Mail any bugs or comments to : rodger@cs.duke.edu");
    minimize.setEnabled(false);
    
    showSolveMenu();
    solvemenu.removeAll();
    solvemenu.add(isDone);
    solvemenu.add(showAll);
    solvemenu.addSeparator();
    solvemenu.add(expandOne);
    expandOne.setLabel(EXPAND_STATE_STR);

    showNameLabels.setEnabled(true);
    showNameLabels.setState(true);

    d.showStateLabels = true;
    d.showDfaLabels = true;
    dfa.d.showStateLabels = true;
    dfa.d.showDfaLabels = true;

    validate();
    a.mode = Machine.MINIMIZE;
    dfa.mode = Machine.MINIMIZE;
  }

  /**
    * sets the Environment to be a Minimize Tree environment
    */
  public void setToMinimizeTree(Environment theSource)
  {
    setEnvSource(theSource);
    theSource.setEnvTarget(this);
    theSource.d.showStateLabels = false;
    theSource.d.showDfaLabels = false;
    theSource.d.repaint();
    d.showStateLabels = true;
    d.showDfaLabels = true;
    d.showStringLabel = false;
    runmenu.setEnabled(false);
    optionsmenu.setEnabled(false);
    solvemenu.setEnabled(false);
    helpmenu.setEnabled(true);
    newItem.setEnabled(false);
    openItem.setEnabled(false);
    saveItem.setEnabled(false);
    saveAsItem.setEnabled(false);
    tabularItem.setEnabled(false);
    isMinimizeDone.setVisible(true);
    showAllMinimize.setVisible(true);
    inputField.setVisible(false);
    inputName.setVisible(false);
    int trap = ((TreeMachine) a).startTree(((Environment)envSource).a);
    trap = (trap != -1) ? (updateStatus("State q" + trap + " is the implicit trap state.")) : (updateStatus("Mail any bugs or comments to : rodger@cs.duke.edu"));
    validate();
  }

  /**
   * Shows the solve menu
   */
  public void showSolveMenu()
  {
    MenuBar mb = getMenuBar();
    if(mb != null)
    {
      //removing helpmenu causes null pointer exception
      //mb.remove(helpmenu);
      solvemenu.setEnabled(true);
      mb.add(solvemenu);
      //mb.add(helpmenu);
    }
  }

  /**
   *  Hides the solve menu
   */
  public void hideSolveMenu()
  {
    MenuBar mb = getMenuBar();
    if(mb != null)
      mb.remove(solvemenu);
  }

  /**
    * Sets the source Environment that generated this Environment
    */
  public void setEnvSource(Environment source) {
    envSource = source;
  }

  /**
    * Sets the Environment that is generated by this environment
    */
  public void setEnvTarget(Environment target) {
    envTarget = target;
    setEnabled(target == null);
  }

  public SourceWindow getEnvSource() {
    return envSource;
  }

  /**
    * Show the regular grammar corresponding to this FSA 
    */
  public void showGrammar()
  {
    if(d.initialState == null)
    {
      String erro = "\n\nThe machine has no initial state";
      OKDialog dg = new OKDialog(erro, this, "Grammar error.");
      dg.show();
      setEnabled(true);
      return;
    }

    ToGrammar tog;
    Grammar g;
    Vector replaced;
    try
    {
       ToGrammarDialog dg = new ToGrammarDialog();
       dg.setLocation( 
	  (Toolkit.getDefaultToolkit().getScreenSize().width - dg.getSize().width) / 2,
	  (Toolkit.getDefaultToolkit().getScreenSize().height - dg.getSize().height) / 2);

       if(type == Machine.FSA)
 	 tog = new FSAToGrammar(d);
       else 
	 tog = new PDAToGrammar(d, dg);

       d.unfocusAll();
       d.unselectTransitions();

       String str;
       Vector stepRules = tog.stepNext();
       while(stepRules != null && !stepRules.isEmpty())
       {
	  d.repaint();
	  dg.setRules(stepRules);
	  str = dg.findResult();
	  d.unfocusAll();
	  d.unselectTransitions();
          if(str.equals(ToGrammarDialog.SHOW))
	     stepRules = null;
	  else
	     stepRules = tog.stepNext();
       }
       dg.setVisible(false);
       dg.dispose();

       g = new Grammar(tog.toGramm());
       replaced = g.simplify();
       g.sortRules();
    }
    catch(BadInputException e)
    {
       if(!e.getMessage().equals(""))
       {
	 OKDialog dg = new OKDialog(e.getMessage(),
	    null, "Grammar conversion error");
	 d.repaint();
	 dg.findResult();
       }
       d.unfocusAll();
       d.unselectTransitions();
       d.repaint();
       setEnabled(true);
       return;
    }
    
    
    if(!replaced.isEmpty())
    {
       String replaceStr = "";
       String[] terms;
       for(Enumeration e = replaced.elements(); e.hasMoreElements(); )
       {
	  terms = (String[]) e.nextElement();
	  replaceStr += "\n\"" + terms[0] +
		"\" will become \"" + terms[1] + "\"";
       }

       OKDialog dg = new OKDialog(
	 "WARNING: In the grammar, all\n" +
	 "terminals are lowercase. The following\n" +
	 "modifications will be made to the\n" +
	 "resulting alphabet:\n" + replaceStr,
	 this, "Warning");

       d.unfocusAll();
       d.unselectTransitions();
       d.repaint();

       dg.findResult();
    }

    setEnabled(true);
    GrammarInputWindow giw = (GrammarInputWindow) mainmenu.createEnvironment(Machine.GRA, Environment.NO_FILE_NAME);
    giw.setLocation( 
       (Toolkit.getDefaultToolkit().getScreenSize().width - giw.getSize().width) / 2,
       (Toolkit.getDefaultToolkit().getScreenSize().height - giw.getSize().height) / 2);

    giw.show();
    d.showStringLabel = false;
    showNameLabels.setState(true);
    giw.displayGrammar(g);
  }

  /**
    * Create regular expression corresponding to this FSA
    */
  public void to_RegularExp()
  {

    if(d.initialState == null) {
      String erro = "\n\nThe machine has no initial state";
      OKDialog dg = new OKDialog(erro, this, "Regular Expression error.");
      dg.show(); 
      return;
    }

     //ToRegExp tre;
     try
     {
	 relabel();
	 FSAtoRegExpAskDialog conv = new FSAtoRegExpAskDialog(mainmenu, d);
	 conv.setLocation(
	    (Toolkit.getDefaultToolkit().getScreenSize().width - conv.getSize().width) / 2,
	    (Toolkit.getDefaultToolkit().getScreenSize().height - conv.getSize().height) / 2);
	 conv.show();
     }
     catch (BadInputException h) 
     {
	 OKDialog dg = new OKDialog(h.getMessage(), this, "Regular Expression error.");
	 dg.show();
     }
  }

  /**
    * Show the answer to the DFA conversion problem
    */
  public void showDfa() {
    if (dfa == null)
      return;

    Machine temp = a;
    if(type == Machine.FSA)
    {
       a = new FSA((FSA)dfa);
    }
    else
    {
       a = new PDA((PDA)dfa);
    }
    d = a.d;

    this.add("Center", d);
    temp.d.setVisible(false);
    d.setVisible(true);
    validate();
    a.placeAllStates(temp,
       (a.mode == Machine.MINIMIZE ||
        a.mode == Machine.NFAtoDFA) ? (FSA) (((Environment)envSource).a) : null);
    d.repaint();

    if(a.mode != Machine.rextoFSA)
    {
       if(type == Machine.FSA)
       	 nfaToDfa.setEnabled(true);

       //make it display normal labels again
       d.showDfaLabels = false;
       d.showStringLabel = false;
       if(dfa != null)
       {
	  dfa.d.showDfaLabels = false;
	  dfa.d.showStringLabel = false;
       }
       else
       {
	  a.d.showDfaLabels = false;
	  a.d.showStringLabel = false;
       }
       hideSolveMenu();
    }
  }

  /**
    * Handles the checking to see if DFA and NFA are identical
    * also used to handle the checking of whether the Regular
    * expression NFA (called variable dfa) and the user NFA are
    * the same
    */
  public void checkDone()
  {
    String erro = AUTOMATON_IS_DETER_STR;
    
    if (dfa == null)
      return;

    d.unmarkAllStates();
    dfa.d.unmarkAllStates();
    d.unselectTransitions();
    d.unfocusAll();

    if(type == Machine.FSA && a.mode == Machine.regtoFSA) {
      erro = a.checkMachine();
      if(erro != null) {
          OKDialog dg = new OKDialog(erro, this, "Automaton check.");
          dg.findResult(); 
      }
      erro = ((FSA)a).checkregtoFSADone(dfa);
      d.repaint();
      OKDialog dg = new OKDialog(erro, this, "Automaton check.");
      dg.findResult(); 
    }
    else if(type == Machine.FSA && a.mode == Machine.rextoFSA)
    {
      erro = ((FSA)a).checkrextoFSADone(dfa);
      d.repaint();
      OKDialog dg = new OKDialog(erro, this, "Automaton check.");
      dg.findResult();
    }
    else
    {
      if(type == Machine.FSA)
        erro = checkDeterministic();
      if(erro != AUTOMATON_IS_DETER_STR)
      {  
        a.showNondets();
        if( erro.equals(AUTOMATON_IS_NON_DETER_STR))
          erro = "\nThe highlighted state(s) are \nnondeterministic";
        OKDialog dg = new OKDialog(erro, this, "Automaton check.");
        dg.findResult(); 
      } else if(type == Machine.FSA && checkMultiLetterLabels()) {
        erro = "\nYour automaton should have no multiple \n letter labels after being converted.";
        OKDialog dg = new OKDialog(erro, this, "Automaton check.");
        dg.findResult(); 
      } else {
        erro =  a.checkIfDone(dfa, envSource instanceof Environment ? 
                              ((Environment)envSource).a : null);
        d.repaint(0);
        OKDialog dg = new OKDialog(erro, this, "Automaton check.");
        dg.findResult();
	if(erro.equals(DFA_IS_CORRECT_STR))
	{
	   nfaToDfa.setEnabled(true);

	   //make it display normal labels again
           d.showDfaLabels = false;
	   d.showStringLabel = false;
	   if(dfa != null)
	   {
	      dfa.d.showDfaLabels = false;
	      dfa.d.showStringLabel = false;
	   }
	   else
	   {
	      a.d.showDfaLabels = false;
	      a.d.showStringLabel = false;
	   }
	   hideSolveMenu();
	}
      }
    }
    d.unfocusAll();
    d.repaint();
    d.selectedState = null;
    setEnabled(true);
    return;
  }


  /**
    * Handles the checking to see if the building is done.
    * no popup messages.
    */
  public boolean checkBuildDone() {

    if(a.mode == 0)
      return true;

    String erro = AUTOMATON_IS_DETER_STR;
    
    if (dfa == null)
      return true;

    d.unmarkAllStates();
    dfa.d.unmarkAllStates();
    d.unselectTransitions();

    if(type == Machine.FSA && a.mode == Machine.regtoFSA) {
      if(a.checkMachine() != null)
        return false;
      erro = ((FSA)a).checkregtoFSADone(dfa);
    }
    else if(type == Machine.FSA && a.mode == Machine.rextoFSA)
    {
      if(a.checkMachine() != null)
        return false;
      erro = ((FSA)a).checkrextoFSADone(dfa);
    }
    else
    { 
      if(type == Machine.FSA)
        erro = checkDeterministic();
      if(erro != AUTOMATON_IS_DETER_STR) 
        return false;
      if(type == Machine.FSA && checkMultiLetterLabels()) 
        return false;
      else {
        erro =  a.checkIfDone(dfa, envSource instanceof Environment ? 
                              ((Environment)envSource).a : null);
      }
    }

    d.unfocusAll();
    d.repaint();
    d.selectedState = null;
    if(erro.equals("\n\nThe PDA is correct.") || erro.equals(Environment.DFA_IS_CORRECT_STR) || erro.equals("\n\nThe FSA is correct."))
      return true;
    return false;
  }

  /**
    * Solves only the selected State in the NFA to DFA window
    */
  public void expandState()
  {
    String answer;
    if(a.mode == Machine.regtoFSA)
    {
       answer = ((FSA) a).expandStringLabelState((FSA)dfa);
    }
    else if(a.mode == Machine.rextoFSA)
    {
       if(regexpconv != null)
       {
	 String val = regexpconv.stepNext();
	 if(val != null)
	 {
            val = RegExpUtilities.minimizeParenthesis(val);
	    inputName.setText("Create the Regular Expression: " + val);
	    inputName.setSize(10, 200); //set the size so it will relayout
            inputLine.validate();
	    this.validate();
	 }
	 else
	 {
	    expandOne.setEnabled(false);
	    OKDialog dg = new OKDialog("Regular Expression conversion\nis complete", 
	       this, "Regular Expression Conversion.");
	    dg.show();
	    inputName.setText("Input String: ");
	    inputName.setSize(10, 200);
	    inputField.setVisible(true);
            inputLine.validate();
            this.validate();
	    hideSolveMenu();
	 }
       }
       return;
    }
    else
    {
       answer = ((FSA) a).expandState((FSA)dfa, (FSA) (((Environment)envSource).a));
    }

    if(!answer.equals(DFA_IS_CORRECT_STR))
    {
       OKDialog dg = new OKDialog(answer, this, "Expand State.");
       dg.show();
    }
    d.repaint();
  }
  
  /**
    * Check if any of the transitions have more than one letter.
    */
  public boolean checkMultiLetterLabels() {
    LabelSeparator breaker = new LabelSeparator((FSA) a);
    return breaker.checkMultiLetterLabels();
  }

  /**
    * Breaks up all multiple letter labels on the desktop into several states.
    */
  public void breakupMultipleLabels() {
    LabelSeparator breaker = new LabelSeparator((FSA) a);
    breaker.breakupMultipleLabels();
  }

  /**
    * minimizes the number of states in the FSA
    */
  public void minimize() {
    String error = null;

    if(d.initialState == null)
      error = "\n\nThe machine has no initial state.";
    else if(!a.showUnreachables().equals(FSA.NO_UNREACHABLES_STR)) { 
      error = "You must remove all unreachable \n states first. (you can use the \n\"Show unreachable\" button for help)";
      d.unfocusAll();
    } else if( checkDeterministic().equals(AUTOMATON_IS_NON_DETER_STR)) {
      a.showNondets();
      error = "The highlighted states are nondeterministic. \n Nondeterministic machines cannot be \n minimized";
    } else if (!checkDeterministic().equals(AUTOMATON_IS_DETER_STR)) 
      error = checkDeterministic();
    if(error != null) {
      OKDialog dg = new OKDialog(error, this, "Minimize.");
      d.repaint();
      dg.findResult();
      d.unfocusAll();
      d.repaint();
      setEnabled(true);
      return;
    }

    if(checkMultiLetterLabels() == true ) {
      TextDialog dg = new TextDialog(LABEL_WARNING_STR, MainMenu.OK_CANCEL_STR,
					  strType() + " Warning");
      String pushed = dg.findResult();
      if (pushed.equals(MainMenu.OK_STR)) 
	breakupMultipleLabels();
      setEnabled(true);
      return;
    } 

    Environment f =  new Environment(Machine.MTC, Environment.NO_FILE_NAME, mainmenu, isApplet);
    f.setToMinimizeTree(this);
    f.setVisible(true);
    f.pack();
    ((MinimizeDesktop) f.d).placeStates();
    setEnabled(true);
  }


  /**
   * Shows the states which are unreachable from
   * the initial state.
   */
  public void showUnreachables() {
     OKDialog dg = new OKDialog(a.showUnreachables(), this, "Unreachable States.");
     d.repaint();
     dg.findResult();
     d.unfocusAll();
     d.repaint();
     setEnabled(true);
  }

  /**
   * Removes the current desktop and opens a brand new one.
   */
  public void newone()
  {
    hideSolveMenu();
    boolean stringlabels = d.showStringLabel;
    boolean showstatelabels = d.showStateLabels;

    if (!testSavedAndProceed())
      return;
    remove(d);
    d = new Desktop(type);

    switch (type) {
      case Machine.FSA:
        a = new FSA(d);
	d.showDfaLabels = showNameLabels.getState();
        d.showStringLabel = stringlabels;
        break;
      case Machine.PDA:
        a = new PDA(d);
        break;
      case Machine.TM1:
        a = new TM1(d);
        break;
      case Machine.TM2:
        a = new TM2(d);
        inputField2.setText("");
        break;
    }
    d.showStateLabels = showstatelabels;
    inputField.setText("");
    add("Center", d);
    fileName = NO_FILE_NAME;
    doLayout();
    setEnvSource(null);
    updateStatus();
    setEnabled(true);
  }


  /**
   * Shows a FileDialog and tries to open the selected file.
   */
  public void open()
  {
      if (!testSavedAndProceed()) {
	  setEnabled(true);
	  return;
      }
    FileDialog fd = new FileDialog(this, "Open " + strType(), FileDialog.LOAD);
    fd.setBackground(Params._dg_backcolor);
    if (currentDir != null)
      fd.setDirectory(currentDir);
    fd.show();
    String strFile = fd.getFile();
    String strDir = fd.getDirectory();
    if (strDir == null || strFile == null) {
	setEnabled(true);
	return;
    }
    if (strFile.endsWith(".*.*"))  // ugly bug in JDK 1.0.2 for win32.
      strFile = strFile.substring(0, strFile.length() - 4);
    if (!strDir.endsWith(MainMenu.fileSeparator))
      strDir += MainMenu.fileSeparator;
    currentDir = strDir;
    String aName = strDir + strFile;
    if (!aName.endsWith("." + strType()))
      aName += "." + strType();
    try {
      open(aName);
    }
    catch (FileNotFoundException e) {
      OKDialog dg = new OKDialog("Could not find file\n"+"'" + aName + "'", 
		this, strType() + " Open Error");
      dg.findResult();
    }
    catch (EOFException e) {
      OKDialog dg = new OKDialog("Unexpected end of file.", this, 
		strType() + " Open Error");
      dg.findResult();
    }
    catch (NumberFormatException e) {
      OKDialog dg = new OKDialog("Numbers not found where\nthey were needed...",
		this, strType() + " Open Error");
      dg.findResult();
    }
    catch (IOException e) {
      OKDialog dg = new OKDialog("An I/O error occured.", this, 
		strType() + " Open Error");
      dg.findResult();
    }
    catch (BadInputException e) {
      OKDialog dg = new OKDialog("File corrupted" + e.getMessage(),
		this, strType() + " Open Error");
      dg.findResult();
    }
    hideSolveMenu();
    setEnabled(true);
  }

  /**
   * Tries to open the given file.
   * @exception flap.BadInputException  if the input is badly formed
   * @exception IOException if the file cannot be read correctly
   * @exception NumberFormatException if the file contains a string isntead of a number
   * @exception EOFException if the file ends too early
   */
  public void open(String aName) throws BadInputException, IOException, NumberFormatException, EOFException {
    boolean	answer = false;
    FileRead	fr = new FileRead();

    if (!aName.endsWith("." + strType()))
      aName += "." + strType();
    fr.read(aName);
    if (fr.a != null) {	// just in case...
      a = fr.a;
      remove(d);
      
      //save the show label state
      /*if(a.mode != Machine.regtoFSA)
	 a.d.showDfaLabels = d.showDfaLabels;
      else
         a.d.showStringLabel = d.showStringLabel;
      */
      a.d.showStateLabels = d.showStateLabels;
      
      d = a.d;
      add("Center", d);
    }
    if(type == Machine.PDA)
	acceptanceChoice.select(((PDA) a).acceptanceType);    
    inputField.setText("");
    doLayout();
    fileName = aName;
    hideSolveMenu();
    updateStatus();
  }

  /**
   * Loads an example machine from a URL given in Params.
   * Applies only when isApplet is true.
   */
  public void loadExample() {
    String exampleurl = Params._e_codebase + Params._e_exampleurl + strType();
    
    try
    {
      URL url = new URL(exampleurl);
      InputStream is = url.openStream();
      FileRead fr = new FileRead();
      fr.setStream(is);
      fr.parseStream();
      if (fr.a != null) {	// just in case...
        a = fr.a;
        remove(d);
        d = a.d;
        add("Center", d);
        updateStatus("Example loaded from " + exampleurl);
      }
      inputField.setText("");
      doLayout();
    } catch (Exception e) {
      OKDialog dg = new OKDialog("Error while loading or parsing.\n" +
         exampleurl + ":\n" + e.getMessage(), this, "Load Example Error");
      dg.show();
    }         
  }
    
  /**
   * Tries to save the current machine.
   */
  public boolean save() {
    if (NO_FILE_NAME.equals(fileName))
      return saveAs();
    else
      return save(fileName);
  }

  /**
   * Opens a FileDialog and tries to save the current machine
   * in the specified file.
   */
  public boolean saveAs() {
    FileDialog fd = new FileDialog(this, "Save " + strType(), FileDialog.SAVE);
    if (currentDir != null)
      fd.setDirectory(currentDir);
    fd.setBackground(Params._dg_backcolor);
    fd.show();
    String strFile = fd.getFile();
    String strDir = fd.getDirectory();
    if (strDir == null || strFile == null)
      return false;
    if (strFile.endsWith(".*.*"))  // ugly bug in JDK 1.0.2 for win32.
      strFile = strFile.substring(0, strFile.length() - 4);
    if (!strDir.endsWith(MainMenu.fileSeparator))
      strDir += MainMenu.fileSeparator;
    currentDir = strDir;
    String aName = strDir + strFile;
    return save(aName);
  }

  /**
   * Tries to save the current machine in the file given
   * by the name.
   */
  public boolean save(String aName) {
    boolean answer = false;
    FileWrite fw = new FileWrite(a);

    try {
      if (aName.trim().endsWith(MainMenu.fileSeparator))
        throw (new Exception("Not a valid file name.\n"));
      if (!aName.endsWith("."+strType()))
        aName += "." + strType();
      fw.write(aName);
      fileName = aName;
      a.d.modified = false;
      updateStatus("File saved");
      answer = true;
    }
    catch (IOException e) {
      OKDialog dg = new OKDialog("I/O Error.\nFile not saved.\n"
		+ e.getMessage(), this, strType() + " Save Error");
      dg.show();
    }
    catch (NoSuchElementException e) {
      OKDialog dg = new OKDialog("Tokenizer Error.\nFile not saved.\n"
		+ e.getMessage(), this, strType() + " Save Error");
      dg.show();
    }
    catch (Exception e) {
      OKDialog dg = new OKDialog("File Error.\nFile not saved.\n"
		+ e.getMessage(), this, strType() + " Save Error");
      dg.show();
    }
    return answer;
  }	// end of method save(String).

  public void setEnabled(boolean b) {
    super.setEnabled(b);
    runmenu.setEnabled(b);
    filemenu.setEnabled(b);
    optionsmenu.setEnabled(b);
    solvemenu.setEnabled(b);
    if(myMultiInputWindow != null)
	myMultiInputWindow.setEnabled(b);
  }

  public void to_dfa() {
    String determ = checkDeterministic();
    
    if(!determ.equals(AUTOMATON_IS_NON_DETER_STR)) {
      OKDialog dg = new OKDialog(determ, this, strType() + " Message");
      dg.findResult();
      setEnabled(true);
    }
    else if(checkMultiLetterLabels() == true ) {
      TextDialog dg = new TextDialog( LABEL_WARNING_STR, MainMenu.OK_CANCEL_STR,
					   strType() + " Warning");
      String pushed = dg.findResult();
      if (pushed.equals(MainMenu.OK_STR)) 
	breakupMultipleLabels();
      setEnabled(true);
    } 
    else {
     	Environment f =  new Environment(Machine.FSA, Environment.NO_FILE_NAME, mainmenu, isApplet);
	f.setToDfa(this);
	f.pack();
	f.setVisible(true);
    }
    
    setEnabled(true);
  }

  /**
   * Opens the Step Run Window.
   */
  public void stepRun() {
    TextDialog dg;

    String[] str = {"Yes", "No"};
    String result;

    if(!checkBuildDone()) {
      dg = new TextDialog("\nThe machine is not completed.\n Do you want to continue?"
                          , str, strType() + " Step Run Warning");
      result = dg.findResult();
      if(result.equals("No")) {
        setEnabled(true);
        return;
      }
    }

    String	error = a.checkMachine();
    String	input = inputField.getText();

    if (error == null) {
      setEnabled(false);
      if (type == Machine.TM2) {
        input += " | " + inputField2.getText();
      }
      a.setInput(input);
      a.init();
      d.changeAspect(true);
      UpperWindow uw = new UpperWindow(strType() + " Step Run", this);
      uw.pack();
      uw.show();
    } else {
      dg = new OKDialog(error, this, strType() + " Error");
      dg.findResult();
      setEnabled(true);
    }
  }	// end of method stepRun.

  /**
   * Does a fast run and shows the result.
   * Gives the opportunity to see the path
   * that lead to acceptance.
   */
  public void fastRun() {
    String	error = a.checkMachine();
    String	input = inputField.getText();

    String result;
    TextDialog dg;
    String[] str = {"Yes", "No"};

    if(!checkBuildDone()) {
      dg = new TextDialog("\nThe machine is not completed.\n Do you want to continue?"
                          , str, strType() + " Fast Run Warning");
      result = dg.findResult();
      if(result.equals("No")) {
        setEnabled(true);
        return;
      }
    }

    if (error == null) {
      if (type == Machine.TM2) 
        input += " | " + inputField2.getText();
      a.setInput(input);
      a.init();
      a.d.initialState.ss = State.NORMAL;
      done = false;
      while (!done) {
        done = true;
	switch (a.fastRun()) {
         
          case Machine.NO_FINAL_STATE_MSG:
              dg = new OKDialog("\nInput Rejected.\n"
              + "(machine has no final state)", this, strType() + " Fast Run");
	      dg.findResult();
	      break;
          case Machine.MAXNCONFIGS_MSG:
            if (a.maxNConfigs < a.capacity) {
              dg = new TextDialog("More than "+a.maxNConfigs
              	+" configurations\n have been generated and the input is\n"
              	+"not accepted yet. Continue?", str, strType()
		+ " Fast Run Warning");  
	      result = dg.findResult();

              if (result.equals("Yes")) {
                a.maxNConfigs *= 2;
                done = false;  
              }  
            } else {
              dg = new OKDialog("More than "+a.capacity
		+" configurations\n have been generated\n."
		+" Unable to continue processing", this, strType()
		+ " Fast Run Error");
              dg.findResult();
            }
            break; 
          case Machine.ACCEPTED_MSG:
            Configuration c = a.acceptingConfig;
            int pathLength = 1;
            while (c.father != null) {
              c = c.father;
              pathLength++;
            }
            dg = new TextDialog("Input Accepted!\n"+a.nConfigs
            	+" configurations have been generated.\n"
            	+" The acceptance path has length "
		+pathLength+"\n Input was accepted in state q"
		+a.acceptingConfig.theState.id+"\nDo you want to see the acceptance path?",
		 str, strType() + " Fast Run");  
	    
            if (dg.findResult().equals("Yes")) {
		TraceWindow tw = new TraceWindow(a, a.acceptingConfig, this,
						 strType() + " Accepting Path");
              tw.show();              
            }   
            break;
          case Machine.NO_MORE_CONFIGS_MSG:
            dg = new OKDialog("\n\nInput rejected.", this, strType() + " Fast Run");
            dg.findResult();
            break;
        }  // end switch
      } // end while (!done)
    } else {
      dg = new OKDialog(error, this, "Error");
      dg.findResult();
    }
    setEnabled(true);
    a.d.repaint();
  }	// end of method fastRun.

  /**
   * Updates the title and the status line.
   */
  public void updateStatus() {
    String typeInTitle = (type == Machine.FSA) ? ( 
      (envSource != null) ? (oldTree == null) ? "NFA to DFA builder" : 
           "Minimize Builder" : (regexpconv != null) ?
	   "Regular Expression to FSA builder" : "Finite State Automaton") :
      ((type == Machine.PDA) ? "Pushdown Automaton" : 
       ((type == Machine.TM1) ? "One-Tape Turing Machine" :
	((type == Machine.TM2) ? "Two-Tape Turing Machine" : "Minimize Tree builder")));
    String strModified = (a.d.modified) ? "(modified)":"";
    setTitle(typeInTitle + ": " + fileName + " " + strModified);
    statusLine.setText("");
  }

  /**
   * Updates the title and the status line.
   * @param message	the new status line.
   */
  public int updateStatus(String message) {
    updateStatus();
    statusLine.setText(message);
    return 0;
  }

  /**
   * Tests if the current machine is modified,
   * asks to save it if it is not modified.
   * @return	false if the user pushed the cancel button, true otherwise.
   */
  public boolean testSavedAndProceed() {
      if (isApplet)
	  return true;
      else if(envTarget != null && !envTarget.testSavedAndProceed())
	  return false;
      else if (a.d.modified) {
	  TextDialog dg = new TextDialog("\nThe current machine is not saved.\n          Do you want to save?", SAVE_BUTTONS, strType() + " Warning");
	  
	  String pushed = dg.findResult();

	  if (pushed.equals(SAVE_STRING)) {
	      if (!save()) 
		  return  false;
	  }
	  else if (pushed.equals(CANCEL_STRING))
	      return  false;
      }
      return true;
  }	// end of method testSavedAndProceed.


    /**
     *  environment is closing. overrides sourceWindow.
     */
    public boolean quit() {

      if (!testSavedAndProceed())
      {
        setEnabled(true);
        return false;
      }
   
	if(envTarget != null) {
	    envTarget.setVisible(false);
	    if(envTarget.oldTree != null)
		envTarget.oldTree.setVisible(false);
	}
	if(oldTree != null) 
	    oldTree.setVisible(false);

	if(envSource != null && envSource instanceof Environment) 
	    ((Environment) envSource).setEnvTarget(null);
	else if(envSource != null && envSource instanceof GrammarInputWindow)
	    ((GrammarInputWindow) envSource).removeFromTargets(this);

	return super.quit();
    }
 
	
  /**
   * Returns a string representing the type of the
   * current machine.
   */
  protected String strType() {
    String answer = "";

    switch (type) {
      case Machine.FSA:
        answer = "FA";
        break;
      case Machine.PDA:
        answer = "PDA";
        break;
      case Machine.TM1:
        answer = "TM";
        break;
      case Machine.TM2:
        answer = "TTM";
        break;
    }
    return answer;
  }	// end of method strType.


    /* 
     * starts a functionThread with the given function inside
     */
  public void  startThread(String functionName) {
	FunctionThread ft;
	Class source;
        
        wasEnabled = isEnabled();
	setEnabled(false);
	try {
	    source = this.getClass();
	    ft = new FunctionThread(source.getMethod(functionName, new Class[0]), this, new Object[0]);
	    ft.start();
	}
	catch(NoSuchMethodException h) {
	    System.out.println("no " + functionName + " method found.");
	}
  }


}	// end of class Environment.




