/*******************************************************************************
* Copyright (c) 2004, 2008 John Krasnay and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* John Krasnay - initial API and implementation
*******************************************************************************/
package net.sf.vex.swing;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
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.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.io.IOException;
import java.net.URL;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.xml.parsers.ParserConfigurationException;
import net.sf.vex.ToolkitPlugin;
import net.sf.vex.core.Color;
import net.sf.vex.core.ColorResource;
import net.sf.vex.core.DisplayDevice;
import net.sf.vex.css.StyleSheet;
import net.sf.vex.dom.Document;
import net.sf.vex.dom.DocumentFragment;
import net.sf.vex.dom.DocumentValidationException;
import net.sf.vex.dom.Element;
import net.sf.vex.layout.Box;
import net.sf.vex.layout.BoxFactory;
import net.sf.vex.widget.HostComponent;
import net.sf.vex.widget.IBoxFilter;
import net.sf.vex.widget.VexWidgetImpl;
import org.xml.sax.SAXException;
/**
*
*/
public class VexComponent extends JComponent implements Scrollable, SelectionProvider {
private VexWidgetImpl impl;
private ActionMap staticActionMap = createActionMap();
private static InputMap staticInputMap = createInputMap();
private Timer caretTimer;
private int originX = 0;
private int originY = 0;
// Temporary clipboard is used during begin/end work
private Clipboard clipboard =
Toolkit.getDefaultToolkit().getSystemClipboard();
private SelectionProviderImpl selectionProvider =
new SelectionProviderImpl();
private static ResourceBundle uiStringBundle;
// Last VexComponent that had the focus. We need this sometimes to
// determine the target of an action.
private static VexComponent lastFocusedComponent;
//======================================================= LISTENERS
private ActionListener caretTimerListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
VexComponent.this.impl.toggleCaret();
}
};
private ComponentListener componentListener = new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
int width = VexComponent.this.getWidth();
VexComponent.this.impl.setLayoutWidth(width);
}
};
private FocusListener focusListener = new FocusListener() {
public void focusGained(FocusEvent e) {
lastFocusedComponent = VexComponent.this;
impl.setFocus(true);
VexComponent.this.caretTimer.start();
}
public void focusLost(FocusEvent e) {
impl.setFocus(false);
VexComponent.this.caretTimer.stop();
}
};
private KeyListener keyListener = new KeyListener() {
public void keyPressed(KeyEvent e) {
//final char NEWLINE = 0xa;
if (VexComponent.this.impl.getDocument() == null) {
Toolkit.getDefaultToolkit().beep();
return;
}
if (e.getKeyCode() == KeyEvent.VK_SHIFT){
return;
}
try {
InputMap map = VexComponent.staticInputMap;
KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
Object keyStrokeBinding = map.get(keyStroke);
if (keyStrokeBinding != null) {
BaseAction action = (BaseAction)VexComponent.this.staticActionMap.get(map.get(keyStroke));
action.actionPerformed(new ActionEvent(VexComponent.this, 0, ""), VexComponent.this.impl);
}else if (!Character.isISOControl(e.getKeyChar())
&& !e.isControlDown()) {
// We check e.isControlDown() to ensure Ctrl-Space does not
// also enter a space.
if (VexComponent.this.impl.hasSelection()) {
VexComponent.this.impl.deleteSelection();
}
insertChar(e.getKeyChar());
}
} catch (DocumentValidationException ex) {
Toolkit.getDefaultToolkit().beep();
} catch (Exception ex){
Toolkit.getDefaultToolkit().beep();
ex.printStackTrace();
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {}
};
private MouseListener mouseListener = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
boolean isButton1 =
(e.getModifiers() & MouseEvent.BUTTON1_MASK) > 0;
if (VexComponent.this.impl.getRootBox() != null && isButton1) {
if (hasFocus()) {
int offset = VexComponent.this.impl.viewToModel(e.getX() - originX, e.getY() - originY);
moveTo(offset);
if (e.getClickCount() == 2) {
selectWord();
}
}
requestFocus();
}
}
};
private MouseMotionListener mouseMotionListener =
new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
boolean isButton1 =
(e.getModifiers() & MouseEvent.BUTTON1_MASK) > 0;
if (VexComponent.this.impl.getRootBox() != null && isButton1) {
int offset = VexComponent.this.viewToModel(e.getX(), e.getY());
moveTo(offset, true);
requestFocus();
}
}
};
/**
* @see net.sf.vex.core.SelectionProvider#addSelectionListener(net.sf.vex.SelectionListener)
*/
public void addSelectionListener(SelectionListener listener) {
this.selectionProvider.addSelectionListener(listener);
}
public boolean canPaste() {
// TODO: sacrifice paste toolbar button state for performance
// (see note below).
if (true) {
return true;
}
// TODO: This next line takes a looong time in X11
// ~130ms on a Pentum-M 1.6GHz on Linux 2.4.21/XFree86 4.3.0
Transferable tfbl = this.clipboard.getContents(null);
DataFlavor flavor = VexSelection.VEX_DOCUMENT_FRAGMENT_FLAVOR;
if (!tfbl.isDataFlavorSupported(flavor)) {
return this.canPasteText();
}
DocumentFragment frag;
try {
frag = (DocumentFragment) tfbl.getTransferData(flavor);
} catch (UnsupportedFlavorException ex) {
return false;
} catch (IOException ex) {
return false;
}
return this.impl.canInsertFragment(frag);
}
/**
* Returns true if the clipboard has plain text content that can be
* pasted. Used to enable/disable the "paste text" action of a containing
* application.
*/
public boolean canPasteText() {
// TODO: sacrifice paste toolbar button state for performance
// (see note below).
if (true) {
return true;
}
// TODO: This next line takes a looong time in X11
// ~130ms on a Pentum-M 1.6GHz on Linux 2.4.21/XFree86 4.3.0
Transferable tfbl = this.clipboard.getContents(null);
DataFlavor plainText = new DataFlavor(String.class, "text/plain");
return tfbl.isDataFlavorSupported(plainText)
&& this.impl.canPasteText();
}
/**
* Copy the current selection to the clipboard.
*/
public void copySelection() {
if (this.impl.hasSelection()) {
StringSelection sel =
new VexSelection(
this.impl.getSelectedText(),
this.impl.getSelectedFragment());
this.clipboard.setContents(sel, sel);
}
}
public void cutSelection() {
this.copySelection();
this.deleteSelection();
}
public void deleteSelection() {
this.impl.deleteSelection();
}
/**
* Returns the VexComponent that last had focus.
*/
public static VexComponent getLastFocusedComponent() {
return lastFocusedComponent;
}
/**
* Returns a string from the resource bundle for the current locale.
* If the string is not found in the resource bundle, returns null.
*
* @param name property for which to return the string.
*/
public static String getUIString(String name) {
if (uiStringBundle == null) {
uiStringBundle =
ResourceBundle.getBundle("net.sf.vex.app.UIStrings");
}
try {
return uiStringBundle.getString(name);
} catch (MissingResourceException ex) {
return null;
}
}
//------------------------------------------------ Scrollable methods
public Dimension getPreferredScrollableViewportSize() {
return this.getPreferredSize();
}
public int getScrollableBlockIncrement(
Rectangle visibleRect,
int orientation,
int direction) {
return Math.max(visibleRect.height - 40, 40);
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
public boolean getScrollableTracksViewportWidth() {
return true;
}
public int getScrollableUnitIncrement(
Rectangle visibleRect,
int orientation,
int direction) {
return 20; // TODO: fix scrolling increment
}
public VexComponent() {
ActionMap customActionMap = new ActionMap();
customActionMap.setParent(staticActionMap);
this.setActionMap(customActionMap);
InputMap customInputMap = new InputMap();
customInputMap.setParent(staticInputMap);
this.setInputMap(JComponent.WHEN_FOCUSED, customInputMap);
this.addComponentListener(this.componentListener);
this.addFocusListener(this.focusListener);
this.addKeyListener(this.keyListener);
this.addMouseListener(this.mouseListener);
this.addMouseMotionListener(this.mouseMotionListener);
this.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
this.caretTimer = new Timer(
UIManager.getInt("TextPane.caretBlinkRate"),
this.caretTimerListener);
this.caretTimer.start();
DisplayDevice.setCurrent(new AwtDisplayDevice());
impl = new VexWidgetImpl(this.hostComponent);
}
public void insertElement(Element element) throws DocumentValidationException {
this.impl.insertElement(element);
}
public void morph(Element element) throws DocumentValidationException {
this.impl.morph(element);
}
public void paint(Graphics g){
this.paintComponent(g);
}
protected void paintComponent(Graphics g) {
long start = 0;
if (ToolkitPlugin.getInstance().isDebugging()) {
start = System.currentTimeMillis();
}
if (this.impl.getDocument() == null){
return;
}
AwtGraphics awtg = new AwtGraphics((Graphics2D) g);
awtg.setOrigin(0, this.originY);
Color bgColor = impl.getBackgroundColor();
if (bgColor == null) {
bgColor = new Color(255, 255, 255);
}
ColorResource color = awtg.createColor(bgColor);
ColorResource oldColor = awtg.setColor(color);
Rectangle r = g.getClipBounds();
awtg.fillRect((int)r.getX() - this.originX, (int)r.getY() - this.originY,(int) r.getWidth(),(int) r.getHeight());
awtg.setColor(oldColor);
color.dispose();
this.impl.paint(awtg, 0, 0);
/*
Graphics2D g2d = (Graphics2D) g;
if (this.isAntiAliased()) {
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
impl.paint(new AwtGraphics(g2d), 0, 0);
if (this.caretVisible && this.isEnabled()) {
if (this.hasFocus()) {
g.setColor(Color.black);
} else {
g.setColor(Color.gray);
}
g2d.setStroke(caretStroke);
g2d.draw(this.caretShapes[0]);
}
*/
if (ToolkitPlugin.getInstance().isDebugging()) {
long end = System.currentTimeMillis();
System.out.println("paint took " + (end - start) + "ms");
}
}
public void paste() throws DocumentValidationException {
if (!this.canPaste()) {
return;
}
try {
Transferable tfbl = this.clipboard.getContents(null);
DataFlavor flavor = VexSelection.VEX_DOCUMENT_FRAGMENT_FLAVOR;
if (tfbl.isDataFlavorSupported(flavor)) {
DocumentFragment frag =
(DocumentFragment) tfbl.getTransferData(flavor);
this.impl.insertFragment(frag);
} else {
this.pasteText();
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (UnsupportedFlavorException ex) {
ex.printStackTrace();
}
}
public void pasteText() throws DocumentValidationException {
try {
Transferable tfbl = this.clipboard.getContents(null);
DataFlavor plainText = new DataFlavor(String.class, "text/plain");
if (tfbl.isDataFlavorSupported(plainText)) {
String text = (String) tfbl.getTransferData(plainText);
this.impl.insertText(text);
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (UnsupportedFlavorException ex) {
ex.printStackTrace();
}
}
public void split() throws DocumentValidationException { this.impl.split(); }
public void undo() throws CannotUndoException { this.impl.undo(); }
public int viewToModel(int x, int y) { return this.impl.viewToModel(x, y); }
public void deleteNextChar() throws DocumentValidationException { this.impl.deleteNextChar(); }
public void deletePreviousChar() throws DocumentValidationException { this.impl.deletePreviousChar(); }
public void doWork(Runnable runnable) { this.impl.doWork(runnable); }
public void doWork(boolean savePosition, Runnable runnable) { this.impl.doWork(savePosition, runnable); }
public void endWork(boolean success) { this.impl.endWork(success); }
public Box findInnermostBox(IBoxFilter filter) { return this.impl.findInnermostBox(filter); }
public BoxFactory getBoxFactory() { return this.impl.getBoxFactory(); }
public int getCaretOffset() { return this.impl.getCaretOffset(); }
public Element getCurrentElement() { return this.impl.getCurrentElement(); }
public Document getDocument() { return this.impl.getDocument(); }
public int getLayoutWidth() { return this.impl.getLayoutWidth(); }
public int getSelectionEnd() { return this.impl.getSelectionEnd(); }
public int getSelectionStart() { return this.impl.getSelectionStart(); }
public DocumentFragment getSelectedFragment() { return this.impl.getSelectedFragment(); }
public String getSelectedText() { return this.impl.getSelectedText(); }
public StyleSheet getStyleSheet() { return this.impl.getStyleSheet(); }
public int getUndoDepth() { return this.impl.getUndoDepth(); }
public String[] getValidInsertElements() { return this.impl.getValidInsertElements(); }
public String[] getValidMorphElements() { return this.impl.getValidMorphElements(); }
public boolean hasSelection() { return this.impl.hasSelection(); }
public void insertChar(char c) throws DocumentValidationException { this.impl.insertChar(c); }
public void insertFragment(DocumentFragment frag) throws DocumentValidationException { this.impl.insertFragment(frag); }
public void insertText(String text) throws DocumentValidationException { this.impl.insertText(text); }
public boolean isDebugging() { return impl.isDebugging(); }
public void moveBy(int distance) { this.impl.moveBy(distance); }
public void moveBy(int distance, boolean select) { this.impl.moveBy(distance, select); }
public void moveTo(int offset) { this.impl.moveTo(offset); }
public void moveTo(int offset, boolean select) { this.impl.moveTo(offset, select); }
public void moveToLineEnd(boolean select) { this.impl.moveToLineEnd(select); }
public void moveToLineStart(boolean select) { this.impl.moveToLineStart(select); }
public void moveToNextLine(boolean select) { this.impl.moveToNextLine(select); }
public void moveToNextPage(boolean select) { this.impl.moveToNextPage(select); }
public void moveToNextWord(boolean select) { this.impl.moveToNextWord(select); }
public void moveToPreviousLine(boolean select) { this.impl.moveToPreviousLine(select); }
public void moveToPreviousPage(boolean select) { this.impl.moveToPreviousPage(select); }
public void moveToPreviousWord(boolean select) { this.impl.moveToPreviousWord(select); }
public void redo() throws CannotRedoException { this.impl.redo(); }
public void removeAttribute(String attributeName) { this.impl.removeAttribute(attributeName); }
public void savePosition(Runnable runnable) { this.impl.savePosition(runnable); }
public void selectAll() { this.impl.selectAll(); }
public void selectWord() { this.impl.selectWord(); }
public void setAttribute(String attributeName, String value) { this.impl.setAttribute(attributeName, value); }
public void setBoxFactory(BoxFactory boxFactory) { this.impl.setBoxFactory(boxFactory); }
public void setDebugging(boolean debugging) { impl.setDebugging(debugging); }
public void setDocument(Document doc, StyleSheet styleSheet) { this.impl.setDocument(doc, styleSheet); }
public void setDocument(URL docUrl, URL ssURL) throws IOException, ParserConfigurationException, SAXException {
this.impl.setDocument(docUrl, ssURL);
}
public void setLayoutWidth(int width) { this.impl.setLayoutWidth(width); }
public void setStyleSheet(StyleSheet styleSheet) { this.impl.setStyleSheet(styleSheet); }
public void setStyleSheet(URL ssUrl) throws IOException { this.impl.setStyleSheet(ssUrl); }
/**
* @see SelectionProvider#removeSelectionListener
*/
public void removeSelectionListener(SelectionListener listener) {
this.selectionProvider.removeSelectionListener(listener);
}
public Action[] getInsertElementActions(){
String[] names = this.getValidInsertElements();
Action[] actions = new Action[names.length];
for (int i = 0; i < names.length; i++){
actions[i] = new InsertElementAction(names[i]);
}
return actions;
}
public Action[] getMorphElementActions(){
String[] names = this.getValidMorphElements();
Action[] actions = new Action[names.length];
for (int i = 0; i < names.length; i++){
actions[i] = new MorphAction(names[i]);
}
return actions;
}
/**
* Display a popup menu of valid elements to insert at the current caret
* position.
*/
public void showInsertElementPopup() {
JPopupMenu popup = new JPopupMenu();
Action[] actions = this.getInsertElementActions();
if (actions.length == 0) {
return;
}
for (int i = 0; i < actions.length; i++) {
popup.add(actions[i]);
}
net.sf.vex.core.Rectangle caretBounds = this.impl.getCaret().getBounds();
popup.show(this, caretBounds.getX() + 10, caretBounds.getY());
}
/**
* Display a popup menu of valid elements to which the current element
* can be morphed.
*/
public void showMorphElementPopup() {
JPopupMenu popup = new JPopupMenu();
Action[] actions = this.getMorphElementActions();
if (actions.length == 0) {
return;
}
for (int i = 0; i < actions.length; i++) {
popup.add(actions[i]);
}
net.sf.vex.core.Rectangle caretBounds = this.impl.getCaret().getBounds();
popup.show(this, caretBounds.getX() + 10, caretBounds.getY());
}
//========================================================= PRIVATE
private HostComponent hostComponent = new HostComponent() {
// This is needed to create a default Graphics object,
// but creating these is really slow, so we cache 'em.
private GraphicsEnvironment graphicsEnvironment = null;
private java.awt.image.BufferedImage dummyImage = null;
public net.sf.vex.core.Graphics createDefaultGraphics() {
if (graphicsEnvironment == null) {
graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gdev = graphicsEnvironment.getDefaultScreenDevice();
GraphicsConfiguration gconf = gdev.getDefaultConfiguration();
dummyImage = gconf.createCompatibleImage(1, 1);
}
Graphics g = graphicsEnvironment.createGraphics(dummyImage);
return new AwtGraphics((Graphics2D) g);
}
public void fireSelectionChanged() {
VexComponent.this.fireSelectionChanged();
}
public void invokeLater(Runnable runnable) {
SwingUtilities.invokeLater(runnable);
}
public void repaint() {
VexComponent.this.repaint();
}
public void repaint(int x, int y, int width, int height) {
VexComponent.this.repaint();//VexComponent.this.repaint(x , y, width, height);
}
public void scrollTo(int left, int top) {
VexComponent.this.setOrigin(-left, -top);
}
public void setPreferredSize(int width, int height) {
/* Dimension size = new Dimension(width, height);
VexComponent.this.setPreferredSize(size);
VexComponent.this.setSize(size);*/
}
public net.sf.vex.core.Rectangle getViewport() {
return new net.sf.vex.core.Rectangle(
0 - VexComponent.this.originX,
0 - VexComponent.this.originY,
VexComponent.this.getWidth(),
VexComponent.this.getHeight());
}
@Override
public boolean isDisposed()
{
// TODO Auto-generated method stub
return false;
}
};
/**
* Action for inserting an element into the document at the current
* offset.
*/
private class InsertElementAction extends AbstractAction {
private String name;
/**
* Class constructor.
*
* @param name Name of the element to insert.
*/
public InsertElementAction(String name) {
super(name);
this.name = name;
}
public void actionPerformed(ActionEvent e) {
try {
insertElement(new Element(this.name));
} catch (DocumentValidationException ex) {
Toolkit.getDefaultToolkit().beep();
}
}
}
/**
* Action for morphing offset.
*/
private class MorphAction extends AbstractAction {
private String name;
/**
* Class constructor.
*
* @param name Name of the element to which the current element
* is to be morphed.
*/
public MorphAction(String name) {
super(name);
this.name = name;
}
public void actionPerformed(ActionEvent e) {
try {
morph(new Element(this.name));
} catch (DocumentValidationException ex) {
Toolkit.getDefaultToolkit().beep();
}
}
}
/**
* Runnable to layout the control.
*/
// private class LayoutRunnable implements Runnable {
// private boolean force;
// public LayoutRunnable(boolean force) {
// this.force = force;
// }
//
// public void run() {
// relayout(force);
// }
// }
/**
* Base class for actions in the action map. Overloads the actionPerformed
* method to pass a VexComponent and allow a DocumentValidationException
* to be returned.
*/
private abstract static class BaseAction extends AbstractAction {
/**
* Calls <code>actionPerformed(ActionEvent e, VexComponent c)</code>,
* and beeps if an exception is thrown.
* @param e ActionEvent
*/
public void actionPerformed(ActionEvent e) {
try {
if (e.getSource() instanceof VexWidgetImpl) {
this.actionPerformed(e, (VexWidgetImpl) e.getSource());
}
} catch (Exception ex) {
ex.printStackTrace(); // TODO: log this
Toolkit.getDefaultToolkit().beep();
}
}
/**
* Performs the action. The corresponding VexComponent is provided,
* and any exception can be thrown.
* @param e the ActionEvent
* @param c the VexComponent
*/
public abstract void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws Exception;
}
public int getHeight(){
return impl.getHeight();
}
public void scrollBy(int x, int y){
setOrigin(originX - x, originY - y);
}
public void scrollTo(int x, int y){
setOrigin(-x, -y);
}
private void setOrigin(int x, int y){
this.originX = x;
this.originY = y;
}
/**
* Creates the ActionMap for the component.
*/
private ActionMap createActionMap() {
ActionMap am = new ActionMap();
am.put("copy-selection", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
copySelection();
}
});
am.put("cut-selection", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
cutSelection();
}
});
am.put("delete-next-char", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.deleteNextChar();
}
});
am.put("delete-previous-char", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.deletePreviousChar();
}
});
am.put("move-to-document-end", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveTo(c.getDocument().getLength() - 1, false);
}
});
am.put("move-to-document-start", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveTo(1, false);
}
});
am.put("move-to-line-end", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToLineEnd(false);
}
});
am.put("move-to-line-start", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToLineStart(false);
}
});
am.put("move-to-next-char", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
int offset = c.getCaretOffset();
if (offset < c.getDocument().getLength() - 1) {
c.moveTo(offset + 1, false);
}
}
});
am.put("move-to-next-line", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToNextLine(false);
}
});
am.put("move-to-next-word", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToNextWord(false);
}
});
am.put("move-to-previous-char", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
int offset = c.getCaretOffset();
if (offset > 1) {
c.moveTo(offset - 1, false);
}
}
});
am.put("move-to-previous-line", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToPreviousLine(false);
}
});
am.put("move-to-previous-word", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToPreviousWord(false);
}
});
am.put("paste", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
paste();
}
});
am.put("paste-text", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
pasteText();
}
});
am.put("redo", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.redo();
}
});
am.put("select-to-document-end", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveTo(c.getDocument().getLength() - 1, true);
}
});
am.put("select-to-document-start", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveTo(1, true);
}
});
am.put("select-to-line-end", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToLineEnd(true);
}
});
am.put("select-to-line-start", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToLineStart(true);
}
});
am.put("select-to-next-char", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
int offset = c.getCaretOffset();
if (offset < c.getDocument().getLength() - 1) {
c.moveTo(offset + 1, true);
}
}
});
am.put("select-to-next-line", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToNextLine(true);
}
});
am.put("select-to-next-word", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToNextWord(true);
}
});
am.put("select-to-previous-char", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
int offset = c.getCaretOffset();
if (offset > 1) {
c.moveTo(offset - 1, true);
}
}
});
am.put("select-to-previous-line", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToPreviousLine(true);
}
});
am.put("select-to-previous-word", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToPreviousWord(true);
}
});
am.put("move-to-previous-page" , new BaseAction(){
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToPreviousPage(false);
}
});
am.put("select-to-previous-page" , new BaseAction(){
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToPreviousPage(true);
}
});
am.put("move-to-next-page" , new BaseAction(){
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToNextPage(false);
}
});
am.put("select-to-next-page" , new BaseAction(){
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.moveToNextPage(true);
}
});
am.put("show-insert-element-popup", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
VexComponent.this.showInsertElementPopup();
}
});
am.put("show-morph-element-popup", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
VexComponent.this.showMorphElementPopup();
}
});
am.put("split", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.split();
}
});
am.put("undo", new BaseAction() {
public void actionPerformed(ActionEvent e, VexWidgetImpl c)
throws DocumentValidationException {
c.undo();
}
});
return am;
}
/**
* Create the input map for the control. This input map can reference
* both static and non-static actions.
*/
public static InputMap createInputMap() {
InputMap im = new InputMap();
im.put(KeyStroke.getKeyStroke("LEFT"), "move-to-previous-char");
im.put(KeyStroke.getKeyStroke("RIGHT"), "move-to-next-char");
im.put(KeyStroke.getKeyStroke("shift LEFT"), "select-to-previous-char");
im.put(KeyStroke.getKeyStroke("shift RIGHT"), "select-to-next-char");
im.put(KeyStroke.getKeyStroke("control LEFT"), "move-to-previous-word");
im.put(KeyStroke.getKeyStroke("control RIGHT"), "move-to-next-word");
im.put(
KeyStroke.getKeyStroke("shift control LEFT"),
"select-to-previous-word");
im.put(
KeyStroke.getKeyStroke("shift control RIGHT"),
"select-to-next-word");
im.put(KeyStroke.getKeyStroke("HOME"), "move-to-line-start");
im.put(KeyStroke.getKeyStroke("END"), "move-to-line-end");
im.put(KeyStroke.getKeyStroke("shift HOME"), "select-to-line-start");
im.put(KeyStroke.getKeyStroke("shift END"), "select-to-line-end");
im.put(KeyStroke.getKeyStroke("PAGE_UP"), "move-to-previous-page");
im.put(KeyStroke.getKeyStroke("PAGE_DOWN"), "move-to-next-page");
im.put(KeyStroke.getKeyStroke("shift PAGE_UP"), "select-to-previous-page");
im.put(KeyStroke.getKeyStroke("shift PAGE_DOWN"), "select-to-next-page");
im.put(
KeyStroke.getKeyStroke("control HOME"),
"move-to-document-start");
im.put(KeyStroke.getKeyStroke("control END"), "move-to-document-end");
im.put(
KeyStroke.getKeyStroke("shift control HOME"),
"select-to-document-start");
im.put(
KeyStroke.getKeyStroke("shift control END"),
"select-to-document-end");
im.put(KeyStroke.getKeyStroke("UP"), "move-to-previous-line");
im.put(KeyStroke.getKeyStroke("DOWN"), "move-to-next-line");
im.put(KeyStroke.getKeyStroke("shift UP"), "select-to-previous-line");
im.put(KeyStroke.getKeyStroke("shift DOWN"), "select-to-next-line");
im.put(KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous-char");
im.put(KeyStroke.getKeyStroke("DELETE"), "delete-next-char");
im.put(KeyStroke.getKeyStroke("ENTER"), "split");
im.put(KeyStroke.getKeyStroke("control C"), "copy-selection");
im.put(KeyStroke.getKeyStroke("control X"), "cut-selection");
im.put(KeyStroke.getKeyStroke("control V"), "paste");
im.put(KeyStroke.getKeyStroke("shift control V"), "paste-text");
im.put(KeyStroke.getKeyStroke("control Y"), "redo");
im.put(KeyStroke.getKeyStroke("control Z"), "undo");
im.put(KeyStroke.getKeyStroke("control U"), "unwrap");
im.put(KeyStroke.getKeyStroke("control SPACE"), "show-insert-element-popup");
im.put(KeyStroke.getKeyStroke("control M"), "show-morph-element-popup");
return im;
}
/**
* Fires a selection changed event to registered selection change listeners.
* This method suppresses events that occur while there are outstanding
* beginWork calls.
*/
private void fireSelectionChanged() {
// if (this.beginWorkCount == 0) {
// this.selectionProvider.fireSelectionChanged(
// new VexComponentSelection(this));
// }
}
/**
* Calls relayout from the Swing event loop rather than immediately.
*
* @param force Layout should be forced, e.g. when the width of
* the component changed.
*/
// public void relayoutLater(boolean force) {
// if (this.rootBox != null) {
// SwingUtilities.invokeLater(new LayoutRunnable(force));
// }
// }
}