//*****************************************************************************
/*
** FILE:   xt_XmlTestbedFrame.java
**
** (c) 1997, 1998 Steve Withall.
**
** HISTORY:
**    27Sep97  stevew  Created.
**    03Mar98  stevew  Switch to Swing, including code from Notepad example
**                      and making the source editor an internal frame.
**    22Mar98  stevew  Added tree view (as internal frame).
**    02May98  stevew  Moved configuration handling into xu_FrameConfigManager.
**    30Jul98  stevew  Moved base to xu_Frame, and now inherit from xu_Frame.
*/
package xt;

import xu.xu_Frame;

import xe.xe_XmlEngine;

import xm.xm_AttToChildConverter;
import xm.xm_DocumentModel;
import xm.xm_NodeTypeDefaultsElement;
import xm.xm_NodeTypeElement;
import xm.xm_NodeTypeRegistry;
import xm.xm_NodeTypeRegistryDialog;
import xm.xm_NodeTypeRegistryListAction;
import xm.xm_ParseException;
import xm.xm_ParseThread;
import xm.xm_SimpleNodeFormatter;
import xm.xm_StatusBar;
import xm.xm_XmlEngine;
import xm.xm_XmlEngineFactory;

import da.da_DatabaseConnectDialog;
import da.da_DatabaseDriverDialog;
import da.da_DatabaseSchemaManager;

import oh.oh_HelpAction;

import xu.xu_ButtonGroupAction;
import xu.xu_CheckBoxAction;
import xu.xu_FrameConfigManager;
import xu.xu_LookAndFeelAction;

import xg.xg_AttList;
import xg.xg_Document;

import eh.eh_Debug;
import eh.eh_DebugBoxAction;
import eh.eh_DebugPrefsAction;
import eh.eh_ListSystemPropertiesAction;

import com.sun.java.swing.preview.filechooser.*;
import com.sun.java.swing.preview.*;

import com.sun.java.swing.text.BadLocationException;
import com.sun.java.swing.text.Document;
import com.sun.java.swing.text.PlainDocument;
import com.sun.java.swing.text.TextAction;

import com.sun.java.swing.tree.DefaultTreeModel;

import com.sun.java.swing.event.InternalFrameEvent;
import com.sun.java.swing.event.InternalFrameListener;

import com.sun.java.swing.border.Border;

import com.sun.java.swing.AbstractAction;
import com.sun.java.swing.Action;
import com.sun.java.swing.BorderFactory;
import com.sun.java.swing.Box;
import com.sun.java.swing.BoxLayout;
import com.sun.java.swing.JCheckBox;
import com.sun.java.swing.ImageIcon;
import com.sun.java.swing.JDesktopPane;
import com.sun.java.swing.JFrame;
import com.sun.java.swing.JInternalFrame;
import com.sun.java.swing.JLabel;
import com.sun.java.swing.JMenuBar;
import com.sun.java.swing.JOptionPane;
import com.sun.java.swing.JPanel;
import com.sun.java.swing.JProgressBar;
import com.sun.java.swing.JScrollPane;
import com.sun.java.swing.JTextArea;
import com.sun.java.swing.JTextField;
import com.sun.java.swing.JToolBar;
import com.sun.java.swing.JViewport;

import java.net.URL;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Toolkit;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;

import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Vector;

//*****************************************************************************
/** A frame for the manipulation, viewing and processing of an XML document.
 */
public class xt_XmlTestbedFrame extends    xu_Frame
                                implements InternalFrameListener
{
    /** The XML engine, responsible for XML processing. It is expected that in
     *  due course, this will be replaced by an xm_XmlEngine, created by an
     *  engine factory, to weaken ties to the xe parser. */
    xe_XmlEngine           TheXmlEngine     = null;

    /** The XML document (model) currently being operated upon. All the
     *  sub-windows on the desktop are views of this document or operate upon
     *  it in other ways (viz the style view). */
    xm_DocumentModel       TheDocumentModel = null;

    /** The pathname of the XML file currently being worked on. */
    String                 FilePathnameString      = new String();

    /** Dialog box for selecting which XML file to open. */
    FileDialog             SelectFileDialog        = null;

    /** The desktop, within which the internal frames live. */
    JDesktopPane           DesktopPane = new JDesktopPane();

    /** Identifies which internal frame is currently selected. If any action is
     *  requested whose meaning depends upon its context, it will be applied to
     *  this frame. Eg. if an XML source is to be parsed, the source of the
     *  document associated with this frame will be parsed. */
    xt_DocumentFrame       ActiveFrame   = null;

    /** The XML document (model) of the currently active internal frame (ie.
     *  the ActiveFrame). This attribute is therefore partly for convenience,
     *  but if all frames are closed, it reverts to TheDocumentModel. */
    xm_DocumentModel       ActiveDocumentModel = null;

    /** The height of each internal frame when it is created. */
    int                    InternalFrameHeight = 385;

    /** The width of each internal frame when it is created. */
    int                    InternalFrameWidth  = 650;

    /** Internal frame for editing the source of the current document. */
    xt_SimpleEditFrame     EditFrame          = null;

    /** Internal frame for styled editing of the source of the current document. */
    xt_StyledEditFrame     StyledEditFrame    = null;

    /** Internal frame for displaying tree view of document. */
    xt_DocumentTreeFrame   TreeFrame          = null;

    /** Internal frame for displaying and applying a stylesheet to the current
     *  document. */
    xt_StyleFrame          StyleFrame         = null;

    // Actions. Note that a couple of actions are inherited from xu_Frame.
	NewAction              TheNewAction              = new NewAction();
	OpenAction             TheOpenAction             = new OpenAction();
	SaveAction             TheSaveAction             = new SaveAction();
	SaveAsAction           TheSaveAsAction           = new SaveAsAction();

	SourceViewAction       TheSourceViewAction       = new SourceViewAction();
	StyledSourceViewAction TheStyledSourceViewAction = new StyledSourceViewAction();
	TreeViewAction         TheTreeViewAction         = new TreeViewAction();
	StyleViewAction        TheStyleViewAction        = new StyleViewAction();

	ProcessAction          TheProcessAction          = new ProcessAction();
	ReformatAction         TheReformatAction         = new ReformatAction();
	AttsToNodesAction      TheAttsToNodesAction      = new AttsToNodesAction();
    xu_CheckBoxAction      ProcessFromFileAction     = new xu_CheckBoxAction("ProcessFromFile");
    xu_CheckBoxAction      ValidateAction            = new xu_CheckBoxAction("Validate");
    xu_CheckBoxAction      VerifyAction              = new xu_CheckBoxAction("Verify");
    xu_CheckBoxAction      StopIfVerifyErrorAction   = new xu_CheckBoxAction("stop-if-verify-error");
    xu_CheckBoxAction      VerboseAction             = new xu_CheckBoxAction("Verbose");
    xu_CheckBoxAction      ParseWhenLoadAction       = new xu_CheckBoxAction("parse-when-load");
	xu_LookAndFeelAction   TheLookAndFeelAction      = null;  // Needs ParentFrame!
	NodeTypeRegistryAction TheNodeTypeRegistryAction = new NodeTypeRegistryAction();
	DatabaseDriversAction  TheDatabaseDriversAction  = new DatabaseDriversAction();
	DatabaseConnectAction  TheDatabaseConnectAction  = new DatabaseConnectAction();
	xu_ButtonGroupAction   ProcessorListAction       = new xu_ButtonGroupAction("ProcessorList");
	ActionListAction       TheActionListAction       = new ActionListAction();
	eh_DebugBoxAction      TheDebugBoxAction         = new eh_DebugBoxAction();

    //*****************************************************************************
    /** Default constructor. Create a new xt_XmlTestbedFrame at a default screen
     *  location.
     *
     *  @param  InputParentFrame  The master frame from which this frame is being
     *                             called
     */
    public xt_XmlTestbedFrame(JFrame  InputParentFrame)
    {
        eh_Debug.add(5, "xt_XmlTestbedFrame: Construct testbed frame (with parent)");
        ParentFrame = InputParentFrame;

        try
        {
            init();
        }
        catch (Exception  InputException)
        {
            eh_Debug.add(2, "xt_XmlTestbedFrame: Error in initialisation: "
                                     + InputException);
            eh_Debug.printStackTrace(2, InputException);
        }
    }

    //*****************************************************************************
    /** Default constructor. Create a new xt_XmlTestbedFrame at a default screen
     *  location.
     */
    public xt_XmlTestbedFrame()
    {
        eh_Debug.add(5, "xt_XmlTestbedFrame: Construct testbed frame");

        try
        {
            init();
        }
        catch (Exception  InputException)
        {
            eh_Debug.add(2, "xt_XmlTestbedFrame: Error in initialisation: "
                                     + InputException);
            eh_Debug.printStackTrace(2, InputException);
        }
    }

    //*****************************************************************************
    /** Initialise.
     */
    public void init()
    {
        MainFrame        = this;
        TheXmlEngine     = new xe_XmlEngine();
        TheDocumentModel = new xm_DocumentModel(TheXmlEngine);

        TheDebugBoxAction.createDebugFrame();  // Display debug frame - to ensure
                                               //  config load errors are shown
        // Register registry, database schema and XSL elements.
        xm_NodeTypeRegistry.setDefaultParserClassName("xe.xe_ElementParser");
        xm_NodeTypeRegistry.setDefaultCustomizerClassName("xc.xc_ElementCustomizer");
        xm_NodeTypeRegistry.setDefaultViewClassName("xv.xv_BoxViewX");
        xm_NodeTypeRegistry.register(xm_NodeTypeDefaultsElement.RegisteredName,
                                     "Node type defaults",
                                     "xm.xm_NodeTypeDefaultsElement");
        xm_NodeTypeRegistry.register(xm_NodeTypeElement.RegisteredName,
                                     "Node type registry entry",
                                     "xm.xm_NodeTypeElement");
        da_DatabaseSchemaManager.registerElementClasses();
        xt_StyleFrame.ensureClassesRegistered();

        loadConfig("config.xt.xt_XmlTestbed");  // Load configuration values,
                                                //  including supported actions
    	// Set frame title and image icon.
    	BasicFrameTitle = TheFrameConfigManager.getFrameTitle();
    	setTitle(BasicFrameTitle);
	    setIconImage(TheFrameConfigManager.getFrameIconImage() );

	    getContentPane().setLayout(new BorderLayout() );
        getContentPane().setFont(new Font("Monospaced", Font.PLAIN, 12));

        Toolkit    DefaultToolkit = Toolkit.getDefaultToolkit();
        Dimension  ScreenSize     = DefaultToolkit.getScreenSize();
        if (ParentFrame == null)
            InternalFrameHeight = ScreenSize.height - 180;
        else
            InternalFrameHeight = ScreenSize.height - 260;
        InternalFrameWidth = ScreenSize.width - 160;

        initControlPanel();  // Create menu bar and toolbar
        initDesktop();       // Set up the desktop and its internal frames

    	MainStatusBar = new xm_StatusBar();
        TheDocumentModel.setStatusBar(MainStatusBar);
    	getContentPane().add("South", MainStatusBar);

	    pack();

        if (ParentFrame == null)
            setBounds(80,  0, ScreenSize.width - 80, ScreenSize.height - 27);
        else
            setBounds(80, 80, ScreenSize.width - 80, ScreenSize.height - 107);
//	    setBounds(TheFrameConfigManager.getFrameRectangle() );
        TheLookAndFeelAction.conformToState();
	    show();
    }

    //*****************************************************************************
    /** Register the actions supported by this frame.
     */
    protected void registerActions()
    {
        super.registerActions();
        
        // Create those actions which need to know about the MainFrame.
	    eh_DebugPrefsAction   TheDebugPrefsAction  = new eh_DebugPrefsAction(MainFrame);

	    TheLookAndFeelAction = new xu_LookAndFeelAction(MainFrame);

    	// Register individual actions.
        TheFrameConfigManager.registerAction(TheNewAction);
	    TheFrameConfigManager.registerAction(TheOpenAction);
	    TheFrameConfigManager.registerAction(TheSaveAction);
	    TheFrameConfigManager.registerAction(TheSaveAsAction);

        TheFrameConfigManager.registerAction(TheSourceViewAction);
        TheFrameConfigManager.registerAction(TheStyledSourceViewAction);
        TheFrameConfigManager.registerAction(TheTreeViewAction);
        TheFrameConfigManager.registerAction(TheStyleViewAction);

	    TheFrameConfigManager.registerAction(TheProcessAction);
	    TheFrameConfigManager.registerAction(TheReformatAction);
	    TheFrameConfigManager.registerAction(TheAttsToNodesAction);
	    TheFrameConfigManager.registerAction(ProcessFromFileAction);
	    TheFrameConfigManager.registerAction(ValidateAction);
	    TheFrameConfigManager.registerAction(VerifyAction);
	    TheFrameConfigManager.registerAction(StopIfVerifyErrorAction);
	    TheFrameConfigManager.registerAction(VerboseAction);
	    TheFrameConfigManager.registerAction(ParseWhenLoadAction);
	    TheFrameConfigManager.registerAction(TheNodeTypeRegistryAction);
	    TheFrameConfigManager.registerAction(TheDatabaseDriversAction);
	    TheFrameConfigManager.registerAction(TheDatabaseConnectAction);
        TheFrameConfigManager.registerAction(ProcessorListAction);
        TheFrameConfigManager.registerAction(TheLookAndFeelAction);
        TheFrameConfigManager.registerAction(TheDebugPrefsAction);
        TheFrameConfigManager.registerAction(new xm_NodeTypeRegistryListAction() );
        TheFrameConfigManager.registerAction(new eh_ListSystemPropertiesAction() );
        TheFrameConfigManager.registerAction(TheActionListAction);
        TheFrameConfigManager.registerAction(TheDebugBoxAction);

    	// Register actions supported by the simple editor.
    	TheFrameConfigManager.registerActions(xt_SimpleEditFrame.getActions() );
    	TheFrameConfigManager.registerActions(xt_StyledEditFrame.getActions() );
    }

    //*****************************************************************************
    /** Initialise the desktop and create the internal frames within it.
     */
    private void initDesktop()
    {
        TheSourceViewAction.conformToState();        // Display source editor if configured
        TheStyledSourceViewAction.conformToState();  // Display styled source editor if configured
        TheTreeViewAction.conformToState();          // Display tree view if configured
        TheStyleViewAction.conformToState();         // Display style view if configured
	    getContentPane().add("Center", DesktopPane);
    }

    //*****************************************************************************
    /** Initialise the edit frame for viewing and editing the current source.
     */
    private void initEditFrame()
    {
        eh_Debug.add(7, "xt_XmlTestbedFrame.initEditFrame");
        EditFrame = new xt_SimpleEditFrame(TheDocumentModel,
                                           getResourceString("SourceEditorTitle"));
        EditFrame.setBounds(0, 0, InternalFrameWidth, InternalFrameHeight);
        addFrameToDesktop(EditFrame);
    }

    //*****************************************************************************
    /** Initialise the edit frame for viewing and editing the current source.
     */
    private void initStyledEditFrame()
    {
        eh_Debug.add(7, "xt_XmlTestbedFrame.initStyledEditFrame");
        StyledEditFrame = new xt_StyledEditFrame(TheDocumentModel,
                                                 getResourceString("StyledSourceEditorTitle"));
        StyledEditFrame.setBounds(90, 45, InternalFrameWidth, InternalFrameHeight);
        addFrameToDesktop(StyledEditFrame);
    }

    //*****************************************************************************
    /** Initialise the tree frame for the tree view of the current document.
     */
    private void initTreeFrame()
    {
        eh_Debug.add(7, "xt_XmlTestbedFrame.initTreeFrame");
        TreeFrame = new xt_DocumentTreeFrame(TheDocumentModel,
                                             getResourceString("TreeViewTitle"));
        TreeFrame.setBounds(30, 15, InternalFrameWidth, InternalFrameHeight);
        addFrameToDesktop(TreeFrame);
    }

    //*****************************************************************************
    /** Initialise the style frame for viewing and editing a stylesheet and applying
     *  it to the current source.
     */
    private void initStyleFrame()
    {
        eh_Debug.add(7, "xt_XmlTestbedFrame.initStyleFrame");
        StyleFrame = new xt_StyleFrame(TheDocumentModel,
                                       getResourceString("StyleViewTitle"),
                                       TheXmlEngine,
                                       MainStatusBar,
                                       this);
        StyleFrame.setBounds(60, 30, InternalFrameWidth, InternalFrameHeight);
        addFrameToDesktop(StyleFrame);
    }

    //*****************************************************************************
    /** Add the given internal frame to the desktop.
     *
     *  @param  InputInternalFrame  The frame to add to the desktop
     */
    private void addFrameToDesktop(xt_DocumentFrame  InputInternalFrame)
    {
        InputInternalFrame.addInternalFrameListener(this);
        try
        {
            if (ActiveFrame != null)
                ActiveFrame.setSelected(false);
            InputInternalFrame.setSelected(true);
        }
        catch (Exception  InputException)
        {
            eh_Debug.add(2, "xt_XmlTestbedFrame.addFrameToDesktop: Attempt to select frame failed: "
                                      + InputException);
        }

        ActiveFrame         = InputInternalFrame;
        ActiveDocumentModel = InputInternalFrame.getDocumentModel();
        DesktopPane.add(InputInternalFrame);
        InputInternalFrame.moveToFront();
    }

    //*****************************************************************************
    //***Internal*frame*listener*methods*******************************************
    //*****************************************************************************
    /** Invoked when an internal frame has been opened.
     */
    public void internalFrameOpened(InternalFrameEvent InputFrameEvent)
    {
        eh_Debug.add(6, "xt_XmlTestbedFrame.internalFrameOpened");
    }

    //*****************************************************************************
    /** Invoked when an internal frame is in the process of being closed.
     *  The close operation can be overridden at this point.
     */
    public void internalFrameClosing(InternalFrameEvent InputFrameEvent)
    {
        eh_Debug.add(6, "xt_XmlTestbedFrame.internalFrameClosing");
    }

    //*****************************************************************************
    /** Invoked when an internal frame has been closed.
     */
    public void internalFrameClosed(InternalFrameEvent InputFrameEvent)
    {
        eh_Debug.add(6, "xt_XmlTestbedFrame.internalFrameClosed");
        //TBD We need to make another frame the current frame.
    }

    //*****************************************************************************
    /** Invoked when an internal frame is iconified.
     */
    public void internalFrameIconified(InternalFrameEvent InputFrameEvent)
    {
        eh_Debug.add(6, "xt_XmlTestbedFrame.internalFrameIconified");
        //TBD Make another frame the current frame?
    }

    //*****************************************************************************
    /** Invoked when an internal frame is de-iconified.
     */
    public void internalFrameDeiconified(InternalFrameEvent InputFrameEvent)
    {
        eh_Debug.add(6, "xt_XmlTestbedFrame.internalFrameDeiconified");
    }

    //*****************************************************************************
    /** Invoked when an internal frame is activated.
     */
    public void internalFrameActivated(InternalFrameEvent InputFrameEvent)
    {
        ActiveFrame         = (xt_DocumentFrame)InputFrameEvent.getSource();
        ActiveDocumentModel = ActiveFrame.getDocumentModel();
        eh_Debug.add(6, "xt_XmlTestbedFrame.internalFrameActivated: Frame '"
                                 + ActiveFrame.getTitle() + "' activated");
    }

    //*****************************************************************************
    /** Invoked when an internal frame is de-activated.
     */
    public void internalFrameDeactivated(InternalFrameEvent InputFrameEvent)
    {
        eh_Debug.add(6, "xt_XmlTestbedFrame.internalFrameDeactivated: Frame '"
                                 + ActiveFrame.getTitle() + "' deactivated");
    }

    //*****************************************************************************
    //**Action*inner*classes*******************************************************
    //*****************************************************************************
    class NewAction extends AbstractAction
    {
        //*****************************************************************************
    	NewAction()
    	{
    	    super("new");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
    	    ActiveDocumentModel.clear();

    	    // Clear the name of the previous file from the frame title.
    	    setTitle(BasicFrameTitle);

            MainStatusBar.setText(9, "");
	        TheSaveAction.setEnabled(false);
	        TheSaveAsAction.setEnabled(false);

    	    validate();
    	}
    }

    //*****************************************************************************
    class OpenAction extends AbstractAction
    {
        //*****************************************************************************
	    OpenAction()
	    {
	       super("open");
	    }

        //*************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            eh_Debug.add(7, "OpenAction.actionPerformed:");
    	    boolean  FileNameSelectedFlag = promptForFileName();
    	    if (!FileNameSelectedFlag)
    	    {
                eh_Debug.add(4, "Null file name selected");
        		return;
    	    }

    	    validate();
    	    ActiveDocumentModel.clear();

    	    // Put the name of this file in the frame title.
    	    setTitle(BasicFrameTitle + " - " + FilePathnameString);
	        TheSaveAction.setEnabled(true);
	        TheSaveAsAction.setEnabled(true);

            xg_AttList  EngineAttList = ProcessorListAction.getAttList();

            try
            {
                // Create an engine to do the processing.
                xm_XmlEngine  TheEngine = xm_XmlEngineFactory.createXmlEngine(EngineAttList);
                TheEngine.setValidateFlag(ValidateAction.getState() );
                TheEngine.setVerifyFlag(VerifyAction.getState() );
                TheEngine.setStopIfVerifyErrorFlag(StopIfVerifyErrorAction.getState() );
                TheEngine.setParseListener(ActiveDocumentModel);
                ActiveDocumentModel.setXmlEngine(TheEngine);
                processXml(TheEngine);
            }
            catch (xm_ParseException InputException)
            {
                MainStatusBar.setText(2, "Error creating XML engine: " + InputException);
            }

	    }

        //*************************************************************************
        public void processXml(xm_XmlEngine  InputXmlEngine)
        {
            int  ParseMode = 0;
            if (ParseWhenLoadAction.getState() )
                ParseMode = xm_ParseThread.LOAD_AND_PARSE_STRING;
            else
                ParseMode = xm_ParseThread.LOAD;
//            boolean    ParseMayProceedFlag = true;
            JTextArea  SourceTextArea      = null;
    	    if (ActiveFrame != null)
                SourceTextArea = ActiveFrame.getSourceTextArea();

            Thread  SourceLoadThread = new xm_ParseThread(ParseMode,
                                                          FilePathnameString,
                                                          ActiveDocumentModel,
                                                          SourceTextArea);
           	SourceLoadThread.start();
	    }

        //*************************************************************************
        public boolean promptForFileName()
        {
	        if (SelectFileDialog == null)
    	        SelectFileDialog = new FileDialog(MainFrame,
    	                                          "Select XML file",
    	                                          FileDialog.LOAD);
    	    SelectFileDialog.show();

    	    String  FileNameDirectoryString = SelectFileDialog.getDirectory();
    	    String  FileNameString          = SelectFileDialog.getFile();
    	    FilePathnameString = FileNameDirectoryString + FileNameString;
            return(FileNameString != null);
       	}
    }

    //*****************************************************************************
    /** Handle pressing of the "Save last XE document as XML file" button. Save the
     *  current XML document to the file whose name is in FilePathnameString.
     */
    class SaveAction extends AbstractAction
    {
        //*****************************************************************************
    	SaveAction()
    	{
    	    super("save");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            MainStatusBar.setText("Saving to file " + FilePathnameString);
            try
            {
                ActiveDocumentModel.save(FilePathnameString);
            }
            catch (Exception  InputException)
            {
                MainStatusBar.setText(2, "Error writing file " + FilePathnameString
                                            + ". Exception = " + InputException);
            }
            MainStatusBar.setText("Saved to file " + FilePathnameString);
    	}
    }

    //*****************************************************************************
    /** Handle pressing of the "Save as ..." button. Save the current XML document
     *  to a newly-supplied file name.
     */
    class SaveAsAction extends AbstractAction
    {
        //*****************************************************************************
    	SaveAsAction()
    	{
    	    super("save-as");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
		    JFileChooser  SaveFileChooser = new JFileChooser(FilePathnameString);
//		    SaveFileChooser.addChoosableFileFilter(new FileFilter() );
            xt_CheckBox  SaveFromSourceCheckBox = new xt_CheckBox("Save source directly?");
            SaveFileChooser.setAccessory(SaveFromSourceCheckBox);
		    int  ChooseState = SaveFileChooser.showSaveDialog(MainFrame);
		    if (ChooseState == JFileChooser.APPROVE_OPTION)
		    {
			    File  SaveFile = SaveFileChooser.getSelectedFile();
			    if (SaveFile == null)
		            JOptionPane.showMessageDialog(MainFrame,
		                                          "No file chosen - no save performed");
			    else
			    {
			        FilePathnameString = SaveFile.getPath();
    	            setTitle(BasicFrameTitle + " - " + FilePathnameString);
                    try
                    {
			            boolean  SaveFromSourceFlag = SaveFromSourceCheckBox.getState();
			            if (SaveFromSourceFlag)
			            {
                            MainStatusBar.setText("Saving source to file "
                                                     + FilePathnameString
                                                     + " direct from source");
                            TheDocumentModel.saveFromSource(FilePathnameString);
                            MainStatusBar.setText("Saved source to file "
                                                     + FilePathnameString);
                        }
			            else
			            {
                            MainStatusBar.setText("Saving to file " + FilePathnameString
                                                     + " from document");
                            TheDocumentModel.save(FilePathnameString);
                            MainStatusBar.setText("Saved to file " + FilePathnameString);
                        }
                    }
                    catch (Exception  InputException)
                    {
                        MainStatusBar.setText(2, "Error writing file " + FilePathnameString + ". Exception = " + InputException);
                    }
			    }
		    }
    	}
    }

    //*****************************************************************************
    /** Process action - use the selected processor to parse either the current
     *  source or the current XML file (depending on the setting of the "Process
     *  direct from file" option and the capabilities of the parser.
     */
    class ProcessAction extends AbstractAction
    {
        //*****************************************************************************
    	ProcessAction()
    	{
    	    super("parse-xml");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            xg_AttList  EngineAttList = ProcessorListAction.getAttList();

            try
            {
                // Create an engine to do the processing.
                xm_XmlEngine  TheEngine = xm_XmlEngineFactory.createXmlEngine(EngineAttList);
                TheEngine.setValidateFlag(ValidateAction.getState() );
                TheEngine.setVerifyFlag(VerifyAction.getState() );
                TheEngine.setStopIfVerifyErrorFlag(StopIfVerifyErrorAction.getState() );
                TheEngine.setParseListener(ActiveDocumentModel);
                ActiveDocumentModel.setXmlEngine(TheEngine);
                processXml(TheEngine);
            }
            catch (xm_ParseException InputException)
            {
                MainStatusBar.setText(2, "Error creating XML engine: " + InputException);
            }
        }

        //*****************************************************************************
        /** Process the source or the selected XML file with the InputXmlEngine.
          */
        public void processXml(xm_XmlEngine  InputXmlEngine)
        {
//            eh_Debug.add(7, "processXml: Processing XML source");
            repaint(50);

            int        ParseMode           = 0;
            boolean    ParseMayProceedFlag = true;
            JTextArea  SourceTextArea      = null;
    	    if (ActiveFrame != null)
                SourceTextArea = ActiveFrame.getSourceTextArea();

            if (ProcessFromFileAction.getState())
                ParseMode = xm_ParseThread.PARSE_FILE;    // Process from file directly
            else
            {
                ParseMode = xm_ParseThread.PARSE_STRING;  // Process current source string
    	        if (ActiveFrame == null)
    	        {
                    MainStatusBar.setText(2, "No active frame from which to read XML source");
                    ParseMayProceedFlag = false;
                }
            }

            if (ParseMayProceedFlag)
            {
                Thread  DocumentParseThread = new xm_ParseThread(ParseMode,
                                                                 FilePathnameString,
                                                                 ActiveDocumentModel,
                                                                 SourceTextArea);
                DocumentParseThread.start();
            }
        }
    }

    //*****************************************************************************
    /** Reformat action - reformat the source of the current XML document.
     */
    class ReformatAction extends AbstractAction
    {
        //*****************************************************************************
    	ReformatAction()
    	{
    	    super("reformat");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            if (    ActiveDocumentModel == null
                 || ActiveDocumentModel.getCurrentDocument() == null)
            {
                MainStatusBar.setText(2, "No current document to reformat");
                return;
            }

            MainStatusBar.setText(3, "Reformatting current document");

            // Give the document decent whitespace, so we can read it easily.
            xm_SimpleNodeFormatter  NodeFormatter = new xm_SimpleNodeFormatter();
            xg_Document  SelectedDocument = ActiveDocumentModel.getCurrentDocument();
            NodeFormatter.format(SelectedDocument);

            try
            {
                // Display the source of the newly-reformatted document.
                StringWriter  SourceWriter = new StringWriter();
                SelectedDocument.save(SourceWriter);
                ActiveDocumentModel.insertString(0, SourceWriter.toString(), null);
                MainStatusBar.setText(3, "Completed reformatting current document");
            }
            catch (IOException InputException)
            {
	            eh_Debug.add(2, "Error saving database meta data to source: " + InputException);
            }
            catch (BadLocationException InputException)
            {
	            eh_Debug.add(2, "Error moving database meta data source to screen: " + InputException);
            }
        }
    }

    //*****************************************************************************
    /** Atts-to-nodes action - convert all attributes in the current XML document
     *  into nodes.
     */
    class AttsToNodesAction extends AbstractAction
    {
        //*****************************************************************************
    	AttsToNodesAction()
    	{
    	    super("atts-to-nodes");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            if (    ActiveDocumentModel == null
                 || ActiveDocumentModel.getCurrentDocument() == null)
            {
                MainStatusBar.setText(2, "No current document to convert");
                return;
            }

            MainStatusBar.setText(3, "Converting attributes to nodes in current document");

            // Convert each attribute in the document (and its children) into nodes.
            xm_AttToChildConverter  AttConverter = new xm_AttToChildConverter();
            xg_Document  SelectedDocument = ActiveDocumentModel.getCurrentDocument();
            AttConverter.format(SelectedDocument);

            try
            {
                // Display the source of the newly-reformatted document.
                StringWriter  SourceWriter = new StringWriter();
                SelectedDocument.save(SourceWriter);
                ActiveDocumentModel.insertString(0, SourceWriter.toString(), null);
                MainStatusBar.setText(3, "Completed converting attributes to nodes in current document");
            }
            catch (IOException InputException)
            {
	            eh_Debug.add(2, "Error saving database meta data to source: "
	                               + InputException);
            }
            catch (BadLocationException InputException)
            {
	            eh_Debug.add(2, "Error moving database meta data source to screen: "
	                               + InputException);
            }
        }
    }

    //*****************************************************************************
    /** Source view window action - toggle display of the source edit view internal
     *  frame.
     */
    class SourceViewAction extends xu_CheckBoxAction
    {
        //*****************************************************************************
    	SourceViewAction()
    	{
    	    super("SourceView");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            super.actionPerformed(InputActionEvent);
            conformToState();
        }

        //*****************************************************************************
        /** Take whatever steps are necessary to bring the Action into line with what
         *  the controlling check box wishes (as indicated by the value of StateFlag).
         *  This method may be called either when the associated menu item is selected
         *  (ie. called by actionPerformed) or during initialization.
         */
        public void conformToState()
        {
            if (StateFlag)
            {
                if (EditFrame == null)
                {
                    eh_Debug.add(5, "Create source view internal frame");
                    initEditFrame();     // Set up the internal frame
                }
            }
            else
            {
                if (EditFrame != null)
                {
                    eh_Debug.add(5, "Kill the source view internal frame");
//                    EditFrame.removeTreeModelListener();  // Stop listening to changes
                    EditFrame.dispose();
                    EditFrame = null;   // Kill the internal frame
                }
            }
        }
    }

    //*****************************************************************************
    /** Source view window action - toggle display of the styled source edit view
     *  internal frame.
     */
    class StyledSourceViewAction extends xu_CheckBoxAction
    {
        //*****************************************************************************
    	StyledSourceViewAction()
    	{
    	    super("StyledSourceView");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            super.actionPerformed(InputActionEvent);
            conformToState();
        }

        //*****************************************************************************
        /** Take whatever steps are necessary to bring the Action into line with what
         *  the controlling check box wishes (as indicated by the value of StateFlag).
         *  This method may be called either when the associated menu item is selected
         *  (ie. called by actionPerformed) or during initialization.
         */
        public void conformToState()
        {
            if (StateFlag)
            {
                if (StyledEditFrame == null)
                {
                    eh_Debug.add(5, "Create styled source view internal frame");
                    TheDocumentModel.setStyledEditModeFlag(true);  // Danger!
                    initStyledEditFrame();     // Set up the internal frame
                }
            }
            else
            {
                if (StyledEditFrame != null)
                {
                    eh_Debug.add(5, "Kill the styled source view internal frame");
//                    EditFrame.removeTreeModelListener();  // Stop listening to changes
                    StyledEditFrame.dispose();
                    StyledEditFrame = null;   // Kill the internal frame
                    TheDocumentModel.setStyledEditModeFlag(false);  // Safe!
                }
            }
        }
    }

    //*****************************************************************************
    /** Tree view window action - toggle display of the tree view internal frame.
     */
    class TreeViewAction extends xu_CheckBoxAction
    {
        //*****************************************************************************
    	TreeViewAction()
    	{
    	    super("TreeView");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            super.actionPerformed(InputActionEvent);
            conformToState();
        }

        //*****************************************************************************
        /** Take whatever steps are necessary to bring the Action into line with what
         *  the controlling check box wishes (as indicated by the value of StateFlag).
         *  This method may be called either when the associated menu item is selected
         *  (ie. called by actionPerformed) or during initialization.
         */
        public void conformToState()
        {
            if (StateFlag)
            {
                if (TreeFrame == null)
                {
                    eh_Debug.add(5, "Create tree view internal frame");
                    initTreeFrame();     // Set up the tree view internal frame
                }
            }
            else
            {
                if (TreeFrame != null)
                {
                    eh_Debug.add(5, "Kill the tree view internal frame");
//                    TreeFrame.removeTreeModelListener();  // Stop listening to changes
                    TreeFrame.dispose();
                    TreeFrame = null;   // Kill the tree view internal frame
                }
            }
        }
    }

    //*****************************************************************************
    /** Style view window action - toggle display of the style view internal frame.
     */
    class StyleViewAction extends xu_CheckBoxAction
    {
        //*****************************************************************************
    	StyleViewAction()
    	{
    	    super("StyleView");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            super.actionPerformed(InputActionEvent);
            conformToState();
        }

        //*****************************************************************************
        /** Take whatever steps are necessary to bring the Action into line with what
         *  the controlling check box wishes (as indicated by the value of StateFlag).
         *  This method may be called either when the associated menu item is selected
         *  (ie. called by actionPerformed) or during initialization.
         */
        public void conformToState()
        {
            if (StateFlag)
            {
                if (StyleFrame == null)
                {
                    eh_Debug.add(5, "Create style view internal frame");
                    initStyleFrame();     // Set up the style view internal frame
                }
            }
            else
            {
                if (StyleFrame != null)
                {
                    eh_Debug.add(5, "Kill the style view internal frame");
//                    StyleFrame.removeTreeModelListener();  // Stop listening to changes
                    StyleFrame.dispose();
                    StyleFrame = null;   // Kill the style view internal frame
                }
            }
        }
    }

    //*****************************************************************************
    /** Node type registry action - display dialog to view and edit the element
     *  types which are registered.
     */
    class NodeTypeRegistryAction extends AbstractAction
    {
        //*****************************************************************************
    	NodeTypeRegistryAction()
    	{
    	    super("RegisteredTypes");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            new xm_NodeTypeRegistryDialog(MainFrame);
        }
    }

    //*****************************************************************************
    /** Database drivers action - display dialog to view and load database drivers.
     */
    class DatabaseDriversAction extends AbstractAction
    {
        //*****************************************************************************
    	DatabaseDriversAction()
    	{
    	    super("DatabaseDrivers");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            new da_DatabaseDriverDialog(MainFrame);
        }
    }

    //*****************************************************************************
    /** Database connect action - an action which displays a dialog to establish
     *  database connections.
     */
    class DatabaseConnectAction extends AbstractAction
    {
        //*****************************************************************************
    	DatabaseConnectAction()
    	{
    	    super("DatabaseConnect");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
            new da_DatabaseConnectDialog(MainFrame, TheDocumentModel);
        }
    }

    //*****************************************************************************
    /** Action list action - an action which outputs to debug a list of all the
     *  actions which have been registered.
     */
    class ActionListAction extends AbstractAction
    {
        //*****************************************************************************
    	ActionListAction()
    	{
    	    super("list-actions");
    	}

        //*****************************************************************************
        public void actionPerformed(ActionEvent  InputActionEvent)
        {
	        TheFrameConfigManager.listToDebug();
        }
    }
}

//*****************************************************************************
