import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
 * An example of an encapsulation of a text editor
 * that is not cohesive.
 * 
 *  @author Prof. David Bernstein, James Madison University
 */
public class TextEditorApplication 
  implements ActionListener, CaretListener, MouseListener
{
  private Caret              caret;
  private JButton            copyButton, cutButton, pasteButton;
  private JFrame             window;
  private JMenu              menu;
  private JMenuBar           menuBar;
  private JMenuItem          copyItem, cutItem, pasteItem;
  private JMenuItem          copyPopup, cutPopup, pastePopup;
  private JPopupMenu         popup;    
  private JTextArea          ta;
  private JToolBar           toolBar;
  private String             clipboard;
  
  /**
   * The entry point of the application.
   * 
   * @param args  The command line arguments (which are ignored)
   */
  public static void main(String[] args)
  {
    new TextEditorApplication();
  }
  
  /**
   * Default Constructor.
   */
  public TextEditorApplication()
  {
    window = new JFrame();
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    clipboard = null;

    ta = new JTextArea();
    caret = ta.getCaret();
    ta.setLineWrap(true);
    ta.setWrapStyleWord(true);
    ta.addMouseListener(this);
    ta.addCaretListener(this);

    constructControls();
    performLayout();

    disableControls();
    window.setVisible(true);
  }
  
  /**
   * Handle actionPerformed messages.
   * 
   * @param ae  The event that generated the message
   */
  public void actionPerformed(ActionEvent ae)
  {
    String ac = ae.getActionCommand();
    
    if      (ac.equals("Copy"))  copy();
    else if (ac.equals("Cut"))   cut();
    else if (ac.equals("Paste")) paste();
  }
  
  /**
   * Handle caretUpdate messages.
   *
   * @param evt   The event that generated the message
   */
  public void caretUpdate(CaretEvent evt)
  {
    disableControls();
    
    if (clipboard != null)
    {
      pasteButton.setEnabled(true);
      pasteItem.setEnabled(true);
      pastePopup.setEnabled(true);
    }
    
    if (ta.getSelectedText() != null)
    {
      cutButton.setEnabled(true);
      cutItem.setEnabled(true);
      cutPopup.setEnabled(true);

      copyButton.setEnabled(true);
      copyItem.setEnabled(true);
      copyPopup.setEnabled(true);
    }
  }
  
  /**
   * Construct the JPopupMenu, JMenu, and JToolBar.
   */
  private void constructControls()
  {
      popup   = new JPopupMenu();
      menu    = new JMenu("Edit");
      toolBar = new JToolBar("Edit");

      ImageIcon icon;
      
      // Copy controls
      icon = new ImageIcon("copy.gif");
      copyItem = constructJMenuItem("Copy", icon);
      menu.add(copyItem);

      copyPopup = constructJMenuItem("Copy", icon);
      popup.add(copyPopup);
  
      copyButton = constructJButton("Copy", icon);
      toolBar.add(copyButton);
      
      // Cut controls
      icon = new ImageIcon("cut.gif");
      cutItem = constructJMenuItem("Cut", icon);
      menu.add(cutItem);

      cutPopup = constructJMenuItem("Cut", icon);
      popup.add(cutPopup);
  
      cutButton = constructJButton("Cut", icon);
      toolBar.add(cutButton);
      
      // Paste controls
      icon = new ImageIcon("paste.gif");
      pasteItem = constructJMenuItem("Paste", icon);
      menu.add(pasteItem);

      pastePopup = constructJMenuItem("Paste", icon);
      popup.add(pastePopup);
  
      pasteButton = constructJButton("Paste", icon);
      toolBar.add(pasteButton);
      
      
      menuBar = new JMenuBar();
      menuBar.add(menu);
  }

  /**
   * Construct a JButton.
   * 
   * @param actionCommand  The action command to use
   * @param icon           The Icon to use
   * @return               The JButton
   */
  private JButton constructJButton(String actionCommand, Icon icon)
  {
    JButton button = new JButton(actionCommand, icon);
    button.addActionListener(this);
    return button;
  }

  /**
   * Construct a JMenuItem.
   * 
   * @param actionCommand  The action command to use
   * @param icon           The Icon to use
   * @return               The JButton
   */
  private JMenuItem constructJMenuItem(String actionCommand, Icon icon)
  {
    JMenuItem item = new JMenuItem(actionCommand, icon);
    item.addActionListener(this);
    return item;
  }    

  /**
   * Copy text to the "clipboard".
   */
  private void copy()
  {
      int  dot, mark;
      
      dot  = caret.getDot();        
      mark = caret.getMark();
      clipboard = ta.getSelectedText();
      ta.select(-1, -1);
      caret.setDot(mark); // Causes a CaretEvent to be fired
      caret.setDot(dot);  // Positions the Caret at the right position
      ta.requestFocus();
  }

  /**
   * Cut text to the "clipboard".
   */
  private void cut()
  {
      int dot;        

      dot = caret.getDot();        
      clipboard = ta.getSelectedText();
      ta.replaceSelection(null);
      ta.select(-1, -1);
      caret.setDot(dot);
      ta.requestFocus();
  }
  
  /**
   * Disable all of the controls.
   */
  private void disableControls()
  {
    copyButton.setEnabled(false);
    copyItem.setEnabled(false);
    copyPopup.setEnabled(false);

    cutButton.setEnabled(false);
    cutItem.setEnabled(false);
    cutPopup.setEnabled(false);

    pasteButton.setEnabled(false);
    pasteItem.setEnabled(false);
    pastePopup.setEnabled(false);
  }
  
  /**
   * Handle mouseClicked messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseClicked(MouseEvent evt)
  {
  }

  /**
   * Handle mouseEntered messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseEntered(MouseEvent evt)
  {
  }

  /**
   * Handle mouseExited messages.
   *
   * @param evt   The event that generated the message
   */
  @Override
  public void mouseExited(MouseEvent evt)
  {
  }

  /**
   * Handle mousePressed messages.
   *
   * @param evt   The event that generated the message
   */
  @Override
  public void mousePressed(MouseEvent evt)
  {
    if (evt.isPopupTrigger() && !popup.isVisible()) 
    {
        Point    p;

        p = evt.getPoint();           
        popup.show(window, p.x, p.y);
    }
  }

  /**
   * Handle mouseReleased messages.
   *
   * @param evt   The event that generated the message
   */
  @Override
  public void mouseReleased(MouseEvent evt)
  {
     if (evt.isPopupTrigger() && !popup.isVisible()) 
     {
         Point    p;

         p = evt.getPoint();           
         popup.show(window, p.x, p.y);
     }
  }

  /**
   * Paste text from the "clipboard".
   */
  private void paste()
  {
      int dot, offset;        

      dot = caret.getDot();
      offset = clipboard.length();
      
      ta.replaceSelection(clipboard);
      clipboard = null;

      caret.setDot(dot + offset);
      ta.requestFocus();
  }
  
  /**
   * Layout this component.
   */
  private void performLayout()
  {
    JPanel cp = (JPanel)window.getContentPane();
    cp.setLayout(new BorderLayout());
    window.setSize(400,400);

    JScrollPane scrollPane = new JScrollPane(ta);
    scrollPane.setHorizontalScrollBarPolicy(
              JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS
              );
    scrollPane.setVerticalScrollBarPolicy(
              JScrollPane.VERTICAL_SCROLLBAR_ALWAYS
              );
    cp.add(scrollPane, BorderLayout.CENTER);
    
    window.setJMenuBar(menuBar);

    JPanel toolbars = new JPanel(new FlowLayout(FlowLayout.LEFT));
    toolbars.add(toolBar);
    cp.add(toolbars, BorderLayout.NORTH);        
  }
}
