All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.stanford.nlp.trees.tregex.gui.DisplayMatchesPanel Maven / Gradle / Ivy

Go to download

Stanford CoreNLP provides a set of natural language analysis tools which can take raw English language text input and give the base forms of words, their parts of speech, whether they are names of companies, people, etc., normalize dates, times, and numeric quantities, mark up the structure of sentences in terms of phrases and word dependencies, and indicate which noun phrases refer to the same entities. It provides the foundational building blocks for higher level text understanding applications.

There is a newer version: 4.5.7
Show newest version
//Copyright (c) 2007-2008 The Board of Trustees of
//Tregex/Tsurgeon, DisplayMatchesPanel - a GUI for tree search and modification
//The Leland Stanford Junior University. All Rights Reserved.

//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.

//This code is a GUI interface to Tregex and Tsurgeon (which were
//written by Rogey Levy and Galen Andrew).

//For more information, bug reports, fixes, contact:
//Christopher Manning
//Dept of Computer Science, Gates 1A
//Stanford CA 94305-9010
//USA
//    Support/Questions: [email protected]
//    Licensing: [email protected]
//http://www-nlp.stanford.edu/software/tregex.shtml

package edu.stanford.nlp.trees.tregex.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.filechooser.FileNameExtensionFilter;

import edu.stanford.nlp.trees.EnglishGrammaticalStructure;
import edu.stanford.nlp.trees.GrammaticalStructureConversionUtils;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.UniversalEnglishGrammaticalStructure;
import edu.stanford.nlp.util.Pair;

/**
 * Class for creating the panel which shows a graphical version of the tree (as in TreeJPanel) as well
 * as the file name of the file from which the tree is from.
 *
 * @author Anna Rafferty
 */
@SuppressWarnings("serial")
public class DisplayMatchesPanel extends JPanel implements ListSelectionListener {

  private JScrollPane scroller;
  private MouseEvent firstMouseEvent = null;

  private String fontName = "";
  private int fontSize = 12;
  private Color defaultColor = Color.BLACK;
  private Color matchedColor = Color.RED;

  private static DisplayMatchesPanel instance = null;
  private ScrollableTreeJPanel tjp;

  private List matchedPartCoordinates;
  private int matchedPartCoordinateIdx = -1;

  public static synchronized DisplayMatchesPanel getInstance() {
    if (instance == null) {
      instance = new DisplayMatchesPanel();
    }
    return instance;
  }

  private DisplayMatchesPanel() {
    //data
    JPanel spaceholder = new JPanel();
    spaceholder.setBackground(Color.white);
    JTextArea message = new JTextArea("For non-English trees, first set up the tree reader and encoding in Preferences. Then load trees from the File menu.");
    message.setEditable(false);
    spaceholder.add(message);

    scroller = new JScrollPane(spaceholder);

    // Fix slow scrolling on OS X
    if (TregexGUI.isMacOSX()) {
      scroller.getVerticalScrollBar().setUnitIncrement(3);
      scroller.getHorizontalScrollBar().setUnitIncrement(3);
    }

    this.setFocusable(true);
    this.setTransferHandler(new DisplayTransferHandler());
    MatchesPanel.getInstance().addListener(this);

    //layout
    this.setLayout(new BorderLayout());
    this.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(),""));
    this.add(scroller, BorderLayout.CENTER);
  }


  private static class DisplayTransferHandler extends TransferHandler {

    public DisplayTransferHandler() {
      super();
    }

    protected static String exportString(JComponent c) {
      if (c instanceof ScrollableTreeJPanel) {
        ScrollableTreeJPanel tjp = (ScrollableTreeJPanel) c;
        return tjp.getTree().pennString();
      }
      return "";
    }

    @Override
    protected Transferable createTransferable(JComponent c) {
      return new StringSelection(exportString(c));
    }

    @Override
    public int getSourceActions(JComponent c) {
      return COPY_OR_MOVE;
    }

  } // end class DisplayTransferHandler


  /**
   * Used to set the single tree to be displayed in this panel (which should match
   * the tregex expression)
   * @param match tree that matches the expression
   */
  public void setMatch(TreeFromFile match, List matchedParts) {
    clearMatches();
    if(match != null)
      addMatch(match, matchedParts);
  }

  /**
   * Remove all trees from the display
   */
  public void clearMatches() {
    JPanel spaceholder = new JPanel();
    spaceholder.setBackground(Color.white);

    scroller.setViewportView(spaceholder);
    scroller.validate();
    scroller.repaint();

    matchedPartCoordinates = null;
    matchedPartCoordinateIdx = -1;
  }

  public class FilenameMouseInputAdapter extends MouseInputAdapter {
    JTextField textField;

    public FilenameMouseInputAdapter(JTextField textField) {
      this.textField = textField;
    }

    private boolean dragNDrop = false;
    @Override
    public void mousePressed(MouseEvent e) {
      if (MatchesPanel.getInstance().isEmpty()) return;
      if(firstMouseEvent == null) {
        firstMouseEvent = e;
      }
      e.consume();
      if(((e.getModifiersEx()) & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) {
        //shift is being held
        addHighlight(textField, firstMouseEvent, e);
      } else if(!HighlightUtils.isInHighlight(e, textField, textField.getHighlighter())) {
        textField.getHighlighter().removeAllHighlights();
        firstMouseEvent = e;
        dragNDrop = false;
        textField.repaint();
      } else {
        //in a highlight, if we drag after this, we'll be DnDing
        dragNDrop = true;
      }
    }

    private boolean addHighlight(JTextField label, MouseEvent mouseEvent1, MouseEvent mouseEvent2) {
      return HighlightUtils.addHighlight(label, mouseEvent1, mouseEvent2);
    }

    @Override
    public void mouseDragged(MouseEvent e) {
      if (MatchesPanel.getInstance().isEmpty()) return;

      if (firstMouseEvent != null) {
        e.consume();
        if(dragNDrop) {
          if(textField == null)
            return;
          if(Point2D.distanceSq(e.getX(), e.getY(), firstMouseEvent.getX(), firstMouseEvent.getY()) > 25) {
            //do DnD
            textField.getTransferHandler().exportAsDrag((JComponent) e.getSource(), firstMouseEvent, TransferHandler.COPY);
          }
        } else {
          addHighlight(textField, firstMouseEvent, e);
        }
      }
    }
  }


  /**
   * Adds the given tree to the display without removing already
   * displayed trees
   * @param match tree to be added
   */
  private void addMatch(TreeFromFile match, List matchedParts) {
    JPanel treeDisplay = new JPanel(new BorderLayout());
    JTextField filename = new JTextField("From file: " + match.getFilename());
    filename.setEditable(false);
    MouseInputAdapter listener = new FilenameMouseInputAdapter(filename);
    filename.addMouseListener(listener);
    filename.addMouseMotionListener(listener);
    treeDisplay.add(filename, BorderLayout.NORTH);
    if(TregexGUI.getInstance().isTdiffEnabled()) {
      tjp = getTreeJPanel(match.getDiffDecoratedTree(), matchedParts);
      tjp.setDiffConstituents(match.getDiffConstituents());
    } else {
      tjp = getTreeJPanel(match.getTree(), matchedParts);
    }

    matchedPartCoordinates = tjp.getMatchedPartCoordinates();
    matchedPartCoordinateIdx = -1;

    treeDisplay.add(tjp, BorderLayout.CENTER);

    filename.setOpaque(true);
    filename.setBackground(tjp.getBackground());
    filename.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));

    scroller.setViewportView(treeDisplay);

    this.revalidate();
    this.repaint();
  }

  void showPrevMatchedPart() {
    if (matchedPartCoordinates.size() == 0)
      return;
    else if (matchedPartCoordinateIdx <= 0)
      matchedPartCoordinateIdx = matchedPartCoordinates.size();

    matchedPartCoordinateIdx--;
    showMatchedPart(matchedPartCoordinateIdx);
  }

  void showNextMatchedPart() {
    if (matchedPartCoordinates.size() == 0)
      return;

    matchedPartCoordinateIdx =
      ++matchedPartCoordinateIdx % matchedPartCoordinates.size();
    showMatchedPart(matchedPartCoordinateIdx);
  }

  private void showMatchedPart(int idx) {
    Point2D.Double coord = matchedPartCoordinates.get(idx);
    Dimension treeSize = tjp.getPreferredSize();

    JScrollBar horizontal = scroller.getHorizontalScrollBar();
    JScrollBar vertical = scroller.getVerticalScrollBar();

    int horizontalLength = horizontal.getMaximum() - horizontal.getMinimum();
    double x = Math.max(0,
                        (coord.getX() / treeSize.getWidth() * horizontalLength
                         - (scroller.getWidth() / 2.0)));

    int verticalLength = vertical.getMaximum() - vertical.getMinimum();
    double y = Math.max(0,
                        (coord.getY() / treeSize.getHeight() * verticalLength
                         - (scroller.getHeight() / 2.0)));

    horizontal.setValue((int) x);
    vertical.setValue((int) y);
  }

  private void doExportTree() {
    JFileChooser chooser = new JFileChooser();
    chooser.setSelectedFile(new File("./tree.png"));
    FileNameExtensionFilter filter = new FileNameExtensionFilter("PNG images", "png");
    chooser.setFileFilter(filter);

    int status = chooser.showSaveDialog(this);

    if (status != JFileChooser.APPROVE_OPTION)
      return;

    Dimension size = tjp.getPreferredSize();
    BufferedImage im = new BufferedImage((int) size.getWidth(),
                                         (int) size.getHeight(),
                                         BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = im.createGraphics();
    tjp.paint(g);

    try {
      ImageIO.write(im, "png", chooser.getSelectedFile());
    } catch (IOException e) {
      JOptionPane.showMessageDialog(this, "Failed to save the tree image file.\n"
                                    + e.getLocalizedMessage(), "Export Error",
                                    JOptionPane.ERROR_MESSAGE);
    }
  }

  // BEGIN - sebschu
  private void showDependencies() {
    EnglishGrammaticalStructure gs = new EnglishGrammaticalStructure(tjp.getTree());
    JOptionPane.showMessageDialog(this, GrammaticalStructureConversionUtils.dependenciesToString(gs, gs.typedDependencies(false), tjp.getTree(), false, false, false), "Dependencies", JOptionPane.INFORMATION_MESSAGE, null);
    
  }
  
  private void showUniversalDependencies() {
    UniversalEnglishGrammaticalStructure gs = new UniversalEnglishGrammaticalStructure(tjp.getTree());
    JOptionPane.showMessageDialog(this, GrammaticalStructureConversionUtils.dependenciesToString(gs, gs.typedDependencies(false), tjp.getTree(), false, false, false), "Universal dependencies", JOptionPane.INFORMATION_MESSAGE, null);
    
  }
  
  // END - sebschu
  

  private ScrollableTreeJPanel getTreeJPanel(Tree t, List matchedParts) {
    final ScrollableTreeJPanel treeJP = new ScrollableTreeJPanel(SwingConstants.CENTER,SwingConstants.TOP);
    treeJP.setFontName(fontName);
    treeJP.setFontSize(fontSize);
    treeJP.setDefaultColor(defaultColor);
    treeJP.setMatchedColor(matchedColor);
    treeJP.setTree(t);
    treeJP.setMatchedParts(matchedParts);
    treeJP.setBackground(Color.WHITE);
    treeJP.setFocusable(true);

    final JPopupMenu treePopup = new JPopupMenu();

    JMenuItem copy = new JMenuItem("Copy");
    copy.setActionCommand((String) TransferHandler.getCopyAction()
                          .getValue(Action.NAME));
    copy.addActionListener(new TregexGUI.TransferActionListener());
    int mask = TregexGUI.isMacOSX() ? InputEvent.META_MASK : InputEvent.CTRL_MASK;
    copy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, mask));
    treePopup.add(copy);

    JMenuItem exportTree = new JMenuItem("Export tree as image");
    exportTree.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          doExportTree();
        }
      });
    treePopup.add(exportTree);

    //BEGIN - sebschu
    
    JMenuItem showDependencies = new JMenuItem("Show dependencies");
    showDependencies.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        showDependencies();
      }
    });
    
    
    treePopup.add(showDependencies);
    
    
    JMenuItem showUniversalDependencies = new JMenuItem("Show universal dependencies");
    showUniversalDependencies.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        showUniversalDependencies();
      }
    });
    
    
    treePopup.add(showUniversalDependencies);
    
    //END - sebschu
    
    treeJP.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
          treeJP.requestFocusInWindow();
        }

        private void maybeShowPopup(MouseEvent e) {
          if (e.isPopupTrigger())
            treePopup.show(e.getComponent(), e.getX(), e.getY());
        }

        @Override
        public void mousePressed(MouseEvent e) {
          maybeShowPopup(e);
        }

        @Override
        public void mouseReleased(MouseEvent e) {
          maybeShowPopup(e);
        }
      });

    DisplayMouseMotionAdapter d = new DisplayMouseMotionAdapter();
    treeJP.addMouseMotionListener(d);
    treeJP.addMouseListener(d);
    treeJP.setTransferHandler(new DisplayTransferHandler());
    InputMap imap = treeJP.getInputMap();
    imap.put(KeyStroke.getKeyStroke("ctrl C"),
        TransferHandler.getCopyAction().getValue(Action.NAME));
    ActionMap map = treeJP.getActionMap();
    map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
        TransferHandler.getCopyAction());
    return treeJP;
  }

  
  private static class DisplayMouseMotionAdapter extends MouseInputAdapter {
    /*
     * Motion listener is based off the Java sun tutorial for DnD transfer
     */
    MouseEvent firstMouseEvent1 = null;

    @Override
    public void mousePressed(MouseEvent e) {
      if (MatchesPanel.getInstance().isEmpty()) return;
      firstMouseEvent1 = e;
      e.consume();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
      if (MatchesPanel.getInstance().isEmpty()) return;

      if (firstMouseEvent1 != null) {
        e.consume();

        int dx = Math.abs(e.getX() - firstMouseEvent1.getX());
        int dy = Math.abs(e.getY() - firstMouseEvent1.getY());
        //Arbitrarily define a 5-pixel shift as the
        //official beginning of a drag.
        if (dx > 5 || dy > 5) {
          //This is a drag, not a click.
          JComponent c = (JComponent)e.getSource();
          //Tell the transfer handler to initiate the drag.
          TransferHandler handler = c.getTransferHandler();
          handler.exportAsDrag(c, firstMouseEvent1, TransferHandler.COPY);
          firstMouseEvent1 = null;
        }
      }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
      firstMouseEvent1 = null;
    }

  } // end class DisplayMouseMotionAdapter


  public void setFontName(String fontName) {
    this.fontName = fontName;
  }

  public void setFontSize(int fontSize) {
    this.fontSize = fontSize;
  }

  public void setFontSizeRepaint(int fontSize) {
    this.fontSize = fontSize;
    if (tjp != null) {
      tjp.setFontSize(fontSize);
      // cdm 2009: it seems like you need to call revalidate and repaint on precisely these components or it doesn't work ... tricky stuff.
      tjp.revalidate();
      scroller.repaint();
    }
  }

  public void setDefaultColor(Color defaultColor) {
    this.defaultColor = defaultColor;
  }

  public void setMatchedColor(Color matchedColor) {
    this.matchedColor = matchedColor;
  }

  public void valueChanged(ListSelectionEvent e) {
    Pair> newMatch = MatchesPanel.getInstance().getSelectedMatch();
    if(newMatch == null)
      clearMatches();
    else
      setMatch(newMatch.first(), newMatch.second());
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy