org.jdesktop.application.TextActions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tink-app Show documentation
Show all versions of tink-app Show documentation
Template based HTML formatter tool for small web sites.
The newest version!
/*
* Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.application;
import java.awt.AWTEvent;
import java.awt.EventQueue;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.FlavorEvent;
import java.awt.datatransfer.FlavorListener;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.ActionMap;
import javax.swing.JComponent;
import javax.swing.TransferHandler;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.Caret;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent;
/**
* An ActionMap class that defines cut/copy/paste/delete.
*
* This class only exists to paper over limitations in the standard JTextComponent
* cut/copy/paste/delete javax.swing.Actions. The standard cut/copy Actions don't
* keep their enabled property in sync with having the focus and (for copy) having
* a non-empty text selection. The standard paste Action's enabled property doesn't
* stay in sync with the current contents of the clipboard. The paste/copy/delete
* actions must also track the JTextComponent editable property.
*
* The new cut/copy/paste/delete are installed lazily, when a JTextComponent gets
* the focus, and before any other focus-change related work is done. See
* updateFocusOwner().
*
* @author Hans Muller ([email protected])
* @author Scott Violet ([email protected])
*/
class TextActions extends AbstractBean {
private final ApplicationContext context;
private final CaretListener textComponentCaretListener;
private final PropertyChangeListener textComponentPCL;
private final String markerActionKey = "TextActions.markerAction";
private final javax.swing.Action markerAction;
private boolean copyEnabled = false; // see setCopyEnabled
private boolean cutEnabled = false; // see setCutEnabled
private boolean pasteEnabled = false; // see setPasteEnabled
private boolean deleteEnabled = false; // see setDeleteEnabled
public TextActions(ApplicationContext context) {
this.context = context;
markerAction = new javax.swing.AbstractAction() {
public void actionPerformed(ActionEvent e) { }
};
textComponentCaretListener = new TextComponentCaretListener();
textComponentPCL = new TextComponentPCL();
getClipboard().addFlavorListener(new ClipboardListener());
}
private ApplicationContext getContext() {
return context;
}
private JComponent getFocusOwner() {
return getContext().getFocusOwner();
}
private Clipboard getClipboard() {
return getContext().getClipboard();
}
/* Called by the KeyboardFocus PropertyChangeListener in ApplicationContext,
* before any other focus-change related work is done.
*/
void updateFocusOwner(JComponent oldOwner, JComponent newOwner) {
if (oldOwner instanceof JTextComponent) {
JTextComponent text = (JTextComponent)oldOwner;
text.removeCaretListener(textComponentCaretListener);
text.removePropertyChangeListener(textComponentPCL);
}
if (newOwner instanceof JTextComponent) {
JTextComponent text = (JTextComponent)newOwner;
maybeInstallTextActions(text);
updateTextActions(text);
text.addCaretListener(textComponentCaretListener);
text.addPropertyChangeListener(textComponentPCL);
}
else if (newOwner == null) {
setCopyEnabled(false);
setCutEnabled(false);
setPasteEnabled(false);
setDeleteEnabled(false);
}
}
private final class ClipboardListener implements FlavorListener {
public void flavorsChanged(FlavorEvent e) {
JComponent c = getFocusOwner();
if (c instanceof JTextComponent) {
updateTextActions((JTextComponent)c);
}
}
}
private final class TextComponentCaretListener implements CaretListener {
public void caretUpdate(CaretEvent e) {
updateTextActions((JTextComponent)(e.getSource()));
}
}
private final class TextComponentPCL implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if ((propertyName == null) || "editable".equals(propertyName)) {
updateTextActions((JTextComponent)(e.getSource()));
}
}
}
private void updateTextActions(JTextComponent text) {
Caret caret = text.getCaret();
boolean selection = (caret.getDot() != caret.getMark());
boolean editable = text.isEditable();
boolean data = getClipboard().isDataFlavorAvailable(DataFlavor.stringFlavor);
setCopyEnabled(selection);
setCutEnabled(editable && selection);
setDeleteEnabled(editable && selection);
setPasteEnabled(editable && data);
}
// TBD: what if text.getActionMap is null, or if it's parent isn't the UI-installed actionMap
private void maybeInstallTextActions(JTextComponent text) {
ActionMap actionMap = text.getActionMap();
if (actionMap.get(markerActionKey) == null) {
actionMap.put(markerActionKey, markerAction);
ActionMap textActions = getContext().getActionMap(getClass(), this);
for(Object key : textActions.keys()) {
actionMap.put(key, textActions.get(key));
}
}
}
/* This method lifted from JTextComponent.java
*/
private int getCurrentEventModifiers() {
int modifiers = 0;
AWTEvent currentEvent = EventQueue.getCurrentEvent();
if (currentEvent instanceof InputEvent) {
modifiers = ((InputEvent)currentEvent).getModifiers();
}
else if (currentEvent instanceof ActionEvent) {
modifiers = ((ActionEvent)currentEvent).getModifiers();
}
return modifiers;
}
private void invokeTextAction(JTextComponent text, String actionName) {
ActionMap actionMap = text.getActionMap().getParent();
long eventTime = EventQueue.getMostRecentEventTime();
int eventMods = getCurrentEventModifiers();
ActionEvent actionEvent =
new ActionEvent(text, ActionEvent.ACTION_PERFORMED, actionName, eventTime, eventMods);
actionMap.get(actionName).actionPerformed(actionEvent);
}
@Action(enabledProperty = "cutEnabled")
public void cut(ActionEvent e) {
Object src = e.getSource();
if (src instanceof JTextComponent) {
invokeTextAction((JTextComponent)src, "cut");
}
}
public boolean isCutEnabled() { return cutEnabled; }
public void setCutEnabled(boolean cutEnabled) {
boolean oldValue = this.cutEnabled;
this.cutEnabled = cutEnabled;
firePropertyChange("cutEnabled", oldValue, this.cutEnabled);
}
@Action(enabledProperty = "copyEnabled")
public void copy(ActionEvent e) {
Object src = e.getSource();
if (src instanceof JTextComponent) {
invokeTextAction((JTextComponent)src, "copy");
}
}
public boolean isCopyEnabled() { return copyEnabled; }
public void setCopyEnabled(boolean copyEnabled) {
boolean oldValue = this.copyEnabled;
this.copyEnabled = copyEnabled;
firePropertyChange("copyEnabled", oldValue, this.copyEnabled);
}
@Action(enabledProperty = "pasteEnabled")
public void paste(ActionEvent e) {
Object src = e.getSource();
if (src instanceof JTextComponent) {
invokeTextAction((JTextComponent)src, "paste");
}
}
public boolean isPasteEnabled() { return pasteEnabled; }
public void setPasteEnabled(boolean pasteEnabled) {
boolean oldValue = this.pasteEnabled;
this.pasteEnabled = pasteEnabled;
firePropertyChange("pasteEnabled", oldValue, this.pasteEnabled);
}
@Action(enabledProperty = "deleteEnabled")
public void delete(ActionEvent e) {
Object src = e.getSource();
if (src instanceof JTextComponent) {
/* The deleteNextCharAction is bound to the delete key in
* text components. The name appears to be a misnomer,
* however it's really a compromise. Calling the method
* by a more accurate name,
* "IfASelectionExistsThenDeleteItOtherwiseDeleteTheNextCharacter"
* would be rather unwieldy.
*/
invokeTextAction((JTextComponent)src, DefaultEditorKit.deleteNextCharAction);
}
}
public boolean isDeleteEnabled() { return deleteEnabled; }
public void setDeleteEnabled(boolean deleteEnabled) {
boolean oldValue = this.deleteEnabled;
this.deleteEnabled = deleteEnabled;
firePropertyChange("deleteEnabled", oldValue, this.deleteEnabled);
}
}