JTextPane text;
text.setText("somewords <img src=\"file:///C:/filepath/fire.png\" text=\"[fire1]\" title=\"[fire2]\" alt=\"[fire3]\" style=\"width:11px;height:11px;\"> otherwords");
Gives me this , which is as expected. But when I highlight it and copy paste it, I get "somewords otherwords". The same thing done inside Firefox when copied would paste "somewords [fire3] otherwords" (it substitutes alt text for image). Is there any way to replicate this behavior where the alt text is copied, or any other indication that a picture was copied? I'm guessing it is not a built in feature, so what I probably need to know is what should be overloaded to mimic this behavior.
Its for an output/chat window so its important that when the users quote it it includes the images (like emotes would)
Update: Successfully overrode the copyAction method... now what?
// (should) allow copying of alt text in place of images
class CustomEditorKit extends HTMLEditorKit {
Action[] modifiedactions;
CustomEditorKit() {
int whereat=-1;
modifiedactions=super.getActions();
for(int k=0;k<super.getActions().length;k++) {
if(super.getActions()[k] instanceof CopyAction) //find where they keep the copyaction
{
whereat=k;
modifiedactions[whereat]=new CustomCopyAction(); //and replace it with a different one
}
}
}
#Override
public Action[] getActions() {
return modifiedactions; //returns the modified version instead of defaultActions
}
public static class CustomCopyAction extends TextAction {
public CustomCopyAction() {
super(copyAction);
}
#Override
public void actionPerformed(ActionEvent e) { //need to change this to substitute images with text, preferably their alt text.
JTextComponent target = getTextComponent(e);
//target.getText() gives full body of html, unbounded by selection area
if (target != null) {
target.copy(); //a confusing and seemingly never ending labyrinth of classes and methods
}
}
}
}
The only way I can think of accomplishing this is by writing your own TransferHandler, and overriding the getSourceActions and exportToClipboard methods.
You can convert the HTML to plain text yourself, rather than letting Swing use the getSelectedText method of JTextPane, by recursively converting each Element of the HTML Document, customizing the conversion in the case where the Element has a NameAttribute of IMG and also has an ALT attribute.
Here's what I came up with:
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.awt.EventQueue;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.TransferHandler;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.BadLocationException;
import javax.swing.text.html.HTML;
public class HTMLCopier
extends TransferHandler {
private static final long serialVersionUID = 1;
private final Collection<DataFlavor> flavors;
HTMLCopier() {
Collection<DataFlavor> flavorList = new LinkedHashSet<>();
Collections.addAll(flavorList,
new DataFlavor(String.class, null),
DataFlavor.stringFlavor);
String[] mimeTypes = {
"text/html", "text/plain"
};
Class<?>[] textClasses = {
Reader.class, String.class, CharBuffer.class, char[].class
};
Class<?>[] byteClasses = {
InputStream.class, ByteBuffer.class, byte[].class
};
String[] charsets = {
Charset.defaultCharset().name(),
StandardCharsets.UTF_8.name(),
StandardCharsets.UTF_16.name(),
StandardCharsets.UTF_16BE.name(),
StandardCharsets.UTF_16LE.name(),
StandardCharsets.ISO_8859_1.name(),
"windows-1252",
StandardCharsets.US_ASCII.name(),
};
try {
flavorList.add(new DataFlavor(
DataFlavor.javaJVMLocalObjectMimeType +
"; class=" + String.class.getName()));
for (String mimeType : mimeTypes) {
for (Class<?> textClass : textClasses) {
flavorList.add(new DataFlavor(String.format(
"%s; class=\"%s\"",
mimeType, textClass.getName())));
}
for (String charset : charsets) {
for (Class<?> byteClass : byteClasses) {
flavorList.add(new DataFlavor(String.format(
"%s; charset=%s; class=\"%s\"",
mimeType, charset, byteClass.getName())));
}
}
}
for (String mimeType : mimeTypes) {
flavorList.add(new DataFlavor(String.format(
"%s; charset=unicode; class=\"%s\"",
mimeType, InputStream.class.getName())));
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
this.flavors = Collections.unmodifiableCollection(flavorList);
}
#Override
public int getSourceActions(JComponent component) {
return COPY_OR_MOVE;
}
#Override
public void exportToClipboard(JComponent component,
Clipboard clipboard,
int action) {
JTextPane pane = (JTextPane) component;
Document doc = pane.getDocument();
int start = pane.getSelectionStart();
int end = pane.getSelectionEnd();
final String html;
final String plainText;
try {
StringWriter writer = new StringWriter(end - start);
pane.getEditorKit().write(writer, doc, start, end - start);
html = writer.toString();
StringBuilder plainTextBuilder = new StringBuilder();
appendTextContent(doc.getDefaultRootElement(), start, end,
plainTextBuilder);
plainText = plainTextBuilder.toString();
} catch (BadLocationException | IOException e) {
throw new RuntimeException(e);
}
Transferable contents = new Transferable() {
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavors.contains(flavor);
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return flavors.toArray(new DataFlavor[0]);
}
#Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException,
IOException {
String data;
if (flavor.isMimeTypeEqual("text/html")) {
data = html;
} else {
data = plainText;
}
Class<?> dataClass = flavor.getRepresentationClass();
if (dataClass.equals(char[].class)) {
return data.toCharArray();
}
if (flavor.isRepresentationClassReader()) {
return new StringReader(data);
}
if (flavor.isRepresentationClassCharBuffer()) {
return CharBuffer.wrap(data);
}
if (flavor.isRepresentationClassByteBuffer()) {
String charset = flavor.getParameter("charset");
return Charset.forName(charset).encode(data);
}
if (flavor.isRepresentationClassInputStream()) {
String charset = flavor.getParameter("charset");
return new ByteArrayInputStream(
data.getBytes(charset));
}
if (dataClass.equals(byte[].class)) {
String charset = flavor.getParameter("charset");
return data.getBytes(charset);
}
return data;
}
};
clipboard.setContents(contents, null);
if (action == MOVE) {
pane.replaceSelection("");
}
}
private void appendTextContent(Element element,
int textStart,
int textEnd,
StringBuilder content)
throws BadLocationException {
int start = element.getStartOffset();
int end = element.getEndOffset();
if (end < textStart || start >= textEnd) {
return;
}
start = Math.max(start, textStart);
end = Math.min(end, textEnd);
AttributeSet attr = element.getAttributes();
Object tag = attr.getAttribute(AttributeSet.NameAttribute);
if (tag.equals(HTML.Tag.HEAD) ||
tag.equals(HTML.Tag.TITLE) ||
tag.equals(HTML.Tag.COMMENT) ||
tag.equals(HTML.Tag.SCRIPT)) {
return;
}
if (tag.equals(HTML.Tag.INPUT) ||
tag.equals(HTML.Tag.TEXTAREA) ||
tag.equals(HTML.Tag.SELECT)) {
// Swing doesn't provide a way to read input values
// dynamically (as far as I know; I could be wrong).
return;
}
if (tag.equals(HTML.Tag.IMG)) {
Object altText = attr.getAttribute(HTML.Attribute.ALT);
if (altText != null) {
content.append(altText);
}
return;
}
if (tag.equals(HTML.Tag.CONTENT)) {
content.append(
element.getDocument().getText(start, end - start));
return;
}
int count = element.getElementCount();
for (int i = 0; i < count; i++) {
appendTextContent(element.getElement(i), textStart, textEnd,
content);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JTextPane text = new JTextPane();
text.setContentType("text/html");
text.setEditable(false);
text.setText("somewords <img src=\"file:///C:/filepath/fire.png\" text=\"[fire1]\" title=\"[fire2]\" alt=\"[fire3]\" style=\"width:11px;height:11px;\"> otherwords");
text.setTransferHandler(new HTMLCopier());
JFrame window = new JFrame("HTML Copier");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new JScrollPane(text));
window.pack();
window.setLocationByPlatform(true);
window.setVisible(true);
text.selectAll();
text.copy();
}
});
}
}
Edit: Updated code to properly place only highlighted text on clipboard.
JTextPane provides method setEditorKit(EditorKit). I think you'll find your solution by providing a custom EditorKit.
You can override the copy and cut actions in a DefaultEditorKit, then pass it to JTextPane.
http://docs.oracle.com/javase/7/docs/api/javax/swing/text/DefaultEditorKit.html#copyAction
Or Java 8 introduces HTMLEditorKit that, if compatible with JTextPane, may provide the behavior you want.
https://docs.oracle.com/javase/8/docs/api/javax/swing/text/html/HTMLEditorKit.html
Related
My question is why same class work fine quickly without any late with jdk 9, but is take some time with jdk 8.
Using jdk 9 and higher will solve my problem for autocomplete search to run smoothly, but then I will face bigger problem is my reports jrxml files will not work with jdk higher than 8.
Any suggestion to make this code work smoothly with jdk 8?
Here is same code for both jdk 9 and 8.
thanks in advance
i upload video to YouTube
here is a link
https://youtu.be/huBr-f-dyCE
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import javax.swing.ComboBoxEditor;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
public class ComboAutoComplete1 extends PlainDocument {
JComboBox comboBox;
ComboBoxModel model;
JTextComponent editor;
// flag to indicate if setSelectedItem has been called
// subsequent calls to remove/insertString should be ignored
boolean selecting=false;
boolean hidePopupOnFocusLoss;
boolean hitBackspace=false;
boolean hitBackspaceOnSelection;
KeyListener editorKeyListener;
FocusListener editorFocusListener;
public ComboAutoComplete1(final JComboBox comboBox) {
this.comboBox = comboBox;
model = comboBox.getModel();
comboBox.addActionListener((ActionEvent e) -> {
if (!selecting) highlightCompletedText(0);
});
comboBox.addPropertyChangeListener((PropertyChangeEvent e) -> {
if (e.getPropertyName().equals("editor")) configureEditor((ComboBoxEditor) e.getNewValue());
if (e.getPropertyName().equals("model")) model = (ComboBoxModel) e.getNewValue();
});
editorKeyListener = new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (comboBox.isDisplayable()) comboBox.setPopupVisible(true);
hitBackspace=false;
switch (e.getKeyCode()) {
// determine if the pressed key is backspace (needed by the remove method)
case KeyEvent.VK_BACK_SPACE : hitBackspace=true;
hitBackspaceOnSelection=editor.getSelectionStart()!=editor.getSelectionEnd();
break;
// ignore delete key
case KeyEvent.VK_DELETE : e.consume();
comboBox.getToolkit().beep();
break;
}
}
};
comboBox.getEditor().getEditorComponent().addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent evt){
}
});
// Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when tabbing out
hidePopupOnFocusLoss=System.getProperty("java.version").startsWith("1.5");
// Highlight whole text when gaining focus
editorFocusListener = new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
highlightCompletedText(0);
}
#Override
public void focusLost(FocusEvent e) {
// Workaround for Bug 5100422 - Hide Popup on focus loss
if (hidePopupOnFocusLoss) comboBox.setPopupVisible(false);
}
};
configureEditor(comboBox.getEditor());
// Handle initially selected object
Object selected = comboBox.getSelectedItem();
if (selected!=null) setText(selected.toString());
highlightCompletedText(0);
}
public static void enable(JComboBox comboBox, String str) {
// has to be editable
comboBox.setEditable(true);
// change the editor's document
new ComboAutoComplete1(comboBox);
}
void configureEditor(ComboBoxEditor newEditor) {
if (editor != null) {
editor.removeKeyListener(editorKeyListener);
editor.removeFocusListener(editorFocusListener);
}
if (newEditor != null) {
editor = (JTextComponent) newEditor.getEditorComponent();
editor.addKeyListener(editorKeyListener);
editor.addFocusListener(editorFocusListener);
editor.setDocument(this);
}
}
#Override
public void remove(int offs, int len) throws BadLocationException {
// return immediately when selecting an item
if (selecting) return;
if (hitBackspace) {
// user hit backspace => move the selection backwards
// old item keeps being selected
if (offs>0) {
if (hitBackspaceOnSelection) offs--;
} else {
// User hit backspace with the cursor positioned on the start => beep
comboBox.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
}
highlightCompletedText(offs);
} else {
super.remove(offs, len);
}
}
#Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
// return immediately when selecting an item
if (selecting) return;
// insert the string into the document
super.insertString(offs, str, a);
// lookup and select a matching item
Object item = lookupItem(getText(0, getLength()));
if (item != null) {
setSelectedItem(item);
} else {
// keep old item selected if there is no match
item = comboBox.getSelectedItem();
// imitate no insert (later on offs will be incremented by str.length(): selection won't move forward)
offs = offs-str.length();
// provide feedback to the user that his input has been received but can not be accepted
comboBox.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
}
setText(item.toString());
// select the completed part
highlightCompletedText(offs+str.length());
}
private void setText(String text) {
try {
// remove all text and insert the completed string
super.remove(0, getLength());
super.insertString(0, text, null);
} catch (BadLocationException e) {
throw new RuntimeException(e.toString());
}
}
private void highlightCompletedText(int start) {
editor.setCaretPosition(getLength());
editor.moveCaretPosition(start);
}
private void setSelectedItem(Object item) {
selecting = true;
model.setSelectedItem(item);
selecting = false;
}
private Object lookupItem(String pattern) {
Object selectedItem = model.getSelectedItem();
// only search for a different item if the currently selected does not match
if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern)) {
return selectedItem;
} else {
// iterate over all items
for (int i=0, n=model.getSize(); i < n; i++) {
Object currentItem = model.getElementAt(i);
// current item starts with the pattern?
if (currentItem != null && startsWithIgnoreCase(currentItem.toString(), pattern)) {
return currentItem;
}
}
}
// no item starts with the pattern => return null
return null;
}
// checks if str1 starts with str2 - ignores case
private boolean startsWithIgnoreCase(String str1, String str2) {
return str1.toUpperCase().startsWith(str2.toUpperCase());
}
private static void createAndShowGUI() {
// the combo box (add/modify items if you like to)
final JComboBox comboBox = new JComboBox();
enable(comboBox,"");
// create and show a window containing the combo box
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(3);
frame.getContentPane().add(comboBox);
frame.pack(); frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(() -> {
createAndShowGUI();
});
}
}
I am using the YouTube embedded link in my website .I want to validate the link as if user paste something else other then embedded link then it should give me an alert invalid URL. I have used so many regex some has already in my code i have commented it .I want regular expression of YouTube embedded link only. Here i my code:
package com.edubot.client.lecture;
import gwt.material.design.client.ui.MaterialButton;
import gwt.material.design.client.ui.MaterialTextBox;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Widget;
public class EmbeddedLink extends Composite {
private static EmbeddedLinkUiBinder uiBinder = GWT
.create(EmbeddedLinkUiBinder.class);
interface EmbeddedLinkUiBinder extends UiBinder<Widget, EmbeddedLink> {
}
#UiField MaterialButton buttonembedded;
// #UiField IFrameElement youtubevideo;
#UiField HTMLPanel htmlpanel;
#UiField MaterialTextBox textbox ;
public EmbeddedLink() {
super();
sinkEvents( Event.ONPASTE );
initWidget(uiBinder.createAndBindUi(this));
}
#Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
switch (event.getTypeInt()) {
case Event.ONPASTE: {
Timer timer = new Timer() {
#Override
public void run() {
// TODO Auto-generated method stub
onPasted();
Window.alert("paste");
}
};
timer.schedule(1000);
}
}
}
// #UiHandler("buttonembedded")
// void onClick(ClickEvent e) {
//// onPasted();
// }
private void onPasted(){
// youtubevideo.setSrc(addEmbeddedLink());
Window.alert("msg1");
if(testEmbeddedLink()) {
String link=textbox.getText().trim();
htmlpanel.getElement().setInnerHTML(link);
Window.alert("Valid URL");
} else {
Window.alert("Invalid URL");
}
}
public boolean testEmbeddedLink(){
String link=textbox.getText().trim();
Window.alert("msg");
String patternString = "(?:http(?:s)?:\/\/)?(?:www\.)?(?:m\.)?(?:youtu\.be\/|youtube\.com\/)(?:)(?:.*&)?v(?:i)?=|(?:embed|v|vi|user)\/[a-zA-Z0-9\-]*";
// String patternString = "<iframe title='YouTube video player' width='' height='' src='http://www.youtube.com/embed/$1' frameborder='0' allowfullscreen='1'></iframe>";
// String patternString = "~<iframe.+?src="https?://www.youtube.com/embed/([a-zA-Z0-9_-]{11})"[^>]+?></iframe>~i";
boolean result = link.matches(patternString);
return result;
}
// "youtube.com/(?<=v|embed\\)?[a-zA-Z0-9]+[^#\\&\\?]*";
// "(?<=youtu.be/?<=v|embed\\/)?[a-zA-Z0-9]+[^#\\&\\?]*";
// "(https?://)?(www\\.)?(yotu\\.be/|youtube\\.com/)?((.+/)?(watch(\\?v=|.+&v=))?(v=)?)([\\w_-]{11})(&.+)?"
// (\"http:\/\/www\.youtube\.com\/v\/\w{11}\&rel\=1\");
// (https?://www.youtube(?:-nocookie)?.com/(?:v|embed)/([a-zA-Z0-9-]+).*)"
// /<iframe.+?src="http://www.youtube.com/embed/([a-zA-Z0-9_-]{11})"[^>]+?>";</iframe>/i";
// "(?:youtube.com)\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})\W";
// "s*(https?://www.youtube(?:-nocookie)?.com/(?:v|embed)/([a-zA-Z0-9-]+).*) ";
// "^.*((youtu.be"+ "\\/)" + "|(v\\/)|(\\/u\\/w\\/)|(embed\\/)|(watch\\?))\\??v?=?([^#\\&\\?]*)
}
private String getYouTubeUrl(String text)
{
String finalUrl = null;
String p = "(//www.youtube(?:-nocookie)?.com/(?:v|embed)/([a-zA-Z0-9-_]+).*)";
if(text.contains("src"))
{
if(text.contains("//") && text.contains("frameborder"))
{
int startpos = text.indexOf("/", text.indexOf("src="));
int endpos = text.indexOf("frameborder");
String url=text.substring(startpos, endpos-2);
if(url.matches(p))
{
finalUrl = url;
}
else
{
Window.alert("You have entered a wrong embed code");
}
}
else
{
Window.alert("You have entered a wrong embed code");
}
}
else
{
Window.alert("You have entered a wrong embed code");
}
return finalUrl;
}
In the following code, I create a jtable with a custom cell editor for the first column and then add undo capabilities to the table. When you run the program, the program allows you to change the values in the first column (test by appending a "d" and then an "e" to the "abc" already there). Now enter control-z (undo) and enter control-z again. It works as expected. But now enter control-z (undo) again. This time the "abc" is erased. It looks like the swing system is setting the initial value of the column and creating an undo event for that action which the user can then undo. My question - how do I write my code so that the user only can undo the actions the user makes?
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.DefaultCellEditor;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
public class UndoExample extends JFrame {
private static final long serialVersionUID = 1L;;
static Boolean objEnableUndoRedoActions = true;
UndoExample rootFrame;
public UndoExample() {
// This procedure starts the whole thing off.
//Create table
final String[] tableColumns = {"Column 1", "Column 2"};
JTable tabUndoExample = new JTable(
new DefaultTableModel(null, tableColumns) {
private static final long serialVersionUID = 1L;
});
final DefaultTableModel tabUndoExampleModel = (DefaultTableModel) tabUndoExample
.getModel();
tabUndoExampleModel.addRow(new Object[]{"abc", true});
tabUndoExampleModel.addRow(new Object[]{"zyw", false});
// Create the undo/redo manager
UndoManager objUndoManager = new UndoManager();
// Create a cell editor
JTextField tfTabField = new JTextField();
TableCellEditor objEditor = new DefaultCellEditor(tfTabField);
// Make the cell editor the default editor for this table's first column
tabUndoExample.getColumnModel().getColumn(0)
.setCellEditor(objEditor);
// Create the undo action on the field's document for the column
tfTabField.getDocument().addUndoableEditListener(
new uelUndoRedoTableCellField(objUndoManager, tabUndoExample));
// Allow undo and redo to be entered by the user
UndoRedoSetKeys(this, "Example", objUndoManager);
tabUndoExample.setInheritsPopupMenu(true);
//Add the table to the frame and show the frame
this.add(tabUndoExample);
this.pack();
setLocationRelativeTo(null);
}
public static void main(final String[] args) {
// Launches the application. This is required syntax.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
final UndoExample rootFrame = new UndoExample();
rootFrame.setVisible(true);
} catch (final Exception e) {
}
}
});
}
#SuppressWarnings("serial")
static class aueUndoRedoTableCellField extends AbstractUndoableEdit {
// Wrap the text edit action item as we need to add the table
// row and column information. This code is invoked when the
// code sees an undo event created and then later when the
// user requests an undo/redo.
JTable objTable = null;
UndoableEdit objUndoableEdit;
int objCol = -1;
int objRow = -1;
public aueUndoRedoTableCellField(UndoableEdit undoableEdit,
JTable table, int row, int col) {
super();
objUndoableEdit = undoableEdit;
objTable = table;
objCol = col;
objRow = row;
}
public void redo() throws CannotRedoException {
// When the user enters redo (or undo), this code sets
// that we are doing an redo (or undo), sets the cursor
// to the right location, and then does the undo (or redo)
// to the table cell.
UndoRedoManagerSetEnabled(false);
super.redo();
#SuppressWarnings("unused")
boolean success = objTable.editCellAt(objRow, objCol);
objTable.changeSelection(objRow, objCol, false, false);
objUndoableEdit.redo();
UndoRedoManagerSetEnabled(true);
}
public void undo() throws CannotUndoException {
super.undo();
UndoRedoManagerSetEnabled(false);
#SuppressWarnings("unused")
boolean success = objTable.editCellAt(objRow, objCol);
objTable.changeSelection(objRow, objCol, false, false);
objUndoableEdit.undo();
UndoRedoManagerSetEnabled(true);
}
}
static class aUndoRedo extends AbstractAction {
// This code is bound to the undo/redo keystrokes and tells
// Java what commands to run when the keys are later entered
// by the user.
private static final long serialVersionUID = 1L;
Boolean objUndo = true;
UndoManager objUndoManager = null;
final String objLocation;
public aUndoRedo(Boolean Undo, UndoManager undoManager, String location) {
super();
objUndo = Undo;
objUndoManager = undoManager;
objLocation = location;
}
#Override
public void actionPerformed(ActionEvent ae) {
try {
// See if operation allowed
if (!objUndoManager.canUndo() && objUndo
|| !objUndoManager.canRedo() && !objUndo)
return;
UndoRedoManagerSetEnabled(false);
if (objUndo) {
objUndoManager.undo();
} else {
objUndoManager.redo();
}
UndoRedoManagerSetEnabled(true);
// Catch errors and let user know
} catch (Exception e) {
UndoRedoManagerSetEnabled(true);
}
}
}
static class uelUndoRedoTableCellField implements UndoableEditListener {
// This action is called when the user changes the table's
// text cell. It saves the change for later undo/redo.
private UndoManager objUndoManager = null;
private JTable objTable = null;
public uelUndoRedoTableCellField(UndoManager undoManager,
JTable table) {
objUndoManager = undoManager;
objTable = table;
}
#Override
public void undoableEditHappened(UndoableEditEvent e) {
// Remember the edit but only if the code isn't doing
// an undo or redo currently.
if (UndoRedoManagerIsEnabled()) {
objUndoManager.addEdit(new aueUndoRedoTableCellField(e
.getEdit(), objTable, objTable.getSelectedRow(),
objTable.getSelectedColumn()));
}
}
}
static public Boolean UndoRedoManagerIsEnabled() {
// See if we are currently doing an undo/redo.
// Return true if so.
return objEnableUndoRedoActions;
}
static public void UndoRedoManagerSetEnabled(Boolean state) {
// Set the state of whether we are in undo/redo code.
objEnableUndoRedoActions = state;
}
static void UndoRedoSetKeys(JFrame frame, final String location, UndoManager undoManager) {
// Allow undo and redo to be called via these keystrokes for this dialog
final String cntl_y = "CNTL_Y";
final KeyStroke ksCntlY = KeyStroke.getKeyStroke("control Y");
final String cntl_z = "CNTL_Z";
final KeyStroke ksCntlZ = KeyStroke.getKeyStroke("control Z");
JRootPane root = frame.getRootPane();
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(ksCntlZ, cntl_z);
root.getActionMap().put(
cntl_z,
new aUndoRedo(true, undoManager, location));
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(ksCntlY, cntl_y);
root.getActionMap().put(
cntl_y,
new aUndoRedo(false, undoManager, location));
}
}
When you press a key, a series of things occur. The JTable, process the key stroke, it checks to see if the cell is editable (as the TableModel), it then asks the editor for the currently selected cell if the event should edit the cell (CellEditor#isCellEditable(EventObject)).
If this method returns true, the editor is prepared, the value from the TableModel is applied to the editor (ie setText is called), and the editor is added to the JTable, finally, the event which triggered the edit mode is re-dispatched to the editor, in your case the Ctrl+Z, which then triggers and undo event, returning the editor it's initial state (before setText was called).
You can try and use something like...
TableCellEditor objEditor = new DefaultCellEditor(tfTabField) {
#Override
public boolean isCellEditable(EventObject anEvent) {
boolean isEditable = super.isCellEditable(anEvent); //To change body of generated methods, choose Tools | Templates.
if (isEditable && anEvent instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) anEvent;
if (ke.isControlDown() && ke.getKeyCode() == KeyEvent.VK_Z) {
isEditable = false;
}
}
return isEditable;
}
};
to prevent the JTable from been placed into edit when a specific key stroke occurs
Updated
So based on Andrew's answer from JTextArea setText() & UndoManager, I devised a "configurable" UndoableEditListener which can be set to ignore undoable actions, for example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class FixedField {
public static void main(String[] args) {
new FixedField();
}
public FixedField() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class UndoableEditHandler implements UndoableEditListener {
private static final int MASK
= Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
private UndoManager undoManager = new UndoManager();
private boolean canUndo = true;
public UndoableEditHandler(JTextField field) {
Document doc = field.getDocument();
doc.addUndoableEditListener(this);
field.getActionMap().put("Undo", new AbstractAction("Undo") {
#Override
public void actionPerformed(ActionEvent evt) {
try {
if (undoManager.canUndo()) {
undoManager.undo();
}
} catch (CannotUndoException e) {
System.out.println(e);
}
}
});
field.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, MASK), "Undo");
field.getActionMap().put("Redo", new AbstractAction("Redo") {
#Override
public void actionPerformed(ActionEvent evt) {
try {
if (undoManager.canRedo()) {
undoManager.redo();
}
} catch (CannotRedoException e) {
System.out.println(e);
}
}
});
field.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, MASK), "Redo");
}
#Override
public void undoableEditHappened(UndoableEditEvent e) {
if (canUndo()) {
undoManager.addEdit(e.getEdit());
}
}
public void setCanUndo(boolean canUndo) {
this.canUndo = canUndo;
}
public boolean canUndo() {
return canUndo;
}
}
public class TestPane extends JPanel {
public TestPane() {
JTextField field = new JTextField(10);
UndoableEditHandler handler = new UndoableEditHandler(field);
handler.setCanUndo(false);
field.setText("Help");
handler.setCanUndo(true);
add(field);
}
}
}
Now, you're going to have to devices your own TableCellEditor to support this, for example...
public static class MyCellEditor extends AbstractCellEditor implements TableCellEditor {
private JTextField editor;
private UndoableEditHandler undoableEditHandler;
public MyCellEditor(JTextField editor) {
this.editor = editor;
undoableEditHandler = new UndoableEditHandler(editor);
editor.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
}
#Override
public Object getCellEditorValue() {
return editor.getText();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
undoableEditHandler.setCanUndo(false);
editor.setText(value == null ? null : value.toString());
undoableEditHandler.setCanUndo(true);
return editor;
}
}
I'm writing an eclipse-plugin which creating a new Console. Please see my source code:
CliConsoleFactory.java
import java.io.IOException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsoleFactory;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.IOConsoleOutputStream;
public class CliConsoleFactory implements IConsoleFactory {
private static final String ENTER_KEY = "\r\n";
private static final String CLI_PROMPT = "CLI> ";
private IConsoleView m_consoleView = null;
#Override
public void openConsole() {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
try {
m_consoleView = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW);
} catch (PartInitException e1) {
e1.printStackTrace();
}
if (m_consoleView == null) {
return;
}
final MyIOConsole myConsole = new MyIOConsole("CLI", null);
final IDocument document = myConsole.getDocument();
document.addDocumentListener(new IDocumentListener() {
#Override
public void documentChanged(DocumentEvent event) {
if (ENTER_KEY.equals(event.getText())) {
// Print the Prompt
writeToConsole(myConsole, CLI_PROMPT);
}
}
#Override
public void documentAboutToBeChanged(DocumentEvent event) {
}
});
ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[] { myConsole });
m_consoleView.display(myConsole);
writeToConsole(myConsole, CLI_PROMPT);
}
private void writeToConsole(final MyIOConsole myConsole, String msg) {
IOConsoleOutputStream stream = myConsole.newOutputStream();
stream.setActivateOnWrite(true);
try {
stream.write(msg);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
MyIOConsole.java
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.console.IOConsole;
public class MyIOConsole extends IOConsole {
public MyIOConsole(String name, ImageDescriptor imageDescriptor) {
super(name, imageDescriptor);
}
}
It works great. When I enter to a new line, the Prompt is "CLI> ", but the Caret position is not okie, it is at the first position of the line instead of the last position. I want to make the Caret move to the last position. Who know please help me.!!!
To gain access to the caret position, you will need to implement a console viewer.
This is the setup I have for my custom console,
public class MyConsole extends IOConsole
{
....
#Override
public IPageBookViewPage createPage(IConsoleView view) {
return new MyConsolePage(this, view);
}
}
public class MyConsolePage extends TextConsolePage
{
....
#Override
protected TextConsoleViewer createViewer(Composite parent) {
return new MyConsoleViewer(parent, (MyConsole) this.getConsole());
}
}
public class MyConsoleViewer extends TextConsoleViewer
{
//This class gives you access to setting the caret position
//by getting the styled text widget and then using setCaretOffset
}
There are multiple ways of getting the styled text widget depending on which method you are overriding. I also created my own Console history class which kept track of the caret offset since I needed additional functionality of using the up and down arrow keys to navigate through previously entered commands.
The best way to implement the MyConsoleViewer is to use Eclipse's vast source code that sets a perfect example. I practically reused all of this class org.eclipse.ui.internal.console.IOConsoleViewer. It even shows examples of setting the caret.
Hope this still helps as your question was a while ago.
I want to be notified if a string is copied to the system clipboard. When a new string is copied from the same source application, the FlavorListener won't get an event. To get informed when another string is copied, i read the string from the clipboard, convert it to a SrtingSelection, which is able to take the ownership, and put it back to the clipboard. Now I got informed twice, once the StringSelection lost ownership and once it takes it back. Is there a way to check for the ownership directly, instead of storing the string and check it equals the new one?
Here is my code so far:
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.FlavorEvent;
import java.awt.datatransfer.FlavorListener;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws Exception {
// The clipboard
final Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
// read clipboard and take ownership to get the FlavorListener notified
// when the content has changed but the owner has not
processClipboard(cb);
cb.addFlavorListener(new FlavorListener() {
#Override
public void flavorsChanged(FlavorEvent e) {
processClipboard(cb);
}
});
// keep thread for testing
Thread.sleep(100000L);
}
public static void processClipboard(Clipboard cb) {
// gets the content of clipboard
Transferable trans = cb.getContents(null);
if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
// cast to string
String s = (String) trans
.getTransferData(DataFlavor.stringFlavor);
System.out.println(s);
// only StringSelection can take ownership, i think
StringSelection ss = new StringSelection(s);
// set content, take ownership
cb.setContents(ss, ss);
} catch (UnsupportedFlavorException e2) {
e2.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
}
I hope you understand my bad english :-(
The previous answer is close to be working.
The real cure is to instead just monitor the event of ownership change. By the monitor's occupying the clipboard as owner when monitoring the clipboard, so when any application changes the clipboard, the ownership would change, so this would reliably indicate the clipboard content change. However, this approach must have sufficient wait to work, (200 ms was found to be working) after an ownership change event before accessing the clipboard and re-occupying the clipboard.
This solution was provided and proved to be working by marc weber at
http://www.coderanch.com/t/377833/java/java/listen-clipboard
I have verified for my purpose. If needed, I can post the solution here.
Yu
To avoid double notification remove the flavor listener before setting the new clipboard content and add the listener again after setting clipboard content.
public class NewClass implements FlavorListener, ClipboardOwner{
private Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
public NewClass() {
System.out.println("NewClass constructor");
clip.setContents(clip.getContents(null), this);
clip.addFlavorListener(this);
try {
Thread.sleep(100000L);
}
catch (InterruptedException e) {
}
}
#Override
public void flavorsChanged(FlavorEvent e) {
System.out.println("ClipBoard Changed!!!");
clip.removeFlavorListener(this);
clip.setContents(clip.getContents(null), this);
clip.addFlavorListener(this);
}
#Override
public void lostOwnership(Clipboard arg0, Transferable arg1) {
System.out.println("ownership losted");
}
}
I think this would work :)
import java.awt.AWTEvent;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.io.IOException;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
public final class ClipboardMonitor extends Observable implements ClipboardOwner {
private static ClipboardMonitor monitor = null;
public ClipboardMonitor() {
gainOwnership();
}
private void gainOwnership() {
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
try {
Transferable content = clip.getContents(null);
DataFlavor[] f = content.getTransferDataFlavors();
boolean imageDetected = false;
for (int i = 0; i < f.length; i++) {
// System.out.println("Name: " + f[i].getHumanPresentableName());
// System.out.println("MimeType: " + f[i].getMimeType());
// System.out.println("PrimaryType: " + f[i].getPrimaryType());
// System.out.println("SubType: " + f[i].getSubType());
if (f[i].equals(DataFlavor.imageFlavor)) {
imageDetected = true;
break;
}
}
if (imageDetected) {
System.out.println("Image content detected");
Transferable t = new Transferable() {
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { DataFlavor.stringFlavor };
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return false;
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return "dummy text instead of snapshot image";
}
};
clip.setContents(t, this);
} else {
clip.setContents(content, this);
}
setChanged();
notifyObservers(content);
} catch (IllegalArgumentException istateexception) {
istateexception.printStackTrace();
} catch (Exception ioexception) {
ioexception.printStackTrace();
}
}
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;
}
public void lostOwnership(Clipboard clipboard, Transferable contents) {
System.out.println("Ownership lost ...");
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(200);
gainOwnership();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public void flushClipboard() {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(""), null);
}
public static final ClipboardMonitor getMonitor() {
if (monitor == null)
monitor = new ClipboardMonitor();
return (monitor);
}
public static void main(String[] args) {
JFrame f = new JFrame();
ClipboardMonitor monitor = ClipboardMonitor.getMonitor();
monitor.addObserver(new Observer() {
public void update(Observable o, Object arg) {
System.out.println("Clipboard has been regained!");
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(500, 100);
f.setVisible(true);
}
}