Log in Help
Print
Homereleasesgate-6.0-build3764-ALLpluginsAlignmentsrcgatealignmentgui 〉 AlignmentEditor.java
 
package gate.alignment.gui;

import gate.Annotation;
import gate.AnnotationSet;
import gate.Document;
import gate.alignment.Alignment;
import gate.alignment.gui.views.LinksView;
import gate.alignment.gui.views.MatrixView;
import gate.alignment.gui.views.ParallelTextView;
import gate.compound.CompoundDocument;
import gate.event.DocumentEvent;
import gate.event.DocumentListener;
import gate.event.FeatureMapListener;
import gate.gui.MainFrame;
import gate.swing.ColorGenerator;
import gate.util.GateRuntimeException;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter;

/**
 * This class provides an editor for aligning texts in a compound
 * document.
 */
public class AlignmentEditor extends JFrame implements FeatureMapListener,
                                           DocumentListener {

  /**
   * initialize the editor
   */
  AlignmentEditor editor = null;

  /**
   * Menus for creating new alignment tasks, setting up active learning
   * setup, specifying user preferences etc.
   */
  JMenuBar menubar;

  /**
   * Currently loaded tasks. Use can select from them.
   */
  JMenu alignmentTasksMenu;

  /**
   * Toolbar to hold alignment viewer controls
   */
  JPanel controls;

  /**
   * The document on which it would work
   */
  private CompoundDocument document;

  /**
   * Shows available alignment features alignment features
   */
  private JComboBox alignmentFeatures;

  /**
   * annotation sets of the source document
   */
  private JComboBox srcAnnotationSets;

  /**
   * annotation sets of the target document
   */
  private JComboBox tgtAnnotationSets;

  /**
   * source annotation set
   */
  private AnnotationSet srcAS;

  /**
   * target annotation set
   */
  private AnnotationSet tgtAS;

  /**
   * Current alignment object
   */
  private Alignment alignment;

  /**
   * Flag to notice user if the window needs reloading
   */
  private boolean reload = false;

  /**
   * Refresh highlights button
   */
  private JButton refreshHighlights;

  /**
   * Source document
   */
  private Document srcDoc;

  /**
   * Target document
   */
  private Document tgtDoc;

  /**
   * Editor used for displaying source document
   */
  JTextArea sourceEditor;

  /**
   * Constant representing default annotation set name
   */
  public static final String DEFAULT_AS_NAME = "<null>";

  /**
   * Editor used for displaying target document
   */
  JTextArea targetEditor;

  /**
   * Timer used for determine exactly when to select a highlight
   */
  protected Timer mouseMovementTimer;

  /**
   * Time before the highlight is selected
   */
  private static final int MOUSE_MOVEMENT_TIMER_DELAY = 300;

  /**
   * Action that should be taken when the mouse has stopped moving and
   * is on one of the highlights
   */
  protected MouseStoppedMovingAction mouseStoppedMovingAction;

  /**
   * Highlight used for highlighting aligned annotations.
   */
  final DefaultHighlighter.DefaultHighlightPainter HIGHLIGHT = new DefaultHighlighter.DefaultHighlightPainter(
          getColor(Color.GREEN, 0.1f));

  /**
   * Highlight used for highlighting selected aligned annotations.
   */
  final DefaultHighlighter.DefaultHighlightPainter SELECTED_HIGHLIGHT = new DefaultHighlighter.DefaultHighlightPainter(
          getColor(Color.RED, 0.5f));

  /**
   * Highlight used for highlighting selected aligned annotations.
   */
  final DefaultHighlighter.DefaultHighlightPainter PROCESSING_HIGHLIGHT = new DefaultHighlighter.DefaultHighlightPainter(
          getColor(Color.BLUE, 0.5f));

  /**
   * Color generator used for generatig color
   */
  private static ColorGenerator colorGenerator = new ColorGenerator();

  /**
   * To store currently selected highlight objects
   */
  private Set<Object> srcSelectedHighlights = new HashSet<Object>();

  /**
   * To store currently selected highlight objects
   */
  private Set<Object> tgtSelectedHighlights = new HashSet<Object>();

  /**
   * Stores alignment tasks objects
   */
  private Map<String, AlignmentTask> alignmentTasks = new HashMap<String, AlignmentTask>();

  /**
   * The current alignment task that is being shown on the screen
   */
  private AlignmentTask currentAT = null;

  /**
   * every alignment task has a view associated with it. This map is
   * used for storing the reference
   */
  private HashMap<AlignmentTask, AlignmentTaskView> alignmentTaskViews = new HashMap<AlignmentTask, AlignmentTaskView>();

  /**
   * Alignment task panel
   */
  private JPanel alignmentTaskPanel = null;

  /**
   * Constructor
   * 
   * @param document
   */
  public AlignmentEditor(CompoundDocument document) {
    // setting the title to be alignment editor
    super("Alignment Editor");

    // using the same icon as used for annotation diff
    setIconImage(((ImageIcon)MainFrame.getIcon("annotation-diff")).getImage());

    // when the main GATE gui closes - so should this one
    MainFrame.getGuiRoots().add(this);
    this.editor = this;
    this.document = document;

    /**
     * When the window is closing, the dispose method should be closed
     */
    addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent we) {
        super.windowClosing(we);
        dispose();
      }
    });

    setBounds(0, 0, 800, 600);
    setLayout(new BorderLayout());
    setVisible(true);

    // ask for the source and target documents
    Document[] documents = askForDocuments();
    if(documents == null) {
      dispose();
      return;
    }
    srcDoc = documents[0];
    tgtDoc = documents[1];

    // based on the source and target documents - initialises the GUI
    initGui();

    // we need to listen to the annotation set update
    srcDoc.addDocumentListener(this);
    tgtDoc.addDocumentListener(this);

    Document doc = this.document.getCurrentDocument();
    this.document.getFeatures().addFeatureMapListener(this);
  }

  /**
   * Disposes the window and cleans up the resource
   */
  @Override
  public void dispose() {
    cleanup();
    super.dispose();
  }

  /** Clean up method that is called when the window is closed */
  public void cleanup() {

    // delete document listeners and feature update listener
    if(this.srcDoc != null)
      this.srcDoc.removeDocumentListener(this);
    
    if(this.tgtDoc != null)
      this.tgtDoc.removeDocumentListener(this);
    
    this.document.getFeatures().removeFeatureMapListener(this);
  }

  /**
   * Introduces transparency to the given color. If c is null, a new
   * random color is generated using the color generater class.
   * 
   * @param c
   * @return
   */
  public static Color getColor(Color c, float alphaValue) {
    float components[] = null;
    if(c == null)
      components = colorGenerator.getNextColor().getComponents(null);
    else components = c.getComponents(null);

    Color colour = new Color(components[0], components[1], components[2],
            alphaValue);
    int rgb = colour.getRGB();
    int alpha = colour.getAlpha();
    int rgba = rgb | (alpha << 24);
    colour = new Color(rgba, true);
    return colour;
  }

  /**
   * Initializes this GUI
   */
  private void initGui() {

    // menu for various things
    menubar = new JMenuBar();
    this.setJMenuBar(menubar);
    this.alignmentTasksMenu = new JMenu("Switch Tasks");
    JMenu taskMenu = new JMenu("Alignment Tasks");
    menubar.add(taskMenu);
    taskMenu.add(new NewAlignmentTaskAction());
    taskMenu.add(this.alignmentTasksMenu);
    taskMenu.add(new LoadAlignmentTaskAction());
    taskMenu.add(new SaveAlignmentTaskAction());

    // toolbar for viewer
    controls = new JPanel(new FlowLayout(FlowLayout.LEFT));
    add(controls, BorderLayout.NORTH);

    // full screen mode
    this.setExtendedState(this.getExtendedState() | JFrame.MAXIMIZED_BOTH);

    // preferred size
    this.setPreferredSize(new Dimension(800, 600));

    // editors for source and target documents
    sourceEditor = new JTextArea();
    targetEditor = new JTextArea();

    // we need scrollpanes to show editors
    JScrollPane sourcePane = new JScrollPane(sourceEditor);
    sourcePane.setBorder(BorderFactory.createTitledBorder(srcDoc.getName()));
    JScrollPane targetPane = new JScrollPane(targetEditor);
    targetPane.setBorder(BorderFactory.createTitledBorder(tgtDoc.getName()));

    JPanel contentPanel = new JPanel();
    contentPanel.setLayout(new GridLayout(1, 2));
    contentPanel.add(sourcePane);
    contentPanel.add(targetPane);
    add(contentPanel, BorderLayout.CENTER);

    // set the contents

    Font f = new Font(sourceEditor.getFont().getFontName(), Font.BOLD, 15);
    sourceEditor.setFont(f);
    targetEditor.setFont(f);
    sourceEditor.setAutoscrolls(false);
    sourceEditor.setLineWrap(true);
    sourceEditor.setWrapStyleWord(true);
    targetEditor.setAutoscrolls(false);
    targetEditor.setLineWrap(true);
    targetEditor.setWrapStyleWord(true);

    sourceEditor.setEditable(false);
    targetEditor.setEditable(false);

    sourceEditor.setText(srcDoc.getContent().toString());
    targetEditor.setText(tgtDoc.getContent().toString());

    /**
     * Obtain all the alignment feature names
     */
    List<String> alignmentFeatureNames = new ArrayList<String>(document
            .getAllAlignmentFeatureNames());
    this.alignmentFeatures = new JComboBox(alignmentFeatureNames.toArray());

    controls.add(new JLabel("Alignment Features:"));
    controls.add(this.alignmentFeatures);

    controls.add(new JLabel(" Source InputAS:"));
    this.srcAnnotationSets = new JComboBox(getASNames(srcDoc).toArray());
    controls.add(this.srcAnnotationSets);

    controls.add(new JLabel(" Target InputAS:"));
    this.tgtAnnotationSets = new JComboBox(getASNames(tgtDoc).toArray());
    controls.add(this.tgtAnnotationSets);

    refreshHighlights = new JButton("Go");
    controls.add(refreshHighlights);

    mouseStoppedMovingAction = new MouseStoppedMovingAction();
    mouseMovementTimer = new javax.swing.Timer(MOUSE_MOVEMENT_TIMER_DELAY,
            mouseStoppedMovingAction);
    mouseMovementTimer.setRepeats(false);
    alignmentTaskPanel = new JPanel();

    add(alignmentTaskPanel, BorderLayout.SOUTH);
    initListeners();
    pack();
  }

  /**
   * Highlighting parent annotations.
   * 
   * @param pair
   */
  private void highlightParentAnnotations(PUAPair pair) {
    try {

      // removing previously highlighted highlight
      for(Object o : srcSelectedHighlights) {
        sourceEditor.getHighlighter().removeHighlight(o);
      }

      // removing previously highlighted highlight
      for(Object o : tgtSelectedHighlights) {
        targetEditor.getHighlighter().removeHighlight(o);
      }

      // add new highlights
      if(pair.sourceAnnotations != null) {
        for(Annotation a : pair.sourceAnnotations) {
          Object o = sourceEditor.getHighlighter().addHighlight(
                  a.getStartNode().getOffset().intValue(),
                  a.getEndNode().getOffset().intValue(), PROCESSING_HIGHLIGHT);
          srcSelectedHighlights.add(o);
        }
      }

      // add new highlights
      if(pair.targetAnnotations != null) {
        for(Annotation a : pair.targetAnnotations) {
          Object o = targetEditor.getHighlighter().addHighlight(
                  a.getStartNode().getOffset().intValue(),
                  a.getEndNode().getOffset().intValue(), PROCESSING_HIGHLIGHT);
          tgtSelectedHighlights.add(o);
        }
      }
    }
    catch(BadLocationException e) {
      throw new GateRuntimeException(e);
    }
  }

  /**
   * Action to show the next pair
   * 
   * @author niraj
   * 
   */
  class NextPairAction extends AbstractAction {

    // Alignment task
    AlignmentTask task;

    // Alignment task view
    AlignmentTaskView view;

    /**
     * Constructor
     * 
     * @param view
     */
    public NextPairAction(AlignmentTaskView view) {
      super("Next");
      this.task = view.alignmentTask;
      this.view = view;
    } // constructor

    public void actionPerformed(ActionEvent e) {

      if(task.current() != null) {

        // ask this only if the pair hasn't been marked as finished
        if(!task.current().isAlignmentFinished()) {
          int answer = JOptionPane.showConfirmDialog(editor,
                  "Is alignment complete for this pair?");
          if(answer == JOptionPane.YES_OPTION) {
            task.getAlignmentActionsManager().executeFinishedAlignmentActions(
                    task.current());
            task.current().setAlignmentFinished(true);
          }
          else if(answer == JOptionPane.CANCEL_OPTION) {
            return;
          }
        }
      }

      if(task.hasNext()) {
        PUAPair pair = task.next();
        task.getAlignmentActionsManager().executePreDisplayActions(pair);
        highlightParentAnnotations(pair);
        view.updateView(pair);
      }
      else {
        JOptionPane.showMessageDialog(editor, "Reached End of the Document");
      }
    }
  }

  /**
   * Shows the previous pair
   * 
   * @author niraj
   * 
   */
  class PreviousPairAction extends AbstractAction {

    AlignmentTask task;

    AlignmentTaskView view;

    public PreviousPairAction(AlignmentTaskView view) {
      super("Previous");
      this.task = view.alignmentTask;
      this.view = view;
    } // constructor

    public void actionPerformed(ActionEvent e) {

      // ask this only if the pair hasn't been marked as finished
      if(!task.current().isAlignmentFinished()) {
        int answer = JOptionPane.showConfirmDialog(editor,
                "Is alignment complete for this pair?");
        if(answer == JOptionPane.YES_OPTION) {
          task.getAlignmentActionsManager().executeFinishedAlignmentActions(
                  task.current());
          task.current().setAlignmentFinished(true);
        }
        else if(answer == JOptionPane.CANCEL_OPTION) {
          return;
        }
      }

      if(task.hasPrevious()) {
        PUAPair pair = task.previous();
        highlightParentAnnotations(pair);
        view.updateView(pair);
      }
      else {
        JOptionPane.showMessageDialog(editor, "Reached Start of the Document");
      }
    }
  }

  /**
   * Create a new alignment task
   * 
   * @author niraj
   * 
   */
  class NewAlignmentTaskAction extends AbstractAction {

    public NewAlignmentTaskAction() {
      super("New Task");
    } // constructor

    public void actionPerformed(ActionEvent e) {
      AlignmentTask alignmentTask = createAlignmentTask();
      if(alignmentTask == null) return;
      alignmentTasks.put(alignmentTask.getName(), alignmentTask);
      alignmentTasksMenu.add(new SwitchAlignmentTaskAction(alignmentTask
              .getName()));
      currentAT = alignmentTask;

      // updateGUI
      alignmentTaskPanel.removeAll();
      AlignmentTaskView view = new AlignmentTaskView(currentAT);
      alignmentTaskPanel.add(view, BorderLayout.CENTER);
      alignmentTaskViews.put(currentAT, view);
      editor.validate();
    }
  }

  class SwitchAlignmentTaskAction extends AbstractAction {
    /**
     * Alignment task name
     */
    String taskName;

    public SwitchAlignmentTaskAction(String caption) {
      super(caption);
      this.taskName = caption;
    } // constructor

    public void actionPerformed(ActionEvent e) {

      currentAT = alignmentTasks.get(taskName);
      AlignmentTaskView view = alignmentTaskViews.get(currentAT);

      // updateGUI
      alignmentTaskPanel.removeAll();
      alignmentTaskPanel.add(view, BorderLayout.CENTER);
      editor.validate();
    }

  }

  class HideTaskAction extends AbstractAction {

    AlignmentTaskView view;

    public HideTaskAction(AlignmentTaskView view) {
      super("Hide");
      this.view = view;
    } // constructor

    public void actionPerformed(ActionEvent e) {

      // updateGUI
      alignmentTaskPanel.removeAll();
      editor.validate();
    }

  }

  class CloseTaskAction extends AbstractAction {

    AlignmentTaskView view;

    public CloseTaskAction(AlignmentTaskView view) {
      super("Close");
      this.view = view;
    } // constructor

    public void actionPerformed(ActionEvent e) {
      if(currentAT != null) {
        alignmentTasks.remove(currentAT.getName());

        for(int i = 0; i < alignmentTasksMenu.getItemCount(); i++) {
          JMenuItem mi = alignmentTasksMenu.getItem(i);
          SwitchAlignmentTaskAction sata = (SwitchAlignmentTaskAction)mi
                  .getAction();
          if(sata.taskName.equals(currentAT.getName())) {
            alignmentTasksMenu.remove(mi);
            alignmentTasksMenu.updateUI();
            break;
          }
        }

        alignmentTaskViews.remove(currentAT);
        currentAT.getAlignmentActionsManager().cleanup();
        currentAT = null;

        // updateGUI
        alignmentTaskPanel.removeAll();
        editor.validate();
      }
    }

  }

  class LoadAlignmentTaskAction extends AbstractAction {

    public LoadAlignmentTaskAction() {
      super("Open Task");
    } // constructor

    public void actionPerformed(ActionEvent e) {
      JFileChooser chooser = MainFrame.getFileChooser();
      chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

      int result = chooser.showOpenDialog(editor);
      if(result == JFileChooser.APPROVE_OPTION) {
        File selected = chooser.getSelectedFile();
        currentAT = AlignmentTask.fromXML(document, selected.getAbsolutePath());
        alignmentTasks.put(currentAT.getName(), currentAT);
        alignmentTasksMenu.add(new SwitchAlignmentTaskAction(currentAT
                .getName()));

        // updateGUI
        alignmentTaskPanel.removeAll();
        AlignmentTaskView view = new AlignmentTaskView(currentAT);
        alignmentTaskPanel.add(view, BorderLayout.CENTER);
        alignmentTaskViews.put(currentAT, view);
        editor.validate();

      }
    }
  }

  class SaveAlignmentTaskAction extends AbstractAction {

    public SaveAlignmentTaskAction() {
      super("Save Task");
    } // constructor

    public void actionPerformed(ActionEvent e) {
      JFileChooser chooser = MainFrame.getFileChooser();
      chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

      int result = chooser.showSaveDialog(editor);
      if(result == JFileChooser.APPROVE_OPTION) {
        File selected = chooser.getSelectedFile();
        AlignmentTask.toXML(currentAT, selected.getAbsolutePath());
      }
    }
  }

  /**
   * Obtains annotation set names from the document String
   * 
   * @param document
   * @return
   */
  private List<String> getASNames(Document document) {
    Set<String> setNames = document.getAnnotationSetNames();
    List<String> setNamesList = null;
    if(setNames == null)
      setNamesList = new ArrayList<String>();
    else setNamesList = new ArrayList<String>(setNames);
    setNamesList.add(0, DEFAULT_AS_NAME);
    return setNamesList;
  }

  private void initListeners() {
    // add focus listener to the current window
    this.addFocusListener(new FocusListener() {

      public void focusGained(FocusEvent e) {
        if(reload) {
          JOptionPane
                  .showMessageDialog(editor,
                          "Alignment information has changed - the document will be refreshed!");
          reload = false;

          // execute the refreshHighlight action
          refreshHighlightsAction();
        }
      }

      public void focusLost(FocusEvent e) {
        // TODO Auto-generated method stub
        // nothing
      }
    });

    // add action listener to the refresh highlights button
    this.refreshHighlights.addActionListener(new ActionListener() {

      // refresh highlights
      public void actionPerformed(ActionEvent e) {
        refreshHighlightsAction();
      }

    });

    SelectHighlightListener sh = new SelectHighlightListener();
    this.sourceEditor.addMouseMotionListener(sh);
    this.targetEditor.addMouseMotionListener(sh);

  }

  private void refreshHighlightsAction() {
    int selectedIndex = srcAnnotationSets.getSelectedIndex();
    if(selectedIndex == -1) return;
    selectedIndex = tgtAnnotationSets.getSelectedIndex();
    if(selectedIndex == -1) return;

    boolean isDefault = srcAnnotationSets.getSelectedIndex() == 0;
    srcAS = isDefault ? srcDoc.getAnnotations() : srcDoc
            .getAnnotations((String)srcAnnotationSets.getSelectedItem());

    isDefault = tgtAnnotationSets.getSelectedIndex() == 0;
    tgtAS = isDefault ? tgtDoc.getAnnotations() : tgtDoc
            .getAnnotations((String)tgtAnnotationSets.getSelectedItem());

    // obtain the alignment object
    alignment = document.getAlignmentInformation((String)alignmentFeatures
            .getSelectedItem());

    refreshHighlights();
  }

  /**
   * Checks the selected alignment information
   */
  private void addHighlights() {
    // obtain all aligned annotations
    int index = this.alignmentFeatures.getSelectedIndex();
    if(index == 0) return;

    addHighlights(sourceEditor, srcAS);
    addHighlights(targetEditor, tgtAS);
  }

  /**
   * Adds highlights to the document - indicating which annotations are
   * aligned
   * 
   * @param alignment
   * @param editorPane
   * @param inputAS
   */
  private void addHighlights(JTextArea editorPane, AnnotationSet inputAS) {
    for(Annotation a : inputAS) {
      if(alignment.isAnnotationAligned(a)) {
        addHighlight(a, editorPane, HIGHLIGHT);
      }
    }
  }

  /**
   * Adds the highlight to annotation
   * 
   * @param a
   * @param editorPane
   * @param painter
   */
  private Object addHighlight(Annotation a, JTextArea editorPane,
          DefaultHighlightPainter painter) {
    try {
      return editorPane.getHighlighter().addHighlight(
              a.getStartNode().getOffset().intValue(),
              a.getEndNode().getOffset().intValue(), painter);
    }
    catch(BadLocationException e) {
      throw new GateRuntimeException(e);
    }
  }

  /**
   * Remove all existing highlights and refresh
   */
  private void refreshHighlights() {
    sourceEditor.getHighlighter().removeAllHighlights();
    targetEditor.getHighlighter().removeAllHighlights();
    addHighlights();
  }

  class SelectHighlightListener implements MouseMotionListener, MouseListener {

    Set<Object> srcToDel = new HashSet<Object>();

    Set<Object> tgtToDel = new HashSet<Object>();

    Map<Annotation, Object> selectedAnnots = new HashMap<Annotation, Object>();

    public void mouseDragged(MouseEvent e) {
      // do not create annotations while dragging
      mouseMovementTimer.stop();
    }

    public void mouseMoved(MouseEvent e) {
      // this triggers select annotation leading to edit annotation or
      // new
      // annotation actions
      // ignore movement if CTRL pressed or dragging
      int modEx = e.getModifiersEx();
      if((modEx & MouseEvent.CTRL_DOWN_MASK) != 0) {
        mouseMovementTimer.stop();
        return;
      }
      if((modEx & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
        mouseMovementTimer.stop();
        return;
      }

      JTextArea textPane = (JTextArea)e.getSource();

      // check the text location is real
      int textLocation = textPane.viewToModel(e.getPoint());
      try {
        Rectangle viewLocation = textPane.modelToView(textLocation);
        // expand the rectangle a bit
        int error = 10;
        viewLocation = new Rectangle(viewLocation.x - error, viewLocation.y
                - error, viewLocation.width + 2 * error, viewLocation.height
                + 2 * error);
        if(viewLocation.contains(e.getPoint())) {
          mouseStoppedMovingAction.setTextLocation(textLocation);
          mouseStoppedMovingAction.setEditorPane(textPane);
        }
        else {
          mouseStoppedMovingAction.setTextLocation(-1);
        }
      }

      catch(BadLocationException e1) {
        // don't do anything
      }
      finally {
        mouseMovementTimer.restart();
      }
    }

    public void mouseExited(MouseEvent e) {
      mouseMovementTimer.stop();
    }

    public void mouseClicked(MouseEvent e) {
      // do nothing
      
    }

    public void mouseEntered(MouseEvent e) {
      // do nothing
      
    }

    public void mousePressed(MouseEvent e) {
      // do nothing
      
    }

    public void mouseReleased(MouseEvent e) {
      // do nothing
      
    }
  }

  private final String LINKS_VIEW = "Links";

  private final String MATRIX_VIEW = "Matrix";

  private final String PARALLEL_VIEW = "Parallel";

  /**
   * types of views we support
   */
  private final String[] VIEWS = new String[] {LINKS_VIEW, MATRIX_VIEW,
      PARALLEL_VIEW};

  private AlignmentTask createAlignmentTask() {
    JPanel mainPanel = new JPanel(new GridBagLayout());
    // two combo-boxes
    JTextField taskName = new JTextField("AlignmentTask"
            + ("" + Math.random() * 5000).substring(0, 4), 25);
    JTextField unitOfAlignment = new JTextField("Token", 25);
    JTextField parentOfUnitOfAlignment = new JTextField("Sentence", 25);

    List<String> alignmentFeatureNames = new ArrayList<String>();
    alignmentFeatureNames.addAll(document.getAllAlignmentFeatureNames());
    JComboBox dataSources = new JComboBox(alignmentFeatureNames.toArray());
    dataSources.setEditable(true);

    JComboBox storeAlignmentIn = new JComboBox(alignmentFeatureNames.toArray());
    storeAlignmentIn.setEditable(true);

    JComboBox viewsToSelectFrom = new JComboBox(VIEWS);
    viewsToSelectFrom.setEditable(false);

    final JTextField actionsFile = new JTextField("", 25);
    JButton actionsFileChooser = new JButton(MainFrame.getIcon("open-file"));

    // fourth row row
    GridBagConstraints constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 1;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Task Name:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 1;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(taskName, constraints);

    // fourth row row
    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 2;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Unit Annotation Type:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 2;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(unitOfAlignment, constraints);

    // fifth row
    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Parent of Unit of Alignment:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(parentOfUnitOfAlignment, constraints);

    // sixth row
    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 4;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Data Source:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 4;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(dataSources, constraints);

    // seventh row
    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 5;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Alignment Feature:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 5;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(storeAlignmentIn, constraints);

    // seventh row
    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 6;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Alignment View:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 6;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(viewsToSelectFrom, constraints);

    // seventh row
    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 7;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Actions Acitons File:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 7;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(actionsFile, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 7;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    mainPanel.add(actionsFileChooser, constraints);
    actionsFileChooser.setBorderPainted(false);
    actionsFileChooser.setContentAreaFilled(false);

    actionsFileChooser.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        // first we need to ask for a new empty directory
        JFileChooser fileChooser = MainFrame.getFileChooser();
        fileChooser.setDialogTitle("Please select actions configuration file");
        fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int result = fileChooser.showOpenDialog(editor);
        if(result == JFileChooser.APPROVE_OPTION) {
          try {
            actionsFile.setText(fileChooser.getSelectedFile().toURI().toURL()
                    .toExternalForm());
          }
          catch(Exception e) {
            actionsFile.setText("");
          }
        }

      }
    });

    int returnValue = JOptionPane.showOptionDialog(editor.getContentPane(),
            mainPanel, "New Alignment Task", JOptionPane.PLAIN_MESSAGE,
            JOptionPane.OK_CANCEL_OPTION, MainFrame.getIcon("annotation-diff"),
            new String[] {"OK", "Cancel"}, "OK");
    if(returnValue == JOptionPane.OK_OPTION) {
      AlignmentTask alignmentTask = new AlignmentTask(document);

      File actionsConfFile = null;
      if(actionsFile.getText().trim().length() > 0) {
        try {
          actionsConfFile = new File(new URL(actionsFile.getText().trim())
                  .toURI());
        }
        catch(MalformedURLException e) {
          e.printStackTrace();
          actionsConfFile = null;
        }
        catch(URISyntaxException e) {
          e.printStackTrace();
          actionsConfFile = null;
        }
        if(!actionsConfFile.exists()) actionsConfFile = null;
      }
      // MainFrame.lockGUI("Initializing alignment task : "+
      // taskName.getText()+"... ");
      try {
        alignmentTask.initialize(taskName.getText(), srcDoc.getName(), tgtDoc
                .getName(), (String)srcAnnotationSets.getSelectedItem(),
                (String)tgtAnnotationSets.getSelectedItem(), unitOfAlignment
                        .getText(), parentOfUnitOfAlignment.getText(),
                (String)dataSources.getSelectedItem(), (String)storeAlignmentIn
                        .getSelectedItem(), (String)viewsToSelectFrom
                        .getSelectedItem(), actionsConfFile == null
                        ? null
                        : actionsConfFile.getAbsolutePath());
      }
      finally {
        // MainFrame.unlockGUI();
      }
      return alignmentTask;
    }
    else {
      return null;
    }
  }

  /**
   * This method invokes a dialog that asks for source and target
   * documents
   * 
   * @return
   */
  private Document[] askForDocuments() {
    JPanel mainPanel = new JPanel(new GridBagLayout());
    // two combo-boxes
    JComboBox sourceDocs = new JComboBox(document.getDocumentIDs().toArray());
    JComboBox targetDocs = new JComboBox(document.getDocumentIDs().toArray());

    GridBagConstraints constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 0;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Source Document:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 0;
    constraints.gridwidth = 1;
    constraints.fill = GridBagConstraints.HORIZONTAL;
    constraints.insets = new Insets(0, 0, 0, 10);
    mainPanel.add(sourceDocs, constraints);

    // second row
    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 1;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.insets = new Insets(0, 0, 0, 5);
    mainPanel.add(new JLabel("Target Document:"), constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = 1;
    constraints.gridwidth = 1;
    constraints.fill = GridBagConstraints.HORIZONTAL;
    constraints.insets = new Insets(0, 0, 0, 10);
    mainPanel.add(targetDocs, constraints);

    int returnValue = JOptionPane.showOptionDialog(editor.getContentPane(),
            mainPanel, "Select Source and Target Documents",
            JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION, MainFrame
                    .getIcon("annotation-diff"), new String[] {"OK", "Cancel"},
            "OK");
    if(returnValue == JOptionPane.OK_OPTION) {
      Document srcDoc = document.getDocument((String)sourceDocs
              .getSelectedItem());
      Document tgtDoc = document.getDocument((String)targetDocs
              .getSelectedItem());
      return new Document[] {srcDoc, tgtDoc};
    }
    else {
      return null;
    }
  }

  class HighlightAlignedAnnotation extends AbstractAction {

    Annotation a;

    Alignment alignment;

    JTextArea container;

    public HighlightAlignedAnnotation(Annotation a, Alignment alignment,
            JTextArea container) {
      super(a.getType());
      this.a = a;
      this.alignment = alignment;
      this.container = container;
    }

    public void actionPerformed(ActionEvent e) {
      // lets delete all the highlights
      for(Object a : srcSelectedHighlights) {
        sourceEditor.getHighlighter().removeHighlight(a);
      }
      for(Object a : tgtSelectedHighlights) {
        targetEditor.getHighlighter().removeHighlight(a);
      }

      // find out the new ones to add
      Set<Annotation> tgtAlignedAnnots = alignment.getAlignedAnnotations(a);
      Set<Annotation> srcAlignedAnnots = new HashSet<Annotation>();

      for(Annotation ta : tgtAlignedAnnots) {
        srcAlignedAnnots.addAll(alignment.getAlignedAnnotations(ta));
      }

      if(container == targetEditor) {
        Set<Annotation> tmp = srcAlignedAnnots;
        srcAlignedAnnots = tgtAlignedAnnots;
        tgtAlignedAnnots = tmp;
      }

      srcAlignedAnnots.retainAll(srcAS);
      tgtAlignedAnnots.retainAll(tgtAS);

      for(Annotation a : srcAlignedAnnots) {
        srcSelectedHighlights.add(addHighlight(a, sourceEditor,
                SELECTED_HIGHLIGHT));
      }

      for(Annotation a : tgtAlignedAnnots) {
        tgtSelectedHighlights.add(addHighlight(a, targetEditor,
                SELECTED_HIGHLIGHT));
      }
    }
  }

  protected class MouseStoppedMovingAction extends AbstractAction {
    public void actionPerformed(ActionEvent evt) {
      if(textLocation == -1) return;
      if(srcAS == null) return;
      JPopupMenu popup = new JPopupMenu();

      // check for annotations at location
      JTextArea editorToUse = editorPane == sourceEditor
              ? sourceEditor
              : targetEditor;
      AnnotationSet setToUse = editorPane == sourceEditor ? srcAS : tgtAS;
      Document docToUse = editorPane == sourceEditor ? srcDoc : tgtDoc;

      HighlightAlignedAnnotation haa = null;
      for(Annotation a : setToUse.get(Math.max(0l, textLocation - 1), Math.min(
              docToUse.getContent().size() - 1, textLocation + 1))) {

        if(alignment.isAnnotationAligned(a)) {
          haa = new HighlightAlignedAnnotation(a, alignment, editorPane);
          popup.add(haa);
        }
      }

      Rectangle rect;
      try {
        if(popup.getComponentCount() > 1) {
          rect = editorToUse.modelToView(textLocation);
          popup.show(editorToUse, rect.x + 10, rect.y);
        }
        else if(popup.getComponentCount() == 1) {
          haa.actionPerformed(null);
        }
      }
      catch(BadLocationException e) {
        e.printStackTrace();
      }
    }

    public void setTextLocation(int textLocation) {
      this.textLocation = textLocation;
    }

    public void setEditorPane(JTextArea editorPane) {
      this.editorPane = editorPane;
    }

    int textLocation;

    JTextArea editorPane;
  }

  class AlignmentTaskView extends JPanel {
    JToolBar viewsToolbar;

    AlignmentView alignmentView;

    JPanel thisInstance = null;

    JButton closeTask = null;

    JButton hideTask = null;

    AlignmentTask alignmentTask;

    public AlignmentTaskView(AlignmentTask alignmentTask) {
      this.alignmentTask = alignmentTask;
      setLayout(new BorderLayout());
      setBorder(BorderFactory.createTitledBorder(alignmentTask.getName()));
      viewsToolbar = new JToolBar();
      thisInstance = this;

      if(alignmentTask.getAlignmentView().equals(LINKS_VIEW)) {
        alignmentView = new LinksView(alignmentTask);
      }
      else if(alignmentTask.getAlignmentView().equals(MATRIX_VIEW)) {
        alignmentView = new MatrixView(alignmentTask);
      }
      else if(alignmentTask.getAlignmentView().endsWith(PARALLEL_VIEW)) {
        alignmentView = new ParallelTextView(alignmentTask);
      }
      else {
        throw new GateRuntimeException("Invalid alignment view :"
                + alignmentTask.getAlignmentView());
      }

      thisInstance.add((JPanel)alignmentView, BorderLayout.CENTER);
      viewsToolbar.add(new JLabel("Selected View:"
              + alignmentTask.getAlignmentView()));
      viewsToolbar.addSeparator();

      viewsToolbar.add(new JLabel("Pair:"));
      viewsToolbar.add(new PreviousPairAction(this));
      NextPairAction npa = new NextPairAction(this);
      viewsToolbar.add(npa);

      viewsToolbar.add(new HideTaskAction(this));
      viewsToolbar.add(new CloseTaskAction(this));

      add(viewsToolbar, BorderLayout.NORTH);
      if(alignmentTask.getActionsFilePath() != null) {
        add(alignmentTask.getAlignmentActionsManager(), BorderLayout.EAST);
      }

      npa.actionPerformed(null);
    }

    public void updateView(PUAPair pair) {
      alignmentView.updateGUI(pair);
    }
  }

  /**
   * When the features are updated
   */
  public void featureMapUpdated() {
    String selectedFeature = null;
    if(this.alignmentFeatures != null) {
      selectedFeature = (String)this.alignmentFeatures.getSelectedItem();
    }

    List<String> alignmentFeatureNames = new ArrayList<String>(document
            .getAllAlignmentFeatureNames());
    final boolean reselect = selectedFeature != null ? alignmentFeatureNames
            .contains(selectedFeature) : false;
    DefaultComboBoxModel model = new DefaultComboBoxModel(alignmentFeatureNames
            .toArray());
    if(this.alignmentFeatures == null) {
      this.alignmentFeatures = new JComboBox(model);
    }
    else {
      this.alignmentFeatures.setModel(model);
    }

    final String featureToReselect = selectedFeature;
    // update ui
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        alignmentFeatures.invalidate();
        alignmentFeatures.updateUI();
        if(reselect) {
          alignmentFeatures.setSelectedItem(featureToReselect);
          refreshHighlightsAction();
        }
        else {
          // delete any highlights
          sourceEditor.getHighlighter().removeAllHighlights();
          targetEditor.getHighlighter().removeAllHighlights();
        }
      }
    });
  }

  /**
   * update the src or target annotation sets when there's any new
   * annotation set added
   */
  public void annotationSetAdded(DocumentEvent e) {
    Document doc = (Document)e.getSource();
    JComboBox comboBox = null;
    if(srcDoc == doc) {
      comboBox = this.srcAnnotationSets;
    }
    else if(tgtDoc == doc) {
      comboBox = this.tgtAnnotationSets;
    }
    else {
      return;
    }

    String selectedASName = null;
    if(comboBox != null) {
      selectedASName = (String)comboBox.getSelectedItem();
    }

    String addedASName = e.getAnnotationSetName();
    if(addedASName == null || addedASName.trim().length() == 0) {
      addedASName = DEFAULT_AS_NAME;
    }

    DefaultComboBoxModel model = (DefaultComboBoxModel)comboBox.getModel();
    model.addElement(addedASName);

    final String ASToReselect = selectedASName;
    final JComboBox finalComboBox = comboBox;

    // update ui
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        finalComboBox.invalidate();
        finalComboBox.updateUI();
        if(ASToReselect != null) {
          finalComboBox.setSelectedItem(ASToReselect);
          refreshHighlightsAction();
        }
        else {
          // delete any highlights
          sourceEditor.getHighlighter().removeAllHighlights();
          targetEditor.getHighlighter().removeAllHighlights();
        }
      }
    });
  }

  /**
   * update the src or target annotation set combobox when an annotation
   * set is deleted.
   */
  public void annotationSetRemoved(DocumentEvent e) {
    Document doc = (Document)e.getSource();
    JComboBox comboBox = null;
    if(srcDoc == doc) {
      comboBox = this.srcAnnotationSets;
    }
    else if(tgtDoc == doc) {
      comboBox = this.tgtAnnotationSets;
    }
    else {
      return;
    }

    String selectedASName = null;
    if(comboBox != null) {
      selectedASName = (String)comboBox.getSelectedItem();
    }

    String deletedASName = e.getAnnotationSetName();
    if(deletedASName == null || deletedASName.trim().length() == 0) {
      deletedASName = DEFAULT_AS_NAME;
    }

    DefaultComboBoxModel model = (DefaultComboBoxModel)comboBox.getModel();
    model.removeElement(deletedASName);

    final String ASToReselect = selectedASName.equals(deletedASName)
            ? null
            : deletedASName;
    final JComboBox finalComboBox = comboBox;

    // update ui
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        finalComboBox.invalidate();
        finalComboBox.updateUI();
        if(ASToReselect != null) {
          finalComboBox.setSelectedItem(ASToReselect);
          refreshHighlightsAction();
        }
        else {
          // delete any highlights
          sourceEditor.getHighlighter().removeAllHighlights();
          targetEditor.getHighlighter().removeAllHighlights();
        }
      }
    });
  }

  public void contentEdited(DocumentEvent e) {
    // we don't do anything here
  }
}