
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 swixml Show documentation
Show all versions of swixml Show documentation
GUI generating engine for Java applications
/*
* 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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy