Index: /main/trunk/gli/classes/dictionary.properties
===================================================================
--- /main/trunk/gli/classes/dictionary.properties	(revision 22604)
+++ /main/trunk/gli/classes/dictionary.properties	(revision 22605)
@@ -526,4 +526,5 @@
 #***** FileActions *****
 FileActions.Calculating_Size:Calculating total size of selected files
+FileActions.ChooseDestinationDirectory:Choose destination directory
 FileActions.Copying:Copying {0}
 FileActions.Could_Not_Delete:Could not delete {0}
@@ -615,4 +616,7 @@
 General.Cancel:Cancel
 General.Cancel_Tooltip:Cancel the changes (will not undo any that have been applied already)
+General.CD:Change Dir...
+General.CD_Tooltip:Browse to a different collect directory
+General.ChooseCollectDirectory:Choose your collect directory...
 General.Close:Close
 General.Close_Tooltip:Close this dialog box
@@ -735,4 +739,5 @@
 #***** Menu Options *****
 Menu.Collapse:Close Folder
+Menu.Copy_Collection:Copy this collection to...
 Menu.Edit:Edit
 Menu.Edit_Copy:Copy (ctrl-c)
@@ -756,4 +761,5 @@
 Menu.Help_About:About...
 Menu.Metadata_View:Assigned Metadata for the {0}
+Menu.Move_Collection:Move this collection to...
 Menu.Open_Externally:Open in external program
 #********************
@@ -861,4 +867,5 @@
 NewCollectionPrompt.Base_Collection:Base this collection on:
 NewCollectionPrompt.Base_Collection_Tooltip:Select an existing collection to base the new collection on
+NewCollectionPrompt.ChooseACollection:Choose a collection
 NewCollectionPrompt.Collection_Description:Description of content:
 NewCollectionPrompt.Collection_Name:Collection folder:
@@ -870,4 +877,6 @@
 NewCollectionPrompt.Instructions:To create a new collection fill out the fields below.
 NewCollectionPrompt.NewCollection:-- New Collection --
+NewCollectionPrompt.OtherCollections:-- Other Collections... --
+NewCollectionPrompt.Select:Select
 NewCollectionPrompt.Title:Create a new Collection.
 NewCollectionPrompt.Title_Clash:The title you have chosen for your collection is already in use. Continue?
@@ -918,4 +927,6 @@
 Preferences.Connection.Library_Path_Tooltip_GS3:The URL of the Greenstone web-server
 Preferences.Connection.Library_Path_Connection_Failure:Failed to connect. Check that the Greenstone server at {0} is running.
+Preferences.Connection.CollectDirectory:Collect Directory:
+Preferences.Connection.CollectDirectory_Tooltip:The path to the collect directory you want to work with.
 Preferences.Connection.ProgramCommand:Preview Command:
 Preferences.Connection.ProgramCommand_Tooltip:The launch command for the program used to view the collection preview. Remember to use %1 as the placeholder for the collection address, and to use speechmarks where necessary.
Index: /main/trunk/gli/src/org/greenstone/gatherer/Gatherer.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/Gatherer.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/Gatherer.java	(revision 22605)
@@ -497,5 +497,30 @@
 	    if (no_load || open_collection_file_path.equals("")) {
 		open_collection_file_path = null;
-	    }
+		//		collectDirectoryHasChanged(); // make sure gsdlsite.cfg's collecthome defaults to GS' collect
+	    }
+
+	    // If opening a collection outside the standard GS collect folder, need
+	    // to open the non-standard collect folder that the collection resides in
+	    if (open_collection_file_path != null && 
+		!open_collection_file_path.startsWith(getCollectDirectoryPath())) {
+		File collectFolder = null;
+		if(!open_collection_file_path.endsWith("gli.col")) { // then it's a collect folder
+		    collectFolder = new File(open_collection_file_path);
+		} else {
+		    // the filepath is a gli.col file. To get the collect folder: the 1st level
+		    // up is collection folder, 2 two levels up is the containing collect folder
+		    collectFolder = new File(open_collection_file_path).getParentFile().getParentFile();
+		}
+
+		// Need to deal with colgroups as well: while there's an etc/collect.cfg in the current
+		// collectFolder, move one level up
+		String cfg_file = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+		if(new File(collectFolder.getAbsolutePath()+File.separator+cfg_file).exists()) { // colgroup
+		    collectFolder = collectFolder.getParentFile();
+		    
+		}
+		setCollectDirectoryPath(collectFolder.getAbsolutePath()); // now set this as the current collect folder
+	    }
+	    
 	}
 	catch (Exception exception) {
@@ -761,4 +786,48 @@
 	    System.err.println("Error: Unable to make directory: " + gli_user_directory);
 	}
+    }
+
+
+    public static void collectDirectoryHasChanged(String oldCollectPath, String newCollectPath) { 
+	if(oldCollectPath.equals(newCollectPath)) {
+	    return; // nothing to be done
+	}
+
+	// first save any open collection in the *old* location
+	if(Gatherer.c_man.getCollection() != null) {
+	    Gatherer.setCollectDirectoryPath(oldCollectPath); // go back to current collection's location
+	    Gatherer.g_man.saveThenCloseCurrentCollection(); // close the current collection first
+	}
+	
+	// change to new collect path
+	Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(), 
+				true, newCollectPath);
+	Gatherer.setCollectDirectoryPath(newCollectPath);
+	
+
+	// refresh the Documents in Greenstone Collections
+	//WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
+	Gatherer.g_man.refreshWorkspaceTreeGreenstoneCollections();
+	
+	// The web server needs to be told where a new (non-standard) collecthome home is.
+	// The web server reads collecthome from cgi-bin/gsdlsite.cfg, where the property
+	// collecthome can be specified if a non-standard collecthome is to be used. If no 
+	// such property is specified in the file, then it assumes the standard GS collecthome.
+	// This method does nothing for a remote Greenstone.	
+	if(Gatherer.isGsdlRemote) {
+	    return;
+	}	
+
+	String gsdlsitecfg = "";
+	if(Gatherer.GS3) { // web/WEB-INF/cgi/gsdl3site.cfg
+	    gsdlsitecfg = Configuration.gsdl3_path + File.separator + "WEB-INF" 
+		+ File.separator + "cgi" + File.separator + "gsdl3site.cfg";
+	} else { // cgi-bin/gsdlsite.cfg
+	    gsdlsitecfg = Configuration.gsdl_path + File.separator 
+		+ "cgi-bin" + File.separator + "gsdlsite.cfg";
+	}
+
+	// non-destructive update of gsdl(3)site.cfg (comments preserved)
+	Utility.updatePropertyConfigFile(gsdlsitecfg, "collecthome", Gatherer.getCollectDirectoryPath());
     }
 
Index: /main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java	(revision 22605)
@@ -402,4 +402,7 @@
      */
     public void closeCollection() {
+	if (collection == null) {
+	    return;
+	}
 	DebugStream.println("Close collection: " + collection.getName());
 
@@ -1154,4 +1157,9 @@
     public void loadCollection(String collection_file_path)
     {
+	if(new File(collection_file_path).isDirectory()) { // collect folder instead of previously-opened collection
+	    Gatherer.setMenuBarEnabled(true);
+	    return; // nothing else to be done, no collection to open
+	}
+
 	// Display a modal progress popup to indicate that the collection is being loaded
 	ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"), Dictionary.get("CollectionManager.Loading_Collection_Please_Wait"));
@@ -1552,6 +1560,6 @@
       // If there was an open collection last session, reopen it
       if (Gatherer.open_collection_file_path != null) {
-        // Load the collection now
-        loadCollection(Gatherer.open_collection_file_path);
+	 // Load the collection now
+	 loadCollection(Gatherer.open_collection_file_path);
       } 
 
Index: /main/trunk/gli/src/org/greenstone/gatherer/file/FileManager.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/file/FileManager.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/file/FileManager.java	(revision 22605)
@@ -62,6 +62,9 @@
     static private FileQueue file_queue = null;
 
-    public static int FILE_TYPE = 0;
-    public static int FOLDER_TYPE = 1;
+    public static final int COPY = 0;
+    public static final int MOVE = 1;
+
+    public static final int FILE_TYPE = 0;
+    public static final int FOLDER_TYPE = 1;
     protected static File startup_directory = null;
 
@@ -129,4 +132,9 @@
     }
 
+    /** For moving and copying of folders. */
+    public void action(File sourceFolder, File targetFolder, int operation) {
+	(new SimpleFileTask(sourceFolder, targetFolder, operation)).start();
+    }
+    
 
     /** Retrieves the file queue object. */
@@ -136,4 +144,80 @@
     }
 
+    /** Performs the simple file task of moving or copying folders. */
+    private class SimpleFileTask
+	extends Thread
+    {
+	private File sourceFolder;
+	private File targetFolder;
+	int operation; // MOVE or COPY
+
+	public SimpleFileTask(File sourceFolder, File targetFolder, int operation)
+	{
+	    this.sourceFolder = sourceFolder;	    
+	    this.targetFolder = targetFolder;
+	    this.operation = operation;
+	}
+
+
+	public void run()
+	{
+	    // check if we're moving or overwriting the current collection
+	    String currentColPath = Gatherer.getCollectDirectoryPath()+CollectionManager.getLoadedCollectionName();
+	    if(currentColPath.equals(sourceFolder.getAbsolutePath()) 
+	       || currentColPath.equals(targetFolder.getAbsolutePath())) {
+		Gatherer.g_man.saveThenCloseCurrentCollection();
+	    }
+	    
+	    // if moving, try a simple move operation (if it works, it
+	    // shouldn't take long at all and doesn't need a progress bar)
+	    if(operation == MOVE && sourceFolder.renameTo(targetFolder)) {
+		//System.err.println("**** A simple renameTo() worked.");
+		WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
+		return;
+	    }
+
+	    // Reset the progress bar and set it to indeterminate while calculating its size
+	    GProgressBar progress_bar = file_queue.getProgressBar();
+	    progress_bar.reset();
+	    progress_bar.setIndeterminate(true);
+
+	    String status = "FileActions.Moving";
+	    if(operation == COPY) {
+		status = "FileActions.Copying";	    
+	    }
+	    progress_bar.setString(Dictionary.get(status));
+	    file_queue.getFileStatus().setText(Dictionary.get(status,
+							      file_queue.formatPath(status,
+									 sourceFolder.getAbsolutePath(), 
+									 file_queue.getFileStatus().getSize().width)));
+
+	    // do the move or copy operation
+	    try {
+		//System.err.println("**** Copying " + sourceFolder + " to: " + targetFolder);
+		file_queue.copyDirectoryContents(sourceFolder, targetFolder);		
+	    } catch(Exception e) {
+		JOptionPane.showMessageDialog(Gatherer.g_man, e.getMessage(), 
+					      "Can't perform file operation", JOptionPane.ERROR_MESSAGE);
+
+		progress_bar.setIndeterminate(false);
+		progress_bar.clear();
+		return;
+	    }
+		
+	    // if moving, delete the original source folder and
+	    // update the docs in GS collections node in the workspace tree
+	    
+	    if(operation == MOVE) {
+		Utility.delete(sourceFolder);
+		WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
+	    }
+
+
+	    progress_bar.setIndeterminate(false);
+	    progress_bar.clear();
+	    file_queue.getFileStatus().setText(Dictionary.get("FileActions.No_Activity"));
+	    progress_bar.setString(Dictionary.get("FileActions.No_Activity"));
+	}
+    }
 
     private class FileTask
Index: /main/trunk/gli/src/org/greenstone/gatherer/file/FileQueue.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/file/FileQueue.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/file/FileQueue.java	(revision 22605)
@@ -162,5 +162,5 @@
      * @return A path <strong>String</strong> no longer than width.
      */
-    private String formatPath(String key, String raw, int width)
+    String formatPath(String key, String raw, int width) // package access
     {
 	JLabel label = new JLabel(Dictionary.get(key, raw));
Index: /main/trunk/gli/src/org/greenstone/gatherer/file/WorkspaceTree.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/file/WorkspaceTree.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/file/WorkspaceTree.java	(revision 22605)
@@ -39,4 +39,5 @@
 import org.greenstone.gatherer.gui.CreateShortcutPrompt;
 import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.util.Utility;
 
 
@@ -217,4 +218,6 @@
 	private JMenuItem rename = null;
 	private JMenuItem replace = null;
+	private JMenuItem copy_collection = null;
+	private JMenuItem move_collection = null;
 
 
@@ -343,4 +346,19 @@
 		// Or map any other level directories
 		else {
+		    // --- Options for Documents In Greenstone Collections (greenstone_collections_node) ---
+		    // all subfolder of Documents In Greenstone Collections can be copied and moved
+		    WorkspaceTreeNode secondLevelNode = (WorkspaceTreeNode) path.getPathComponent(1);
+
+		    if (secondLevelNode.toString().equals(Dictionary.get("Tree.World"))) {
+			// Can move and copy the collection folders across
+			copy_collection = new JMenuItem(Dictionary.get("Menu.Copy_Collection"), KeyEvent.VK_P);
+			copy_collection.addActionListener(this);
+			add(copy_collection);
+			
+			move_collection = new JMenuItem(Dictionary.get("Menu.Move_Collection"), KeyEvent.VK_M);
+			move_collection.addActionListener(this);
+			add(move_collection);		    
+		    }
+
 		    create_shortcut = new JMenuItem(Dictionary.get("MappingPrompt.Map"), KeyEvent.VK_S);
 		    create_shortcut.addActionListener(this);
@@ -386,4 +404,46 @@
 		Gatherer.f_man.openFileInExternalApplication(node.getFile());
 	    }
+	    
+	    // Copy or move a collection from Documents in Greenstone Collections
+	    else if (source == move_collection || source == copy_collection) {
+
+		JFileChooser chooser = new JFileChooser(Gatherer.getCollectDirectoryPath());
+		chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);		
+		chooser.setDialogTitle(Dictionary.get("FileActions.ChooseDestinationDirectory"));
+		int returnVal = chooser.showOpenDialog(Gatherer.g_man.gather_pane);
+
+		if(returnVal == JFileChooser.APPROVE_OPTION) {		    
+
+		    // the node that the user rightclicked on ends up
+		    // being the col's import folder
+		    File sourceFolder = node.getFile(); 
+		    if(sourceFolder.getName().equals("import")) { 
+			sourceFolder = sourceFolder.getParentFile();
+		    }
+		    String target = chooser.getSelectedFile().getAbsolutePath();
+		    File targetFolder = new File(target+File.separator+sourceFolder.getName());
+		    
+		    // some sanity checks
+		    if(targetFolder.equals(sourceFolder)) { // directory has not changed. No copy/move performed
+			JOptionPane.showMessageDialog(Gatherer.g_man, 
+						      "Can't move " + sourceFolder + " " + "to itself\n(" + targetFolder + ").", 
+						      "Source and destination directories are the same", 
+						      JOptionPane.ERROR_MESSAGE);
+			return;
+		    } else if(targetFolder.exists()) { // option to overwrite or not
+			if(JOptionPane.showConfirmDialog(Gatherer.g_man, 
+							 "Directory " + targetFolder + " already exists. Overwrite?", 
+							 "Destination directory already exists", 
+							 JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
+			    return;
+			}
+			// else can overwrite: delete the existing targetfolder before copying/moving
+			Utility.delete(targetFolder);
+		    } 
+		    
+		    int operation = (source == move_collection) ? FileManager.MOVE : FileManager.COPY;
+		    Gatherer.f_man.action(sourceFolder, targetFolder, operation);
+		}
+	    }
   	}
     }
Index: /main/trunk/gli/src/org/greenstone/gatherer/gui/GUIManager.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/gui/GUIManager.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/gui/GUIManager.java	(revision 22605)
@@ -506,5 +506,7 @@
 	}
 	else {
-	    Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(), true, null);
+	    // if there was no open collection, then write out the collect dir path for next time
+	    Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(), 
+				    true, Gatherer.getCollectDirectoryPath()); // null
 	}
 
@@ -646,4 +648,8 @@
     }
 
+    public void refreshWorkspaceTreeGreenstoneCollections()
+    {
+	refreshWorkspaceTree(WorkspaceTree.LIBRARY_CONTENTS_CHANGED);
+    }
 
     /** Specifies whether a certain tab is enabled or not. */
Index: /main/trunk/gli/src/org/greenstone/gatherer/gui/NewCollectionDetailsPrompt.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/gui/NewCollectionDetailsPrompt.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/gui/NewCollectionDetailsPrompt.java	(revision 22605)
@@ -34,4 +34,6 @@
 import javax.swing.event.*;
 import javax.swing.text.*;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileView;
 import org.greenstone.gatherer.Configuration;
 import org.greenstone.gatherer.Dictionary;
@@ -39,4 +41,5 @@
 import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
 import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.JarTools;
 import org.greenstone.gatherer.util.StaticStrings;
 import org.greenstone.gatherer.util.Utility;
@@ -45,12 +48,11 @@
     extends ModalDialog {
 
-     
-	
-    static public boolean titleClashes(String title, File current_config_file) {
+    
+    static public boolean titleClashes(String collectDir, String title, File current_config_file) {
 	// An empty collection title never clashes with anything. It may look ugly in the final collection but there is nothing wrong with having no title for a particular language.
 	if(title == null || title.length() == 0) {
 	    return false;
 	}
-	File collect_directory = new File(Gatherer.getCollectDirectoryPath());
+	File collect_directory = new File(collectDir);
 	String file_name = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
 	File children[] = collect_directory.listFiles();
@@ -72,5 +74,7 @@
 
     private boolean cancelled;
+    private boolean collectDirChanged;
     private File base_final;
+    private JButton chdir_button;
     private JButton create_button;
     private JComboBox base_collection;
@@ -79,6 +83,9 @@
     private JTextArea description;
     private JTextField title;
+    private String collectBasePath;
+    private String newCollectPath;
     private String description_final;
     private String title_final="";
+
     static private Dimension COMPONENT_SIZE = new Dimension(230, 25);
     /** The size of this new collection dialog box. */
@@ -94,4 +101,8 @@
         this.setComponentOrientation(Dictionary.getOrientation());
 	this.self = this;
+	newCollectPath = Gatherer.getCollectDirectoryPath();
+	collectBasePath = null;
+	collectDirChanged = false;
+
 	// Setup
 	setJMenuBar(new SimpleMenuBar("creatingacollection"));
@@ -101,22 +112,6 @@
 	// Model building. Build a model of all of the collections in the gsdl collect directory with the appropriate directories.
 	Vector base_collection_model = new Vector();
-	// need to modify this to base a coll on any collection from any site
-	if (Gatherer.GS3 && !Gatherer.isGsdlRemote) {
-	    File sites_dir = new File(Gatherer.getSitesDirectoryPath());
-	    File [] sites = sites_dir.listFiles();
-	    for (int i=0; i<sites.length; i++) {
-		File collect_directory = new File(sites_dir + File.separator + sites[i].getName() + File.separator + "collect");
-		if (collect_directory.exists()) {
-		    addCollectionsToModel(base_collection_model, collect_directory, sites[i].getName());
-		}
-	    }
-	} else {
-	   File collect_directory = new File(Gatherer.getCollectDirectoryPath());
-	   addCollectionsToModel(base_collection_model, collect_directory, null);
-	}
-
-	// Sort the result.
-	Collections.sort(base_collection_model);
-	base_collection_model.add(0, new Item(null, Dictionary.get("NewCollectionPrompt.NewCollection")));
+	setupBaseCollections(base_collection_model, null); // if no collect directory passed in: use default collect directory
+
 
 	// Creation
@@ -190,7 +185,14 @@
         button_pane.setComponentOrientation(Dictionary.getOrientation());
 	create_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+	chdir_button = new GLIButton(Dictionary.get("General.CD"), Dictionary.get("General.CD_Tooltip"));
 	JButton cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
 
 	// Connection
+	chdir_button.addActionListener(new ChangeDirListener());
+	if(Gatherer.isGsdlRemote) { // for client GLI disable changing directories
+	    chdir_button.setEnabled(false);	    
+	} else { // can't base collections on any in OTHER collect dirs in client GLI
+	    base_collection.addActionListener(new OtherCollectionsListener());
+	}
 	cancel_button.addActionListener(new CancelListener());
 	create_button.addActionListener(new CreateListener());
@@ -238,5 +240,6 @@
 
 	button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
-	button_pane.setLayout(new GridLayout(1,2));
+	button_pane.setLayout(new GridLayout(1,3));
+	button_pane.add(chdir_button);
 	button_pane.add(create_button);
 	button_pane.add(cancel_button);
@@ -327,4 +330,40 @@
     public String getTitle() {
 	return title_final;
+    }
+
+    private void setupBaseCollections(Vector base_collection_model, String collectDirectory) {
+
+	File collect_directory = null;
+	if(collectDirectory != null) {
+	    collect_directory = new File(collectDirectory);
+	}
+
+	// need to modify this to base a coll on any collection from any site
+	if (Gatherer.GS3 && !Gatherer.isGsdlRemote) {
+	    File sites_dir = new File(Gatherer.getSitesDirectoryPath());
+	    File [] sites = sites_dir.listFiles();
+	    for (int i=0; i<sites.length; i++) {		
+		if(collect_directory == null) {
+		    collect_directory = new File(sites_dir + File.separator + sites[i].getName() + File.separator + "collect");
+		} 
+		if (collect_directory.exists()) {
+		    addCollectionsToModel(base_collection_model, collect_directory, sites[i].getName());
+		}
+	    }
+	} else {
+	    if(collect_directory == null) { // if no collect directory passed in: use default collect directory
+		collect_directory = new File(Gatherer.getCollectDirectoryPath());
+	    }
+	   addCollectionsToModel(base_collection_model, collect_directory, null);
+	}
+
+	// Sort the result.
+	Collections.sort(base_collection_model);
+	base_collection_model.add(0, new Item(null, Dictionary.get("NewCollectionPrompt.NewCollection")));
+	
+	// last item allows one to open up collections from other collect folders to base the new one on
+	if(!Gatherer.isGsdlRemote) { // Not yet implemented for client GLI
+	    base_collection_model.add(new Item(null, Dictionary.get("NewCollectionPrompt.OtherCollections")));
+	}
     }
 
@@ -363,4 +402,110 @@
     }
 
+    private class OtherCollectionsListener implements ActionListener {
+
+	// the JComboBox listener will receive an ActionEvent 
+	// when a selection has been made
+
+	public void actionPerformed(ActionEvent event) {
+
+	    // If the Other Collections item was selected (which is the last one in the list)
+	    if(base_collection.getSelectedIndex() == base_collection.getItemCount()-1) {
+
+		// open a filechooser in whatever is the current collect directory
+		// within the NewCollectionDetailsPrompt's settings
+		if(collectBasePath == null) {
+		    collectBasePath = newCollectPath;
+		}
+
+		JFileChooser chooser = new JFileChooser(collectBasePath);
+		chooser.setFileView(new CollectionFileView());
+		chooser.setDialogTitle(Dictionary.get("NewCollectionPrompt.ChooseACollection"));
+		chooser.setApproveButtonText(Dictionary.get("NewCollectionPrompt.Select"));
+		
+		// to prevent the user from traversing into collection folders,
+		// need to:
+		// - NOT set the filechooser to Directories only, but files and directories
+		// (so that they can still select directories)
+		// - add a filefilter that accepts only directories (i.e. rejects files)
+		// - remove the accept all filter
+		chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
+		//chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+		chooser.setFileFilter(new CollectionFileFilter());
+		chooser.setAcceptAllFileFilterUsed(false);
+
+		int returnVal = chooser.showOpenDialog(NewCollectionDetailsPrompt.this);
+		if(returnVal == JFileChooser.APPROVE_OPTION) {
+
+		    File selectedFile = chooser.getSelectedFile(); 
+		    
+		    // don't bother reloading if nothing has changed
+		    if(collectBasePath.equals(selectedFile.getAbsolutePath() + File.separator)) { 
+			// don't want Other Collections... selected
+			base_collection.setSelectedIndex(0);
+		    } else {
+
+			// If the selected directory is a collection folder get its collect dir,
+			// make collectBasePath the collect dir, and select the collection itself
+			String cfg = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+			boolean isCollection = false;
+			while(new File(selectedFile, cfg).exists()) { // while loop handles collectgroup
+			    selectedFile = selectedFile.getParentFile();
+			    isCollection = true;
+			} // else may be a collect dir, in which case leave it as it is again
+
+			// found collect directory for base collections
+			collectBasePath = selectedFile.getAbsolutePath()+File.separator; 
+
+			// change the available collections to base this collection on
+			base_collection.removeAllItems();
+			Vector base_collection_model = new Vector();
+			setupBaseCollections(base_collection_model, collectBasePath);
+			for(int i = 0; i < base_collection_model.size(); i++) {
+			    Item item = (Item)base_collection_model.get(i);
+			    base_collection.addItem(item);
+
+			    // select the originally chosen collection, if any, in the combobox
+			    if(isCollection && chooser.getSelectedFile().equals(item.getFile())) {
+				base_collection.setSelectedIndex(i);
+			    }
+			}
+			
+			// But if the user selected a collect group, set combobox to New Collection
+			if(!isCollection) {
+			    // if it was a collectgroup, nothing new would be selected, set to New Coll			    
+			    if(base_collection.getSelectedIndex() == base_collection.getItemCount()-1) {
+				base_collection.setSelectedIndex(0);
+			    }
+			}
+
+		    } 
+
+		} else { // cancelled out of dialog, so jump to New Collection
+		    base_collection.setSelectedIndex(0);
+		}		
+	    }
+	}
+    }
+
+    private class ChangeDirListener implements ActionListener {
+	public void actionPerformed(ActionEvent event) {
+	    String oldCollectPath = newCollectPath;
+	    JFileChooser chooser = new JFileChooser(newCollectPath);
+	    chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+	    chooser.setDialogTitle(Dictionary.get("General.ChooseCollectDirectory"));
+
+	    int returnVal = chooser.showOpenDialog(NewCollectionDetailsPrompt.this);
+	    if(returnVal == JFileChooser.APPROVE_OPTION) {
+		newCollectPath = chooser.getSelectedFile().getAbsolutePath() + File.separator;
+
+		// Must NOT change the available collections to base this collection on here.
+		// It will change any collection the user previously chose to base the newcoll on.
+
+		// will be used to test for duplicate collection names in current collect dir
+		collectDirChanged = newCollectPath.equals(oldCollectPath) ? false : true;
+	    }
+	}
+    }
+
     private class CancelListener
 	implements ActionListener {
@@ -387,5 +532,6 @@
 	    // We must ensure that the collection title is unique. This is a pain in the nether regions as we are forced to load the collect.cfg of each other collection in turn looking for a conflicting title
 	    else {
-		if(titleClashes(title_final, null)) {
+		String collectDir = collectDirChanged ? newCollectPath : Gatherer.getCollectDirectoryPath();
+		if(titleClashes(collectDir, title_final, null)) {
 		    JOptionPane jOptionPane=new JOptionPane();
                     jOptionPane.setComponentOrientation(Dictionary.getOrientation());
@@ -395,4 +541,11 @@
 		    }
 		}
+	    }
+
+	    // going through with collection creation
+	    if(collectDirChanged) {		
+		Gatherer.collectDirectoryHasChanged(Gatherer.getCollectDirectoryPath(), newCollectPath); 
+		// will tell the server that the collect directory has changed and that
+		// the workspace needs to be refreshed (Documents in Greenstone Collections)
 	    }
 
@@ -435,5 +588,16 @@
 	}
 	public int compareTo(Object other) {
-	    return toString().toLowerCase().compareTo(other.toString().toLowerCase());
+	    if(this == null && other == null) {
+		return 0;
+	    }
+	    else if(other == null) { 
+		return 1; 
+	    }
+	    else if(this == null) {
+		return -1;
+	    } 
+	    else {
+		return toString().toLowerCase().compareTo(other.toString().toLowerCase());
+	    }
 	}
 	public boolean equals(Object other) {
@@ -450,4 +614,112 @@
 	}
     }
+
+    /** FileFilter for the Filechooser to choose the collection to base the new one on: 
+     * Only displays directories. */
+    private class CollectionFileFilter extends FileFilter {	
+	public boolean accept(File f) {
+	    return f.isDirectory();
+	}
+	public String getDescription() {
+	    return "greenstone collection view"; //return "directories only";
+	}
+    }
+
+    /** Folders that can be used as a base collection have a special icon */
+    private class CollectionFileView extends FileView {
+	final String configfile_name;	
+	final ImageIcon COLLECTION_ICON = JarTools.getImage("gsCollection.gif", true);
+	
+	private File previous = null;
+	private boolean previousWasCollection = false;
+
+	public CollectionFileView() {
+	    configfile_name = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+	}
+
+	// A human readable description of the file.
+	public String getDescription(File f) {
+	    if(!f.equals(previous)) {
+		previous = f;
+		// no idea if it's a collection or not, calculate this next time
+		previousWasCollection = false; 
+	    }
+	    return null; // FilewView superclass will handle it
+	}
+        
+	// The icon that represents this file in the JFileChooser.
+	public Icon getIcon(File f) {
+	    
+	    if(f.equals(previous) && previousWasCollection) {
+		return COLLECTION_ICON;
+	    } 
+	    
+	    if(isCollectionBase(f)) {
+		return COLLECTION_ICON;
+	    }	    
+	    
+	    return null; // FileView superclass will do the default
+	}
+        
+	// The name of the file.
+	public String getName(File f) { 
+	    if(!f.equals(previous)) {
+		previous = f;
+		// no idea if it's a collection or not, calculate this next time
+		previousWasCollection = false; 
+	    }
+	    return f.getName(); 
+	}
+
+	// A human readable description of the type of the file.
+	public String getTypeDescription(File f) {
+	    if(f.equals(previous) && previousWasCollection) {
+		return "collection";
+	    } 
+	    if(isCollectionBase(f)) {
+		return "collection";
+	    }
+	    // else
+	    return "not a collection";
+	}
+
+	// Whether the directory is traversable or not.
+	public Boolean isTraversable(File f) {
+	    if(f.equals(previous) && previousWasCollection) {
+		return Boolean.FALSE;
+	    }		
+	    
+	    if(isCollectionBase(f)) {
+		return Boolean.FALSE;
+	    }
+
+	    if(f.isFile()) { // not traversable
+		return Boolean.FALSE;
+	    }
+	    // any other kind of directory is traversable
+	    return Boolean.TRUE;	    
+	}
+
+    
+	/** returns false for collect groups and collect dirs. */
+	private boolean isCollectionBase(File f) {	    
+	    previous = f;
+	    previousWasCollection = false;
+
+	    if(!f.isFile()) {
+		// If the directory has a etc/collect.cfg file then it looks like a collection
+		File collect_cfg_file = new File(f, configfile_name);
+		if (collect_cfg_file.exists()) {
+		    // Check for group coll: Check the collect.cfg for collectgroup=true
+		    BasicCollectionConfiguration config = new BasicCollectionConfiguration(collect_cfg_file);
+		    if (!config.getCollectGroup().equals("true")) {
+			previousWasCollection = true;
+		    }		
+		}
+	    }
+	    return previousWasCollection;
+	}
+
+    }
 }
 
Index: /main/trunk/gli/src/org/greenstone/gatherer/gui/OpenCollectionDialog.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/gui/OpenCollectionDialog.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/gui/OpenCollectionDialog.java	(revision 22605)
@@ -58,4 +58,5 @@
     private int result;
     private JButton cancel_button;
+    private JButton chdir_button;
     private JButton open_button;
     private JList collection_list;
@@ -63,4 +64,5 @@
     private JPanel description_pane;
     private String filename;
+    private String newCollectPath;
 
     public OpenCollectionDialog() {
@@ -70,5 +72,6 @@
 	setSize(SIZE);
 	setTitle(Dictionary.get("OpenCollectionDialog.Title"));
-	
+	newCollectPath = Gatherer.getCollectDirectoryPath();
+
 	// Creation
 	JPanel content_pane = (JPanel) getContentPane();
@@ -103,8 +106,12 @@
 	open_button = new GLIButton(Dictionary.get("OpenCollectionDialog.Open"), Dictionary.get("OpenCollectionDialog.Open_Tooltip"));
 	open_button.setEnabled(false);
-	
+	chdir_button = new GLIButton(Dictionary.get("General.CD"), Dictionary.get("General.CD_Tooltip"));
 	cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
 	
 	// Connection
+	chdir_button.addActionListener(new ChangeDirListener());
+	if(Gatherer.isGsdlRemote) { // disable changing directories for client GLI
+	    chdir_button.setEnabled(false);
+	}
 	cancel_button.addActionListener(new CancelListener());
 	open_button.addActionListener(new OpenListener());
@@ -137,8 +144,6 @@
 	center_pane.add(description_pane);
 
-	button_pane.setLayout(new GridLayout(1,4));
-        JPanel tmp =new JPanel();
-        tmp.setComponentOrientation(Dictionary.getOrientation());
-	button_pane.add(tmp);
+	button_pane.setLayout(new GridLayout(1,3));
+        button_pane.add(chdir_button);
 	button_pane.add(open_button);
 	button_pane.add(cancel_button);
@@ -164,4 +169,24 @@
     public String getFileName() {
 	return this.filename;
+    }
+
+    private class ChangeDirListener implements ActionListener {
+	public void actionPerformed(ActionEvent event) {
+	    JFileChooser chooser = new JFileChooser(newCollectPath);
+	    chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+	    chooser.setDialogTitle(Dictionary.get("General.ChooseCollectDirectory"));
+	    int returnVal = chooser.showOpenDialog(OpenCollectionDialog.this);
+	    if(returnVal == JFileChooser.APPROVE_OPTION) {
+		String oldCollectPath = newCollectPath;
+		newCollectPath = chooser.getSelectedFile().getAbsolutePath() + File.separator;
+		
+		if(!newCollectPath.equals(oldCollectPath)) {
+		    // reload the list of available collections using this new path
+		    CollectionListModel listModel = (CollectionListModel)collection_list.getModel();
+		    listModel.reload(newCollectPath);
+		    collection_list.repaint();
+		}
+	    } 
+	}
     }
 
@@ -268,5 +293,11 @@
 	public CollectionListModel() {
 	    data = new TreeSet();
-	    File collect_directory = new File(Gatherer.getCollectDirectoryPath());
+	    reload(Gatherer.getCollectDirectoryPath());
+	}
+
+	// to be called when the collection directory has changed
+	public void reload(String collectDirectoryPath) {
+	    data.clear();
+	    File collect_directory = new File(collectDirectoryPath);
 	    if (!collect_directory.exists()) {
 		collect_directory = null;
@@ -276,4 +307,7 @@
 	    load_collection_configs(data,collect_directory);
 
+	    // list size/contents has changed, this listmodel must notify the 
+	    // encapsulating JList, else it won't be able to display longer lists
+	    fireContentsChanged(this, 0, data.size()); 
 	}
 
@@ -389,4 +423,8 @@
 		OpenCollectionDialog.this.dispose();
 	    }
+
+	    Gatherer.collectDirectoryHasChanged(Gatherer.getCollectDirectoryPath(), newCollectPath); 
+	        // will tell the server that the collect directory has changed and that
+		// the workspace needs to be refreshed (Documents in Greenstone Collections)
 	}
     }
Index: /main/trunk/gli/src/org/greenstone/gatherer/gui/Preferences.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/gui/Preferences.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/gui/Preferences.java	(revision 22605)
@@ -68,4 +68,5 @@
     private JButton apply_button;
     private JButton cancel_button;
+    private JButton chdir_button;
     private JButton ok_button;
     private JCheckBox show_file_size_checkbox;
@@ -92,4 +93,6 @@
     private JTextField program_field;
     private JTextField proxy_host_field;
+    private JTextField collect_dir_field;
+
     private Preferences self;
 
@@ -298,4 +301,21 @@
 	    }
 	}
+
+	JPanel collect_dir_pane = new JPanel();
+        collect_dir_pane.setComponentOrientation(Dictionary.getOrientation());
+	collect_dir_pane.setPreferredSize(ROW_SIZE);
+	JLabel collect_dir_label = new JLabel(Dictionary.get("Preferences.Connection.CollectDirectory"));
+	collect_dir_label.setPreferredSize(LABEL_SIZE);
+	collect_dir_label.setComponentOrientation(Dictionary.getOrientation());
+	collect_dir_field = new JTextField(Gatherer.getCollectDirectoryPath());
+	//collect_dir_field = new JTextField(Configuration.getString("general.open_collection"+Configuration.gliPropertyNameSuffix(), true));
+	collect_dir_field.setCaretPosition(0);
+	collect_dir_field.setToolTipText(Dictionary.get("Preferences.Connection.CollectDirectory_Tooltip"));
+	collect_dir_field.setEditable(false);
+	JButton chdir_button = new GLIButton(Dictionary.get("General.CD"), Dictionary.get("General.CD_Tooltip"));
+	chdir_button.addActionListener(new ChangeDirListener());
+	if(Gatherer.isGsdlRemote) { // disable changing directories for client GLI
+	    chdir_button.setEnabled(false);
+	}
 	
 	boolean currently_enabled = Configuration.get("general.use_proxy", true);
@@ -370,4 +390,9 @@
 	}
 
+	collect_dir_pane.setLayout(new BorderLayout());
+	collect_dir_pane.add(collect_dir_label, BorderLayout.LINE_START);
+	collect_dir_pane.add(collect_dir_field, BorderLayout.CENTER);
+	collect_dir_pane.add(chdir_button, BorderLayout.LINE_END);
+
 	proxy_host_pane.setLayout(new BorderLayout());
 	proxy_host_pane.add(proxy_host_label, BorderLayout.LINE_START);
@@ -389,4 +414,6 @@
 	    connection_pane.add(servlet_pane);
 	}
+	connection_pane.add(collect_dir_pane);
+
 	connection_pane.add(use_proxy_checkbox);
 	connection_pane.add(proxy_host_pane);
@@ -731,4 +758,14 @@
     }
 
+    private class ChangeDirListener implements ActionListener {
+	public void actionPerformed(ActionEvent event) {
+	    JFileChooser chooser = new JFileChooser(collect_dir_field.getText());
+	    chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+	    int returnVal = chooser.showOpenDialog(Preferences.this);
+	    if(returnVal == JFileChooser.APPROVE_OPTION) {
+		collect_dir_field.setText(chooser.getSelectedFile().getAbsolutePath() + File.separator);
+	    } // else collect_dir_field text remains as it is
+	}
+    }
 
     private class OKButtonListener
@@ -826,4 +863,14 @@
 		Configuration.setSiteAndServlet(new_site, (String)servlet_combobox.getSelectedItem());
 	    }
+
+	    // collect directory change
+	    String newCollectPath = collect_dir_field.getText();
+	    if(!newCollectPath.endsWith(File.separator)) {
+		newCollectPath += File.separator;
+	    }
+	    Gatherer.collectDirectoryHasChanged(Gatherer.getCollectDirectoryPath(), newCollectPath);
+		// will tell the server that the collect directory has changed and that
+		// the workspace needs to be refreshed (Documents in Greenstone Collections)
+
 	   
 	    Configuration.set("general.use_proxy", true, use_proxy_checkbox.isSelected());
Index: /main/trunk/gli/src/org/greenstone/gatherer/util/Utility.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/util/Utility.java	(revision 22604)
+++ /main/trunk/gli/src/org/greenstone/gatherer/util/Utility.java	(revision 22605)
@@ -459,4 +459,79 @@
     return new_name;
   }
+
+    
+    /** Write out a property line--a (property, value) pair--to the gsdl(3)site.cfg file.
+     * If the file already contains the line as-is, it is not re-written.	
+     * If the file doesn't contain the line, it is appended.
+     * If the file contained a different value for the property, the line is corrected
+     * and the file is written out.
+     * Not using the Properties class, as we want to keep the file's contents in the 
+     * same order and preserve all the comments in as they're meant to help the user.
+     */
+    public static void updatePropertyConfigFile(String filename, String propertyName, String propertyValue) {
+	BufferedReader fin = null;
+	BufferedWriter fout = null;
+	StringBuffer contents = new StringBuffer();
+	String insertLine = propertyName+"\t"+propertyValue+"\n"; // new line after every propertyLine
+	boolean found = false;
+	try {
+	    fin = new BufferedReader(new FileReader(filename));
+	    String line = "";
+	    while((line = fin.readLine()) != null) {
+		if(line.startsWith(propertyName)) { // won't match comment
+		    found = true;
+		    if(line.equals(insertLine)) { // file is already correct, nothing to do
+			fin.close();
+			fin = null;
+			break;
+		    } else {
+			contents.append(insertLine);
+		    }
+
+		} else { // any other line
+		    contents.append(line);		    
+		    contents.append("\n"); // ensures the required new line at end of file
+		}
+	    }
+
+	    if(fin != null) { // need to write something out to the file
+		fin.close();
+		fin = null;
+
+		// if collecthome wasn't already specified in the file, append it
+		if(!found) { 
+		    fout = new BufferedWriter(new FileWriter(filename, true)); // append mode
+		    fout.write(insertLine, 0, insertLine.length());
+		} else {
+		    
+		    fout = new BufferedWriter(new FileWriter(filename)); // hopefully this will overwrite
+		    fout.write(contents.toString(), 0, contents.length());
+		}
+
+		fout.close();
+		fout = null;
+	    } // else the file is fine
+	} catch(IOException e) {
+	    System.err.println("*** Could not update file: " + filename); 
+	    System.err.println("with the " + propertyName + " property set to " + propertyValue);
+	    System.err.println("Exception occurred: " + e.getMessage());
+	} finally {
+	    try {
+		if(fin != null) {
+		    fin.close();
+		    fin = null;
+		}
+		if(fout != null) {
+		    fout.close();
+		    fout = null;
+		}
+	    } catch(IOException ioe) {
+		fin = null;
+		fout = null;
+	    }
+	    
+	}
+	
+    }
    
 }
