/* MultiComparator.java
 *
 * created: Tue Sep 11 2001
 *
 * This file is part of Artemis
 *
 * Copyright (C) 2001,2002  Genome Research Limited
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /nfs/pathsoft/repository/powmap/diana/components/MultiComparator.java,v 1.18 2002/11/04 18:03:45 kmr Exp $
 */

package diana.components;

import AppGlobal;

import diana.*;
import diana.plot.*;
import diana.sequence.*;

import uk.ac.sanger.pathogens.OutOfRangeException;
import uk.ac.sanger.pathogens.ReadOnlyException;
import uk.ac.sanger.pathogens.InputStreamProgressListener;
import uk.ac.sanger.pathogens.embl.EntryInformationException;
import uk.ac.sanger.pathogens.embl.Range;
import uk.ac.sanger.pathogens.embl.DocumentEntryFactory;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.Vector;
import java.util.EventObject;
import java.net.URL;

/**
 *  This Frame component contains an arbitrary number of AlignmentViewer
 *  components and FeatureDisplay components along with ComparatorGlue objects
 *  to keep them synchronized.
 *
 *  @author Kim Rutherford <kmr@sanger.ac.uk>
 *  @version $Id: MultiComparator.java,v 1.18 2002/11/04 18:03:45 kmr Exp $
 **/

public class MultiComparator extends Frame {
  /**
   *  Initialise entry_group_array and comparison_data_array and create all
   *  the FeatureDisplay and AlignmentViewer components.
   *  @param entry_group_array The EntryGroup components to display.
   *  @param comparison_data_array The ComparisonData to display.  This array
   *    must have one element less than entry_group_array.
   *  @param progress_listener The object to which InputStreamProgressEvents
   *    will be send while reading.
   **/
  public MultiComparator (final EntryGroup [] entry_group_array,
                          final ComparisonData [] comparison_data_array,
                          final InputStreamProgressListener
                            progress_listener) {
    if (entry_group_array.length != comparison_data_array.length + 1) {
      throw new Error ("internal error - " +
                       "MultiComparator got illegal arguments");
    }

    this.entry_group_array = entry_group_array;
    this.comparison_data_array = comparison_data_array;
    this.progress_listener = progress_listener;

    this.entry_sources = AppGlobal.getEntrySources (this, null);

    final StringBuffer title_buffer = new StringBuffer ();

    title_buffer.append ("ACT: ");

    for (int i = 0 ; i < entry_group_array.length - 1 ; ++i) {
      final String sequence_name =
        entry_group_array[i].getDefaultEntry ().getName ();
      title_buffer.append (sequence_name).append (" vs ");
    }

    final EntryGroup last_entry_group =
      entry_group_array[entry_group_array.length - 1];

    title_buffer.append (last_entry_group.getDefaultEntry ().getName ());

    setTitle (title_buffer.toString ());

    makeSelectionArray ();
    makeGotoEventSourceArray ();

    feature_display_array =
      new FeatureDisplay [entry_group_array.length];
    base_plot_group_array =
      new BasePlotGroup [entry_group_array.length];
    alignment_viewer_array =
      new AlignmentViewer [comparison_data_array.length];
    comparator_glue_array =
      new ComparatorGlue [comparison_data_array.length];

    for (int i = 0 ; i < getEntryGroupArray ().length ; ++i) {
      final EntryGroup this_entry_group = getEntryGroupArray ()[i];

      final BasePlotGroup base_plot_group =
        new BasePlotGroup (this_entry_group,
                           this,
                           getSelectionArray ()[i],
                           getGotoEventSourceArray ()[i]);

      feature_display_array[i] =
        new FeatureDisplay (this_entry_group,
                            getSelectionArray ()[i],
                            getGotoEventSourceArray ()[i],
                            base_plot_group,
                            // put scrollbar at the bottom normally
                            !(i == getEntryGroupArray ().length - 1 &&
                              getEntryGroupArray ().length == 2));

      feature_display_array[i].setShowLabels (false);
      feature_display_array[i].setHardLeftEdge (false);

      if (getEntryGroupArray ().length > 2) {
        feature_display_array[i].setShowForwardFrameLines (false);
        feature_display_array[i].setShowReverseFrameLines (false);
      }

      feature_display_array[i].addDisplayAdjustmentListener (base_plot_group);

      base_plot_group_array[i] = base_plot_group;

      this_entry_group.ref ();
    }

    for (int i = 0 ; i < getComparisonDataArray ().length ; ++i) {
      alignment_viewer_array[i] =
        new AlignmentViewer (getFeatureDisplayArray ()[i] ,
                             getFeatureDisplayArray ()[i + 1],
                             getComparisonDataArray ()[i]);

      comparator_glue_array[i] =
        new ComparatorGlue (this,
                            getFeatureDisplayArray ()[i],
                            getFeatureDisplayArray ()[i + 1],
                            getAlignmentViewerArray ()[i]);
    }

    final Font default_font = getDefaultFont ();

    setFont (default_font);

    makeMenus ();

    GridBagLayout gridbag = new GridBagLayout();
    setLayout (gridbag);

    GridBagConstraints c = new GridBagConstraints();

    c.gridwidth  = GridBagConstraints.REMAINDER;
    c.fill       = GridBagConstraints.HORIZONTAL;
    c.anchor     = GridBagConstraints.NORTH;
    c.gridheight = 1;
    c.weightx    = 1;
    c.weighty    = 0;

    for (int i = 0 ; i < getFeatureDisplayArray ().length ; ++i) {
      if (!(i == getEntryGroupArray ().length - 1 &&
            getEntryGroupArray ().length == 2)) {
        // put graph above the sequence in this case
        c.weighty = 0;
        gridbag.setConstraints (base_plot_group_array[i], c);
        add (base_plot_group_array[i]);
      }

      c.weighty = 0;
      gridbag.setConstraints (feature_display_array[i], c);
      add (feature_display_array[i]);

      if (i == getEntryGroupArray ().length - 1 &&
            getEntryGroupArray ().length == 2) {
        // put graph below the sequence in this case
        c.weighty = 0;
        gridbag.setConstraints (base_plot_group_array[i], c);
        add (base_plot_group_array[i]);
      }

      if (i < getAlignmentViewerArray ().length) {
        c.fill = GridBagConstraints.BOTH;
        c.weighty = 1;
        gridbag.setConstraints (getAlignmentViewerArray ()[i], c);
        add (getAlignmentViewerArray ()[i]);
      }
    }

    for (int i = 0 ; i < getEntryGroupArray ().length ; ++i) {
      final EntryGroupChangeListener change_listener =
        new EntryGroupChangeListener () {
          public void entryGroupChanged (final EntryGroupChangeEvent event) {
            switch (event.getType ()) {
            case EntryGroupChangeEvent.ENTRY_ADDED:
            case EntryGroupChangeEvent.ENTRY_DELETED:
              makeFileMenu ();
              break;
            }
          }
        };

      getEntryGroupArray ()[i].addEntryGroupChangeListener (change_listener);

      final EntryChangeListener entry_change_listener =
        new EntryChangeListener () {
          public void entryChanged (final EntryChangeEvent event) {
            switch (event.getType ()) {
            case EntryChangeEvent.NAME_CHANGED:
              makeFileMenu ();
              break;
            }
          }
        };

      getEntryGroupArray ()[i].addEntryChangeListener (entry_change_listener);
    }

    addWindowListener (new WindowAdapter () {
      public void windowClosing (WindowEvent event) {
        closeComparator ();
      }
    });

    final URL icon_url = Splash.class.getResource ("/icon.gif");

    if (icon_url != null) {
      Toolkit toolkit = Toolkit.getDefaultToolkit ();

      final Image icon_image = toolkit.getImage (icon_url);

      MediaTracker tracker = new MediaTracker (this);

      tracker.addImage (icon_image, 0);

      try {
        tracker.waitForAll ();
        setIconImage (icon_image);
      } catch (InterruptedException e) {
        // ignore and continue
      }
    }

    final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();

    packme ();

    int screen_height = screen.height;
    int screen_width = screen.width;

    if (getAlignmentViewerArray ().length > 0) {
      if (screen_width <= 900 || screen_height <= 700) {
        setSize (screen_width * 9 / 10, screen_height * 9 / 10);
      } else {
        setSize (900, 700);
      }
    }

    setLocation (new Point ((screen.width - getSize ().width) / 2,
                            (screen.height - getSize ().height) / 2));
  }

  /**
   *  Move all the sequences (and comparisons) up one place.  The first and
   *  last EntryGroups should be the same (reference).
   **/
  private void rotateSequences () {
    final EntryGroup [] new_entry_groups =
      new EntryGroup [getEntryGroupArray ().length];

    final ComparisonData [] new_comparison_data =
      new ComparisonData [getComparisonDataArray ().length];

    for (int i = 1 ; i < new_entry_groups.length ; ++i) {
      new_entry_groups[i - 1] = getEntryGroupArray ()[i];
    }

    new_entry_groups[new_entry_groups.length - 1] = new_entry_groups[0];

    for (int i = 1 ; i < new_comparison_data.length ; ++i) {
      new_comparison_data[i - 1] = getComparisonDataArray ()[i];
    }

    new_comparison_data[new_comparison_data.length - 1] =
      getComparisonDataArray()[0];

    final MultiComparator new_comparator =
      new MultiComparator (new_entry_groups, new_comparison_data,
                           progress_listener);

    setVisible (false);

    new_comparator.setVisible (true);

    closeComparator ();
  }

  /**
   *  If there are no unsaved changes, close this EntryEdit.  Otherwise ask
   *  the user first.
   **/
  private void closeComparator () {
    for (int i = 0 ; i < getEntryGroupArray ().length ; ++i) {
      final EntryGroup entry_group = getEntryGroupArray ()[i];

      if (entry_group.hasUnsavedChanges () &&
          entry_group.refCount () == 1) {
        final YesNoDialog yes_no_dialog =
          new YesNoDialog (this,
                           "there are unsaved changes - really close?");

        if (!yes_no_dialog.getResult ()) {
          return;
        }
      }
    }

    for (int i = 0 ; i < getEntryGroupArray ().length ; ++i) {
      final EntryGroup entry_group = getEntryGroupArray ()[i];

      entry_group.unref ();
    }

    dispose ();
  }

  /**
   *  This method will call pack () and then move the Frame to the centre of
   *  the screen.  (Implementation of the FeatureDisplayOwner interface).
   **/
  public void packme () {
    pack ();

    final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();

    final int x_position = (screen.width - getSize ().width) / 2;
    int y_position = (screen.height - getSize ().height) / 2;

    if (y_position < 10) {
      y_position = 10;
    }

    setLocation (new Point (x_position, y_position));
  }

  /**
   *  Make one Selection object each EntryGroup.
   **/
  private void makeSelectionArray () {
    selection_array = new Selection [getEntryGroupArray ().length];
    for (int i = 0 ; i < selection_array.length ; ++i) {
      selection_array[i] = new Selection (null);
    }
  }

  /**
   *  Make subject_goto_event_source and query_goto_event_source and wire them
   *  togeather so that goto events do the right thing.
   **/
  private void makeGotoEventSourceArray () {
    goto_event_source_array =
      new GotoEventSource [getEntryGroupArray ().length];

    for (int i = 0 ; i < goto_event_source_array.length ; ++i) {
      goto_event_source_array[i] =
        new SimpleGotoEventSource (getEntryGroupArray ()[i]) {
          /**
           *  Send the given event to all the GotoListeners.
           **/
          public void sendGotoEvent (final GotoEvent goto_event) {
            // temporarily disable moving so that the other FeatureDisplay
            // doesn't start moving (because of the listeners set up in
            // addDisplayListeners ()
            super.sendGotoEvent (goto_event);
          }
        };
    }
  }

  /**
   *  Save the given entry, prompting for a file name if necessary.
   **/
  private void saveEntry (final Entry entry) {
    try {
      if (entry.getName () == null) {
        // prompt for a name
        final FileDialog dialog =
          new FileDialog (this,
                          "Select an file name...",
                          FileDialog.SAVE);

        dialog.setVisible (true);

        if (dialog.getFile () == null || dialog.getFile ().length () == 0) {
          new MessageDialog (this, "no file name given for save");
          return;
        } else {
          final String file_name = dialog.getDirectory () +
            java.io.File.separator + dialog.getFile ();

          if (new File (file_name).exists ()) {
            final YesNoDialog yes_no_dialog =
              new YesNoDialog (this, "this file exists: " + file_name +
                               " overwrite it?");

            if (yes_no_dialog.getResult ()) {
              // yes - continue
            } else {
              // no
              return;
            }
          }

          final MessageDialog message;

          message = new MessageDialog (this,
                                       "saving to " + file_name + " ...",
                                       false);

          try {
            entry.save (file_name, DocumentEntryFactory.ANY_FORMAT, false);
            entry.setName (file_name);
          } catch (EntryInformationException e) {
            final YesNoDialog yes_no_dialog =
              new YesNoDialog (this, "destination format can't handle all " +
                               "keys/qualifiers continue?");
            if (yes_no_dialog.getResult ()) {
              try {
                entry.save (file_name, DocumentEntryFactory.ANY_FORMAT, true);
                entry.setName (file_name);
              } catch (EntryInformationException e2) {
                throw new Error ("internal error - unexpected exception: "+ e);
              }
            } else {
              return;
            }
          } finally {
            message.dispose ();
          }
        }
      } else {
        final MessageDialog message =
          new MessageDialog (this,
                             "saving to " + entry.getName () + " ...",
                             false);

        entry.save (DocumentEntryFactory.ANY_FORMAT);

        message.dispose ();
      }
    } catch (ReadOnlyException e) {
      new MessageDialog (this, "this entry is read only");
      return;
    } catch (IOException e) {
      new MessageDialog (this, "error while saving: " + e);
      return;
    } catch (EntryInformationException e) {
      new MessageDialog (this, "error while saving: " + e);
      return;
    }
  }

  /**
   *  Return the name to use for the sub Menu for the given EntryGroup.
   **/
  private String makeNewSubMenuName (final EntryGroup entry_group) {
    final Entry sequence_entry = entry_group.getSequenceEntry ();

    if (sequence_entry == null) {
      return "(no name)";
    } else {
      final String sequence_name = sequence_entry.getName ();
      if (sequence_name == null) {
        return "(no name)";
      } else {
        return sequence_name;
      }
    }

  }

  /**
   *  Make and add the menus for this component.
   **/
  private void makeMenus () {
    final Font default_font = getDefaultFont ();

    setMenuBar (menu_bar);

    makeFileMenu ();

    menu_bar.add (file_menu);

    final Menu entries_menu = new Menu ("Entries");
    menu_bar.add (entries_menu);
    final Menu select_menu = new Menu ("Select");
    menu_bar.add (select_menu);
    final Menu view_menu = new Menu ("View");
    menu_bar.add (view_menu);
    final Menu goto_menu = new Menu ("Goto");
    menu_bar.add (goto_menu);
    final Menu edit_menu = new Menu ("Edit");
    menu_bar.add (edit_menu);
    final Menu create_menu = new Menu ("Create");
    menu_bar.add (create_menu);
    final Menu write_menu = new Menu ("Write");
    menu_bar.add (write_menu);
    Menu run_menu = null;
    if (Options.isUnixHost ()) {
      run_menu = new Menu ("Run");
      menu_bar.add (run_menu);
    }
    final Menu graph_menu = new Menu ("Graph");
    menu_bar.add (graph_menu);

    for (int i = 0 ; i < getEntryGroupArray ().length ; ++i) {
      final EntryGroup entry_group = getEntryGroupArray ()[i];

      final String sub_menu_name = makeNewSubMenuName (entry_group);

      final EntryGroupMenu this_entries_menu =
        new EntryGroupMenu (this,
                            getEntryGroupArray ()[i],
                            sub_menu_name);
      entries_menu.add (this_entries_menu);

      final SelectMenu this_select_menu =
        new SelectMenu (this,
                        getSelectionArray ()[i],
                        getGotoEventSourceArray ()[i],
                        getEntryGroupArray ()[i],
                        getBasePlotGroupArray ()[i],
                        sub_menu_name);
      select_menu.add (this_select_menu);

      final ViewMenu this_view_menu =
        new ViewMenu (this,
                      getSelectionArray ()[i],
                      getGotoEventSourceArray ()[i],
                      getEntryGroupArray ()[i],
                      getBasePlotGroupArray ()[i],
                      sub_menu_name);
      view_menu.add (this_view_menu);

      final GotoMenu this_goto_menu =
        new GotoMenu (this,
                      getSelectionArray ()[i],
                      getGotoEventSourceArray ()[i],
                      getEntryGroupArray ()[i],
                      sub_menu_name);
      goto_menu.add (this_goto_menu);

      AddMenu this_create_menu = null;

      if (Options.isStandAlone ()) {
        final EditMenu this_edit_menu =
          new EditMenu (this,
                        getSelectionArray ()[i],
                        getGotoEventSourceArray ()[i],
                        getEntryGroupArray ()[i],
                        getBasePlotGroupArray ()[i],
                        sub_menu_name);
        edit_menu.add (this_edit_menu);

        this_create_menu =
          new AddMenu (this,
                       getSelectionArray ()[i],
                       getEntryGroupArray ()[i],
                       getGotoEventSourceArray ()[i],
                       getBasePlotGroupArray ()[i],
                       sub_menu_name);
        create_menu.add (this_create_menu);

        final WriteMenu this_write_menu =
          new WriteMenu (this,
                         getSelectionArray ()[i],
                         getEntryGroupArray ()[i],
                         sub_menu_name);
        write_menu.add (this_write_menu);

        if (Options.isUnixHost ()) {
          final RunMenu this_run_menu =
            new RunMenu (this,
                         getSelectionArray ()[i],
                         sub_menu_name);
          run_menu.add (this_run_menu);
        }
      }

      final GraphMenu this_graph_menu =
        new GraphMenu (this,
                       getEntryGroupArray ()[i],
                       getBasePlotGroupArray ()[i],
                       getFeatureDisplayArray ()[i],
                       sub_menu_name);
      graph_menu.add (this_graph_menu);
    }

    final Menu display_menu = new Menu ("Display");

    final MenuItem hide_on_frame_lines_item =
      new MenuItem ("Hide Frame Lines");
    hide_on_frame_lines_item.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent event) {
        for (int i = 0 ; i < getFeatureDisplayArray ().length ; ++i) {
          getFeatureDisplayArray ()[i].setShowForwardFrameLines (false);
          getFeatureDisplayArray ()[i].setShowReverseFrameLines (false);
        }
      }
    });

    display_menu.add (hide_on_frame_lines_item);

    final MenuItem show_on_frame_lines_item =
      new MenuItem ("Show Frame Lines");
    show_on_frame_lines_item.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent event) {
        for (int i = 0 ; i < getFeatureDisplayArray ().length ; ++i) {
          getFeatureDisplayArray ()[i].setShowForwardFrameLines (true);
          getFeatureDisplayArray ()[i].setShowReverseFrameLines (true);
        }
      }
    });

    display_menu.add (show_on_frame_lines_item);

    menu_bar.add (display_menu);
  }

  /**
   *  Make a new File menu replacing the current one (if any).
   **/
  private void makeFileMenu () {
    file_menu.removeAll ();

    final EntryGroup [] entry_group_array = getEntryGroupArray ();

    if (entry_group_array[0] ==
        entry_group_array[entry_group_array.length - 1]) {
      final MenuItem rotate_button = new MenuItem ("Rotate Sequences");
      rotate_button.addActionListener (new ActionListener () {
        public void actionPerformed (ActionEvent event) {
          rotateSequences ();
        }
      });

      file_menu.add (rotate_button);

      file_menu.addSeparator ();
    }

    for (int i = 0 ; i < getEntryGroupArray ().length ; ++i) {
      final EntryGroup entry_group = getEntryGroupArray ()[i];

      final String new_menu_name = makeNewSubMenuName (entry_group);

      final Menu entry_group_menu = new Menu (new_menu_name);

      file_menu.add (entry_group_menu);

      for (int source_index = 0 ;
           source_index < entry_sources.size () ;
           ++source_index) {
        final EntrySource this_source =
          entry_sources.elementAt (source_index);

        if (this_source.isFullEntrySource ()) {
          continue;
        }

        String entry_source_name = this_source.getSourceName ();

        String menu_item_name = null;

        if (entry_source_name.equals ("Filesystem")) {
          menu_item_name = "Read An Entry ...";
        } else {
          menu_item_name = "Read An Entry From " + entry_source_name + " ...";
        }

        final MenuItem read_entry = new MenuItem (menu_item_name);

        read_entry.addActionListener (new ActionListener () {
          public void actionPerformed (ActionEvent event) {
            try {
              final Entry new_entry =
                this_source.getEntry (entry_group.getBases (), true);

              if (new_entry != null) {
                entry_group.add (new_entry);
              }
            } catch (OutOfRangeException e) {
              new MessageDialog (MultiComparator.this,
                                 "read failed: one of the features in the" +
                                 " entry has an out of range location: " +
                                 e.getMessage ());
              return;
            } catch (IOException e) {
              new MessageDialog (MultiComparator.this,
                                 "read failed due to an IO error: " +
                                 e.getMessage ());
              return;
            }
          }
        });

        entry_group_menu.add (read_entry);
      }

      entry_group_menu.addSeparator ();

      if (entry_group == null || entry_group.size () == 0) {
        // don't create a menu
      } else {

        final Menu save_entry_menu =
          new Menu ("Save Entry");

        for (int entry_index = 0 ;
             entry_index < entry_group.size () ;
             ++entry_index) {
          final Entry this_entry = entry_group.elementAt (entry_index);

          String entry_name = this_entry.getName ();

          if (entry_name == null) {
            entry_name = "no name";
          }

          final ActionListener save_entry_listener =
            new ActionListener () {
              public void actionPerformed (final ActionEvent event) {
                MultiComparator.this.saveEntry (this_entry);
              }
            };

          final MenuItem save_entry_item = new MenuItem (entry_name);

          save_entry_item.addActionListener (save_entry_listener);

          save_entry_menu.add (save_entry_item);
        }

        entry_group_menu.add (save_entry_menu);

        final MenuItem save_all_menu = new MenuItem ("Save All");

        save_all_menu.addActionListener (new ActionListener () {
          public void actionPerformed (ActionEvent event) {
            for (int entry_index = 0 ;
                 entry_index < entry_group.size () ;
                 ++entry_index) {
              final Entry this_entry = entry_group.elementAt (entry_index);

              MultiComparator.this.saveEntry (this_entry);
            }
          }
        });

        entry_group_menu.add (save_all_menu);

        entry_group_menu.addSeparator ();
      }

      final MenuItem edit_subject = new MenuItem ("Edit In Artemis");
      edit_subject.addActionListener (new ActionListener () {
        public void actionPerformed (ActionEvent event) {
          final EntryEdit entry_edit = new EntryEdit (entry_group);

          entry_edit.setVisible (true);
        }
      });

      entry_group_menu.add (edit_subject);
    }

    file_menu.addSeparator ();

    final MenuItem close_button = new MenuItem ("Close");
    close_button.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent event) {
        closeComparator ();
      }
    });

    file_menu.add (close_button);
  }

  /**
   *  Return the current default font (from Diana.options).
   **/
  private Font getDefaultFont () {
    return Options.getOptions ().getFont ();
  }

  /**
   *  Return the EntryGroup objects that were passed to the constructor.
   **/
  private EntryGroup [] getEntryGroupArray () {
    return entry_group_array;
  }

  /**
   *  Return the ComparisonData objects that were passed to the constructor.
   **/
  private ComparisonData [] getComparisonDataArray () {
    return comparison_data_array;
  }

  /**
   *  Return the AlignmentViewer objects that were created in the constructor.
   **/
  private AlignmentViewer [] getAlignmentViewerArray () {
    return alignment_viewer_array;
  }

  /**
   *  Return the FeatureDisplay objects that were created in the constructor.
   **/
  private FeatureDisplay [] getFeatureDisplayArray () {
    return feature_display_array;
  }

  /**
   *  Return the Selection objects that were created in the constructor.
   **/
  private Selection [] getSelectionArray () {
    return selection_array;
  }

  /**
   *  Return the GotoEventSource objects that were created in the constructor.
   **/
  private GotoEventSource [] getGotoEventSourceArray () {
    return goto_event_source_array;
  }

  /**
   *  Return the BasePlotGroup objects that were created in the constructor.
   **/
  private BasePlotGroup [] getBasePlotGroupArray () {
    return base_plot_group_array;
  }

  /**
   *  The menu_bar is created in makeMenus ().
   **/
  private final MenuBar menu_bar = new MenuBar ();

  /**
   *  An array of EntryGroup objects to display (set by the constructor).  The
   *  array will contain at least one EntryGroup and will be exactly one
   *  element longer than comparison_data_array.
   **/
  private EntryGroup [] entry_group_array = null;

  /**
   *  An array of ComparisonData objects to display (set by the constructor).
   *  The array will be exactly one element shorter than entry_group_array.
   **/
  private ComparisonData [] comparison_data_array = null;

  /**
   *  An array of Selection objects - one per EntryGroup.  Created by
   *  makeSelectionArray()
   **/
  private Selection [] selection_array = null;

  /**
   *  An array of GotoEventSource objects - one per EntryGroup.  Created by
   *  makeGotoEventSourceArray()
   **/
  private GotoEventSource [] goto_event_source_array = null;

  /**
   *  An array of FeatureDisplay objects - one per EntryGroup.  Created in the
   *  constructor.
   **/
  private FeatureDisplay [] feature_display_array = null;

  /**
   *  An array of AlignmentViewer objects - one per ComparisonData.  Created
   *  in the constructor.
   **/
  private AlignmentViewer [] alignment_viewer_array = null;

  /**
   *  An array of AlignmentViewer objects - one per ComparisonData.  Created
   *  in the constructor.
   **/
  private ComparatorGlue [] comparator_glue_array = null;

  /**
   *  An array of BasePlotGroup objects - one per FeatureDisplay/EntryGroup.
   *  Created in the constructor.
   **/
  private BasePlotGroup [] base_plot_group_array = null;

  /**
   *  This menu is populated by makeFileMenu ().
   **/
  private final Menu file_menu = new Menu ("File");

  /**
   *  The EntrySourceVector reference that is created in the constructor.
   **/
  private EntrySourceVector entry_sources;

  /**
   *  Used to show the progress of loading file.
   **/
  private InputStreamProgressListener progress_listener;
}
