Index: /main/trunk/gli/src/org/greenstone/gatherer/gui/FormatConversionDialog.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/gui/FormatConversionDialog.java	(revision 29005)
+++ /main/trunk/gli/src/org/greenstone/gatherer/gui/FormatConversionDialog.java	(revision 29006)
@@ -14,7 +14,4 @@
 import org.w3c.dom.*;
 
-//import javax.swing.*;
-import java.awt.Dimension;
-
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -27,5 +24,5 @@
 import java.io.OutputStreamWriter;
 
-
+//import java.awt.Dimension;
 import java.awt.*;
 import java.awt.event.*;
@@ -42,7 +39,7 @@
 // Need to use dictionary for labels
 // convertFormat() for remote GS
-// HTML tidy (put all "<br/>" back to "<br>" in luce/etc/collectionConfig.xml and run again)
+// + HTML tidy (put all "<br/>" back to "<br>" in luce/etc/collectionConfig.xml and run again)
 // Help tooltips on buttons
-// Undo, Redo buttons
+// + Undo, Redo buttons
 // Split class into dialog/widgets and data processing
 
@@ -77,4 +74,5 @@
     private JButton accept_all_button = null;
     private JButton undo_button = null;
+    private JButton redo_button = null;
     private JButton htmltidy_button = null;
     private JButton xmltidy_button = null;
@@ -83,5 +81,5 @@
     private JLabel count_label = null;
     private JLabel statusbar = null;
-
+    private UndoManager undoManager;
 
     public FormatConversionDialog (File collect_cfg_file, Document xml_file_doc, NodeList gsf_format_gs2_list) {
@@ -96,9 +94,4 @@
 	setTitle(Dictionary.get("FormatConversionDialog.Title"));
 	setDefaultCloseOperation(DISPOSE_ON_CLOSE);
-	
-	gs2_textarea = new NumberedJTextArea();
-	gs3_textarea = new NumberedJTextArea();	
-	initTextArea(gs2_textarea);
-	initTextArea(gs3_textarea);
 	
 
@@ -116,6 +109,17 @@
 	JPanel button1_panel = new JPanel();
 	button1_panel.setComponentOrientation(Dictionary.getOrientation());
-	xmltidy_button = new JButton("XML Tidy");
+	undo_button = new JButton("Undo");
+	undo_button.addActionListener(new UndoListener());
+	undo_button.setEnabled(false);
+
+	redo_button = new JButton("Redo");
+	redo_button.addActionListener(new RedoListener());
+	redo_button.setEnabled(false);
+
+	xmltidy_button = new JButton("XHTML Tidy");
 	xmltidy_button.addActionListener(new XMLTidyButtonListener());
+
+	button1_panel.add(undo_button);
+	button1_panel.add(redo_button);
 	button1_panel.add(xmltidy_button);
 
@@ -135,4 +139,19 @@
 
 
+	// The undoable text areas. Adding the UndoableEditListener has to come after instantiation
+	// of the undo and redo buttons, since the listener expects these buttons to already exist
+	undoManager = new UndoManager();
+
+	gs2_textarea = new NumberedJTextArea();
+	gs3_textarea = new NumberedJTextArea();
+
+	CustomUndoableEditListener customUndoableEditListener = new CustomUndoableEditListener();
+	gs2_textarea.getDocument().addUndoableEditListener(customUndoableEditListener);
+	gs3_textarea.getDocument().addUndoableEditListener(customUndoableEditListener);
+
+	initTextArea(gs2_textarea);
+	initTextArea(gs3_textarea);
+
+
 	JPanel centre_panel = new JPanel();
 	centre_panel.setLayout(new BoxLayout(centre_panel, BoxLayout.Y_AXIS));
@@ -197,5 +216,9 @@
     }
 
+    public int getDialogResult() {
+	return dlgResult; // OK_OPTION or CANCEL_OPTION
+    }
     
+    //*************************PROCESSING FUNCTIONS***************************//
     public static int checkForGS2FormatStatements(File collect_cfg_file) {
 
@@ -243,8 +266,4 @@
     }
 
-
-    public int getDialogResult() {
-	return dlgResult; // OK_OPTION or CANCEL_OPTION
-    }
 
     /** 
@@ -367,5 +386,5 @@
 	} 
 
-	increment();
+	increment(); // modifies the textareas to initialise them
 	setVisible(true);
     }
@@ -424,5 +443,5 @@
     }
 
-    // HTML tidy adds all entire HTML tags around a single format statement. This method removes it.
+    // HTML tidy adds entire HTML tags around a single format statement. This method removes it.
     private String removeHTMLTags(int i, String gs3formatstr_notags, boolean startsWithTD) {
 
@@ -461,5 +480,5 @@
 
 
-    //******* ACCESS FUNCTIONS *********
+    //**************** ACCESS FUNCTIONS ***************//
 
     // gs2 format text is the text string that goes into <gsf:format-gs2/>: <gsf:format-gs2>text</gsf:format-gs2>
@@ -603,5 +622,5 @@
     }
 
-    //*************************
+    //*************************FUNCTIONS THAT INTERACT WITH WIDGETS************************//
     // increment() loads the next values into the dialog
     private boolean increment() {
@@ -612,4 +631,9 @@
 	statusbar.setText("");
 
+	// as we're on a new screen of dialog, need to clear all undo/redo history
+	undoManager.discardAllEdits();
+	undo_button.setEnabled(false);
+	redo_button.setEnabled(false);
+
 	int len = gsf_format_gs2_list.getLength();
 	count_label.setText((current_index+1) + " / " + len);
@@ -621,4 +645,84 @@
     }
 
+
+    private void setStatus(String msg) {
+	statusbar.setText(msg);
+    }
+    private void setErrorStatus(String msg) {
+	statusbar.setBackground(Color.red);
+	statusbar.setText(msg);
+    }
+
+
+    public void dispose() {
+	//System.err.println("@@@@ DIALOG CLOSING!");
+	if(dlgResult != OpenCollectionDialog.CANCEL_OPTION) {
+	    // Need to remove the <gsf:gs3-root/> siblings of all <gsf:format-gs2/>
+	    // Then, get the children of each <gsf:gs3-root/> and add these as siblings of <gsf:format-gs2/>
+	    
+	    
+	    int len = gsf_format_gs2_list.getLength();
+	    for(int k=len-1; k >= 0; k--) {
+		Element parent = (Element)getParentNode(k);		
+		
+		//parent.normalize();
+
+		NodeList children = parent.getChildNodes();
+
+		// now have to loop/remove nodes in reverse order, since the following loop 
+		// modifies the very nodelist we're looping over by removing nodes from it
+
+		int numChildren = children.getLength()-1;
+
+		for(int i=numChildren; i >= 0; i--) {
+		//for(int i = 0; i < children.getLength(); i++) {
+		    Node child = children.item(i);
+
+		    if(child.getNodeName().equals(GSF_FORMAT_GS2_TAG)) { 
+			// if we're dealing with gs2-format-stmts, put their textnode contents in CData sections
+			// http://www.w3schools.com/xml/xml_cdata.asp
+			// This avoids having to look at html-encoded gs2-format tags in the Format pane
+
+			Element gs2format = (Element)child;
+			String gs2formatstr = XMLTools.getElementTextValue(gs2format);
+			gs2formatstr = Codec.transform(gs2formatstr, Codec.ESCAPEDHTML_TO_UNESCAPED);
+			
+			Node textNode = XMLTools.getNodeTextNode(gs2format);
+			Node cdataSection = gs2format.getOwnerDocument().createCDATASection(gs2formatstr); 
+			gs2format.replaceChild(cdataSection, textNode);
+		    } 
+		    else if(child.getNodeName().equals(GSF_GS3_ROOT_TAG)) { 
+
+			// remove GS3 node and append its children to the parent in its place
+			// the <gsf:gs3-root /> elements wouldn't be in the xml_file_doc DOM tree
+			// unless they were valid XML, so don't need to check for validity here
+			
+			Node gs3root = child;
+			NodeList gs3_format_lines = gs3root.getChildNodes();
+
+			for(int j = 0; j < gs3_format_lines.getLength(); j++) {
+			    Node duplicate = gs3_format_lines.item(j).cloneNode(true);
+			    parent.appendChild(duplicate);			    
+			}
+			gs3root = parent.removeChild(gs3root);
+			gs3root = null; // finished processing
+
+		    } // else - skip all nodes other than <gsf:format-gs2/> and <gsf:gs3-root/>
+		}
+	    }
+ 
+	    Element root = xml_file_doc.getDocumentElement();
+	    //System.err.println("### XML file to write out:\n" + XMLTools.xmlNodeToString(root));
+	    
+	    // Finally, write out the collection xml file
+	    String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
+	    XMLTools.writeXMLFile(collect_cfg_file, xml_file_doc, nonEscapingTagNames);
+	    
+	}
+	super.dispose();
+	
+    }
+
+    //******************INNER CLASSES including LISTENERS and STREAMGOBBLERS****************//
 
     /**
@@ -775,73 +879,4 @@
     }
 
-    public void dispose() {
-	//System.err.println("@@@@ DIALOG CLOSING!");
-	if(dlgResult != OpenCollectionDialog.CANCEL_OPTION) {
-	    // Need to remove the <gsf:gs3-root/> siblings of all <gsf:format-gs2/>
-	    // Then, get the children of each <gsf:gs3-root/> and add these as siblings of <gsf:format-gs2/>
-	    
-	    
-	    int len = gsf_format_gs2_list.getLength();
-	    for(int k=len-1; k >= 0; k--) {
-		Element parent = (Element)getParentNode(k);		
-		
-		//parent.normalize();
-
-		NodeList children = parent.getChildNodes();
-
-		// now have to loop/remove nodes in reverse order, since the following loop 
-		// modifies the very nodelist we're looping over by removing nodes from it
-
-		int numChildren = children.getLength()-1;
-
-		for(int i=numChildren; i >= 0; i--) {
-		//for(int i = 0; i < children.getLength(); i++) {
-		    Node child = children.item(i);
-
-		    if(child.getNodeName().equals(GSF_FORMAT_GS2_TAG)) { 
-			// if we're dealing with gs2-format-stmts, put their textnode contents in CData sections
-			// http://www.w3schools.com/xml/xml_cdata.asp
-			// This avoids having to look at html-encoded gs2-format tags in the Format pane
-
-			Element gs2format = (Element)child;
-			String gs2formatstr = XMLTools.getElementTextValue(gs2format);
-			gs2formatstr = Codec.transform(gs2formatstr, Codec.ESCAPEDHTML_TO_UNESCAPED);
-			
-			Node textNode = XMLTools.getNodeTextNode(gs2format);
-			Node cdataSection = gs2format.getOwnerDocument().createCDATASection(gs2formatstr); 
-			gs2format.replaceChild(cdataSection, textNode);
-		    } 
-		    else if(child.getNodeName().equals(GSF_GS3_ROOT_TAG)) { 
-
-			// remove GS3 node and append its children to the parent in its place
-			// the <gsf:gs3-root /> elements wouldn't be in the xml_file_doc DOM tree
-			// unless they were valid XML, so don't need to check for validity here
-			
-			Node gs3root = child;
-			NodeList gs3_format_lines = gs3root.getChildNodes();
-
-			for(int j = 0; j < gs3_format_lines.getLength(); j++) {
-			    Node duplicate = gs3_format_lines.item(j).cloneNode(true);
-			    parent.appendChild(duplicate);			    
-			}
-			gs3root = parent.removeChild(gs3root);
-			gs3root = null; // finished processing
-
-		    } // else - skip all nodes other than <gsf:format-gs2/> and <gsf:gs3-root/>
-		}
-	    }
- 
-	    Element root = xml_file_doc.getDocumentElement();
-	    //System.err.println("### XML file to write out:\n" + XMLTools.xmlNodeToString(root));
-	    
-	    // Finally, write out the collection xml file
-	    String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
-	    XMLTools.writeXMLFile(collect_cfg_file, xml_file_doc, nonEscapingTagNames);
-	    
-	}
-	super.dispose();
-	
-    }
-
     private class XMLTidyButtonListener implements ActionListener {
 	// run HTML tidy taking input from stdin
@@ -885,5 +920,5 @@
     {
 	InputStream is = null;
-	StringBuffer outputstr = new StringBuffer(); //String outputstr = "";
+	StringBuffer outputstr = new StringBuffer();
 	boolean split_newlines = false;
 	
@@ -909,7 +944,7 @@
 		while ( (line = br.readLine()) != null) {
 		    //System.out.println("@@@ GOT LINE: " + line);
-		    outputstr.append(line); // outputstr += line;
+		    outputstr.append(line);
 		    if(split_newlines) {
-			outputstr.append("\n"); //outputstr += "\n";
+			outputstr.append("\n");
 		    }
 
@@ -968,10 +1003,57 @@
     }
 
-    private void setStatus(String msg) {
-	statusbar.setText(msg);
-    }
-    private void setErrorStatus(String msg) {
-	statusbar.setBackground(Color.red);
-	statusbar.setText(msg);
-    }
+    private class UndoListener implements ActionListener
+    {	
+	public void actionPerformed(ActionEvent event)
+	{
+	    try	{
+		if (undoManager.canUndo()) {						
+		    redo_button.setEnabled(true);
+		    undoManager.undo();					
+		}
+		
+		if (!undoManager.canUndo()) {
+		    undo_button.setEnabled(false);
+		} else {
+		    undo_button.setEnabled(true);
+		}
+		
+	    } catch (Exception e) {
+		System.err.println("Exception trying to undo: " + e.getMessage());
+	    }
+	}
+    }
+    
+    private class RedoListener implements ActionListener
+    {
+	public void actionPerformed(ActionEvent evt)
+	{
+	    try	{
+		if (undoManager.canRedo()) {
+		    undo_button.setEnabled(true); // the difference with Format4gs3Manager, and no DocumentListener
+		    undoManager.redo();
+		}
+		
+		if (!undoManager.canRedo()) {
+		    redo_button.setEnabled(false);
+		} else {
+		    redo_button.setEnabled(true);
+		}
+		
+	    } catch (Exception e) {
+		System.err.println("Exception trying to redo: " + e.getMessage());
+	    }
+	}
+    }
+
+    private class CustomUndoableEditListener implements UndoableEditListener {
+	public void undoableEditHappened(UndoableEditEvent evt)
+	{
+	    
+	    undoManager.addEdit(evt.getEdit());
+	    redo_button.setEnabled(false);
+	    undo_button.setEnabled(true);
+	}
+    }
+
 }
